From 45493b89ac69ce48929e5a197d0f347b8116c203 Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Mon, 30 Mar 2026 21:58:05 -0400 Subject: [PATCH 1/7] ColladaLoader: Add support for instance_joint. --- types/three/examples/jsm/loaders/ColladaLoader.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/types/three/examples/jsm/loaders/ColladaLoader.d.ts b/types/three/examples/jsm/loaders/ColladaLoader.d.ts index 70efc74fe..42117ccbb 100644 --- a/types/three/examples/jsm/loaders/ColladaLoader.d.ts +++ b/types/three/examples/jsm/loaders/ColladaLoader.d.ts @@ -21,6 +21,7 @@ export interface ColladaLibrary { kinematicsModels: Record; physicsModels: Record; kinematicsScenes: Record; + joints: Record; } export interface Collada { From 7d87957181963c23be2cf1bf04f962f8d1641fef Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Mon, 30 Mar 2026 21:58:36 -0400 Subject: [PATCH 2/7] Update three.js --- three.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/three.js b/three.js index 112ba9b71..87c407b70 160000 --- a/three.js +++ b/three.js @@ -1 +1 @@ -Subproject commit 112ba9b71c29e984e339dd95ab42584b43029940 +Subproject commit 87c407b70235a6cdf64f96fa3f74d6fb9b0ccbbc From 9edf8e024881a19ba56b426b04e8e1a638064d89 Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Mon, 30 Mar 2026 21:59:35 -0400 Subject: [PATCH 3/7] Add examples --- examples-testing/examples/css2d_label.ts | 189 +++++ examples-testing/examples/css3d_mixed.ts | 138 +++ examples-testing/examples/css3d_molecules.ts | 353 ++++++++ .../examples/css3d_orthographic.ts | 208 +++++ .../examples/css3d_periodictable.ts | 793 ++++++++++++++++++ examples-testing/examples/css3d_sandbox.ts | 180 ++++ examples-testing/examples/css3d_sprites.ts | 157 ++++ examples-testing/examples/css3d_youtube.ts | 79 ++ examples-testing/examples/games_fps.ts | 377 +++++++++ .../examples/misc_animation_groups.ts | 128 +++ .../examples/misc_animation_keys.ts | 132 +++ .../examples/misc_boxselection.ts | 137 +++ .../examples/misc_controls_arcball.ts | 211 +++++ .../examples/misc_controls_drag.ts | 153 ++++ .../examples/misc_controls_fly.ts | 218 +++++ .../examples/misc_controls_map.ts | 102 +++ .../examples/misc_controls_orbit.ts | 95 +++ .../examples/misc_controls_pointerlock.ts | 245 ++++++ .../examples/misc_controls_trackball.ts | 138 +++ .../examples/misc_controls_transform.ts | 182 ++++ .../examples/misc_exporter_draco.ts | 117 +++ .../examples/misc_exporter_exr.ts | 158 ++++ .../examples/misc_exporter_gltf.ts | 534 ++++++++++++ .../examples/misc_exporter_ktx2.ts | 145 ++++ .../examples/misc_exporter_obj.ts | 192 +++++ .../examples/misc_exporter_ply.ts | 156 ++++ .../examples/misc_exporter_stl.ts | 129 +++ .../examples/misc_exporter_usdz.ts | 130 +++ examples-testing/examples/misc_uv_tests.ts | 44 + .../examples/physics_ammo_instancing.ts | 132 +++ .../examples/physics_jolt_instancing.ts | 133 +++ .../examples/physics_rapier_basic.ts | 144 ++++ .../physics_rapier_character_controller.ts | 187 +++++ .../examples/physics_rapier_instancing.ts | 142 ++++ .../examples/physics_rapier_joints.ts | 130 +++ .../examples/physics_rapier_terrain.ts | 295 +++++++ .../physics_rapier_vehicle_controller.ts | 297 +++++++ examples-testing/examples/svg_lines.ts | 87 ++ examples-testing/examples/svg_sandbox.ts | 212 +++++ .../examples/webaudio_orientation.ts | 141 ++++ examples-testing/examples/webaudio_sandbox.ts | 223 +++++ examples-testing/examples/webaudio_timing.ts | 155 ++++ .../examples/webaudio_visualizer.ts | 86 ++ .../examples/webgl_animation_keyframes.ts | 96 +++ .../examples/webgl_animation_multiple.ts | 200 +++++ .../webgl_animation_skinning_morph.ts | 190 +++++ .../examples/webgl_animation_walk.ts | 379 +++++++++ .../examples/webgl_buffergeometry.ts | 178 ++++ ...webgl_buffergeometry_attributes_integer.ts | 142 ++++ .../webgl_buffergeometry_attributes_none.ts | 56 ++ ...fergeometry_custom_attributes_particles.ts | 103 +++ .../webgl_buffergeometry_drawrange.ts | 239 ++++++ .../webgl_buffergeometry_glbufferattribute.ts | 140 ++++ .../examples/webgl_buffergeometry_indexed.ts | 137 +++ .../webgl_buffergeometry_instancing.ts | 138 +++ ...gl_buffergeometry_instancing_billboards.ts | 86 ++ ...l_buffergeometry_instancing_interleaved.ts | 152 ++++ .../examples/webgl_buffergeometry_lines.ts | 121 +++ .../webgl_buffergeometry_lines_indexed.ts | 179 ++++ .../examples/webgl_buffergeometry_points.ts | 109 +++ ...webgl_buffergeometry_points_interleaved.ts | 122 +++ .../webgl_buffergeometry_rawshader.ts | 97 +++ .../webgl_buffergeometry_selective_draw.ts | 150 ++++ .../examples/webgl_buffergeometry_uint.ts | 177 ++++ examples-testing/examples/webgl_camera.ts | 218 +++++ .../examples/webgl_camera_array.ts | 104 +++ .../webgl_camera_logarithmicdepthbuffer.ts | 248 ++++++ .../examples/webgl_clipculldistance.ts | 113 +++ examples-testing/examples/webgl_clipping.ts | 195 +++++ .../examples/webgl_clipping_advanced.ts | 357 ++++++++ .../examples/webgl_clipping_intersection.ts | 137 +++ .../examples/webgl_clipping_stencil.ts | 263 ++++++ .../examples/webgl_custom_attributes.ts | 100 +++ .../examples/webgl_custom_attributes_lines.ts | 121 +++ .../webgl_custom_attributes_points.ts | 117 +++ .../webgl_custom_attributes_points2.ts | 193 +++++ .../webgl_custom_attributes_points3.ts | 200 +++++ examples-testing/examples/webgl_decals.ts | 240 ++++++ .../examples/webgl_effects_anaglyph.ts | 120 +++ .../examples/webgl_effects_ascii.ts | 81 ++ .../examples/webgl_effects_parallaxbarrier.ts | 110 +++ .../examples/webgl_effects_stereo.ts | 98 +++ .../examples/webgl_framebuffer_texture.ts | 151 ++++ .../examples/webgl_furnace_test.ts | 96 +++ examples-testing/examples/webgl_geometries.ts | 165 ++++ .../examples/webgl_geometry_colors.ts | 177 ++++ .../webgl_geometry_colors_lookuptable.ts | 148 ++++ .../examples/webgl_geometry_convex.ts | 117 +++ .../examples/webgl_geometry_cube.ts | 46 + .../examples/webgl_geometry_extrude_shapes.ts | 159 ++++ .../webgl_geometry_extrude_splines.ts | 310 +++++++ .../examples/webgl_geometry_minecraft.ts | 184 ++++ .../examples/webgl_geometry_nurbs.ts | 298 +++++++ .../examples/webgl_geometry_shapes.ts | 363 ++++++++ .../examples/webgl_geometry_teapot.ts | 185 ++++ .../examples/webgl_geometry_terrain.ts | 174 ++++ .../webgl_geometry_terrain_raycast.ts | 206 +++++ .../examples/webgl_geometry_text.ts | 312 +++++++ .../examples/webgl_geometry_text_shapes.ts | 112 +++ .../examples/webgl_geometry_text_stroke.ts | 152 ++++ .../examples/webgl_gpgpu_birds.ts | 313 +++++++ .../examples/webgl_gpgpu_birds_gltf.ts | 413 +++++++++ .../examples/webgl_gpgpu_protoplanet.ts | 280 +++++++ .../examples/webgl_gpgpu_water.ts | 583 +++++++++++++ examples-testing/examples/webgl_helpers.ts | 117 +++ .../examples/webgl_instancing_dynamic.ts | 175 ++++ .../examples/webgl_instancing_morph.ts | 150 ++++ .../examples/webgl_instancing_performance.ts | 262 ++++++ .../examples/webgl_instancing_raycast.ts | 116 +++ .../examples/webgl_instancing_scatter.ts | 257 ++++++ .../webgl_interactive_buffergeometry.ts | 244 ++++++ .../examples/webgl_interactive_cubes.ts | 114 +++ .../examples/webgl_interactive_cubes_gpu.ts | 231 +++++ .../examples/webgl_interactive_cubes_ortho.ts | 129 +++ .../examples/webgl_interactive_lines.ts | 160 ++++ .../examples/webgl_interactive_points.ts | 143 ++++ .../webgl_interactive_raycasting_points.ts | 223 +++++ .../webgl_interactive_voxelpainter.ts | 158 ++++ examples-testing/examples/webgl_lensflares.ts | 140 ++++ examples-testing/examples/webgl_lightprobe.ts | 142 ++++ .../examples/webgl_lightprobe_cubecamera.ts | 85 ++ .../examples/webgl_lights_hemisphere.ts | 191 +++++ .../examples/webgl_lights_physical.ts | 237 ++++++ .../examples/webgl_lights_rectarealight.ts | 110 +++ .../examples/webgl_lights_spotlight.ts | 202 +++++ .../examples/webgl_lights_spotlights.ts | 133 +++ .../examples/webgl_lines_colors.ts | 181 ++++ .../examples/webgl_lines_dashed.ts | 186 ++++ examples-testing/examples/webgl_lines_fat.ts | 235 ++++++ .../examples/webgl_lines_fat_raycasting.ts | 280 +++++++ .../examples/webgl_lines_fat_wireframe.ts | 210 +++++ examples-testing/examples/webgl_loader_3dm.ts | 95 +++ examples-testing/examples/webgl_loader_3ds.ts | 62 ++ .../examples/webgl_loader_3dtiles.ts | 78 ++ examples-testing/examples/webgl_loader_3mf.ts | 105 +++ .../examples/webgl_loader_3mf_materials.ts | 106 +++ examples-testing/examples/webgl_loader_amf.ts | 62 ++ examples-testing/examples/webgl_loader_bvh.ts | 64 ++ .../examples/webgl_loader_collada.ts | 86 ++ .../examples/webgl_loader_collada_skinning.ts | 100 +++ .../examples/webgl_loader_draco.ts | 83 ++ examples-testing/examples/webgl_loader_fbx.ts | 169 ++++ .../examples/webgl_loader_fbx_nurbs.ts | 61 ++ .../examples/webgl_loader_gcode.ts | 81 ++ .../examples/webgl_loader_gltf.ts | 167 ++++ .../webgl_loader_gltf_animation_pointer.ts | 91 ++ .../examples/webgl_loader_gltf_anisotropy.ts | 68 ++ .../examples/webgl_loader_gltf_avif.ts | 61 ++ .../examples/webgl_loader_gltf_compressed.ts | 83 ++ .../examples/webgl_loader_gltf_dispersion.ts | 66 ++ .../examples/webgl_loader_gltf_instancing.ts | 69 ++ .../examples/webgl_loader_gltf_iridescence.ts | 66 ++ .../webgl_loader_gltf_progressive_lod.ts | 143 ++++ .../examples/webgl_loader_gltf_sheen.ts | 72 ++ .../webgl_loader_gltf_transmission.ts | 83 ++ .../examples/webgl_loader_imagebitmap.ts | 113 +++ examples-testing/examples/webgl_loader_kmz.ts | 59 ++ examples-testing/examples/webgl_loader_lwo.ts | 69 ++ .../examples/webgl_loader_md2_control.ts | 292 +++++++ examples-testing/examples/webgl_loader_mdd.ts | 65 ++ examples-testing/examples/webgl_loader_obj.ts | 72 ++ examples-testing/examples/webgl_loader_pcd.ts | 78 ++ examples-testing/examples/webgl_loader_pdb.ts | 208 +++++ examples-testing/examples/webgl_loader_ply.ts | 146 ++++ examples-testing/examples/webgl_loader_svg.ts | 206 +++++ .../examples/webgl_loader_texture_dds.ts | 218 +++++ .../examples/webgl_loader_texture_ktx.ts | 165 ++++ .../examples/webgl_loader_texture_tga.ts | 90 ++ .../examples/webgl_loader_texture_tiff.ts | 87 ++ .../examples/webgl_loader_texture_ultrahdr.ts | 101 +++ examples-testing/examples/webgl_loader_ttf.ts | 231 +++++ .../examples/webgl_loader_usdz.ts | 68 ++ examples-testing/examples/webgl_loader_vox.ts | 99 +++ .../examples/webgl_loader_vrml.ts | 119 +++ examples-testing/examples/webgl_loader_vtk.ts | 123 +++ examples-testing/examples/webgl_loader_xyz.ts | 65 ++ examples-testing/examples/webgl_lod.ts | 91 ++ .../examples/webgl_marchingcubes.ts | 314 +++++++ .../examples/webgl_materials_alphahash.ts | 178 ++++ .../examples/webgl_materials_blending.ts | 149 ++++ .../webgl_materials_blending_custom.ts | 214 +++++ .../examples/webgl_materials_bumpmap.ts | 134 +++ .../examples/webgl_materials_car.ts | 168 ++++ .../examples/webgl_materials_cubemap.ts | 115 +++ .../webgl_materials_cubemap_dynamic.ts | 115 +++ .../webgl_materials_cubemap_mipmaps.ts | 119 +++ .../webgl_materials_cubemap_refraction.ts | 126 +++ ...bgl_materials_cubemap_render_to_mipmaps.ts | 183 ++++ .../webgl_materials_displacementmap.ts | 224 +++++ .../examples/webgl_materials_envmaps.ts | 131 +++ .../examples/webgl_materials_envmaps_exr.ts | 153 ++++ .../webgl_materials_envmaps_fasthdr.ts | 169 ++++ ...webgl_materials_envmaps_groundprojected.ts | 151 ++++ .../examples/webgl_materials_envmaps_hdr.ts | 163 ++++ .../examples/webgl_materials_modified.ts | 115 +++ .../webgl_materials_normalmap_object_space.ts | 82 ++ .../webgl_materials_physical_clearcoat.ts | 208 +++++ .../webgl_materials_physical_transmission.ts | 192 +++++ ...l_materials_physical_transmission_alpha.ts | 194 +++++ .../webgl_materials_texture_anisotropy.ts | 143 ++++ .../webgl_materials_texture_canvas.ts | 92 ++ .../webgl_materials_texture_filters.ts | 165 ++++ .../webgl_materials_texture_manualmipmap.ts | 175 ++++ .../webgl_materials_texture_partialupdate.ts | 104 +++ .../webgl_materials_texture_rotation.ts | 112 +++ .../examples/webgl_materials_toon.ts | 152 ++++ .../examples/webgl_materials_video.ts | 208 +++++ .../examples/webgl_materials_video_webcam.ts | 79 ++ .../examples/webgl_materials_wireframe.ts | 107 +++ examples-testing/examples/webgl_math_obb.ts | 192 +++++ .../webgl_math_orientation_transform.ts | 120 +++ examples-testing/examples/webgl_mesh_batch.ts | 347 ++++++++ examples-testing/examples/webgl_mirror.ts | 193 +++++ .../examples/webgl_modifier_edgesplit.ts | 136 +++ .../examples/webgl_modifier_simplifier.ts | 77 ++ .../examples/webgl_modifier_tessellation.ts | 142 ++++ .../examples/webgl_morphtargets.ts | 116 +++ .../examples/webgl_morphtargets_face.ts | 108 +++ .../examples/webgl_morphtargets_horse.ts | 100 +++ .../examples/webgl_morphtargets_sphere.ts | 105 +++ .../examples/webgl_multiple_elements.ts | 139 +++ .../examples/webgl_multiple_rendertargets.ts | 133 +++ .../webgl_multiple_scenes_comparison.ts | 98 +++ .../examples/webgl_multiple_views.ts | 235 ++++++ .../webgl_multisampled_renderbuffers.ts | 133 +++ .../examples/webgl_panorama_cube.ts | 83 ++ .../webgl_panorama_equirectangular.ts | 112 +++ .../examples/webgl_performance.ts | 77 ++ .../examples/webgl_pmrem_cubemap.ts | 76 ++ .../examples/webgl_pmrem_equirectangular.ts | 76 ++ examples-testing/examples/webgl_pmrem_test.ts | 141 ++++ .../examples/webgl_points_billboards.ts | 120 +++ .../examples/webgl_points_sprites.ts | 167 ++++ .../examples/webgl_points_waves.ts | 145 ++++ examples-testing/examples/webgl_portal.ts | 218 +++++ .../examples/webgl_postprocessing.ts | 86 ++ .../examples/webgl_postprocessing_advanced.ts | 304 +++++++ .../webgl_postprocessing_afterimage.ts | 72 ++ .../webgl_postprocessing_backgrounds.ts | 214 +++++ .../examples/webgl_postprocessing_fxaa.ts | 122 +++ .../examples/webgl_postprocessing_glitch.ts | 108 +++ .../examples/webgl_postprocessing_godrays.ts | 176 ++++ .../examples/webgl_postprocessing_gtao.ts | 218 +++++ .../examples/webgl_postprocessing_masking.ts | 101 +++ .../examples/webgl_postprocessing_outline.ts | 282 +++++++ .../examples/webgl_postprocessing_pixel.ts | 231 +++++ .../webgl_postprocessing_procedural.ts | 77 ++ .../webgl_postprocessing_rgb_halftone.ts | 170 ++++ .../examples/webgl_postprocessing_sao.ts | 146 ++++ .../examples/webgl_postprocessing_smaa.ts | 106 +++ .../examples/webgl_postprocessing_sobel.ts | 111 +++ .../examples/webgl_postprocessing_ssaa.ts | 214 +++++ .../examples/webgl_postprocessing_ssao.ts | 125 +++ .../examples/webgl_postprocessing_ssr.ts | 262 ++++++ .../examples/webgl_postprocessing_taa.ts | 139 +++ .../webgl_postprocessing_transition.ts | 214 +++++ .../webgl_postprocessing_unreal_bloom.ts | 139 +++ ...l_postprocessing_unreal_bloom_selective.ts | 208 +++++ examples-testing/examples/webgl_random_uv.ts | 338 ++++++++ .../examples/webgl_raycaster_sprite.ts | 103 +++ .../examples/webgl_raycaster_texture.ts | 286 +++++++ .../examples/webgl_read_float_buffer.ts | 153 ++++ examples-testing/examples/webgl_refraction.ts | 138 +++ examples-testing/examples/webgl_rtt.ts | 171 ++++ examples-testing/examples/webgl_shader.ts | 50 ++ .../examples/webgl_shader_lava.ts | 104 +++ .../examples/webgl_shaders_ocean.ts | 195 +++++ .../examples/webgl_shaders_sky.ts | 114 +++ .../examples/webgl_shadow_contact.ts | 272 ++++++ examples-testing/examples/webgl_shadowmap.ts | 310 +++++++ .../examples/webgl_shadowmap_csm.ts | 253 ++++++ .../examples/webgl_shadowmap_pcss.ts | 165 ++++ .../examples/webgl_shadowmap_performance.ts | 282 +++++++ .../examples/webgl_shadowmap_pointlight.ts | 141 ++++ .../examples/webgl_shadowmap_progressive.ts | 204 +++++ .../examples/webgl_shadowmap_viewer.ts | 181 ++++ .../examples/webgl_shadowmap_vsm.ts | 203 +++++ examples-testing/examples/webgl_shadowmesh.ts | 253 ++++++ examples-testing/examples/webgl_simple_gi.ts | 172 ++++ examples-testing/examples/webgl_sprites.ts | 187 +++++ .../examples/webgl_test_memory.ts | 65 ++ .../examples/webgl_test_memory2.ts | 81 ++ .../examples/webgl_test_wide_gamut.ts | 130 +++ .../webgl_texture2darray_compressed.ts | 91 ++ .../webgl_texture2darray_layerupdate.ts | 131 +++ examples-testing/examples/webgl_texture3d.ts | 128 +++ .../examples/webgl_texture3d_partialupdate.ts | 326 +++++++ .../examples/webgl_tonemapping.ts | 172 ++++ .../examples/webgl_tsl_clearcoat.ts | 194 +++++ .../examples/webgl_tsl_instancing.ts | 286 +++++++ .../examples/webgl_tsl_shadowmap.ts | 160 ++++ .../examples/webgl_tsl_skinning.ts | 74 ++ examples-testing/examples/webgl_ubo.ts | 140 ++++ examples-testing/examples/webgl_ubo_arrays.ts | 174 ++++ .../examples/webgl_video_kinect.ts | 114 +++ .../webgl_video_panorama_equirectangular.ts | 95 +++ .../examples/webgl_volume_cloud.ts | 279 ++++++ .../examples/webgl_volume_instancing.ts | 222 +++++ .../examples/webgl_volume_perlin.ts | 205 +++++ examples-testing/examples/webgl_watch.ts | 243 ++++++ .../examples/webgpu_animation_retargeting.ts | 295 +++++++ ...ebgpu_animation_retargeting_readyplayer.ts | 165 ++++ examples-testing/examples/webgpu_backdrop.ts | 134 +++ .../examples/webgpu_backdrop_area.ts | 157 ++++ .../examples/webgpu_backdrop_water.ts | 247 ++++++ examples-testing/examples/webgpu_camera.ts | 211 +++++ .../examples/webgpu_camera_array.ts | 101 +++ .../webgpu_camera_logarithmicdepthbuffer.ts | 239 ++++++ examples-testing/examples/webgpu_caustics.ts | 202 +++++ .../examples/webgpu_centroid_sampling.ts | 199 +++++ examples-testing/examples/webgpu_clearcoat.ts | 194 +++++ examples-testing/examples/webgpu_clipping.ts | 203 +++++ .../examples/webgpu_compile_async.ts | 265 ++++++ .../examples/webgpu_compute_audio.ts | 178 ++++ .../examples/webgpu_compute_birds.ts | 424 ++++++++++ .../examples/webgpu_compute_cloth.ts | 487 +++++++++++ .../examples/webgpu_compute_geometry.ts | 190 +++++ .../examples/webgpu_compute_particles.ts | 225 +++++ .../examples/webgpu_compute_particles_rain.ts | 329 ++++++++ .../examples/webgpu_compute_particles_snow.ts | 328 ++++++++ .../examples/webgpu_compute_points.ts | 124 +++ .../examples/webgpu_compute_sort_bitonic.ts | 457 ++++++++++ .../examples/webgpu_compute_texture.ts | 96 +++ .../examples/webgpu_compute_texture_3d.ts | 193 +++++ .../webgpu_compute_texture_pingpong.ts | 170 ++++ .../examples/webgpu_cubemap_adjustments.ts | 165 ++++ .../examples/webgpu_cubemap_dynamic.ts | 130 +++ .../examples/webgpu_cubemap_mix.ts | 78 ++ .../examples/webgpu_custom_fog.ts | 135 +++ .../examples/webgpu_custom_fog_background.ts | 86 ++ .../examples/webgpu_custom_fog_scattering.ts | 161 ++++ .../examples/webgpu_display_stereo.ts | 191 +++++ .../examples/webgpu_equirectangular.ts | 57 ++ .../examples/webgpu_fog_height.ts | 99 +++ examples-testing/examples/webgpu_hdr.ts | 124 +++ .../examples/webgpu_instance_mesh.ts | 98 +++ .../examples/webgpu_instance_path.ts | 147 ++++ .../examples/webgpu_instance_points.ts | 203 +++++ .../examples/webgpu_instancing_morph.ts | 145 ++++ examples-testing/examples/webgpu_layers.ts | 144 ++++ .../examples/webgpu_lensflares.ts | 137 +++ .../examples/webgpu_lightprobe.ts | 136 +++ .../examples/webgpu_lightprobe_cubecamera.ts | 90 ++ .../examples/webgpu_lights_dynamic.ts | 242 ++++++ .../examples/webgpu_lights_ies_spotlight.ts | 168 ++++ .../examples/webgpu_lights_phong.ts | 136 +++ .../examples/webgpu_lights_physical.ts | 236 ++++++ .../examples/webgpu_lights_pointlights.ts | 189 +++++ .../examples/webgpu_lights_rectarealight.ts | 87 ++ .../examples/webgpu_lights_selective.ts | 150 ++++ .../examples/webgpu_lights_spotlight.ts | 201 +++++ .../examples/webgpu_lights_tiled.ts | 180 ++++ examples-testing/examples/webgpu_lines_fat.ts | 255 ++++++ .../examples/webgpu_lines_fat_raycasting.ts | 286 +++++++ .../examples/webgpu_lines_fat_wireframe.ts | 210 +++++ .../examples/webgpu_loader_gltf.ts | 183 ++++ .../examples/webgpu_loader_gltf_anisotropy.ts | 64 ++ .../examples/webgpu_loader_gltf_compressed.ts | 69 ++ .../examples/webgpu_loader_gltf_dispersion.ts | 63 ++ .../webgpu_loader_gltf_iridescence.ts | 70 ++ .../examples/webgpu_loader_gltf_sheen.ts | 81 ++ .../webgpu_loader_gltf_transmission.ts | 83 ++ .../examples/webgpu_loader_materialx.ts | 247 ++++++ .../examples/webgpu_loader_texture_ktx2.ts | 189 +++++ .../examples/webgpu_materials_alphahash.ts | 129 +++ .../examples/webgpu_materials_arrays.ts | 135 +++ .../examples/webgpu_materials_basic.ts | 129 +++ .../webgpu_materials_cubemap_mipmaps.ts | 119 +++ .../webgpu_materials_displacementmap.ts | 195 +++++ .../examples/webgpu_materials_envmaps.ts | 132 +++ .../webgpu_materials_envmaps_bpcem.ts | 202 +++++ .../examples/webgpu_materials_lightmap.ts | 105 +++ .../examples/webgpu_materials_matcap.ts | 194 +++++ .../examples/webgpu_materials_sss.ts | 153 ++++ .../webgpu_materials_texture_manualmipmap.ts | 170 ++++ .../examples/webgpu_materials_toon.ts | 149 ++++ .../examples/webgpu_materials_transmission.ts | 173 ++++ .../examples/webgpu_materials_video.ts | 185 ++++ .../examples/webgpu_materialx_noise.ts | 146 ++++ .../examples/webgpu_mesh_batch.ts | 265 ++++++ examples-testing/examples/webgpu_mirror.ts | 194 +++++ .../examples/webgpu_modifier_curve.ts | 154 ++++ .../examples/webgpu_morphtargets.ts | 119 +++ .../examples/webgpu_morphtargets_face.ts | 98 +++ examples-testing/examples/webgpu_mrt.ts | 123 +++ examples-testing/examples/webgpu_mrt_mask.ts | 132 +++ .../examples/webgpu_multiple_canvas.ts | 111 +++ .../examples/webgpu_multiple_elements.ts | 142 ++++ .../examples/webgpu_multiple_rendertargets.ts | 97 +++ .../webgpu_multiple_rendertargets_readback.ts | 161 ++++ .../webgpu_multisampled_renderbuffers.ts | 129 +++ examples-testing/examples/webgpu_occlusion.ts | 97 +++ examples-testing/examples/webgpu_ocean.ts | 181 ++++ .../examples/webgpu_parallax_uv.ts | 135 +++ examples-testing/examples/webgpu_particles.ts | 151 ++++ .../examples/webgpu_performance.ts | 101 +++ .../webgpu_performance_renderbundle.ts | 197 +++++ .../examples/webgpu_pmrem_cubemap.ts | 75 ++ .../examples/webgpu_pmrem_equirectangular.ts | 75 ++ .../examples/webgpu_pmrem_scene.ts | 106 +++ .../examples/webgpu_pmrem_test.ts | 138 +++ examples-testing/examples/webgpu_portal.ts | 156 ++++ .../examples/webgpu_postprocessing.ts | 81 ++ .../examples/webgpu_postprocessing_3dlut.ts | 228 +++++ .../webgpu_postprocessing_afterimage.ts | 151 ++++ .../webgpu_postprocessing_anamorphic.ts | 104 +++ .../examples/webgpu_postprocessing_ao.ts | 210 +++++ .../examples/webgpu_postprocessing_bloom.ts | 120 +++ .../webgpu_postprocessing_bloom_emissive.ts | 115 +++ .../webgpu_postprocessing_bloom_selective.ts | 124 +++ .../examples/webgpu_postprocessing_ca.ts | 266 ++++++ .../webgpu_postprocessing_difference.ts | 93 ++ .../examples/webgpu_postprocessing_dof.ts | 132 +++ .../webgpu_postprocessing_dof_basic.ts | 154 ++++ .../examples/webgpu_postprocessing_fxaa.ts | 129 +++ .../examples/webgpu_postprocessing_godrays.ts | 209 +++++ .../webgpu_postprocessing_lensflare.ts | 135 +++ .../examples/webgpu_postprocessing_masking.ts | 88 ++ .../webgpu_postprocessing_motion_blur.ts | 199 +++++ .../examples/webgpu_postprocessing_outline.ts | 232 +++++ .../examples/webgpu_postprocessing_pixel.ts | 237 ++++++ .../webgpu_postprocessing_radial_blur.ts | 149 ++++ .../examples/webgpu_postprocessing_retro.ts | 370 ++++++++ .../examples/webgpu_postprocessing_smaa.ts | 98 +++ .../examples/webgpu_postprocessing_sobel.ts | 94 +++ .../examples/webgpu_postprocessing_ssaa.ts | 170 ++++ .../examples/webgpu_postprocessing_ssgi.ts | 244 ++++++ .../webgpu_postprocessing_ssgi_ballpool.ts | 413 +++++++++ .../examples/webgpu_postprocessing_ssr.ts | 211 +++++ .../examples/webgpu_postprocessing_sss.ts | 185 ++++ .../examples/webgpu_postprocessing_traa.ts | 93 ++ .../webgpu_postprocessing_transition.ts | 207 +++++ .../examples/webgpu_procedural_texture.ts | 75 ++ .../examples/webgpu_reflection.ts | 345 ++++++++ .../examples/webgpu_reflection_roughness.ts | 121 +++ .../examples/webgpu_refraction.ts | 144 ++++ .../webgpu_rendertarget_2d-array_3d.ts | 237 ++++++ .../examples/webgpu_reversed_depth_buffer.ts | 169 ++++ examples-testing/examples/webgpu_rtt.ts | 90 ++ .../examples/webgpu_shadow_contact.ts | 205 +++++ examples-testing/examples/webgpu_shadowmap.ts | 166 ++++ .../examples/webgpu_shadowmap_array.ts | 330 ++++++++ .../examples/webgpu_shadowmap_csm.ts | 268 ++++++ .../examples/webgpu_shadowmap_opacity.ts | 117 +++ .../examples/webgpu_shadowmap_pointlight.ts | 142 ++++ .../examples/webgpu_shadowmap_progressive.ts | 203 +++++ .../examples/webgpu_shadowmap_vsm.ts | 197 +++++ examples-testing/examples/webgpu_skinning.ts | 72 ++ .../examples/webgpu_skinning_instancing.ts | 128 +++ examples-testing/examples/webgpu_sprites.ts | 102 +++ .../examples/webgpu_storage_buffer.ts | 169 ++++ .../examples/webgpu_struct_drawindirect.ts | 212 +++++ .../examples/webgpu_test_memory.ts | 223 +++++ .../examples/webgpu_texturegrad.ts | 116 +++ .../examples/webgpu_textures_2d-array.ts | 69 ++ .../webgpu_textures_2d-array_compressed.ts | 86 ++ .../examples/webgpu_textures_anisotropy.ts | 145 ++++ .../examples/webgpu_textures_partialupdate.ts | 105 +++ .../examples/webgpu_tonemapping.ts | 141 ++++ .../examples/webgpu_tsl_angular_slicing.ts | 175 ++++ examples-testing/examples/webgpu_tsl_earth.ts | 174 ++++ .../examples/webgpu_tsl_galaxy.ts | 99 +++ examples-testing/examples/webgpu_tsl_graph.ts | 228 +++++ .../examples/webgpu_tsl_halftone.ts | 214 +++++ .../examples/webgpu_tsl_interoperability.ts | 263 ++++++ .../examples/webgpu_tsl_procedural_terrain.ts | 318 +++++++ .../examples/webgpu_tsl_raging_sea.ts | 178 ++++ .../examples/webgpu_tsl_vfx_flames.ts | 203 +++++ .../webgpu_tsl_vfx_linkedparticles.ts | 445 ++++++++++ .../examples/webgpu_tsl_vfx_tornado.ts | 289 +++++++ examples-testing/examples/webgpu_tsl_wood.ts | 249 ++++++ .../examples/webgpu_video_panorama.ts | 99 +++ .../examples/webgpu_volume_caustics.ts | 304 +++++++ .../examples/webgpu_volume_cloud.ts | 165 ++++ .../examples/webgpu_volume_lighting.ts | 248 ++++++ .../webgpu_volume_lighting_rectarea.ts | 256 ++++++ .../examples/webgpu_volume_lighting_traa.ts | 298 +++++++ .../examples/webgpu_volume_perlin.ts | 114 +++ examples-testing/examples/webgpu_water.ts | 199 +++++ examples-testing/examples/webgpu_xr_cubes.ts | 216 +++++ .../examples/webgpu_xr_native_layers.ts | 638 ++++++++++++++ .../examples/webgpu_xr_rollercoaster.ts | 216 +++++ examples-testing/examples/webxr_ar_cones.ts | 66 ++ examples-testing/examples/webxr_ar_hittest.ts | 119 +++ .../examples/webxr_ar_lighting.ts | 124 +++ .../examples/webxr_ar_plane_detection.ts | 46 + .../examples/webxr_vr_handinput.ts | 126 +++ .../examples/webxr_vr_panorama.ts | 92 ++ .../examples/webxr_vr_panorama_depth.ts | 93 ++ .../examples/webxr_vr_rollercoaster.ts | 211 +++++ examples-testing/examples/webxr_vr_sandbox.ts | 192 +++++ examples-testing/examples/webxr_vr_video.ts | 92 ++ .../examples/webxr_xr_controls_transform.ts | 218 +++++ .../webxr_xr_dragging_custom_depth.ts | 395 +++++++++ 494 files changed, 82925 insertions(+) create mode 100644 examples-testing/examples/css2d_label.ts create mode 100644 examples-testing/examples/css3d_mixed.ts create mode 100644 examples-testing/examples/css3d_molecules.ts create mode 100644 examples-testing/examples/css3d_orthographic.ts create mode 100644 examples-testing/examples/css3d_periodictable.ts create mode 100644 examples-testing/examples/css3d_sandbox.ts create mode 100644 examples-testing/examples/css3d_sprites.ts create mode 100644 examples-testing/examples/css3d_youtube.ts create mode 100644 examples-testing/examples/games_fps.ts create mode 100644 examples-testing/examples/misc_animation_groups.ts create mode 100644 examples-testing/examples/misc_animation_keys.ts create mode 100644 examples-testing/examples/misc_boxselection.ts create mode 100644 examples-testing/examples/misc_controls_arcball.ts create mode 100644 examples-testing/examples/misc_controls_drag.ts create mode 100644 examples-testing/examples/misc_controls_fly.ts create mode 100644 examples-testing/examples/misc_controls_map.ts create mode 100644 examples-testing/examples/misc_controls_orbit.ts create mode 100644 examples-testing/examples/misc_controls_pointerlock.ts create mode 100644 examples-testing/examples/misc_controls_trackball.ts create mode 100644 examples-testing/examples/misc_controls_transform.ts create mode 100644 examples-testing/examples/misc_exporter_draco.ts create mode 100644 examples-testing/examples/misc_exporter_exr.ts create mode 100644 examples-testing/examples/misc_exporter_gltf.ts create mode 100644 examples-testing/examples/misc_exporter_ktx2.ts create mode 100644 examples-testing/examples/misc_exporter_obj.ts create mode 100644 examples-testing/examples/misc_exporter_ply.ts create mode 100644 examples-testing/examples/misc_exporter_stl.ts create mode 100644 examples-testing/examples/misc_exporter_usdz.ts create mode 100644 examples-testing/examples/misc_uv_tests.ts create mode 100644 examples-testing/examples/physics_ammo_instancing.ts create mode 100644 examples-testing/examples/physics_jolt_instancing.ts create mode 100644 examples-testing/examples/physics_rapier_basic.ts create mode 100644 examples-testing/examples/physics_rapier_character_controller.ts create mode 100644 examples-testing/examples/physics_rapier_instancing.ts create mode 100644 examples-testing/examples/physics_rapier_joints.ts create mode 100644 examples-testing/examples/physics_rapier_terrain.ts create mode 100644 examples-testing/examples/physics_rapier_vehicle_controller.ts create mode 100644 examples-testing/examples/svg_lines.ts create mode 100644 examples-testing/examples/svg_sandbox.ts create mode 100644 examples-testing/examples/webaudio_orientation.ts create mode 100644 examples-testing/examples/webaudio_sandbox.ts create mode 100644 examples-testing/examples/webaudio_timing.ts create mode 100644 examples-testing/examples/webaudio_visualizer.ts create mode 100644 examples-testing/examples/webgl_animation_keyframes.ts create mode 100644 examples-testing/examples/webgl_animation_multiple.ts create mode 100644 examples-testing/examples/webgl_animation_skinning_morph.ts create mode 100644 examples-testing/examples/webgl_animation_walk.ts create mode 100644 examples-testing/examples/webgl_buffergeometry.ts create mode 100644 examples-testing/examples/webgl_buffergeometry_attributes_integer.ts create mode 100644 examples-testing/examples/webgl_buffergeometry_attributes_none.ts create mode 100644 examples-testing/examples/webgl_buffergeometry_custom_attributes_particles.ts create mode 100644 examples-testing/examples/webgl_buffergeometry_drawrange.ts create mode 100644 examples-testing/examples/webgl_buffergeometry_glbufferattribute.ts create mode 100644 examples-testing/examples/webgl_buffergeometry_indexed.ts create mode 100644 examples-testing/examples/webgl_buffergeometry_instancing.ts create mode 100644 examples-testing/examples/webgl_buffergeometry_instancing_billboards.ts create mode 100644 examples-testing/examples/webgl_buffergeometry_instancing_interleaved.ts create mode 100644 examples-testing/examples/webgl_buffergeometry_lines.ts create mode 100644 examples-testing/examples/webgl_buffergeometry_lines_indexed.ts create mode 100644 examples-testing/examples/webgl_buffergeometry_points.ts create mode 100644 examples-testing/examples/webgl_buffergeometry_points_interleaved.ts create mode 100644 examples-testing/examples/webgl_buffergeometry_rawshader.ts create mode 100644 examples-testing/examples/webgl_buffergeometry_selective_draw.ts create mode 100644 examples-testing/examples/webgl_buffergeometry_uint.ts create mode 100644 examples-testing/examples/webgl_camera.ts create mode 100644 examples-testing/examples/webgl_camera_array.ts create mode 100644 examples-testing/examples/webgl_camera_logarithmicdepthbuffer.ts create mode 100644 examples-testing/examples/webgl_clipculldistance.ts create mode 100644 examples-testing/examples/webgl_clipping.ts create mode 100644 examples-testing/examples/webgl_clipping_advanced.ts create mode 100644 examples-testing/examples/webgl_clipping_intersection.ts create mode 100644 examples-testing/examples/webgl_clipping_stencil.ts create mode 100644 examples-testing/examples/webgl_custom_attributes.ts create mode 100644 examples-testing/examples/webgl_custom_attributes_lines.ts create mode 100644 examples-testing/examples/webgl_custom_attributes_points.ts create mode 100644 examples-testing/examples/webgl_custom_attributes_points2.ts create mode 100644 examples-testing/examples/webgl_custom_attributes_points3.ts create mode 100644 examples-testing/examples/webgl_decals.ts create mode 100644 examples-testing/examples/webgl_effects_anaglyph.ts create mode 100644 examples-testing/examples/webgl_effects_ascii.ts create mode 100644 examples-testing/examples/webgl_effects_parallaxbarrier.ts create mode 100644 examples-testing/examples/webgl_effects_stereo.ts create mode 100644 examples-testing/examples/webgl_framebuffer_texture.ts create mode 100644 examples-testing/examples/webgl_furnace_test.ts create mode 100644 examples-testing/examples/webgl_geometries.ts create mode 100644 examples-testing/examples/webgl_geometry_colors.ts create mode 100644 examples-testing/examples/webgl_geometry_colors_lookuptable.ts create mode 100644 examples-testing/examples/webgl_geometry_convex.ts create mode 100644 examples-testing/examples/webgl_geometry_cube.ts create mode 100644 examples-testing/examples/webgl_geometry_extrude_shapes.ts create mode 100644 examples-testing/examples/webgl_geometry_extrude_splines.ts create mode 100644 examples-testing/examples/webgl_geometry_minecraft.ts create mode 100644 examples-testing/examples/webgl_geometry_nurbs.ts create mode 100644 examples-testing/examples/webgl_geometry_shapes.ts create mode 100644 examples-testing/examples/webgl_geometry_teapot.ts create mode 100644 examples-testing/examples/webgl_geometry_terrain.ts create mode 100644 examples-testing/examples/webgl_geometry_terrain_raycast.ts create mode 100644 examples-testing/examples/webgl_geometry_text.ts create mode 100644 examples-testing/examples/webgl_geometry_text_shapes.ts create mode 100644 examples-testing/examples/webgl_geometry_text_stroke.ts create mode 100644 examples-testing/examples/webgl_gpgpu_birds.ts create mode 100644 examples-testing/examples/webgl_gpgpu_birds_gltf.ts create mode 100644 examples-testing/examples/webgl_gpgpu_protoplanet.ts create mode 100644 examples-testing/examples/webgl_gpgpu_water.ts create mode 100644 examples-testing/examples/webgl_helpers.ts create mode 100644 examples-testing/examples/webgl_instancing_dynamic.ts create mode 100644 examples-testing/examples/webgl_instancing_morph.ts create mode 100644 examples-testing/examples/webgl_instancing_performance.ts create mode 100644 examples-testing/examples/webgl_instancing_raycast.ts create mode 100644 examples-testing/examples/webgl_instancing_scatter.ts create mode 100644 examples-testing/examples/webgl_interactive_buffergeometry.ts create mode 100644 examples-testing/examples/webgl_interactive_cubes.ts create mode 100644 examples-testing/examples/webgl_interactive_cubes_gpu.ts create mode 100644 examples-testing/examples/webgl_interactive_cubes_ortho.ts create mode 100644 examples-testing/examples/webgl_interactive_lines.ts create mode 100644 examples-testing/examples/webgl_interactive_points.ts create mode 100644 examples-testing/examples/webgl_interactive_raycasting_points.ts create mode 100644 examples-testing/examples/webgl_interactive_voxelpainter.ts create mode 100644 examples-testing/examples/webgl_lensflares.ts create mode 100644 examples-testing/examples/webgl_lightprobe.ts create mode 100644 examples-testing/examples/webgl_lightprobe_cubecamera.ts create mode 100644 examples-testing/examples/webgl_lights_hemisphere.ts create mode 100644 examples-testing/examples/webgl_lights_physical.ts create mode 100644 examples-testing/examples/webgl_lights_rectarealight.ts create mode 100644 examples-testing/examples/webgl_lights_spotlight.ts create mode 100644 examples-testing/examples/webgl_lights_spotlights.ts create mode 100644 examples-testing/examples/webgl_lines_colors.ts create mode 100644 examples-testing/examples/webgl_lines_dashed.ts create mode 100644 examples-testing/examples/webgl_lines_fat.ts create mode 100644 examples-testing/examples/webgl_lines_fat_raycasting.ts create mode 100644 examples-testing/examples/webgl_lines_fat_wireframe.ts create mode 100644 examples-testing/examples/webgl_loader_3dm.ts create mode 100644 examples-testing/examples/webgl_loader_3ds.ts create mode 100644 examples-testing/examples/webgl_loader_3dtiles.ts create mode 100644 examples-testing/examples/webgl_loader_3mf.ts create mode 100644 examples-testing/examples/webgl_loader_3mf_materials.ts create mode 100644 examples-testing/examples/webgl_loader_amf.ts create mode 100644 examples-testing/examples/webgl_loader_bvh.ts create mode 100644 examples-testing/examples/webgl_loader_collada.ts create mode 100644 examples-testing/examples/webgl_loader_collada_skinning.ts create mode 100644 examples-testing/examples/webgl_loader_draco.ts create mode 100644 examples-testing/examples/webgl_loader_fbx.ts create mode 100644 examples-testing/examples/webgl_loader_fbx_nurbs.ts create mode 100644 examples-testing/examples/webgl_loader_gcode.ts create mode 100644 examples-testing/examples/webgl_loader_gltf.ts create mode 100644 examples-testing/examples/webgl_loader_gltf_animation_pointer.ts create mode 100644 examples-testing/examples/webgl_loader_gltf_anisotropy.ts create mode 100644 examples-testing/examples/webgl_loader_gltf_avif.ts create mode 100644 examples-testing/examples/webgl_loader_gltf_compressed.ts create mode 100644 examples-testing/examples/webgl_loader_gltf_dispersion.ts create mode 100644 examples-testing/examples/webgl_loader_gltf_instancing.ts create mode 100644 examples-testing/examples/webgl_loader_gltf_iridescence.ts create mode 100644 examples-testing/examples/webgl_loader_gltf_progressive_lod.ts create mode 100644 examples-testing/examples/webgl_loader_gltf_sheen.ts create mode 100644 examples-testing/examples/webgl_loader_gltf_transmission.ts create mode 100644 examples-testing/examples/webgl_loader_imagebitmap.ts create mode 100644 examples-testing/examples/webgl_loader_kmz.ts create mode 100644 examples-testing/examples/webgl_loader_lwo.ts create mode 100644 examples-testing/examples/webgl_loader_md2_control.ts create mode 100644 examples-testing/examples/webgl_loader_mdd.ts create mode 100644 examples-testing/examples/webgl_loader_obj.ts create mode 100644 examples-testing/examples/webgl_loader_pcd.ts create mode 100644 examples-testing/examples/webgl_loader_pdb.ts create mode 100644 examples-testing/examples/webgl_loader_ply.ts create mode 100644 examples-testing/examples/webgl_loader_svg.ts create mode 100644 examples-testing/examples/webgl_loader_texture_dds.ts create mode 100644 examples-testing/examples/webgl_loader_texture_ktx.ts create mode 100644 examples-testing/examples/webgl_loader_texture_tga.ts create mode 100644 examples-testing/examples/webgl_loader_texture_tiff.ts create mode 100644 examples-testing/examples/webgl_loader_texture_ultrahdr.ts create mode 100644 examples-testing/examples/webgl_loader_ttf.ts create mode 100644 examples-testing/examples/webgl_loader_usdz.ts create mode 100644 examples-testing/examples/webgl_loader_vox.ts create mode 100644 examples-testing/examples/webgl_loader_vrml.ts create mode 100644 examples-testing/examples/webgl_loader_vtk.ts create mode 100644 examples-testing/examples/webgl_loader_xyz.ts create mode 100644 examples-testing/examples/webgl_lod.ts create mode 100644 examples-testing/examples/webgl_marchingcubes.ts create mode 100644 examples-testing/examples/webgl_materials_alphahash.ts create mode 100644 examples-testing/examples/webgl_materials_blending.ts create mode 100644 examples-testing/examples/webgl_materials_blending_custom.ts create mode 100644 examples-testing/examples/webgl_materials_bumpmap.ts create mode 100644 examples-testing/examples/webgl_materials_car.ts create mode 100644 examples-testing/examples/webgl_materials_cubemap.ts create mode 100644 examples-testing/examples/webgl_materials_cubemap_dynamic.ts create mode 100644 examples-testing/examples/webgl_materials_cubemap_mipmaps.ts create mode 100644 examples-testing/examples/webgl_materials_cubemap_refraction.ts create mode 100644 examples-testing/examples/webgl_materials_cubemap_render_to_mipmaps.ts create mode 100644 examples-testing/examples/webgl_materials_displacementmap.ts create mode 100644 examples-testing/examples/webgl_materials_envmaps.ts create mode 100644 examples-testing/examples/webgl_materials_envmaps_exr.ts create mode 100644 examples-testing/examples/webgl_materials_envmaps_fasthdr.ts create mode 100644 examples-testing/examples/webgl_materials_envmaps_groundprojected.ts create mode 100644 examples-testing/examples/webgl_materials_envmaps_hdr.ts create mode 100644 examples-testing/examples/webgl_materials_modified.ts create mode 100644 examples-testing/examples/webgl_materials_normalmap_object_space.ts create mode 100644 examples-testing/examples/webgl_materials_physical_clearcoat.ts create mode 100644 examples-testing/examples/webgl_materials_physical_transmission.ts create mode 100644 examples-testing/examples/webgl_materials_physical_transmission_alpha.ts create mode 100644 examples-testing/examples/webgl_materials_texture_anisotropy.ts create mode 100644 examples-testing/examples/webgl_materials_texture_canvas.ts create mode 100644 examples-testing/examples/webgl_materials_texture_filters.ts create mode 100644 examples-testing/examples/webgl_materials_texture_manualmipmap.ts create mode 100644 examples-testing/examples/webgl_materials_texture_partialupdate.ts create mode 100644 examples-testing/examples/webgl_materials_texture_rotation.ts create mode 100644 examples-testing/examples/webgl_materials_toon.ts create mode 100644 examples-testing/examples/webgl_materials_video.ts create mode 100644 examples-testing/examples/webgl_materials_video_webcam.ts create mode 100644 examples-testing/examples/webgl_materials_wireframe.ts create mode 100644 examples-testing/examples/webgl_math_obb.ts create mode 100644 examples-testing/examples/webgl_math_orientation_transform.ts create mode 100644 examples-testing/examples/webgl_mesh_batch.ts create mode 100644 examples-testing/examples/webgl_mirror.ts create mode 100644 examples-testing/examples/webgl_modifier_edgesplit.ts create mode 100644 examples-testing/examples/webgl_modifier_simplifier.ts create mode 100644 examples-testing/examples/webgl_modifier_tessellation.ts create mode 100644 examples-testing/examples/webgl_morphtargets.ts create mode 100644 examples-testing/examples/webgl_morphtargets_face.ts create mode 100644 examples-testing/examples/webgl_morphtargets_horse.ts create mode 100644 examples-testing/examples/webgl_morphtargets_sphere.ts create mode 100644 examples-testing/examples/webgl_multiple_elements.ts create mode 100644 examples-testing/examples/webgl_multiple_rendertargets.ts create mode 100644 examples-testing/examples/webgl_multiple_scenes_comparison.ts create mode 100644 examples-testing/examples/webgl_multiple_views.ts create mode 100644 examples-testing/examples/webgl_multisampled_renderbuffers.ts create mode 100644 examples-testing/examples/webgl_panorama_cube.ts create mode 100644 examples-testing/examples/webgl_panorama_equirectangular.ts create mode 100644 examples-testing/examples/webgl_performance.ts create mode 100644 examples-testing/examples/webgl_pmrem_cubemap.ts create mode 100644 examples-testing/examples/webgl_pmrem_equirectangular.ts create mode 100644 examples-testing/examples/webgl_pmrem_test.ts create mode 100644 examples-testing/examples/webgl_points_billboards.ts create mode 100644 examples-testing/examples/webgl_points_sprites.ts create mode 100644 examples-testing/examples/webgl_points_waves.ts create mode 100644 examples-testing/examples/webgl_portal.ts create mode 100644 examples-testing/examples/webgl_postprocessing.ts create mode 100644 examples-testing/examples/webgl_postprocessing_advanced.ts create mode 100644 examples-testing/examples/webgl_postprocessing_afterimage.ts create mode 100644 examples-testing/examples/webgl_postprocessing_backgrounds.ts create mode 100644 examples-testing/examples/webgl_postprocessing_fxaa.ts create mode 100644 examples-testing/examples/webgl_postprocessing_glitch.ts create mode 100644 examples-testing/examples/webgl_postprocessing_godrays.ts create mode 100644 examples-testing/examples/webgl_postprocessing_gtao.ts create mode 100644 examples-testing/examples/webgl_postprocessing_masking.ts create mode 100644 examples-testing/examples/webgl_postprocessing_outline.ts create mode 100644 examples-testing/examples/webgl_postprocessing_pixel.ts create mode 100644 examples-testing/examples/webgl_postprocessing_procedural.ts create mode 100644 examples-testing/examples/webgl_postprocessing_rgb_halftone.ts create mode 100644 examples-testing/examples/webgl_postprocessing_sao.ts create mode 100644 examples-testing/examples/webgl_postprocessing_smaa.ts create mode 100644 examples-testing/examples/webgl_postprocessing_sobel.ts create mode 100644 examples-testing/examples/webgl_postprocessing_ssaa.ts create mode 100644 examples-testing/examples/webgl_postprocessing_ssao.ts create mode 100644 examples-testing/examples/webgl_postprocessing_ssr.ts create mode 100644 examples-testing/examples/webgl_postprocessing_taa.ts create mode 100644 examples-testing/examples/webgl_postprocessing_transition.ts create mode 100644 examples-testing/examples/webgl_postprocessing_unreal_bloom.ts create mode 100644 examples-testing/examples/webgl_postprocessing_unreal_bloom_selective.ts create mode 100644 examples-testing/examples/webgl_random_uv.ts create mode 100644 examples-testing/examples/webgl_raycaster_sprite.ts create mode 100644 examples-testing/examples/webgl_raycaster_texture.ts create mode 100644 examples-testing/examples/webgl_read_float_buffer.ts create mode 100644 examples-testing/examples/webgl_refraction.ts create mode 100644 examples-testing/examples/webgl_rtt.ts create mode 100644 examples-testing/examples/webgl_shader.ts create mode 100644 examples-testing/examples/webgl_shader_lava.ts create mode 100644 examples-testing/examples/webgl_shaders_ocean.ts create mode 100644 examples-testing/examples/webgl_shaders_sky.ts create mode 100644 examples-testing/examples/webgl_shadow_contact.ts create mode 100644 examples-testing/examples/webgl_shadowmap.ts create mode 100644 examples-testing/examples/webgl_shadowmap_csm.ts create mode 100644 examples-testing/examples/webgl_shadowmap_pcss.ts create mode 100644 examples-testing/examples/webgl_shadowmap_performance.ts create mode 100644 examples-testing/examples/webgl_shadowmap_pointlight.ts create mode 100644 examples-testing/examples/webgl_shadowmap_progressive.ts create mode 100644 examples-testing/examples/webgl_shadowmap_viewer.ts create mode 100644 examples-testing/examples/webgl_shadowmap_vsm.ts create mode 100644 examples-testing/examples/webgl_shadowmesh.ts create mode 100644 examples-testing/examples/webgl_simple_gi.ts create mode 100644 examples-testing/examples/webgl_sprites.ts create mode 100644 examples-testing/examples/webgl_test_memory.ts create mode 100644 examples-testing/examples/webgl_test_memory2.ts create mode 100644 examples-testing/examples/webgl_test_wide_gamut.ts create mode 100644 examples-testing/examples/webgl_texture2darray_compressed.ts create mode 100644 examples-testing/examples/webgl_texture2darray_layerupdate.ts create mode 100644 examples-testing/examples/webgl_texture3d.ts create mode 100644 examples-testing/examples/webgl_texture3d_partialupdate.ts create mode 100644 examples-testing/examples/webgl_tonemapping.ts create mode 100644 examples-testing/examples/webgl_tsl_clearcoat.ts create mode 100644 examples-testing/examples/webgl_tsl_instancing.ts create mode 100644 examples-testing/examples/webgl_tsl_shadowmap.ts create mode 100644 examples-testing/examples/webgl_tsl_skinning.ts create mode 100644 examples-testing/examples/webgl_ubo.ts create mode 100644 examples-testing/examples/webgl_ubo_arrays.ts create mode 100644 examples-testing/examples/webgl_video_kinect.ts create mode 100644 examples-testing/examples/webgl_video_panorama_equirectangular.ts create mode 100644 examples-testing/examples/webgl_volume_cloud.ts create mode 100644 examples-testing/examples/webgl_volume_instancing.ts create mode 100644 examples-testing/examples/webgl_volume_perlin.ts create mode 100644 examples-testing/examples/webgl_watch.ts create mode 100644 examples-testing/examples/webgpu_animation_retargeting.ts create mode 100644 examples-testing/examples/webgpu_animation_retargeting_readyplayer.ts create mode 100644 examples-testing/examples/webgpu_backdrop.ts create mode 100644 examples-testing/examples/webgpu_backdrop_area.ts create mode 100644 examples-testing/examples/webgpu_backdrop_water.ts create mode 100644 examples-testing/examples/webgpu_camera.ts create mode 100644 examples-testing/examples/webgpu_camera_array.ts create mode 100644 examples-testing/examples/webgpu_camera_logarithmicdepthbuffer.ts create mode 100644 examples-testing/examples/webgpu_caustics.ts create mode 100644 examples-testing/examples/webgpu_centroid_sampling.ts create mode 100644 examples-testing/examples/webgpu_clearcoat.ts create mode 100644 examples-testing/examples/webgpu_clipping.ts create mode 100644 examples-testing/examples/webgpu_compile_async.ts create mode 100644 examples-testing/examples/webgpu_compute_audio.ts create mode 100644 examples-testing/examples/webgpu_compute_birds.ts create mode 100644 examples-testing/examples/webgpu_compute_cloth.ts create mode 100644 examples-testing/examples/webgpu_compute_geometry.ts create mode 100644 examples-testing/examples/webgpu_compute_particles.ts create mode 100644 examples-testing/examples/webgpu_compute_particles_rain.ts create mode 100644 examples-testing/examples/webgpu_compute_particles_snow.ts create mode 100644 examples-testing/examples/webgpu_compute_points.ts create mode 100644 examples-testing/examples/webgpu_compute_sort_bitonic.ts create mode 100644 examples-testing/examples/webgpu_compute_texture.ts create mode 100644 examples-testing/examples/webgpu_compute_texture_3d.ts create mode 100644 examples-testing/examples/webgpu_compute_texture_pingpong.ts create mode 100644 examples-testing/examples/webgpu_cubemap_adjustments.ts create mode 100644 examples-testing/examples/webgpu_cubemap_dynamic.ts create mode 100644 examples-testing/examples/webgpu_cubemap_mix.ts create mode 100644 examples-testing/examples/webgpu_custom_fog.ts create mode 100644 examples-testing/examples/webgpu_custom_fog_background.ts create mode 100644 examples-testing/examples/webgpu_custom_fog_scattering.ts create mode 100644 examples-testing/examples/webgpu_display_stereo.ts create mode 100644 examples-testing/examples/webgpu_equirectangular.ts create mode 100644 examples-testing/examples/webgpu_fog_height.ts create mode 100644 examples-testing/examples/webgpu_hdr.ts create mode 100644 examples-testing/examples/webgpu_instance_mesh.ts create mode 100644 examples-testing/examples/webgpu_instance_path.ts create mode 100644 examples-testing/examples/webgpu_instance_points.ts create mode 100644 examples-testing/examples/webgpu_instancing_morph.ts create mode 100644 examples-testing/examples/webgpu_layers.ts create mode 100644 examples-testing/examples/webgpu_lensflares.ts create mode 100644 examples-testing/examples/webgpu_lightprobe.ts create mode 100644 examples-testing/examples/webgpu_lightprobe_cubecamera.ts create mode 100644 examples-testing/examples/webgpu_lights_dynamic.ts create mode 100644 examples-testing/examples/webgpu_lights_ies_spotlight.ts create mode 100644 examples-testing/examples/webgpu_lights_phong.ts create mode 100644 examples-testing/examples/webgpu_lights_physical.ts create mode 100644 examples-testing/examples/webgpu_lights_pointlights.ts create mode 100644 examples-testing/examples/webgpu_lights_rectarealight.ts create mode 100644 examples-testing/examples/webgpu_lights_selective.ts create mode 100644 examples-testing/examples/webgpu_lights_spotlight.ts create mode 100644 examples-testing/examples/webgpu_lights_tiled.ts create mode 100644 examples-testing/examples/webgpu_lines_fat.ts create mode 100644 examples-testing/examples/webgpu_lines_fat_raycasting.ts create mode 100644 examples-testing/examples/webgpu_lines_fat_wireframe.ts create mode 100644 examples-testing/examples/webgpu_loader_gltf.ts create mode 100644 examples-testing/examples/webgpu_loader_gltf_anisotropy.ts create mode 100644 examples-testing/examples/webgpu_loader_gltf_compressed.ts create mode 100644 examples-testing/examples/webgpu_loader_gltf_dispersion.ts create mode 100644 examples-testing/examples/webgpu_loader_gltf_iridescence.ts create mode 100644 examples-testing/examples/webgpu_loader_gltf_sheen.ts create mode 100644 examples-testing/examples/webgpu_loader_gltf_transmission.ts create mode 100644 examples-testing/examples/webgpu_loader_materialx.ts create mode 100644 examples-testing/examples/webgpu_loader_texture_ktx2.ts create mode 100644 examples-testing/examples/webgpu_materials_alphahash.ts create mode 100644 examples-testing/examples/webgpu_materials_arrays.ts create mode 100644 examples-testing/examples/webgpu_materials_basic.ts create mode 100644 examples-testing/examples/webgpu_materials_cubemap_mipmaps.ts create mode 100644 examples-testing/examples/webgpu_materials_displacementmap.ts create mode 100644 examples-testing/examples/webgpu_materials_envmaps.ts create mode 100644 examples-testing/examples/webgpu_materials_envmaps_bpcem.ts create mode 100644 examples-testing/examples/webgpu_materials_lightmap.ts create mode 100644 examples-testing/examples/webgpu_materials_matcap.ts create mode 100644 examples-testing/examples/webgpu_materials_sss.ts create mode 100644 examples-testing/examples/webgpu_materials_texture_manualmipmap.ts create mode 100644 examples-testing/examples/webgpu_materials_toon.ts create mode 100644 examples-testing/examples/webgpu_materials_transmission.ts create mode 100644 examples-testing/examples/webgpu_materials_video.ts create mode 100644 examples-testing/examples/webgpu_materialx_noise.ts create mode 100644 examples-testing/examples/webgpu_mesh_batch.ts create mode 100644 examples-testing/examples/webgpu_mirror.ts create mode 100644 examples-testing/examples/webgpu_modifier_curve.ts create mode 100644 examples-testing/examples/webgpu_morphtargets.ts create mode 100644 examples-testing/examples/webgpu_morphtargets_face.ts create mode 100644 examples-testing/examples/webgpu_mrt.ts create mode 100644 examples-testing/examples/webgpu_mrt_mask.ts create mode 100644 examples-testing/examples/webgpu_multiple_canvas.ts create mode 100644 examples-testing/examples/webgpu_multiple_elements.ts create mode 100644 examples-testing/examples/webgpu_multiple_rendertargets.ts create mode 100644 examples-testing/examples/webgpu_multiple_rendertargets_readback.ts create mode 100644 examples-testing/examples/webgpu_multisampled_renderbuffers.ts create mode 100644 examples-testing/examples/webgpu_occlusion.ts create mode 100644 examples-testing/examples/webgpu_ocean.ts create mode 100644 examples-testing/examples/webgpu_parallax_uv.ts create mode 100644 examples-testing/examples/webgpu_particles.ts create mode 100644 examples-testing/examples/webgpu_performance.ts create mode 100644 examples-testing/examples/webgpu_performance_renderbundle.ts create mode 100644 examples-testing/examples/webgpu_pmrem_cubemap.ts create mode 100644 examples-testing/examples/webgpu_pmrem_equirectangular.ts create mode 100644 examples-testing/examples/webgpu_pmrem_scene.ts create mode 100644 examples-testing/examples/webgpu_pmrem_test.ts create mode 100644 examples-testing/examples/webgpu_portal.ts create mode 100644 examples-testing/examples/webgpu_postprocessing.ts create mode 100644 examples-testing/examples/webgpu_postprocessing_3dlut.ts create mode 100644 examples-testing/examples/webgpu_postprocessing_afterimage.ts create mode 100644 examples-testing/examples/webgpu_postprocessing_anamorphic.ts create mode 100644 examples-testing/examples/webgpu_postprocessing_ao.ts create mode 100644 examples-testing/examples/webgpu_postprocessing_bloom.ts create mode 100644 examples-testing/examples/webgpu_postprocessing_bloom_emissive.ts create mode 100644 examples-testing/examples/webgpu_postprocessing_bloom_selective.ts create mode 100644 examples-testing/examples/webgpu_postprocessing_ca.ts create mode 100644 examples-testing/examples/webgpu_postprocessing_difference.ts create mode 100644 examples-testing/examples/webgpu_postprocessing_dof.ts create mode 100644 examples-testing/examples/webgpu_postprocessing_dof_basic.ts create mode 100644 examples-testing/examples/webgpu_postprocessing_fxaa.ts create mode 100644 examples-testing/examples/webgpu_postprocessing_godrays.ts create mode 100644 examples-testing/examples/webgpu_postprocessing_lensflare.ts create mode 100644 examples-testing/examples/webgpu_postprocessing_masking.ts create mode 100644 examples-testing/examples/webgpu_postprocessing_motion_blur.ts create mode 100644 examples-testing/examples/webgpu_postprocessing_outline.ts create mode 100644 examples-testing/examples/webgpu_postprocessing_pixel.ts create mode 100644 examples-testing/examples/webgpu_postprocessing_radial_blur.ts create mode 100644 examples-testing/examples/webgpu_postprocessing_retro.ts create mode 100644 examples-testing/examples/webgpu_postprocessing_smaa.ts create mode 100644 examples-testing/examples/webgpu_postprocessing_sobel.ts create mode 100644 examples-testing/examples/webgpu_postprocessing_ssaa.ts create mode 100644 examples-testing/examples/webgpu_postprocessing_ssgi.ts create mode 100644 examples-testing/examples/webgpu_postprocessing_ssgi_ballpool.ts create mode 100644 examples-testing/examples/webgpu_postprocessing_ssr.ts create mode 100644 examples-testing/examples/webgpu_postprocessing_sss.ts create mode 100644 examples-testing/examples/webgpu_postprocessing_traa.ts create mode 100644 examples-testing/examples/webgpu_postprocessing_transition.ts create mode 100644 examples-testing/examples/webgpu_procedural_texture.ts create mode 100644 examples-testing/examples/webgpu_reflection.ts create mode 100644 examples-testing/examples/webgpu_reflection_roughness.ts create mode 100644 examples-testing/examples/webgpu_refraction.ts create mode 100644 examples-testing/examples/webgpu_rendertarget_2d-array_3d.ts create mode 100644 examples-testing/examples/webgpu_reversed_depth_buffer.ts create mode 100644 examples-testing/examples/webgpu_rtt.ts create mode 100644 examples-testing/examples/webgpu_shadow_contact.ts create mode 100644 examples-testing/examples/webgpu_shadowmap.ts create mode 100644 examples-testing/examples/webgpu_shadowmap_array.ts create mode 100644 examples-testing/examples/webgpu_shadowmap_csm.ts create mode 100644 examples-testing/examples/webgpu_shadowmap_opacity.ts create mode 100644 examples-testing/examples/webgpu_shadowmap_pointlight.ts create mode 100644 examples-testing/examples/webgpu_shadowmap_progressive.ts create mode 100644 examples-testing/examples/webgpu_shadowmap_vsm.ts create mode 100644 examples-testing/examples/webgpu_skinning.ts create mode 100644 examples-testing/examples/webgpu_skinning_instancing.ts create mode 100644 examples-testing/examples/webgpu_sprites.ts create mode 100644 examples-testing/examples/webgpu_storage_buffer.ts create mode 100644 examples-testing/examples/webgpu_struct_drawindirect.ts create mode 100644 examples-testing/examples/webgpu_test_memory.ts create mode 100644 examples-testing/examples/webgpu_texturegrad.ts create mode 100644 examples-testing/examples/webgpu_textures_2d-array.ts create mode 100644 examples-testing/examples/webgpu_textures_2d-array_compressed.ts create mode 100644 examples-testing/examples/webgpu_textures_anisotropy.ts create mode 100644 examples-testing/examples/webgpu_textures_partialupdate.ts create mode 100644 examples-testing/examples/webgpu_tonemapping.ts create mode 100644 examples-testing/examples/webgpu_tsl_angular_slicing.ts create mode 100644 examples-testing/examples/webgpu_tsl_earth.ts create mode 100644 examples-testing/examples/webgpu_tsl_galaxy.ts create mode 100644 examples-testing/examples/webgpu_tsl_graph.ts create mode 100644 examples-testing/examples/webgpu_tsl_halftone.ts create mode 100644 examples-testing/examples/webgpu_tsl_interoperability.ts create mode 100644 examples-testing/examples/webgpu_tsl_procedural_terrain.ts create mode 100644 examples-testing/examples/webgpu_tsl_raging_sea.ts create mode 100644 examples-testing/examples/webgpu_tsl_vfx_flames.ts create mode 100644 examples-testing/examples/webgpu_tsl_vfx_linkedparticles.ts create mode 100644 examples-testing/examples/webgpu_tsl_vfx_tornado.ts create mode 100644 examples-testing/examples/webgpu_tsl_wood.ts create mode 100644 examples-testing/examples/webgpu_video_panorama.ts create mode 100644 examples-testing/examples/webgpu_volume_caustics.ts create mode 100644 examples-testing/examples/webgpu_volume_cloud.ts create mode 100644 examples-testing/examples/webgpu_volume_lighting.ts create mode 100644 examples-testing/examples/webgpu_volume_lighting_rectarea.ts create mode 100644 examples-testing/examples/webgpu_volume_lighting_traa.ts create mode 100644 examples-testing/examples/webgpu_volume_perlin.ts create mode 100644 examples-testing/examples/webgpu_water.ts create mode 100644 examples-testing/examples/webgpu_xr_cubes.ts create mode 100644 examples-testing/examples/webgpu_xr_native_layers.ts create mode 100644 examples-testing/examples/webgpu_xr_rollercoaster.ts create mode 100644 examples-testing/examples/webxr_ar_cones.ts create mode 100644 examples-testing/examples/webxr_ar_hittest.ts create mode 100644 examples-testing/examples/webxr_ar_lighting.ts create mode 100644 examples-testing/examples/webxr_ar_plane_detection.ts create mode 100644 examples-testing/examples/webxr_vr_handinput.ts create mode 100644 examples-testing/examples/webxr_vr_panorama.ts create mode 100644 examples-testing/examples/webxr_vr_panorama_depth.ts create mode 100644 examples-testing/examples/webxr_vr_rollercoaster.ts create mode 100644 examples-testing/examples/webxr_vr_sandbox.ts create mode 100644 examples-testing/examples/webxr_vr_video.ts create mode 100644 examples-testing/examples/webxr_xr_controls_transform.ts create mode 100644 examples-testing/examples/webxr_xr_dragging_custom_depth.ts diff --git a/examples-testing/examples/css2d_label.ts b/examples-testing/examples/css2d_label.ts new file mode 100644 index 000000000..1b2889980 --- /dev/null +++ b/examples-testing/examples/css2d_label.ts @@ -0,0 +1,189 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { CSS2DRenderer, CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let gui; + +let camera, scene, renderer, labelRenderer; + +const layers = { + 'Toggle Name': function () { + camera.layers.toggle(0); + }, + 'Toggle Mass': function () { + camera.layers.toggle(1); + }, + 'Enable All': function () { + camera.layers.enableAll(); + }, + + 'Disable All': function () { + camera.layers.disableAll(); + }, +}; + +const timer = new THREE.Timer(); +timer.connect(document); +const textureLoader = new THREE.TextureLoader(); + +let moon; + +init(); +animate(); + +function init() { + const EARTH_RADIUS = 1; + const MOON_RADIUS = 0.27; + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 200); + camera.position.set(10, 5, 20); + camera.layers.enableAll(); + + scene = new THREE.Scene(); + + const dirLight = new THREE.DirectionalLight(0xffffff, 3); + dirLight.position.set(0, 0, 1); + dirLight.layers.enableAll(); + scene.add(dirLight); + + const axesHelper = new THREE.AxesHelper(5); + axesHelper.layers.enableAll(); + scene.add(axesHelper); + + // + + const earthGeometry = new THREE.SphereGeometry(EARTH_RADIUS, 16, 16); + const earthMaterial = new THREE.MeshPhongMaterial({ + specular: 0x333333, + shininess: 5, + map: textureLoader.load('textures/planets/earth_atmos_2048.jpg'), + specularMap: textureLoader.load('textures/planets/earth_specular_2048.jpg'), + normalMap: textureLoader.load('textures/planets/earth_normal_2048.jpg'), + normalScale: new THREE.Vector2(0.85, 0.85), + }); + earthMaterial.map.colorSpace = THREE.SRGBColorSpace; + const earth = new THREE.Mesh(earthGeometry, earthMaterial); + scene.add(earth); + + const moonGeometry = new THREE.SphereGeometry(MOON_RADIUS, 16, 16); + const moonMaterial = new THREE.MeshPhongMaterial({ + shininess: 5, + map: textureLoader.load('textures/planets/moon_1024.jpg'), + }); + moonMaterial.map.colorSpace = THREE.SRGBColorSpace; + moon = new THREE.Mesh(moonGeometry, moonMaterial); + scene.add(moon); + + // + + earth.layers.enableAll(); + moon.layers.enableAll(); + + const earthDiv = document.createElement('div'); + earthDiv.className = 'label'; + earthDiv.textContent = 'Earth'; + earthDiv.style.backgroundColor = 'transparent'; + + const earthLabel = new CSS2DObject(earthDiv); + earthLabel.position.set(1.5 * EARTH_RADIUS, 0, 0); + earthLabel.center.set(0, 1); + earth.add(earthLabel); + earthLabel.layers.set(0); + + const earthMassDiv = document.createElement('div'); + earthMassDiv.className = 'label'; + earthMassDiv.textContent = '5.97237e24 kg'; + earthMassDiv.style.backgroundColor = 'transparent'; + + const earthMassLabel = new CSS2DObject(earthMassDiv); + earthMassLabel.position.set(1.5 * EARTH_RADIUS, 0, 0); + earthMassLabel.center.set(0, 0); + earth.add(earthMassLabel); + earthMassLabel.layers.set(1); + + const moonDiv = document.createElement('div'); + moonDiv.className = 'label'; + moonDiv.textContent = 'Moon'; + moonDiv.style.backgroundColor = 'transparent'; + + const moonLabel = new CSS2DObject(moonDiv); + moonLabel.position.set(1.5 * MOON_RADIUS, 0, 0); + moonLabel.center.set(0, 1); + moon.add(moonLabel); + moonLabel.layers.set(0); + + const moonMassDiv = document.createElement('div'); + moonMassDiv.className = 'label'; + moonMassDiv.textContent = '7.342e22 kg'; + moonMassDiv.style.backgroundColor = 'transparent'; + + const moonMassLabel = new CSS2DObject(moonMassDiv); + moonMassLabel.position.set(1.5 * MOON_RADIUS, 0, 0); + moonMassLabel.center.set(0, 0); + moon.add(moonMassLabel); + moonMassLabel.layers.set(1); + + // + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + document.body.appendChild(renderer.domElement); + + labelRenderer = new CSS2DRenderer(); + labelRenderer.setSize(window.innerWidth, window.innerHeight); + labelRenderer.domElement.style.position = 'absolute'; + labelRenderer.domElement.style.top = '0px'; + document.body.appendChild(labelRenderer.domElement); + + const controls = new OrbitControls(camera, labelRenderer.domElement); + controls.minDistance = 5; + controls.maxDistance = 100; + + // + + window.addEventListener('resize', onWindowResize); + + initGui(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + labelRenderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + requestAnimationFrame(animate); + + const elapsed = timer.getElapsed(); + + moon.position.set(Math.sin(elapsed) * 5, 0, Math.cos(elapsed) * 5); + + renderer.render(scene, camera); + labelRenderer.render(scene, camera); +} + +// + +function initGui() { + gui = new GUI(); + + gui.title('Camera Layers'); + + gui.add(layers, 'Toggle Name'); + gui.add(layers, 'Toggle Mass'); + gui.add(layers, 'Enable All'); + gui.add(layers, 'Disable All'); + + gui.open(); +} diff --git a/examples-testing/examples/css3d_mixed.ts b/examples-testing/examples/css3d_mixed.ts new file mode 100644 index 000000000..b526e73e5 --- /dev/null +++ b/examples-testing/examples/css3d_mixed.ts @@ -0,0 +1,138 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { CSS3DRenderer, CSS3DObject } from 'three/addons/renderers/CSS3DRenderer.js'; + +let camera, scene, rendererCSS3D, rendererWebGL; +let controls; + +init(); + +function init() { + const controlsDomElement = document.createElement('div'); + controlsDomElement.style.position = 'absolute'; + controlsDomElement.style.top = '0'; + controlsDomElement.style.width = '100%'; + controlsDomElement.style.height = '100%'; + document.body.appendChild(controlsDomElement); + + rendererCSS3D = new CSS3DRenderer(); + rendererCSS3D.setSize(window.innerWidth, window.innerHeight); + document.body.appendChild(rendererCSS3D.domElement); + + rendererWebGL = new THREE.WebGLRenderer({ antialias: true, alpha: true }); + rendererWebGL.domElement.style.position = 'absolute'; + rendererWebGL.domElement.style.top = '0'; + rendererWebGL.domElement.style.pointerEvents = 'none'; + rendererWebGL.setPixelRatio(window.devicePixelRatio); + rendererWebGL.setSize(window.innerWidth, window.innerHeight); + rendererWebGL.toneMapping = THREE.NeutralToneMapping; + rendererWebGL.setAnimationLoop(animate); + document.body.appendChild(rendererWebGL.domElement); + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000); + camera.position.set(-1000, 500, 1500); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xf0f0f0); + + // Add room + const roomGeometry = new THREE.EdgesGeometry(new THREE.BoxGeometry(4000, 2000, 4000, 10, 5, 10)); + const roomMaterial = new THREE.LineBasicMaterial({ color: 0x000000, opacity: 0.2, transparent: true }); + const room = new THREE.LineSegments(roomGeometry, roomMaterial); + scene.add(room); + + // Add light + const hemisphereLight = new THREE.HemisphereLight(0xffffff, 0x444444, 4); + hemisphereLight.position.set(-25, 100, 50); + scene.add(hemisphereLight); + + // Add cutout mesh + const geometry = new THREE.PlaneGeometry(1024, 768); + const material = new THREE.MeshBasicMaterial({ + color: 0xff0000, + blending: THREE.NoBlending, + opacity: 0, + premultipliedAlpha: true, + }); + const mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + // Add frame + const frame = buildFrame(1024, 768, 50); + scene.add(frame); + + // Add CSS3D element + const iframe = document.createElement('iframe'); + iframe.style.width = '1028px'; + iframe.style.height = '768px'; + iframe.style.border = '0px'; + iframe.style.backfaceVisibility = 'hidden'; + iframe.src = './#webgl_animation_keyframes'; + scene.add(new CSS3DObject(iframe)); + + // Add controls + controls = new OrbitControls(camera); + controls.connect(controlsDomElement); + controls.addEventListener('start', () => (iframe.style.pointerEvents = 'none')); + controls.addEventListener('end', () => (iframe.style.pointerEvents = 'auto')); + controls.enableDamping = true; + + window.addEventListener('resize', onWindowResize); +} + +function buildFrame(width, height, thickness) { + const group = new THREE.Group(); + const material = new THREE.MeshStandardMaterial({ color: 0x2200ff }); + + // Create the frame border + const outerShape = new THREE.Shape(); + outerShape.moveTo(-(width / 2 + thickness), -(height / 2 + thickness)); + outerShape.lineTo(width / 2 + thickness, -(height / 2 + thickness)); + outerShape.lineTo(width / 2 + thickness, height / 2 + thickness); + outerShape.lineTo(-(width / 2 + thickness), height / 2 + thickness); + outerShape.lineTo(-(width / 2 + thickness), -(height / 2 + thickness)); + + // Create inner rectangle (hole) + const innerHole = new THREE.Path(); + innerHole.moveTo(-width / 2, -height / 2); + innerHole.lineTo(width / 2, -height / 2); + innerHole.lineTo(width / 2, height / 2); + innerHole.lineTo(-width / 2, height / 2); + innerHole.lineTo(-width / 2, -height / 2); + + outerShape.holes.push(innerHole); + + const frameGeometry = new THREE.ExtrudeGeometry(outerShape, { + depth: thickness, + bevelEnabled: false, + }); + + const frameMesh = new THREE.Mesh(frameGeometry, material); + frameMesh.position.z = -thickness / 2; + group.add(frameMesh); + + // Add back plane + const backGeometry = new THREE.PlaneGeometry(width + thickness * 2, height + thickness * 2); + const backMesh = new THREE.Mesh(backGeometry, material); + backMesh.position.set(0, 0, -thickness / 2); + backMesh.rotation.y = Math.PI; + group.add(backMesh); + + return group; +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + rendererWebGL.setSize(window.innerWidth, window.innerHeight); + rendererCSS3D.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + controls.update(); + + rendererWebGL.render(scene, camera); + rendererCSS3D.render(scene, camera); +} diff --git a/examples-testing/examples/css3d_molecules.ts b/examples-testing/examples/css3d_molecules.ts new file mode 100644 index 000000000..538472607 --- /dev/null +++ b/examples-testing/examples/css3d_molecules.ts @@ -0,0 +1,353 @@ +import * as THREE from 'three'; + +import { TrackballControls } from 'three/addons/controls/TrackballControls.js'; +import { PDBLoader } from 'three/addons/loaders/PDBLoader.js'; +import { CSS3DRenderer, CSS3DObject, CSS3DSprite } from 'three/addons/renderers/CSS3DRenderer.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let camera, scene, renderer; +let controls; +let root; + +const objects = []; +const tmpVec1 = new THREE.Vector3(); +const tmpVec2 = new THREE.Vector3(); +const tmpVec3 = new THREE.Vector3(); +const tmpVec4 = new THREE.Vector3(); +const offset = new THREE.Vector3(); + +const VIZ_TYPE = { + Atoms: 0, + Bonds: 1, + 'Atoms + Bonds': 2, +}; + +const MOLECULES = { + Ethanol: 'ethanol.pdb', + Aspirin: 'aspirin.pdb', + Caffeine: 'caffeine.pdb', + Nicotine: 'nicotine.pdb', + LSD: 'lsd.pdb', + Cocaine: 'cocaine.pdb', + Cholesterol: 'cholesterol.pdb', + Lycopene: 'lycopene.pdb', + Glucose: 'glucose.pdb', + 'Aluminium oxide': 'Al2O3.pdb', + Cubane: 'cubane.pdb', + Copper: 'cu.pdb', + Fluorite: 'caf2.pdb', + Salt: 'nacl.pdb', + 'YBCO superconductor': 'ybco.pdb', + Buckyball: 'buckyball.pdb', + // 'Diamond': 'diamond.pdb', + Graphite: 'graphite.pdb', +}; + +const params = { + vizType: 2, + molecule: 'caffeine.pdb', +}; + +const loader = new PDBLoader(); +const colorSpriteMap = {}; +const baseSprite = document.createElement('img'); + +init(); +animate(); + +function init() { + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 5000); + camera.position.z = 1000; + + scene = new THREE.Scene(); + + root = new THREE.Object3D(); + scene.add(root); + + // + + renderer = new CSS3DRenderer(); + renderer.setSize(window.innerWidth, window.innerHeight); + document.getElementById('container').appendChild(renderer.domElement); + + // + + controls = new TrackballControls(camera, renderer.domElement); + controls.rotateSpeed = 0.5; + + // + + baseSprite.onload = function () { + loadMolecule(params.molecule); + }; + + baseSprite.src = 'textures/sprites/ball.png'; + + // + + window.addEventListener('resize', onWindowResize); + + // + + const gui = new GUI(); + + gui.add(params, 'vizType', VIZ_TYPE).onChange(changeVizType); + gui.add(params, 'molecule', MOLECULES).onChange(loadMolecule); + gui.open(); +} + +function changeVizType(value) { + if (value === 0) showAtoms(); + else if (value === 1) showBonds(); + else showAtomsBonds(); +} + +// + +function showAtoms() { + for (let i = 0; i < objects.length; i++) { + const object = objects[i]; + + if (object instanceof CSS3DSprite) { + object.element.style.display = ''; + object.visible = true; + } else { + object.element.style.display = 'none'; + object.visible = false; + } + } +} + +function showBonds() { + for (let i = 0; i < objects.length; i++) { + const object = objects[i]; + + if (object instanceof CSS3DSprite) { + object.element.style.display = 'none'; + object.visible = false; + } else { + object.element.style.display = ''; + object.element.style.height = object.userData.bondLengthFull; + object.visible = true; + } + } +} + +function showAtomsBonds() { + for (let i = 0; i < objects.length; i++) { + const object = objects[i]; + + object.element.style.display = ''; + object.visible = true; + + if (!(object instanceof CSS3DSprite)) { + object.element.style.height = object.userData.bondLengthShort; + } + } +} + +// + +function colorify(ctx, width, height, color) { + const r = color.r, + g = color.g, + b = color.b; + + const imageData = ctx.getImageData(0, 0, width, height); + const data = imageData.data; + + for (let i = 0, l = data.length; i < l; i += 4) { + data[i + 0] *= r; + data[i + 1] *= g; + data[i + 2] *= b; + } + + ctx.putImageData(imageData, 0, 0); +} + +function imageToCanvas(image) { + const width = image.width; + const height = image.height; + + const canvas = document.createElement('canvas'); + + canvas.width = width; + canvas.height = height; + + const context = canvas.getContext('2d'); + context.drawImage(image, 0, 0, width, height); + + return canvas; +} + +// + +function loadMolecule(model) { + const url = 'models/pdb/' + model; + + for (let i = 0; i < objects.length; i++) { + const object = objects[i]; + object.parent.remove(object); + } + + objects.length = 0; + + loader.load(url, function (pdb) { + const geometryAtoms = pdb.geometryAtoms; + const geometryBonds = pdb.geometryBonds; + const json = pdb.json; + + geometryAtoms.computeBoundingBox(); + geometryAtoms.boundingBox.getCenter(offset).negate(); + + geometryAtoms.translate(offset.x, offset.y, offset.z); + geometryBonds.translate(offset.x, offset.y, offset.z); + + const positionAtoms = geometryAtoms.getAttribute('position'); + const colorAtoms = geometryAtoms.getAttribute('color'); + + const position = new THREE.Vector3(); + const color = new THREE.Color(); + + for (let i = 0; i < positionAtoms.count; i++) { + position.fromBufferAttribute(positionAtoms, i); + color.fromBufferAttribute(colorAtoms, i); + + const atomJSON = json.atoms[i]; + const element = atomJSON[4]; + + if (!colorSpriteMap[element]) { + const canvas = imageToCanvas(baseSprite); + const context = canvas.getContext('2d'); + + colorify(context, canvas.width, canvas.height, color); + + const dataUrl = canvas.toDataURL(); + + colorSpriteMap[element] = dataUrl; + } + + const colorSprite = colorSpriteMap[element]; + + const atom = document.createElement('img'); + atom.src = colorSprite; + + const object = new CSS3DSprite(atom); + object.position.copy(position); + object.position.multiplyScalar(75); + + object.matrixAutoUpdate = false; + object.updateMatrix(); + + root.add(object); + + objects.push(object); + } + + const positionBonds = geometryBonds.getAttribute('position'); + + const start = new THREE.Vector3(); + const end = new THREE.Vector3(); + + for (let i = 0; i < positionBonds.count; i += 2) { + start.fromBufferAttribute(positionBonds, i); + end.fromBufferAttribute(positionBonds, i + 1); + + start.multiplyScalar(75); + end.multiplyScalar(75); + + tmpVec1.subVectors(end, start); + const bondLength = tmpVec1.length() - 50; + + // + + let bond = document.createElement('div'); + bond.className = 'bond'; + bond.style.height = bondLength + 'px'; + + let object = new CSS3DObject(bond); + object.position.copy(start); + object.position.lerp(end, 0.5); + + object.userData.bondLengthShort = bondLength + 'px'; + object.userData.bondLengthFull = bondLength + 55 + 'px'; + + // + + const axis = tmpVec2.set(0, 1, 0).cross(tmpVec1); + const radians = Math.acos(tmpVec3.set(0, 1, 0).dot(tmpVec4.copy(tmpVec1).normalize())); + + const objMatrix = new THREE.Matrix4().makeRotationAxis(axis.normalize(), radians); + object.matrix.copy(objMatrix); + object.quaternion.setFromRotationMatrix(object.matrix); + + object.matrixAutoUpdate = false; + object.updateMatrix(); + + root.add(object); + + objects.push(object); + + // + + const joint = new THREE.Object3D(); + joint.position.copy(start); + joint.position.lerp(end, 0.5); + + joint.matrix.copy(objMatrix); + joint.quaternion.setFromRotationMatrix(joint.matrix); + + joint.matrixAutoUpdate = false; + joint.updateMatrix(); + + bond = document.createElement('div'); + bond.className = 'bond'; + bond.style.height = bondLength + 'px'; + + object = new CSS3DObject(bond); + object.rotation.y = Math.PI / 2; + + object.matrixAutoUpdate = false; + object.updateMatrix(); + + object.userData.bondLengthShort = bondLength + 'px'; + object.userData.bondLengthFull = bondLength + 55 + 'px'; + + object.userData.joint = joint; + + joint.add(object); + root.add(joint); + + objects.push(object); + } + + //console.log( "CSS3DObjects:", objects.length ); + + changeVizType(params.vizType); + }); +} + +// + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + requestAnimationFrame(animate); + controls.update(); + + const time = Date.now() * 0.0004; + + root.rotation.x = time; + root.rotation.y = time * 0.7; + + render(); +} + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/css3d_orthographic.ts b/examples-testing/examples/css3d_orthographic.ts new file mode 100644 index 000000000..4aabbed08 --- /dev/null +++ b/examples-testing/examples/css3d_orthographic.ts @@ -0,0 +1,208 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { CSS3DRenderer, CSS3DObject } from 'three/addons/renderers/CSS3DRenderer.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let camera, scene, renderer; + +let scene2, renderer2; + +const frustumSize = 500; + +init(); +animate(); + +function init() { + const aspect = window.innerWidth / window.innerHeight; + camera = new THREE.OrthographicCamera( + (frustumSize * aspect) / -2, + (frustumSize * aspect) / 2, + frustumSize / 2, + frustumSize / -2, + 1, + 1000, + ); + + camera.position.set(-200, 200, 200); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xf0f0f0); + + scene2 = new THREE.Scene(); + + const material = new THREE.MeshBasicMaterial({ + color: 0x000000, + wireframe: true, + wireframeLinewidth: 1, + side: THREE.DoubleSide, + }); + + // left + createPlane( + 100, + 100, + 'chocolate', + new THREE.Vector3(-50, 0, 0), + new THREE.Euler(0, -90 * THREE.MathUtils.DEG2RAD, 0), + ); + // right + createPlane(100, 100, 'saddlebrown', new THREE.Vector3(0, 0, 50), new THREE.Euler(0, 0, 0)); + // top + createPlane( + 100, + 100, + 'yellowgreen', + new THREE.Vector3(0, 50, 0), + new THREE.Euler(-90 * THREE.MathUtils.DEG2RAD, 0, 0), + ); + // bottom + createPlane( + 300, + 300, + 'seagreen', + new THREE.Vector3(0, -50, 0), + new THREE.Euler(-90 * THREE.MathUtils.DEG2RAD, 0, 0), + ); + + // + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + document.body.appendChild(renderer.domElement); + + renderer2 = new CSS3DRenderer(); + renderer2.setSize(window.innerWidth, window.innerHeight); + renderer2.domElement.style.position = 'absolute'; + renderer2.domElement.style.top = 0; + document.body.appendChild(renderer2.domElement); + + const controls = new OrbitControls(camera, renderer2.domElement); + controls.minZoom = 0.5; + controls.maxZoom = 2; + + function createPlane(width, height, cssColor, pos, rot) { + const element = document.createElement('div'); + element.style.width = width + 'px'; + element.style.height = height + 'px'; + element.style.opacity = 0.75; + element.style.background = cssColor; + + const object = new CSS3DObject(element); + object.position.copy(pos); + object.rotation.copy(rot); + scene2.add(object); + + const geometry = new THREE.PlaneGeometry(width, height); + const mesh = new THREE.Mesh(geometry, material); + mesh.position.copy(object.position); + mesh.rotation.copy(object.rotation); + scene.add(mesh); + } + + window.addEventListener('resize', onWindowResize); + createPanel(); +} + +function onWindowResize() { + const aspect = window.innerWidth / window.innerHeight; + + camera.left = (-frustumSize * aspect) / 2; + camera.right = (frustumSize * aspect) / 2; + camera.top = frustumSize / 2; + camera.bottom = -frustumSize / 2; + + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + renderer2.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + requestAnimationFrame(animate); + + renderer.render(scene, camera); + renderer2.render(scene2, camera); +} + +function createPanel() { + const panel = new GUI(); + const folder1 = panel.addFolder('camera setViewOffset').close(); + + const settings = { + setViewOffset() { + folder1.children[1].enable().setValue(window.innerWidth); + folder1.children[2].enable().setValue(window.innerHeight); + folder1.children[3].enable().setValue(0); + folder1.children[4].enable().setValue(0); + folder1.children[5].enable().setValue(window.innerWidth); + folder1.children[6].enable().setValue(window.innerHeight); + }, + fullWidth: 0, + fullHeight: 0, + offsetX: 0, + offsetY: 0, + width: 0, + height: 0, + clearViewOffset() { + folder1.children[1].setValue(0).disable(); + folder1.children[2].setValue(0).disable(); + folder1.children[3].setValue(0).disable(); + folder1.children[4].setValue(0).disable(); + folder1.children[5].setValue(0).disable(); + folder1.children[6].setValue(0).disable(); + camera.clearViewOffset(); + }, + }; + + folder1.add(settings, 'setViewOffset'); + folder1 + .add(settings, 'fullWidth', window.screen.width / 4, window.screen.width * 2, 1) + .onChange(val => updateCameraViewOffset({ fullWidth: val })) + .disable(); + folder1 + .add(settings, 'fullHeight', window.screen.height / 4, window.screen.height * 2, 1) + .onChange(val => updateCameraViewOffset({ fullHeight: val })) + .disable(); + folder1 + .add(settings, 'offsetX', 0, 256, 1) + .onChange(val => updateCameraViewOffset({ x: val })) + .disable(); + folder1 + .add(settings, 'offsetY', 0, 256, 1) + .onChange(val => updateCameraViewOffset({ y: val })) + .disable(); + folder1 + .add(settings, 'width', window.screen.width / 4, window.screen.width * 2, 1) + .onChange(val => updateCameraViewOffset({ width: val })) + .disable(); + folder1 + .add(settings, 'height', window.screen.height / 4, window.screen.height * 2, 1) + .onChange(val => updateCameraViewOffset({ height: val })) + .disable(); + folder1.add(settings, 'clearViewOffset'); +} + +function updateCameraViewOffset({ fullWidth, fullHeight, x, y, width, height }) { + if (!camera.view) { + camera.setViewOffset( + fullWidth || window.innerWidth, + fullHeight || window.innerHeight, + x || 0, + y || 0, + width || window.innerWidth, + height || window.innerHeight, + ); + } else { + camera.setViewOffset( + fullWidth || camera.view.fullWidth, + fullHeight || camera.view.fullHeight, + x || camera.view.offsetX, + y || camera.view.offsetY, + width || camera.view.width, + height || camera.view.height, + ); + } +} diff --git a/examples-testing/examples/css3d_periodictable.ts b/examples-testing/examples/css3d_periodictable.ts new file mode 100644 index 000000000..e3a33f796 --- /dev/null +++ b/examples-testing/examples/css3d_periodictable.ts @@ -0,0 +1,793 @@ +import * as THREE from 'three'; + +import TWEEN from 'three/addons/libs/tween.module.js'; +import { TrackballControls } from 'three/addons/controls/TrackballControls.js'; +import { CSS3DRenderer, CSS3DObject } from 'three/addons/renderers/CSS3DRenderer.js'; + +const table = [ + 'H', + 'Hydrogen', + '1.00794', + 1, + 1, + 'He', + 'Helium', + '4.002602', + 18, + 1, + 'Li', + 'Lithium', + '6.941', + 1, + 2, + 'Be', + 'Beryllium', + '9.012182', + 2, + 2, + 'B', + 'Boron', + '10.811', + 13, + 2, + 'C', + 'Carbon', + '12.0107', + 14, + 2, + 'N', + 'Nitrogen', + '14.0067', + 15, + 2, + 'O', + 'Oxygen', + '15.9994', + 16, + 2, + 'F', + 'Fluorine', + '18.9984032', + 17, + 2, + 'Ne', + 'Neon', + '20.1797', + 18, + 2, + 'Na', + 'Sodium', + '22.98976...', + 1, + 3, + 'Mg', + 'Magnesium', + '24.305', + 2, + 3, + 'Al', + 'Aluminium', + '26.9815386', + 13, + 3, + 'Si', + 'Silicon', + '28.0855', + 14, + 3, + 'P', + 'Phosphorus', + '30.973762', + 15, + 3, + 'S', + 'Sulfur', + '32.065', + 16, + 3, + 'Cl', + 'Chlorine', + '35.453', + 17, + 3, + 'Ar', + 'Argon', + '39.948', + 18, + 3, + 'K', + 'Potassium', + '39.948', + 1, + 4, + 'Ca', + 'Calcium', + '40.078', + 2, + 4, + 'Sc', + 'Scandium', + '44.955912', + 3, + 4, + 'Ti', + 'Titanium', + '47.867', + 4, + 4, + 'V', + 'Vanadium', + '50.9415', + 5, + 4, + 'Cr', + 'Chromium', + '51.9961', + 6, + 4, + 'Mn', + 'Manganese', + '54.938045', + 7, + 4, + 'Fe', + 'Iron', + '55.845', + 8, + 4, + 'Co', + 'Cobalt', + '58.933195', + 9, + 4, + 'Ni', + 'Nickel', + '58.6934', + 10, + 4, + 'Cu', + 'Copper', + '63.546', + 11, + 4, + 'Zn', + 'Zinc', + '65.38', + 12, + 4, + 'Ga', + 'Gallium', + '69.723', + 13, + 4, + 'Ge', + 'Germanium', + '72.63', + 14, + 4, + 'As', + 'Arsenic', + '74.9216', + 15, + 4, + 'Se', + 'Selenium', + '78.96', + 16, + 4, + 'Br', + 'Bromine', + '79.904', + 17, + 4, + 'Kr', + 'Krypton', + '83.798', + 18, + 4, + 'Rb', + 'Rubidium', + '85.4678', + 1, + 5, + 'Sr', + 'Strontium', + '87.62', + 2, + 5, + 'Y', + 'Yttrium', + '88.90585', + 3, + 5, + 'Zr', + 'Zirconium', + '91.224', + 4, + 5, + 'Nb', + 'Niobium', + '92.90628', + 5, + 5, + 'Mo', + 'Molybdenum', + '95.96', + 6, + 5, + 'Tc', + 'Technetium', + '(98)', + 7, + 5, + 'Ru', + 'Ruthenium', + '101.07', + 8, + 5, + 'Rh', + 'Rhodium', + '102.9055', + 9, + 5, + 'Pd', + 'Palladium', + '106.42', + 10, + 5, + 'Ag', + 'Silver', + '107.8682', + 11, + 5, + 'Cd', + 'Cadmium', + '112.411', + 12, + 5, + 'In', + 'Indium', + '114.818', + 13, + 5, + 'Sn', + 'Tin', + '118.71', + 14, + 5, + 'Sb', + 'Antimony', + '121.76', + 15, + 5, + 'Te', + 'Tellurium', + '127.6', + 16, + 5, + 'I', + 'Iodine', + '126.90447', + 17, + 5, + 'Xe', + 'Xenon', + '131.293', + 18, + 5, + 'Cs', + 'Caesium', + '132.9054', + 1, + 6, + 'Ba', + 'Barium', + '132.9054', + 2, + 6, + 'La', + 'Lanthanum', + '138.90547', + 4, + 9, + 'Ce', + 'Cerium', + '140.116', + 5, + 9, + 'Pr', + 'Praseodymium', + '140.90765', + 6, + 9, + 'Nd', + 'Neodymium', + '144.242', + 7, + 9, + 'Pm', + 'Promethium', + '(145)', + 8, + 9, + 'Sm', + 'Samarium', + '150.36', + 9, + 9, + 'Eu', + 'Europium', + '151.964', + 10, + 9, + 'Gd', + 'Gadolinium', + '157.25', + 11, + 9, + 'Tb', + 'Terbium', + '158.92535', + 12, + 9, + 'Dy', + 'Dysprosium', + '162.5', + 13, + 9, + 'Ho', + 'Holmium', + '164.93032', + 14, + 9, + 'Er', + 'Erbium', + '167.259', + 15, + 9, + 'Tm', + 'Thulium', + '168.93421', + 16, + 9, + 'Yb', + 'Ytterbium', + '173.054', + 17, + 9, + 'Lu', + 'Lutetium', + '174.9668', + 18, + 9, + 'Hf', + 'Hafnium', + '178.49', + 4, + 6, + 'Ta', + 'Tantalum', + '180.94788', + 5, + 6, + 'W', + 'Tungsten', + '183.84', + 6, + 6, + 'Re', + 'Rhenium', + '186.207', + 7, + 6, + 'Os', + 'Osmium', + '190.23', + 8, + 6, + 'Ir', + 'Iridium', + '192.217', + 9, + 6, + 'Pt', + 'Platinum', + '195.084', + 10, + 6, + 'Au', + 'Gold', + '196.966569', + 11, + 6, + 'Hg', + 'Mercury', + '200.59', + 12, + 6, + 'Tl', + 'Thallium', + '204.3833', + 13, + 6, + 'Pb', + 'Lead', + '207.2', + 14, + 6, + 'Bi', + 'Bismuth', + '208.9804', + 15, + 6, + 'Po', + 'Polonium', + '(209)', + 16, + 6, + 'At', + 'Astatine', + '(210)', + 17, + 6, + 'Rn', + 'Radon', + '(222)', + 18, + 6, + 'Fr', + 'Francium', + '(223)', + 1, + 7, + 'Ra', + 'Radium', + '(226)', + 2, + 7, + 'Ac', + 'Actinium', + '(227)', + 4, + 10, + 'Th', + 'Thorium', + '232.03806', + 5, + 10, + 'Pa', + 'Protactinium', + '231.0588', + 6, + 10, + 'U', + 'Uranium', + '238.02891', + 7, + 10, + 'Np', + 'Neptunium', + '(237)', + 8, + 10, + 'Pu', + 'Plutonium', + '(244)', + 9, + 10, + 'Am', + 'Americium', + '(243)', + 10, + 10, + 'Cm', + 'Curium', + '(247)', + 11, + 10, + 'Bk', + 'Berkelium', + '(247)', + 12, + 10, + 'Cf', + 'Californium', + '(251)', + 13, + 10, + 'Es', + 'Einstenium', + '(252)', + 14, + 10, + 'Fm', + 'Fermium', + '(257)', + 15, + 10, + 'Md', + 'Mendelevium', + '(258)', + 16, + 10, + 'No', + 'Nobelium', + '(259)', + 17, + 10, + 'Lr', + 'Lawrencium', + '(262)', + 18, + 10, + 'Rf', + 'Rutherfordium', + '(267)', + 4, + 7, + 'Db', + 'Dubnium', + '(268)', + 5, + 7, + 'Sg', + 'Seaborgium', + '(271)', + 6, + 7, + 'Bh', + 'Bohrium', + '(272)', + 7, + 7, + 'Hs', + 'Hassium', + '(270)', + 8, + 7, + 'Mt', + 'Meitnerium', + '(276)', + 9, + 7, + 'Ds', + 'Darmstadium', + '(281)', + 10, + 7, + 'Rg', + 'Roentgenium', + '(280)', + 11, + 7, + 'Cn', + 'Copernicium', + '(285)', + 12, + 7, + 'Nh', + 'Nihonium', + '(286)', + 13, + 7, + 'Fl', + 'Flerovium', + '(289)', + 14, + 7, + 'Mc', + 'Moscovium', + '(290)', + 15, + 7, + 'Lv', + 'Livermorium', + '(293)', + 16, + 7, + 'Ts', + 'Tennessine', + '(294)', + 17, + 7, + 'Og', + 'Oganesson', + '(294)', + 18, + 7, +]; + +let camera, scene, renderer; +let controls; + +const objects = []; +const targets = { table: [], sphere: [], helix: [], grid: [] }; + +init(); +animate(); + +function init() { + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 10000); + camera.position.z = 3000; + + scene = new THREE.Scene(); + + // table + + for (let i = 0; i < table.length; i += 5) { + const element = document.createElement('div'); + element.className = 'element'; + element.style.backgroundColor = 'rgba(0,127,127,' + (Math.random() * 0.5 + 0.25) + ')'; + + const number = document.createElement('div'); + number.className = 'number'; + number.textContent = i / 5 + 1; + element.appendChild(number); + + const symbol = document.createElement('div'); + symbol.className = 'symbol'; + symbol.textContent = table[i]; + element.appendChild(symbol); + + const details = document.createElement('div'); + details.className = 'details'; + details.innerHTML = table[i + 1] + '
' + table[i + 2]; + element.appendChild(details); + + const objectCSS = new CSS3DObject(element); + objectCSS.position.x = Math.random() * 4000 - 2000; + objectCSS.position.y = Math.random() * 4000 - 2000; + objectCSS.position.z = Math.random() * 4000 - 2000; + scene.add(objectCSS); + + objects.push(objectCSS); + + // + + const object = new THREE.Object3D(); + object.position.x = table[i + 3] * 140 - 1330; + object.position.y = -(table[i + 4] * 180) + 990; + + targets.table.push(object); + } + + // sphere + + const vector = new THREE.Vector3(); + + for (let i = 0, l = objects.length; i < l; i++) { + const phi = Math.acos(-1 + (2 * i) / l); + const theta = Math.sqrt(l * Math.PI) * phi; + + const object = new THREE.Object3D(); + + object.position.setFromSphericalCoords(800, phi, theta); + + vector.copy(object.position).multiplyScalar(2); + + object.lookAt(vector); + + targets.sphere.push(object); + } + + // helix + + for (let i = 0, l = objects.length; i < l; i++) { + const theta = i * 0.175 + Math.PI; + const y = -(i * 8) + 450; + + const object = new THREE.Object3D(); + + object.position.setFromCylindricalCoords(900, theta, y); + + vector.x = object.position.x * 2; + vector.y = object.position.y; + vector.z = object.position.z * 2; + + object.lookAt(vector); + + targets.helix.push(object); + } + + // grid + + for (let i = 0; i < objects.length; i++) { + const object = new THREE.Object3D(); + + object.position.x = (i % 5) * 400 - 800; + object.position.y = -(Math.floor(i / 5) % 5) * 400 + 800; + object.position.z = Math.floor(i / 25) * 1000 - 2000; + + targets.grid.push(object); + } + + // + + renderer = new CSS3DRenderer(); + renderer.setSize(window.innerWidth, window.innerHeight); + document.getElementById('container').appendChild(renderer.domElement); + + // + + controls = new TrackballControls(camera, renderer.domElement); + controls.minDistance = 500; + controls.maxDistance = 6000; + controls.addEventListener('change', render); + + const buttonTable = document.getElementById('table'); + buttonTable.addEventListener('click', function () { + transform(targets.table, 2000); + }); + + const buttonSphere = document.getElementById('sphere'); + buttonSphere.addEventListener('click', function () { + transform(targets.sphere, 2000); + }); + + const buttonHelix = document.getElementById('helix'); + buttonHelix.addEventListener('click', function () { + transform(targets.helix, 2000); + }); + + const buttonGrid = document.getElementById('grid'); + buttonGrid.addEventListener('click', function () { + transform(targets.grid, 2000); + }); + + transform(targets.table, 2000); + + // + + window.addEventListener('resize', onWindowResize); +} + +function transform(targets, duration) { + TWEEN.removeAll(); + + for (let i = 0; i < objects.length; i++) { + const object = objects[i]; + const target = targets[i]; + + new TWEEN.Tween(object.position) + .to( + { x: target.position.x, y: target.position.y, z: target.position.z }, + Math.random() * duration + duration, + ) + .easing(TWEEN.Easing.Exponential.InOut) + .start(); + + new TWEEN.Tween(object.rotation) + .to( + { x: target.rotation.x, y: target.rotation.y, z: target.rotation.z }, + Math.random() * duration + duration, + ) + .easing(TWEEN.Easing.Exponential.InOut) + .start(); + } + + new TWEEN.Tween(this) + .to({}, duration * 2) + .onUpdate(render) + .start(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + render(); +} + +function animate() { + requestAnimationFrame(animate); + + TWEEN.update(); + + controls.update(); +} + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/css3d_sandbox.ts b/examples-testing/examples/css3d_sandbox.ts new file mode 100644 index 000000000..1088b84b1 --- /dev/null +++ b/examples-testing/examples/css3d_sandbox.ts @@ -0,0 +1,180 @@ +import * as THREE from 'three'; + +import { TrackballControls } from 'three/addons/controls/TrackballControls.js'; +import { CSS3DRenderer, CSS3DObject } from 'three/addons/renderers/CSS3DRenderer.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let camera, scene, renderer; + +let scene2, renderer2; + +let controls; + +init(); +animate(); + +function init() { + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(200, 200, 200); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xf0f0f0); + + scene2 = new THREE.Scene(); + + const material = new THREE.MeshBasicMaterial({ + color: 0x000000, + wireframe: true, + wireframeLinewidth: 1, + side: THREE.DoubleSide, + }); + + // + + for (let i = 0; i < 10; i++) { + const element = document.createElement('div'); + element.style.width = '100px'; + element.style.height = '100px'; + element.style.opacity = i < 5 ? 0.5 : 1; + element.style.background = new THREE.Color(Math.random() * 0xffffff).getStyle(); + + const object = new CSS3DObject(element); + object.position.x = Math.random() * 200 - 100; + object.position.y = Math.random() * 200 - 100; + object.position.z = Math.random() * 200 - 100; + object.rotation.x = Math.random(); + object.rotation.y = Math.random(); + object.rotation.z = Math.random(); + object.scale.x = Math.random() + 0.5; + object.scale.y = Math.random() + 0.5; + scene2.add(object); + + const geometry = new THREE.PlaneGeometry(100, 100); + const mesh = new THREE.Mesh(geometry, material); + mesh.position.copy(object.position); + mesh.rotation.copy(object.rotation); + mesh.scale.copy(object.scale); + scene.add(mesh); + } + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + document.body.appendChild(renderer.domElement); + + renderer2 = new CSS3DRenderer(); + renderer2.setSize(window.innerWidth, window.innerHeight); + renderer2.domElement.style.position = 'absolute'; + renderer2.domElement.style.top = 0; + document.body.appendChild(renderer2.domElement); + + controls = new TrackballControls(camera, renderer2.domElement); + + window.addEventListener('resize', onWindowResize); + createPanel(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + renderer2.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + requestAnimationFrame(animate); + + controls.update(); + + renderer.render(scene, camera); + renderer2.render(scene2, camera); +} + +function createPanel() { + const panel = new GUI(); + const folder1 = panel.addFolder('camera setViewOffset').close(); + + const settings = { + setViewOffset() { + folder1.children[1].enable().setValue(window.innerWidth); + folder1.children[2].enable().setValue(window.innerHeight); + folder1.children[3].enable().setValue(0); + folder1.children[4].enable().setValue(0); + folder1.children[5].enable().setValue(window.innerWidth); + folder1.children[6].enable().setValue(window.innerHeight); + }, + fullWidth: 0, + fullHeight: 0, + offsetX: 0, + offsetY: 0, + width: 0, + height: 0, + clearViewOffset() { + folder1.children[1].setValue(0).disable(); + folder1.children[2].setValue(0).disable(); + folder1.children[3].setValue(0).disable(); + folder1.children[4].setValue(0).disable(); + folder1.children[5].setValue(0).disable(); + folder1.children[6].setValue(0).disable(); + camera.clearViewOffset(); + }, + }; + + folder1.add(settings, 'setViewOffset'); + folder1 + .add(settings, 'fullWidth', window.screen.width / 4, window.screen.width * 2, 1) + .onChange(val => updateCameraViewOffset({ fullWidth: val })) + .disable(); + folder1 + .add(settings, 'fullHeight', window.screen.height / 4, window.screen.height * 2, 1) + .onChange(val => updateCameraViewOffset({ fullHeight: val })) + .disable(); + folder1 + .add(settings, 'offsetX', 0, 256, 1) + .onChange(val => updateCameraViewOffset({ x: val })) + .disable(); + folder1 + .add(settings, 'offsetY', 0, 256, 1) + .onChange(val => updateCameraViewOffset({ y: val })) + .disable(); + folder1 + .add(settings, 'width', window.screen.width / 4, window.screen.width * 2, 1) + .onChange(val => updateCameraViewOffset({ width: val })) + .disable(); + folder1 + .add(settings, 'height', window.screen.height / 4, window.screen.height * 2, 1) + .onChange(val => updateCameraViewOffset({ height: val })) + .disable(); + folder1.add(settings, 'clearViewOffset'); +} + +function updateCameraViewOffset({ fullWidth, fullHeight, x, y, width, height }) { + if (!camera.view) { + camera.setViewOffset( + fullWidth || window.innerWidth, + fullHeight || window.innerHeight, + x || 0, + y || 0, + width || window.innerWidth, + height || window.innerHeight, + ); + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + } else { + camera.setViewOffset( + fullWidth || camera.view.fullWidth, + fullHeight || camera.view.fullHeight, + x || camera.view.offsetX, + y || camera.view.offsetY, + width || camera.view.width, + height || camera.view.height, + ); + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + } +} diff --git a/examples-testing/examples/css3d_sprites.ts b/examples-testing/examples/css3d_sprites.ts new file mode 100644 index 000000000..39c3455a5 --- /dev/null +++ b/examples-testing/examples/css3d_sprites.ts @@ -0,0 +1,157 @@ +import * as THREE from 'three'; + +import TWEEN from 'three/addons/libs/tween.module.js'; +import { TrackballControls } from 'three/addons/controls/TrackballControls.js'; +import { CSS3DRenderer, CSS3DSprite } from 'three/addons/renderers/CSS3DRenderer.js'; + +let camera, scene, renderer; +let controls; + +const particlesTotal = 512; +const positions = []; +const objects = []; +let current = 0; + +init(); +animate(); + +function init() { + camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 5000); + camera.position.set(600, 400, 1500); + camera.lookAt(0, 0, 0); + + scene = new THREE.Scene(); + + const image = document.createElement('img'); + image.addEventListener('load', function () { + for (let i = 0; i < particlesTotal; i++) { + const object = new CSS3DSprite(image.cloneNode()); + ((object.position.x = Math.random() * 4000 - 2000), + (object.position.y = Math.random() * 4000 - 2000), + (object.position.z = Math.random() * 4000 - 2000)); + scene.add(object); + + objects.push(object); + } + + transition(); + }); + image.src = 'textures/sprite.png'; + + // Plane + + const amountX = 16; + const amountZ = 32; + const separationPlane = 150; + const offsetX = ((amountX - 1) * separationPlane) / 2; + const offsetZ = ((amountZ - 1) * separationPlane) / 2; + + for (let i = 0; i < particlesTotal; i++) { + const x = (i % amountX) * separationPlane; + const z = Math.floor(i / amountX) * separationPlane; + const y = (Math.sin(x * 0.5) + Math.sin(z * 0.5)) * 200; + + positions.push(x - offsetX, y, z - offsetZ); + } + + // Cube + + const amount = 8; + const separationCube = 150; + const offset = ((amount - 1) * separationCube) / 2; + + for (let i = 0; i < particlesTotal; i++) { + const x = (i % amount) * separationCube; + const y = Math.floor((i / amount) % amount) * separationCube; + const z = Math.floor(i / (amount * amount)) * separationCube; + + positions.push(x - offset, y - offset, z - offset); + } + + // Random + + for (let i = 0; i < particlesTotal; i++) { + positions.push(Math.random() * 4000 - 2000, Math.random() * 4000 - 2000, Math.random() * 4000 - 2000); + } + + // Sphere + + const radius = 750; + + for (let i = 0; i < particlesTotal; i++) { + const phi = Math.acos(-1 + (2 * i) / particlesTotal); + const theta = Math.sqrt(particlesTotal * Math.PI) * phi; + + positions.push( + radius * Math.cos(theta) * Math.sin(phi), + radius * Math.sin(theta) * Math.sin(phi), + radius * Math.cos(phi), + ); + } + + // + + renderer = new CSS3DRenderer(); + renderer.setSize(window.innerWidth, window.innerHeight); + document.getElementById('container').appendChild(renderer.domElement); + + // + + controls = new TrackballControls(camera, renderer.domElement); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function transition() { + const offset = current * particlesTotal * 3; + const duration = 2000; + + for (let i = 0, j = offset; i < particlesTotal; i++, j += 3) { + const object = objects[i]; + + new TWEEN.Tween(object.position) + .to( + { + x: positions[j], + y: positions[j + 1], + z: positions[j + 2], + }, + Math.random() * duration + duration, + ) + .easing(TWEEN.Easing.Exponential.InOut) + .start(); + } + + new TWEEN.Tween(this) + .to({}, duration * 3) + .onComplete(transition) + .start(); + + current = (current + 1) % 4; +} + +function animate() { + requestAnimationFrame(animate); + + TWEEN.update(); + controls.update(); + + const time = performance.now(); + + for (let i = 0, l = objects.length; i < l; i++) { + const object = objects[i]; + const scale = Math.sin((Math.floor(object.position.x) + time) * 0.002) * 0.3 + 1; + object.scale.set(scale, scale, scale); + } + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/css3d_youtube.ts b/examples-testing/examples/css3d_youtube.ts new file mode 100644 index 000000000..62652f87f --- /dev/null +++ b/examples-testing/examples/css3d_youtube.ts @@ -0,0 +1,79 @@ +import * as THREE from 'three'; + +import { TrackballControls } from 'three/addons/controls/TrackballControls.js'; +import { CSS3DRenderer, CSS3DObject } from 'three/addons/renderers/CSS3DRenderer.js'; + +let camera, scene, renderer; +let controls; + +function Element(id, x, y, z, ry) { + const div = document.createElement('div'); + div.style.width = '480px'; + div.style.height = '360px'; + div.style.backgroundColor = '#000'; + + const iframe = document.createElement('iframe'); + iframe.style.width = '480px'; + iframe.style.height = '360px'; + iframe.style.border = '0px'; + iframe.src = ['https://www.youtube.com/embed/', id, '?rel=0'].join(''); + div.appendChild(iframe); + + const object = new CSS3DObject(div); + object.position.set(x, y, z); + object.rotation.y = ry; + + return object; +} + +init(); +animate(); + +function init() { + const container = document.getElementById('container'); + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 5000); + camera.position.set(500, 350, 750); + + scene = new THREE.Scene(); + + renderer = new CSS3DRenderer(); + renderer.setSize(window.innerWidth, window.innerHeight); + container.appendChild(renderer.domElement); + + const group = new THREE.Group(); + group.add(new Element('SJOz3qjfQXU', 0, 0, 240, 0)); + group.add(new Element('Y2-xZ-1HE-Q', 240, 0, 0, Math.PI / 2)); + group.add(new Element('IrydklNpcFI', 0, 0, -240, Math.PI)); + group.add(new Element('9ubytEsCaS0', -240, 0, 0, -Math.PI / 2)); + scene.add(group); + + controls = new TrackballControls(camera, renderer.domElement); + controls.rotateSpeed = 4; + + window.addEventListener('resize', onWindowResize); + + // Block iframe events when dragging camera + + const blocker = document.getElementById('blocker'); + blocker.style.display = 'none'; + + controls.addEventListener('start', function () { + blocker.style.display = ''; + }); + controls.addEventListener('end', function () { + blocker.style.display = 'none'; + }); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + requestAnimationFrame(animate); + controls.update(); + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/games_fps.ts b/examples-testing/examples/games_fps.ts new file mode 100644 index 000000000..ed08fb5fa --- /dev/null +++ b/examples-testing/examples/games_fps.ts @@ -0,0 +1,377 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +import { Octree } from 'three/addons/math/Octree.js'; +import { OctreeHelper } from 'three/addons/helpers/OctreeHelper.js'; + +import { Capsule } from 'three/addons/math/Capsule.js'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +const timer = new THREE.Timer(); +timer.connect(document); + +const scene = new THREE.Scene(); +scene.background = new THREE.Color(0x88ccee); +scene.fog = new THREE.Fog(0x88ccee, 0, 50); + +const camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 1000); +camera.rotation.order = 'YXZ'; + +const fillLight1 = new THREE.HemisphereLight(0x8dc1de, 0x00668d, 1.5); +fillLight1.position.set(2, 1, 1); +scene.add(fillLight1); + +const directionalLight = new THREE.DirectionalLight(0xffffff, 2.5); +directionalLight.position.set(-5, 25, -1); +directionalLight.castShadow = true; +directionalLight.shadow.camera.near = 0.01; +directionalLight.shadow.camera.far = 500; +directionalLight.shadow.camera.right = 30; +directionalLight.shadow.camera.left = -30; +directionalLight.shadow.camera.top = 30; +directionalLight.shadow.camera.bottom = -30; +directionalLight.shadow.mapSize.width = 1024; +directionalLight.shadow.mapSize.height = 1024; +directionalLight.shadow.radius = 4; +directionalLight.shadow.bias = -0.00006; +scene.add(directionalLight); + +const container = document.getElementById('container'); + +const renderer = new THREE.WebGLRenderer({ antialias: true }); +renderer.setPixelRatio(window.devicePixelRatio); +renderer.setSize(window.innerWidth, window.innerHeight); +renderer.setAnimationLoop(animate); +renderer.shadowMap.enabled = true; +renderer.shadowMap.type = THREE.VSMShadowMap; +renderer.toneMapping = THREE.ACESFilmicToneMapping; +container.appendChild(renderer.domElement); + +const stats = new Stats(); +stats.domElement.style.position = 'absolute'; +stats.domElement.style.top = '0px'; +container.appendChild(stats.domElement); + +const GRAVITY = 30; + +const NUM_SPHERES = 100; +const SPHERE_RADIUS = 0.2; + +const STEPS_PER_FRAME = 5; + +const sphereGeometry = new THREE.IcosahedronGeometry(SPHERE_RADIUS, 5); +const sphereMaterial = new THREE.MeshLambertMaterial({ color: 0xdede8d }); + +const spheres = []; +let sphereIdx = 0; + +for (let i = 0; i < NUM_SPHERES; i++) { + const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial); + sphere.castShadow = true; + sphere.receiveShadow = true; + + scene.add(sphere); + + spheres.push({ + mesh: sphere, + collider: new THREE.Sphere(new THREE.Vector3(0, -100, 0), SPHERE_RADIUS), + velocity: new THREE.Vector3(), + }); +} + +const worldOctree = new Octree(); + +const playerCollider = new Capsule(new THREE.Vector3(0, 0.35, 0), new THREE.Vector3(0, 1, 0), 0.35); + +const playerVelocity = new THREE.Vector3(); +const playerDirection = new THREE.Vector3(); + +let playerOnFloor = false; +let mouseTime = 0; + +const keyStates = {}; + +const vector1 = new THREE.Vector3(); +const vector2 = new THREE.Vector3(); +const vector3 = new THREE.Vector3(); + +document.addEventListener('keydown', event => { + keyStates[event.code] = true; +}); + +document.addEventListener('keyup', event => { + keyStates[event.code] = false; +}); + +container.addEventListener('mousedown', () => { + document.body.requestPointerLock(); + + mouseTime = performance.now(); +}); + +document.addEventListener('mouseup', () => { + if (document.pointerLockElement !== null) throwBall(); +}); + +document.body.addEventListener('mousemove', event => { + if (document.pointerLockElement === document.body) { + camera.rotation.y -= event.movementX / 500; + camera.rotation.x -= event.movementY / 500; + } +}); + +window.addEventListener('resize', onWindowResize); + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function throwBall() { + const sphere = spheres[sphereIdx]; + + camera.getWorldDirection(playerDirection); + + sphere.collider.center.copy(playerCollider.end).addScaledVector(playerDirection, playerCollider.radius * 1.5); + + // throw the ball with more force if we hold the button longer, and if we move forward + + const impulse = 15 + 30 * (1 - Math.exp((mouseTime - performance.now()) * 0.001)); + + sphere.velocity.copy(playerDirection).multiplyScalar(impulse); + sphere.velocity.addScaledVector(playerVelocity, 2); + + sphereIdx = (sphereIdx + 1) % spheres.length; +} + +function playerCollisions() { + const result = worldOctree.capsuleIntersect(playerCollider); + + playerOnFloor = false; + + if (result) { + // determine if the surface we bumped into is something we can stand on + + playerOnFloor = result.normal.y >= 0.15; // allow slopes up to ~81° but ignore sheer vertical walls + + if (!playerOnFloor) { + playerVelocity.addScaledVector(result.normal, -result.normal.dot(playerVelocity)); + } + + if (result.depth >= 1e-10) { + playerCollider.translate(result.normal.multiplyScalar(result.depth)); + } + } +} + +function updatePlayer(deltaTime) { + let damping = Math.exp(-4 * deltaTime) - 1; + + if (!playerOnFloor) { + playerVelocity.y -= GRAVITY * deltaTime; + + // small air resistance + damping *= 0.1; + } + + playerVelocity.addScaledVector(playerVelocity, damping); + + const deltaPosition = playerVelocity.clone().multiplyScalar(deltaTime); + playerCollider.translate(deltaPosition); + + playerCollisions(); + + camera.position.copy(playerCollider.end); +} + +function playerSphereCollision(sphere) { + const center = vector1.addVectors(playerCollider.start, playerCollider.end).multiplyScalar(0.5); + + const sphere_center = sphere.collider.center; + + const r = playerCollider.radius + sphere.collider.radius; + const r2 = r * r; + + // approximation: player = 3 spheres + + for (const point of [playerCollider.start, playerCollider.end, center]) { + const d2 = point.distanceToSquared(sphere_center); + + if (d2 < r2) { + const normal = vector1.subVectors(point, sphere_center).normalize(); + const v1 = vector2.copy(normal).multiplyScalar(normal.dot(playerVelocity)); + const v2 = vector3.copy(normal).multiplyScalar(normal.dot(sphere.velocity)); + + playerVelocity.add(v2).sub(v1); + sphere.velocity.add(v1).sub(v2); + + const d = (r - Math.sqrt(d2)) / 2; + sphere_center.addScaledVector(normal, -d); + } + } +} + +function spheresCollisions() { + for (let i = 0, length = spheres.length; i < length; i++) { + const s1 = spheres[i]; + + for (let j = i + 1; j < length; j++) { + const s2 = spheres[j]; + + const d2 = s1.collider.center.distanceToSquared(s2.collider.center); + const r = s1.collider.radius + s2.collider.radius; + const r2 = r * r; + + if (d2 < r2) { + const normal = vector1.subVectors(s1.collider.center, s2.collider.center).normalize(); + const v1 = vector2.copy(normal).multiplyScalar(normal.dot(s1.velocity)); + const v2 = vector3.copy(normal).multiplyScalar(normal.dot(s2.velocity)); + + s1.velocity.add(v2).sub(v1); + s2.velocity.add(v1).sub(v2); + + const d = (r - Math.sqrt(d2)) / 2; + + s1.collider.center.addScaledVector(normal, d); + s2.collider.center.addScaledVector(normal, -d); + } + } + } +} + +function updateSpheres(deltaTime) { + spheres.forEach(sphere => { + sphere.collider.center.addScaledVector(sphere.velocity, deltaTime); + + const result = worldOctree.sphereIntersect(sphere.collider); + + if (result) { + sphere.velocity.addScaledVector(result.normal, -result.normal.dot(sphere.velocity) * 1.5); + sphere.collider.center.add(result.normal.multiplyScalar(result.depth)); + } else { + sphere.velocity.y -= GRAVITY * deltaTime; + } + + const damping = Math.exp(-1.5 * deltaTime) - 1; + sphere.velocity.addScaledVector(sphere.velocity, damping); + + playerSphereCollision(sphere); + }); + + spheresCollisions(); + + for (const sphere of spheres) { + sphere.mesh.position.copy(sphere.collider.center); + } +} + +function getForwardVector() { + camera.getWorldDirection(playerDirection); + playerDirection.y = 0; + playerDirection.normalize(); + + return playerDirection; +} + +function getSideVector() { + camera.getWorldDirection(playerDirection); + playerDirection.y = 0; + playerDirection.normalize(); + playerDirection.cross(camera.up); + + return playerDirection; +} + +function controls(deltaTime) { + // gives a bit of air control + const speedDelta = deltaTime * (playerOnFloor ? 25 : 8); + + if (keyStates['KeyW']) { + playerVelocity.add(getForwardVector().multiplyScalar(speedDelta)); + } + + if (keyStates['KeyS']) { + playerVelocity.add(getForwardVector().multiplyScalar(-speedDelta)); + } + + if (keyStates['KeyA']) { + playerVelocity.add(getSideVector().multiplyScalar(-speedDelta)); + } + + if (keyStates['KeyD']) { + playerVelocity.add(getSideVector().multiplyScalar(speedDelta)); + } + + if (playerOnFloor) { + if (keyStates['Space']) { + playerVelocity.y = 15; + } + } +} + +const loader = new GLTFLoader().setPath('./models/gltf/'); + +loader.load('collision-world.glb', gltf => { + scene.add(gltf.scene); + + worldOctree.fromGraphNode(gltf.scene); + + gltf.scene.traverse(child => { + if (child.isMesh) { + child.castShadow = true; + child.receiveShadow = true; + + if (child.material.map) { + child.material.map.anisotropy = 4; + } + } + }); + + const helper = new OctreeHelper(worldOctree); + helper.visible = false; + scene.add(helper); + + const gui = new GUI({ width: 200 }); + gui.add({ debug: false }, 'debug').onChange(function (value) { + helper.visible = value; + }); +}); + +function teleportPlayerIfOob() { + if (camera.position.y <= -25) { + playerCollider.start.set(0, 0.35, 0); + playerCollider.end.set(0, 1, 0); + playerCollider.radius = 0.35; + camera.position.copy(playerCollider.end); + camera.rotation.set(0, 0, 0); + } +} + +function animate() { + timer.update(); + + const deltaTime = Math.min(0.05, timer.getDelta()) / STEPS_PER_FRAME; + + // we look for collisions in substeps to mitigate the risk of + // an object traversing another too quickly for detection. + + for (let i = 0; i < STEPS_PER_FRAME; i++) { + controls(deltaTime); + + updatePlayer(deltaTime); + + updateSpheres(deltaTime); + + teleportPlayerIfOob(); + } + + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/misc_animation_groups.ts b/examples-testing/examples/misc_animation_groups.ts new file mode 100644 index 000000000..9fed234fc --- /dev/null +++ b/examples-testing/examples/misc_animation_groups.ts @@ -0,0 +1,128 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +let stats, timer; +let scene, camera, renderer, mixer; + +init(); + +function init() { + scene = new THREE.Scene(); + + // + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(50, 50, 100); + camera.lookAt(scene.position); + + // all objects of this animation group share a common animation state + + const animationGroup = new THREE.AnimationObjectGroup(); + + // + + const geometry = new THREE.BoxGeometry(5, 5, 5); + const material = new THREE.MeshBasicMaterial({ transparent: true }); + + // + + for (let i = 0; i < 5; i++) { + for (let j = 0; j < 5; j++) { + const mesh = new THREE.Mesh(geometry, material); + + mesh.position.x = 32 - 16 * i; + mesh.position.y = 0; + mesh.position.z = 32 - 16 * j; + + scene.add(mesh); + animationGroup.add(mesh); + } + } + + // create some keyframe tracks + + const xAxis = new THREE.Vector3(1, 0, 0); + const qInitial = new THREE.Quaternion().setFromAxisAngle(xAxis, 0); + const qFinal = new THREE.Quaternion().setFromAxisAngle(xAxis, Math.PI); + const quaternionKF = new THREE.QuaternionKeyframeTrack( + '.quaternion', + [0, 1, 2], + [ + qInitial.x, + qInitial.y, + qInitial.z, + qInitial.w, + qFinal.x, + qFinal.y, + qFinal.z, + qFinal.w, + qInitial.x, + qInitial.y, + qInitial.z, + qInitial.w, + ], + ); + + const colorKF = new THREE.ColorKeyframeTrack( + '.material.color', + [0, 1, 2], + [1, 0, 0, 0, 1, 0, 0, 0, 1], + THREE.InterpolateDiscrete, + ); + const opacityKF = new THREE.NumberKeyframeTrack('.material.opacity', [0, 1, 2], [1, 0, 1]); + + // create clip + + const clip = new THREE.AnimationClip('default', 3, [quaternionKF, colorKF, opacityKF]); + + // apply the animation group to the mixer as the root object + + mixer = new THREE.AnimationMixer(animationGroup); + + const clipAction = mixer.clipAction(clip); + clipAction.play(); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // + + stats = new Stats(); + document.body.appendChild(stats.dom); + + // + + timer = new THREE.Timer(); + timer.connect(document); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + const delta = timer.getDelta(); + + if (mixer) { + mixer.update(delta); + } + + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/misc_animation_keys.ts b/examples-testing/examples/misc_animation_keys.ts new file mode 100644 index 000000000..32d497e5a --- /dev/null +++ b/examples-testing/examples/misc_animation_keys.ts @@ -0,0 +1,132 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +let stats, timer; +let scene, camera, renderer, mixer; + +init(); + +function init() { + scene = new THREE.Scene(); + + // + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(25, 25, 50); + camera.lookAt(scene.position); + + // + + const axesHelper = new THREE.AxesHelper(10); + scene.add(axesHelper); + + // + + const geometry = new THREE.BoxGeometry(5, 5, 5); + const material = new THREE.MeshBasicMaterial({ color: 0xffffff, transparent: true }); + const mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + // create a keyframe track (i.e. a timed sequence of keyframes) for each animated property + // Note: the keyframe track type should correspond to the type of the property being animated + + // POSITION + const positionKF = new THREE.VectorKeyframeTrack('.position', [0, 1, 2], [0, 0, 0, 30, 0, 0, 0, 0, 0]); + + // SCALE + const scaleKF = new THREE.VectorKeyframeTrack('.scale', [0, 1, 2], [1, 1, 1, 2, 2, 2, 1, 1, 1]); + + // ROTATION + // Rotation should be performed using quaternions, using a THREE.QuaternionKeyframeTrack + // Interpolating Euler angles (.rotation property) can be problematic and is currently not supported + + // set up rotation about x axis + const xAxis = new THREE.Vector3(1, 0, 0); + + const qInitial = new THREE.Quaternion().setFromAxisAngle(xAxis, 0); + const qFinal = new THREE.Quaternion().setFromAxisAngle(xAxis, Math.PI); + const quaternionKF = new THREE.QuaternionKeyframeTrack( + '.quaternion', + [0, 1, 2], + [ + qInitial.x, + qInitial.y, + qInitial.z, + qInitial.w, + qFinal.x, + qFinal.y, + qFinal.z, + qFinal.w, + qInitial.x, + qInitial.y, + qInitial.z, + qInitial.w, + ], + ); + + // COLOR + const colorKF = new THREE.ColorKeyframeTrack( + '.material.color', + [0, 1, 2], + [1, 0, 0, 0, 1, 0, 0, 0, 1], + THREE.InterpolateDiscrete, + ); + + // OPACITY + const opacityKF = new THREE.NumberKeyframeTrack('.material.opacity', [0, 1, 2], [1, 0, 1]); + + // create an animation sequence with the tracks + // If a negative time value is passed, the duration will be calculated from the times of the passed tracks array + const clip = new THREE.AnimationClip('Action', 3, [scaleKF, positionKF, quaternionKF, colorKF, opacityKF]); + + // setup the THREE.AnimationMixer + mixer = new THREE.AnimationMixer(mesh); + + // create a ClipAction and set it to play + const clipAction = mixer.clipAction(clip); + clipAction.play(); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // + + stats = new Stats(); + document.body.appendChild(stats.dom); + + // + + timer = new THREE.Timer(); + timer.connect(document); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + const delta = timer.getDelta(); + + if (mixer) { + mixer.update(delta); + } + + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/misc_boxselection.ts b/examples-testing/examples/misc_boxselection.ts new file mode 100644 index 000000000..e7079c405 --- /dev/null +++ b/examples-testing/examples/misc_boxselection.ts @@ -0,0 +1,137 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { SelectionBox } from 'three/addons/interactive/SelectionBox.js'; +import { SelectionHelper } from 'three/addons/interactive/SelectionHelper.js'; + +let container, stats; +let camera, scene, renderer; + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 500); + camera.position.z = 50; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xf0f0f0); + + scene.add(new THREE.AmbientLight(0xaaaaaa)); + + const light = new THREE.SpotLight(0xffffff, 10000); + light.position.set(0, 25, 50); + light.angle = Math.PI / 5; + + light.castShadow = true; + light.shadow.camera.near = 10; + light.shadow.camera.far = 100; + light.shadow.mapSize.width = 1024; + light.shadow.mapSize.height = 1024; + + scene.add(light); + + const geometry = new THREE.BoxGeometry(); + + for (let i = 0; i < 200; i++) { + const object = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({ color: Math.random() * 0xffffff })); + + object.position.x = Math.random() * 80 - 40; + object.position.y = Math.random() * 45 - 25; + object.position.z = Math.random() * 45 - 25; + + object.rotation.x = Math.random() * 2 * Math.PI; + object.rotation.y = Math.random() * 2 * Math.PI; + object.rotation.z = Math.random() * 2 * Math.PI; + + object.scale.x = Math.random() * 2 + 1; + object.scale.y = Math.random() * 2 + 1; + object.scale.z = Math.random() * 2 + 1; + + object.castShadow = true; + object.receiveShadow = true; + + scene.add(object); + } + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.shadowMap.enabled = true; + renderer.shadowMap.type = THREE.PCFShadowMap; + + container.appendChild(renderer.domElement); + + stats = new Stats(); + container.appendChild(stats.dom); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + renderer.render(scene, camera); + + stats.update(); +} + +const selectionBox = new SelectionBox(camera, scene); +const helper = new SelectionHelper(renderer, 'selectBox'); + +document.addEventListener('pointerdown', function (event) { + for (const item of selectionBox.collection) { + item.material.emissive.set(0x000000); + } + + selectionBox.startPoint.set( + (event.clientX / window.innerWidth) * 2 - 1, + -(event.clientY / window.innerHeight) * 2 + 1, + 0.5, + ); +}); + +document.addEventListener('pointermove', function (event) { + if (helper.isDown) { + for (let i = 0; i < selectionBox.collection.length; i++) { + selectionBox.collection[i].material.emissive.set(0x000000); + } + + selectionBox.endPoint.set( + (event.clientX / window.innerWidth) * 2 - 1, + -(event.clientY / window.innerHeight) * 2 + 1, + 0.5, + ); + + const allSelected = selectionBox.select(); + + for (let i = 0; i < allSelected.length; i++) { + allSelected[i].material.emissive.set(0xffffff); + } + } +}); + +document.addEventListener('pointerup', function (event) { + selectionBox.endPoint.set( + (event.clientX / window.innerWidth) * 2 - 1, + -(event.clientY / window.innerHeight) * 2 + 1, + 0.5, + ); + + const allSelected = selectionBox.select(); + + for (let i = 0; i < allSelected.length; i++) { + allSelected[i].material.emissive.set(0xffffff); + } +}); diff --git a/examples-testing/examples/misc_controls_arcball.ts b/examples-testing/examples/misc_controls_arcball.ts new file mode 100644 index 000000000..f2611be64 --- /dev/null +++ b/examples-testing/examples/misc_controls_arcball.ts @@ -0,0 +1,211 @@ +import * as THREE from 'three'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { ArcballControls } from 'three/addons/controls/ArcballControls.js'; + +import { OBJLoader } from 'three/addons/loaders/OBJLoader.js'; +import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; + +const cameras = ['Orthographic', 'Perspective']; +const cameraType = { type: 'Perspective' }; + +const perspectiveDistance = 2.5; +const orthographicDistance = 120; +let camera, controls, scene, renderer, gui; +let folderOptions, folderAnimations; + +const arcballGui = { + gizmoVisible: true, + + setArcballControls: function () { + controls = new ArcballControls(camera, renderer.domElement, scene); + controls.addEventListener('change', render); + + this.gizmoVisible = true; + + this.populateGui(); + }, + + populateGui: function () { + folderOptions.add(controls, 'enabled').name('Enable controls'); + folderOptions.add(controls, 'enableFocus').name('Enable focus'); + folderOptions.add(controls, 'enableGrid').name('Enable Grid'); + folderOptions.add(controls, 'enableRotate').name('Enable rotate'); + folderOptions.add(controls, 'enablePan').name('Enable pan'); + folderOptions.add(controls, 'enableZoom').name('Enable zoom'); + folderOptions.add(controls, 'cursorZoom').name('Cursor zoom'); + folderOptions.add(controls, 'adjustNearFar').name('adjust near/far'); + folderOptions.add(controls, 'scaleFactor', 1.1, 10, 0.1).name('Scale factor'); + folderOptions.add(controls, 'minDistance', 0, 50, 0.5).name('Min distance'); + folderOptions.add(controls, 'maxDistance', 0, 50, 0.5).name('Max distance'); + folderOptions.add(controls, 'minZoom', 0, 50, 0.5).name('Min zoom'); + folderOptions.add(controls, 'maxZoom', 0, 50, 0.5).name('Max zoom'); + folderOptions + .add(arcballGui, 'gizmoVisible') + .name('Show gizmos') + .onChange(function () { + controls.setGizmosVisible(arcballGui.gizmoVisible); + }); + folderOptions.add(controls, 'copyState').name('Copy state(ctrl+c)'); + folderOptions.add(controls, 'pasteState').name('Paste state(ctrl+v)'); + folderOptions.add(controls, 'reset').name('Reset'); + folderAnimations.add(controls, 'enableAnimations').name('Enable anim.'); + folderAnimations.add(controls, 'dampingFactor', 0, 100, 1).name('Damping'); + folderAnimations.add(controls, 'wMax', 0, 100, 1).name('Angular spd'); + }, +}; + +init(); + +function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.toneMapping = THREE.ReinhardToneMapping; + renderer.toneMappingExposure = 3; + renderer.domElement.style.background = 'linear-gradient( 180deg, rgba( 0,0,0,1 ) 0%, rgba( 128,128,255,1 ) 100% )'; + container.appendChild(renderer.domElement); + + // + + scene = new THREE.Scene(); + + camera = makePerspectiveCamera(); + camera.position.set(0, 0, perspectiveDistance); + + const material = new THREE.MeshStandardMaterial(); + + new OBJLoader().setPath('models/obj/cerberus/').load('Cerberus.obj', function (group) { + const textureLoader = new THREE.TextureLoader().setPath('models/obj/cerberus/'); + + material.roughness = 1; + material.metalness = 1; + + const diffuseMap = textureLoader.load('Cerberus_A.jpg', render); + diffuseMap.colorSpace = THREE.SRGBColorSpace; + material.map = diffuseMap; + + material.metalnessMap = material.roughnessMap = textureLoader.load('Cerberus_RM.jpg', render); + material.normalMap = textureLoader.load('Cerberus_N.jpg', render); + + material.map.wrapS = THREE.RepeatWrapping; + material.roughnessMap.wrapS = THREE.RepeatWrapping; + material.metalnessMap.wrapS = THREE.RepeatWrapping; + material.normalMap.wrapS = THREE.RepeatWrapping; + + group.traverse(function (child) { + if (child.isMesh) { + child.material = material; + } + }); + + group.rotation.y = Math.PI / 2; + group.position.x += 0.25; + scene.add(group); + render(); + + new HDRLoader().setPath('textures/equirectangular/').load('venice_sunset_1k.hdr', function (hdrEquirect) { + hdrEquirect.mapping = THREE.EquirectangularReflectionMapping; + + scene.environment = hdrEquirect; + + render(); + }); + + window.addEventListener('keydown', onKeyDown); + window.addEventListener('resize', onWindowResize); + + // + + gui = new GUI(); + gui.add(cameraType, 'type', cameras) + .name('Choose Camera') + .onChange(function () { + setCamera(cameraType.type); + }); + + folderOptions = gui.addFolder('Arcball parameters'); + folderAnimations = folderOptions.addFolder('Animations'); + + arcballGui.setArcballControls(); + + render(); + }); +} + +function makeOrthographicCamera() { + const halfFovV = THREE.MathUtils.DEG2RAD * 45 * 0.5; + const halfFovH = Math.atan((window.innerWidth / window.innerHeight) * Math.tan(halfFovV)); + + const halfW = perspectiveDistance * Math.tan(halfFovH); + const halfH = perspectiveDistance * Math.tan(halfFovV); + const near = 0.01; + const far = 2000; + const newCamera = new THREE.OrthographicCamera(-halfW, halfW, halfH, -halfH, near, far); + return newCamera; +} + +function makePerspectiveCamera() { + const fov = 45; + const aspect = window.innerWidth / window.innerHeight; + const near = 0.01; + const far = 2000; + const newCamera = new THREE.PerspectiveCamera(fov, aspect, near, far); + return newCamera; +} + +function onWindowResize() { + if (camera.type == 'OrthographicCamera') { + const halfFovV = THREE.MathUtils.DEG2RAD * 45 * 0.5; + const halfFovH = Math.atan((window.innerWidth / window.innerHeight) * Math.tan(halfFovV)); + + const halfW = perspectiveDistance * Math.tan(halfFovH); + const halfH = perspectiveDistance * Math.tan(halfFovV); + camera.left = -halfW; + camera.right = halfW; + camera.top = halfH; + camera.bottom = -halfH; + } else if (camera.type == 'PerspectiveCamera') { + camera.aspect = window.innerWidth / window.innerHeight; + } + + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + render(); +} + +function render() { + renderer.render(scene, camera); +} + +function onKeyDown(event) { + if (event.key === 'c') { + if (event.ctrlKey || event.metaKey) { + controls.copyState(); + } + } else if (event.key === 'v') { + if (event.ctrlKey || event.metaKey) { + controls.pasteState(); + } + } +} + +function setCamera(type) { + if (type == 'Orthographic') { + camera = makeOrthographicCamera(); + camera.position.set(0, 0, orthographicDistance); + } else if (type == 'Perspective') { + camera = makePerspectiveCamera(); + camera.position.set(0, 0, perspectiveDistance); + } + + controls.setCamera(camera); + + render(); +} diff --git a/examples-testing/examples/misc_controls_drag.ts b/examples-testing/examples/misc_controls_drag.ts new file mode 100644 index 000000000..b12b0421e --- /dev/null +++ b/examples-testing/examples/misc_controls_drag.ts @@ -0,0 +1,153 @@ +import * as THREE from 'three'; + +import { DragControls } from 'three/addons/controls/DragControls.js'; + +let container; +let camera, scene, renderer; +let controls, group; +let enableSelection = false; + +const objects = []; + +const mouse = new THREE.Vector2(), + raycaster = new THREE.Raycaster(); + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 500); + camera.position.z = 25; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xf0f0f0); + + scene.add(new THREE.AmbientLight(0xaaaaaa)); + + const light = new THREE.SpotLight(0xffffff, 10000); + light.position.set(0, 25, 50); + light.angle = Math.PI / 9; + + light.castShadow = true; + light.shadow.camera.near = 10; + light.shadow.camera.far = 100; + light.shadow.mapSize.width = 1024; + light.shadow.mapSize.height = 1024; + + scene.add(light); + + group = new THREE.Group(); + scene.add(group); + + const geometry = new THREE.BoxGeometry(); + + for (let i = 0; i < 200; i++) { + const object = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({ color: Math.random() * 0xffffff })); + + object.position.x = Math.random() * 30 - 15; + object.position.y = Math.random() * 15 - 7.5; + object.position.z = Math.random() * 20 - 10; + + object.rotation.x = Math.random() * 2 * Math.PI; + object.rotation.y = Math.random() * 2 * Math.PI; + object.rotation.z = Math.random() * 2 * Math.PI; + + object.scale.x = Math.random() * 2 + 1; + object.scale.y = Math.random() * 2 + 1; + object.scale.z = Math.random() * 2 + 1; + + object.castShadow = true; + object.receiveShadow = true; + + scene.add(object); + + objects.push(object); + } + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.shadowMap.enabled = true; + renderer.shadowMap.type = THREE.PCFShadowMap; + + container.appendChild(renderer.domElement); + + controls = new DragControls([...objects], camera, renderer.domElement); + controls.rotateSpeed = 2; + controls.addEventListener('drag', render); + + // + + window.addEventListener('resize', onWindowResize); + + document.addEventListener('click', onClick); + window.addEventListener('keydown', onKeyDown); + window.addEventListener('keyup', onKeyUp); + + render(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + render(); +} + +function onKeyDown(event) { + enableSelection = event.keyCode === 16 ? true : false; + + if (event.keyCode === 77) { + controls.touches.ONE = controls.touches.ONE === THREE.TOUCH.PAN ? THREE.TOUCH.ROTATE : THREE.TOUCH.PAN; + } +} + +function onKeyUp() { + enableSelection = false; +} + +function onClick(event) { + event.preventDefault(); + + if (enableSelection === true) { + const draggableObjects = controls.objects; + draggableObjects.length = 0; + + mouse.x = (event.clientX / window.innerWidth) * 2 - 1; + mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; + + raycaster.setFromCamera(mouse, camera); + + const intersections = raycaster.intersectObjects(objects, true); + + if (intersections.length > 0) { + const object = intersections[0].object; + + if (group.children.includes(object) === true) { + object.material.emissive.set(0x000000); + scene.attach(object); + } else { + object.material.emissive.set(0xaaaaaa); + group.attach(object); + } + + controls.transformGroup = true; + draggableObjects.push(group); + } + + if (group.children.length === 0) { + controls.transformGroup = false; + draggableObjects.push(...objects); + } + } + + render(); +} + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/misc_controls_fly.ts b/examples-testing/examples/misc_controls_fly.ts new file mode 100644 index 000000000..6493ebd8a --- /dev/null +++ b/examples-testing/examples/misc_controls_fly.ts @@ -0,0 +1,218 @@ +import * as THREE from 'three/webgpu'; +import { pass } from 'three/tsl'; +import { film } from 'three/addons/tsl/display/FilmNode.js'; + +import Stats from 'three/addons/libs/stats.module.js'; +import { FlyControls } from 'three/addons/controls/FlyControls.js'; + +const radius = 6371; +const tilt = 0.41; +const rotationSpeed = 0.02; + +const cloudsScale = 1.005; +const moonScale = 0.23; + +const MARGIN = 0; +let SCREEN_HEIGHT = window.innerHeight - MARGIN * 2; +let SCREEN_WIDTH = window.innerWidth; + +let camera, controls, scene, renderer, stats; +let geometry, meshPlanet, meshClouds, meshMoon; +let dirLight; + +let renderPipeline; + +const textureLoader = new THREE.TextureLoader(); + +let d, dPlanet, dMoon; +const dMoonVec = new THREE.Vector3(); + +const timer = new THREE.Timer(); +timer.connect(document); + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(25, SCREEN_WIDTH / SCREEN_HEIGHT, 50, 1e7); + camera.position.z = radius * 5; + + scene = new THREE.Scene(); + scene.fog = new THREE.FogExp2(0x000000, 0.00000025); + + dirLight = new THREE.DirectionalLight(0xffffff, 3); + dirLight.position.set(-1, 0, 1).normalize(); + scene.add(dirLight); + + const materialNormalMap = new THREE.MeshPhongMaterial({ + specular: 0x7c7c7c, + shininess: 15, + map: textureLoader.load('textures/planets/earth_atmos_2048.jpg'), + specularMap: textureLoader.load('textures/planets/earth_specular_2048.jpg'), + normalMap: textureLoader.load('textures/planets/earth_normal_2048.jpg'), + + // y scale is negated to compensate for normal map handedness. + normalScale: new THREE.Vector2(0.85, -0.85), + }); + materialNormalMap.map.colorSpace = THREE.SRGBColorSpace; + + // planet + + geometry = new THREE.SphereGeometry(radius, 100, 50); + + meshPlanet = new THREE.Mesh(geometry, materialNormalMap); + meshPlanet.rotation.y = 0; + meshPlanet.rotation.z = tilt; + scene.add(meshPlanet); + + // clouds + + const materialClouds = new THREE.MeshLambertMaterial({ + map: textureLoader.load('textures/planets/earth_clouds_1024.png'), + transparent: true, + }); + materialClouds.map.colorSpace = THREE.SRGBColorSpace; + + meshClouds = new THREE.Mesh(geometry, materialClouds); + meshClouds.scale.set(cloudsScale, cloudsScale, cloudsScale); + meshClouds.rotation.z = tilt; + scene.add(meshClouds); + + // moon + + const materialMoon = new THREE.MeshPhongMaterial({ + map: textureLoader.load('textures/planets/moon_1024.jpg'), + }); + materialMoon.map.colorSpace = THREE.SRGBColorSpace; + + meshMoon = new THREE.Mesh(geometry, materialMoon); + meshMoon.position.set(radius * 5, 0, 0); + meshMoon.scale.set(moonScale, moonScale, moonScale); + scene.add(meshMoon); + + // stars + + const r = radius, + starsGeometry = [new THREE.BufferGeometry(), new THREE.BufferGeometry()]; + + const vertices1 = []; + const vertices2 = []; + + const vertex = new THREE.Vector3(); + + for (let i = 0; i < 250; i++) { + vertex.x = Math.random() * 2 - 1; + vertex.y = Math.random() * 2 - 1; + vertex.z = Math.random() * 2 - 1; + vertex.multiplyScalar(r); + + vertices1.push(vertex.x, vertex.y, vertex.z); + } + + for (let i = 0; i < 1500; i++) { + vertex.x = Math.random() * 2 - 1; + vertex.y = Math.random() * 2 - 1; + vertex.z = Math.random() * 2 - 1; + vertex.multiplyScalar(r); + + vertices2.push(vertex.x, vertex.y, vertex.z); + } + + starsGeometry[0].setAttribute('position', new THREE.Float32BufferAttribute(vertices1, 3)); + starsGeometry[1].setAttribute('position', new THREE.Float32BufferAttribute(vertices2, 3)); + + const starsMaterials = [ + new THREE.PointsMaterial({ color: 0x9c9c9c }), + new THREE.PointsMaterial({ color: 0x838383 }), + new THREE.PointsMaterial({ color: 0x5a5a5a }), + ]; + + for (let i = 10; i < 30; i++) { + const stars = new THREE.Points(starsGeometry[i % 2], starsMaterials[i % 3]); + + stars.rotation.x = Math.random() * 6; + stars.rotation.y = Math.random() * 6; + stars.rotation.z = Math.random() * 6; + stars.scale.setScalar(i * 10); + + stars.matrixAutoUpdate = false; + stars.updateMatrix(); + + scene.add(stars); + } + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // + + controls = new FlyControls(camera, renderer.domElement); + + controls.movementSpeed = 1000; + controls.domElement = renderer.domElement; + controls.rollSpeed = Math.PI / 24; + controls.autoForward = false; + controls.dragToLook = false; + + // + + stats = new Stats(); + document.body.appendChild(stats.dom); + + window.addEventListener('resize', onWindowResize); + + // postprocessing + + renderPipeline = new THREE.RenderPipeline(renderer); + + const scenePass = pass(scene, camera); + const scenePassColor = scenePass.getTextureNode(); + + renderPipeline.outputNode = film(scenePassColor); +} + +function onWindowResize() { + SCREEN_HEIGHT = window.innerHeight; + SCREEN_WIDTH = window.innerWidth; + + camera.aspect = SCREEN_WIDTH / SCREEN_HEIGHT; + camera.updateProjectionMatrix(); + + renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); +} + +function animate() { + timer.update(); + + render(); + stats.update(); +} + +function render() { + // rotate the planet and clouds + + const delta = timer.getDelta(); + + meshPlanet.rotation.y += rotationSpeed * delta; + meshClouds.rotation.y += 1.25 * rotationSpeed * delta; + + // slow down as we approach the surface + + dPlanet = camera.position.length(); + + dMoonVec.subVectors(camera.position, meshMoon.position); + dMoon = dMoonVec.length(); + + if (dMoon < dPlanet) { + d = dMoon - radius * moonScale * 1.01; + } else { + d = dPlanet - radius * 1.01; + } + + controls.movementSpeed = 0.33 * d; + controls.update(delta); + + renderPipeline.render(); +} diff --git a/examples-testing/examples/misc_controls_map.ts b/examples-testing/examples/misc_controls_map.ts new file mode 100644 index 000000000..9c7af0cda --- /dev/null +++ b/examples-testing/examples/misc_controls_map.ts @@ -0,0 +1,102 @@ +import * as THREE from 'three'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { MapControls } from 'three/addons/controls/MapControls.js'; + +let camera, controls, scene, renderer; + +init(); +//render(); // remove when using animation loop + +function init() { + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xcccccc); + scene.fog = new THREE.FogExp2(0xcccccc, 0.002); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(0, 200, -200); + + // controls + + controls = new MapControls(camera, renderer.domElement); + + //controls.addEventListener( 'change', render ); // call this only in static scenes (i.e., if there is no animation loop) + + controls.enableDamping = true; // an animation loop is required when either damping or auto-rotation are enabled + controls.dampingFactor = 0.05; + + controls.screenSpacePanning = false; + + controls.minDistance = 100; + controls.maxDistance = 500; + + controls.maxPolarAngle = Math.PI / 2; + + // world + + const geometry = new THREE.BoxGeometry(); + geometry.translate(0, 0.5, 0); + const material = new THREE.MeshPhongMaterial({ color: 0xeeeeee, flatShading: true }); + + const mesh = new THREE.InstancedMesh(geometry, material, 500); + const dummy = new THREE.Object3D(); + + for (let i = 0; i < 500; i++) { + dummy.position.x = Math.random() * 1600 - 800; + dummy.position.y = 0; + dummy.position.z = Math.random() * 1600 - 800; + dummy.scale.x = 20; + dummy.scale.y = Math.random() * 80 + 10; + dummy.scale.z = 20; + + dummy.updateMatrix(); + mesh.setMatrixAt(i, dummy.matrix); + } + + scene.add(mesh); + + // lights + + const dirLight1 = new THREE.DirectionalLight(0xffffff, 3); + dirLight1.position.set(1, 1, 1); + scene.add(dirLight1); + + const dirLight2 = new THREE.DirectionalLight(0x002288, 3); + dirLight2.position.set(-1, -1, -1); + scene.add(dirLight2); + + const ambientLight = new THREE.AmbientLight(0x555555); + scene.add(ambientLight); + + // + + window.addEventListener('resize', onWindowResize); + + const gui = new GUI(); + gui.add(controls, 'zoomToCursor'); + gui.add(controls, 'screenSpacePanning'); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + controls.update(); // only required if controls.enableDamping = true, or if controls.autoRotate = true + + render(); +} + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/misc_controls_orbit.ts b/examples-testing/examples/misc_controls_orbit.ts new file mode 100644 index 000000000..7d2ec2626 --- /dev/null +++ b/examples-testing/examples/misc_controls_orbit.ts @@ -0,0 +1,95 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let camera, controls, scene, renderer; + +init(); +//render(); // remove when using animation loop + +function init() { + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xcccccc); + scene.fog = new THREE.FogExp2(0xcccccc, 0.002); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(400, 200, 0); + + // controls + + controls = new OrbitControls(camera, renderer.domElement); + controls.listenToKeyEvents(window); // optional + + //controls.addEventListener( 'change', render ); // call this only in static scenes (i.e., if there is no animation loop) + + controls.enableDamping = true; // an animation loop is required when either damping or auto-rotation are enabled + controls.dampingFactor = 0.05; + + controls.screenSpacePanning = false; + + controls.minDistance = 100; + controls.maxDistance = 500; + + controls.cursorStyle = 'grab'; + + controls.maxPolarAngle = Math.PI / 2; + + // world + + const geometry = new THREE.ConeGeometry(10, 30, 4, 1); + const material = new THREE.MeshPhongMaterial({ color: 0xffffff, flatShading: true }); + + const mesh = new THREE.InstancedMesh(geometry, material, 500); + const dummy = new THREE.Object3D(); + + for (let i = 0; i < 500; i++) { + dummy.position.x = Math.random() * 1600 - 800; + dummy.position.y = 0; + dummy.position.z = Math.random() * 1600 - 800; + + dummy.updateMatrix(); + mesh.setMatrixAt(i, dummy.matrix); + } + + scene.add(mesh); + + // lights + + const dirLight1 = new THREE.DirectionalLight(0xffffff, 3); + dirLight1.position.set(1, 1, 1); + scene.add(dirLight1); + + const dirLight2 = new THREE.DirectionalLight(0x002288, 3); + dirLight2.position.set(-1, -1, -1); + scene.add(dirLight2); + + const ambientLight = new THREE.AmbientLight(0x555555); + scene.add(ambientLight); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + controls.update(); // only required if controls.enableDamping = true, or if controls.autoRotate = true + + render(); +} + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/misc_controls_pointerlock.ts b/examples-testing/examples/misc_controls_pointerlock.ts new file mode 100644 index 000000000..0b6fcc516 --- /dev/null +++ b/examples-testing/examples/misc_controls_pointerlock.ts @@ -0,0 +1,245 @@ +import * as THREE from 'three'; + +import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js'; + +let camera, scene, renderer, controls; + +const objects = []; + +let raycaster; + +let moveForward = false; +let moveBackward = false; +let moveLeft = false; +let moveRight = false; +let canJump = false; + +let prevTime = performance.now(); +const velocity = new THREE.Vector3(); +const direction = new THREE.Vector3(); +const vertex = new THREE.Vector3(); +const color = new THREE.Color(); + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.y = 10; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xffffff); + scene.fog = new THREE.Fog(0xffffff, 0, 750); + + const light = new THREE.HemisphereLight(0xeeeeff, 0x777788, 2.5); + light.position.set(0.5, 1, 0.75); + scene.add(light); + + controls = new PointerLockControls(camera, document.body); + + const blocker = document.getElementById('blocker'); + const instructions = document.getElementById('instructions'); + + instructions.addEventListener('click', function () { + controls.lock(); + }); + + controls.addEventListener('lock', function () { + instructions.style.display = 'none'; + blocker.style.display = 'none'; + }); + + controls.addEventListener('unlock', function () { + blocker.style.display = 'block'; + instructions.style.display = ''; + }); + + scene.add(controls.object); + + const onKeyDown = function (event) { + switch (event.code) { + case 'ArrowUp': + case 'KeyW': + moveForward = true; + break; + + case 'ArrowLeft': + case 'KeyA': + moveLeft = true; + break; + + case 'ArrowDown': + case 'KeyS': + moveBackward = true; + break; + + case 'ArrowRight': + case 'KeyD': + moveRight = true; + break; + + case 'Space': + if (canJump === true) velocity.y += 350; + canJump = false; + break; + } + }; + + const onKeyUp = function (event) { + switch (event.code) { + case 'ArrowUp': + case 'KeyW': + moveForward = false; + break; + + case 'ArrowLeft': + case 'KeyA': + moveLeft = false; + break; + + case 'ArrowDown': + case 'KeyS': + moveBackward = false; + break; + + case 'ArrowRight': + case 'KeyD': + moveRight = false; + break; + } + }; + + document.addEventListener('keydown', onKeyDown); + document.addEventListener('keyup', onKeyUp); + + raycaster = new THREE.Raycaster(new THREE.Vector3(), new THREE.Vector3(0, -1, 0), 0, 10); + + // floor + + let floorGeometry = new THREE.PlaneGeometry(2000, 2000, 100, 100); + floorGeometry.rotateX(-Math.PI / 2); + + // vertex displacement + + let position = floorGeometry.attributes.position; + + for (let i = 0, l = position.count; i < l; i++) { + vertex.fromBufferAttribute(position, i); + + vertex.x += Math.random() * 20 - 10; + vertex.y += Math.random() * 2; + vertex.z += Math.random() * 20 - 10; + + position.setXYZ(i, vertex.x, vertex.y, vertex.z); + } + + floorGeometry = floorGeometry.toNonIndexed(); // ensure each face has unique vertices + + position = floorGeometry.attributes.position; + const colorsFloor = []; + + for (let i = 0, l = position.count; i < l; i++) { + color.setHSL(Math.random() * 0.3 + 0.5, 0.75, Math.random() * 0.25 + 0.75, THREE.SRGBColorSpace); + colorsFloor.push(color.r, color.g, color.b); + } + + floorGeometry.setAttribute('color', new THREE.Float32BufferAttribute(colorsFloor, 3)); + + const floorMaterial = new THREE.MeshBasicMaterial({ vertexColors: true }); + + const floor = new THREE.Mesh(floorGeometry, floorMaterial); + scene.add(floor); + + // objects + + const boxGeometry = new THREE.BoxGeometry(20, 20, 20).toNonIndexed(); + + position = boxGeometry.attributes.position; + const colorsBox = []; + + for (let i = 0, l = position.count; i < l; i++) { + color.setHSL(Math.random() * 0.3 + 0.5, 0.75, Math.random() * 0.25 + 0.75, THREE.SRGBColorSpace); + colorsBox.push(color.r, color.g, color.b); + } + + boxGeometry.setAttribute('color', new THREE.Float32BufferAttribute(colorsBox, 3)); + + for (let i = 0; i < 500; i++) { + const boxMaterial = new THREE.MeshPhongMaterial({ specular: 0xffffff, flatShading: true, vertexColors: true }); + boxMaterial.color.setHSL(Math.random() * 0.2 + 0.5, 0.75, Math.random() * 0.25 + 0.75, THREE.SRGBColorSpace); + + const box = new THREE.Mesh(boxGeometry, boxMaterial); + box.position.x = Math.floor(Math.random() * 20 - 10) * 20; + box.position.y = Math.floor(Math.random() * 20) * 20 + 10; + box.position.z = Math.floor(Math.random() * 20 - 10) * 20; + + scene.add(box); + objects.push(box); + } + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + const time = performance.now(); + + if (controls.isLocked === true) { + raycaster.ray.origin.copy(controls.object.position); + raycaster.ray.origin.y -= 10; + + const intersections = raycaster.intersectObjects(objects, false); + + const onObject = intersections.length > 0; + + const delta = (time - prevTime) / 1000; + + velocity.x -= velocity.x * 10.0 * delta; + velocity.z -= velocity.z * 10.0 * delta; + + velocity.y -= 9.8 * 100.0 * delta; // 100.0 = mass + + direction.z = Number(moveForward) - Number(moveBackward); + direction.x = Number(moveRight) - Number(moveLeft); + direction.normalize(); // this ensures consistent movements in all directions + + if (moveForward || moveBackward) velocity.z -= direction.z * 400.0 * delta; + if (moveLeft || moveRight) velocity.x -= direction.x * 400.0 * delta; + + if (onObject === true) { + velocity.y = Math.max(0, velocity.y); + canJump = true; + } + + controls.moveRight(-velocity.x * delta); + controls.moveForward(-velocity.z * delta); + + controls.object.position.y += velocity.y * delta; // new behavior + + if (controls.object.position.y < 10) { + velocity.y = 0; + controls.object.position.y = 10; + + canJump = true; + } + } + + prevTime = time; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/misc_controls_trackball.ts b/examples-testing/examples/misc_controls_trackball.ts new file mode 100644 index 000000000..c2512a352 --- /dev/null +++ b/examples-testing/examples/misc_controls_trackball.ts @@ -0,0 +1,138 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { TrackballControls } from 'three/addons/controls/TrackballControls.js'; + +let perspectiveCamera, orthographicCamera, controls, scene, renderer, stats; + +const params = { + orthographicCamera: false, +}; + +const frustumSize = 400; + +init(); + +function init() { + const aspect = window.innerWidth / window.innerHeight; + + perspectiveCamera = new THREE.PerspectiveCamera(60, aspect, 1, 1000); + perspectiveCamera.position.z = 500; + + orthographicCamera = new THREE.OrthographicCamera( + (frustumSize * aspect) / -2, + (frustumSize * aspect) / 2, + frustumSize / 2, + frustumSize / -2, + 1, + 1000, + ); + orthographicCamera.position.z = 500; + + // world + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xcccccc); + scene.fog = new THREE.FogExp2(0xcccccc, 0.002); + + const geometry = new THREE.ConeGeometry(10, 30, 4, 1); + const material = new THREE.MeshPhongMaterial({ color: 0xffffff, flatShading: true }); + + const mesh = new THREE.InstancedMesh(geometry, material, 500); + const dummy = new THREE.Object3D(); + + for (let i = 0; i < 500; i++) { + dummy.position.x = (Math.random() - 0.5) * 1000; + dummy.position.y = (Math.random() - 0.5) * 1000; + dummy.position.z = (Math.random() - 0.5) * 1000; + + dummy.updateMatrix(); + mesh.setMatrixAt(i, dummy.matrix); + } + + scene.add(mesh); + + // lights + + const dirLight1 = new THREE.DirectionalLight(0xffffff, 3); + dirLight1.position.set(1, 1, 1); + scene.add(dirLight1); + + const dirLight2 = new THREE.DirectionalLight(0x002288, 3); + dirLight2.position.set(-1, -1, -1); + scene.add(dirLight2); + + const ambientLight = new THREE.AmbientLight(0x555555); + scene.add(ambientLight); + + // renderer + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + stats = new Stats(); + document.body.appendChild(stats.dom); + + // + + const gui = new GUI(); + gui.add(params, 'orthographicCamera') + .name('use orthographic') + .onChange(function (value) { + controls.dispose(); + + createControls(value ? orthographicCamera : perspectiveCamera); + }); + + // + + window.addEventListener('resize', onWindowResize); + + createControls(perspectiveCamera); +} + +function createControls(camera) { + controls = new TrackballControls(camera, renderer.domElement); + + controls.rotateSpeed = 1.0; + controls.zoomSpeed = 1.2; + controls.panSpeed = 0.8; + + controls.keys = ['KeyA', 'KeyS', 'KeyD']; +} + +function onWindowResize() { + const aspect = window.innerWidth / window.innerHeight; + + perspectiveCamera.aspect = aspect; + perspectiveCamera.updateProjectionMatrix(); + + orthographicCamera.left = (-frustumSize * aspect) / 2; + orthographicCamera.right = (frustumSize * aspect) / 2; + orthographicCamera.top = frustumSize / 2; + orthographicCamera.bottom = -frustumSize / 2; + orthographicCamera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + controls.handleResize(); +} + +function animate() { + controls.update(); + + render(); + + stats.update(); +} + +function render() { + const camera = params.orthographicCamera ? orthographicCamera : perspectiveCamera; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/misc_controls_transform.ts b/examples-testing/examples/misc_controls_transform.ts new file mode 100644 index 000000000..6f7793d33 --- /dev/null +++ b/examples-testing/examples/misc_controls_transform.ts @@ -0,0 +1,182 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { TransformControls } from 'three/addons/controls/TransformControls.js'; + +let cameraPersp, cameraOrtho, currentCamera; +let scene, renderer, control, orbit; + +init(); +render(); + +function init() { + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + document.body.appendChild(renderer.domElement); + + const aspect = window.innerWidth / window.innerHeight; + + const frustumSize = 5; + + cameraPersp = new THREE.PerspectiveCamera(50, aspect, 0.1, 100); + cameraOrtho = new THREE.OrthographicCamera( + -frustumSize * aspect, + frustumSize * aspect, + frustumSize, + -frustumSize, + 0.1, + 100, + ); + currentCamera = cameraPersp; + + currentCamera.position.set(5, 2.5, 5); + + scene = new THREE.Scene(); + scene.add(new THREE.GridHelper(5, 10, 0x888888, 0x444444)); + + const ambientLight = new THREE.AmbientLight(0xffffff); + scene.add(ambientLight); + + const light = new THREE.DirectionalLight(0xffffff, 4); + light.position.set(1, 1, 1); + scene.add(light); + + const texture = new THREE.TextureLoader().load('textures/crate.gif', render); + texture.colorSpace = THREE.SRGBColorSpace; + texture.anisotropy = renderer.capabilities.getMaxAnisotropy(); + + const geometry = new THREE.BoxGeometry(); + const material = new THREE.MeshLambertMaterial({ map: texture }); + + orbit = new OrbitControls(currentCamera, renderer.domElement); + orbit.update(); + orbit.addEventListener('change', render); + + control = new TransformControls(currentCamera, renderer.domElement); + control.addEventListener('change', render); + control.addEventListener('dragging-changed', function (event) { + orbit.enabled = !event.value; + }); + + const mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + control.attach(mesh); + + const gizmo = control.getHelper(); + scene.add(gizmo); + + window.addEventListener('resize', onWindowResize); + + window.addEventListener('keydown', function (event) { + switch (event.key) { + case 'q': + control.setSpace(control.space === 'local' ? 'world' : 'local'); + break; + + case 'Shift': + control.setTranslationSnap(1); + control.setRotationSnap(THREE.MathUtils.degToRad(15)); + control.setScaleSnap(0.25); + break; + + case 'w': + control.setMode('translate'); + break; + + case 'e': + control.setMode('rotate'); + break; + + case 'r': + control.setMode('scale'); + break; + + case 'c': + const position = currentCamera.position.clone(); + + currentCamera = currentCamera.isPerspectiveCamera ? cameraOrtho : cameraPersp; + currentCamera.position.copy(position); + + orbit.object = currentCamera; + control.camera = currentCamera; + + currentCamera.lookAt(orbit.target.x, orbit.target.y, orbit.target.z); + onWindowResize(); + break; + + case 'v': + const randomFoV = Math.random() + 0.1; + const randomZoom = Math.random() + 0.1; + + cameraPersp.fov = randomFoV * 160; + cameraOrtho.bottom = -randomFoV * 500; + cameraOrtho.top = randomFoV * 500; + + cameraPersp.zoom = randomZoom * 5; + cameraOrtho.zoom = randomZoom * 5; + onWindowResize(); + break; + + case '+': + case '=': + control.setSize(control.size + 0.1); + break; + + case '-': + case '_': + control.setSize(Math.max(control.size - 0.1, 0.1)); + break; + + case 'x': + control.showX = !control.showX; + break; + + case 'y': + control.showY = !control.showY; + break; + + case 'z': + control.showZ = !control.showZ; + break; + + case ' ': + control.enabled = !control.enabled; + break; + + case 'Escape': + control.reset(); + break; + } + }); + + window.addEventListener('keyup', function (event) { + switch (event.key) { + case 'Shift': + control.setTranslationSnap(null); + control.setRotationSnap(null); + control.setScaleSnap(null); + break; + } + }); +} + +function onWindowResize() { + const aspect = window.innerWidth / window.innerHeight; + + cameraPersp.aspect = aspect; + cameraPersp.updateProjectionMatrix(); + + cameraOrtho.left = cameraOrtho.bottom * aspect; + cameraOrtho.right = cameraOrtho.top * aspect; + cameraOrtho.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + render(); +} + +function render() { + renderer.render(scene, currentCamera); +} diff --git a/examples-testing/examples/misc_exporter_draco.ts b/examples-testing/examples/misc_exporter_draco.ts new file mode 100644 index 000000000..40a62fb18 --- /dev/null +++ b/examples-testing/examples/misc_exporter_draco.ts @@ -0,0 +1,117 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { DRACOExporter } from 'three/addons/exporters/DRACOExporter.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let scene, camera, renderer, exporter, mesh; + +const params = { + export: exportFile, +}; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(4, 2, 4); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xa0a0a0); + scene.fog = new THREE.Fog(0xa0a0a0, 4, 20); + + exporter = new DRACOExporter(); + + // + + const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444, 3); + hemiLight.position.set(0, 20, 0); + scene.add(hemiLight); + + const directionalLight = new THREE.DirectionalLight(0xffffff, 3); + directionalLight.position.set(0, 20, 10); + directionalLight.castShadow = true; + directionalLight.shadow.camera.top = 2; + directionalLight.shadow.camera.bottom = -2; + directionalLight.shadow.camera.left = -2; + directionalLight.shadow.camera.right = 2; + scene.add(directionalLight); + + // ground + + const ground = new THREE.Mesh( + new THREE.PlaneGeometry(40, 40), + new THREE.MeshPhongMaterial({ color: 0xbbbbbb, depthWrite: false }), + ); + ground.rotation.x = -Math.PI / 2; + ground.receiveShadow = true; + scene.add(ground); + + const grid = new THREE.GridHelper(40, 20, 0x000000, 0x000000); + grid.material.opacity = 0.2; + grid.material.transparent = true; + scene.add(grid); + + // export mesh + + const geometry = new THREE.TorusKnotGeometry(0.75, 0.2, 200, 30); + const material = new THREE.MeshPhongMaterial({ color: 0x00ff00 }); + mesh = new THREE.Mesh(geometry, material); + mesh.castShadow = true; + mesh.position.y = 1.5; + scene.add(mesh); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.shadowMap.enabled = true; + document.body.appendChild(renderer.domElement); + + // + + const controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, 1.5, 0); + controls.update(); + + // + + window.addEventListener('resize', onWindowResize); + + const gui = new GUI(); + + gui.add(params, 'export').name('Export DRC'); + gui.open(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + renderer.render(scene, camera); +} + +function exportFile() { + const result = exporter.parse(mesh); + saveArrayBuffer(result, 'file.drc'); +} + +const link = document.createElement('a'); +link.style.display = 'none'; +document.body.appendChild(link); + +function save(blob, filename) { + link.href = URL.createObjectURL(blob); + link.download = filename; + link.click(); +} + +function saveArrayBuffer(buffer, filename) { + save(new Blob([buffer], { type: 'application/octet-stream' }), filename); +} diff --git a/examples-testing/examples/misc_exporter_exr.ts b/examples-testing/examples/misc_exporter_exr.ts new file mode 100644 index 000000000..014ea58a0 --- /dev/null +++ b/examples-testing/examples/misc_exporter_exr.ts @@ -0,0 +1,158 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { EXRExporter, ZIP_COMPRESSION, ZIPS_COMPRESSION, NO_COMPRESSION } from 'three/addons/exporters/EXRExporter.js'; +import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let scene, camera, renderer, exporter, mesh, controls, renderTarget, dataTexture; + +const params = { + target: 'pmrem', + type: 'HalfFloatType', + compression: 'ZIP', + export: exportFile, +}; + +init(); + +function init() { + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(10, 0, 0); + + scene = new THREE.Scene(); + + exporter = new EXRExporter(); + const hdrLoader = new HDRLoader(); + + // + + const pmremGenerator = new THREE.PMREMGenerator(renderer); + pmremGenerator.compileEquirectangularShader(); + + hdrLoader.load('textures/equirectangular/san_giuseppe_bridge_2k.hdr', function (texture) { + texture.mapping = THREE.EquirectangularReflectionMapping; + + renderTarget = pmremGenerator.fromEquirectangular(texture); + scene.background = renderTarget.texture; + }); + + createDataTexture(); + + // + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + controls.rotateSpeed = -0.25; // negative, to track mouse pointer + + // + + window.addEventListener('resize', onWindowResize); + + const gui = new GUI(); + + const input = gui.addFolder('Input'); + input.add(params, 'target').options(['pmrem', 'data-texture']).onChange(swapScene); + + const options = gui.addFolder('Output Options'); + options.add(params, 'type').options(['FloatType', 'HalfFloatType']); + options.add(params, 'compression').options(['ZIP', 'ZIPS', 'NONE']); + + gui.add(params, 'export').name('Export EXR'); + gui.open(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + controls.update(); + renderer.render(scene, camera); +} + +function createDataTexture() { + const normal = new THREE.Vector3(); + const coord = new THREE.Vector2(); + const size = 800, + radius = 320, + factor = (Math.PI * 0.5) / radius; + const data = new Float32Array(4 * size * size); + + for (let i = 0; i < size; i++) { + for (let j = 0; j < size; j++) { + const idx = i * size * 4 + j * 4; + coord.set(j, i).subScalar(size / 2); + + if (coord.length() < radius) + normal.set(Math.sin(coord.x * factor), Math.sin(coord.y * factor), Math.cos(coord.x * factor)); + else normal.set(0, 0, 1); + + data[idx + 0] = 0.5 + 0.5 * normal.x; + data[idx + 1] = 0.5 + 0.5 * normal.y; + data[idx + 2] = 0.5 + 0.5 * normal.z; + data[idx + 3] = 1; + } + } + + dataTexture = new THREE.DataTexture(data, size, size, THREE.RGBAFormat, THREE.FloatType); + dataTexture.needsUpdate = true; + + const material = new THREE.MeshBasicMaterial({ map: dataTexture }); + const quad = new THREE.PlaneGeometry(50, 50); + mesh = new THREE.Mesh(quad, material); + mesh.visible = false; + + scene.add(mesh); +} + +function swapScene() { + if (params.target == 'pmrem') { + camera.position.set(10, 0, 0); + controls.enabled = true; + scene.background = renderTarget.texture; + mesh.visible = false; + } else { + camera.position.set(0, 0, 70); + controls.enabled = false; + scene.background = new THREE.Color(0, 0, 0); + mesh.visible = true; + } +} + +async function exportFile() { + let result, exportType, exportCompression; + + if (params.type == 'HalfFloatType') exportType = THREE.HalfFloatType; + else exportType = THREE.FloatType; + + if (params.compression == 'ZIP') exportCompression = ZIP_COMPRESSION; + else if (params.compression == 'ZIPS') exportCompression = ZIPS_COMPRESSION; + else exportCompression = NO_COMPRESSION; + + if (params.target == 'pmrem') + result = await exporter.parse(renderer, renderTarget, { type: exportType, compression: exportCompression }); + else result = await exporter.parse(dataTexture, { type: exportType, compression: exportCompression }); + + saveArrayBuffer(result, params.target + '.exr'); +} + +function saveArrayBuffer(buffer, filename) { + const blob = new Blob([buffer], { type: 'image/x-exr' }); + const link = document.createElement('a'); + + link.href = URL.createObjectURL(blob); + link.download = filename; + link.click(); +} diff --git a/examples-testing/examples/misc_exporter_gltf.ts b/examples-testing/examples/misc_exporter_gltf.ts new file mode 100644 index 000000000..f6fe03450 --- /dev/null +++ b/examples-testing/examples/misc_exporter_gltf.ts @@ -0,0 +1,534 @@ +import * as THREE from 'three'; + +import { GLTFExporter } from 'three/addons/exporters/GLTFExporter.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js'; +import { MeshoptDecoder } from 'three/addons/libs/meshopt_decoder.module.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import * as TextureUtils from 'three/addons/utils/WebGLTextureUtils.js'; + +function exportGLTF(input) { + const gltfExporter = new GLTFExporter().setTextureUtils(TextureUtils); + + const options = { + trs: params.trs, + onlyVisible: params.onlyVisible, + binary: params.binary, + maxTextureSize: params.maxTextureSize, + }; + gltfExporter.parse( + input, + function (result) { + if (result instanceof ArrayBuffer) { + saveArrayBuffer(result, 'scene.glb'); + } else { + const output = JSON.stringify(result, null, 2); + console.log(output); + saveString(output, 'scene.gltf'); + } + }, + function (error) { + console.log('An error happened during parsing', error); + }, + options, + ); +} + +const link = document.createElement('a'); +link.style.display = 'none'; +document.body.appendChild(link); // Firefox workaround, see #6594 + +function save(blob, filename) { + link.href = URL.createObjectURL(blob); + link.download = filename; + link.click(); + + // URL.revokeObjectURL( url ); breaks Firefox... +} + +function saveString(text, filename) { + save(new Blob([text], { type: 'text/plain' }), filename); +} + +function saveArrayBuffer(buffer, filename) { + save(new Blob([buffer], { type: 'application/octet-stream' }), filename); +} + +let container; + +let camera, object, object2, material, geometry, scene1, scene2, renderer; +let gridHelper, sphere, model, coffeemat, webpBox; + +const params = { + trs: false, + onlyVisible: true, + binary: false, + maxTextureSize: 4096, + exportScene1: exportScene1, + exportScenes: exportScenes, + exportSphere: exportSphere, + exportModel: exportModel, + exportObjects: exportObjects, + exportSceneObject: exportSceneObject, + exportCompressedObject: exportCompressedObject, + exportWebPModel: exportWebPModel, +}; + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + // Make linear gradient texture + + const data = new Uint8ClampedArray(100 * 100 * 4); + + for (let y = 0; y < 100; y++) { + for (let x = 0; x < 100; x++) { + const stride = 4 * (100 * y + x); + + data[stride] = Math.round((255 * y) / 99); + data[stride + 1] = Math.round(255 - (255 * y) / 99); + data[stride + 2] = 0; + data[stride + 3] = 255; + } + } + + const gradientTexture = new THREE.DataTexture(data, 100, 100, THREE.RGBAFormat); + gradientTexture.minFilter = THREE.LinearFilter; + gradientTexture.magFilter = THREE.LinearFilter; + gradientTexture.needsUpdate = true; + + scene1 = new THREE.Scene(); + scene1.name = 'Scene1'; + + // --------------------------------------------------------------------- + // Perspective Camera + // --------------------------------------------------------------------- + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000); + camera.position.set(600, 400, 0); + + camera.name = 'PerspectiveCamera'; + scene1.add(camera); + + // --------------------------------------------------------------------- + // Ambient light + // --------------------------------------------------------------------- + const ambientLight = new THREE.AmbientLight(0xcccccc); + ambientLight.name = 'AmbientLight'; + scene1.add(ambientLight); + + // --------------------------------------------------------------------- + // DirectLight + // --------------------------------------------------------------------- + const dirLight = new THREE.DirectionalLight(0xffffff, 3); + dirLight.target.position.set(0, 0, -1); + dirLight.add(dirLight.target); + dirLight.lookAt(-1, -1, 0); + dirLight.name = 'DirectionalLight'; + scene1.add(dirLight); + + // --------------------------------------------------------------------- + // Grid + // --------------------------------------------------------------------- + gridHelper = new THREE.GridHelper(2000, 20, 0xc1c1c1, 0x8d8d8d); + gridHelper.position.y = -50; + gridHelper.name = 'Grid'; + scene1.add(gridHelper); + + // --------------------------------------------------------------------- + // Axes + // --------------------------------------------------------------------- + const axes = new THREE.AxesHelper(500); + axes.name = 'AxesHelper'; + scene1.add(axes); + + // --------------------------------------------------------------------- + // Simple geometry with basic material + // --------------------------------------------------------------------- + // Icosahedron + const mapGrid = new THREE.TextureLoader().load('textures/uv_grid_opengl.jpg'); + mapGrid.wrapS = mapGrid.wrapT = THREE.RepeatWrapping; + mapGrid.colorSpace = THREE.SRGBColorSpace; + material = new THREE.MeshBasicMaterial({ + color: 0xffffff, + map: mapGrid, + }); + + object = new THREE.Mesh(new THREE.IcosahedronGeometry(75, 0), material); + object.position.set(-200, 0, 200); + object.name = 'Icosahedron'; + scene1.add(object); + + // Octahedron + material = new THREE.MeshBasicMaterial({ + color: 0x0000ff, + wireframe: true, + }); + object = new THREE.Mesh(new THREE.OctahedronGeometry(75, 1), material); + object.position.set(0, 0, 200); + object.name = 'Octahedron'; + scene1.add(object); + + // Tetrahedron + material = new THREE.MeshBasicMaterial({ + color: 0xff0000, + transparent: true, + opacity: 0.5, + }); + + object = new THREE.Mesh(new THREE.TetrahedronGeometry(75, 0), material); + object.position.set(200, 0, 200); + object.name = 'Tetrahedron'; + scene1.add(object); + + // --------------------------------------------------------------------- + // Buffered geometry primitives + // --------------------------------------------------------------------- + // Sphere + material = new THREE.MeshStandardMaterial({ + color: 0xffff00, + metalness: 0.5, + roughness: 1.0, + flatShading: true, + }); + material.map = gradientTexture; + material.bumpMap = mapGrid; + sphere = new THREE.Mesh(new THREE.SphereGeometry(70, 10, 10), material); + sphere.position.set(0, 0, 0); + sphere.name = 'Sphere'; + scene1.add(sphere); + + // Cylinder + material = new THREE.MeshStandardMaterial({ + color: 0xff00ff, + flatShading: true, + }); + object = new THREE.Mesh(new THREE.CylinderGeometry(10, 80, 100), material); + object.position.set(200, 0, 0); + object.name = 'Cylinder'; + scene1.add(object); + + // TorusKnot + material = new THREE.MeshStandardMaterial({ + color: 0xff0000, + roughness: 1, + }); + object = new THREE.Mesh(new THREE.TorusKnotGeometry(50, 15, 40, 10), material); + object.position.set(-200, 0, 0); + object.name = 'Cylinder'; + scene1.add(object); + + // --------------------------------------------------------------------- + // Hierarchy + // --------------------------------------------------------------------- + const mapWood = new THREE.TextureLoader().load('textures/hardwood2_diffuse.jpg'); + material = new THREE.MeshStandardMaterial({ map: mapWood, side: THREE.DoubleSide }); + + object = new THREE.Mesh(new THREE.BoxGeometry(40, 100, 100), material); + object.position.set(-200, 0, 400); + object.name = 'Cube'; + scene1.add(object); + + object2 = new THREE.Mesh(new THREE.BoxGeometry(40, 40, 40, 2, 2, 2), material); + object2.position.set(0, 0, 50); + object2.rotation.set(0, 45, 0); + object2.name = 'SubCube'; + object.add(object2); + + // --------------------------------------------------------------------- + // Groups + // --------------------------------------------------------------------- + const group1 = new THREE.Group(); + group1.name = 'Group'; + scene1.add(group1); + + const group2 = new THREE.Group(); + group2.name = 'subGroup'; + group2.position.set(0, 50, 0); + group1.add(group2); + + object2 = new THREE.Mesh(new THREE.BoxGeometry(30, 30, 30), material); + object2.name = 'Cube in group'; + object2.position.set(0, 0, 400); + group2.add(object2); + + // --------------------------------------------------------------------- + // THREE.Line Strip + // --------------------------------------------------------------------- + geometry = new THREE.BufferGeometry(); + let numPoints = 100; + let positions = new Float32Array(numPoints * 3); + + for (let i = 0; i < numPoints; i++) { + positions[i * 3] = i; + positions[i * 3 + 1] = Math.sin(i / 2) * 20; + positions[i * 3 + 2] = 0; + } + + geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); + object = new THREE.Line(geometry, new THREE.LineBasicMaterial({ color: 0xffff00 })); + object.position.set(-50, 0, -200); + scene1.add(object); + + // --------------------------------------------------------------------- + // THREE.Line Loop + // --------------------------------------------------------------------- + geometry = new THREE.BufferGeometry(); + numPoints = 5; + const radius = 70; + positions = new Float32Array(numPoints * 3); + + for (let i = 0; i < numPoints; i++) { + const s = (i * Math.PI * 2) / numPoints; + positions[i * 3] = radius * Math.sin(s); + positions[i * 3 + 1] = radius * Math.cos(s); + positions[i * 3 + 2] = 0; + } + + geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); + object = new THREE.LineLoop(geometry, new THREE.LineBasicMaterial({ color: 0xffff00 })); + object.position.set(0, 0, -200); + + scene1.add(object); + + // --------------------------------------------------------------------- + // THREE.Points + // --------------------------------------------------------------------- + numPoints = 100; + const pointsArray = new Float32Array(numPoints * 3); + for (let i = 0; i < numPoints; i++) { + pointsArray[3 * i] = -50 + Math.random() * 100; + pointsArray[3 * i + 1] = Math.random() * 100; + pointsArray[3 * i + 2] = -50 + Math.random() * 100; + } + + const pointsGeo = new THREE.BufferGeometry(); + pointsGeo.setAttribute('position', new THREE.BufferAttribute(pointsArray, 3)); + + const pointsMaterial = new THREE.PointsMaterial({ color: 0xffff00, size: 5 }); + const pointCloud = new THREE.Points(pointsGeo, pointsMaterial); + pointCloud.name = 'Points'; + pointCloud.position.set(-200, 0, -200); + scene1.add(pointCloud); + + // --------------------------------------------------------------------- + // Ortho camera + // --------------------------------------------------------------------- + + const height = 1000; // frustum height + const aspect = window.innerWidth / window.innerHeight; + + const cameraOrtho = new THREE.OrthographicCamera(-height * aspect, height * aspect, height, -height, 0, 2000); + cameraOrtho.position.set(600, 400, 0); + cameraOrtho.lookAt(0, 0, 0); + scene1.add(cameraOrtho); + cameraOrtho.name = 'OrthographicCamera'; + + material = new THREE.MeshLambertMaterial({ + color: 0xffff00, + side: THREE.DoubleSide, + }); + + object = new THREE.Mesh(new THREE.CircleGeometry(50, 20, 0, Math.PI * 2), material); + object.position.set(200, 0, -400); + scene1.add(object); + + object = new THREE.Mesh(new THREE.RingGeometry(10, 50, 20, 5, 0, Math.PI * 2), material); + object.position.set(0, 0, -400); + scene1.add(object); + + object = new THREE.Mesh(new THREE.CylinderGeometry(25, 75, 100, 40, 5), material); + object.position.set(-200, 0, -400); + scene1.add(object); + + // + const points = []; + + for (let i = 0; i < 50; i++) { + points.push(new THREE.Vector2(Math.sin(i * 0.2) * Math.sin(i * 0.1) * 15 + 50, (i - 5) * 2)); + } + + object = new THREE.Mesh(new THREE.LatheGeometry(points, 20), material); + object.position.set(200, 0, 400); + scene1.add(object); + + // --------------------------------------------------------------------- + // Big red box hidden just for testing `onlyVisible` option + // --------------------------------------------------------------------- + material = new THREE.MeshBasicMaterial({ + color: 0xff0000, + }); + object = new THREE.Mesh(new THREE.BoxGeometry(200, 200, 200), material); + object.position.set(0, 0, 0); + object.name = 'CubeHidden'; + object.visible = false; + scene1.add(object); + + // --------------------------------------------------------------------- + // Model requiring KHR_mesh_quantization + // --------------------------------------------------------------------- + const loader = new GLTFLoader(); + loader.load('models/gltf/ShaderBall.glb', function (gltf) { + model = gltf.scene; + model.scale.setScalar(50); + model.position.set(200, -40, -200); + scene1.add(model); + }); + + // --------------------------------------------------------------------- + // Model requiring KHR_mesh_quantization + // --------------------------------------------------------------------- + + material = new THREE.MeshBasicMaterial({ + color: 0xffffff, + }); + object = new THREE.InstancedMesh(new THREE.BoxGeometry(10, 10, 10, 2, 2, 2), material, 50); + const matrix = new THREE.Matrix4(); + const color = new THREE.Color(); + for (let i = 0; i < 50; i++) { + matrix.setPosition(Math.random() * 100 - 50, Math.random() * 100 - 50, Math.random() * 100 - 50); + object.setMatrixAt(i, matrix); + object.setColorAt(i, color.setHSL(i / 50, 1, 0.5)); + } + + object.position.set(400, 0, 200); + scene1.add(object); + + // --------------------------------------------------------------------- + // 2nd THREE.Scene + // --------------------------------------------------------------------- + scene2 = new THREE.Scene(); + object = new THREE.Mesh(new THREE.BoxGeometry(100, 100, 100), material); + object.position.set(0, 0, 0); + object.name = 'Cube2ndScene'; + scene2.name = 'Scene2'; + scene2.add(object); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = 1; + + container.appendChild(renderer.domElement); + + // + + window.addEventListener('resize', onWindowResize); + + // --------------------------------------------------------------------- + // Exporting compressed textures and meshes (KTX2 / Draco / Meshopt) + // --------------------------------------------------------------------- + const ktx2Loader = new KTX2Loader().setTranscoderPath('jsm/libs/basis/').detectSupport(renderer); + + const gltfLoader = new GLTFLoader().setPath('models/gltf/'); + gltfLoader.setKTX2Loader(ktx2Loader); + gltfLoader.setMeshoptDecoder(MeshoptDecoder); + gltfLoader.load('coffeemat.glb', function (gltf) { + gltf.scene.position.x = 400; + gltf.scene.position.z = -200; + + scene1.add(gltf.scene); + + coffeemat = gltf.scene; + }); + + // --------------------------------------------------------------------- + // Box with WebP texture (EXT_texture_webp) + // --------------------------------------------------------------------- + const canvas = document.createElement('canvas'); + canvas.width = 64; + canvas.height = 64; + const ctx = canvas.getContext('2d'); + ctx.fillStyle = '#005BBB'; + ctx.fillRect(0, 0, 64, 64); + ctx.fillStyle = '#FFD500'; + ctx.fillRect(16, 16, 32, 32); + + const webpTexture = new THREE.CanvasTexture(canvas); + webpTexture.userData.mimeType = 'image/webp'; + webpTexture.colorSpace = THREE.SRGBColorSpace; + + webpBox = new THREE.Mesh(new THREE.BoxGeometry(100, 100, 100), new THREE.MeshBasicMaterial({ map: webpTexture })); + webpBox.position.set(400, 0, 0); + webpBox.name = 'WebPBox'; + scene1.add(webpBox); + + // + + const gui = new GUI(); + + let h = gui.addFolder('Settings'); + h.add(params, 'trs').name('Use TRS'); + h.add(params, 'onlyVisible').name('Only Visible Objects'); + h.add(params, 'binary').name('Binary (GLB)'); + h.add(params, 'maxTextureSize', 2, 8192).name('Max Texture Size').step(1); + + h = gui.addFolder('Export'); + h.add(params, 'exportScene1').name('Export Scene 1'); + h.add(params, 'exportScenes').name('Export Scene 1 and 2'); + h.add(params, 'exportSphere').name('Export Sphere'); + h.add(params, 'exportModel').name('Export Model'); + h.add(params, 'exportObjects').name('Export Sphere With Grid'); + h.add(params, 'exportSceneObject').name('Export Scene 1 and Object'); + h.add(params, 'exportCompressedObject').name('Export Coffeemat (from compressed data)'); + h.add(params, 'exportWebPModel').name('Export WebP Model (EXT_texture_webp)'); + + gui.open(); +} + +function exportScene1() { + exportGLTF(scene1); +} + +function exportScenes() { + exportGLTF([scene1, scene2]); +} + +function exportSphere() { + exportGLTF(sphere); +} + +function exportModel() { + exportGLTF(model); +} + +function exportObjects() { + exportGLTF([sphere, gridHelper]); +} + +function exportSceneObject() { + exportGLTF([scene1, gridHelper]); +} + +function exportCompressedObject() { + exportGLTF([coffeemat]); +} + +function exportWebPModel() { + exportGLTF(webpBox); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + const timer = Date.now() * 0.0001; + + camera.position.x = Math.cos(timer) * 800; + camera.position.z = Math.sin(timer) * 800; + + camera.lookAt(scene1.position); + renderer.render(scene1, camera); +} diff --git a/examples-testing/examples/misc_exporter_ktx2.ts b/examples-testing/examples/misc_exporter_ktx2.ts new file mode 100644 index 000000000..c96889a24 --- /dev/null +++ b/examples-testing/examples/misc_exporter_ktx2.ts @@ -0,0 +1,145 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { KTX2Exporter } from 'three/addons/exporters/KTX2Exporter.js'; +import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let scene, camera, renderer, exporter, mesh, controls, renderTarget, dataTexture; + +const params = { + target: 'pmrem', + export: exportFile, +}; + +init(); + +function init() { + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.toneMapping = THREE.AgXToneMapping; + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(10, 0, 0); + + scene = new THREE.Scene(); + + exporter = new KTX2Exporter(); + const hdrLoader = new HDRLoader(); + + // + + const pmremGenerator = new THREE.PMREMGenerator(renderer); + pmremGenerator.compileEquirectangularShader(); + + hdrLoader.load('textures/equirectangular/venice_sunset_1k.hdr', function (texture) { + texture.mapping = THREE.EquirectangularReflectionMapping; + + renderTarget = pmremGenerator.fromEquirectangular(texture); + scene.background = renderTarget.texture; + }); + + createDataTexture(); + + // + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + controls.rotateSpeed = -0.25; // negative, to track mouse pointer + + // + + window.addEventListener('resize', onWindowResize); + + const gui = new GUI(); + + gui.add(params, 'target').options(['pmrem', 'data-texture']).onChange(swapScene); + gui.add(params, 'export').name('Export KTX2'); + gui.open(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + controls.update(); + renderer.render(scene, camera); +} + +function createDataTexture() { + const normal = new THREE.Vector3(); + const coord = new THREE.Vector2(); + const size = 800, + radius = 320, + factor = (Math.PI * 0.5) / radius; + const data = new Float32Array(4 * size * size); + + for (let i = 0; i < size; i++) { + for (let j = 0; j < size; j++) { + const idx = i * size * 4 + j * 4; + coord.set(j, i).subScalar(size / 2); + + if (coord.length() < radius) + normal.set(Math.sin(coord.x * factor), Math.sin(coord.y * factor), Math.cos(coord.x * factor)); + else normal.set(0, 0, 1); + + data[idx + 0] = 0.5 + 0.5 * normal.x; + data[idx + 1] = 0.5 + 0.5 * normal.y; + data[idx + 2] = 0.5 + 0.5 * normal.z; + data[idx + 3] = 1; + } + } + + dataTexture = new THREE.DataTexture(data, size, size, THREE.RGBAFormat, THREE.FloatType); + dataTexture.needsUpdate = true; + + const material = new THREE.MeshBasicMaterial({ map: dataTexture }); + const quad = new THREE.PlaneGeometry(50, 50); + mesh = new THREE.Mesh(quad, material); + mesh.visible = false; + + scene.add(mesh); +} + +function swapScene() { + if (params.target == 'pmrem') { + camera.position.set(10, 0, 0); + controls.enabled = true; + scene.background = renderTarget.texture; + mesh.visible = false; + renderer.toneMapping = THREE.AgXToneMapping; + } else { + camera.position.set(0, 0, 70); + controls.enabled = false; + scene.background = new THREE.Color(0, 0, 0); + mesh.visible = true; + renderer.toneMapping = THREE.NoToneMapping; + } +} + +async function exportFile() { + let result; + + if (params.target == 'pmrem') result = await exporter.parse(renderer, renderTarget); + else result = await exporter.parse(dataTexture); + + saveArrayBuffer(result, params.target + '.ktx2'); +} + +function saveArrayBuffer(buffer, filename) { + const blob = new Blob([buffer], { type: 'image/ktx2' }); + const link = document.createElement('a'); + + link.href = URL.createObjectURL(blob); + link.download = filename; + link.click(); +} diff --git a/examples-testing/examples/misc_exporter_obj.ts b/examples-testing/examples/misc_exporter_obj.ts new file mode 100644 index 000000000..025034daf --- /dev/null +++ b/examples-testing/examples/misc_exporter_obj.ts @@ -0,0 +1,192 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { OBJExporter } from 'three/addons/exporters/OBJExporter.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let camera, scene, renderer; + +const params = { + addTriangle: addTriangle, + addCube: addCube, + addCylinder: addCylinder, + addMultiple: addMultiple, + addTransformed: addTransformed, + addPoints: addPoints, + exportToObj: exportToObj, +}; + +init(); + +function init() { + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(0, 0, 400); + + scene = new THREE.Scene(); + + const ambientLight = new THREE.AmbientLight(0xffffff); + scene.add(ambientLight); + + const directionalLight = new THREE.DirectionalLight(0xffffff, 2.5); + directionalLight.position.set(0, 1, 1); + scene.add(directionalLight); + + const gui = new GUI(); + + let h = gui.addFolder('Geometry Selection'); + h.add(params, 'addTriangle').name('Triangle'); + h.add(params, 'addCube').name('Cube'); + h.add(params, 'addCylinder').name('Cylinder'); + h.add(params, 'addMultiple').name('Multiple objects'); + h.add(params, 'addTransformed').name('Transformed objects'); + h.add(params, 'addPoints').name('Point Cloud'); + + h = gui.addFolder('Export'); + h.add(params, 'exportToObj').name('Export OBJ'); + + gui.open(); + + addGeometry(1); + + window.addEventListener('resize', onWindowResize); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.enablePan = false; +} + +function exportToObj() { + const exporter = new OBJExporter(); + const result = exporter.parse(scene); + saveString(result, 'object.obj'); +} + +function addGeometry(type) { + for (let i = 0; i < scene.children.length; i++) { + const child = scene.children[i]; + + if (child.isMesh || child.isPoints) { + child.geometry.dispose(); + scene.remove(child); + i--; + } + } + + if (type === 1) { + const material = new THREE.MeshLambertMaterial({ color: 0x00cc00 }); + const geometry = generateTriangleGeometry(); + + scene.add(new THREE.Mesh(geometry, material)); + } else if (type === 2) { + const material = new THREE.MeshLambertMaterial({ color: 0x00cc00 }); + const geometry = new THREE.BoxGeometry(100, 100, 100); + scene.add(new THREE.Mesh(geometry, material)); + } else if (type === 3) { + const material = new THREE.MeshLambertMaterial({ color: 0x00cc00 }); + const geometry = new THREE.CylinderGeometry(50, 50, 100, 30, 1); + scene.add(new THREE.Mesh(geometry, material)); + } else if (type === 4 || type === 5) { + const material = new THREE.MeshLambertMaterial({ color: 0x00cc00 }); + const geometry = generateTriangleGeometry(); + + const mesh = new THREE.Mesh(geometry, material); + mesh.position.x = -200; + scene.add(mesh); + + const geometry2 = new THREE.BoxGeometry(100, 100, 100); + const mesh2 = new THREE.Mesh(geometry2, material); + scene.add(mesh2); + + const geometry3 = new THREE.CylinderGeometry(50, 50, 100, 30, 1); + const mesh3 = new THREE.Mesh(geometry3, material); + mesh3.position.x = 200; + scene.add(mesh3); + + if (type === 5) { + mesh.rotation.y = Math.PI / 4.0; + mesh2.rotation.y = Math.PI / 4.0; + mesh3.rotation.y = Math.PI / 4.0; + } + } else if (type === 6) { + const points = [0, 0, 0, 100, 0, 0, 100, 100, 0, 0, 100, 0]; + const colors = [0.5, 0, 0, 0.5, 0, 0, 0, 0.5, 0, 0, 0.5, 0]; + + const geometry = new THREE.BufferGeometry(); + geometry.setAttribute('position', new THREE.Float32BufferAttribute(points, 3)); + geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); + + const material = new THREE.PointsMaterial({ size: 10, vertexColors: true }); + + const pointCloud = new THREE.Points(geometry, material); + pointCloud.name = 'point cloud'; + scene.add(pointCloud); + } +} + +function addTriangle() { + addGeometry(1); +} + +function addCube() { + addGeometry(2); +} + +function addCylinder() { + addGeometry(3); +} + +function addMultiple() { + addGeometry(4); +} + +function addTransformed() { + addGeometry(5); +} + +function addPoints() { + addGeometry(6); +} + +const link = document.createElement('a'); +link.style.display = 'none'; +document.body.appendChild(link); + +function save(blob, filename) { + link.href = URL.createObjectURL(blob); + link.download = filename; + link.click(); +} + +function saveString(text, filename) { + save(new Blob([text], { type: 'text/plain' }), filename); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + renderer.render(scene, camera); +} + +function generateTriangleGeometry() { + const geometry = new THREE.BufferGeometry(); + const vertices = []; + + vertices.push(-50, -50, 0); + vertices.push(50, -50, 0); + vertices.push(50, 50, 0); + + geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); + geometry.computeVertexNormals(); + + return geometry; +} diff --git a/examples-testing/examples/misc_exporter_ply.ts b/examples-testing/examples/misc_exporter_ply.ts new file mode 100644 index 000000000..b7e324688 --- /dev/null +++ b/examples-testing/examples/misc_exporter_ply.ts @@ -0,0 +1,156 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { PLYExporter } from 'three/addons/exporters/PLYExporter.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let scene, camera, renderer, exporter, mesh; + +const params = { + exportASCII: exportASCII, + exportBinaryBigEndian: exportBinaryBigEndian, + exportBinaryLittleEndian: exportBinaryLittleEndian, +}; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(4, 2, 4); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xa0a0a0); + scene.fog = new THREE.Fog(0xa0a0a0, 4, 20); + + exporter = new PLYExporter(); + + // + + const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444, 3); + hemiLight.position.set(0, 20, 0); + scene.add(hemiLight); + + const directionalLight = new THREE.DirectionalLight(0xffffff, 3); + directionalLight.position.set(0, 20, 10); + directionalLight.castShadow = true; + directionalLight.shadow.camera.top = 2; + directionalLight.shadow.camera.bottom = -2; + directionalLight.shadow.camera.left = -2; + directionalLight.shadow.camera.right = 2; + scene.add(directionalLight); + + // ground + + const ground = new THREE.Mesh( + new THREE.PlaneGeometry(40, 40), + new THREE.MeshPhongMaterial({ color: 0xcbcbcb, depthWrite: false }), + ); + ground.rotation.x = -Math.PI / 2; + ground.receiveShadow = true; + scene.add(ground); + + const grid = new THREE.GridHelper(40, 20, 0x000000, 0x000000); + grid.material.opacity = 0.2; + grid.material.transparent = true; + scene.add(grid); + + // export mesh + + const geometry = new THREE.BoxGeometry(); + const material = new THREE.MeshPhongMaterial({ vertexColors: true }); + + // color vertices based on vertex positions + const colors = geometry.getAttribute('position').array.slice(); + for (let i = 0, l = colors.length; i < l; i++) { + if (colors[i] > 0) colors[i] = 0.5; + else colors[i] = 0; + } + + geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3, false)); + + mesh = new THREE.Mesh(geometry, material); + mesh.castShadow = true; + mesh.position.y = 0.5; + scene.add(mesh); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.shadowMap.enabled = true; + document.body.appendChild(renderer.domElement); + + // + + const controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, 0.5, 0); + controls.update(); + + // + + window.addEventListener('resize', onWindowResize); + + const gui = new GUI(); + + gui.add(params, 'exportASCII').name('Export PLY (ASCII)'); + gui.add(params, 'exportBinaryBigEndian').name('Export PLY (Binary BE)'); + gui.add(params, 'exportBinaryLittleEndian').name('Export PLY (Binary LE)'); + gui.open(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + renderer.render(scene, camera); +} + +function exportASCII() { + exporter.parse(mesh, function (result) { + saveString(result, 'box.ply'); + }); +} + +function exportBinaryBigEndian() { + exporter.parse( + mesh, + function (result) { + saveArrayBuffer(result, 'box.ply'); + }, + { binary: true }, + ); +} + +function exportBinaryLittleEndian() { + exporter.parse( + mesh, + function (result) { + saveArrayBuffer(result, 'box.ply'); + }, + { binary: true, littleEndian: true }, + ); +} + +const link = document.createElement('a'); +link.style.display = 'none'; +document.body.appendChild(link); + +function save(blob, filename) { + link.href = URL.createObjectURL(blob); + link.download = filename; + link.click(); +} + +function saveString(text, filename) { + save(new Blob([text], { type: 'text/plain' }), filename); +} + +function saveArrayBuffer(buffer, filename) { + save(new Blob([buffer], { type: 'application/octet-stream' }), filename); +} diff --git a/examples-testing/examples/misc_exporter_stl.ts b/examples-testing/examples/misc_exporter_stl.ts new file mode 100644 index 000000000..ff6d6e2b5 --- /dev/null +++ b/examples-testing/examples/misc_exporter_stl.ts @@ -0,0 +1,129 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { STLExporter } from 'three/addons/exporters/STLExporter.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let scene, camera, renderer, exporter, mesh; + +const params = { + exportASCII: exportASCII, + exportBinary: exportBinary, +}; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(4, 2, 4); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xa0a0a0); + scene.fog = new THREE.Fog(0xa0a0a0, 4, 20); + + exporter = new STLExporter(); + + // + + const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444, 3); + hemiLight.position.set(0, 20, 0); + scene.add(hemiLight); + + const directionalLight = new THREE.DirectionalLight(0xffffff, 3); + directionalLight.position.set(0, 20, 10); + directionalLight.castShadow = true; + directionalLight.shadow.camera.top = 2; + directionalLight.shadow.camera.bottom = -2; + directionalLight.shadow.camera.left = -2; + directionalLight.shadow.camera.right = 2; + scene.add(directionalLight); + + // ground + + const ground = new THREE.Mesh( + new THREE.PlaneGeometry(40, 40), + new THREE.MeshPhongMaterial({ color: 0xbbbbbb, depthWrite: false }), + ); + ground.rotation.x = -Math.PI / 2; + ground.receiveShadow = true; + scene.add(ground); + + const grid = new THREE.GridHelper(40, 20, 0x000000, 0x000000); + grid.material.opacity = 0.2; + grid.material.transparent = true; + scene.add(grid); + + // export mesh + + const geometry = new THREE.BoxGeometry(); + const material = new THREE.MeshPhongMaterial({ color: 0x00ff00 }); + + mesh = new THREE.Mesh(geometry, material); + mesh.castShadow = true; + mesh.position.y = 0.5; + scene.add(mesh); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.shadowMap.enabled = true; + document.body.appendChild(renderer.domElement); + + // + + const controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, 0.5, 0); + controls.update(); + + // + + window.addEventListener('resize', onWindowResize); + + const gui = new GUI(); + + gui.add(params, 'exportASCII').name('Export STL (ASCII)'); + gui.add(params, 'exportBinary').name('Export STL (Binary)'); + gui.open(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + renderer.render(scene, camera); +} + +function exportASCII() { + const result = exporter.parse(mesh); + saveString(result, 'box.stl'); +} + +function exportBinary() { + const result = exporter.parse(mesh, { binary: true }); + saveArrayBuffer(result, 'box.stl'); +} + +const link = document.createElement('a'); +link.style.display = 'none'; +document.body.appendChild(link); + +function save(blob, filename) { + link.href = URL.createObjectURL(blob); + link.download = filename; + link.click(); +} + +function saveString(text, filename) { + save(new Blob([text], { type: 'text/plain' }), filename); +} + +function saveArrayBuffer(buffer, filename) { + save(new Blob([buffer], { type: 'application/octet-stream' }), filename); +} diff --git a/examples-testing/examples/misc_exporter_usdz.ts b/examples-testing/examples/misc_exporter_usdz.ts new file mode 100644 index 000000000..f1ce65485 --- /dev/null +++ b/examples-testing/examples/misc_exporter_usdz.ts @@ -0,0 +1,130 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; + +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { USDZExporter } from 'three/addons/exporters/USDZExporter.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let camera, scene, renderer; + +const params = { + exportUSDZ: exportUSDZ, +}; + +init(); +render(); + +function init() { + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + document.body.appendChild(renderer.domElement); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); + camera.position.set(-2.5, 0.6, 3.0); + + const pmremGenerator = new THREE.PMREMGenerator(renderer); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xf0f0f0); + scene.environment = pmremGenerator.fromScene(new RoomEnvironment(), 0.04).texture; + + const loader = new GLTFLoader().setPath('models/gltf/DamagedHelmet/glTF/'); + loader.load('DamagedHelmet.gltf', async function (gltf) { + scene.add(gltf.scene); + + const shadowMesh = createSpotShadowMesh(); + shadowMesh.position.y = -1.1; + shadowMesh.position.z = -0.25; + shadowMesh.scale.setScalar(2); + scene.add(shadowMesh); + + render(); + + // USDZ + + const exporter = new USDZExporter(); + const arraybuffer = await exporter.parseAsync(gltf.scene); + const blob = new Blob([arraybuffer], { type: 'application/octet-stream' }); + + const link = document.getElementById('link'); + link.href = URL.createObjectURL(blob); + }); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.addEventListener('change', render); // use if there is no animation loop + controls.minDistance = 2; + controls.maxDistance = 10; + controls.target.set(0, -0.15, -0.2); + controls.update(); + + window.addEventListener('resize', onWindowResize); + + const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent); + + if (isIOS === false) { + const gui = new GUI(); + + gui.add(params, 'exportUSDZ').name('Export USDZ'); + gui.open(); + } +} + +function createSpotShadowMesh() { + const canvas = document.createElement('canvas'); + canvas.width = 128; + canvas.height = 128; + + const context = canvas.getContext('2d'); + const gradient = context.createRadialGradient( + canvas.width / 2, + canvas.height / 2, + 0, + canvas.width / 2, + canvas.height / 2, + canvas.width / 2, + ); + gradient.addColorStop(0.1, 'rgba(130,130,130,1)'); + gradient.addColorStop(1, 'rgba(255,255,255,1)'); + + context.fillStyle = gradient; + context.fillRect(0, 0, canvas.width, canvas.height); + + const shadowTexture = new THREE.CanvasTexture(canvas); + + const geometry = new THREE.PlaneGeometry(); + const material = new THREE.MeshBasicMaterial({ + map: shadowTexture, + blending: THREE.MultiplyBlending, + toneMapped: false, + premultipliedAlpha: true, + }); + + const mesh = new THREE.Mesh(geometry, material); + mesh.rotation.x = -Math.PI / 2; + + return mesh; +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + render(); +} + +function exportUSDZ() { + const link = document.getElementById('link'); + link.click(); +} + +// + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/misc_uv_tests.ts b/examples-testing/examples/misc_uv_tests.ts new file mode 100644 index 000000000..4f782d45f --- /dev/null +++ b/examples-testing/examples/misc_uv_tests.ts @@ -0,0 +1,44 @@ +import * as THREE from 'three'; + +import { UVsDebug } from 'three/addons/utils/UVsDebug.js'; + +/* + * This is to help debug UVs problems in geometry, + * as well as allow a new user to visualize what UVs are about. + */ + +function test(name, geometry) { + const d = document.createElement('div'); + + d.innerHTML = '

' + name + '

'; + + d.appendChild(UVsDebug(geometry)); + + document.body.appendChild(d); +} + +const points = []; + +for (let i = 0; i < 10; i++) { + points.push(new THREE.Vector2(Math.sin(i * 0.2) * 15 + 50, (i - 5) * 2)); +} + +// + +test('new THREE.PlaneGeometry( 100, 100, 4, 4 )', new THREE.PlaneGeometry(100, 100, 4, 4)); + +test('new THREE.SphereGeometry( 75, 12, 6 )', new THREE.SphereGeometry(75, 12, 6)); + +test('new THREE.IcosahedronGeometry( 30, 1 )', new THREE.IcosahedronGeometry(30, 1)); + +test('new THREE.OctahedronGeometry( 30, 2 )', new THREE.OctahedronGeometry(30, 2)); + +test('new THREE.CylinderGeometry( 25, 75, 100, 10, 5 )', new THREE.CylinderGeometry(25, 75, 100, 10, 5)); + +test('new THREE.BoxGeometry( 100, 100, 100, 4, 4, 4 )', new THREE.BoxGeometry(100, 100, 100, 4, 4, 4)); + +test('new THREE.LatheGeometry( points, 8 )', new THREE.LatheGeometry(points, 8)); + +test('new THREE.TorusGeometry( 50, 20, 8, 8 )', new THREE.TorusGeometry(50, 20, 8, 8)); + +test('new THREE.TorusKnotGeometry( 50, 10, 12, 6 )', new THREE.TorusKnotGeometry(50, 10, 12, 6)); diff --git a/examples-testing/examples/physics_ammo_instancing.ts b/examples-testing/examples/physics_ammo_instancing.ts new file mode 100644 index 000000000..5cbee3260 --- /dev/null +++ b/examples-testing/examples/physics_ammo_instancing.ts @@ -0,0 +1,132 @@ +import * as THREE from 'three'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { AmmoPhysics } from 'three/addons/physics/AmmoPhysics.js'; +import Stats from 'three/addons/libs/stats.module.js'; + +let camera, scene, renderer, stats; +let physics, position; + +let boxes, spheres; + +init(); + +async function init() { + physics = await AmmoPhysics(); + position = new THREE.Vector3(); + + // + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(-1, 1.5, 2); + camera.lookAt(0, 0.5, 0); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x666666); + + const hemiLight = new THREE.HemisphereLight(); + scene.add(hemiLight); + + const dirLight = new THREE.DirectionalLight(0xffffff, 3); + dirLight.position.set(5, 5, 5); + dirLight.castShadow = true; + dirLight.shadow.camera.zoom = 2; + scene.add(dirLight); + + const shadowPlane = new THREE.Mesh( + new THREE.PlaneGeometry(10, 10), + new THREE.ShadowMaterial({ + color: 0x444444, + }), + ); + shadowPlane.rotation.x = -Math.PI / 2; + shadowPlane.receiveShadow = true; + scene.add(shadowPlane); + + const floorCollider = new THREE.Mesh( + new THREE.BoxGeometry(10, 5, 10), + new THREE.MeshBasicMaterial({ color: 0x666666 }), + ); + floorCollider.position.y = -2.5; + floorCollider.userData.physics = { mass: 0 }; + floorCollider.visible = false; + scene.add(floorCollider); + + // + + const material = new THREE.MeshLambertMaterial(); + + const matrix = new THREE.Matrix4(); + const color = new THREE.Color(); + + // Boxes + + const geometryBox = new THREE.BoxGeometry(0.075, 0.075, 0.075); + boxes = new THREE.InstancedMesh(geometryBox, material, 400); + boxes.instanceMatrix.setUsage(THREE.DynamicDrawUsage); // will be updated every frame + boxes.castShadow = true; + boxes.receiveShadow = true; + boxes.userData.physics = { mass: 1 }; + scene.add(boxes); + + for (let i = 0; i < boxes.count; i++) { + matrix.setPosition(Math.random() - 0.5, Math.random() * 2, Math.random() - 0.5); + boxes.setMatrixAt(i, matrix); + boxes.setColorAt(i, color.setHex(0xffffff * Math.random())); + } + + // Spheres + + const geometrySphere = new THREE.IcosahedronGeometry(0.05, 4); + spheres = new THREE.InstancedMesh(geometrySphere, material, 400); + spheres.instanceMatrix.setUsage(THREE.DynamicDrawUsage); // will be updated every frame + spheres.castShadow = true; + spheres.receiveShadow = true; + spheres.userData.physics = { mass: 1 }; + scene.add(spheres); + + for (let i = 0; i < spheres.count; i++) { + matrix.setPosition(Math.random() - 0.5, Math.random() * 2, Math.random() - 0.5); + spheres.setMatrixAt(i, matrix); + spheres.setColorAt(i, color.setHex(0xffffff * Math.random())); + } + + physics.addScene(scene); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.shadowMap.enabled = true; + document.body.appendChild(renderer.domElement); + + stats = new Stats(); + document.body.appendChild(stats.dom); + + // + + const controls = new OrbitControls(camera, renderer.domElement); + controls.target.y = 0.5; + controls.update(); + + setInterval(() => { + let index = Math.floor(Math.random() * boxes.count); + + position.set(0, Math.random() + 1, 0); + physics.setMeshPosition(boxes, position, index); + + // + + index = Math.floor(Math.random() * spheres.count); + + position.set(0, Math.random() + 1, 0); + physics.setMeshPosition(spheres, position, index); + }, 1000 / 60); +} + +function animate() { + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/physics_jolt_instancing.ts b/examples-testing/examples/physics_jolt_instancing.ts new file mode 100644 index 000000000..70980b8d5 --- /dev/null +++ b/examples-testing/examples/physics_jolt_instancing.ts @@ -0,0 +1,133 @@ +import * as THREE from 'three/webgpu'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { JoltPhysics } from 'three/addons/physics/JoltPhysics.js'; +import Stats from 'three/addons/libs/stats.module.js'; + +let camera, scene, renderer, stats; +let physics, position; + +let boxes, spheres; + +init(); + +async function init() { + physics = await JoltPhysics(); + position = new THREE.Vector3(); + + // + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(-1, 1.5, 2); + camera.lookAt(0, 0.5, 0); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x666666); + + const hemiLight = new THREE.HemisphereLight(); + scene.add(hemiLight); + + const dirLight = new THREE.DirectionalLight(0xffffff, 3); + dirLight.position.set(5, 5, 5); + dirLight.castShadow = true; + dirLight.shadow.camera.zoom = 2; + dirLight.shadow.bias = -0.001; + scene.add(dirLight); + + const shadowPlane = new THREE.Mesh( + new THREE.PlaneGeometry(10, 10), + new THREE.ShadowMaterial({ + color: 0x444444, + }), + ); + shadowPlane.rotation.x = -Math.PI / 2; + shadowPlane.receiveShadow = true; + scene.add(shadowPlane); + + const floorCollider = new THREE.Mesh( + new THREE.BoxGeometry(10, 5, 10), + new THREE.MeshBasicMaterial({ color: 0x666666 }), + ); + floorCollider.position.y = -2.5; + floorCollider.userData.physics = { mass: 0 }; + floorCollider.visible = false; + scene.add(floorCollider); + + // + + const material = new THREE.MeshLambertMaterial(); + + const matrix = new THREE.Matrix4(); + const color = new THREE.Color(); + + // Boxes + + const geometryBox = new THREE.BoxGeometry(0.075, 0.075, 0.075); + boxes = new THREE.InstancedMesh(geometryBox, material, 400); + boxes.instanceMatrix.setUsage(THREE.DynamicDrawUsage); // will be updated every frame + boxes.castShadow = true; + boxes.receiveShadow = true; + boxes.userData.physics = { mass: 1 }; + scene.add(boxes); + + for (let i = 0; i < boxes.count; i++) { + matrix.setPosition(Math.random() - 0.5, Math.random() * 2, Math.random() - 0.5); + boxes.setMatrixAt(i, matrix); + boxes.setColorAt(i, color.setHex(0xffffff * Math.random())); + } + + // Spheres + + const geometrySphere = new THREE.IcosahedronGeometry(0.05, 4); + spheres = new THREE.InstancedMesh(geometrySphere, material, 400); + spheres.instanceMatrix.setUsage(THREE.DynamicDrawUsage); // will be updated every frame + spheres.castShadow = true; + spheres.receiveShadow = true; + spheres.userData.physics = { mass: 1 }; + scene.add(spheres); + + for (let i = 0; i < spheres.count; i++) { + matrix.setPosition(Math.random() - 0.5, Math.random() * 2, Math.random() - 0.5); + spheres.setMatrixAt(i, matrix); + spheres.setColorAt(i, color.setHex(0xffffff * Math.random())); + } + + physics.addScene(scene); + + // + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.shadowMap.enabled = true; + document.body.appendChild(renderer.domElement); + + stats = new Stats(); + document.body.appendChild(stats.dom); + + // + + const controls = new OrbitControls(camera, renderer.domElement); + controls.target.y = 0.5; + controls.update(); + + setInterval(() => { + let index = Math.floor(Math.random() * boxes.count); + + position.set(0, Math.random() + 1, 0); + physics.setMeshPosition(boxes, position, index); + + // + + index = Math.floor(Math.random() * spheres.count); + + position.set(0, Math.random() + 1, 0); + physics.setMeshPosition(spheres, position, index); + }, 1000 / 60); +} + +function animate() { + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/physics_rapier_basic.ts b/examples-testing/examples/physics_rapier_basic.ts new file mode 100644 index 000000000..0bacda80c --- /dev/null +++ b/examples-testing/examples/physics_rapier_basic.ts @@ -0,0 +1,144 @@ +import * as THREE from 'three'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { RapierPhysics } from 'three/addons/physics/RapierPhysics.js'; +import { RapierHelper } from 'three/addons/helpers/RapierHelper.js'; +import { RoundedBoxGeometry } from 'three/addons/geometries/RoundedBoxGeometry.js'; +import Stats from 'three/addons/libs/stats.module.js'; + +let camera, scene, renderer, stats, controls; +let physics, physicsHelper; + +init(); + +async function init() { + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xbfd1e5); + + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(0, 3, 10); + + const ambient = new THREE.HemisphereLight(0x555555, 0xffffff); + + scene.add(ambient); + + const light = new THREE.DirectionalLight(0xffffff, 4); + + light.position.set(0, 12.5, 12.5); + light.castShadow = true; + light.shadow.radius = 3; + light.shadow.blurSamples = 8; + light.shadow.mapSize.width = 1024; + light.shadow.mapSize.height = 1024; + + const size = 10; + light.shadow.camera.left = -size; + light.shadow.camera.bottom = -size; + light.shadow.camera.right = size; + light.shadow.camera.top = size; + light.shadow.camera.near = 1; + light.shadow.camera.far = 50; + + scene.add(light); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.shadowMap.enabled = true; + document.body.appendChild(renderer.domElement); + renderer.setAnimationLoop(animate); + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + controls.target = new THREE.Vector3(0, 2, 0); + controls.update(); + + const geometry = new THREE.BoxGeometry(10, 0.5, 10); + const material = new THREE.MeshStandardMaterial({ color: 0xffffff }); + + const floor = new THREE.Mesh(geometry, material); + floor.receiveShadow = true; + + floor.position.y = -0.25; + floor.userData.physics = { mass: 0 }; + + scene.add(floor); + + new THREE.TextureLoader().load('textures/grid.png', function (texture) { + texture.wrapS = THREE.RepeatWrapping; + texture.wrapT = THREE.RepeatWrapping; + texture.repeat.set(20, 20); + floor.material.map = texture; + floor.material.needsUpdate = true; + }); + + stats = new Stats(); + document.body.appendChild(stats.dom); + + initPhysics(); + + onWindowResize(); + + window.addEventListener('resize', onWindowResize, false); +} + +async function initPhysics() { + //Initialize physics engine using the script in the jsm/physics folder + physics = await RapierPhysics(); + + physics.addScene(scene); + + addBody(); + + //Optionally display collider outlines + physicsHelper = new RapierHelper(physics.world); + scene.add(physicsHelper); + + setInterval(addBody, 1000); +} + +const geometries = [ + new THREE.BoxGeometry(1, 1, 1), + new THREE.SphereGeometry(0.5), + new RoundedBoxGeometry(1, 1, 1, 2, 0.25), +]; + +function addBody() { + const geometry = geometries[Math.floor(Math.random() * geometries.length)]; + const material = new THREE.MeshStandardMaterial({ color: Math.floor(Math.random() * 0xffffff) }); + + const mesh = new THREE.Mesh(geometry, material); + mesh.castShadow = true; + + mesh.position.set(Math.random() * 2 - 1, Math.random() * 3 + 6, Math.random() * 2 - 1); + + scene.add(mesh); + + //parameter 2 - mass, parameter 3 - restitution ( how bouncy ) + physics.addMesh(mesh, 1, 0.5); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + for (const object of scene.children) { + if (object.isMesh) { + if (object.position.y < -10) { + scene.remove(object); + physics.removeMesh(object); + } + } + } + + if (physicsHelper) physicsHelper.update(); + + controls.update(); + + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/physics_rapier_character_controller.ts b/examples-testing/examples/physics_rapier_character_controller.ts new file mode 100644 index 000000000..8e797a98c --- /dev/null +++ b/examples-testing/examples/physics_rapier_character_controller.ts @@ -0,0 +1,187 @@ +import * as THREE from 'three'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { RapierPhysics } from 'three/addons/physics/RapierPhysics.js'; +import { RapierHelper } from 'three/addons/helpers/RapierHelper.js'; +import Stats from 'three/addons/libs/stats.module.js'; + +let camera, scene, renderer, stats; +let physics, characterController, physicsHelper; +let player, movement; + +init(); + +async function init() { + scene = new THREE.Scene(); + + scene.background = new THREE.Color(0xbfd1e5); + + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(2, 5, 15); + + const ambient = new THREE.HemisphereLight(0x555555, 0xffffff); + + scene.add(ambient); + + const light = new THREE.DirectionalLight(0xffffff, 3); + + light.position.set(0, 12.5, 12.5); + light.castShadow = true; + light.shadow.radius = 3; + light.shadow.blurSamples = 8; + light.shadow.mapSize.width = 1024; + light.shadow.mapSize.height = 1024; + + const size = 10; + light.shadow.camera.left = -size; + light.shadow.camera.bottom = -size; + light.shadow.camera.right = size; + light.shadow.camera.top = size; + light.shadow.camera.near = 1; + light.shadow.camera.far = 50; + + scene.add(light); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.shadowMap.enabled = true; + document.body.appendChild(renderer.domElement); + renderer.setAnimationLoop(animate); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.target = new THREE.Vector3(0, 2, 0); + controls.update(); + + const geometry = new THREE.BoxGeometry(20, 0.5, 20); + const material = new THREE.MeshStandardMaterial({ color: 0xffffff }); + + const ground = new THREE.Mesh(geometry, material); + ground.receiveShadow = true; + + ground.position.y = -0.25; + ground.userData.physics = { mass: 0 }; + + scene.add(ground); + + new THREE.TextureLoader().load('textures/grid.png', function (texture) { + texture.wrapS = THREE.RepeatWrapping; + texture.wrapT = THREE.RepeatWrapping; + texture.repeat.set(20, 20); + ground.material.map = texture; + ground.material.needsUpdate = true; + }); + + for (let i = 0; i < 10; i++) addBody(Math.random() > 0.7); + + stats = new Stats(); + document.body.appendChild(stats.dom); + + initPhysics(); + + onWindowResize(); + + // Movement input + movement = { forward: 0, right: 0 }; + + window.addEventListener('keydown', event => { + if (event.key === 'w' || event.key === 'ArrowUp') movement.forward = 1; + if (event.key === 's' || event.key === 'ArrowDown') movement.forward = -1; + if (event.key === 'a' || event.key === 'ArrowLeft') movement.right = -1; + if (event.key === 'd' || event.key === 'ArrowRight') movement.right = 1; + }); + + window.addEventListener('keyup', event => { + if (event.key === 'w' || event.key === 's' || event.key === 'ArrowUp' || event.key === 'ArrowDown') + movement.forward = 0; + if (event.key === 'a' || event.key === 'd' || event.key === 'ArrowLeft' || event.key === 'ArrowRight') + movement.right = 0; + }); + + window.addEventListener('resize', onWindowResize, false); +} + +function random(min, max) { + return Math.random() * (max - min) + min; +} + +async function initPhysics() { + //Initialize physics engine using the script in the jsm/physics folder + physics = await RapierPhysics(); + + //Optionally display collider outlines + physicsHelper = new RapierHelper(physics.world); + scene.add(physicsHelper); + + physics.addScene(scene); + + addCharacterController(); +} + +function addCharacterController() { + // Character Capsule + const geometry = new THREE.CapsuleGeometry(0.3, 1, 8, 8); + const material = new THREE.MeshStandardMaterial({ color: 0x0000ff }); + player = new THREE.Mesh(geometry, material); + player.castShadow = true; + player.position.set(0, 0.8, 0); + scene.add(player); + + // Rapier Character Controller + characterController = physics.world.createCharacterController(0.01); + characterController.setApplyImpulsesToDynamicBodies(true); + characterController.setCharacterMass(3); + const colliderDesc = physics.RAPIER.ColliderDesc.capsule(0.5, 0.3).setTranslation(0, 0.8, 0); + player.userData.collider = physics.world.createCollider(colliderDesc); +} + +function addBody(fixed = true) { + const geometry = fixed ? new THREE.BoxGeometry(1, 1, 1) : new THREE.SphereGeometry(0.25); + const material = new THREE.MeshStandardMaterial({ color: fixed ? 0xff0000 : 0x00ff00 }); + + const mesh = new THREE.Mesh(geometry, material); + mesh.castShadow = true; + + mesh.position.set(random(-10, 10), 0.5, random(-10, 10)); + + mesh.userData.physics = { mass: fixed ? 0 : 0.5, restitution: fixed ? 0 : 0.3 }; + + scene.add(mesh); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + if (physicsHelper) physicsHelper.update(); + + renderer.render(scene, camera); + + if (physics && characterController) { + const deltaTime = 1 / 60; + + // Character movement + const speed = 2.5 * deltaTime; + const moveVector = new physics.RAPIER.Vector3(movement.right * speed, 0, -movement.forward * speed); + + characterController.computeColliderMovement(player.userData.collider, moveVector); + + // Read the result. + const translation = characterController.computedMovement(); + const position = player.userData.collider.translation(); + + position.x += translation.x; + position.y += translation.y; + position.z += translation.z; + + player.userData.collider.setTranslation(position); + + // Sync Three.js mesh with Rapier collider + player.position.set(position.x, position.y, position.z); + } + + stats.update(); +} diff --git a/examples-testing/examples/physics_rapier_instancing.ts b/examples-testing/examples/physics_rapier_instancing.ts new file mode 100644 index 000000000..20509f7fd --- /dev/null +++ b/examples-testing/examples/physics_rapier_instancing.ts @@ -0,0 +1,142 @@ +import * as THREE from 'three'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { RapierPhysics } from 'three/addons/physics/RapierPhysics.js'; +import Stats from 'three/addons/libs/stats.module.js'; + +let camera, scene, renderer, stats; +let physics, position; + +let boxes, spheres; + +init(); + +async function init() { + physics = await RapierPhysics(); + position = new THREE.Vector3(); + + // + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(-1, 1.5, 2); + camera.lookAt(0, 0.5, 0); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x666666); + + const hemiLight = new THREE.HemisphereLight(); + scene.add(hemiLight); + + const dirLight = new THREE.DirectionalLight(0xffffff, 3); + dirLight.position.set(5, 5, 5); + dirLight.castShadow = true; + dirLight.shadow.camera.zoom = 2; + scene.add(dirLight); + + const shadowPlane = new THREE.Mesh( + new THREE.PlaneGeometry(10, 10), + new THREE.ShadowMaterial({ + color: 0x444444, + }), + ); + shadowPlane.rotation.x = -Math.PI / 2; + shadowPlane.receiveShadow = true; + scene.add(shadowPlane); + + const floorCollider = new THREE.Mesh( + new THREE.BoxGeometry(10, 5, 10), + new THREE.MeshBasicMaterial({ color: 0x666666 }), + ); + floorCollider.position.y = -2.5; + floorCollider.userData.physics = { mass: 0 }; + floorCollider.visible = false; + scene.add(floorCollider); + + // + + const material = new THREE.MeshLambertMaterial(); + + const matrix = new THREE.Matrix4(); + const color = new THREE.Color(); + + // Boxes + + const geometryBox = new THREE.BoxGeometry(0.075, 0.075, 0.075); + boxes = new THREE.InstancedMesh(geometryBox, material, 400); + boxes.instanceMatrix.setUsage(THREE.DynamicDrawUsage); // will be updated every frame + boxes.castShadow = true; + boxes.receiveShadow = true; + boxes.userData.physics = { mass: 1 }; + scene.add(boxes); + + for (let i = 0; i < boxes.count; i++) { + matrix.setPosition(Math.random() - 0.5, Math.random() * 2, Math.random() - 0.5); + boxes.setMatrixAt(i, matrix); + boxes.setColorAt(i, color.setHex(0xffffff * Math.random())); + } + + // Spheres + + const geometrySphere = new THREE.IcosahedronGeometry(0.05, 4); + spheres = new THREE.InstancedMesh(geometrySphere, material, 400); + spheres.instanceMatrix.setUsage(THREE.DynamicDrawUsage); // will be updated every frame + spheres.castShadow = true; + spheres.receiveShadow = true; + spheres.userData.physics = { mass: 1 }; + scene.add(spheres); + + for (let i = 0; i < spheres.count; i++) { + matrix.setPosition(Math.random() - 0.5, Math.random() * 2, Math.random() - 0.5); + spheres.setMatrixAt(i, matrix); + spheres.setColorAt(i, color.setHex(0xffffff * Math.random())); + } + + physics.addScene(scene); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.shadowMap.enabled = true; + document.body.appendChild(renderer.domElement); + + stats = new Stats(); + document.body.appendChild(stats.dom); + + // + + const controls = new OrbitControls(camera, renderer.domElement); + controls.target.y = 0.5; + controls.update(); + + setInterval(() => { + let index = Math.floor(Math.random() * boxes.count); + + position.set(0, Math.random() + 1, 0); + physics.setMeshPosition(boxes, position, index); + + // + + index = Math.floor(Math.random() * spheres.count); + + position.set(0, Math.random() + 1, 0); + physics.setMeshPosition(spheres, position, index); + }, 1000 / 60); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/physics_rapier_joints.ts b/examples-testing/examples/physics_rapier_joints.ts new file mode 100644 index 000000000..5f8805fe1 --- /dev/null +++ b/examples-testing/examples/physics_rapier_joints.ts @@ -0,0 +1,130 @@ +import * as THREE from 'three'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { RapierPhysics } from 'three/addons/physics/RapierPhysics.js'; +import { RapierHelper } from 'three/addons/helpers/RapierHelper.js'; +import Stats from 'three/addons/libs/stats.module.js'; + +let camera, scene, renderer, stats; +let physics, pivot, physicsHelper; + +init(); + +async function init() { + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xbfd1e5); + + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(0, 3, 10); + + const ambient = new THREE.HemisphereLight(0x555555, 0xffffff); + + scene.add(ambient); + + const light = new THREE.DirectionalLight(0xffffff, 4); + + light.position.set(0, 12.5, 12.5); + light.castShadow = true; + light.shadow.radius = 3; + light.shadow.blurSamples = 8; + light.shadow.mapSize.width = 1024; + light.shadow.mapSize.height = 1024; + + const size = 10; + light.shadow.camera.left = -size; + light.shadow.camera.bottom = -size; + light.shadow.camera.right = size; + light.shadow.camera.top = size; + light.shadow.camera.near = 1; + light.shadow.camera.far = 50; + + scene.add(light); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.shadowMap.enabled = true; + document.body.appendChild(renderer.domElement); + renderer.setAnimationLoop(animate); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.target = new THREE.Vector3(0, 2, 0); + controls.update(); + + stats = new Stats(); + document.body.appendChild(stats.dom); + + //Create pivot point + const geometry = new THREE.SphereGeometry(0.5); + const material = new THREE.MeshStandardMaterial({ color: 0xff0000 }); + + pivot = new THREE.Mesh(geometry, material); + + pivot.position.y = 6; + pivot.userData.physics = { mass: 0 }; + + scene.add(pivot); + + initPhysics(); + + onWindowResize(); + + window.addEventListener('resize', onWindowResize, false); +} + +async function initPhysics() { + //Initialize physics engine using the script in the jsm/physics folder + physics = await RapierPhysics(); + + physics.addScene(scene); + + //Optionally display collider outlines + physicsHelper = new RapierHelper(physics.world); + scene.add(physicsHelper); + + const link1 = addLink(pivot, 0); + const link2 = addLink(link1, 2); + addLink(link2, 4); +} + +//link - the mesh that the new link will be attached to +//x - used to position the new link +function addLink(link, x) { + const geometry = new THREE.CapsuleGeometry(0.25, 1.8); + const material = new THREE.MeshStandardMaterial({ color: 0xcccc00 }); + + const mesh = new THREE.Mesh(geometry, material); + mesh.rotateZ(Math.PI * 0.5); + + mesh.position.set(x + 0.9, 5.8, 0); + + scene.add(mesh); + + physics.addMesh(mesh, 1, 0.5); + + const jointParams = physics.RAPIER.JointData.spherical( + link == pivot ? new physics.RAPIER.Vector3(0, -0.5, 0) : new physics.RAPIER.Vector3(0, -1.15, 0), // Joint position in world space + new physics.RAPIER.Vector3(0, 1.15, 0), // Corresponding attachment on sphere + ); + const body1 = link.userData.physics.body; + const body2 = mesh.userData.physics.body; + body2.setAngularDamping(10.0); + + physics.world.createImpulseJoint(jointParams, body1, body2, true); + + return mesh; +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + if (physicsHelper) physicsHelper.update(); + + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/physics_rapier_terrain.ts b/examples-testing/examples/physics_rapier_terrain.ts new file mode 100644 index 000000000..7708ceab8 --- /dev/null +++ b/examples-testing/examples/physics_rapier_terrain.ts @@ -0,0 +1,295 @@ +import * as THREE from 'three'; +import Stats from 'three/addons/libs/stats.module.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { RapierPhysics } from 'three/addons/physics/RapierPhysics.js'; + +// Heightfield parameters +const terrainWidthExtents = 100; +const terrainDepthExtents = 100; +const terrainWidth = 128; +const terrainDepth = 128; +const terrainHalfWidth = terrainWidth / 2; +const terrainHalfDepth = terrainDepth / 2; +const terrainMaxHeight = 8; +const terrainMinHeight = -2; + +// Graphics variables +let container, stats; +let camera, scene, renderer; +let terrainMesh; +const timer = new THREE.Timer(); +timer.connect(document); + +// Physics variables +let physics; +const dynamicObjects = []; +let heightData = null; + +let time = 0; +const objectTimePeriod = 3; +let timeNextSpawn = time + objectTimePeriod; +const maxNumObjects = 30; + +init(); + +async function init() { + heightData = generateHeight(terrainWidth, terrainDepth, terrainMinHeight, terrainMaxHeight); + initGraphics(); + await initPhysics(); + // Start animation loop only after physics is initialized + renderer.setAnimationLoop(animate); +} + +function initGraphics() { + container = document.getElementById('container'); + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + // Remove setAnimationLoop from here since we'll start it after physics init + renderer.shadowMap.enabled = true; + container.appendChild(renderer.domElement); + + stats = new Stats(); + stats.domElement.style.position = 'absolute'; + stats.domElement.style.top = '0px'; + container.appendChild(stats.domElement); + + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.2, 2000); + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xbfd1e5); + + camera.position.y = + heightData[terrainHalfWidth + terrainHalfDepth * terrainWidth] * (terrainMaxHeight - terrainMinHeight) + 5; + camera.position.z = terrainDepthExtents / 2; + camera.lookAt(0, 0, 0); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.enableZoom = false; + + const geometry = new THREE.PlaneGeometry( + terrainWidthExtents, + terrainDepthExtents, + terrainWidth - 1, + terrainDepth - 1, + ); + geometry.rotateX(-Math.PI / 2); + + const vertices = geometry.attributes.position.array; + + for (let i = 0, j = 0, l = vertices.length; i < l; i++, j += 3) { + vertices[j + 1] = heightData[i]; + } + + geometry.computeVertexNormals(); + + const groundMaterial = new THREE.MeshPhongMaterial({ color: 0xc7c7c7 }); + terrainMesh = new THREE.Mesh(geometry, groundMaterial); + terrainMesh.receiveShadow = true; + terrainMesh.castShadow = true; + scene.add(terrainMesh); + + const textureLoader = new THREE.TextureLoader(); + textureLoader.load('textures/grid.png', function (texture) { + texture.wrapS = THREE.RepeatWrapping; + texture.wrapT = THREE.RepeatWrapping; + texture.repeat.set(terrainWidth - 1, terrainDepth - 1); + groundMaterial.map = texture; + groundMaterial.needsUpdate = true; + }); + + const ambientLight = new THREE.AmbientLight(0xbbbbbb); + scene.add(ambientLight); + + const light = new THREE.DirectionalLight(0xffffff, 3); + light.position.set(100, 100, 50); + light.castShadow = true; + const dLight = 200; + const sLight = dLight * 0.25; + light.shadow.camera.left = -sLight; + light.shadow.camera.right = sLight; + light.shadow.camera.top = sLight; + light.shadow.camera.bottom = -sLight; + light.shadow.camera.near = dLight / 30; + light.shadow.camera.far = dLight; + light.shadow.mapSize.x = 1024 * 2; + light.shadow.mapSize.y = 1024 * 2; + scene.add(light); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + renderer.setSize(window.innerWidth, window.innerHeight); +} + +async function initPhysics() { + physics = await RapierPhysics(); + + // Create the terrain body using RapierPhysics module + physics.addHeightfield(terrainMesh, terrainWidth - 1, terrainDepth - 1, heightData, { + x: terrainWidthExtents, + y: 1.0, + z: terrainDepthExtents, + }); + + // Continue with adding other dynamic objects as needed +} + +function generateHeight(width, depth, minHeight, maxHeight) { + const size = width * depth; + const data = new Float32Array(size); + const hRange = maxHeight - minHeight; + const w2 = width / 2; + const d2 = depth / 2; + const phaseMult = 12; + let p = 0; + + for (let j = 0; j < depth; j++) { + for (let i = 0; i < width; i++) { + const radius = Math.sqrt(Math.pow((i - w2) / w2, 2.0) + Math.pow((j - d2) / d2, 2.0)); + const height = (Math.sin(radius * phaseMult) + 1) * 0.5 * hRange + minHeight; + data[p] = height; + p++; + } + } + + return data; +} + +function generateObject() { + const numTypes = 3; // cones not working + const objectType = Math.ceil(Math.random() * numTypes); + let threeObject = null; + const objectSize = 3; + let radius, height; + + switch (objectType) { + case 1: // Sphere + radius = 1 + Math.random() * objectSize; + threeObject = new THREE.Mesh(new THREE.SphereGeometry(radius, 20, 20), createObjectMaterial()); + break; + case 2: // Box + const sx = 1 + Math.random() * objectSize; + const sy = 1 + Math.random() * objectSize; + const sz = 1 + Math.random() * objectSize; + threeObject = new THREE.Mesh(new THREE.BoxGeometry(sx, sy, sz, 1, 1, 1), createObjectMaterial()); + break; + case 3: // Cylinder + radius = 1 + Math.random() * objectSize; + height = 1 + Math.random() * objectSize; + threeObject = new THREE.Mesh( + new THREE.CylinderGeometry(radius, radius, height, 20, 1), + createObjectMaterial(), + ); + break; + default: // Cone + radius = 1 + Math.random() * objectSize; + height = 2 + Math.random() * objectSize; + threeObject = new THREE.Mesh(new THREE.ConeGeometry(radius, height, 20, 2), createObjectMaterial()); + break; + } + + // Position objects higher and with more randomization to prevent clustering + threeObject.position.set( + (Math.random() - 0.5) * terrainWidth * 0.6, + terrainMaxHeight + objectSize + 15 + Math.random() * 5, // Even higher with randomization + (Math.random() - 0.5) * terrainDepth * 0.6, + ); + + const mass = objectSize * 5; + const restitution = 0.3; // Add some bounciness + + // Add to scene first + scene.add(threeObject); + + // Add physics to the object + physics.addMesh(threeObject, mass, restitution); + + // Store the object for later reference + dynamicObjects.push(threeObject); + + // Force a small delay before adding the next object + timeNextSpawn = time + 0.5; + + threeObject.receiveShadow = true; + threeObject.castShadow = true; +} + +function createObjectMaterial() { + const c = Math.floor(Math.random() * (1 << 24)); + return new THREE.MeshPhongMaterial({ color: c }); +} + +function animate() { + timer.update(); + + render(); + stats.update(); +} + +function render() { + const deltaTime = timer.getDelta(); + + // Generate new objects with a delay between them + if (dynamicObjects.length < maxNumObjects && time > timeNextSpawn) { + // Generate object directly in this frame + generateObject(); + // timeNextSpawn is now set in generateObject() + } + + // Clean up objects that have fallen off the terrain + for (let i = dynamicObjects.length - 1; i >= 0; i--) { + const obj = dynamicObjects[i]; + if (obj.position.y < terrainMinHeight - 10) { + // Remove from scene and physics world + scene.remove(obj); + dynamicObjects.splice(i, 1); + } + } + + updatePhysics(); + renderer.render(scene, camera); + time += deltaTime; +} + +function updatePhysics() { + // Check for objects that might need help with physics + for (let i = 0, il = dynamicObjects.length; i < il; i++) { + const objThree = dynamicObjects[i]; + + // If object is not moving but should be (based on height), try to fix it + if (objThree.position.y > 1.0) { + // Check if physics body exists + if (objThree.userData && objThree.userData.physics && objThree.userData.physics.body) { + const body = objThree.userData.physics.body; + + // Make sure body is awake + if (typeof body.wakeUp === 'function') { + body.wakeUp(); + } + + // Check velocity and apply impulse if needed + if (typeof body.linvel === 'function') { + const velocity = body.linvel(); + const speed = Math.sqrt( + velocity.x * velocity.x + velocity.y * velocity.y + velocity.z * velocity.z, + ); + + // If object is very slow, give it a stronger impulse + if (speed < 0.5) { + body.applyImpulse({ x: 0, y: -2.0, z: 0 }, true); + } + } + } else { + // If the object doesn't have a physics body but should, recreate it + const mass = 5; // Default mass + const restitution = 0.3; // Default restitution + + // Recreate physics for the object + physics.addMesh(objThree, mass, restitution); + } + } + } +} diff --git a/examples-testing/examples/physics_rapier_vehicle_controller.ts b/examples-testing/examples/physics_rapier_vehicle_controller.ts new file mode 100644 index 000000000..fac5ef082 --- /dev/null +++ b/examples-testing/examples/physics_rapier_vehicle_controller.ts @@ -0,0 +1,297 @@ +import * as THREE from 'three'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { RapierPhysics } from 'three/addons/physics/RapierPhysics.js'; +import { RapierHelper } from 'three/addons/helpers/RapierHelper.js'; +import Stats from 'three/addons/libs/stats.module.js'; + +let camera, scene, renderer, stats; +let physics, physicsHelper, controls; +let car, chassis, wheels, movement, vehicleController; + +init(); + +async function init() { + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xbfd1e5); + + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(0, 4, 10); + + const ambient = new THREE.HemisphereLight(0x555555, 0xffffff); + + scene.add(ambient); + + const light = new THREE.DirectionalLight(0xffffff, 4); + + light.position.set(0, 12.5, 12.5); + light.castShadow = true; + light.shadow.radius = 3; + light.shadow.blurSamples = 8; + light.shadow.mapSize.width = 2048; + light.shadow.mapSize.height = 2048; + + const size = 40; + light.shadow.camera.left = -size; + light.shadow.camera.bottom = -size; + light.shadow.camera.right = size; + light.shadow.camera.top = size; + light.shadow.camera.near = 1; + light.shadow.camera.far = 50; + + scene.add(light); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.shadowMap.enabled = true; + document.body.appendChild(renderer.domElement); + renderer.setAnimationLoop(animate); + + controls = new OrbitControls(camera, renderer.domElement); + controls.target = new THREE.Vector3(0, 2, 0); + controls.update(); + + const geometry = new THREE.BoxGeometry(100, 0.5, 100); + const material = new THREE.MeshStandardMaterial({ color: 0xffffff }); + + const ground = new THREE.Mesh(geometry, material); + ground.receiveShadow = true; + + ground.position.set(0, -0.25, -20); + ground.userData.physics = { mass: 0 }; + + scene.add(ground); + + new THREE.TextureLoader().load('textures/grid.png', function (texture) { + texture.wrapS = THREE.RepeatWrapping; + texture.wrapT = THREE.RepeatWrapping; + texture.repeat.set(80, 80); + ground.material.map = texture; + ground.material.needsUpdate = true; + }); + + stats = new Stats(); + document.body.appendChild(stats.dom); + + initPhysics(); + + onWindowResize(); + + // Movement input + movement = { + forward: 0, + right: 0, + brake: 0, + reset: false, + accelerateForce: { value: 0, min: -30, max: 30, step: 1 }, + brakeForce: { value: 0, min: 0, max: 1, step: 0.05 }, + }; + + window.addEventListener('keydown', event => { + //console.log( event.key ); + if (event.key === 'w' || event.key === 'ArrowUp') movement.forward = -1; + if (event.key === 's' || event.key === 'ArrowDown') movement.forward = 1; + if (event.key === 'a' || event.key === 'ArrowLeft') movement.right = 1; + if (event.key === 'd' || event.key === 'ArrowRight') movement.right = -1; + if (event.key === 'r') movement.reset = true; + if (event.key === ' ') movement.brake = 1; + }); + + window.addEventListener('keyup', event => { + if (event.key === 'w' || event.key === 's' || event.key === 'ArrowUp' || event.key === 'ArrowDown') + movement.forward = 0; + if (event.key === 'a' || event.key === 'd' || event.key === 'ArrowLeft' || event.key === 'ArrowRight') + movement.right = 0; + if (event.key === 'r') movement.reset = false; + if (event.key === ' ') movement.brake = 0; + }); + + window.addEventListener('resize', onWindowResize, false); +} + +async function initPhysics() { + //Initialize physics engine using the script in the jsm/physics folder + physics = await RapierPhysics(); + + //Optionally display collider outlines + physicsHelper = new RapierHelper(physics.world); + scene.add(physicsHelper); + + physics.addScene(scene); + + createCar(); +} + +function createCar() { + const geometry = new THREE.BoxGeometry(2, 1, 4); + const material = new THREE.MeshStandardMaterial({ color: 0xff0000 }); + const mesh = new THREE.Mesh(geometry, material); + mesh.castShadow = true; + scene.add(mesh); + car = mesh; + + mesh.position.y = 1; + + physics.addMesh(mesh, 10, 0.8); // addMesh places the RigidBody in the mesh.userData.physics object + chassis = mesh.userData.physics.body; + + vehicleController = physics.world.createVehicleController(chassis); + + wheels = []; + + addWheel(0, { x: -1, y: 0, z: -1.5 }, mesh); + addWheel(1, { x: 1, y: 0, z: -1.5 }, mesh); + addWheel(2, { x: -1, y: 0, z: 1.5 }, mesh); + addWheel(3, { x: 1, y: 0, z: 1.5 }, mesh); + + vehicleController.setWheelSteering(0, Math.PI / 4); + vehicleController.setWheelSteering(1, Math.PI / 4); +} + +function addWheel(index, pos, carMesh) { + // Define wheel properties + const wheelRadius = 0.3; + const wheelWidth = 0.4; + const suspensionRestLength = 0.8; + const wheelPosition = pos; // Position relative to chassis + const wheelDirection = { x: 0.0, y: -1.0, z: 0.0 }; // Downward direction + const wheelAxle = { x: -1.0, y: 0.0, z: 0.0 }; // Axle direction + + // Add the wheel to the vehicle controller + vehicleController.addWheel(wheelPosition, wheelDirection, wheelAxle, suspensionRestLength, wheelRadius); + + // Set suspension stiffness for wheel + vehicleController.setWheelSuspensionStiffness(index, 24.0); + + // Set wheel friction + vehicleController.setWheelFrictionSlip(index, 1000.0); + + // Enable steering for the wheel + vehicleController.setWheelSteering(index, pos.z < 0); + + // Create a wheel mesh + const geometry = new THREE.CylinderGeometry(wheelRadius, wheelRadius, wheelWidth, 16); + geometry.rotateZ(Math.PI * 0.5); + const material = new THREE.MeshStandardMaterial({ color: 0x000000 }); + const wheel = new THREE.Mesh(geometry, material); + + wheel.castShadow = true; + + wheel.position.copy(pos); + + wheels.push(wheel); + carMesh.add(wheel); +} + +function updateWheels() { + if (vehicleController === undefined) return; + + const wheelSteeringQuat = new THREE.Quaternion(); + const wheelRotationQuat = new THREE.Quaternion(); + const up = new THREE.Vector3(0, 1, 0); + + //const chassisPosition = chassis.translation(); + + wheels.forEach((wheel, index) => { + const wheelAxleCs = vehicleController.wheelAxleCs(index); + const connection = vehicleController.wheelChassisConnectionPointCs(index).y || 0; + const suspension = vehicleController.wheelSuspensionLength(index) || 0; + const steering = vehicleController.wheelSteering(index) || 0; + const rotationRad = vehicleController.wheelRotation(index) || 0; + + wheel.position.y = connection - suspension; + + wheelSteeringQuat.setFromAxisAngle(up, steering); + wheelRotationQuat.setFromAxisAngle(wheelAxleCs, rotationRad); + + wheel.quaternion.multiplyQuaternions(wheelSteeringQuat, wheelRotationQuat); + }); +} + +function updateCarControl() { + if (movement.reset) { + chassis.setTranslation(new physics.RAPIER.Vector3(0, 1, 0), true); + chassis.setRotation(new physics.RAPIER.Quaternion(0, 0, 0, 1), true); + chassis.setLinvel(new physics.RAPIER.Vector3(0, 0, 0), true); + chassis.setAngvel(new physics.RAPIER.Vector3(0, 0, 0), true); + + movement.accelerateForce.value = 0; + movement.brakeForce.value = 0; + + return; + } + + let accelerateForce = 0; + + if (movement.forward < 0) { + //if (movement.accelerateForce.value === 0) chassis.wakeUp(); + accelerateForce = movement.accelerateForce.value - movement.accelerateForce.step; + if (accelerateForce < movement.accelerateForce.min) accelerateForce = movement.accelerateForce.min; + } else if (movement.forward > 0) { + //if (movement.accelerateForce.value === 0) chassis.wakeUp(); + accelerateForce = movement.accelerateForce.value + movement.accelerateForce.step; + + if (accelerateForce > movement.accelerateForce.max) accelerateForce = movement.accelerateForce.max; + } else { + if (chassis.isSleeping()) chassis.wakeUp(); + } + + movement.accelerateForce.value = accelerateForce; + + //console.log(accelerateForce); + + let brakeForce = 0; + + if (movement.brake > 0) { + brakeForce = movement.brakeForce.value + movement.brakeForce.step; + if (brakeForce > movement.brakeForce.max) brakeForce = movement.brakeForce.max; + } + + movement.brakeForce.value = brakeForce; + + const engineForce = accelerateForce; + + vehicleController.setWheelEngineForce(0, engineForce); + vehicleController.setWheelEngineForce(1, engineForce); + + const currentSteering = vehicleController.wheelSteering(0); + const steerDirection = movement.right; + const steerAngle = Math.PI / 4; + + const steering = THREE.MathUtils.lerp(currentSteering, steerAngle * steerDirection, 0.25); + + vehicleController.setWheelSteering(0, steering); + vehicleController.setWheelSteering(1, steering); + + const wheelBrake = movement.brake * brakeForce; + vehicleController.setWheelBrake(0, wheelBrake); + vehicleController.setWheelBrake(1, wheelBrake); + vehicleController.setWheelBrake(2, wheelBrake); + vehicleController.setWheelBrake(3, wheelBrake); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + if (vehicleController) { + updateCarControl(); + vehicleController.updateVehicle(1 / 60); + updateWheels(); + } + + if (controls && car) { + controls.target.copy(car.position); + controls.update(); + } + + if (physicsHelper) physicsHelper.update(); + + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/svg_lines.ts b/examples-testing/examples/svg_lines.ts new file mode 100644 index 000000000..99b74c405 --- /dev/null +++ b/examples-testing/examples/svg_lines.ts @@ -0,0 +1,87 @@ +import * as THREE from 'three'; + +import { SVGRenderer } from 'three/addons/renderers/SVGRenderer.js'; + +THREE.ColorManagement.enabled = false; + +let camera, scene, renderer; + +init(); +animate(); + +function init() { + camera = new THREE.PerspectiveCamera(33, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.z = 10; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0, 0, 0); + + renderer = new SVGRenderer(); + renderer.setSize(window.innerWidth, window.innerHeight); + document.body.appendChild(renderer.domElement); + + // + + const vertices = []; + const divisions = 50; + + for (let i = 0; i <= divisions; i++) { + const v = (i / divisions) * (Math.PI * 2); + + const x = Math.sin(v); + const z = Math.cos(v); + + vertices.push(x, 0, z); + } + + const geometry = new THREE.BufferGeometry(); + geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); + + // + + for (let i = 1; i <= 3; i++) { + const material = new THREE.LineBasicMaterial({ + color: Math.random() * 0xffffff, + linewidth: 10, + }); + const line = new THREE.Line(geometry, material); + line.scale.setScalar(i / 3); + scene.add(line); + } + + const material = new THREE.LineDashedMaterial({ + color: 'blue', + linewidth: 1, + dashSize: 10, + gapSize: 10, + }); + const line = new THREE.Line(geometry, material); + line.scale.setScalar(2); + scene.add(line); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + let count = 0; + const time = performance.now() / 1000; + + scene.traverse(function (child) { + child.rotation.x = count + time / 3; + child.rotation.z = count + time / 4; + + count++; + }); + + renderer.render(scene, camera); + requestAnimationFrame(animate); +} diff --git a/examples-testing/examples/svg_sandbox.ts b/examples-testing/examples/svg_sandbox.ts new file mode 100644 index 000000000..fa66f10e1 --- /dev/null +++ b/examples-testing/examples/svg_sandbox.ts @@ -0,0 +1,212 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { SVGRenderer, SVGObject } from 'three/addons/renderers/SVGRenderer.js'; + +THREE.ColorManagement.enabled = false; + +let camera, scene, renderer, stats, controls; + +let group; + +init(); +animate(); + +function init() { + camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000); + camera.position.z = 500; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xf0f0f0); + + // QRCODE + + const loader = new THREE.BufferGeometryLoader(); + loader.load('models/json/QRCode_buffergeometry.json', function (geometry) { + mesh = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({ vertexColors: true })); + mesh.scale.x = mesh.scale.y = mesh.scale.z = 2; + scene.add(mesh); + }); + + // CUBES + + const boxGeometry = new THREE.BoxGeometry(100, 100, 100); + + let mesh = new THREE.Mesh( + boxGeometry, + new THREE.MeshBasicMaterial({ color: 0x0000ff, opacity: 0.5, transparent: true }), + ); + mesh.position.x = 500; + mesh.rotation.x = Math.random(); + mesh.rotation.y = Math.random(); + mesh.scale.x = mesh.scale.y = mesh.scale.z = 2; + scene.add(mesh); + + mesh = new THREE.Mesh(boxGeometry, new THREE.MeshBasicMaterial({ color: Math.random() * 0xffffff })); + mesh.position.x = 500; + mesh.position.y = 500; + mesh.rotation.x = Math.random(); + mesh.rotation.y = Math.random(); + mesh.scale.x = mesh.scale.y = mesh.scale.z = 2; + scene.add(mesh); + + // PLANE + + mesh = new THREE.Mesh( + new THREE.PlaneGeometry(100, 100), + new THREE.MeshBasicMaterial({ color: Math.random() * 0xffffff, side: THREE.DoubleSide }), + ); + mesh.position.y = -500; + mesh.scale.x = mesh.scale.y = mesh.scale.z = 2; + scene.add(mesh); + + // CYLINDER + + mesh = new THREE.Mesh( + new THREE.CylinderGeometry(20, 100, 200, 10), + new THREE.MeshBasicMaterial({ color: Math.random() * 0xffffff }), + ); + mesh.position.x = -500; + mesh.rotation.x = -Math.PI / 2; + mesh.scale.x = mesh.scale.y = mesh.scale.z = 2; + scene.add(mesh); + + // POLYFIELD + + const geometry = new THREE.BufferGeometry(); + const material = new THREE.MeshBasicMaterial({ vertexColors: true, side: THREE.DoubleSide }); + + const v = new THREE.Vector3(); + const v0 = new THREE.Vector3(); + const v1 = new THREE.Vector3(); + const v2 = new THREE.Vector3(); + const color = new THREE.Color(); + + const vertices = []; + const colors = []; + + for (let i = 0; i < 100; i++) { + v.set(Math.random() * 1000 - 500, Math.random() * 1000 - 500, Math.random() * 1000 - 500); + + v0.set(Math.random() * 100 - 50, Math.random() * 100 - 50, Math.random() * 100 - 50); + + v1.set(Math.random() * 100 - 50, Math.random() * 100 - 50, Math.random() * 100 - 50); + + v2.set(Math.random() * 100 - 50, Math.random() * 100 - 50, Math.random() * 100 - 50); + + v0.add(v); + v1.add(v); + v2.add(v); + + color.setHex(Math.random() * 0xffffff); + + // create a single triangle + + vertices.push(v0.x, v0.y, v0.z); + vertices.push(v1.x, v1.y, v1.z); + vertices.push(v2.x, v2.y, v2.z); + + colors.push(color.r, color.g, color.b); + colors.push(color.r, color.g, color.b); + colors.push(color.r, color.g, color.b); + } + + geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); + geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); + + group = new THREE.Mesh(geometry, material); + group.scale.set(2, 2, 2); + scene.add(group); + + // SPRITES + + for (let i = 0; i < 50; i++) { + const material = new THREE.SpriteMaterial({ color: Math.random() * 0xffffff }); + const sprite = new THREE.Sprite(material); + sprite.position.x = Math.random() * 1000 - 500; + sprite.position.y = Math.random() * 1000 - 500; + sprite.position.z = Math.random() * 1000 - 500; + sprite.scale.set(64, 64, 1); + scene.add(sprite); + } + + // CUSTOM + + const node = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); + node.setAttribute('stroke', 'black'); + node.setAttribute('fill', 'red'); + node.setAttribute('r', '40'); + + for (let i = 0; i < 50; i++) { + const object = new SVGObject(node.cloneNode()); + object.position.x = Math.random() * 1000 - 500; + object.position.y = Math.random() * 1000 - 500; + object.position.z = Math.random() * 1000 - 500; + scene.add(object); + } + + // CUSTOM FROM FILE + + const fileLoader = new THREE.FileLoader(); + fileLoader.load('models/svg/hexagon.svg', function (svg) { + const node = document.createElementNS('http://www.w3.org/2000/svg', 'g'); + const parser = new DOMParser(); + const doc = parser.parseFromString(svg, 'image/svg+xml'); + + node.appendChild(doc.documentElement); + + const object = new SVGObject(node); + object.position.x = 500; + scene.add(object); + }); + + // LIGHTS + + const ambient = new THREE.AmbientLight(0x80ffff); + scene.add(ambient); + + const directional = new THREE.DirectionalLight(0xffff00); + directional.position.set(-1, 0.5, 0); + scene.add(directional); + + renderer = new SVGRenderer(); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setQuality('low'); + document.body.appendChild(renderer.domElement); + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + + stats = new Stats(); + document.body.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + requestAnimationFrame(animate); + + render(); + stats.update(); +} + +function render() { + group.rotation.x += 0.01; + + controls.update(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webaudio_orientation.ts b/examples-testing/examples/webaudio_orientation.ts new file mode 100644 index 000000000..7baaa88a0 --- /dev/null +++ b/examples-testing/examples/webaudio_orientation.ts @@ -0,0 +1,141 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { PositionalAudioHelper } from 'three/addons/helpers/PositionalAudioHelper.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +let scene, camera, renderer; + +const startButton = document.getElementById('startButton'); +startButton.addEventListener('click', init); + +function init() { + const overlay = document.getElementById('overlay'); + overlay.remove(); + + const container = document.getElementById('container'); + + // + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(3, 2, 3); + + const reflectionCube = new THREE.CubeTextureLoader() + .setPath('textures/cube/SwedishRoyalCastle/') + .load(['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg']); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xa0a0a0); + scene.fog = new THREE.Fog(0xa0a0a0, 2, 20); + + // + + const hemiLight = new THREE.HemisphereLight(0xffffff, 0x8d8d8d, 3); + hemiLight.position.set(0, 20, 0); + scene.add(hemiLight); + + const dirLight = new THREE.DirectionalLight(0xffffff, 3); + dirLight.position.set(5, 5, 0); + dirLight.castShadow = true; + dirLight.shadow.camera.top = 1; + dirLight.shadow.camera.bottom = -1; + dirLight.shadow.camera.left = -1; + dirLight.shadow.camera.right = 1; + dirLight.shadow.camera.near = 0.1; + dirLight.shadow.camera.far = 20; + scene.add(dirLight); + + // scene.add( new THREE.CameraHelper( dirLight.shadow.camera ) ); + + // + + const mesh = new THREE.Mesh( + new THREE.PlaneGeometry(50, 50), + new THREE.MeshPhongMaterial({ color: 0xcbcbcb, depthWrite: false }), + ); + mesh.rotation.x = -Math.PI / 2; + mesh.receiveShadow = true; + scene.add(mesh); + + const grid = new THREE.GridHelper(50, 50, 0xc1c1c1, 0xc1c1c1); + scene.add(grid); + + // + + const listener = new THREE.AudioListener(); + camera.add(listener); + + const audioElement = document.getElementById('music'); + audioElement.play(); + + const positionalAudio = new THREE.PositionalAudio(listener); + positionalAudio.setMediaElementSource(audioElement); + positionalAudio.setRefDistance(1); + positionalAudio.setDirectionalCone(180, 230, 0.1); + + const helper = new PositionalAudioHelper(positionalAudio, 0.1); + positionalAudio.add(helper); + + // + + const gltfLoader = new GLTFLoader(); + gltfLoader.load('models/gltf/BoomBox.glb', function (gltf) { + const boomBox = gltf.scene; + boomBox.position.set(0, 0.2, 0); + boomBox.scale.set(20, 20, 20); + + boomBox.traverse(function (object) { + if (object.isMesh) { + object.material.envMap = reflectionCube; + object.geometry.rotateY(-Math.PI); + object.castShadow = true; + } + }); + + boomBox.add(positionalAudio); + scene.add(boomBox); + + renderer.setAnimationLoop(animate); + }); + + // sound is damped behind this wall + + const wallGeometry = new THREE.BoxGeometry(2, 1, 0.1); + const wallMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000, transparent: true, opacity: 0.5 }); + + const wall = new THREE.Mesh(wallGeometry, wallMaterial); + wall.position.set(0, 0.5, -0.5); + scene.add(wall); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.shadowMap.enabled = true; + container.appendChild(renderer.domElement); + + // + + const controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, 0.1, 0); + controls.update(); + controls.minDistance = 0.5; + controls.maxDistance = 10; + controls.maxPolarAngle = 0.5 * Math.PI; + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webaudio_sandbox.ts b/examples-testing/examples/webaudio_sandbox.ts new file mode 100644 index 000000000..34afca985 --- /dev/null +++ b/examples-testing/examples/webaudio_sandbox.ts @@ -0,0 +1,223 @@ +import * as THREE from 'three'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { FirstPersonControls } from 'three/addons/controls/FirstPersonControls.js'; + +let camera, controls, scene, renderer, light; + +let material1, material2, material3; + +let analyser1, analyser2, analyser3; + +const timer = new THREE.Timer(); +timer.connect(document); + +const startButton = document.getElementById('startButton'); +startButton.addEventListener('click', init); + +function init() { + const overlay = document.getElementById('overlay'); + overlay.remove(); + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000); + camera.position.set(0, 25, 0); + + const listener = new THREE.AudioListener(); + camera.add(listener); + + scene = new THREE.Scene(); + scene.fog = new THREE.FogExp2(0x000000, 0.0025); + + light = new THREE.DirectionalLight(0xffffff, 3); + light.position.set(0, 0.5, 1).normalize(); + scene.add(light); + + const sphere = new THREE.SphereGeometry(20, 32, 16); + + material1 = new THREE.MeshPhongMaterial({ color: 0xffaa00, flatShading: true, shininess: 0 }); + material2 = new THREE.MeshPhongMaterial({ color: 0xff2200, flatShading: true, shininess: 0 }); + material3 = new THREE.MeshPhongMaterial({ color: 0x6622aa, flatShading: true, shininess: 0 }); + + // sound spheres + + const mesh1 = new THREE.Mesh(sphere, material1); + mesh1.position.set(-250, 30, 0); + scene.add(mesh1); + + const sound1 = new THREE.PositionalAudio(listener); + const songElement = document.getElementById('song'); + sound1.setMediaElementSource(songElement); + sound1.setRefDistance(20); + songElement.play(); + mesh1.add(sound1); + + // + + const mesh2 = new THREE.Mesh(sphere, material2); + mesh2.position.set(250, 30, 0); + scene.add(mesh2); + + const sound2 = new THREE.PositionalAudio(listener); + const skullbeatzElement = document.getElementById('skullbeatz'); + sound2.setMediaElementSource(skullbeatzElement); + sound2.setRefDistance(20); + skullbeatzElement.play(); + mesh2.add(sound2); + + // + + const mesh3 = new THREE.Mesh(sphere, material3); + mesh3.position.set(0, 30, -250); + scene.add(mesh3); + + const sound3 = new THREE.PositionalAudio(listener); + const oscillator = listener.context.createOscillator(); + oscillator.type = 'sine'; + oscillator.frequency.setValueAtTime(144, sound3.context.currentTime); + oscillator.start(0); + sound3.setNodeSource(oscillator); + sound3.setRefDistance(20); + sound3.setVolume(0.5); + mesh3.add(sound3); + + // analysers + + analyser1 = new THREE.AudioAnalyser(sound1, 32); + analyser2 = new THREE.AudioAnalyser(sound2, 32); + analyser3 = new THREE.AudioAnalyser(sound3, 32); + + // global ambient audio + + const sound4 = new THREE.Audio(listener); + const utopiaElement = document.getElementById('utopia'); + sound4.setMediaElementSource(utopiaElement); + sound4.setVolume(0.5); + utopiaElement.play(); + + // ground + + const helper = new THREE.GridHelper(1000, 10, 0x444444, 0x444444); + helper.position.y = 0.1; + scene.add(helper); + + // + + const SoundControls = function () { + this.master = listener.getMasterVolume(); + this.firstSphere = sound1.getVolume(); + this.secondSphere = sound2.getVolume(); + this.thirdSphere = sound3.getVolume(); + this.Ambient = sound4.getVolume(); + }; + + const GeneratorControls = function () { + this.frequency = oscillator.frequency.value; + this.wavetype = oscillator.type; + }; + + const gui = new GUI(); + const soundControls = new SoundControls(); + const generatorControls = new GeneratorControls(); + const volumeFolder = gui.addFolder('sound volume'); + const generatorFolder = gui.addFolder('sound generator'); + + volumeFolder + .add(soundControls, 'master') + .min(0.0) + .max(1.0) + .step(0.01) + .onChange(function () { + listener.setMasterVolume(soundControls.master); + }); + volumeFolder + .add(soundControls, 'firstSphere') + .min(0.0) + .max(1.0) + .step(0.01) + .onChange(function () { + sound1.setVolume(soundControls.firstSphere); + }); + volumeFolder + .add(soundControls, 'secondSphere') + .min(0.0) + .max(1.0) + .step(0.01) + .onChange(function () { + sound2.setVolume(soundControls.secondSphere); + }); + + volumeFolder + .add(soundControls, 'thirdSphere') + .min(0.0) + .max(1.0) + .step(0.01) + .onChange(function () { + sound3.setVolume(soundControls.thirdSphere); + }); + volumeFolder + .add(soundControls, 'Ambient') + .min(0.0) + .max(1.0) + .step(0.01) + .onChange(function () { + sound4.setVolume(soundControls.Ambient); + }); + volumeFolder.open(); + generatorFolder + .add(generatorControls, 'frequency') + .min(50.0) + .max(5000.0) + .step(1.0) + .onChange(function () { + oscillator.frequency.setValueAtTime(generatorControls.frequency, listener.context.currentTime); + }); + generatorFolder + .add(generatorControls, 'wavetype', ['sine', 'square', 'sawtooth', 'triangle']) + .onChange(function () { + oscillator.type = generatorControls.wavetype; + }); + + generatorFolder.open(); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // + + controls = new FirstPersonControls(camera, renderer.domElement); + + controls.movementSpeed = 70; + controls.lookSpeed = 0.05; + controls.lookVertical = false; + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + const delta = timer.getDelta(); + + controls.update(delta); + + material1.emissive.b = analyser1.getAverageFrequency() / 256; + material2.emissive.b = analyser2.getAverageFrequency() / 256; + material3.emissive.b = analyser3.getAverageFrequency() / 256; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webaudio_timing.ts b/examples-testing/examples/webaudio_timing.ts new file mode 100644 index 000000000..f37b5c5cf --- /dev/null +++ b/examples-testing/examples/webaudio_timing.ts @@ -0,0 +1,155 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let scene, camera, renderer, timer; + +const objects = []; + +const speed = 2.5; +const height = 3; +const offset = 0.5; + +const startButton = document.getElementById('startButton'); +startButton.addEventListener('click', init); + +function init() { + const overlay = document.getElementById('overlay'); + overlay.remove(); + + const container = document.getElementById('container'); + + scene = new THREE.Scene(); + + timer = new THREE.Timer(); + timer.connect(document); + + // + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(7, 3, 7); + + // lights + + const ambientLight = new THREE.AmbientLight(0xcccccc); + scene.add(ambientLight); + + const directionalLight = new THREE.DirectionalLight(0xffffff, 2.5); + directionalLight.position.set(0, 5, 5); + scene.add(directionalLight); + + const d = 5; + directionalLight.castShadow = true; + directionalLight.shadow.camera.left = -d; + directionalLight.shadow.camera.right = d; + directionalLight.shadow.camera.top = d; + directionalLight.shadow.camera.bottom = -d; + + directionalLight.shadow.camera.near = 1; + directionalLight.shadow.camera.far = 20; + + directionalLight.shadow.mapSize.x = 1024; + directionalLight.shadow.mapSize.y = 1024; + + // audio + + const audioLoader = new THREE.AudioLoader(); + + const listener = new THREE.AudioListener(); + camera.add(listener); + + // floor + + const floorGeometry = new THREE.PlaneGeometry(10, 10); + const floorMaterial = new THREE.MeshLambertMaterial({ color: 0x4676b6 }); + + const floor = new THREE.Mesh(floorGeometry, floorMaterial); + floor.rotation.x = Math.PI * -0.5; + floor.receiveShadow = true; + scene.add(floor); + + // objects + + const count = 5; + const radius = 3; + + const ballGeometry = new THREE.SphereGeometry(0.3, 32, 16); + ballGeometry.translate(0, 0.3, 0); + const ballMaterial = new THREE.MeshLambertMaterial({ color: 0xcccccc }); + + // create objects when audio buffer is loaded + + audioLoader.load('sounds/ping_pong.mp3', function (buffer) { + for (let i = 0; i < count; i++) { + const s = (i / count) * Math.PI * 2; + + const ball = new THREE.Mesh(ballGeometry, ballMaterial); + ball.castShadow = true; + ball.userData.down = false; + + ball.position.x = radius * Math.cos(s); + ball.position.z = radius * Math.sin(s); + + const audio = new THREE.PositionalAudio(listener); + audio.setBuffer(buffer); + ball.add(audio); + + scene.add(ball); + objects.push(ball); + } + + renderer.setAnimationLoop(animate); + }); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.shadowMap.enabled = true; + container.appendChild(renderer.domElement); + + // + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 1; + controls.maxDistance = 25; + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + const time = timer.getElapsed(); + + for (let i = 0; i < objects.length; i++) { + const ball = objects[i]; + + const previousHeight = ball.position.y; + ball.position.y = Math.abs(Math.sin(i * offset + time * speed) * height); + + if (ball.position.y < previousHeight) { + ball.userData.down = true; + } else { + if (ball.userData.down === true) { + // ball changed direction from down to up + + const audio = ball.children[0]; + audio.play(); // play audio with perfect timing when ball hits the surface + ball.userData.down = false; + } + } + } + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webaudio_visualizer.ts b/examples-testing/examples/webaudio_visualizer.ts new file mode 100644 index 000000000..a3f58cb36 --- /dev/null +++ b/examples-testing/examples/webaudio_visualizer.ts @@ -0,0 +1,86 @@ +import * as THREE from 'three'; + +let scene, camera, renderer, analyser, uniforms; + +const startButton = document.getElementById('startButton'); +startButton.addEventListener('click', init); + +function init() { + const fftSize = 128; + + // + + const overlay = document.getElementById('overlay'); + overlay.remove(); + + // + + const container = document.getElementById('container'); + + scene = new THREE.Scene(); + + camera = new THREE.Camera(); + + // + + const listener = new THREE.AudioListener(); + + const audio = new THREE.Audio(listener); + const file = './sounds/376737_Skullbeatz___Bad_Cat_Maste.mp3'; + + if (/(iPad|iPhone|iPod)/g.test(navigator.userAgent)) { + const loader = new THREE.AudioLoader(); + loader.load(file, function (buffer) { + audio.setBuffer(buffer); + audio.play(); + }); + } else { + const mediaElement = new Audio(file); + mediaElement.play(); + + audio.setMediaElementSource(mediaElement); + } + + analyser = new THREE.AudioAnalyser(audio, fftSize); + + // + + uniforms = { + tAudioData: { value: new THREE.DataTexture(analyser.data, fftSize / 2, 1, THREE.RedFormat) }, + }; + + const material = new THREE.ShaderMaterial({ + uniforms: uniforms, + vertexShader: document.getElementById('vertexShader').textContent, + fragmentShader: document.getElementById('fragmentShader').textContent, + }); + + const geometry = new THREE.PlaneGeometry(1, 1); + + const mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + analyser.getFrequencyData(); + + uniforms.tAudioData.value.needsUpdate = true; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_animation_keyframes.ts b/examples-testing/examples/webgl_animation_keyframes.ts new file mode 100644 index 000000000..20a44a9ce --- /dev/null +++ b/examples-testing/examples/webgl_animation_keyframes.ts @@ -0,0 +1,96 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { Sky } from 'three/addons/objects/Sky.js'; + +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; + +let mixer; + +const timer = new THREE.Timer(); +timer.connect(document); +const container = document.getElementById('container'); + +const stats = new Stats(); +container.appendChild(stats.dom); + +const renderer = new THREE.WebGLRenderer({ antialias: true }); +renderer.setPixelRatio(window.devicePixelRatio); +renderer.setSize(window.innerWidth, window.innerHeight); +renderer.toneMapping = THREE.ACESFilmicToneMapping; +container.appendChild(renderer.domElement); + +const scene = new THREE.Scene(); + +// Sky + +const sky = new Sky(); +sky.scale.setScalar(10000); +scene.add(sky); + +const uniforms = sky.material.uniforms; +uniforms['turbidity'].value = 0; +uniforms['rayleigh'].value = 3; +uniforms['mieDirectionalG'].value = 0.7; +uniforms['cloudElevation'].value = 1; +uniforms['sunPosition'].value.set(-0.8, 0.19, 0.56); // elevation: 11, azimuth: -55 + +const pmremGenerator = new THREE.PMREMGenerator(renderer); +const environment = pmremGenerator.fromScene(sky).texture; +scene.environment = environment; + +const camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 100); +camera.position.set(5, 2, 8); + +const controls = new OrbitControls(camera, renderer.domElement); +controls.enableDamping = true; +controls.target.set(0, 0.7, 0); +controls.update(); + +const dracoLoader = new DRACOLoader(); +dracoLoader.setDecoderPath('jsm/libs/draco/gltf/'); + +const loader = new GLTFLoader(); +loader.setDRACOLoader(dracoLoader); +loader.load( + 'models/gltf/LittlestTokyo.glb', + function (gltf) { + const model = gltf.scene; + model.position.set(1, 1, 0); + model.scale.set(0.01, 0.01, 0.01); + scene.add(model); + + mixer = new THREE.AnimationMixer(model); + mixer.clipAction(gltf.animations[0]).play(); + + renderer.setAnimationLoop(animate); + }, + undefined, + function (e) { + console.error(e); + }, +); + +window.onresize = function () { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +}; + +function animate() { + timer.update(); + + const delta = timer.getDelta(); + + mixer.update(delta); + + controls.update(); + + stats.update(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_animation_multiple.ts b/examples-testing/examples/webgl_animation_multiple.ts new file mode 100644 index 000000000..87355e968 --- /dev/null +++ b/examples-testing/examples/webgl_animation_multiple.ts @@ -0,0 +1,200 @@ +import * as THREE from 'three'; + +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import * as SkeletonUtils from 'three/addons/utils/SkeletonUtils.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let camera, scene, renderer, timer; +let model, animations; + +const mixers = [], + objects = []; + +const params = { + sharedSkeleton: false, +}; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(2, 3, -6); + camera.lookAt(0, 1, 0); + + timer = new THREE.Timer(); + timer.connect(document); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xa0a0a0); + scene.fog = new THREE.Fog(0xa0a0a0, 10, 50); + + const hemiLight = new THREE.HemisphereLight(0xffffff, 0x8d8d8d, 3); + hemiLight.position.set(0, 20, 0); + scene.add(hemiLight); + + const dirLight = new THREE.DirectionalLight(0xffffff, 3); + dirLight.position.set(-3, 10, -10); + dirLight.castShadow = true; + dirLight.shadow.camera.top = 4; + dirLight.shadow.camera.bottom = -4; + dirLight.shadow.camera.left = -4; + dirLight.shadow.camera.right = 4; + dirLight.shadow.camera.near = 0.1; + dirLight.shadow.camera.far = 40; + scene.add(dirLight); + + // scene.add( new THREE.CameraHelper( dirLight.shadow.camera ) ); + + // ground + + const mesh = new THREE.Mesh( + new THREE.PlaneGeometry(200, 200), + new THREE.MeshPhongMaterial({ color: 0xcbcbcb, depthWrite: false }), + ); + mesh.rotation.x = -Math.PI / 2; + mesh.receiveShadow = true; + scene.add(mesh); + + const loader = new GLTFLoader(); + loader.load('models/gltf/Soldier.glb', function (gltf) { + model = gltf.scene; + animations = gltf.animations; + + model.traverse(function (object) { + if (object.isMesh) object.castShadow = true; + }); + + setupDefaultScene(); + }); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.shadowMap.enabled = true; + document.body.appendChild(renderer.domElement); + + window.addEventListener('resize', onWindowResize); + + const gui = new GUI(); + + gui.add(params, 'sharedSkeleton').onChange(function () { + clearScene(); + + if (params.sharedSkeleton === true) { + setupSharedSkeletonScene(); + } else { + setupDefaultScene(); + } + }); + gui.open(); +} + +function clearScene() { + for (const mixer of mixers) { + mixer.stopAllAction(); + } + + mixers.length = 0; + + // + + for (const object of objects) { + scene.remove(object); + + scene.traverse(function (child) { + if (child.isSkinnedMesh) child.skeleton.dispose(); + }); + } +} + +function setupDefaultScene() { + // three cloned models with independent skeletons. + // each model can have its own animation state + + const model1 = SkeletonUtils.clone(model); + const model2 = SkeletonUtils.clone(model); + const model3 = SkeletonUtils.clone(model); + + model1.position.x = -2; + model2.position.x = 0; + model3.position.x = 2; + + const mixer1 = new THREE.AnimationMixer(model1); + const mixer2 = new THREE.AnimationMixer(model2); + const mixer3 = new THREE.AnimationMixer(model3); + + mixer1.clipAction(animations[0]).play(); // idle + mixer2.clipAction(animations[1]).play(); // run + mixer3.clipAction(animations[3]).play(); // walk + + scene.add(model1, model2, model3); + + objects.push(model1, model2, model3); + mixers.push(mixer1, mixer2, mixer3); +} + +function setupSharedSkeletonScene() { + // three cloned models with a single shared skeleton. + // all models share the same animation state + + const sharedModel = SkeletonUtils.clone(model); + const shareSkinnedMesh = sharedModel.getObjectByName('vanguard_Mesh'); + const sharedSkeleton = shareSkinnedMesh.skeleton; + const sharedParentBone = sharedModel.getObjectByName('mixamorigHips'); + scene.add(sharedParentBone); // the bones need to be in the scene for the animation to work + + const model1 = shareSkinnedMesh.clone(); + const model2 = shareSkinnedMesh.clone(); + const model3 = shareSkinnedMesh.clone(); + + model1.bindMode = THREE.DetachedBindMode; + model2.bindMode = THREE.DetachedBindMode; + model3.bindMode = THREE.DetachedBindMode; + + const identity = new THREE.Matrix4(); + + model1.bind(sharedSkeleton, identity); + model2.bind(sharedSkeleton, identity); + model3.bind(sharedSkeleton, identity); + + model1.position.x = -2; + model2.position.x = 0; + model3.position.x = 2; + + // apply transformation from the glTF asset + + model1.scale.setScalar(0.01); + model1.rotation.x = -Math.PI * 0.5; + model2.scale.setScalar(0.01); + model2.rotation.x = -Math.PI * 0.5; + model3.scale.setScalar(0.01); + model3.rotation.x = -Math.PI * 0.5; + + // + + const mixer = new THREE.AnimationMixer(sharedParentBone); + mixer.clipAction(animations[1]).play(); + + scene.add(sharedParentBone, model1, model2, model3); + + objects.push(sharedParentBone, model1, model2, model3); + mixers.push(mixer); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + const delta = timer.getDelta(); + + for (const mixer of mixers) mixer.update(delta); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_animation_skinning_morph.ts b/examples-testing/examples/webgl_animation_skinning_morph.ts new file mode 100644 index 000000000..0ae68e7d7 --- /dev/null +++ b/examples-testing/examples/webgl_animation_skinning_morph.ts @@ -0,0 +1,190 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +let container, stats, timer, gui, mixer, actions, activeAction, previousAction; +let camera, scene, renderer, model, face; + +const api = { state: 'Walking' }; + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 100); + camera.position.set(-5, 3, 10); + camera.lookAt(0, 2, 0); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xe0e0e0); + scene.fog = new THREE.Fog(0xe0e0e0, 20, 100); + + timer = new THREE.Timer(); + timer.connect(document); + + // lights + + const hemiLight = new THREE.HemisphereLight(0xffffff, 0x8d8d8d, 3); + hemiLight.position.set(0, 20, 0); + scene.add(hemiLight); + + const dirLight = new THREE.DirectionalLight(0xffffff, 3); + dirLight.position.set(0, 20, 10); + scene.add(dirLight); + + // ground + + const mesh = new THREE.Mesh( + new THREE.PlaneGeometry(2000, 2000), + new THREE.MeshPhongMaterial({ color: 0xcbcbcb, depthWrite: false }), + ); + mesh.rotation.x = -Math.PI / 2; + scene.add(mesh); + + const grid = new THREE.GridHelper(200, 40, 0x000000, 0x000000); + grid.material.opacity = 0.2; + grid.material.transparent = true; + scene.add(grid); + + // model + + const loader = new GLTFLoader(); + loader.load( + 'models/gltf/RobotExpressive/RobotExpressive.glb', + function (gltf) { + model = gltf.scene; + scene.add(model); + + createGUI(model, gltf.animations); + }, + undefined, + function (e) { + console.error(e); + }, + ); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + window.addEventListener('resize', onWindowResize); + + // stats + stats = new Stats(); + container.appendChild(stats.dom); +} + +function createGUI(model, animations) { + const states = ['Idle', 'Walking', 'Running', 'Dance', 'Death', 'Sitting', 'Standing']; + const emotes = ['Jump', 'Yes', 'No', 'Wave', 'Punch', 'ThumbsUp']; + + gui = new GUI(); + + mixer = new THREE.AnimationMixer(model); + + actions = {}; + + for (let i = 0; i < animations.length; i++) { + const clip = animations[i]; + const action = mixer.clipAction(clip); + actions[clip.name] = action; + + if (emotes.indexOf(clip.name) >= 0 || states.indexOf(clip.name) >= 4) { + action.clampWhenFinished = true; + action.loop = THREE.LoopOnce; + } + } + + // states + + const statesFolder = gui.addFolder('States'); + + const clipCtrl = statesFolder.add(api, 'state').options(states); + + clipCtrl.onChange(function () { + fadeToAction(api.state, 0.5); + }); + + statesFolder.open(); + + // emotes + + const emoteFolder = gui.addFolder('Emotes'); + + function createEmoteCallback(name) { + api[name] = function () { + fadeToAction(name, 0.2); + + mixer.addEventListener('finished', restoreState); + }; + + emoteFolder.add(api, name); + } + + function restoreState() { + mixer.removeEventListener('finished', restoreState); + + fadeToAction(api.state, 0.2); + } + + for (let i = 0; i < emotes.length; i++) { + createEmoteCallback(emotes[i]); + } + + emoteFolder.open(); + + // expressions + + face = model.getObjectByName('Head_4'); + + const expressions = Object.keys(face.morphTargetDictionary); + const expressionFolder = gui.addFolder('Expressions'); + + for (let i = 0; i < expressions.length; i++) { + expressionFolder.add(face.morphTargetInfluences, i, 0, 1, 0.01).name(expressions[i]); + } + + activeAction = actions['Walking']; + activeAction.play(); + + expressionFolder.open(); +} + +function fadeToAction(name, duration) { + previousAction = activeAction; + activeAction = actions[name]; + + if (previousAction !== activeAction) { + previousAction.fadeOut(duration); + } + + activeAction.reset().setEffectiveTimeScale(1).setEffectiveWeight(1).fadeIn(duration).play(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + timer.update(); + + const dt = timer.getDelta(); + + if (mixer) mixer.update(dt); + + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/webgl_animation_walk.ts b/examples-testing/examples/webgl_animation_walk.ts new file mode 100644 index 000000000..b4ab9c444 --- /dev/null +++ b/examples-testing/examples/webgl_animation_walk.ts @@ -0,0 +1,379 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; + +let scene, renderer, camera, floor, orbitControls; +let group, followGroup, model, skeleton, mixer, timer; + +let actions; + +const settings = { + show_skeleton: false, + fixe_transition: true, +}; + +const PI = Math.PI; +const PI90 = Math.PI / 2; + +const controls = { + key: [0, 0], + ease: new THREE.Vector3(), + position: new THREE.Vector3(), + up: new THREE.Vector3(0, 1, 0), + rotate: new THREE.Quaternion(), + current: 'Idle', + fadeDuration: 0.5, + runVelocity: 5, + walkVelocity: 1.8, + rotateSpeed: 0.05, + floorDecale: 0, +}; + +init(); + +function init() { + const container = document.getElementById('container'); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(0, 2, -5); + + timer = new THREE.Timer(); + timer.connect(document); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x5e5d5d); + scene.fog = new THREE.Fog(0x5e5d5d, 2, 20); + + group = new THREE.Group(); + scene.add(group); + + followGroup = new THREE.Group(); + scene.add(followGroup); + + const dirLight = new THREE.DirectionalLight(0xffffff, 5); + dirLight.position.set(-2, 5, -3); + dirLight.castShadow = true; + const cam = dirLight.shadow.camera; + cam.top = cam.right = 2; + cam.bottom = cam.left = -2; + cam.near = 3; + cam.far = 8; + dirLight.shadow.mapSize.set(1024, 1024); + followGroup.add(dirLight); + followGroup.add(dirLight.target); + + //scene.add( new THREE.CameraHelper( cam ) ); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = 0.5; + renderer.shadowMap.enabled = true; + renderer.shadowMap.type = THREE.PCFShadowMap; + container.appendChild(renderer.domElement); + + orbitControls = new OrbitControls(camera, renderer.domElement); + orbitControls.target.set(0, 1, 0); + orbitControls.enableDamping = true; + orbitControls.enablePan = false; + orbitControls.maxPolarAngle = PI90 - 0.05; + orbitControls.update(); + + // EVENTS + + window.addEventListener('resize', onWindowResize); + document.addEventListener('keydown', onKeyDown); + document.addEventListener('keyup', onKeyUp); + + // DEMO + + new HDRLoader().setPath('textures/equirectangular/').load('lobe.hdr', function (texture) { + texture.mapping = THREE.EquirectangularReflectionMapping; + scene.environment = texture; + scene.environmentIntensity = 1.5; + + loadModel(); + addFloor(); + }); +} + +function addFloor() { + const size = 50; + const repeat = 16; + + const maxAnisotropy = renderer.capabilities.getMaxAnisotropy(); + + const floorT = new THREE.TextureLoader().load('textures/floors/FloorsCheckerboard_S_Diffuse.jpg'); + floorT.colorSpace = THREE.SRGBColorSpace; + floorT.repeat.set(repeat, repeat); + floorT.wrapS = floorT.wrapT = THREE.RepeatWrapping; + floorT.anisotropy = maxAnisotropy; + + const floorN = new THREE.TextureLoader().load('textures/floors/FloorsCheckerboard_S_Normal.jpg'); + floorN.repeat.set(repeat, repeat); + floorN.wrapS = floorN.wrapT = THREE.RepeatWrapping; + floorN.anisotropy = maxAnisotropy; + + const mat = new THREE.MeshStandardMaterial({ + map: floorT, + normalMap: floorN, + normalScale: new THREE.Vector2(0.5, 0.5), + color: 0x404040, + depthWrite: false, + roughness: 0.85, + }); + + const g = new THREE.PlaneGeometry(size, size, 50, 50); + g.rotateX(-PI90); + + floor = new THREE.Mesh(g, mat); + floor.receiveShadow = true; + scene.add(floor); + + controls.floorDecale = (size / repeat) * 4; + + const bulbGeometry = new THREE.SphereGeometry(0.05, 16, 8); + const bulbLight = new THREE.PointLight(0xffee88, 2, 500, 2); + + const bulbMat = new THREE.MeshStandardMaterial({ emissive: 0xffffee, emissiveIntensity: 1, color: 0x000000 }); + bulbLight.add(new THREE.Mesh(bulbGeometry, bulbMat)); + bulbLight.position.set(1, 0.1, -3); + bulbLight.castShadow = true; + floor.add(bulbLight); +} + +function loadModel() { + const loader = new GLTFLoader(); + loader.load('models/gltf/Soldier.glb', function (gltf) { + model = gltf.scene; + group.add(model); + model.rotation.y = PI; + group.rotation.y = PI; + + model.traverse(function (object) { + if (object.isMesh) { + if (object.name == 'vanguard_Mesh') { + object.castShadow = true; + object.receiveShadow = true; + //object.material.envMapIntensity = 0.5; + object.material.metalness = 1.0; + object.material.roughness = 0.2; + object.material.color.set(1, 1, 1); + object.material.metalnessMap = object.material.map; + } else { + object.material.metalness = 1; + object.material.roughness = 0; + object.material.transparent = true; + object.material.opacity = 0.8; + object.material.color.set(1, 1, 1); + } + } + }); + + // + + skeleton = new THREE.SkeletonHelper(model); + skeleton.setColors(new THREE.Color(0xe000ff), new THREE.Color(0x00e0ff)); + skeleton.visible = false; + scene.add(skeleton); + + // + + createPanel(); + + // + + const animations = gltf.animations; + + mixer = new THREE.AnimationMixer(model); + + actions = { + Idle: mixer.clipAction(animations[0]), + Walk: mixer.clipAction(animations[3]), + Run: mixer.clipAction(animations[1]), + }; + + for (const m in actions) { + actions[m].enabled = true; + actions[m].setEffectiveTimeScale(1); + if (m !== 'Idle') actions[m].setEffectiveWeight(0); + } + + actions.Idle.play(); + + animate(); + }); +} + +function updateCharacter(delta) { + const fade = controls.fadeDuration; + const key = controls.key; + const up = controls.up; + const ease = controls.ease; + const rotate = controls.rotate; + const position = controls.position; + const azimuth = orbitControls.getAzimuthalAngle(); + + const active = key[0] === 0 && key[1] === 0 ? false : true; + const play = active ? (key[2] ? 'Run' : 'Walk') : 'Idle'; + + // change animation + + if (controls.current != play) { + const current = actions[play]; + const old = actions[controls.current]; + controls.current = play; + + if (settings.fixe_transition) { + current.reset(); + current.weight = 1.0; + current.stopFading(); + old.stopFading(); + // synchro if not idle + if (play !== 'Idle') current.time = old.time * (current.getClip().duration / old.getClip().duration); + old._scheduleFading(fade, old.getEffectiveWeight(), 0); + current._scheduleFading(fade, current.getEffectiveWeight(), 1); + current.play(); + } else { + setWeight(current, 1.0); + old.fadeOut(fade); + current.reset().fadeIn(fade).play(); + } + } + + // move object + + if (controls.current !== 'Idle') { + // run/walk velocity + const velocity = controls.current == 'Run' ? controls.runVelocity : controls.walkVelocity; + + // direction with key + ease.set(key[1], 0, key[0]).multiplyScalar(velocity * delta); + + // calculate camera direction + const angle = unwrapRad(Math.atan2(ease.x, ease.z) + azimuth); + rotate.setFromAxisAngle(up, angle); + + // apply camera angle on ease + controls.ease.applyAxisAngle(up, azimuth); + + position.add(ease); + camera.position.add(ease); + + group.position.copy(position); + group.quaternion.rotateTowards(rotate, controls.rotateSpeed); + + orbitControls.target.copy(position).add({ x: 0, y: 1, z: 0 }); + followGroup.position.copy(position); + + // Move the floor without any limit + const dx = position.x - floor.position.x; + const dz = position.z - floor.position.z; + if (Math.abs(dx) > controls.floorDecale) floor.position.x += dx; + if (Math.abs(dz) > controls.floorDecale) floor.position.z += dz; + } + + if (mixer) mixer.update(delta); + + orbitControls.update(); +} + +function unwrapRad(r) { + return Math.atan2(Math.sin(r), Math.cos(r)); +} + +function createPanel() { + const panel = new GUI({ width: 310 }); + + panel.add(settings, 'show_skeleton').onChange(b => { + skeleton.visible = b; + }); + + panel.add(settings, 'fixe_transition'); +} + +function setWeight(action, weight) { + action.enabled = true; + action.setEffectiveTimeScale(1); + action.setEffectiveWeight(weight); +} + +function onKeyDown(event) { + const key = controls.key; + switch (event.code) { + case 'ArrowUp': + case 'KeyW': + case 'KeyZ': + key[0] = -1; + break; + case 'ArrowDown': + case 'KeyS': + key[0] = 1; + break; + case 'ArrowLeft': + case 'KeyA': + case 'KeyQ': + key[1] = -1; + break; + case 'ArrowRight': + case 'KeyD': + key[1] = 1; + break; + case 'ShiftLeft': + case 'ShiftRight': + key[2] = 1; + break; + } +} + +function onKeyUp(event) { + const key = controls.key; + switch (event.code) { + case 'ArrowUp': + case 'KeyW': + case 'KeyZ': + key[0] = key[0] < 0 ? 0 : key[0]; + break; + case 'ArrowDown': + case 'KeyS': + key[0] = key[0] > 0 ? 0 : key[0]; + break; + case 'ArrowLeft': + case 'KeyA': + case 'KeyQ': + key[1] = key[1] < 0 ? 0 : key[1]; + break; + case 'ArrowRight': + case 'KeyD': + key[1] = key[1] > 0 ? 0 : key[1]; + break; + case 'ShiftLeft': + case 'ShiftRight': + key[2] = 0; + break; + } +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + // Render loop + + const delta = timer.getDelta(); + + updateCharacter(delta); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_buffergeometry.ts b/examples-testing/examples/webgl_buffergeometry.ts new file mode 100644 index 000000000..28b2c96a4 --- /dev/null +++ b/examples-testing/examples/webgl_buffergeometry.ts @@ -0,0 +1,178 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +let container, stats; + +let camera, scene, renderer; + +let mesh; + +init(); +animate(); + +function init() { + container = document.getElementById('container'); + + // + + camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 1, 3500); + camera.position.z = 2750; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x050505); + scene.fog = new THREE.Fog(0x050505, 2000, 3500); + + // + + scene.add(new THREE.AmbientLight(0xcccccc)); + + const light1 = new THREE.DirectionalLight(0xffffff, 1.5); + light1.position.set(1, 1, 1); + scene.add(light1); + + const light2 = new THREE.DirectionalLight(0xffffff, 4.5); + light2.position.set(0, -1, 0); + scene.add(light2); + + // + + const triangles = 160000; + + const geometry = new THREE.BufferGeometry(); + + const positions = []; + const normals = []; + const colors = []; + + const color = new THREE.Color(); + + const n = 800, + n2 = n / 2; // triangles spread in the cube + const d = 12, + d2 = d / 2; // individual triangle size + + const pA = new THREE.Vector3(); + const pB = new THREE.Vector3(); + const pC = new THREE.Vector3(); + + const cb = new THREE.Vector3(); + const ab = new THREE.Vector3(); + + for (let i = 0; i < triangles; i++) { + // positions + + const x = Math.random() * n - n2; + const y = Math.random() * n - n2; + const z = Math.random() * n - n2; + + const ax = x + Math.random() * d - d2; + const ay = y + Math.random() * d - d2; + const az = z + Math.random() * d - d2; + + const bx = x + Math.random() * d - d2; + const by = y + Math.random() * d - d2; + const bz = z + Math.random() * d - d2; + + const cx = x + Math.random() * d - d2; + const cy = y + Math.random() * d - d2; + const cz = z + Math.random() * d - d2; + + positions.push(ax, ay, az); + positions.push(bx, by, bz); + positions.push(cx, cy, cz); + + // flat face normals + + pA.set(ax, ay, az); + pB.set(bx, by, bz); + pC.set(cx, cy, cz); + + cb.subVectors(pC, pB); + ab.subVectors(pA, pB); + cb.cross(ab); + + cb.normalize(); + + const nx = cb.x; + const ny = cb.y; + const nz = cb.z; + + normals.push(nx, ny, nz); + normals.push(nx, ny, nz); + normals.push(nx, ny, nz); + + // colors + + const vx = x / n + 0.5; + const vy = y / n + 0.5; + const vz = z / n + 0.5; + + color.setRGB(vx, vy, vz); + + const alpha = Math.random(); + + colors.push(color.r, color.g, color.b, alpha); + colors.push(color.r, color.g, color.b, alpha); + colors.push(color.r, color.g, color.b, alpha); + } + + function disposeArray() { + this.array = null; + } + + geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3).onUpload(disposeArray)); + geometry.setAttribute('normal', new THREE.Float32BufferAttribute(normals, 3).onUpload(disposeArray)); + geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 4).onUpload(disposeArray)); + + geometry.computeBoundingSphere(); + + const material = new THREE.MeshPhongMaterial({ + color: 0xd5d5d5, + specular: 0xffffff, + shininess: 250, + side: THREE.DoubleSide, + vertexColors: true, + transparent: true, + }); + + mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + // + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + const time = Date.now() * 0.001; + + mesh.rotation.x = time * 0.25; + mesh.rotation.y = time * 0.5; + + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/webgl_buffergeometry_attributes_integer.ts b/examples-testing/examples/webgl_buffergeometry_attributes_integer.ts new file mode 100644 index 000000000..00490b716 --- /dev/null +++ b/examples-testing/examples/webgl_buffergeometry_attributes_integer.ts @@ -0,0 +1,142 @@ +import * as THREE from 'three'; + +let camera, scene, renderer, mesh; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 1, 3500); + camera.position.z = 2500; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x050505); + scene.fog = new THREE.Fog(0x050505, 2000, 3500); + + // geometry + + const triangles = 10000; + + const geometry = new THREE.BufferGeometry(); + + const positions = []; + const uvs = []; + const textureIndices = []; + + const n = 800, + n2 = n / 2; // triangles spread in the cube + const d = 50, + d2 = d / 2; // individual triangle size + + for (let i = 0; i < triangles; i++) { + // positions + + const x = Math.random() * n - n2; + const y = Math.random() * n - n2; + const z = Math.random() * n - n2; + + const ax = x + Math.random() * d - d2; + const ay = y + Math.random() * d - d2; + const az = z + Math.random() * d - d2; + + const bx = x + Math.random() * d - d2; + const by = y + Math.random() * d - d2; + const bz = z + Math.random() * d - d2; + + const cx = x + Math.random() * d - d2; + const cy = y + Math.random() * d - d2; + const cz = z + Math.random() * d - d2; + + positions.push(ax, ay, az); + positions.push(bx, by, bz); + positions.push(cx, cy, cz); + + // uvs + + uvs.push(0, 0); + uvs.push(0.5, 1); + uvs.push(1, 0); + + // texture indices + + const t = i % 3; + textureIndices.push(t, t, t); + } + + geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)); + geometry.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2)); + geometry.setAttribute('textureIndex', new THREE.Int16BufferAttribute(textureIndices, 1)); + geometry.attributes.textureIndex.gpuType = THREE.IntType; + + geometry.computeBoundingSphere(); + + // material + + const loader = new THREE.TextureLoader(); + + const map1 = loader.load('textures/crate.gif'); + const map2 = loader.load('textures/floors/FloorsCheckerboard_S_Diffuse.jpg'); + const map3 = loader.load('textures/terrain/grasslight-big.jpg'); + + const material = new THREE.ShaderMaterial({ + uniforms: { + uTextures: { + value: [map1, map2, map3], + }, + }, + vertexShader: /* glsl */ ` + in int textureIndex; + + flat out int vIndex; // "flat" indicates that the value will not be interpolated (required for integer attributes) + out vec2 vUv; + + void main() { + + vIndex = textureIndex; + vUv = uv; + + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + + } + `, + fragmentShader: /* glsl */ ` + flat in int vIndex; + in vec2 vUv; + + uniform sampler2D uTextures[ 3 ]; + + out vec4 outColor; + + void main() { + + if ( vIndex == 0 ) outColor = texture( uTextures[ 0 ], vUv ); + else if ( vIndex == 1 ) outColor = texture( uTextures[ 1 ], vUv ); + else if ( vIndex == 2 ) outColor = texture( uTextures[ 2 ], vUv ); + + } + `, + side: THREE.DoubleSide, + glslVersion: THREE.GLSL3, + }); + + // mesh + + mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + // renderer + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); +} + +function animate() { + const time = Date.now() * 0.001; + + mesh.rotation.x = time * 0.25; + mesh.rotation.y = time * 0.5; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_buffergeometry_attributes_none.ts b/examples-testing/examples/webgl_buffergeometry_attributes_none.ts new file mode 100644 index 000000000..a1424e871 --- /dev/null +++ b/examples-testing/examples/webgl_buffergeometry_attributes_none.ts @@ -0,0 +1,56 @@ +import * as THREE from 'three'; + +let camera, scene, renderer, mesh; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 1, 3500); + camera.position.z = 4; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x050505); + scene.fog = new THREE.Fog(0x050505, 2000, 3500); + + // geometry + + const triangleCount = 10000; + const vertexCountPerTriangle = 3; + const vertexCount = triangleCount * vertexCountPerTriangle; + + const geometry = new THREE.BufferGeometry(); + geometry.setDrawRange(0, vertexCount); + + // material + + const material = new THREE.RawShaderMaterial({ + uniforms: { + seed: { value: 42 }, + }, + vertexShader: document.getElementById('vertexShader').textContent, + fragmentShader: document.getElementById('fragmentShader').textContent, + side: THREE.DoubleSide, + glslVersion: THREE.GLSL3, + }); + + // mesh + + mesh = new THREE.Mesh(geometry, material); + mesh.frustumCulled = false; + scene.add(mesh); + + // renderer + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); +} + +function animate(time) { + mesh.rotation.x = (time / 1000.0) * 0.25; + mesh.rotation.y = (time / 1000.0) * 0.5; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_buffergeometry_custom_attributes_particles.ts b/examples-testing/examples/webgl_buffergeometry_custom_attributes_particles.ts new file mode 100644 index 000000000..0dffa65cc --- /dev/null +++ b/examples-testing/examples/webgl_buffergeometry_custom_attributes_particles.ts @@ -0,0 +1,103 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +let renderer, scene, camera, stats; + +let particleSystem, uniforms, geometry; + +const particles = 100000; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 10000); + camera.position.z = 300; + + scene = new THREE.Scene(); + + uniforms = { + pointTexture: { value: new THREE.TextureLoader().load('textures/sprites/spark1.png') }, + }; + + const shaderMaterial = new THREE.ShaderMaterial({ + uniforms: uniforms, + vertexShader: document.getElementById('vertexshader').textContent, + fragmentShader: document.getElementById('fragmentshader').textContent, + + blending: THREE.AdditiveBlending, + depthTest: false, + transparent: true, + vertexColors: true, + }); + + const radius = 200; + + geometry = new THREE.BufferGeometry(); + + const positions = []; + const colors = []; + const sizes = []; + + const color = new THREE.Color(); + + for (let i = 0; i < particles; i++) { + positions.push((Math.random() * 2 - 1) * radius); + positions.push((Math.random() * 2 - 1) * radius); + positions.push((Math.random() * 2 - 1) * radius); + + color.setHSL(i / particles, 1.0, 0.5); + + colors.push(color.r, color.g, color.b); + + sizes.push(20); + } + + geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)); + geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); + geometry.setAttribute('size', new THREE.Float32BufferAttribute(sizes, 1).setUsage(THREE.DynamicDrawUsage)); + + particleSystem = new THREE.Points(geometry, shaderMaterial); + + scene.add(particleSystem); + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + + const container = document.getElementById('container'); + container.appendChild(renderer.domElement); + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + const time = Date.now() * 0.005; + + particleSystem.rotation.z = 0.01 * time; + + const sizes = geometry.attributes.size.array; + + for (let i = 0; i < particles; i++) { + sizes[i] = 10 * (1 + Math.sin(0.1 * i + time)); + } + + geometry.attributes.size.needsUpdate = true; + + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/webgl_buffergeometry_drawrange.ts b/examples-testing/examples/webgl_buffergeometry_drawrange.ts new file mode 100644 index 000000000..142ff43bf --- /dev/null +++ b/examples-testing/examples/webgl_buffergeometry_drawrange.ts @@ -0,0 +1,239 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let group; +let container, stats; +const particlesData = []; +let camera, scene, renderer; +let positions, colors; +let particles; +let pointCloud; +let particlePositions; +let linesMesh; + +const maxParticleCount = 1000; +let particleCount = 500; +const r = 800; +const rHalf = r / 2; + +const effectController = { + showDots: true, + showLines: true, + minDistance: 150, + limitConnections: false, + maxConnections: 20, + particleCount: 500, +}; + +init(); + +function initGUI() { + const gui = new GUI(); + + gui.add(effectController, 'showDots').onChange(function (value) { + pointCloud.visible = value; + }); + gui.add(effectController, 'showLines').onChange(function (value) { + linesMesh.visible = value; + }); + gui.add(effectController, 'minDistance', 10, 300); + gui.add(effectController, 'limitConnections'); + gui.add(effectController, 'maxConnections', 0, 30, 1); + gui.add(effectController, 'particleCount', 0, maxParticleCount, 1).onChange(function (value) { + particleCount = value; + particles.setDrawRange(0, particleCount); + }); +} + +function init() { + initGUI(); + + container = document.getElementById('container'); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 4000); + camera.position.z = 1750; + + const controls = new OrbitControls(camera, container); + controls.minDistance = 1000; + controls.maxDistance = 3000; + + scene = new THREE.Scene(); + + group = new THREE.Group(); + scene.add(group); + + const helper = new THREE.BoxHelper(new THREE.Mesh(new THREE.BoxGeometry(r, r, r))); + helper.material.color.setHex(0x474747); + helper.material.blending = THREE.AdditiveBlending; + helper.material.transparent = true; + group.add(helper); + + const segments = maxParticleCount * maxParticleCount; + + positions = new Float32Array(segments * 3); + colors = new Float32Array(segments * 3); + + const pMaterial = new THREE.PointsMaterial({ + color: 0xffffff, + size: 3, + blending: THREE.AdditiveBlending, + transparent: true, + sizeAttenuation: false, + }); + + particles = new THREE.BufferGeometry(); + particlePositions = new Float32Array(maxParticleCount * 3); + + for (let i = 0; i < maxParticleCount; i++) { + const x = Math.random() * r - r / 2; + const y = Math.random() * r - r / 2; + const z = Math.random() * r - r / 2; + + particlePositions[i * 3] = x; + particlePositions[i * 3 + 1] = y; + particlePositions[i * 3 + 2] = z; + + // add it to the geometry + particlesData.push({ + velocity: new THREE.Vector3(-1 + Math.random() * 2, -1 + Math.random() * 2, -1 + Math.random() * 2), + numConnections: 0, + }); + } + + particles.setDrawRange(0, particleCount); + particles.setAttribute( + 'position', + new THREE.BufferAttribute(particlePositions, 3).setUsage(THREE.DynamicDrawUsage), + ); + + // create the particle system + pointCloud = new THREE.Points(particles, pMaterial); + group.add(pointCloud); + + const geometry = new THREE.BufferGeometry(); + + geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3).setUsage(THREE.DynamicDrawUsage)); + geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3).setUsage(THREE.DynamicDrawUsage)); + + geometry.computeBoundingSphere(); + + geometry.setDrawRange(0, 0); + + const material = new THREE.LineBasicMaterial({ + vertexColors: true, + blending: THREE.AdditiveBlending, + transparent: true, + }); + + linesMesh = new THREE.LineSegments(geometry, material); + group.add(linesMesh); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + // + + stats = new Stats(); + container.appendChild(stats.dom); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + let vertexpos = 0; + let colorpos = 0; + let numConnected = 0; + + for (let i = 0; i < particleCount; i++) particlesData[i].numConnections = 0; + + for (let i = 0; i < particleCount; i++) { + // get the particle + const particleData = particlesData[i]; + + particlePositions[i * 3] += particleData.velocity.x; + particlePositions[i * 3 + 1] += particleData.velocity.y; + particlePositions[i * 3 + 2] += particleData.velocity.z; + + if (particlePositions[i * 3 + 1] < -rHalf || particlePositions[i * 3 + 1] > rHalf) + particleData.velocity.y = -particleData.velocity.y; + + if (particlePositions[i * 3] < -rHalf || particlePositions[i * 3] > rHalf) + particleData.velocity.x = -particleData.velocity.x; + + if (particlePositions[i * 3 + 2] < -rHalf || particlePositions[i * 3 + 2] > rHalf) + particleData.velocity.z = -particleData.velocity.z; + + if (effectController.limitConnections && particleData.numConnections >= effectController.maxConnections) + continue; + + // Check collision + for (let j = i + 1; j < particleCount; j++) { + const particleDataB = particlesData[j]; + if (effectController.limitConnections && particleDataB.numConnections >= effectController.maxConnections) + continue; + + const dx = particlePositions[i * 3] - particlePositions[j * 3]; + const dy = particlePositions[i * 3 + 1] - particlePositions[j * 3 + 1]; + const dz = particlePositions[i * 3 + 2] - particlePositions[j * 3 + 2]; + const dist = Math.sqrt(dx * dx + dy * dy + dz * dz); + + if (dist < effectController.minDistance) { + particleData.numConnections++; + particleDataB.numConnections++; + + const alpha = 1.0 - dist / effectController.minDistance; + + positions[vertexpos++] = particlePositions[i * 3]; + positions[vertexpos++] = particlePositions[i * 3 + 1]; + positions[vertexpos++] = particlePositions[i * 3 + 2]; + + positions[vertexpos++] = particlePositions[j * 3]; + positions[vertexpos++] = particlePositions[j * 3 + 1]; + positions[vertexpos++] = particlePositions[j * 3 + 2]; + + colors[colorpos++] = alpha; + colors[colorpos++] = alpha; + colors[colorpos++] = alpha; + + colors[colorpos++] = alpha; + colors[colorpos++] = alpha; + colors[colorpos++] = alpha; + + numConnected++; + } + } + } + + linesMesh.geometry.setDrawRange(0, numConnected * 2); + linesMesh.geometry.attributes.position.needsUpdate = true; + linesMesh.geometry.attributes.color.needsUpdate = true; + + pointCloud.geometry.attributes.position.needsUpdate = true; + + render(); + + stats.update(); +} + +function render() { + const time = Date.now() * 0.001; + + group.rotation.y = time * 0.1; + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_buffergeometry_glbufferattribute.ts b/examples-testing/examples/webgl_buffergeometry_glbufferattribute.ts new file mode 100644 index 000000000..80fa828bb --- /dev/null +++ b/examples-testing/examples/webgl_buffergeometry_glbufferattribute.ts @@ -0,0 +1,140 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +let container, stats; + +let camera, scene, renderer; + +let points; + +const particles = 300000; +let drawCount = 10000; + +init(); +animate(); + +function init() { + container = document.getElementById('container'); + + // + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + + container.appendChild(renderer.domElement); + + // + + camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 5, 3500); + camera.position.z = 2750; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x050505); + scene.fog = new THREE.Fog(0x050505, 2000, 3500); + + // + + const geometry = new THREE.BufferGeometry(); + + const positions = []; + const positions2 = []; + const colors = []; + + const color = new THREE.Color(); + + const n = 1000, + n2 = n / 2; // particles spread in the cube + + for (let i = 0; i < particles; i++) { + // positions + + const x = Math.random() * n - n2; + const y = Math.random() * n - n2; + const z = Math.random() * n - n2; + + positions.push(x, y, z); + positions2.push(z * 0.3, x * 0.3, y * 0.3); + + // colors + + const vx = x / n + 0.5; + const vy = y / n + 0.5; + const vz = z / n + 0.5; + + color.setRGB(vx, vy, vz, THREE.SRGBColorSpace); + + const hex = color.getHex(THREE.LinearSRGBColorSpace); + colors.push((hex >> 16) & 255, (hex >> 8) & 255, hex & 255); + } + + const gl = renderer.getContext(); + + const pos = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, pos); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW); + + const pos2 = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, pos2); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions2), gl.STATIC_DRAW); + + const rgb = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, rgb); + gl.bufferData(gl.ARRAY_BUFFER, new Uint8Array(colors), gl.STATIC_DRAW); + + const posAttr1 = new THREE.GLBufferAttribute(pos, gl.FLOAT, 3, 4, particles); + const posAttr2 = new THREE.GLBufferAttribute(pos2, gl.FLOAT, 3, 4, particles); + geometry.setAttribute('position', posAttr1); + + setInterval(function () { + const attr = geometry.getAttribute('position'); + + geometry.setAttribute('position', attr === posAttr1 ? posAttr2 : posAttr1); + }, 2000); + + geometry.setAttribute('color', new THREE.GLBufferAttribute(rgb, gl.UNSIGNED_BYTE, 3, 1, particles, true)); + + // + + const material = new THREE.PointsMaterial({ size: 15, vertexColors: true }); + + points = new THREE.Points(geometry, material); + + geometry.boundingSphere = new THREE.Sphere().set(new THREE.Vector3(), 500); + + scene.add(points); + + // + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + drawCount = (Math.max(5000, drawCount) + Math.floor(500 * Math.random())) % particles; + points.geometry.setDrawRange(0, drawCount); + + const time = Date.now() * 0.001; + + points.rotation.x = time * 0.1; + points.rotation.y = time * 0.2; + + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/webgl_buffergeometry_indexed.ts b/examples-testing/examples/webgl_buffergeometry_indexed.ts new file mode 100644 index 000000000..a2f9f3795 --- /dev/null +++ b/examples-testing/examples/webgl_buffergeometry_indexed.ts @@ -0,0 +1,137 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let camera, scene, renderer, stats; + +let mesh; + +init(); + +function init() { + // + + camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 1, 3500); + camera.position.z = 64; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x050505); + + // + + const light = new THREE.HemisphereLight(); + light.intensity = 3; + scene.add(light); + + // + + const geometry = new THREE.BufferGeometry(); + + const indices = []; + + const vertices = []; + const normals = []; + const colors = []; + + const size = 20; + const segments = 10; + + const halfSize = size / 2; + const segmentSize = size / segments; + + const _color = new THREE.Color(); + + // generate vertices, normals and color data for a simple grid geometry + + for (let i = 0; i <= segments; i++) { + const y = i * segmentSize - halfSize; + + for (let j = 0; j <= segments; j++) { + const x = j * segmentSize - halfSize; + + vertices.push(x, -y, 0); + normals.push(0, 0, 1); + + const r = x / size + 0.5; + const g = y / size + 0.5; + + _color.setRGB(r, g, 1, THREE.SRGBColorSpace); + + colors.push(_color.r, _color.g, _color.b); + } + } + + // generate indices (data for element array buffer) + + for (let i = 0; i < segments; i++) { + for (let j = 0; j < segments; j++) { + const a = i * (segments + 1) + (j + 1); + const b = i * (segments + 1) + j; + const c = (i + 1) * (segments + 1) + j; + const d = (i + 1) * (segments + 1) + (j + 1); + + // generate two faces (triangles) per iteration + + indices.push(a, b, d); // face one + indices.push(b, c, d); // face two + } + } + + // + + geometry.setIndex(indices); + geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); + geometry.setAttribute('normal', new THREE.Float32BufferAttribute(normals, 3)); + geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); + + const material = new THREE.MeshPhongMaterial({ + side: THREE.DoubleSide, + vertexColors: true, + }); + + mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // + + stats = new Stats(); + document.body.appendChild(stats.dom); + + // + + const gui = new GUI(); + gui.add(material, 'wireframe'); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + const time = Date.now() * 0.001; + + mesh.rotation.x = time * 0.25; + mesh.rotation.y = time * 0.5; + + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/webgl_buffergeometry_instancing.ts b/examples-testing/examples/webgl_buffergeometry_instancing.ts new file mode 100644 index 000000000..b27f500f0 --- /dev/null +++ b/examples-testing/examples/webgl_buffergeometry_instancing.ts @@ -0,0 +1,138 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let container, stats; + +let camera, scene, renderer; + +init(); + +function init() { + container = document.getElementById('container'); + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10); + camera.position.z = 2; + + scene = new THREE.Scene(); + + // geometry + + const vector = new THREE.Vector4(); + + const instances = 50000; + + const positions = []; + const offsets = []; + const colors = []; + const orientationsStart = []; + const orientationsEnd = []; + + positions.push(0.025, -0.025, 0); + positions.push(-0.025, 0.025, 0); + positions.push(0, 0, 0.025); + + // instanced attributes + + for (let i = 0; i < instances; i++) { + // offsets + + offsets.push(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5); + + // colors + + colors.push(Math.random(), Math.random(), Math.random(), Math.random()); + + // orientation start + + vector.set(Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1); + vector.normalize(); + + orientationsStart.push(vector.x, vector.y, vector.z, vector.w); + + // orientation end + + vector.set(Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1); + vector.normalize(); + + orientationsEnd.push(vector.x, vector.y, vector.z, vector.w); + } + + const geometry = new THREE.InstancedBufferGeometry(); + geometry.instanceCount = instances; // set so its initialized for dat.GUI, will be set in first draw otherwise + + geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)); + + geometry.setAttribute('offset', new THREE.InstancedBufferAttribute(new Float32Array(offsets), 3)); + geometry.setAttribute('color', new THREE.InstancedBufferAttribute(new Float32Array(colors), 4)); + geometry.setAttribute( + 'orientationStart', + new THREE.InstancedBufferAttribute(new Float32Array(orientationsStart), 4), + ); + geometry.setAttribute('orientationEnd', new THREE.InstancedBufferAttribute(new Float32Array(orientationsEnd), 4)); + + // material + + const material = new THREE.RawShaderMaterial({ + uniforms: { + time: { value: 1.0 }, + sineTime: { value: 1.0 }, + }, + vertexShader: document.getElementById('vertexShader').textContent, + fragmentShader: document.getElementById('fragmentShader').textContent, + side: THREE.DoubleSide, + forceSinglePass: true, + transparent: true, + }); + + // + + const mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + // + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + // + + const gui = new GUI({ width: 350 }); + gui.add(geometry, 'instanceCount', 0, instances); + + // + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + const time = performance.now(); + + const object = scene.children[0]; + + object.rotation.y = time * 0.0005; + object.material.uniforms['time'].value = time * 0.005; + object.material.uniforms['sineTime'].value = Math.sin(object.material.uniforms['time'].value * 0.05); + + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/webgl_buffergeometry_instancing_billboards.ts b/examples-testing/examples/webgl_buffergeometry_instancing_billboards.ts new file mode 100644 index 000000000..2158dff39 --- /dev/null +++ b/examples-testing/examples/webgl_buffergeometry_instancing_billboards.ts @@ -0,0 +1,86 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +let container, stats; + +let camera, scene, renderer; +let geometry, material, mesh; + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 5000); + camera.position.z = 1400; + + scene = new THREE.Scene(); + + const circleGeometry = new THREE.CircleGeometry(1, 6); + + geometry = new THREE.InstancedBufferGeometry(); + geometry.index = circleGeometry.index; + geometry.attributes = circleGeometry.attributes; + + const particleCount = 75000; + + const translateArray = new Float32Array(particleCount * 3); + + for (let i = 0, i3 = 0, l = particleCount; i < l; i++, i3 += 3) { + translateArray[i3 + 0] = Math.random() * 2 - 1; + translateArray[i3 + 1] = Math.random() * 2 - 1; + translateArray[i3 + 2] = Math.random() * 2 - 1; + } + + geometry.setAttribute('translate', new THREE.InstancedBufferAttribute(translateArray, 3)); + + material = new THREE.RawShaderMaterial({ + uniforms: { + map: { value: new THREE.TextureLoader().load('textures/sprites/circle.png') }, + time: { value: 0.0 }, + }, + vertexShader: document.getElementById('vshader').textContent, + fragmentShader: document.getElementById('fshader').textContent, + depthTest: true, + depthWrite: true, + }); + + mesh = new THREE.Mesh(geometry, material); + mesh.scale.set(500, 500, 500); + scene.add(mesh); + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + stats = new Stats(); + container.appendChild(stats.dom); + + window.addEventListener('resize', onWindowResize); + + return true; +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + const time = performance.now() * 0.0005; + + material.uniforms['time'].value = time; + + mesh.rotation.x = time * 0.2; + mesh.rotation.y = time * 0.4; + + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/webgl_buffergeometry_instancing_interleaved.ts b/examples-testing/examples/webgl_buffergeometry_instancing_interleaved.ts new file mode 100644 index 000000000..bef2c264d --- /dev/null +++ b/examples-testing/examples/webgl_buffergeometry_instancing_interleaved.ts @@ -0,0 +1,152 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +let container, stats; +let camera, scene, renderer, mesh; + +const instances = 5000; +let lastTime = 0; + +const moveQ = new THREE.Quaternion(0.5, 0.5, 0.5, 0.0).normalize(); +const tmpQ = new THREE.Quaternion(); +const tmpM = new THREE.Matrix4(); +const currentM = new THREE.Matrix4(); + +init(); + +function init() { + container = document.getElementById('container'); + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1000); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x101010); + + // geometry + + const geometry = new THREE.InstancedBufferGeometry(); + + // per mesh data x,y,z,w,u,v,s,t for 4-element alignment + // only use x,y,z and u,v; but x, y, z, nx, ny, nz, u, v would be a good layout + const vertexBuffer = new THREE.InterleavedBuffer( + new Float32Array([ + // Front + -1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, -1, -1, 1, 0, 0, 1, 0, 0, 1, -1, 1, 0, 1, 1, 0, 0, + // Back + 1, 1, -1, 0, 1, 0, 0, 0, -1, 1, -1, 0, 0, 0, 0, 0, 1, -1, -1, 0, 1, 1, 0, 0, -1, -1, -1, 0, 0, 1, 0, 0, + // Left + -1, 1, -1, 0, 1, 1, 0, 0, -1, 1, 1, 0, 1, 0, 0, 0, -1, -1, -1, 0, 0, 1, 0, 0, -1, -1, 1, 0, 0, 0, 0, 0, + // Right + 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, -1, 0, 1, 1, 0, 0, 1, -1, 1, 0, 0, 0, 0, 0, 1, -1, -1, 0, 0, 1, 0, 0, + // Top + -1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, -1, 1, -1, 0, 0, 1, 0, 0, 1, 1, -1, 0, 1, 1, 0, 0, + // Bottom + 1, -1, 1, 0, 1, 0, 0, 0, -1, -1, 1, 0, 0, 0, 0, 0, 1, -1, -1, 0, 1, 1, 0, 0, -1, -1, -1, 0, 0, 1, 0, 0, + ]), + 8, + ); + + // Use vertexBuffer, starting at offset 0, 3 items in position attribute + const positions = new THREE.InterleavedBufferAttribute(vertexBuffer, 3, 0); + geometry.setAttribute('position', positions); + // Use vertexBuffer, starting at offset 4, 2 items in uv attribute + const uvs = new THREE.InterleavedBufferAttribute(vertexBuffer, 2, 4); + geometry.setAttribute('uv', uvs); + + const indices = new Uint16Array([ + 0, 2, 1, 2, 3, 1, 4, 6, 5, 6, 7, 5, 8, 10, 9, 10, 11, 9, 12, 14, 13, 14, 15, 13, 16, 17, 18, 18, 17, 19, 20, 21, + 22, 22, 21, 23, + ]); + + geometry.setIndex(new THREE.BufferAttribute(indices, 1)); + + // material + + const material = new THREE.MeshBasicMaterial(); + material.map = new THREE.TextureLoader().load('textures/crate.gif'); + material.map.colorSpace = THREE.SRGBColorSpace; + material.map.flipY = false; + + // per instance data + + const matrix = new THREE.Matrix4(); + const offset = new THREE.Vector3(); + const orientation = new THREE.Quaternion(); + const scale = new THREE.Vector3(1, 1, 1); + let x, y, z, w; + + mesh = new THREE.InstancedMesh(geometry, material, instances); + + for (let i = 0; i < instances; i++) { + // offsets + + x = Math.random() * 100 - 50; + y = Math.random() * 100 - 50; + z = Math.random() * 100 - 50; + + offset.set(x, y, z).normalize(); + offset.multiplyScalar(5); // move out at least 5 units from center in current direction + offset.set(x + offset.x, y + offset.y, z + offset.z); + + // orientations + + x = Math.random() * 2 - 1; + y = Math.random() * 2 - 1; + z = Math.random() * 2 - 1; + w = Math.random() * 2 - 1; + + orientation.set(x, y, z, w).normalize(); + + matrix.compose(offset, orientation, scale); + + mesh.setMatrixAt(i, matrix); + } + + scene.add(mesh); + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + stats = new Stats(); + container.appendChild(stats.dom); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + const time = performance.now(); + + mesh.rotation.y = time * 0.00005; + + const delta = (time - lastTime) / 5000; + tmpQ.set(moveQ.x * delta, moveQ.y * delta, moveQ.z * delta, 1).normalize(); + tmpM.makeRotationFromQuaternion(tmpQ); + + for (let i = 0, il = instances; i < il; i++) { + mesh.getMatrixAt(i, currentM); + currentM.multiply(tmpM); + mesh.setMatrixAt(i, currentM); + } + + mesh.instanceMatrix.needsUpdate = true; + mesh.computeBoundingSphere(); + + lastTime = time; + + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/webgl_buffergeometry_lines.ts b/examples-testing/examples/webgl_buffergeometry_lines.ts new file mode 100644 index 000000000..d039e0112 --- /dev/null +++ b/examples-testing/examples/webgl_buffergeometry_lines.ts @@ -0,0 +1,121 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +let container, stats, timer; + +let camera, scene, renderer; + +let line; + +const segments = 10000; +const r = 800; +let t = 0; + +init(); + +function init() { + container = document.getElementById('container'); + + // + + camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 1, 4000); + camera.position.z = 2750; + + scene = new THREE.Scene(); + + timer = new THREE.Timer(); + timer.connect(document); + + const geometry = new THREE.BufferGeometry(); + const material = new THREE.LineBasicMaterial({ vertexColors: true }); + + const positions = []; + const colors = []; + + for (let i = 0; i < segments; i++) { + const x = Math.random() * r - r / 2; + const y = Math.random() * r - r / 2; + const z = Math.random() * r - r / 2; + + // positions + + positions.push(x, y, z); + + // colors + + colors.push(x / r + 0.5); + colors.push(y / r + 0.5); + colors.push(z / r + 0.5); + } + + geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)); + geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); + generateMorphTargets(geometry); + + geometry.computeBoundingSphere(); + + line = new THREE.Line(geometry, material); + scene.add(line); + + // + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + + container.appendChild(renderer.domElement); + + // + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + timer.update(); + + const delta = timer.getDelta(); + const time = timer.getElapsed(); + + line.rotation.x = time * 0.25; + line.rotation.y = time * 0.5; + + t += delta * 0.5; + line.morphTargetInfluences[0] = Math.abs(Math.sin(t)); + + renderer.render(scene, camera); + + stats.update(); +} + +function generateMorphTargets(geometry) { + const data = []; + + for (let i = 0; i < segments; i++) { + const x = Math.random() * r - r / 2; + const y = Math.random() * r - r / 2; + const z = Math.random() * r - r / 2; + + data.push(x, y, z); + } + + const morphTarget = new THREE.Float32BufferAttribute(data, 3); + morphTarget.name = 'target1'; + + geometry.morphAttributes.position = [morphTarget]; +} diff --git a/examples-testing/examples/webgl_buffergeometry_lines_indexed.ts b/examples-testing/examples/webgl_buffergeometry_lines_indexed.ts new file mode 100644 index 000000000..58296087e --- /dev/null +++ b/examples-testing/examples/webgl_buffergeometry_lines_indexed.ts @@ -0,0 +1,179 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +let container, stats; + +let camera, scene, renderer; + +let parent_node; + +init(); + +function init() { + container = document.getElementById('container'); + + camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 1, 10000); + camera.position.z = 9000; + + scene = new THREE.Scene(); + + const geometry = new THREE.BufferGeometry(); + const material = new THREE.LineBasicMaterial({ vertexColors: true }); + + const indices = []; + const positions = []; + const colors = []; + + let next_positions_index = 0; + + // + + const iteration_count = 4; + const rangle = (60 * Math.PI) / 180.0; + + function add_vertex(v) { + positions.push(v.x, v.y, v.z); + colors.push(Math.random() * 0.5 + 0.5, Math.random() * 0.5 + 0.5, 1); + + return next_positions_index++; + } + + // simple Koch curve + + function snowflake_iteration(p0, p4, depth) { + if (--depth < 0) { + const i = next_positions_index - 1; // p0 already there + add_vertex(p4); + indices.push(i, i + 1); + + return; + } + + const v = p4.clone().sub(p0); + const v_tier = v.clone().multiplyScalar(1 / 3); + const p1 = p0.clone().add(v_tier); + + const angle = Math.atan2(v.y, v.x) + rangle; + const length = v_tier.length(); + const p2 = p1.clone(); + p2.x += Math.cos(angle) * length; + p2.y += Math.sin(angle) * length; + + const p3 = p0.clone().add(v_tier).add(v_tier); + + snowflake_iteration(p0, p1, depth); + snowflake_iteration(p1, p2, depth); + snowflake_iteration(p2, p3, depth); + snowflake_iteration(p3, p4, depth); + } + + function snowflake(points, loop, x_offset) { + for (let iteration = 0; iteration != iteration_count; iteration++) { + add_vertex(points[0]); + + for (let p_index = 0, p_count = points.length - 1; p_index != p_count; p_index++) { + snowflake_iteration(points[p_index], points[p_index + 1], iteration); + } + + if (loop) snowflake_iteration(points[points.length - 1], points[0], iteration); + + // translate input curve for next iteration + + for (let p_index = 0, p_count = points.length; p_index != p_count; p_index++) { + points[p_index].x += x_offset; + } + } + } + + let y = 0; + + snowflake([new THREE.Vector3(0, y, 0), new THREE.Vector3(500, y, 0)], false, 600); + + y += 600; + snowflake( + [new THREE.Vector3(0, y, 0), new THREE.Vector3(250, y + 400, 0), new THREE.Vector3(500, y, 0)], + true, + 600, + ); + + y += 600; + snowflake( + [ + new THREE.Vector3(0, y, 0), + new THREE.Vector3(500, y, 0), + new THREE.Vector3(500, y + 500, 0), + new THREE.Vector3(0, y + 500, 0), + ], + true, + 600, + ); + + y += 1000; + snowflake( + [ + new THREE.Vector3(250, y, 0), + new THREE.Vector3(500, y, 0), + new THREE.Vector3(250, y, 0), + new THREE.Vector3(250, y + 250, 0), + new THREE.Vector3(250, y, 0), + new THREE.Vector3(0, y, 0), + new THREE.Vector3(250, y, 0), + new THREE.Vector3(250, y - 250, 0), + new THREE.Vector3(250, y, 0), + ], + false, + 600, + ); + + // + + geometry.setIndex(indices); + geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)); + geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); + geometry.computeBoundingSphere(); + + const lineSegments = new THREE.LineSegments(geometry, material); + lineSegments.position.x -= 1200; + lineSegments.position.y -= 1200; + + parent_node = new THREE.Object3D(); + parent_node.add(lineSegments); + + scene.add(parent_node); + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + + container.appendChild(renderer.domElement); + + // + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + const time = Date.now() * 0.001; + + parent_node.rotation.z = time * 0.5; + + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/webgl_buffergeometry_points.ts b/examples-testing/examples/webgl_buffergeometry_points.ts new file mode 100644 index 000000000..4547d9d08 --- /dev/null +++ b/examples-testing/examples/webgl_buffergeometry_points.ts @@ -0,0 +1,109 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +let container, stats; + +let camera, scene, renderer; + +let points; + +init(); +animate(); + +function init() { + container = document.getElementById('container'); + + // + + camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 5, 3500); + camera.position.z = 2750; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x050505); + scene.fog = new THREE.Fog(0x050505, 2000, 3500); + + // + + const particles = 500000; + + const geometry = new THREE.BufferGeometry(); + + const positions = []; + const colors = []; + + const color = new THREE.Color(); + + const n = 1000, + n2 = n / 2; // particles spread in the cube + + for (let i = 0; i < particles; i++) { + // positions + + const x = Math.random() * n - n2; + const y = Math.random() * n - n2; + const z = Math.random() * n - n2; + + positions.push(x, y, z); + + // colors + + const vx = x / n + 0.5; + const vy = y / n + 0.5; + const vz = z / n + 0.5; + + color.setRGB(vx, vy, vz, THREE.SRGBColorSpace); + + colors.push(color.r, color.g, color.b); + } + + geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)); + geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); + + geometry.computeBoundingSphere(); + + // + + const material = new THREE.PointsMaterial({ size: 15, vertexColors: true }); + + points = new THREE.Points(geometry, material); + scene.add(points); + + // + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + + container.appendChild(renderer.domElement); + + // + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + const time = Date.now() * 0.001; + + points.rotation.x = time * 0.25; + points.rotation.y = time * 0.5; + + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/webgl_buffergeometry_points_interleaved.ts b/examples-testing/examples/webgl_buffergeometry_points_interleaved.ts new file mode 100644 index 000000000..93eed992e --- /dev/null +++ b/examples-testing/examples/webgl_buffergeometry_points_interleaved.ts @@ -0,0 +1,122 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +let container, stats; + +let camera, scene, renderer; + +let points; + +init(); + +function init() { + container = document.getElementById('container'); + + camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 5, 3500); + camera.position.z = 2750; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x050505); + scene.fog = new THREE.Fog(0x050505, 2000, 3500); + + // + + const particles = 500000; + + const geometry = new THREE.BufferGeometry(); + + // create a generic buffer of binary data (a single particle has 16 bytes of data) + + const arrayBuffer = new ArrayBuffer(particles * 16); + + // the following typed arrays share the same buffer + + const interleavedFloat32Buffer = new Float32Array(arrayBuffer); + const interleavedUint8Buffer = new Uint8Array(arrayBuffer); + + // + + const color = new THREE.Color(); + + const n = 1000, + n2 = n / 2; // particles spread in the cube + + for (let i = 0; i < interleavedFloat32Buffer.length; i += 4) { + // position (first 12 bytes) + + const x = Math.random() * n - n2; + const y = Math.random() * n - n2; + const z = Math.random() * n - n2; + + interleavedFloat32Buffer[i + 0] = x; + interleavedFloat32Buffer[i + 1] = y; + interleavedFloat32Buffer[i + 2] = z; + + // color (last 4 bytes) + + const vx = x / n + 0.5; + const vy = y / n + 0.5; + const vz = z / n + 0.5; + + color.setRGB(vx, vy, vz, THREE.SRGBColorSpace); + + const j = (i + 3) * 4; + + interleavedUint8Buffer[j + 0] = color.r * 255; + interleavedUint8Buffer[j + 1] = color.g * 255; + interleavedUint8Buffer[j + 2] = color.b * 255; + interleavedUint8Buffer[j + 3] = 0; // not needed + } + + const interleavedBuffer32 = new THREE.InterleavedBuffer(interleavedFloat32Buffer, 4); + const interleavedBuffer8 = new THREE.InterleavedBuffer(interleavedUint8Buffer, 16); + + geometry.setAttribute('position', new THREE.InterleavedBufferAttribute(interleavedBuffer32, 3, 0, false)); + geometry.setAttribute('color', new THREE.InterleavedBufferAttribute(interleavedBuffer8, 3, 12, true)); + + // + + const material = new THREE.PointsMaterial({ size: 15, vertexColors: true }); + + points = new THREE.Points(geometry, material); + scene.add(points); + + // + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + + container.appendChild(renderer.domElement); + + // + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + const time = Date.now() * 0.001; + + points.rotation.x = time * 0.25; + points.rotation.y = time * 0.5; + + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/webgl_buffergeometry_rawshader.ts b/examples-testing/examples/webgl_buffergeometry_rawshader.ts new file mode 100644 index 000000000..5bc113dc3 --- /dev/null +++ b/examples-testing/examples/webgl_buffergeometry_rawshader.ts @@ -0,0 +1,97 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +let container, stats; + +let camera, scene, renderer; + +init(); + +function init() { + container = document.getElementById('container'); + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10); + camera.position.z = 2; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x101010); + + // geometry + // nr of triangles with 3 vertices per triangle + const vertexCount = 200 * 3; + + const geometry = new THREE.BufferGeometry(); + + const positions = []; + const colors = []; + + for (let i = 0; i < vertexCount; i++) { + // adding x,y,z + positions.push(Math.random() - 0.5); + positions.push(Math.random() - 0.5); + positions.push(Math.random() - 0.5); + + // adding r,g,b,a + colors.push(Math.random() * 255); + colors.push(Math.random() * 255); + colors.push(Math.random() * 255); + colors.push(Math.random() * 255); + } + + const positionAttribute = new THREE.Float32BufferAttribute(positions, 3); + const colorAttribute = new THREE.Uint8BufferAttribute(colors, 4); + + colorAttribute.normalized = true; // this will map the buffer values to 0.0f - +1.0f in the shader + + geometry.setAttribute('position', positionAttribute); + geometry.setAttribute('color', colorAttribute); + + // material + + const material = new THREE.RawShaderMaterial({ + uniforms: { + time: { value: 1.0 }, + }, + vertexShader: document.getElementById('vertexShader').textContent, + fragmentShader: document.getElementById('fragmentShader').textContent, + side: THREE.DoubleSide, + transparent: true, + }); + + const mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + stats = new Stats(); + container.appendChild(stats.dom); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + const time = performance.now(); + + const object = scene.children[0]; + + object.rotation.y = time * 0.0005; + object.material.uniforms.time.value = time * 0.005; + + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/webgl_buffergeometry_selective_draw.ts b/examples-testing/examples/webgl_buffergeometry_selective_draw.ts new file mode 100644 index 000000000..d07176c51 --- /dev/null +++ b/examples-testing/examples/webgl_buffergeometry_selective_draw.ts @@ -0,0 +1,150 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +let camera, scene, renderer, stats; +let geometry, mesh; +const numLat = 100; +const numLng = 200; +let numLinesCulled = 0; + +init(); + +function init() { + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.01, 10); + camera.position.z = 3.5; + + stats = new Stats(); + document.body.appendChild(stats.dom); + + window.addEventListener('resize', onWindowResize); + + addLines(1.0); + + const hideLinesButton = document.getElementById('hideLines'); + hideLinesButton.addEventListener('click', hideLines); + + const showAllLinesButton = document.getElementById('showAllLines'); + showAllLinesButton.addEventListener('click', showAllLines); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); +} + +function addLines(radius) { + geometry = new THREE.BufferGeometry(); + const linePositions = new Float32Array(numLat * numLng * 3 * 2); + const lineColors = new Float32Array(numLat * numLng * 3 * 2); + const visible = new Float32Array(numLat * numLng * 2); + + for (let i = 0; i < numLat; ++i) { + for (let j = 0; j < numLng; ++j) { + const lat = (Math.random() * Math.PI) / 50.0 + (i / numLat) * Math.PI; + const lng = (Math.random() * Math.PI) / 50.0 + (j / numLng) * 2 * Math.PI; + + const index = i * numLng + j; + + linePositions[index * 6 + 0] = 0; + linePositions[index * 6 + 1] = 0; + linePositions[index * 6 + 2] = 0; + linePositions[index * 6 + 3] = radius * Math.sin(lat) * Math.cos(lng); + linePositions[index * 6 + 4] = radius * Math.cos(lat); + linePositions[index * 6 + 5] = radius * Math.sin(lat) * Math.sin(lng); + + const color = new THREE.Color(0xffffff); + + color.setHSL(lat / Math.PI, 1.0, 0.2); + lineColors[index * 6 + 0] = color.r; + lineColors[index * 6 + 1] = color.g; + lineColors[index * 6 + 2] = color.b; + + color.setHSL(lat / Math.PI, 1.0, 0.7); + lineColors[index * 6 + 3] = color.r; + lineColors[index * 6 + 4] = color.g; + lineColors[index * 6 + 5] = color.b; + + // non-0 is visible + visible[index * 2 + 0] = 1.0; + visible[index * 2 + 1] = 1.0; + } + } + + geometry.setAttribute('position', new THREE.BufferAttribute(linePositions, 3)); + geometry.setAttribute('vertColor', new THREE.BufferAttribute(lineColors, 3)); + geometry.setAttribute('visible', new THREE.BufferAttribute(visible, 1)); + + geometry.computeBoundingSphere(); + + const shaderMaterial = new THREE.ShaderMaterial({ + vertexShader: document.getElementById('vertexshader').textContent, + fragmentShader: document.getElementById('fragmentshader').textContent, + }); + + mesh = new THREE.LineSegments(geometry, shaderMaterial); + scene.add(mesh); + + updateCount(); +} + +function updateCount() { + const str = + '1 draw call, ' + + numLat * numLng + + ' lines, ' + + numLinesCulled + + ' culled (author)'; + document.getElementById('title').innerHTML = str.replace(/\B(?=(\d{3})+(?!\d))/g, ','); +} + +function hideLines() { + for (let i = 0; i < geometry.attributes.visible.array.length; i += 2) { + if (Math.random() > 0.75) { + if (geometry.attributes.visible.array[i + 0]) { + ++numLinesCulled; + } + + geometry.attributes.visible.array[i + 0] = 0; + geometry.attributes.visible.array[i + 1] = 0; + } + } + + geometry.attributes.visible.needsUpdate = true; + + updateCount(); +} + +function showAllLines() { + numLinesCulled = 0; + + for (let i = 0; i < geometry.attributes.visible.array.length; i += 2) { + geometry.attributes.visible.array[i + 0] = 1; + geometry.attributes.visible.array[i + 1] = 1; + } + + geometry.attributes.visible.needsUpdate = true; + + updateCount(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + const time = Date.now() * 0.001; + + mesh.rotation.x = time * 0.25; + mesh.rotation.y = time * 0.5; + + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/webgl_buffergeometry_uint.ts b/examples-testing/examples/webgl_buffergeometry_uint.ts new file mode 100644 index 000000000..0b8df6ec7 --- /dev/null +++ b/examples-testing/examples/webgl_buffergeometry_uint.ts @@ -0,0 +1,177 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +let container, stats; + +let camera, scene, renderer; + +let mesh; + +init(); + +function init() { + container = document.getElementById('container'); + + // + + camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 1, 3500); + camera.position.z = 2750; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x050505); + scene.fog = new THREE.Fog(0x050505, 2000, 3500); + + // + + scene.add(new THREE.AmbientLight(0xcccccc)); + + const light1 = new THREE.DirectionalLight(0xffffff, 1.5); + light1.position.set(1, 1, 1); + scene.add(light1); + + const light2 = new THREE.DirectionalLight(0xffffff, 4.5); + light2.position.set(0, -1, 0); + scene.add(light2); + + // + + const triangles = 500000; + + const geometry = new THREE.BufferGeometry(); + + const positions = []; + const normals = []; + const colors = []; + + const color = new THREE.Color(); + + const n = 800, + n2 = n / 2; // triangles spread in the cube + const d = 12, + d2 = d / 2; // individual triangle size + + const pA = new THREE.Vector3(); + const pB = new THREE.Vector3(); + const pC = new THREE.Vector3(); + + const cb = new THREE.Vector3(); + const ab = new THREE.Vector3(); + + for (let i = 0; i < triangles; i++) { + // positions + + const x = Math.random() * n - n2; + const y = Math.random() * n - n2; + const z = Math.random() * n - n2; + + const ax = x + Math.random() * d - d2; + const ay = y + Math.random() * d - d2; + const az = z + Math.random() * d - d2; + + const bx = x + Math.random() * d - d2; + const by = y + Math.random() * d - d2; + const bz = z + Math.random() * d - d2; + + const cx = x + Math.random() * d - d2; + const cy = y + Math.random() * d - d2; + const cz = z + Math.random() * d - d2; + + positions.push(ax, ay, az); + positions.push(bx, by, bz); + positions.push(cx, cy, cz); + + // flat face normals + + pA.set(ax, ay, az); + pB.set(bx, by, bz); + pC.set(cx, cy, cz); + + cb.subVectors(pC, pB); + ab.subVectors(pA, pB); + cb.cross(ab); + + cb.normalize(); + + const nx = cb.x; + const ny = cb.y; + const nz = cb.z; + + normals.push(nx * 32767, ny * 32767, nz * 32767); + normals.push(nx * 32767, ny * 32767, nz * 32767); + normals.push(nx * 32767, ny * 32767, nz * 32767); + + // colors + + const vx = x / n + 0.5; + const vy = y / n + 0.5; + const vz = z / n + 0.5; + + color.setRGB(vx, vy, vz); + + colors.push(color.r * 255, color.g * 255, color.b * 255); + colors.push(color.r * 255, color.g * 255, color.b * 255); + colors.push(color.r * 255, color.g * 255, color.b * 255); + } + + const positionAttribute = new THREE.Float32BufferAttribute(positions, 3); + const normalAttribute = new THREE.Int16BufferAttribute(normals, 3); + const colorAttribute = new THREE.Uint8BufferAttribute(colors, 3); + + normalAttribute.normalized = true; // this will map the buffer values to 0.0f - +1.0f in the shader + colorAttribute.normalized = true; + + geometry.setAttribute('position', positionAttribute); + geometry.setAttribute('normal', normalAttribute); + geometry.setAttribute('color', colorAttribute); + + geometry.computeBoundingSphere(); + + const material = new THREE.MeshPhongMaterial({ + color: 0xd5d5d5, + specular: 0xffffff, + shininess: 250, + side: THREE.DoubleSide, + vertexColors: true, + }); + + mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + // + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + const time = Date.now() * 0.001; + + mesh.rotation.x = time * 0.25; + mesh.rotation.y = time * 0.5; + + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/webgl_camera.ts b/examples-testing/examples/webgl_camera.ts new file mode 100644 index 000000000..f3d663603 --- /dev/null +++ b/examples-testing/examples/webgl_camera.ts @@ -0,0 +1,218 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +let SCREEN_WIDTH = window.innerWidth; +let SCREEN_HEIGHT = window.innerHeight; +let aspect = SCREEN_WIDTH / SCREEN_HEIGHT; + +let container, stats; +let camera, scene, renderer, mesh; +let cameraRig, activeCamera, activeHelper; +let cameraPerspective, cameraOrtho; +let cameraPerspectiveHelper, cameraOrthoHelper; +const frustumSize = 600; + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + scene = new THREE.Scene(); + + // + + camera = new THREE.PerspectiveCamera(50, 0.5 * aspect, 1, 10000); + camera.position.z = 2500; + + cameraPerspective = new THREE.PerspectiveCamera(50, 0.5 * aspect, 150, 1000); + + cameraPerspectiveHelper = new THREE.CameraHelper(cameraPerspective); + scene.add(cameraPerspectiveHelper); + + // + cameraOrtho = new THREE.OrthographicCamera( + (0.5 * frustumSize * aspect) / -2, + (0.5 * frustumSize * aspect) / 2, + frustumSize / 2, + frustumSize / -2, + 150, + 1000, + ); + + cameraOrthoHelper = new THREE.CameraHelper(cameraOrtho); + scene.add(cameraOrthoHelper); + + // + + activeCamera = cameraPerspective; + activeHelper = cameraPerspectiveHelper; + + // counteract different front orientation of cameras vs rig + + cameraOrtho.rotation.y = Math.PI; + cameraPerspective.rotation.y = Math.PI; + + cameraRig = new THREE.Group(); + + cameraRig.add(cameraPerspective); + cameraRig.add(cameraOrtho); + + scene.add(cameraRig); + + // + + mesh = new THREE.Mesh( + new THREE.SphereGeometry(100, 16, 8), + new THREE.MeshBasicMaterial({ color: 0xffffff, wireframe: true }), + ); + scene.add(mesh); + + const mesh2 = new THREE.Mesh( + new THREE.SphereGeometry(50, 16, 8), + new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true }), + ); + mesh2.position.y = 150; + mesh.add(mesh2); + + const mesh3 = new THREE.Mesh( + new THREE.SphereGeometry(5, 16, 8), + new THREE.MeshBasicMaterial({ color: 0x0000ff, wireframe: true }), + ); + mesh3.position.z = 150; + cameraRig.add(mesh3); + + // + + const geometry = new THREE.BufferGeometry(); + const vertices = []; + + for (let i = 0; i < 10000; i++) { + vertices.push(THREE.MathUtils.randFloatSpread(2000)); // x + vertices.push(THREE.MathUtils.randFloatSpread(2000)); // y + vertices.push(THREE.MathUtils.randFloatSpread(2000)); // z + } + + geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); + + const particles = new THREE.Points(geometry, new THREE.PointsMaterial({ color: 0x888888 })); + scene.add(particles); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + renderer.setScissorTest(true); + + // + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); + document.addEventListener('keydown', onKeyDown); +} + +// + +function onKeyDown(event) { + switch (event.keyCode) { + case 79 /*O*/: + activeCamera = cameraOrtho; + activeHelper = cameraOrthoHelper; + + break; + + case 80 /*P*/: + activeCamera = cameraPerspective; + activeHelper = cameraPerspectiveHelper; + + break; + } +} + +// + +function onWindowResize() { + SCREEN_WIDTH = window.innerWidth; + SCREEN_HEIGHT = window.innerHeight; + aspect = SCREEN_WIDTH / SCREEN_HEIGHT; + + renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); + + camera.aspect = 0.5 * aspect; + camera.updateProjectionMatrix(); + + cameraPerspective.aspect = 0.5 * aspect; + cameraPerspective.updateProjectionMatrix(); + + cameraOrtho.left = (-0.5 * frustumSize * aspect) / 2; + cameraOrtho.right = (0.5 * frustumSize * aspect) / 2; + cameraOrtho.top = frustumSize / 2; + cameraOrtho.bottom = -frustumSize / 2; + cameraOrtho.updateProjectionMatrix(); +} + +// + +function animate() { + render(); + stats.update(); +} + +function render() { + const r = Date.now() * 0.0005; + + mesh.position.x = 700 * Math.cos(r); + mesh.position.z = 700 * Math.sin(r); + mesh.position.y = 700 * Math.sin(r); + + mesh.children[0].position.x = 70 * Math.cos(2 * r); + mesh.children[0].position.z = 70 * Math.sin(r); + + if (activeCamera === cameraPerspective) { + cameraPerspective.fov = 35 + 30 * Math.sin(0.5 * r); + cameraPerspective.far = mesh.position.length(); + cameraPerspective.updateProjectionMatrix(); + + cameraPerspectiveHelper.update(); + cameraPerspectiveHelper.visible = true; + + cameraOrthoHelper.visible = false; + } else { + cameraOrtho.far = mesh.position.length(); + cameraOrtho.updateProjectionMatrix(); + + cameraOrthoHelper.update(); + cameraOrthoHelper.visible = true; + + cameraPerspectiveHelper.visible = false; + } + + cameraRig.lookAt(mesh.position); + + // + + activeHelper.visible = false; + + renderer.setClearColor(0x000000, 1); + renderer.setScissor(0, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT); + renderer.setViewport(0, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT); + renderer.render(scene, activeCamera); + + // + + activeHelper.visible = true; + + renderer.setClearColor(0x111111, 1); + renderer.setScissor(SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT); + renderer.setViewport(SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT); + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_camera_array.ts b/examples-testing/examples/webgl_camera_array.ts new file mode 100644 index 000000000..8b10e27cb --- /dev/null +++ b/examples-testing/examples/webgl_camera_array.ts @@ -0,0 +1,104 @@ +import * as THREE from 'three'; + +let camera, scene, renderer; +let mesh; +const AMOUNT = 6; + +init(); + +function init() { + const ASPECT_RATIO = window.innerWidth / window.innerHeight; + + const WIDTH = (window.innerWidth / AMOUNT) * window.devicePixelRatio; + const HEIGHT = (window.innerHeight / AMOUNT) * window.devicePixelRatio; + + const cameras = []; + + for (let y = 0; y < AMOUNT; y++) { + for (let x = 0; x < AMOUNT; x++) { + const subcamera = new THREE.PerspectiveCamera(40, ASPECT_RATIO, 0.1, 10); + subcamera.viewport = new THREE.Vector4( + Math.floor(x * WIDTH), + Math.floor(y * HEIGHT), + Math.ceil(WIDTH), + Math.ceil(HEIGHT), + ); + subcamera.position.x = x / AMOUNT - 0.5; + subcamera.position.y = 0.5 - y / AMOUNT; + subcamera.position.z = 1.5; + subcamera.position.multiplyScalar(2); + subcamera.lookAt(0, 0, 0); + subcamera.updateMatrixWorld(); + cameras.push(subcamera); + } + } + + camera = new THREE.ArrayCamera(cameras); + camera.position.z = 3; + + scene = new THREE.Scene(); + + scene.add(new THREE.AmbientLight(0x999999)); + + const light = new THREE.DirectionalLight(0xffffff, 3); + light.position.set(0.5, 0.5, 1); + light.castShadow = true; + light.shadow.camera.zoom = 4; // tighter shadow map + scene.add(light); + + const geometryBackground = new THREE.PlaneGeometry(100, 100); + const materialBackground = new THREE.MeshPhongMaterial({ color: 0x000066 }); + + const background = new THREE.Mesh(geometryBackground, materialBackground); + background.receiveShadow = true; + background.position.set(0, 0, -1); + scene.add(background); + + const geometryCylinder = new THREE.CylinderGeometry(0.5, 0.5, 1, 32); + const materialCylinder = new THREE.MeshPhongMaterial({ color: 0xff0000 }); + + mesh = new THREE.Mesh(geometryCylinder, materialCylinder); + mesh.castShadow = true; + mesh.receiveShadow = true; + scene.add(mesh); + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.shadowMap.enabled = true; + document.body.appendChild(renderer.domElement); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + const ASPECT_RATIO = window.innerWidth / window.innerHeight; + const WIDTH = (window.innerWidth / AMOUNT) * window.devicePixelRatio; + const HEIGHT = (window.innerHeight / AMOUNT) * window.devicePixelRatio; + + camera.aspect = ASPECT_RATIO; + camera.updateProjectionMatrix(); + + for (let y = 0; y < AMOUNT; y++) { + for (let x = 0; x < AMOUNT; x++) { + const subcamera = camera.cameras[AMOUNT * y + x]; + + subcamera.viewport.set(Math.floor(x * WIDTH), Math.floor(y * HEIGHT), Math.ceil(WIDTH), Math.ceil(HEIGHT)); + + subcamera.aspect = ASPECT_RATIO; + subcamera.updateProjectionMatrix(); + } + } + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + mesh.rotation.x += 0.005; + mesh.rotation.z += 0.01; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_camera_logarithmicdepthbuffer.ts b/examples-testing/examples/webgl_camera_logarithmicdepthbuffer.ts new file mode 100644 index 000000000..f1d440004 --- /dev/null +++ b/examples-testing/examples/webgl_camera_logarithmicdepthbuffer.ts @@ -0,0 +1,248 @@ +import * as THREE from 'three'; + +import { FontLoader } from 'three/addons/loaders/FontLoader.js'; +import { TextGeometry } from 'three/addons/geometries/TextGeometry.js'; + +import Stats from 'three/addons/libs/stats.module.js'; + +// 1 micrometer to 100 billion light years in one scene, with 1 unit = 1 meter? preposterous! and yet... +const NEAR = 1e-6, + FAR = 1e27; +let SCREEN_WIDTH = window.innerWidth; +let SCREEN_HEIGHT = window.innerHeight; +let screensplit = 0.25, + screensplit_right = 0; +const mouse = [0.5, 0.5]; +let zoompos = -100, + minzoomspeed = 0.015; +let zoomspeed = minzoomspeed; + +let container, border, stats; +const objects = {}; + +// Generate a number of text labels, from 1µm in size up to 100,000,000 light years +// Try to use some descriptive real-world examples of objects at each scale + +const labeldata = [ + { size: 0.01, scale: 0.0001, label: 'microscopic (1µm)' }, // FIXME - triangulating text fails at this size, so we scale instead + { size: 0.01, scale: 0.1, label: 'minuscule (1mm)' }, + { size: 0.01, scale: 1.0, label: 'tiny (1cm)' }, + { size: 1, scale: 1.0, label: 'child-sized (1m)' }, + { size: 10, scale: 1.0, label: 'tree-sized (10m)' }, + { size: 100, scale: 1.0, label: 'building-sized (100m)' }, + { size: 1000, scale: 1.0, label: 'medium (1km)' }, + { size: 10000, scale: 1.0, label: 'city-sized (10km)' }, + { size: 3400000, scale: 1.0, label: 'moon-sized (3,400 Km)' }, + { size: 12000000, scale: 1.0, label: 'planet-sized (12,000 km)' }, + { size: 1400000000, scale: 1.0, label: 'sun-sized (1,400,000 km)' }, + { size: 7.47e12, scale: 1.0, label: 'solar system-sized (50Au)' }, + { size: 9.4605284e15, scale: 1.0, label: 'gargantuan (1 light year)' }, + { size: 3.08567758e16, scale: 1.0, label: 'ludicrous (1 parsec)' }, + { size: 1e19, scale: 1.0, label: 'mind boggling (1000 light years)' }, +]; + +init(); + +function init() { + container = document.getElementById('container'); + + const loader = new FontLoader(); + loader.load('fonts/helvetiker_regular.typeface.json', function (font) { + const scene = initScene(font); + + // Initialize two copies of the same scene, one with normal z-buffer and one with logarithmic z-buffer + objects.normal = initView(scene, 'normal', false); + objects.logzbuf = initView(scene, 'logzbuf', true); + + animate(); + }); + + stats = new Stats(); + container.appendChild(stats.dom); + + // Resize border allows the user to easily compare effects of logarithmic depth buffer over the whole scene + border = document.getElementById('renderer_border'); + border.addEventListener('pointerdown', onBorderPointerDown); + + window.addEventListener('mousemove', onMouseMove); + window.addEventListener('resize', onWindowResize); + window.addEventListener('wheel', onMouseWheel); +} + +function initView(scene, name, logDepthBuf) { + const framecontainer = document.getElementById('container_' + name); + + const camera = new THREE.PerspectiveCamera(50, (screensplit * SCREEN_WIDTH) / SCREEN_HEIGHT, NEAR, FAR); + scene.add(camera); + + const renderer = new THREE.WebGLRenderer({ antialias: true, logarithmicDepthBuffer: logDepthBuf }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(SCREEN_WIDTH / 2, SCREEN_HEIGHT); + renderer.domElement.style.position = 'relative'; + renderer.domElement.id = 'renderer_' + name; + framecontainer.appendChild(renderer.domElement); + + return { container: framecontainer, renderer: renderer, scene: scene, camera: camera }; +} + +function initScene(font) { + const scene = new THREE.Scene(); + + scene.add(new THREE.AmbientLight(0x777777)); + + const light = new THREE.DirectionalLight(0xffffff, 3); + light.position.set(100, 100, 100); + scene.add(light); + + const materialargs = { + color: 0xffffff, + specular: 0x050505, + shininess: 50, + emissive: 0x000000, + }; + + const geometry = new THREE.SphereGeometry(0.5, 24, 12); + + for (let i = 0; i < labeldata.length; i++) { + const scale = labeldata[i].scale || 1; + + const labelgeo = new TextGeometry(labeldata[i].label, { + font: font, + size: labeldata[i].size, + depth: labeldata[i].size / 2, + }); + + labelgeo.computeBoundingSphere(); + + // center text + labelgeo.translate(-labelgeo.boundingSphere.radius, 0, 0); + + materialargs.color = new THREE.Color().setHSL(Math.random(), 0.5, 0.5); + + const material = new THREE.MeshPhongMaterial(materialargs); + + const group = new THREE.Group(); + group.position.z = -labeldata[i].size * scale; + scene.add(group); + + const textmesh = new THREE.Mesh(labelgeo, material); + textmesh.scale.set(scale, scale, scale); + textmesh.position.z = -labeldata[i].size * scale; + textmesh.position.y = (labeldata[i].size / 4) * scale; + group.add(textmesh); + + const dotmesh = new THREE.Mesh(geometry, material); + dotmesh.position.y = (-labeldata[i].size / 4) * scale; + dotmesh.scale.multiplyScalar(labeldata[i].size * scale); + group.add(dotmesh); + } + + return scene; +} + +function updateRendererSizes() { + // Recalculate size for both renderers when screen size or split location changes + + SCREEN_WIDTH = window.innerWidth; + SCREEN_HEIGHT = window.innerHeight; + + screensplit_right = 1 - screensplit; + + objects.normal.renderer.setSize(screensplit * SCREEN_WIDTH, SCREEN_HEIGHT); + objects.normal.camera.aspect = (screensplit * SCREEN_WIDTH) / SCREEN_HEIGHT; + objects.normal.camera.updateProjectionMatrix(); + objects.normal.camera.setViewOffset(SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, SCREEN_WIDTH * screensplit, SCREEN_HEIGHT); + objects.normal.container.style.width = screensplit * 100 + '%'; + + objects.logzbuf.renderer.setSize(screensplit_right * SCREEN_WIDTH, SCREEN_HEIGHT); + objects.logzbuf.camera.aspect = (screensplit_right * SCREEN_WIDTH) / SCREEN_HEIGHT; + objects.logzbuf.camera.updateProjectionMatrix(); + objects.logzbuf.camera.setViewOffset( + SCREEN_WIDTH, + SCREEN_HEIGHT, + SCREEN_WIDTH * screensplit, + 0, + SCREEN_WIDTH * screensplit_right, + SCREEN_HEIGHT, + ); + objects.logzbuf.container.style.width = screensplit_right * 100 + '%'; + + border.style.left = screensplit * 100 + '%'; +} + +function animate() { + requestAnimationFrame(animate); + render(); +} + +function render() { + // Put some limits on zooming + const minzoom = labeldata[0].size * labeldata[0].scale * 1; + const maxzoom = labeldata[labeldata.length - 1].size * labeldata[labeldata.length - 1].scale * 100; + let damping = Math.abs(zoomspeed) > minzoomspeed ? 0.95 : 1.0; + + // Zoom out faster the further out you go + const zoom = THREE.MathUtils.clamp(Math.pow(Math.E, zoompos), minzoom, maxzoom); + zoompos = Math.log(zoom); + + // Slow down quickly at the zoom limits + if ((zoom == minzoom && zoomspeed < 0) || (zoom == maxzoom && zoomspeed > 0)) { + damping = 0.85; + } + + zoompos += zoomspeed; + zoomspeed *= damping; + + objects.normal.camera.position.x = Math.sin(0.5 * Math.PI * (mouse[0] - 0.5)) * zoom; + objects.normal.camera.position.y = Math.sin(0.25 * Math.PI * (mouse[1] - 0.5)) * zoom; + objects.normal.camera.position.z = Math.cos(0.5 * Math.PI * (mouse[0] - 0.5)) * zoom; + objects.normal.camera.lookAt(objects.normal.scene.position); + + // Clone camera settings across both scenes + objects.logzbuf.camera.position.copy(objects.normal.camera.position); + objects.logzbuf.camera.quaternion.copy(objects.normal.camera.quaternion); + + // Update renderer sizes if the split has changed + if (screensplit_right != 1 - screensplit) { + updateRendererSizes(); + } + + objects.normal.renderer.render(objects.normal.scene, objects.normal.camera); + objects.logzbuf.renderer.render(objects.logzbuf.scene, objects.logzbuf.camera); + + stats.update(); +} + +function onWindowResize() { + updateRendererSizes(); +} + +function onBorderPointerDown() { + // activate draggable window resizing bar + window.addEventListener('pointermove', onBorderPointerMove); + window.addEventListener('pointerup', onBorderPointerUp); +} + +function onBorderPointerMove(ev) { + screensplit = Math.max(0, Math.min(1, ev.clientX / window.innerWidth)); +} + +function onBorderPointerUp() { + window.removeEventListener('pointermove', onBorderPointerMove); + window.removeEventListener('pointerup', onBorderPointerUp); +} + +function onMouseMove(ev) { + mouse[0] = ev.clientX / window.innerWidth; + mouse[1] = ev.clientY / window.innerHeight; +} + +function onMouseWheel(ev) { + const amount = ev.deltaY; + if (amount === 0) return; + const dir = amount / Math.abs(amount); + zoomspeed = dir / 10; + + // Slow down default zoom speed after user starts zooming, to give them more control + minzoomspeed = 0.001; +} diff --git a/examples-testing/examples/webgl_clipculldistance.ts b/examples-testing/examples/webgl_clipculldistance.ts new file mode 100644 index 000000000..42c1ff17f --- /dev/null +++ b/examples-testing/examples/webgl_clipculldistance.ts @@ -0,0 +1,113 @@ +import * as THREE from 'three'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import Stats from 'three/addons/libs/stats.module.js'; + +let camera, controls, timer, scene, renderer, stats; + +let material; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10); + camera.position.z = 2; + + scene = new THREE.Scene(); + + timer = new THREE.Timer(); + timer.connect(document); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + if (renderer.extensions.has('WEBGL_clip_cull_distance') === false) { + document.getElementById('notSupported').style.display = ''; + return; + } + + const ext = renderer.getContext().getExtension('WEBGL_clip_cull_distance'); + const gl = renderer.getContext(); + + gl.enable(ext.CLIP_DISTANCE0_WEBGL); + + // geometry + + const vertexCount = 200 * 3; + + const geometry = new THREE.BufferGeometry(); + + const positions = []; + const colors = []; + + for (let i = 0; i < vertexCount; i++) { + // adding x,y,z + positions.push(Math.random() - 0.5); + positions.push(Math.random() - 0.5); + positions.push(Math.random() - 0.5); + + // adding r,g,b,a + colors.push(Math.random() * 255); + colors.push(Math.random() * 255); + colors.push(Math.random() * 255); + colors.push(Math.random() * 255); + } + + const positionAttribute = new THREE.Float32BufferAttribute(positions, 3); + const colorAttribute = new THREE.Uint8BufferAttribute(colors, 4); + colorAttribute.normalized = true; + + geometry.setAttribute('position', positionAttribute); + geometry.setAttribute('color', colorAttribute); + + // material + + material = new THREE.ShaderMaterial({ + uniforms: { + time: { value: 1.0 }, + }, + vertexShader: document.getElementById('vertexShader').textContent, + fragmentShader: document.getElementById('fragmentShader').textContent, + side: THREE.DoubleSide, + transparent: true, + vertexColors: true, + }); + + material.extensions.clipCullDistance = true; + + const mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + // + + controls = new OrbitControls(camera, renderer.domElement); + + // + + stats = new Stats(); + document.body.appendChild(stats.dom); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + controls.update(); + stats.update(); + + material.uniforms.time.value = timer.getElapsed(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_clipping.ts b/examples-testing/examples/webgl_clipping.ts new file mode 100644 index 000000000..cde10c7d1 --- /dev/null +++ b/examples-testing/examples/webgl_clipping.ts @@ -0,0 +1,195 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let camera, scene, renderer, startTime, object, stats; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(36, window.innerWidth / window.innerHeight, 0.25, 16); + + camera.position.set(0, 1.3, 3); + + scene = new THREE.Scene(); + + // Lights + + scene.add(new THREE.AmbientLight(0xcccccc)); + + const spotLight = new THREE.SpotLight(0xffffff, 60); + spotLight.angle = Math.PI / 5; + spotLight.penumbra = 0.2; + spotLight.position.set(2, 3, 3); + spotLight.castShadow = true; + spotLight.shadow.camera.near = 3; + spotLight.shadow.camera.far = 10; + spotLight.shadow.mapSize.width = 1024; + spotLight.shadow.mapSize.height = 1024; + scene.add(spotLight); + + const dirLight = new THREE.DirectionalLight(0x55505a, 3); + dirLight.position.set(0, 3, 0); + dirLight.castShadow = true; + dirLight.shadow.camera.near = 1; + dirLight.shadow.camera.far = 10; + + dirLight.shadow.camera.right = 1; + dirLight.shadow.camera.left = -1; + dirLight.shadow.camera.top = 1; + dirLight.shadow.camera.bottom = -1; + + dirLight.shadow.mapSize.width = 1024; + dirLight.shadow.mapSize.height = 1024; + scene.add(dirLight); + + // ***** Clipping planes: ***** + + const localPlane = new THREE.Plane(new THREE.Vector3(0, -1, 0), 0.8); + const globalPlane = new THREE.Plane(new THREE.Vector3(-1, 0, 0), 0.1); + + // Geometry + + const material = new THREE.MeshPhongMaterial({ + color: 0x80ee10, + shininess: 100, + side: THREE.DoubleSide, + + // ***** Clipping setup (material): ***** + clippingPlanes: [localPlane], + clipShadows: true, + + alphaToCoverage: true, + }); + + const geometry = new THREE.TorusKnotGeometry(0.4, 0.08, 95, 20); + + object = new THREE.Mesh(geometry, material); + object.castShadow = true; + scene.add(object); + + const ground = new THREE.Mesh( + new THREE.PlaneGeometry(9, 9, 1, 1), + new THREE.MeshPhongMaterial({ color: 0xa0adaf, shininess: 150 }), + ); + + ground.rotation.x = -Math.PI / 2; // rotates X/Y to X/Z + ground.receiveShadow = true; + scene.add(ground); + + // Renderer + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.shadowMap.enabled = true; + document.body.appendChild(renderer.domElement); + + window.addEventListener('resize', onWindowResize); + + // ***** Clipping setup (renderer): ***** + const globalPlanes = [globalPlane], + Empty = Object.freeze([]); + renderer.clippingPlanes = Empty; // GUI sets it to globalPlanes + renderer.localClippingEnabled = true; + + // Stats + + stats = new Stats(); + document.body.appendChild(stats.dom); + + // Controls + + const controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, 1, 0); + controls.update(); + + // GUI + + const gui = new GUI(), + props = { + alphaToCoverage: true, + }, + folderLocal = gui.addFolder('Local Clipping'), + propsLocal = { + get Enabled() { + return renderer.localClippingEnabled; + }, + set Enabled(v) { + renderer.localClippingEnabled = v; + }, + + get Shadows() { + return material.clipShadows; + }, + set Shadows(v) { + material.clipShadows = v; + }, + + get Plane() { + return localPlane.constant; + }, + set Plane(v) { + localPlane.constant = v; + }, + }, + folderGlobal = gui.addFolder('Global Clipping'), + propsGlobal = { + get Enabled() { + return renderer.clippingPlanes !== Empty; + }, + set Enabled(v) { + renderer.clippingPlanes = v ? globalPlanes : Empty; + }, + + get Plane() { + return globalPlane.constant; + }, + set Plane(v) { + globalPlane.constant = v; + }, + }; + + gui.add(props, 'alphaToCoverage').onChange(function (value) { + ground.material.alphaToCoverage = value; + ground.material.needsUpdate = true; + + material.alphaToCoverage = value; + material.needsUpdate = true; + }); + folderLocal.add(propsLocal, 'Enabled'); + folderLocal.add(propsLocal, 'Shadows'); + folderLocal.add(propsLocal, 'Plane', 0.3, 1.25); + + folderGlobal.add(propsGlobal, 'Enabled'); + folderGlobal.add(propsGlobal, 'Plane', -0.4, 3); + + // Start + + startTime = Date.now(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + const currentTime = Date.now(); + const time = (currentTime - startTime) / 1000; + + object.position.y = 0.8; + object.rotation.x = time * 0.5; + object.rotation.y = time * 0.2; + object.scale.setScalar(Math.cos(time) * 0.125 + 0.875); + + stats.begin(); + renderer.render(scene, camera); + stats.end(); +} diff --git a/examples-testing/examples/webgl_clipping_advanced.ts b/examples-testing/examples/webgl_clipping_advanced.ts new file mode 100644 index 000000000..f65f00043 --- /dev/null +++ b/examples-testing/examples/webgl_clipping_advanced.ts @@ -0,0 +1,357 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +function planesFromMesh(vertices, indices) { + // creates a clipping volume from a convex triangular mesh + // specified by the arrays 'vertices' and 'indices' + + const n = indices.length / 3, + result = new Array(n); + + for (let i = 0, j = 0; i < n; ++i, j += 3) { + const a = vertices[indices[j]], + b = vertices[indices[j + 1]], + c = vertices[indices[j + 2]]; + + result[i] = new THREE.Plane().setFromCoplanarPoints(a, b, c); + } + + return result; +} + +function createPlanes(n) { + // creates an array of n uninitialized plane objects + + const result = new Array(n); + + for (let i = 0; i !== n; ++i) result[i] = new THREE.Plane(); + + return result; +} + +function assignTransformedPlanes(planesOut, planesIn, matrix) { + // sets an array of existing planes to transformed 'planesIn' + + for (let i = 0, n = planesIn.length; i !== n; ++i) planesOut[i].copy(planesIn[i]).applyMatrix4(matrix); +} + +function cylindricalPlanes(n, innerRadius) { + const result = createPlanes(n); + + for (let i = 0; i !== n; ++i) { + const plane = result[i], + angle = (i * Math.PI * 2) / n; + + plane.normal.set(Math.cos(angle), 0, Math.sin(angle)); + + plane.constant = innerRadius; + } + + return result; +} + +const planeToMatrix = (function () { + // creates a matrix that aligns X/Y to a given plane + + // temporaries: + const xAxis = new THREE.Vector3(), + yAxis = new THREE.Vector3(), + trans = new THREE.Vector3(); + + return function planeToMatrix(plane) { + const zAxis = plane.normal, + matrix = new THREE.Matrix4(); + + // Hughes & Moeller '99 + // "Building an Orthonormal Basis from a Unit Vector." + + if (Math.abs(zAxis.x) > Math.abs(zAxis.z)) { + yAxis.set(-zAxis.y, zAxis.x, 0); + } else { + yAxis.set(0, -zAxis.z, zAxis.y); + } + + xAxis.crossVectors(yAxis.normalize(), zAxis); + + plane.coplanarPoint(trans); + return matrix.set( + xAxis.x, + yAxis.x, + zAxis.x, + trans.x, + xAxis.y, + yAxis.y, + zAxis.y, + trans.y, + xAxis.z, + yAxis.z, + zAxis.z, + trans.z, + 0, + 0, + 0, + 1, + ); + }; +})(); + +// A regular tetrahedron for the clipping volume: + +const Vertices = [ + new THREE.Vector3(+1, 0, +Math.SQRT1_2), + new THREE.Vector3(-1, 0, +Math.SQRT1_2), + new THREE.Vector3(0, +1, -Math.SQRT1_2), + new THREE.Vector3(0, -1, -Math.SQRT1_2), + ], + Indices = [0, 1, 2, 0, 2, 3, 0, 3, 1, 1, 3, 2], + Planes = planesFromMesh(Vertices, Indices), + PlaneMatrices = Planes.map(planeToMatrix), + GlobalClippingPlanes = cylindricalPlanes(5, 2.5), + Empty = Object.freeze([]); + +let camera, scene, renderer, startTime, stats, object, clipMaterial, volumeVisualization, globalClippingPlanes; + +function init() { + camera = new THREE.PerspectiveCamera(36, window.innerWidth / window.innerHeight, 0.25, 16); + + camera.position.set(0, 1.5, 3); + + scene = new THREE.Scene(); + + // Lights + + scene.add(new THREE.AmbientLight(0xffffff)); + + const spotLight = new THREE.SpotLight(0xffffff, 60); + spotLight.angle = Math.PI / 5; + spotLight.penumbra = 0.2; + spotLight.position.set(2, 3, 3); + spotLight.castShadow = true; + spotLight.shadow.camera.near = 3; + spotLight.shadow.camera.far = 10; + spotLight.shadow.mapSize.width = 1024; + spotLight.shadow.mapSize.height = 1024; + scene.add(spotLight); + + const dirLight = new THREE.DirectionalLight(0xffffff, 1.5); + dirLight.position.set(0, 2, 0); + dirLight.castShadow = true; + dirLight.shadow.camera.near = 1; + dirLight.shadow.camera.far = 10; + + dirLight.shadow.camera.right = 1; + dirLight.shadow.camera.left = -1; + dirLight.shadow.camera.top = 1; + dirLight.shadow.camera.bottom = -1; + + dirLight.shadow.mapSize.width = 1024; + dirLight.shadow.mapSize.height = 1024; + scene.add(dirLight); + + // Geometry + + clipMaterial = new THREE.MeshPhongMaterial({ + color: 0xee0a10, + shininess: 100, + side: THREE.DoubleSide, + // Clipping setup: + clippingPlanes: createPlanes(Planes.length), + clipShadows: true, + }); + + const count = 5 * 5 * 5; + const geometry = new THREE.BoxGeometry(0.18, 0.18, 0.18); + object = new THREE.InstancedMesh(geometry, clipMaterial, count); + object.castShadow = true; + + let i = 0; + const matrix = new THREE.Matrix4(); + + for (let z = -2; z <= 2; ++z) + for (let y = -2; y <= 2; ++y) + for (let x = -2; x <= 2; ++x) { + matrix.setPosition(x / 5, y / 5, z / 5); + object.setMatrixAt(i++, matrix); + } + + scene.add(object); + + const planeGeometry = new THREE.PlaneGeometry(3, 3, 1, 1), + color = new THREE.Color(); + + volumeVisualization = new THREE.Group(); + volumeVisualization.visible = false; + + for (let i = 0, n = Planes.length; i !== n; ++i) { + const material = new THREE.MeshBasicMaterial({ + color: color.setHSL(i / n, 0.5, 0.5).getHex(), + side: THREE.DoubleSide, + + opacity: 0.2, + transparent: true, + + // clip to the others to show the volume (wildly + // intersecting transparent planes look bad) + clippingPlanes: clipMaterial.clippingPlanes.filter(function (_, j) { + return j !== i; + }), + + // no need to enable shadow clipping - the plane + // visualization does not cast shadows + }); + + const mesh = new THREE.Mesh(planeGeometry, material); + mesh.matrixAutoUpdate = false; + + volumeVisualization.add(mesh); + } + + scene.add(volumeVisualization); + + const ground = new THREE.Mesh( + planeGeometry, + new THREE.MeshPhongMaterial({ + color: 0xa0adaf, + shininess: 10, + }), + ); + ground.rotation.x = -Math.PI / 2; + ground.scale.multiplyScalar(3); + ground.receiveShadow = true; + scene.add(ground); + + // Renderer + + const container = document.body; + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.shadowMap.enabled = true; + container.appendChild(renderer.domElement); + // Clipping setup: + globalClippingPlanes = createPlanes(GlobalClippingPlanes.length); + renderer.clippingPlanes = Empty; + renderer.localClippingEnabled = true; + + window.addEventListener('resize', onWindowResize); + + // Stats + + stats = new Stats(); + container.appendChild(stats.dom); + + // Controls + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 1; + controls.maxDistance = 8; + controls.target.set(0, 1, 0); + controls.update(); + + // GUI + + const gui = new GUI(), + folder = gui.addFolder('Local Clipping'), + props = { + get Enabled() { + return renderer.localClippingEnabled; + }, + set Enabled(v) { + renderer.localClippingEnabled = v; + if (!v) volumeVisualization.visible = false; + }, + + get Shadows() { + return clipMaterial.clipShadows; + }, + set Shadows(v) { + clipMaterial.clipShadows = v; + }, + + get Visualize() { + return volumeVisualization.visible; + }, + set Visualize(v) { + if (renderer.localClippingEnabled) volumeVisualization.visible = v; + }, + }; + + folder.add(props, 'Enabled'); + folder.add(props, 'Shadows'); + folder.add(props, 'Visualize').listen(); + + gui.addFolder('Global Clipping').add( + { + get Enabled() { + return renderer.clippingPlanes !== Empty; + }, + set Enabled(v) { + renderer.clippingPlanes = v ? globalClippingPlanes : Empty; + }, + }, + 'Enabled', + ); + + // Start + + startTime = Date.now(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function setObjectWorldMatrix(object, matrix) { + // set the orientation of an object based on a world matrix + + const parent = object.parent; + scene.updateMatrixWorld(); + object.matrix.copy(parent.matrixWorld).invert(); + object.applyMatrix4(matrix); +} + +const transform = new THREE.Matrix4(), + tmpMatrix = new THREE.Matrix4(); + +function animate() { + const currentTime = Date.now(), + time = (currentTime - startTime) / 1000; + + object.position.y = 1; + object.rotation.x = time * 0.5; + object.rotation.y = time * 0.2; + + object.updateMatrix(); + transform.copy(object.matrix); + + const bouncy = Math.cos(time * 0.5) * 0.5 + 0.7; + transform.multiply(tmpMatrix.makeScale(bouncy, bouncy, bouncy)); + + assignTransformedPlanes(clipMaterial.clippingPlanes, Planes, transform); + + const planeMeshes = volumeVisualization.children; + + for (let i = 0, n = planeMeshes.length; i !== n; ++i) { + tmpMatrix.multiplyMatrices(transform, PlaneMatrices[i]); + setObjectWorldMatrix(planeMeshes[i], tmpMatrix); + } + + transform.makeRotationY(time * 0.1); + + assignTransformedPlanes(globalClippingPlanes, GlobalClippingPlanes, transform); + + stats.begin(); + renderer.render(scene, camera); + stats.end(); +} + +init(); diff --git a/examples-testing/examples/webgl_clipping_intersection.ts b/examples-testing/examples/webgl_clipping_intersection.ts new file mode 100644 index 000000000..5f45e45df --- /dev/null +++ b/examples-testing/examples/webgl_clipping_intersection.ts @@ -0,0 +1,137 @@ +import * as THREE from 'three'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let camera, scene, renderer; + +const params = { + clipIntersection: true, + planeConstant: 0, + showHelpers: false, + alphaToCoverage: true, +}; + +const clipPlanes = [ + new THREE.Plane(new THREE.Vector3(1, 0, 0), 0), + new THREE.Plane(new THREE.Vector3(0, -1, 0), 0), + new THREE.Plane(new THREE.Vector3(0, 0, -1), 0), +]; + +init(); +render(); + +function init() { + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.localClippingEnabled = true; + document.body.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 200); + + camera.position.set(-1.5, 2.5, 3.0); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.addEventListener('change', render); // use only if there is no animation loop + controls.minDistance = 1; + controls.maxDistance = 10; + controls.enablePan = false; + + const light = new THREE.HemisphereLight(0xffffff, 0x080808, 4.5); + light.position.set(-1.25, 1, 1.25); + scene.add(light); + + // + + const group = new THREE.Group(); + + for (let i = 1; i <= 30; i += 2) { + const geometry = new THREE.SphereGeometry(i / 30, 48, 24); + + const material = new THREE.MeshPhongMaterial({ + color: new THREE.Color().setHSL(Math.random(), 0.5, 0.5, THREE.SRGBColorSpace), + side: THREE.DoubleSide, + clippingPlanes: clipPlanes, + clipIntersection: params.clipIntersection, + alphaToCoverage: true, + }); + + group.add(new THREE.Mesh(geometry, material)); + } + + scene.add(group); + + // helpers + + const helpers = new THREE.Group(); + helpers.add(new THREE.PlaneHelper(clipPlanes[0], 2, 0xff0000)); + helpers.add(new THREE.PlaneHelper(clipPlanes[1], 2, 0x00ff00)); + helpers.add(new THREE.PlaneHelper(clipPlanes[2], 2, 0x0000ff)); + helpers.visible = false; + scene.add(helpers); + + // gui + + const gui = new GUI(); + + gui.add(params, 'alphaToCoverage').onChange(function (value) { + group.children.forEach(c => { + c.material.alphaToCoverage = Boolean(value); + c.material.needsUpdate = true; + }); + + render(); + }); + + gui.add(params, 'clipIntersection') + .name('clip intersection') + .onChange(function (value) { + const children = group.children; + + for (let i = 0; i < children.length; i++) { + children[i].material.clipIntersection = value; + } + + render(); + }); + + gui.add(params, 'planeConstant', -1, 1) + .step(0.01) + .name('plane constant') + .onChange(function (value) { + for (let j = 0; j < clipPlanes.length; j++) { + clipPlanes[j].constant = value; + } + + render(); + }); + + gui.add(params, 'showHelpers') + .name('show helpers') + .onChange(function (value) { + helpers.visible = value; + + render(); + }); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + render(); +} + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_clipping_stencil.ts b/examples-testing/examples/webgl_clipping_stencil.ts new file mode 100644 index 000000000..7cb1f5c52 --- /dev/null +++ b/examples-testing/examples/webgl_clipping_stencil.ts @@ -0,0 +1,263 @@ +import * as THREE from 'three'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import Stats from 'three/addons/libs/stats.module.js'; + +let camera, scene, renderer, object, stats; +let planes, planeObjects, planeHelpers; +let timer; + +const params = { + animate: true, + planeX: { + constant: 0, + negated: false, + displayHelper: false, + }, + planeY: { + constant: 0, + negated: false, + displayHelper: false, + }, + planeZ: { + constant: 0, + negated: false, + displayHelper: false, + }, +}; + +init(); + +function createPlaneStencilGroup(geometry, plane, renderOrder) { + const group = new THREE.Group(); + const baseMat = new THREE.MeshBasicMaterial(); + baseMat.depthWrite = false; + baseMat.depthTest = false; + baseMat.colorWrite = false; + baseMat.stencilWrite = true; + baseMat.stencilFunc = THREE.AlwaysStencilFunc; + + // back faces + const mat0 = baseMat.clone(); + mat0.side = THREE.BackSide; + mat0.clippingPlanes = [plane]; + mat0.stencilFail = THREE.IncrementWrapStencilOp; + mat0.stencilZFail = THREE.IncrementWrapStencilOp; + mat0.stencilZPass = THREE.IncrementWrapStencilOp; + + const mesh0 = new THREE.Mesh(geometry, mat0); + mesh0.renderOrder = renderOrder; + group.add(mesh0); + + // front faces + const mat1 = baseMat.clone(); + mat1.side = THREE.FrontSide; + mat1.clippingPlanes = [plane]; + mat1.stencilFail = THREE.DecrementWrapStencilOp; + mat1.stencilZFail = THREE.DecrementWrapStencilOp; + mat1.stencilZPass = THREE.DecrementWrapStencilOp; + + const mesh1 = new THREE.Mesh(geometry, mat1); + mesh1.renderOrder = renderOrder; + + group.add(mesh1); + + return group; +} + +function init() { + timer = new THREE.Timer(); + timer.connect(document); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(36, window.innerWidth / window.innerHeight, 1, 100); + camera.position.set(2, 2, 2); + + scene.add(new THREE.AmbientLight(0xffffff, 1.5)); + + const dirLight = new THREE.DirectionalLight(0xffffff, 3); + dirLight.position.set(5, 10, 7.5); + dirLight.castShadow = true; + dirLight.shadow.camera.right = 2; + dirLight.shadow.camera.left = -2; + dirLight.shadow.camera.top = 2; + dirLight.shadow.camera.bottom = -2; + + dirLight.shadow.mapSize.width = 1024; + dirLight.shadow.mapSize.height = 1024; + scene.add(dirLight); + + planes = [ + new THREE.Plane(new THREE.Vector3(-1, 0, 0), 0), + new THREE.Plane(new THREE.Vector3(0, -1, 0), 0), + new THREE.Plane(new THREE.Vector3(0, 0, -1), 0), + ]; + + planeHelpers = planes.map(p => new THREE.PlaneHelper(p, 2, 0xffffff)); + planeHelpers.forEach(ph => { + ph.visible = false; + scene.add(ph); + }); + + const geometry = new THREE.TorusKnotGeometry(0.4, 0.15, 220, 60); + object = new THREE.Group(); + scene.add(object); + + // Set up clip plane rendering + planeObjects = []; + const planeGeom = new THREE.PlaneGeometry(4, 4); + + for (let i = 0; i < 3; i++) { + const poGroup = new THREE.Group(); + const plane = planes[i]; + const stencilGroup = createPlaneStencilGroup(geometry, plane, i + 1); + + // plane is clipped by the other clipping planes + const planeMat = new THREE.MeshStandardMaterial({ + color: 0xe91e63, + metalness: 0.1, + roughness: 0.75, + clippingPlanes: planes.filter(p => p !== plane), + + stencilWrite: true, + stencilRef: 0, + stencilFunc: THREE.NotEqualStencilFunc, + stencilFail: THREE.ReplaceStencilOp, + stencilZFail: THREE.ReplaceStencilOp, + stencilZPass: THREE.ReplaceStencilOp, + }); + const po = new THREE.Mesh(planeGeom, planeMat); + po.onAfterRender = function (renderer) { + renderer.clearStencil(); + }; + + po.renderOrder = i + 1.1; + + object.add(stencilGroup); + poGroup.add(po); + planeObjects.push(po); + scene.add(poGroup); + } + + const material = new THREE.MeshStandardMaterial({ + color: 0xffc107, + metalness: 0.1, + roughness: 0.75, + clippingPlanes: planes, + clipShadows: true, + shadowSide: THREE.DoubleSide, + }); + + // add the color + const clippedColorFront = new THREE.Mesh(geometry, material); + clippedColorFront.castShadow = true; + clippedColorFront.renderOrder = 6; + object.add(clippedColorFront); + + const ground = new THREE.Mesh( + new THREE.PlaneGeometry(9, 9, 1, 1), + new THREE.ShadowMaterial({ color: 0x000000, opacity: 0.25, side: THREE.DoubleSide }), + ); + + ground.rotation.x = -Math.PI / 2; // rotates X/Y to X/Z + ground.position.y = -1; + ground.receiveShadow = true; + scene.add(ground); + + // Renderer + renderer = new THREE.WebGLRenderer({ antialias: true, stencil: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setClearColor(0x263238); + renderer.setAnimationLoop(animate); + renderer.shadowMap.enabled = true; + renderer.localClippingEnabled = true; + document.body.appendChild(renderer.domElement); + + // Stats + stats = new Stats(); + document.body.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); + + // Controls + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 2; + controls.maxDistance = 20; + controls.update(); + + // GUI + const gui = new GUI(); + gui.add(params, 'animate'); + + const planeX = gui.addFolder('planeX'); + planeX.add(params.planeX, 'displayHelper').onChange(v => (planeHelpers[0].visible = v)); + planeX + .add(params.planeX, 'constant') + .min(-1) + .max(1) + .onChange(d => (planes[0].constant = d)); + planeX.add(params.planeX, 'negated').onChange(() => { + planes[0].negate(); + params.planeX.constant = planes[0].constant; + }); + planeX.open(); + + const planeY = gui.addFolder('planeY'); + planeY.add(params.planeY, 'displayHelper').onChange(v => (planeHelpers[1].visible = v)); + planeY + .add(params.planeY, 'constant') + .min(-1) + .max(1) + .onChange(d => (planes[1].constant = d)); + planeY.add(params.planeY, 'negated').onChange(() => { + planes[1].negate(); + params.planeY.constant = planes[1].constant; + }); + planeY.open(); + + const planeZ = gui.addFolder('planeZ'); + planeZ.add(params.planeZ, 'displayHelper').onChange(v => (planeHelpers[2].visible = v)); + planeZ + .add(params.planeZ, 'constant') + .min(-1) + .max(1) + .onChange(d => (planes[2].constant = d)); + planeZ.add(params.planeZ, 'negated').onChange(() => { + planes[2].negate(); + params.planeZ.constant = planes[2].constant; + }); + planeZ.open(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + const delta = timer.getDelta(); + + if (params.animate) { + object.rotation.x += delta * 0.5; + object.rotation.y += delta * 0.2; + } + + for (let i = 0; i < planeObjects.length; i++) { + const plane = planes[i]; + const po = planeObjects[i]; + plane.coplanarPoint(po.position); + po.lookAt(po.position.x - plane.normal.x, po.position.y - plane.normal.y, po.position.z - plane.normal.z); + } + + stats.begin(); + renderer.render(scene, camera); + stats.end(); +} diff --git a/examples-testing/examples/webgl_custom_attributes.ts b/examples-testing/examples/webgl_custom_attributes.ts new file mode 100644 index 000000000..0dc897748 --- /dev/null +++ b/examples-testing/examples/webgl_custom_attributes.ts @@ -0,0 +1,100 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +let renderer, scene, camera, stats; + +let sphere, uniforms; + +let displacement, noise; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 10000); + camera.position.z = 300; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x050505); + + uniforms = { + amplitude: { value: 1.0 }, + color: { value: new THREE.Color(0xff2200) }, + colorTexture: { value: new THREE.TextureLoader().load('textures/water.jpg') }, + }; + + uniforms['colorTexture'].value.wrapS = uniforms['colorTexture'].value.wrapT = THREE.RepeatWrapping; + + const shaderMaterial = new THREE.ShaderMaterial({ + uniforms: uniforms, + vertexShader: document.getElementById('vertexshader').textContent, + fragmentShader: document.getElementById('fragmentshader').textContent, + }); + + const radius = 50, + segments = 128, + rings = 64; + + const geometry = new THREE.SphereGeometry(radius, segments, rings); + + displacement = new Float32Array(geometry.attributes.position.count); + noise = new Float32Array(geometry.attributes.position.count); + + for (let i = 0; i < displacement.length; i++) { + noise[i] = Math.random() * 5; + } + + geometry.setAttribute('displacement', new THREE.BufferAttribute(displacement, 1)); + + sphere = new THREE.Mesh(geometry, shaderMaterial); + scene.add(sphere); + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + + const container = document.getElementById('container'); + container.appendChild(renderer.domElement); + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + render(); + stats.update(); +} + +function render() { + const time = Date.now() * 0.01; + + sphere.rotation.y = sphere.rotation.z = 0.01 * time; + + uniforms['amplitude'].value = 2.5 * Math.sin(sphere.rotation.y * 0.125); + uniforms['color'].value.offsetHSL(0.0005, 0, 0); + + for (let i = 0; i < displacement.length; i++) { + displacement[i] = Math.sin(0.1 * i + time); + + noise[i] += 0.5 * (0.5 - Math.random()); + noise[i] = THREE.MathUtils.clamp(noise[i], -5, 5); + + displacement[i] += noise[i]; + } + + sphere.geometry.attributes.displacement.needsUpdate = true; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_custom_attributes_lines.ts b/examples-testing/examples/webgl_custom_attributes_lines.ts new file mode 100644 index 000000000..3e2454e92 --- /dev/null +++ b/examples-testing/examples/webgl_custom_attributes_lines.ts @@ -0,0 +1,121 @@ +import * as THREE from 'three'; + +import { FontLoader } from 'three/addons/loaders/FontLoader.js'; +import { TextGeometry } from 'three/addons/geometries/TextGeometry.js'; + +import Stats from 'three/addons/libs/stats.module.js'; + +let renderer, scene, camera, stats; + +let line, uniforms; + +const loader = new FontLoader(); +loader.load('fonts/helvetiker_bold.typeface.json', function (font) { + init(font); +}); + +function init(font) { + camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 10000); + camera.position.z = 400; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x050505); + + uniforms = { + amplitude: { value: 5.0 }, + opacity: { value: 0.3 }, + color: { value: new THREE.Color(0xffffff) }, + }; + + const shaderMaterial = new THREE.ShaderMaterial({ + uniforms: uniforms, + vertexShader: document.getElementById('vertexshader').textContent, + fragmentShader: document.getElementById('fragmentshader').textContent, + blending: THREE.AdditiveBlending, + depthTest: false, + transparent: true, + }); + + const geometry = new TextGeometry('three.js', { + font: font, + + size: 50, + depth: 15, + curveSegments: 10, + + bevelThickness: 5, + bevelSize: 1.5, + bevelEnabled: true, + bevelSegments: 10, + }); + + geometry.center(); + + const count = geometry.attributes.position.count; + + const displacement = new THREE.Float32BufferAttribute(count * 3, 3); + geometry.setAttribute('displacement', displacement); + + const customColor = new THREE.Float32BufferAttribute(count * 3, 3); + geometry.setAttribute('customColor', customColor); + + const color = new THREE.Color(0xffffff); + + for (let i = 0, l = customColor.count; i < l; i++) { + color.setHSL(i / l, 0.5, 0.5); + color.toArray(customColor.array, i * customColor.itemSize); + } + + line = new THREE.Line(geometry, shaderMaterial); + line.rotation.x = 0.2; + scene.add(line); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + + const container = document.getElementById('container'); + container.appendChild(renderer.domElement); + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + render(); + stats.update(); +} + +function render() { + const time = Date.now() * 0.001; + + line.rotation.y = 0.25 * time; + + uniforms.amplitude.value = Math.sin(0.5 * time); + uniforms.color.value.offsetHSL(0.0005, 0, 0); + + const attributes = line.geometry.attributes; + const array = attributes.displacement.array; + + for (let i = 0, l = array.length; i < l; i += 3) { + array[i] += 0.3 * (0.5 - Math.random()); + array[i + 1] += 0.3 * (0.5 - Math.random()); + array[i + 2] += 0.3 * (0.5 - Math.random()); + } + + attributes.displacement.needsUpdate = true; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_custom_attributes_points.ts b/examples-testing/examples/webgl_custom_attributes_points.ts new file mode 100644 index 000000000..ae112980a --- /dev/null +++ b/examples-testing/examples/webgl_custom_attributes_points.ts @@ -0,0 +1,117 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +let renderer, scene, camera, stats; + +let sphere; + +const WIDTH = window.innerWidth; +const HEIGHT = window.innerHeight; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(40, WIDTH / HEIGHT, 1, 10000); + camera.position.z = 300; + + scene = new THREE.Scene(); + + const amount = 100000; + const radius = 200; + + const positions = new Float32Array(amount * 3); + const colors = new Float32Array(amount * 3); + const sizes = new Float32Array(amount); + + const vertex = new THREE.Vector3(); + const color = new THREE.Color(0xffffff); + + for (let i = 0; i < amount; i++) { + vertex.x = (Math.random() * 2 - 1) * radius; + vertex.y = (Math.random() * 2 - 1) * radius; + vertex.z = (Math.random() * 2 - 1) * radius; + vertex.toArray(positions, i * 3); + + if (vertex.x < 0) { + color.setHSL(0.5 + 0.1 * (i / amount), 0.7, 0.5); + } else { + color.setHSL(0.0 + 0.1 * (i / amount), 0.9, 0.5); + } + + color.toArray(colors, i * 3); + + sizes[i] = 10; + } + + const geometry = new THREE.BufferGeometry(); + geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); + geometry.setAttribute('customColor', new THREE.BufferAttribute(colors, 3)); + geometry.setAttribute('size', new THREE.BufferAttribute(sizes, 1)); + + // + + const material = new THREE.ShaderMaterial({ + uniforms: { + color: { value: new THREE.Color(0xffffff) }, + pointTexture: { value: new THREE.TextureLoader().load('textures/sprites/spark1.png') }, + }, + vertexShader: document.getElementById('vertexshader').textContent, + fragmentShader: document.getElementById('fragmentshader').textContent, + + blending: THREE.AdditiveBlending, + depthTest: false, + transparent: true, + }); + + // + + sphere = new THREE.Points(geometry, material); + scene.add(sphere); + + // + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(WIDTH, HEIGHT); + renderer.setAnimationLoop(animate); + + const container = document.getElementById('container'); + container.appendChild(renderer.domElement); + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + render(); + stats.update(); +} + +function render() { + const time = Date.now() * 0.005; + + sphere.rotation.z = 0.01 * time; + + const geometry = sphere.geometry; + const attributes = geometry.attributes; + + for (let i = 0; i < attributes.size.array.length; i++) { + attributes.size.array[i] = 14 + 13 * Math.sin(0.1 * i + time); + } + + attributes.size.needsUpdate = true; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_custom_attributes_points2.ts b/examples-testing/examples/webgl_custom_attributes_points2.ts new file mode 100644 index 000000000..edd158fa1 --- /dev/null +++ b/examples-testing/examples/webgl_custom_attributes_points2.ts @@ -0,0 +1,193 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js'; + +let renderer, scene, camera, stats; +let sphere, length1; + +const WIDTH = window.innerWidth; +const HEIGHT = window.innerHeight; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(45, WIDTH / HEIGHT, 1, 10000); + camera.position.z = 300; + + scene = new THREE.Scene(); + + const radius = 100, + segments = 68, + rings = 38; + + let sphereGeometry = new THREE.SphereGeometry(radius, segments, rings); + let boxGeometry = new THREE.BoxGeometry(0.8 * radius, 0.8 * radius, 0.8 * radius, 10, 10, 10); + + // if normal and uv attributes are not removed, mergeVertices() can't consolidate identical vertices with different normal/uv data + + sphereGeometry.deleteAttribute('normal'); + sphereGeometry.deleteAttribute('uv'); + + boxGeometry.deleteAttribute('normal'); + boxGeometry.deleteAttribute('uv'); + + sphereGeometry = BufferGeometryUtils.mergeVertices(sphereGeometry); + boxGeometry = BufferGeometryUtils.mergeVertices(boxGeometry); + + const combinedGeometry = BufferGeometryUtils.mergeGeometries([sphereGeometry, boxGeometry]); + const positionAttribute = combinedGeometry.getAttribute('position'); + + const colors = []; + const sizes = []; + + const color = new THREE.Color(); + const vertex = new THREE.Vector3(); + + length1 = sphereGeometry.getAttribute('position').count; + + for (let i = 0, l = positionAttribute.count; i < l; i++) { + vertex.fromBufferAttribute(positionAttribute, i); + + if (i < length1) { + color.setHSL(0.01 + 0.1 * (i / length1), 0.99, (vertex.y + radius) / (4 * radius)); + } else { + color.setHSL(0.6, 0.75, 0.25 + vertex.y / (2 * radius)); + } + + color.toArray(colors, i * 3); + + sizes[i] = i < length1 ? 10 : 40; + } + + const geometry = new THREE.BufferGeometry(); + geometry.setAttribute('position', positionAttribute); + geometry.setAttribute('size', new THREE.Float32BufferAttribute(sizes, 1)); + geometry.setAttribute('ca', new THREE.Float32BufferAttribute(colors, 3)); + + // + + const texture = new THREE.TextureLoader().load('textures/sprites/disc.png'); + texture.wrapS = THREE.RepeatWrapping; + texture.wrapT = THREE.RepeatWrapping; + + const material = new THREE.ShaderMaterial({ + uniforms: { + color: { value: new THREE.Color(0xffffff) }, + pointTexture: { value: texture }, + }, + vertexShader: document.getElementById('vertexshader').textContent, + fragmentShader: document.getElementById('fragmentshader').textContent, + transparent: true, + }); + + // + + sphere = new THREE.Points(geometry, material); + scene.add(sphere); + + // + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(WIDTH, HEIGHT); + renderer.setAnimationLoop(animate); + + const container = document.getElementById('container'); + container.appendChild(renderer.domElement); + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function sortPoints() { + const vector = new THREE.Vector3(); + + // Model View Projection matrix + + const matrix = new THREE.Matrix4(); + matrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse); + matrix.multiply(sphere.matrixWorld); + + // + + const geometry = sphere.geometry; + + let index = geometry.getIndex(); + const positions = geometry.getAttribute('position').array; + const length = positions.length / 3; + + if (index === null) { + const array = new Uint16Array(length); + + for (let i = 0; i < length; i++) { + array[i] = i; + } + + index = new THREE.BufferAttribute(array, 1); + + geometry.setIndex(index); + } + + const sortArray = []; + + for (let i = 0; i < length; i++) { + vector.fromArray(positions, i * 3); + vector.applyMatrix4(matrix); + + sortArray.push([vector.z, i]); + } + + function numericalSort(a, b) { + return b[0] - a[0]; + } + + sortArray.sort(numericalSort); + + const indices = index.array; + + for (let i = 0; i < length; i++) { + indices[i] = sortArray[i][1]; + } + + geometry.index.needsUpdate = true; +} + +function animate() { + render(); + stats.update(); +} + +function render() { + const time = Date.now() * 0.005; + + sphere.rotation.y = 0.02 * time; + sphere.rotation.z = 0.02 * time; + + const geometry = sphere.geometry; + const attributes = geometry.attributes; + + for (let i = 0; i < attributes.size.array.length; i++) { + if (i < length1) { + attributes.size.array[i] = 16 + 12 * Math.sin(0.1 * i + time); + } + } + + attributes.size.needsUpdate = true; + + sortPoints(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_custom_attributes_points3.ts b/examples-testing/examples/webgl_custom_attributes_points3.ts new file mode 100644 index 000000000..1bca8ccd4 --- /dev/null +++ b/examples-testing/examples/webgl_custom_attributes_points3.ts @@ -0,0 +1,200 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js'; + +let renderer, scene, camera, stats; + +let object; + +let vertices1; + +const WIDTH = window.innerWidth; +const HEIGHT = window.innerHeight; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(40, WIDTH / HEIGHT, 1, 1000); + camera.position.z = 500; + + scene = new THREE.Scene(); + + let radius = 100; + const inner = 0.6 * radius; + const vertex = new THREE.Vector3(); + const vertices = []; + + for (let i = 0; i < 100000; i++) { + vertex.x = Math.random() * 2 - 1; + vertex.y = Math.random() * 2 - 1; + vertex.z = Math.random() * 2 - 1; + vertex.multiplyScalar(radius); + + if ( + vertex.x > inner || + vertex.x < -inner || + vertex.y > inner || + vertex.y < -inner || + vertex.z > inner || + vertex.z < -inner + ) + vertices.push(vertex.x, vertex.y, vertex.z); + } + + vertices1 = vertices.length / 3; + + radius = 200; + + let boxGeometry1 = new THREE.BoxGeometry(radius, 0.1 * radius, 0.1 * radius, 50, 5, 5); + + // if normal and uv attributes are not removed, mergeVertices() can't consolidate identical vertices with different normal/uv data + + boxGeometry1.deleteAttribute('normal'); + boxGeometry1.deleteAttribute('uv'); + + boxGeometry1 = BufferGeometryUtils.mergeVertices(boxGeometry1); + + const matrix = new THREE.Matrix4(); + const position = new THREE.Vector3(); + const rotation = new THREE.Euler(); + const quaternion = new THREE.Quaternion(); + const scale = new THREE.Vector3(1, 1, 1); + + function addGeo(geo, x, y, z, ry) { + position.set(x, y, z); + rotation.set(0, ry, 0); + + matrix.compose(position, quaternion.setFromEuler(rotation), scale); + + const positionAttribute = geo.getAttribute('position'); + + for (let i = 0, l = positionAttribute.count; i < l; i++) { + vertex.fromBufferAttribute(positionAttribute, i); + vertex.applyMatrix4(matrix); + vertices.push(vertex.x, vertex.y, vertex.z); + } + } + + // side 1 + + addGeo(boxGeometry1, 0, 110, 110, 0); + addGeo(boxGeometry1, 0, 110, -110, 0); + addGeo(boxGeometry1, 0, -110, 110, 0); + addGeo(boxGeometry1, 0, -110, -110, 0); + + // side 2 + + addGeo(boxGeometry1, 110, 110, 0, Math.PI / 2); + addGeo(boxGeometry1, 110, -110, 0, Math.PI / 2); + addGeo(boxGeometry1, -110, 110, 0, Math.PI / 2); + addGeo(boxGeometry1, -110, -110, 0, Math.PI / 2); + + // corner edges + + let boxGeometry2 = new THREE.BoxGeometry(0.1 * radius, radius * 1.2, 0.1 * radius, 5, 60, 5); + + boxGeometry2.deleteAttribute('normal'); + boxGeometry2.deleteAttribute('uv'); + + boxGeometry2 = BufferGeometryUtils.mergeVertices(boxGeometry2); + + addGeo(boxGeometry2, 110, 0, 110, 0); + addGeo(boxGeometry2, 110, 0, -110, 0); + addGeo(boxGeometry2, -110, 0, 110, 0); + addGeo(boxGeometry2, -110, 0, -110, 0); + + const positionAttribute = new THREE.Float32BufferAttribute(vertices, 3); + + const colors = []; + const sizes = []; + + const color = new THREE.Color(); + + for (let i = 0; i < positionAttribute.count; i++) { + if (i < vertices1) { + color.setHSL(0.5 + 0.2 * (i / vertices1), 1, 0.5); + } else { + color.setHSL(0.1, 1, 0.5); + } + + color.toArray(colors, i * 3); + + sizes[i] = i < vertices1 ? 10 : 40; + } + + const geometry = new THREE.BufferGeometry(); + geometry.setAttribute('position', positionAttribute); + geometry.setAttribute('ca', new THREE.Float32BufferAttribute(colors, 3)); + geometry.setAttribute('size', new THREE.Float32BufferAttribute(sizes, 1)); + + // + + const texture = new THREE.TextureLoader().load('textures/sprites/ball.png'); + texture.wrapS = THREE.RepeatWrapping; + texture.wrapT = THREE.RepeatWrapping; + + const material = new THREE.ShaderMaterial({ + uniforms: { + amplitude: { value: 1.0 }, + color: { value: new THREE.Color(0xffffff) }, + pointTexture: { value: texture }, + }, + vertexShader: document.getElementById('vertexshader').textContent, + fragmentShader: document.getElementById('fragmentshader').textContent, + }); + + // + + object = new THREE.Points(geometry, material); + scene.add(object); + + // + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(WIDTH, HEIGHT); + renderer.setAnimationLoop(animate); + + const container = document.getElementById('container'); + container.appendChild(renderer.domElement); + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + render(); + stats.update(); +} + +function render() { + const time = Date.now() * 0.01; + + object.rotation.y = object.rotation.z = 0.02 * time; + + const geometry = object.geometry; + const attributes = geometry.attributes; + + for (let i = 0; i < attributes.size.array.length; i++) { + if (i < vertices1) { + attributes.size.array[i] = Math.max(0, 26 + 32 * Math.sin(0.1 * i + 0.6 * time)); + } + } + + attributes.size.needsUpdate = true; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_decals.ts b/examples-testing/examples/webgl_decals.ts new file mode 100644 index 000000000..8f77c30fc --- /dev/null +++ b/examples-testing/examples/webgl_decals.ts @@ -0,0 +1,240 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { DecalGeometry } from 'three/addons/geometries/DecalGeometry.js'; + +const container = document.getElementById('container'); + +let renderer, scene, camera, stats; +let mesh; +let raycaster; +let line; + +const intersection = { + intersects: false, + point: new THREE.Vector3(), + normal: new THREE.Vector3(), +}; +const mouse = new THREE.Vector2(); +const intersects = []; + +const textureLoader = new THREE.TextureLoader(); +const decalDiffuse = textureLoader.load('textures/decal/decal-diffuse.png'); +decalDiffuse.colorSpace = THREE.SRGBColorSpace; +const decalNormal = textureLoader.load('textures/decal/decal-normal.jpg'); + +const decalMaterial = new THREE.MeshPhongMaterial({ + specular: 0x444444, + map: decalDiffuse, + normalMap: decalNormal, + normalScale: new THREE.Vector2(1, 1), + shininess: 30, + transparent: true, + depthTest: true, + depthWrite: false, + polygonOffset: true, + polygonOffsetFactor: -4, + wireframe: false, +}); + +const decals = []; +let mouseHelper; +const position = new THREE.Vector3(); +const orientation = new THREE.Euler(); +const size = new THREE.Vector3(10, 10, 10); + +const params = { + minScale: 10, + maxScale: 20, + rotate: true, + clear: function () { + removeDecals(); + }, +}; + +init(); + +function init() { + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + stats = new Stats(); + container.appendChild(stats.dom); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.z = 120; + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 50; + controls.maxDistance = 200; + + scene.add(new THREE.AmbientLight(0x666666)); + + const dirLight1 = new THREE.DirectionalLight(0xffddcc, 3); + dirLight1.position.set(1, 0.75, 0.5); + scene.add(dirLight1); + + const dirLight2 = new THREE.DirectionalLight(0xccccff, 3); + dirLight2.position.set(-1, 0.75, -0.5); + scene.add(dirLight2); + + const geometry = new THREE.BufferGeometry(); + geometry.setFromPoints([new THREE.Vector3(), new THREE.Vector3()]); + + line = new THREE.Line(geometry, new THREE.LineBasicMaterial()); + scene.add(line); + + loadLeePerrySmith(); + + raycaster = new THREE.Raycaster(); + + mouseHelper = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 10), new THREE.MeshNormalMaterial()); + mouseHelper.visible = false; + scene.add(mouseHelper); + + window.addEventListener('resize', onWindowResize); + + let moved = false; + + controls.addEventListener('change', function () { + moved = true; + }); + + window.addEventListener('pointerdown', function () { + moved = false; + }); + + window.addEventListener('pointerup', function (event) { + if (moved === false) { + checkIntersection(event.clientX, event.clientY); + + if (intersection.intersects) shoot(); + } + }); + + window.addEventListener('pointermove', onPointerMove); + + function onPointerMove(event) { + if (event.isPrimary) { + checkIntersection(event.clientX, event.clientY); + } + } + + function checkIntersection(x, y) { + if (mesh === undefined) return; + + mouse.x = (x / window.innerWidth) * 2 - 1; + mouse.y = -(y / window.innerHeight) * 2 + 1; + + raycaster.setFromCamera(mouse, camera); + raycaster.intersectObject(mesh, false, intersects); + + if (intersects.length > 0) { + const p = intersects[0].point; + mouseHelper.position.copy(p); + intersection.point.copy(p); + + const normalMatrix = new THREE.Matrix3().getNormalMatrix(mesh.matrixWorld); + + const n = intersects[0].face.normal.clone(); + n.applyNormalMatrix(normalMatrix); + n.multiplyScalar(10); + n.add(intersects[0].point); + + intersection.normal.copy(intersects[0].face.normal); + mouseHelper.lookAt(n); + + const positions = line.geometry.attributes.position; + positions.setXYZ(0, p.x, p.y, p.z); + positions.setXYZ(1, n.x, n.y, n.z); + positions.needsUpdate = true; + + intersection.intersects = true; + + intersects.length = 0; + } else { + intersection.intersects = false; + } + } + + const gui = new GUI(); + + gui.add(params, 'minScale', 1, 30); + gui.add(params, 'maxScale', 1, 30); + gui.add(params, 'rotate'); + gui.add(params, 'clear'); + gui.open(); +} + +function loadLeePerrySmith() { + const map = textureLoader.load('models/gltf/LeePerrySmith/Map-COL.jpg'); + map.colorSpace = THREE.SRGBColorSpace; + const specularMap = textureLoader.load('models/gltf/LeePerrySmith/Map-SPEC.jpg'); + const normalMap = textureLoader.load('models/gltf/LeePerrySmith/Infinite-Level_02_Tangent_SmoothUV.jpg'); + + const loader = new GLTFLoader(); + + loader.load('models/gltf/LeePerrySmith/LeePerrySmith.glb', function (gltf) { + mesh = gltf.scene.children[0]; + mesh.material = new THREE.MeshPhongMaterial({ + specular: 0x111111, + map: map, + specularMap: specularMap, + normalMap: normalMap, + shininess: 25, + }); + + scene.add(mesh); + mesh.scale.multiplyScalar(10); + }); +} + +function shoot() { + position.copy(intersection.point); + orientation.copy(mouseHelper.rotation); + + if (params.rotate) orientation.z = Math.random() * 2 * Math.PI; + + const scale = params.minScale + Math.random() * (params.maxScale - params.minScale); + size.set(scale, scale, scale); + + const material = decalMaterial.clone(); + material.color.setHex(Math.random() * 0xffffff); + + const m = new THREE.Mesh(new DecalGeometry(mesh, position, orientation, size), material); + m.renderOrder = decals.length; // give decals a fixed render order + + decals.push(m); + + mesh.attach(m); +} + +function removeDecals() { + decals.forEach(function (d) { + mesh.remove(d); + }); + + decals.length = 0; +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/webgl_effects_anaglyph.ts b/examples-testing/examples/webgl_effects_anaglyph.ts new file mode 100644 index 000000000..7e67c9173 --- /dev/null +++ b/examples-testing/examples/webgl_effects_anaglyph.ts @@ -0,0 +1,120 @@ +import * as THREE from 'three'; + +import { AnaglyphEffect } from 'three/addons/effects/AnaglyphEffect.js'; + +let container, camera, scene, renderer, effect; + +const spheres = []; + +let mouseX = 0; +let mouseY = 0; + +let windowHalfX = window.innerWidth / 2; +let windowHalfY = window.innerHeight / 2; + +document.addEventListener('mousemove', onDocumentMouseMove); + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.01, 100); + camera.position.z = 3; + + const path = 'textures/cube/pisa/'; + const format = '.png'; + const urls = [ + path + 'px' + format, + path + 'nx' + format, + path + 'py' + format, + path + 'ny' + format, + path + 'pz' + format, + path + 'nz' + format, + ]; + + const textureCube = new THREE.CubeTextureLoader().load(urls); + + scene = new THREE.Scene(); + scene.background = textureCube; + + const geometry = new THREE.SphereGeometry(0.1, 32, 16); + const material = new THREE.MeshBasicMaterial({ color: 0xffffff, envMap: textureCube }); + + for (let i = 0; i < 500; i++) { + const mesh = new THREE.Mesh(geometry, material); + + mesh.position.x = Math.random() * 10 - 5; + mesh.position.y = Math.random() * 10 - 5; + mesh.position.z = Math.random() * 10 - 5; + + mesh.scale.x = mesh.scale.y = mesh.scale.z = Math.random() * 3 + 1; + + scene.add(mesh); + + spheres.push(mesh); + } + + // + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + const width = window.innerWidth || 2; + const height = window.innerHeight || 2; + + effect = new AnaglyphEffect(renderer); + effect.setSize(width, height); + + // Configure stereo parameters for physically-correct rendering + // eyeSep: interpupillary distance (default 0.064m / 64mm for humans) + // planeDistance: distance to the zero-parallax plane (objects here appear at screen depth) + effect.eyeSep = 0.064; + effect.planeDistance = 3; // Match camera distance to origin for zero parallax at scene center + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + windowHalfX = window.innerWidth / 2; + windowHalfY = window.innerHeight / 2; + + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + effect.setSize(window.innerWidth, window.innerHeight); +} + +function onDocumentMouseMove(event) { + mouseX = (event.clientX - windowHalfX) / 100; + mouseY = (event.clientY - windowHalfY) / 100; +} + +// + +function animate() { + render(); +} + +function render() { + const timer = 0.0001 * Date.now(); + + camera.position.x += (mouseX - camera.position.x) * 0.05; + camera.position.y += (-mouseY - camera.position.y) * 0.05; + + camera.lookAt(scene.position); + + for (let i = 0, il = spheres.length; i < il; i++) { + const sphere = spheres[i]; + + sphere.position.x = 5 * Math.cos(timer + i); + sphere.position.y = 5 * Math.sin(timer + i * 1.1); + } + + effect.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_effects_ascii.ts b/examples-testing/examples/webgl_effects_ascii.ts new file mode 100644 index 000000000..a412bb79e --- /dev/null +++ b/examples-testing/examples/webgl_effects_ascii.ts @@ -0,0 +1,81 @@ +import * as THREE from 'three'; + +import { AsciiEffect } from 'three/addons/effects/AsciiEffect.js'; +import { TrackballControls } from 'three/addons/controls/TrackballControls.js'; + +let camera, controls, scene, renderer, effect; + +let sphere, plane; + +const start = Date.now(); + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.y = 150; + camera.position.z = 500; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0, 0, 0); + + const pointLight1 = new THREE.PointLight(0xffffff, 3, 0, 0); + pointLight1.position.set(500, 500, 500); + scene.add(pointLight1); + + const pointLight2 = new THREE.PointLight(0xffffff, 1, 0, 0); + pointLight2.position.set(-500, -500, -500); + scene.add(pointLight2); + + sphere = new THREE.Mesh(new THREE.SphereGeometry(200, 20, 10), new THREE.MeshPhongMaterial({ flatShading: true })); + scene.add(sphere); + + // Plane + + plane = new THREE.Mesh(new THREE.PlaneGeometry(400, 400), new THREE.MeshBasicMaterial({ color: 0xe0e0e0 })); + plane.position.y = -200; + plane.rotation.x = -Math.PI / 2; + scene.add(plane); + + renderer = new THREE.WebGLRenderer(); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + + effect = new AsciiEffect(renderer, ' .:-+*=%@#', { invert: true }); + effect.setSize(window.innerWidth, window.innerHeight); + effect.domElement.style.color = 'white'; + effect.domElement.style.backgroundColor = 'black'; + + // Special case: append effect.domElement, instead of renderer.domElement. + // AsciiEffect creates a custom domElement (a div container) where the ASCII elements are placed. + + document.body.appendChild(effect.domElement); + + controls = new TrackballControls(camera, effect.domElement); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + effect.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + const timer = Date.now() - start; + + sphere.position.y = Math.abs(Math.sin(timer * 0.002)) * 150; + sphere.rotation.x = timer * 0.0003; + sphere.rotation.z = timer * 0.0002; + + controls.update(); + + effect.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_effects_parallaxbarrier.ts b/examples-testing/examples/webgl_effects_parallaxbarrier.ts new file mode 100644 index 000000000..90c867973 --- /dev/null +++ b/examples-testing/examples/webgl_effects_parallaxbarrier.ts @@ -0,0 +1,110 @@ +import * as THREE from 'three'; + +import { ParallaxBarrierEffect } from 'three/addons/effects/ParallaxBarrierEffect.js'; + +let container, camera, scene, renderer, effect; + +const spheres = []; + +let mouseX = 0; +let mouseY = 0; + +let windowHalfX = window.innerWidth / 2; +let windowHalfY = window.innerHeight / 2; + +document.addEventListener('mousemove', onDocumentMouseMove); + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.01, 100); + camera.position.z = 3; + + const path = 'textures/cube/pisa/'; + const format = '.png'; + const urls = [ + path + 'px' + format, + path + 'nx' + format, + path + 'py' + format, + path + 'ny' + format, + path + 'pz' + format, + path + 'nz' + format, + ]; + + const textureCube = new THREE.CubeTextureLoader().load(urls); + + scene = new THREE.Scene(); + scene.background = textureCube; + + const geometry = new THREE.SphereGeometry(0.1, 32, 16); + const material = new THREE.MeshBasicMaterial({ color: 0xffffff, envMap: textureCube }); + + for (let i = 0; i < 500; i++) { + const mesh = new THREE.Mesh(geometry, material); + + mesh.position.x = Math.random() * 10 - 5; + mesh.position.y = Math.random() * 10 - 5; + mesh.position.z = Math.random() * 10 - 5; + + mesh.scale.x = mesh.scale.y = mesh.scale.z = Math.random() * 3 + 1; + + scene.add(mesh); + + spheres.push(mesh); + } + + // + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + const width = window.innerWidth || 2; + const height = window.innerHeight || 2; + + effect = new ParallaxBarrierEffect(renderer); + effect.setSize(width, height); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + windowHalfX = window.innerWidth / 2; + windowHalfY = window.innerHeight / 2; + + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + effect.setSize(window.innerWidth, window.innerHeight); +} + +function onDocumentMouseMove(event) { + mouseX = (event.clientX - windowHalfX) / 100; + mouseY = (event.clientY - windowHalfY) / 100; +} + +// + +function animate() { + const timer = 0.0001 * Date.now(); + + camera.position.x += (mouseX - camera.position.x) * 0.05; + camera.position.y += (-mouseY - camera.position.y) * 0.05; + + camera.lookAt(scene.position); + + for (let i = 0, il = spheres.length; i < il; i++) { + const sphere = spheres[i]; + + sphere.position.x = 5 * Math.cos(timer + i); + sphere.position.y = 5 * Math.sin(timer + i * 1.1); + } + + effect.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_effects_stereo.ts b/examples-testing/examples/webgl_effects_stereo.ts new file mode 100644 index 000000000..673850541 --- /dev/null +++ b/examples-testing/examples/webgl_effects_stereo.ts @@ -0,0 +1,98 @@ +import * as THREE from 'three'; + +import { StereoEffect } from 'three/addons/effects/StereoEffect.js'; + +let container, camera, scene, renderer, effect; + +const spheres = []; + +let mouseX = 0, + mouseY = 0; + +let windowHalfX = window.innerWidth / 2; +let windowHalfY = window.innerHeight / 2; + +document.addEventListener('mousemove', onDocumentMouseMove); + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.z = 3; + + scene = new THREE.Scene(); + scene.background = new THREE.CubeTextureLoader() + .setPath('textures/cube/Park3Med/') + .load(['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg']); + + const geometry = new THREE.SphereGeometry(0.1, 32, 16); + + const textureCube = new THREE.CubeTextureLoader() + .setPath('textures/cube/Park3Med/') + .load(['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg']); + textureCube.mapping = THREE.CubeRefractionMapping; + + const material = new THREE.MeshBasicMaterial({ color: 0xffffff, envMap: textureCube, refractionRatio: 0.95 }); + + for (let i = 0; i < 500; i++) { + const mesh = new THREE.Mesh(geometry, material); + mesh.position.x = Math.random() * 10 - 5; + mesh.position.y = Math.random() * 10 - 5; + mesh.position.z = Math.random() * 10 - 5; + mesh.scale.x = mesh.scale.y = mesh.scale.z = Math.random() * 3 + 1; + scene.add(mesh); + + spheres.push(mesh); + } + + // + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + effect = new StereoEffect(renderer); + effect.setSize(window.innerWidth, window.innerHeight); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + windowHalfX = window.innerWidth / 2; + windowHalfY = window.innerHeight / 2; + + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + effect.setSize(window.innerWidth, window.innerHeight); +} + +function onDocumentMouseMove(event) { + mouseX = (event.clientX - windowHalfX) * 0.01; + mouseY = (event.clientY - windowHalfY) * 0.01; +} + +// + +function animate() { + const timer = 0.0001 * Date.now(); + + camera.position.x += (mouseX - camera.position.x) * 0.05; + camera.position.y += (-mouseY - camera.position.y) * 0.05; + camera.lookAt(scene.position); + + for (let i = 0, il = spheres.length; i < il; i++) { + const sphere = spheres[i]; + + sphere.position.x = 5 * Math.cos(timer + i); + sphere.position.y = 5 * Math.sin(timer + i * 1.1); + } + + effect.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_framebuffer_texture.ts b/examples-testing/examples/webgl_framebuffer_texture.ts new file mode 100644 index 000000000..df4acc9d6 --- /dev/null +++ b/examples-testing/examples/webgl_framebuffer_texture.ts @@ -0,0 +1,151 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import * as GeometryUtils from 'three/addons/utils/GeometryUtils.js'; + +let camera, scene, renderer; +let line, sprite, texture; + +let cameraOrtho, sceneOrtho; + +let offset = 0; + +const dpr = window.devicePixelRatio; + +const textureSize = 128 * dpr; +const vector = new THREE.Vector2(); +const color = new THREE.Color(); + +init(); + +function init() { + // + + const width = window.innerWidth; + const height = window.innerHeight; + + camera = new THREE.PerspectiveCamera(70, width / height, 1, 1000); + camera.position.z = 20; + + cameraOrtho = new THREE.OrthographicCamera(-width / 2, width / 2, height / 2, -height / 2, 1, 10); + cameraOrtho.position.z = 10; + + scene = new THREE.Scene(); + sceneOrtho = new THREE.Scene(); + + // + + const points = GeometryUtils.gosper(8); + + const geometry = new THREE.BufferGeometry(); + const positionAttribute = new THREE.Float32BufferAttribute(points, 3); + geometry.setAttribute('position', positionAttribute); + geometry.center(); + + const colorAttribute = new THREE.BufferAttribute(new Float32Array(positionAttribute.array.length), 3); + colorAttribute.setUsage(THREE.DynamicDrawUsage); + geometry.setAttribute('color', colorAttribute); + + const material = new THREE.LineBasicMaterial({ vertexColors: true }); + + line = new THREE.Line(geometry, material); + line.scale.setScalar(0.05); + scene.add(line); + + // + + texture = new THREE.FramebufferTexture(textureSize, textureSize); + + // + + const spriteMaterial = new THREE.SpriteMaterial({ map: texture }); + sprite = new THREE.Sprite(spriteMaterial); + sprite.scale.set(textureSize, textureSize, 1); + sceneOrtho.add(sprite); + + updateSpritePosition(); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.autoClear = false; + document.body.appendChild(renderer.domElement); + + // + + const selection = document.getElementById('selection'); + const controls = new OrbitControls(camera, selection); + controls.enablePan = false; + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + cameraOrtho.left = -width / 2; + cameraOrtho.right = width / 2; + cameraOrtho.top = height / 2; + cameraOrtho.bottom = -height / 2; + cameraOrtho.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + updateSpritePosition(); +} + +function updateSpritePosition() { + const halfWidth = window.innerWidth / 2; + const halfHeight = window.innerHeight / 2; + + const halfImageWidth = textureSize / 2; + const halfImageHeight = textureSize / 2; + + sprite.position.set(-halfWidth + halfImageWidth, halfHeight - halfImageHeight, 1); +} + +function animate() { + const colorAttribute = line.geometry.getAttribute('color'); + updateColors(colorAttribute); + + // scene rendering + + renderer.clear(); + renderer.render(scene, camera); + + // calculate start position for copying data + + vector.x = (window.innerWidth * dpr) / 2 - textureSize / 2; + vector.y = (window.innerHeight * dpr) / 2 - textureSize / 2; + + renderer.copyFramebufferToTexture(texture, vector); + + renderer.clearDepth(); + renderer.render(sceneOrtho, cameraOrtho); +} + +function updateColors(colorAttribute) { + const l = colorAttribute.count; + + for (let i = 0; i < l; i++) { + const h = ((offset + i) % l) / l; + + color.setHSL(h, 1, 0.5); + colorAttribute.setX(i, color.r); + colorAttribute.setY(i, color.g); + colorAttribute.setZ(i, color.b); + } + + colorAttribute.needsUpdate = true; + + offset -= 25; +} diff --git a/examples-testing/examples/webgl_furnace_test.ts b/examples-testing/examples/webgl_furnace_test.ts new file mode 100644 index 000000000..a81954176 --- /dev/null +++ b/examples-testing/examples/webgl_furnace_test.ts @@ -0,0 +1,96 @@ +import * as THREE from 'three'; + +let scene, camera, renderer, radianceMap; + +const COLOR = 0xcccccc; + +function init() { + const width = window.innerWidth; + const height = window.innerHeight; + const aspect = width / height; + + // renderer + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setSize(width, height); + renderer.setPixelRatio(window.devicePixelRatio); + document.body.appendChild(renderer.domElement); + + window.addEventListener('resize', onWindowResize); + + document.body.addEventListener('mouseover', function () { + scene.traverse(function (child) { + if (child.isMesh) child.material.color.setHex(0xffffff); + }); + + render(); + }); + + document.body.addEventListener('mouseout', function () { + scene.traverse(function (child) { + if (child.isMesh) child.material.color.setHex(0xccccff); // tinted for visibility + }); + + render(); + }); + + // scene + + scene = new THREE.Scene(); + + // camera + camera = new THREE.PerspectiveCamera(40, aspect, 1, 30); + camera.position.set(0, 0, 18); +} + +function createObjects() { + const geometry = new THREE.SphereGeometry(0.4, 32, 16); + + for (let x = 0; x <= 10; x++) { + for (let y = 0; y <= 10; y++) { + const material = new THREE.MeshPhysicalMaterial({ + roughness: x / 10, + metalness: y / 10, + color: 0xffffff, + envMap: radianceMap, + envMapIntensity: 1, + transmission: 0, + ior: 1.5, + }); + + const mesh = new THREE.Mesh(geometry, material); + mesh.position.x = x - 5; + mesh.position.y = 5 - y; + scene.add(mesh); + } + } +} + +function createEnvironment() { + const envScene = new THREE.Scene(); + envScene.background = new THREE.Color(COLOR); + + const pmremGenerator = new THREE.PMREMGenerator(renderer); + radianceMap = pmremGenerator.fromScene(envScene).texture; + pmremGenerator.dispose(); + + scene.background = envScene.background; +} + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); + + render(); +} + +function render() { + renderer.render(scene, camera); +} + +Promise.resolve().then(init).then(createEnvironment).then(createObjects).then(render); diff --git a/examples-testing/examples/webgl_geometries.ts b/examples-testing/examples/webgl_geometries.ts new file mode 100644 index 000000000..0eb50120c --- /dev/null +++ b/examples-testing/examples/webgl_geometries.ts @@ -0,0 +1,165 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { ParametricGeometry } from 'three/addons/geometries/ParametricGeometry.js'; +import { plane, klein, mobius } from 'three/addons/geometries/ParametricFunctions.js'; + +let camera, scene, renderer, stats; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000); + camera.position.y = 500; + + scene = new THREE.Scene(); + + let object, geometry; + + const ambientLight = new THREE.AmbientLight(0xcccccc, 1.5); + scene.add(ambientLight); + + const pointLight = new THREE.PointLight(0xffffff, 2.5, 0, 0); + camera.add(pointLight); + scene.add(camera); + + const map = new THREE.TextureLoader().load('textures/uv_grid_opengl.jpg'); + map.wrapS = map.wrapT = THREE.RepeatWrapping; + map.anisotropy = 16; + map.colorSpace = THREE.SRGBColorSpace; + + const material = new THREE.MeshPhongMaterial({ map: map, side: THREE.DoubleSide }); + + // + + object = new THREE.Mesh(new THREE.SphereGeometry(75, 20, 10), material); + object.position.set(-300, 0, 300); + scene.add(object); + + object = new THREE.Mesh(new THREE.IcosahedronGeometry(75), material); + object.position.set(-100, 0, 300); + scene.add(object); + + object = new THREE.Mesh(new THREE.OctahedronGeometry(75), material); + object.position.set(100, 0, 300); + scene.add(object); + + object = new THREE.Mesh(new THREE.TetrahedronGeometry(75), material); + object.position.set(300, 0, 300); + scene.add(object); + + // + + object = new THREE.Mesh(new THREE.PlaneGeometry(100, 100, 4, 4), material); + object.position.set(-300, 0, 100); + scene.add(object); + + object = new THREE.Mesh(new THREE.BoxGeometry(100, 100, 100, 4, 4, 4), material); + object.position.set(-100, 0, 100); + scene.add(object); + + object = new THREE.Mesh(new THREE.CircleGeometry(50, 20, 0, Math.PI * 2), material); + object.position.set(100, 0, 100); + scene.add(object); + + object = new THREE.Mesh(new THREE.RingGeometry(10, 50, 20, 5, 0, Math.PI * 2), material); + object.position.set(300, 0, 100); + scene.add(object); + + // + + object = new THREE.Mesh(new THREE.CylinderGeometry(25, 75, 100, 40, 5), material); + object.position.set(-300, 0, -100); + scene.add(object); + + const points = []; + + for (let i = 0; i < 50; i++) { + points.push(new THREE.Vector2(Math.sin(i * 0.2) * Math.sin(i * 0.1) * 15 + 50, (i - 5) * 2)); + } + + object = new THREE.Mesh(new THREE.LatheGeometry(points, 20), material); + object.position.set(-100, 0, -100); + scene.add(object); + + object = new THREE.Mesh(new THREE.TorusGeometry(50, 20, 20, 20), material); + object.position.set(100, 0, -100); + scene.add(object); + + object = new THREE.Mesh(new THREE.TorusKnotGeometry(50, 10, 50, 20), material); + object.position.set(300, 0, -100); + scene.add(object); + + // + + object = new THREE.Mesh(new THREE.CapsuleGeometry(20, 50), material); + object.position.set(-300, 0, -300); + scene.add(object); + + geometry = new ParametricGeometry(plane, 10, 10); + geometry.scale(100, 100, 100); + geometry.center(); + object = new THREE.Mesh(geometry, material); + object.position.set(-100, 0, -300); + scene.add(object); + + geometry = new ParametricGeometry(klein, 20, 20); + object = new THREE.Mesh(geometry, material); + object.position.set(100, 0, -300); + object.scale.multiplyScalar(5); + scene.add(object); + + geometry = new ParametricGeometry(mobius, 20, 20); + object = new THREE.Mesh(geometry, material); + object.position.set(300, 0, -300); + object.scale.multiplyScalar(30); + scene.add(object); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + stats = new Stats(); + document.body.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + render(); + stats.update(); +} + +function render() { + const timer = Date.now() * 0.0001; + + camera.position.x = Math.cos(timer) * 800; + camera.position.z = Math.sin(timer) * 800; + + camera.lookAt(scene.position); + + scene.traverse(function (object) { + if (object.isMesh === true) { + object.rotation.x = timer * 5; + object.rotation.y = timer * 2.5; + } + }); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_geometry_colors.ts b/examples-testing/examples/webgl_geometry_colors.ts new file mode 100644 index 000000000..3166c03bc --- /dev/null +++ b/examples-testing/examples/webgl_geometry_colors.ts @@ -0,0 +1,177 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +let container, stats; + +let camera, scene, renderer; + +let mouseX = 0, + mouseY = 0; + +let windowHalfX = window.innerWidth / 2; +let windowHalfY = window.innerHeight / 2; + +init(); + +function init() { + container = document.getElementById('container'); + + camera = new THREE.PerspectiveCamera(20, window.innerWidth / window.innerHeight, 1, 10000); + camera.position.z = 1800; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xffffff); + + const light = new THREE.DirectionalLight(0xffffff, 3); + light.position.set(0, 0, 1); + scene.add(light); + + // shadow + + const canvas = document.createElement('canvas'); + canvas.width = 128; + canvas.height = 128; + + const context = canvas.getContext('2d'); + const gradient = context.createRadialGradient( + canvas.width / 2, + canvas.height / 2, + 0, + canvas.width / 2, + canvas.height / 2, + canvas.width / 2, + ); + gradient.addColorStop(0.1, 'rgba(210,210,210,1)'); + gradient.addColorStop(1, 'rgba(255,255,255,1)'); + + context.fillStyle = gradient; + context.fillRect(0, 0, canvas.width, canvas.height); + + const shadowTexture = new THREE.CanvasTexture(canvas); + + const shadowMaterial = new THREE.MeshBasicMaterial({ map: shadowTexture }); + const shadowGeo = new THREE.PlaneGeometry(300, 300, 1, 1); + + let shadowMesh; + + shadowMesh = new THREE.Mesh(shadowGeo, shadowMaterial); + shadowMesh.position.y = -250; + shadowMesh.rotation.x = -Math.PI / 2; + scene.add(shadowMesh); + + shadowMesh = new THREE.Mesh(shadowGeo, shadowMaterial); + shadowMesh.position.y = -250; + shadowMesh.position.x = -400; + shadowMesh.rotation.x = -Math.PI / 2; + scene.add(shadowMesh); + + shadowMesh = new THREE.Mesh(shadowGeo, shadowMaterial); + shadowMesh.position.y = -250; + shadowMesh.position.x = 400; + shadowMesh.rotation.x = -Math.PI / 2; + scene.add(shadowMesh); + + const radius = 200; + + const geometry1 = new THREE.IcosahedronGeometry(radius, 1); + + const count = geometry1.attributes.position.count; + const arrayType = typeof Float16Array !== 'undefined' ? Float16Array : Float32Array; + geometry1.setAttribute('color', new THREE.BufferAttribute(new arrayType(count * 3), 3)); + + const geometry2 = geometry1.clone(); + const geometry3 = geometry1.clone(); + + const color = new THREE.Color(); + const positions1 = geometry1.attributes.position; + const positions2 = geometry2.attributes.position; + const positions3 = geometry3.attributes.position; + const colors1 = geometry1.attributes.color; + const colors2 = geometry2.attributes.color; + const colors3 = geometry3.attributes.color; + + for (let i = 0; i < count; i++) { + color.setHSL((positions1.getY(i) / radius + 1) / 2, 1.0, 0.5, THREE.SRGBColorSpace); + colors1.setXYZ(i, color.r, color.g, color.b); + + color.setHSL(0, (positions2.getY(i) / radius + 1) / 2, 0.5, THREE.SRGBColorSpace); + colors2.setXYZ(i, color.r, color.g, color.b); + + color.setRGB(1, 0.8 - (positions3.getY(i) / radius + 1) / 2, 0, THREE.SRGBColorSpace); + colors3.setXYZ(i, color.r, color.g, color.b); + } + + const material = new THREE.MeshPhongMaterial({ + color: 0xffffff, + flatShading: true, + vertexColors: true, + shininess: 0, + }); + + const wireframeMaterial = new THREE.MeshBasicMaterial({ color: 0x000000, wireframe: true, transparent: true }); + + let mesh = new THREE.Mesh(geometry1, material); + let wireframe = new THREE.Mesh(geometry1, wireframeMaterial); + mesh.add(wireframe); + mesh.position.x = -400; + mesh.rotation.x = -1.87; + scene.add(mesh); + + mesh = new THREE.Mesh(geometry2, material); + wireframe = new THREE.Mesh(geometry2, wireframeMaterial); + mesh.add(wireframe); + mesh.position.x = 400; + scene.add(mesh); + + mesh = new THREE.Mesh(geometry3, material); + wireframe = new THREE.Mesh(geometry3, wireframeMaterial); + mesh.add(wireframe); + scene.add(mesh); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + stats = new Stats(); + container.appendChild(stats.dom); + + document.addEventListener('mousemove', onDocumentMouseMove); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + windowHalfX = window.innerWidth / 2; + windowHalfY = window.innerHeight / 2; + + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function onDocumentMouseMove(event) { + mouseX = event.clientX - windowHalfX; + mouseY = event.clientY - windowHalfY; +} + +// + +function animate() { + render(); + stats.update(); +} + +function render() { + camera.position.x += (mouseX - camera.position.x) * 0.05; + camera.position.y += (-mouseY - camera.position.y) * 0.05; + + camera.lookAt(scene.position); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_geometry_colors_lookuptable.ts b/examples-testing/examples/webgl_geometry_colors_lookuptable.ts new file mode 100644 index 000000000..6b0138529 --- /dev/null +++ b/examples-testing/examples/webgl_geometry_colors_lookuptable.ts @@ -0,0 +1,148 @@ +import * as THREE from 'three'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { Lut } from 'three/addons/math/Lut.js'; + +let container; + +let perpCamera, orthoCamera, renderer, lut; + +let mesh, sprite; +let scene, uiScene; + +let params; + +init(); + +function init() { + container = document.getElementById('container'); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xffffff); + + uiScene = new THREE.Scene(); + + lut = new Lut(); + + const width = window.innerWidth; + const height = window.innerHeight; + + perpCamera = new THREE.PerspectiveCamera(60, width / height, 1, 100); + perpCamera.position.set(0, 0, 10); + scene.add(perpCamera); + + orthoCamera = new THREE.OrthographicCamera(-1, 1, 1, -1, 1, 2); + orthoCamera.position.set(0.5, 0, 1); + + sprite = new THREE.Sprite( + new THREE.SpriteMaterial({ + map: new THREE.CanvasTexture(lut.createCanvas()), + }), + ); + sprite.material.map.colorSpace = THREE.SRGBColorSpace; + sprite.scale.x = 0.125; + uiScene.add(sprite); + + mesh = new THREE.Mesh( + undefined, + new THREE.MeshLambertMaterial({ + side: THREE.DoubleSide, + color: 0xf5f5f5, + vertexColors: true, + }), + ); + scene.add(mesh); + + params = { + colorMap: 'rainbow', + }; + loadModel(); + + const pointLight = new THREE.PointLight(0xffffff, 3, 0, 0); + perpCamera.add(pointLight); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.autoClear = false; + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(width, height); + container.appendChild(renderer.domElement); + + window.addEventListener('resize', onWindowResize); + + const controls = new OrbitControls(perpCamera, renderer.domElement); + controls.addEventListener('change', render); + + const gui = new GUI(); + + gui.add(params, 'colorMap', ['rainbow', 'cooltowarm', 'blackbody', 'grayscale']).onChange(function () { + updateColors(); + render(); + }); +} + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + perpCamera.aspect = width / height; + perpCamera.updateProjectionMatrix(); + + renderer.setSize(width, height); + render(); +} + +function render() { + renderer.clear(); + renderer.render(scene, perpCamera); + renderer.render(uiScene, orthoCamera); +} + +function loadModel() { + const loader = new THREE.BufferGeometryLoader(); + loader.load('models/json/pressure.json', function (geometry) { + geometry.center(); + geometry.computeVertexNormals(); + + // default color attribute + const colors = []; + + for (let i = 0, n = geometry.attributes.position.count; i < n; ++i) { + colors.push(1, 1, 1); + } + + geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); + + mesh.geometry = geometry; + updateColors(); + + render(); + }); +} + +function updateColors() { + lut.setColorMap(params.colorMap); + + lut.setMax(2000); + lut.setMin(0); + + const geometry = mesh.geometry; + const pressures = geometry.attributes.pressure; + const colors = geometry.attributes.color; + const color = new THREE.Color(); + + for (let i = 0; i < pressures.array.length; i++) { + const colorValue = pressures.array[i]; + + color.copy(lut.getColor(colorValue)).convertSRGBToLinear(); + + colors.setXYZ(i, color.r, color.g, color.b); + } + + colors.needsUpdate = true; + + const map = sprite.material.map; + lut.updateCanvas(map.image); + map.needsUpdate = true; +} diff --git a/examples-testing/examples/webgl_geometry_convex.ts b/examples-testing/examples/webgl_geometry_convex.ts new file mode 100644 index 000000000..09516c070 --- /dev/null +++ b/examples-testing/examples/webgl_geometry_convex.ts @@ -0,0 +1,117 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { ConvexGeometry } from 'three/addons/geometries/ConvexGeometry.js'; +import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js'; + +let group, camera, scene, renderer; + +init(); + +function init() { + scene = new THREE.Scene(); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // camera + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(15, 20, 30); + scene.add(camera); + + // controls + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 20; + controls.maxDistance = 50; + controls.maxPolarAngle = Math.PI / 2; + + // ambient light + + scene.add(new THREE.AmbientLight(0x666666)); + + // point light + + const light = new THREE.PointLight(0xffffff, 3, 0, 0); + camera.add(light); + + // helper + + scene.add(new THREE.AxesHelper(20)); + + // textures + + const loader = new THREE.TextureLoader(); + const texture = loader.load('textures/sprites/disc.png'); + texture.colorSpace = THREE.SRGBColorSpace; + + group = new THREE.Group(); + scene.add(group); + + // points + + let dodecahedronGeometry = new THREE.DodecahedronGeometry(10); + + // if normal and uv attributes are not removed, mergeVertices() can't consolidate identical vertices with different normal/uv data + + dodecahedronGeometry.deleteAttribute('normal'); + dodecahedronGeometry.deleteAttribute('uv'); + + dodecahedronGeometry = BufferGeometryUtils.mergeVertices(dodecahedronGeometry); + + const vertices = []; + const positionAttribute = dodecahedronGeometry.getAttribute('position'); + + for (let i = 0; i < positionAttribute.count; i++) { + const vertex = new THREE.Vector3(); + vertex.fromBufferAttribute(positionAttribute, i); + vertices.push(vertex); + } + + const pointsMaterial = new THREE.PointsMaterial({ + color: 0x0080ff, + map: texture, + size: 1, + alphaTest: 0.5, + }); + + const pointsGeometry = new THREE.BufferGeometry().setFromPoints(vertices); + + const points = new THREE.Points(pointsGeometry, pointsMaterial); + group.add(points); + + // convex hull + + const meshMaterial = new THREE.MeshLambertMaterial({ + color: 0xffffff, + opacity: 0.5, + side: THREE.DoubleSide, + transparent: true, + }); + + const meshGeometry = new ConvexGeometry(vertices); + + const mesh = new THREE.Mesh(meshGeometry, meshMaterial); + group.add(mesh); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + group.rotation.y += 0.005; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_geometry_cube.ts b/examples-testing/examples/webgl_geometry_cube.ts new file mode 100644 index 000000000..572601acb --- /dev/null +++ b/examples-testing/examples/webgl_geometry_cube.ts @@ -0,0 +1,46 @@ +import * as THREE from 'three'; + +let camera, scene, renderer; +let mesh; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.z = 2; + + scene = new THREE.Scene(); + + const texture = new THREE.TextureLoader().load('textures/crate.gif'); + texture.colorSpace = THREE.SRGBColorSpace; + + const geometry = new THREE.BoxGeometry(); + const material = new THREE.MeshBasicMaterial({ map: texture }); + + mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + mesh.rotation.x += 0.005; + mesh.rotation.y += 0.01; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_geometry_extrude_shapes.ts b/examples-testing/examples/webgl_geometry_extrude_shapes.ts new file mode 100644 index 000000000..a905c8ea3 --- /dev/null +++ b/examples-testing/examples/webgl_geometry_extrude_shapes.ts @@ -0,0 +1,159 @@ +import * as THREE from 'three'; + +import { TrackballControls } from 'three/addons/controls/TrackballControls.js'; + +let camera, scene, renderer, controls; + +init(); + +function init() { + const info = document.createElement('div'); + info.style.position = 'absolute'; + info.style.top = '10px'; + info.style.width = '100%'; + info.style.textAlign = 'center'; + info.style.color = '#fff'; + info.innerHTML = + 'three.js webgl - geometry extrude shapes'; + document.body.appendChild(info); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x222222); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(0, 0, 500); + + controls = new TrackballControls(camera, renderer.domElement); + controls.minDistance = 200; + controls.maxDistance = 500; + + scene.add(new THREE.AmbientLight(0x666666)); + + const light = new THREE.PointLight(0xffffff, 3, 0, 0); + light.position.copy(camera.position); + scene.add(light); + + // + + const closedSpline = new THREE.CatmullRomCurve3([ + new THREE.Vector3(-60, -100, 60), + new THREE.Vector3(-60, 20, 60), + new THREE.Vector3(-60, 120, 60), + new THREE.Vector3(60, 20, -60), + new THREE.Vector3(60, -100, -60), + ]); + + closedSpline.curveType = 'catmullrom'; + closedSpline.closed = true; + + const extrudeSettings1 = { + steps: 100, + bevelEnabled: false, + extrudePath: closedSpline, + }; + + const pts1 = [], + count = 3; + + for (let i = 0; i < count; i++) { + const l = 20; + + const a = ((2 * i) / count) * Math.PI; + + pts1.push(new THREE.Vector2(Math.cos(a) * l, Math.sin(a) * l)); + } + + const shape1 = new THREE.Shape(pts1); + + const geometry1 = new THREE.ExtrudeGeometry(shape1, extrudeSettings1); + + const material1 = new THREE.MeshLambertMaterial({ color: 0xb00000, wireframe: false }); + + const mesh1 = new THREE.Mesh(geometry1, material1); + + scene.add(mesh1); + + // + + const randomPoints = []; + + for (let i = 0; i < 10; i++) { + randomPoints.push( + new THREE.Vector3((i - 4.5) * 50, THREE.MathUtils.randFloat(-50, 50), THREE.MathUtils.randFloat(-50, 50)), + ); + } + + const randomSpline = new THREE.CatmullRomCurve3(randomPoints); + + // + + const extrudeSettings2 = { + steps: 200, + bevelEnabled: false, + extrudePath: randomSpline, + }; + + const pts2 = [], + numPts = 5; + + for (let i = 0; i < numPts * 2; i++) { + const l = i % 2 == 1 ? 10 : 20; + + const a = (i / numPts) * Math.PI; + + pts2.push(new THREE.Vector2(Math.cos(a) * l, Math.sin(a) * l)); + } + + const shape2 = new THREE.Shape(pts2); + + const geometry2 = new THREE.ExtrudeGeometry(shape2, extrudeSettings2); + + const material2 = new THREE.MeshLambertMaterial({ color: 0xff8000, wireframe: false }); + + const mesh2 = new THREE.Mesh(geometry2, material2); + + scene.add(mesh2); + + // + + const materials = [material1, material2]; + + const extrudeSettings3 = { + depth: 20, + steps: 1, + bevelEnabled: true, + bevelThickness: 2, + bevelSize: 4, + bevelSegments: 1, + }; + + const geometry3 = new THREE.ExtrudeGeometry(shape2, extrudeSettings3); + + const mesh3 = new THREE.Mesh(geometry3, materials); + + mesh3.position.set(50, 100, 50); + + scene.add(mesh3); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + controls.update(); + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_geometry_extrude_splines.ts b/examples-testing/examples/webgl_geometry_extrude_splines.ts new file mode 100644 index 000000000..8636812f7 --- /dev/null +++ b/examples-testing/examples/webgl_geometry_extrude_splines.ts @@ -0,0 +1,310 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import * as Curves from 'three/addons/curves/CurveExtras.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let container, stats; + +let camera, scene, renderer, splineCamera, cameraHelper, cameraEye; + +const direction = new THREE.Vector3(); +const binormal = new THREE.Vector3(); +const normal = new THREE.Vector3(); +const position = new THREE.Vector3(); +const lookAt = new THREE.Vector3(); + +const pipeSpline = new THREE.CatmullRomCurve3([ + new THREE.Vector3(0, 10, -10), + new THREE.Vector3(10, 0, -10), + new THREE.Vector3(20, 0, 0), + new THREE.Vector3(30, 0, 10), + new THREE.Vector3(30, 0, 20), + new THREE.Vector3(20, 0, 30), + new THREE.Vector3(10, 0, 30), + new THREE.Vector3(0, 0, 30), + new THREE.Vector3(-10, 10, 30), + new THREE.Vector3(-10, 20, 30), + new THREE.Vector3(0, 30, 30), + new THREE.Vector3(10, 30, 30), + new THREE.Vector3(20, 30, 15), + new THREE.Vector3(10, 30, 10), + new THREE.Vector3(0, 30, 10), + new THREE.Vector3(-10, 20, 10), + new THREE.Vector3(-10, 10, 10), + new THREE.Vector3(0, 0, 10), + new THREE.Vector3(10, -10, 10), + new THREE.Vector3(20, -15, 10), + new THREE.Vector3(30, -15, 10), + new THREE.Vector3(40, -15, 10), + new THREE.Vector3(50, -15, 10), + new THREE.Vector3(60, 0, 10), + new THREE.Vector3(70, 0, 0), + new THREE.Vector3(80, 0, 0), + new THREE.Vector3(90, 0, 0), + new THREE.Vector3(100, 0, 0), +]); + +const sampleClosedSpline = new THREE.CatmullRomCurve3([ + new THREE.Vector3(0, -40, -40), + new THREE.Vector3(0, 40, -40), + new THREE.Vector3(0, 140, -40), + new THREE.Vector3(0, 40, 40), + new THREE.Vector3(0, -40, 40), +]); + +sampleClosedSpline.curveType = 'catmullrom'; +sampleClosedSpline.closed = true; + +// Keep a dictionary of Curve instances +const splines = { + GrannyKnot: new Curves.GrannyKnot(), + HeartCurve: new Curves.HeartCurve(3.5), + VivianiCurve: new Curves.VivianiCurve(70), + KnotCurve: new Curves.KnotCurve(), + HelixCurve: new Curves.HelixCurve(), + TrefoilKnot: new Curves.TrefoilKnot(), + TorusKnot: new Curves.TorusKnot(20), + CinquefoilKnot: new Curves.CinquefoilKnot(20), + TrefoilPolynomialKnot: new Curves.TrefoilPolynomialKnot(14), + FigureEightPolynomialKnot: new Curves.FigureEightPolynomialKnot(), + DecoratedTorusKnot4a: new Curves.DecoratedTorusKnot4a(), + DecoratedTorusKnot4b: new Curves.DecoratedTorusKnot4b(), + DecoratedTorusKnot5a: new Curves.DecoratedTorusKnot5a(), + DecoratedTorusKnot5c: new Curves.DecoratedTorusKnot5c(), + PipeSpline: pipeSpline, + SampleClosedSpline: sampleClosedSpline, +}; + +let parent, tubeGeometry, mesh; + +const params = { + spline: 'GrannyKnot', + scale: 4, + extrusionSegments: 100, + radiusSegments: 3, + closed: true, + animationView: false, + lookAhead: false, + cameraHelper: false, +}; + +const material = new THREE.MeshLambertMaterial({ color: 0xff00ff }); + +const wireframeMaterial = new THREE.MeshBasicMaterial({ + color: 0x000000, + opacity: 0.3, + wireframe: true, + transparent: true, +}); + +function addTube() { + if (mesh !== undefined) { + parent.remove(mesh); + mesh.geometry.dispose(); + } + + const extrudePath = splines[params.spline]; + + tubeGeometry = new THREE.TubeGeometry( + extrudePath, + params.extrusionSegments, + 2, + params.radiusSegments, + params.closed, + ); + + addGeometry(tubeGeometry); + + setScale(); +} + +function setScale() { + mesh.scale.set(params.scale, params.scale, params.scale); +} + +function addGeometry(geometry) { + // 3D shape + + mesh = new THREE.Mesh(geometry, material); + const wireframe = new THREE.Mesh(geometry, wireframeMaterial); + mesh.add(wireframe); + + parent.add(mesh); +} + +function animateCamera() { + cameraHelper.visible = params.cameraHelper; + cameraEye.visible = params.cameraHelper; +} + +init(); + +function init() { + container = document.getElementById('container'); + + // camera + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.01, 10000); + camera.position.set(0, 50, 500); + + // scene + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xf0f0f0); + + // light + + scene.add(new THREE.AmbientLight(0xffffff)); + + const light = new THREE.DirectionalLight(0xffffff, 1.5); + light.position.set(0, 0, 1); + scene.add(light); + + // tube + + parent = new THREE.Object3D(); + scene.add(parent); + + splineCamera = new THREE.PerspectiveCamera(84, window.innerWidth / window.innerHeight, 0.01, 1000); + parent.add(splineCamera); + + cameraHelper = new THREE.CameraHelper(splineCamera); + scene.add(cameraHelper); + + addTube(); + + // debug camera + + cameraEye = new THREE.Mesh(new THREE.SphereGeometry(5), new THREE.MeshBasicMaterial({ color: 0xdddddd })); + parent.add(cameraEye); + + cameraHelper.visible = params.cameraHelper; + cameraEye.visible = params.cameraHelper; + + // renderer + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + // stats + + stats = new Stats(); + container.appendChild(stats.dom); + + // dat.GUI + + const gui = new GUI({ width: 285 }); + + const folderGeometry = gui.addFolder('Geometry'); + folderGeometry.add(params, 'spline', Object.keys(splines)).onChange(function () { + addTube(); + }); + folderGeometry + .add(params, 'scale', 2, 10) + .step(2) + .onChange(function () { + setScale(); + }); + folderGeometry + .add(params, 'extrusionSegments', 50, 500) + .step(50) + .onChange(function () { + addTube(); + }); + folderGeometry + .add(params, 'radiusSegments', 2, 12) + .step(1) + .onChange(function () { + addTube(); + }); + folderGeometry.add(params, 'closed').onChange(function () { + addTube(); + }); + folderGeometry.open(); + + const folderCamera = gui.addFolder('Camera'); + folderCamera.add(params, 'animationView').onChange(function () { + animateCamera(); + }); + folderCamera.add(params, 'lookAhead').onChange(function () { + animateCamera(); + }); + folderCamera.add(params, 'cameraHelper').onChange(function () { + animateCamera(); + }); + folderCamera.open(); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 100; + controls.maxDistance = 2000; + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + render(); + stats.update(); +} + +function render() { + // animate camera along spline + + const time = Date.now(); + const looptime = 20 * 1000; + const t = (time % looptime) / looptime; + + tubeGeometry.parameters.path.getPointAt(t, position); + position.multiplyScalar(params.scale); + + // interpolation + + const segments = tubeGeometry.tangents.length; + const pickt = t * segments; + const pick = Math.floor(pickt); + const pickNext = (pick + 1) % segments; + + binormal.subVectors(tubeGeometry.binormals[pickNext], tubeGeometry.binormals[pick]); + binormal.multiplyScalar(pickt - pick).add(tubeGeometry.binormals[pick]); + + tubeGeometry.parameters.path.getTangentAt(t, direction); + const offset = 15; + + normal.copy(binormal).cross(direction); + + // we move on a offset on its binormal + + position.add(normal.clone().multiplyScalar(offset)); + + splineCamera.position.copy(position); + cameraEye.position.copy(position); + + // using arclength for stabilization in look ahead + + tubeGeometry.parameters.path.getPointAt((t + 30 / tubeGeometry.parameters.path.getLength()) % 1, lookAt); + lookAt.multiplyScalar(params.scale); + + // camera orientation 2 - up orientation via normal + + if (!params.lookAhead) lookAt.copy(position).add(direction); + splineCamera.matrix.lookAt(splineCamera.position, lookAt, normal); + splineCamera.quaternion.setFromRotationMatrix(splineCamera.matrix); + + cameraHelper.update(); + + renderer.render(scene, params.animationView === true ? splineCamera : camera); +} diff --git a/examples-testing/examples/webgl_geometry_minecraft.ts b/examples-testing/examples/webgl_geometry_minecraft.ts new file mode 100644 index 000000000..220156552 --- /dev/null +++ b/examples-testing/examples/webgl_geometry_minecraft.ts @@ -0,0 +1,184 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { FirstPersonControls } from 'three/addons/controls/FirstPersonControls.js'; +import { ImprovedNoise } from 'three/addons/math/ImprovedNoise.js'; +import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js'; + +let container, stats; + +let camera, controls, scene, renderer; + +const worldWidth = 128, + worldDepth = 128; +const worldHalfWidth = worldWidth / 2; +const worldHalfDepth = worldDepth / 2; +const data = generateHeight(worldWidth, worldDepth); + +const timer = new THREE.Timer(); +timer.connect(document); + +init(); + +function init() { + container = document.getElementById('container'); + + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 20000); + camera.position.y = getY(worldHalfWidth, worldHalfDepth) * 100 + 100; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xbfd1e5); + + // sides + + const matrix = new THREE.Matrix4(); + + const pxGeometry = new THREE.PlaneGeometry(100, 100); + pxGeometry.attributes.uv.array[1] = 0.5; + pxGeometry.attributes.uv.array[3] = 0.5; + pxGeometry.rotateY(Math.PI / 2); + pxGeometry.translate(50, 0, 0); + + const nxGeometry = new THREE.PlaneGeometry(100, 100); + nxGeometry.attributes.uv.array[1] = 0.5; + nxGeometry.attributes.uv.array[3] = 0.5; + nxGeometry.rotateY(-Math.PI / 2); + nxGeometry.translate(-50, 0, 0); + + const pyGeometry = new THREE.PlaneGeometry(100, 100); + pyGeometry.attributes.uv.array[5] = 0.5; + pyGeometry.attributes.uv.array[7] = 0.5; + pyGeometry.rotateX(-Math.PI / 2); + pyGeometry.translate(0, 50, 0); + + const pzGeometry = new THREE.PlaneGeometry(100, 100); + pzGeometry.attributes.uv.array[1] = 0.5; + pzGeometry.attributes.uv.array[3] = 0.5; + pzGeometry.translate(0, 0, 50); + + const nzGeometry = new THREE.PlaneGeometry(100, 100); + nzGeometry.attributes.uv.array[1] = 0.5; + nzGeometry.attributes.uv.array[3] = 0.5; + nzGeometry.rotateY(Math.PI); + nzGeometry.translate(0, 0, -50); + + // + + const geometries = []; + + for (let z = 0; z < worldDepth; z++) { + for (let x = 0; x < worldWidth; x++) { + const h = getY(x, z); + + matrix.makeTranslation(x * 100 - worldHalfWidth * 100, h * 100, z * 100 - worldHalfDepth * 100); + + const px = getY(x + 1, z); + const nx = getY(x - 1, z); + const pz = getY(x, z + 1); + const nz = getY(x, z - 1); + + geometries.push(pyGeometry.clone().applyMatrix4(matrix)); + + if ((px !== h && px !== h + 1) || x === 0) { + geometries.push(pxGeometry.clone().applyMatrix4(matrix)); + } + + if ((nx !== h && nx !== h + 1) || x === worldWidth - 1) { + geometries.push(nxGeometry.clone().applyMatrix4(matrix)); + } + + if ((pz !== h && pz !== h + 1) || z === worldDepth - 1) { + geometries.push(pzGeometry.clone().applyMatrix4(matrix)); + } + + if ((nz !== h && nz !== h + 1) || z === 0) { + geometries.push(nzGeometry.clone().applyMatrix4(matrix)); + } + } + } + + const geometry = BufferGeometryUtils.mergeGeometries(geometries); + geometry.computeBoundingSphere(); + + const texture = new THREE.TextureLoader().load('textures/minecraft/atlas.png'); + texture.colorSpace = THREE.SRGBColorSpace; + texture.magFilter = THREE.NearestFilter; + + const mesh = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({ map: texture, side: THREE.DoubleSide })); + scene.add(mesh); + + const ambientLight = new THREE.AmbientLight(0xeeeeee, 3); + scene.add(ambientLight); + + const directionalLight = new THREE.DirectionalLight(0xffffff, 12); + directionalLight.position.set(1, 1, 0.5).normalize(); + scene.add(directionalLight); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + controls = new FirstPersonControls(camera, renderer.domElement); + + controls.movementSpeed = 1000; + controls.lookSpeed = 0.125; + controls.lookVertical = true; + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function generateHeight(width, height) { + const data = [], + perlin = new ImprovedNoise(), + size = width * height, + z = Math.random() * 100; + + let quality = 2; + + for (let j = 0; j < 4; j++) { + if (j === 0) for (let i = 0; i < size; i++) data[i] = 0; + + for (let i = 0; i < size; i++) { + const x = i % width, + y = (i / width) | 0; + data[i] += perlin.noise(x / quality, y / quality, z) * quality; + } + + quality *= 4; + } + + return data; +} + +function getY(x, z) { + return (data[x + z * worldWidth] * 0.15) | 0; +} + +// + +function animate() { + timer.update(); + + render(); + stats.update(); +} + +function render() { + controls.update(timer.getDelta()); + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_geometry_nurbs.ts b/examples-testing/examples/webgl_geometry_nurbs.ts new file mode 100644 index 000000000..ecd79c67e --- /dev/null +++ b/examples-testing/examples/webgl_geometry_nurbs.ts @@ -0,0 +1,298 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { NURBSCurve } from 'three/addons/curves/NURBSCurve.js'; +import { NURBSSurface } from 'three/addons/curves/NURBSSurface.js'; +import { NURBSVolume } from 'three/addons/curves/NURBSVolume.js'; +import { ParametricGeometry } from 'three/addons/geometries/ParametricGeometry.js'; + +let container, stats; + +let camera, scene, renderer; +let group; + +let targetRotation = 0; +let targetRotationOnPointerDown = 0; + +let pointerX = 0; +let pointerXOnPointerDown = 0; + +let windowHalfX = window.innerWidth / 2; + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 2000); + camera.position.set(0, 150, 750); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xf0f0f0); + + scene.add(new THREE.AmbientLight(0xffffff)); + + const light = new THREE.DirectionalLight(0xffffff, 3); + light.position.set(1, 1, 1); + scene.add(light); + + group = new THREE.Group(); + group.position.y = 50; + scene.add(group); + + // NURBS curve + + const nurbsControlPoints = []; + const nurbsKnots = []; + const nurbsDegree = 3; + + for (let i = 0; i <= nurbsDegree; i++) { + nurbsKnots.push(0); + } + + for (let i = 0, j = 20; i < j; i++) { + nurbsControlPoints.push( + new THREE.Vector4( + Math.random() * 400 - 200, + Math.random() * 400, + Math.random() * 400 - 200, + 1, // weight of control point: higher means stronger attraction + ), + ); + + const knot = (i + 1) / (j - nurbsDegree); + nurbsKnots.push(THREE.MathUtils.clamp(knot, 0, 1)); + } + + const nurbsCurve = new NURBSCurve(nurbsDegree, nurbsKnots, nurbsControlPoints); + + const nurbsGeometry = new THREE.BufferGeometry(); + nurbsGeometry.setFromPoints(nurbsCurve.getPoints(200)); + + const nurbsMaterial = new THREE.LineBasicMaterial({ color: 0x333333 }); + + const nurbsLine = new THREE.Line(nurbsGeometry, nurbsMaterial); + nurbsLine.position.set(0, -100, 0); + group.add(nurbsLine); + + const nurbsControlPointsGeometry = new THREE.BufferGeometry(); + nurbsControlPointsGeometry.setFromPoints(nurbsCurve.controlPoints); + + const nurbsControlPointsMaterial = new THREE.LineBasicMaterial({ + color: 0x333333, + opacity: 0.25, + transparent: true, + }); + + const nurbsControlPointsLine = new THREE.Line(nurbsControlPointsGeometry, nurbsControlPointsMaterial); + nurbsControlPointsLine.position.copy(nurbsLine.position); + group.add(nurbsControlPointsLine); + + // NURBS surface + { + const nsControlPoints = [ + [ + new THREE.Vector4(-200, -200, 100, 1), + new THREE.Vector4(-200, -100, -200, 1), + new THREE.Vector4(-200, 100, 250, 1), + new THREE.Vector4(-200, 200, -100, 1), + ], + [ + new THREE.Vector4(0, -200, 0, 1), + new THREE.Vector4(0, -100, -100, 5), + new THREE.Vector4(0, 100, 150, 5), + new THREE.Vector4(0, 200, 0, 1), + ], + [ + new THREE.Vector4(200, -200, -100, 1), + new THREE.Vector4(200, -100, 200, 1), + new THREE.Vector4(200, 100, -250, 1), + new THREE.Vector4(200, 200, 100, 1), + ], + ]; + const degree1 = 2; + const degree2 = 3; + const knots1 = [0, 0, 0, 1, 1, 1]; + const knots2 = [0, 0, 0, 0, 1, 1, 1, 1]; + const nurbsSurface = new NURBSSurface(degree1, degree2, knots1, knots2, nsControlPoints); + + const map = new THREE.TextureLoader().load('textures/uv_grid_opengl.jpg'); + map.wrapS = map.wrapT = THREE.RepeatWrapping; + map.anisotropy = 16; + map.colorSpace = THREE.SRGBColorSpace; + + function getSurfacePoint(u, v, target) { + return nurbsSurface.getPoint(u, v, target); + } + + const geometry = new ParametricGeometry(getSurfacePoint, 20, 20); + const material = new THREE.MeshLambertMaterial({ map: map, side: THREE.DoubleSide }); + const object = new THREE.Mesh(geometry, material); + object.position.set(-400, 100, 0); + object.scale.multiplyScalar(1); + group.add(object); + } + + // NURBS volume + { + const nsControlPoints = [ + [ + [new THREE.Vector4(-200, -200, -200, 1), new THREE.Vector4(-200, -200, 200, 1)], + [new THREE.Vector4(-200, -100, -200, 1), new THREE.Vector4(-200, -100, 200, 1)], + [new THREE.Vector4(-200, 100, -200, 1), new THREE.Vector4(-200, 100, 200, 1)], + [new THREE.Vector4(-200, 200, -200, 1), new THREE.Vector4(-200, 200, 200, 1)], + ], + [ + [new THREE.Vector4(0, -200, -200, 1), new THREE.Vector4(0, -200, 200, 1)], + [new THREE.Vector4(0, -100, -200, 1), new THREE.Vector4(0, -100, 200, 1)], + [new THREE.Vector4(0, 100, -200, 1), new THREE.Vector4(0, 100, 200, 1)], + [new THREE.Vector4(0, 200, -200, 1), new THREE.Vector4(0, 200, 200, 1)], + ], + [ + [new THREE.Vector4(200, -200, -200, 1), new THREE.Vector4(200, -200, 200, 1)], + [new THREE.Vector4(200, -100, 0, 1), new THREE.Vector4(200, -100, 100, 1)], + [new THREE.Vector4(200, 100, 0, 1), new THREE.Vector4(200, 100, 100, 1)], + [new THREE.Vector4(200, 200, 0, 1), new THREE.Vector4(200, 200, 100, 1)], + ], + ]; + const degree1 = 2; + const degree2 = 3; + const degree3 = 1; + const knots1 = [0, 0, 0, 1, 1, 1]; + const knots2 = [0, 0, 0, 0, 1, 1, 1, 1]; + const knots3 = [0, 0, 1, 1]; + const nurbsVolume = new NURBSVolume(degree1, degree2, degree3, knots1, knots2, knots3, nsControlPoints); + + const map = new THREE.TextureLoader().load('textures/uv_grid_opengl.jpg'); + map.wrapS = map.wrapT = THREE.RepeatWrapping; + map.anisotropy = 16; + map.colorSpace = THREE.SRGBColorSpace; + + // Since ParametricGeometry() only support bi-variate parametric geometries + // we create evaluation functions for different surfaces with one of the three + // parameter values (u, v, w) kept constant and create multiple THREE.Mesh + // objects one for each surface + function getSurfacePointFront(u, v, target) { + return nurbsVolume.getPoint(u, v, 0, target); + } + + function getSurfacePointMiddle(u, v, target) { + return nurbsVolume.getPoint(u, v, 0.5, target); + } + + function getSurfacePointBack(u, v, target) { + return nurbsVolume.getPoint(u, v, 1, target); + } + + function getSurfacePointTop(u, w, target) { + return nurbsVolume.getPoint(u, 1, w, target); + } + + function getSurfacePointSide(v, w, target) { + return nurbsVolume.getPoint(0, v, w, target); + } + + const geometryFront = new ParametricGeometry(getSurfacePointFront, 20, 20); + const materialFront = new THREE.MeshLambertMaterial({ map: map, side: THREE.DoubleSide }); + const objectFront = new THREE.Mesh(geometryFront, materialFront); + objectFront.position.set(400, 100, 0); + objectFront.scale.multiplyScalar(0.5); + group.add(objectFront); + + const geometryMiddle = new ParametricGeometry(getSurfacePointMiddle, 20, 20); + const materialMiddle = new THREE.MeshLambertMaterial({ map: map, side: THREE.DoubleSide }); + const objectMiddle = new THREE.Mesh(geometryMiddle, materialMiddle); + objectMiddle.position.set(400, 100, 0); + objectMiddle.scale.multiplyScalar(0.5); + group.add(objectMiddle); + + const geometryBack = new ParametricGeometry(getSurfacePointBack, 20, 20); + const materialBack = new THREE.MeshLambertMaterial({ map: map, side: THREE.DoubleSide }); + const objectBack = new THREE.Mesh(geometryBack, materialBack); + objectBack.position.set(400, 100, 0); + objectBack.scale.multiplyScalar(0.5); + group.add(objectBack); + + const geometryTop = new ParametricGeometry(getSurfacePointTop, 20, 20); + const materialTop = new THREE.MeshLambertMaterial({ map: map, side: THREE.DoubleSide }); + const objectTop = new THREE.Mesh(geometryTop, materialTop); + objectTop.position.set(400, 100, 0); + objectTop.scale.multiplyScalar(0.5); + group.add(objectTop); + + const geometrySide = new ParametricGeometry(getSurfacePointSide, 20, 20); + const materialSide = new THREE.MeshLambertMaterial({ map: map, side: THREE.DoubleSide }); + const objectSide = new THREE.Mesh(geometrySide, materialSide); + objectSide.position.set(400, 100, 0); + objectSide.scale.multiplyScalar(0.5); + group.add(objectSide); + } + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + stats = new Stats(); + container.appendChild(stats.dom); + + container.style.touchAction = 'none'; + container.addEventListener('pointerdown', onPointerDown); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + windowHalfX = window.innerWidth / 2; + + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function onPointerDown(event) { + if (event.isPrimary === false) return; + + pointerXOnPointerDown = event.clientX - windowHalfX; + targetRotationOnPointerDown = targetRotation; + + document.addEventListener('pointermove', onPointerMove); + document.addEventListener('pointerup', onPointerUp); +} + +function onPointerMove(event) { + if (event.isPrimary === false) return; + + pointerX = event.clientX - windowHalfX; + + targetRotation = targetRotationOnPointerDown + (pointerX - pointerXOnPointerDown) * 0.02; +} + +function onPointerUp(event) { + if (event.isPrimary === false) return; + + document.removeEventListener('pointermove', onPointerMove); + document.removeEventListener('pointerup', onPointerUp); +} + +// + +function animate() { + render(); + stats.update(); +} + +function render() { + group.rotation.y += (targetRotation - group.rotation.y) * 0.05; + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_geometry_shapes.ts b/examples-testing/examples/webgl_geometry_shapes.ts new file mode 100644 index 000000000..0c25d8855 --- /dev/null +++ b/examples-testing/examples/webgl_geometry_shapes.ts @@ -0,0 +1,363 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +let container, stats; + +let camera, scene, renderer; + +let group; + +let targetRotation = 0; +let targetRotationOnPointerDown = 0; + +let pointerX = 0; +let pointerXOnPointerDown = 0; + +let windowHalfX = window.innerWidth / 2; + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xf0f0f0); + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(0, 150, 500); + scene.add(camera); + + const light = new THREE.PointLight(0xffffff, 2.5, 0, 0); + camera.add(light); + + group = new THREE.Group(); + group.position.y = 50; + scene.add(group); + + const loader = new THREE.TextureLoader(); + const texture = loader.load('textures/uv_grid_opengl.jpg'); + texture.colorSpace = THREE.SRGBColorSpace; + + // it's necessary to apply these settings in order to correctly display the texture on a shape geometry + + texture.wrapS = texture.wrapT = THREE.RepeatWrapping; + texture.repeat.set(0.008, 0.008); + + function addShape(shape, extrudeSettings, color, x, y, z, rx, ry, rz, s) { + // flat shape with texture + // note: default UVs generated by THREE.ShapeGeometry are simply the x- and y-coordinates of the vertices + + let geometry = new THREE.ShapeGeometry(shape); + + let mesh = new THREE.Mesh(geometry, new THREE.MeshPhongMaterial({ side: THREE.DoubleSide, map: texture })); + mesh.position.set(x, y, z - 175); + mesh.rotation.set(rx, ry, rz); + mesh.scale.set(s, s, s); + group.add(mesh); + + // flat shape + + geometry = new THREE.ShapeGeometry(shape); + + mesh = new THREE.Mesh(geometry, new THREE.MeshPhongMaterial({ color: color, side: THREE.DoubleSide })); + mesh.position.set(x, y, z - 125); + mesh.rotation.set(rx, ry, rz); + mesh.scale.set(s, s, s); + group.add(mesh); + + // extruded shape + + geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings); + + mesh = new THREE.Mesh(geometry, new THREE.MeshPhongMaterial({ color: color })); + mesh.position.set(x, y, z - 75); + mesh.rotation.set(rx, ry, rz); + mesh.scale.set(s, s, s); + group.add(mesh); + + addLineShape(shape, color, x, y, z, rx, ry, rz, s); + } + + function addLineShape(shape, color, x, y, z, rx, ry, rz, s) { + // lines + + shape.autoClose = true; + + const points = shape.getPoints(); + const spacedPoints = shape.getSpacedPoints(50); + + const geometryPoints = new THREE.BufferGeometry().setFromPoints(points); + const geometrySpacedPoints = new THREE.BufferGeometry().setFromPoints(spacedPoints); + + // solid line + + let line = new THREE.Line(geometryPoints, new THREE.LineBasicMaterial({ color: color })); + line.position.set(x, y, z - 25); + line.rotation.set(rx, ry, rz); + line.scale.set(s, s, s); + group.add(line); + + // line from equidistance sampled points + + line = new THREE.Line(geometrySpacedPoints, new THREE.LineBasicMaterial({ color: color })); + line.position.set(x, y, z + 25); + line.rotation.set(rx, ry, rz); + line.scale.set(s, s, s); + group.add(line); + + // vertices from real points + + let particles = new THREE.Points(geometryPoints, new THREE.PointsMaterial({ color: color, size: 4 })); + particles.position.set(x, y, z + 75); + particles.rotation.set(rx, ry, rz); + particles.scale.set(s, s, s); + group.add(particles); + + // equidistance sampled points + + particles = new THREE.Points(geometrySpacedPoints, new THREE.PointsMaterial({ color: color, size: 4 })); + particles.position.set(x, y, z + 125); + particles.rotation.set(rx, ry, rz); + particles.scale.set(s, s, s); + group.add(particles); + } + + // California + + const californiaPts = []; + + californiaPts.push(new THREE.Vector2(610, 320)); + californiaPts.push(new THREE.Vector2(450, 300)); + californiaPts.push(new THREE.Vector2(392, 392)); + californiaPts.push(new THREE.Vector2(266, 438)); + californiaPts.push(new THREE.Vector2(190, 570)); + californiaPts.push(new THREE.Vector2(190, 600)); + californiaPts.push(new THREE.Vector2(160, 620)); + californiaPts.push(new THREE.Vector2(160, 650)); + californiaPts.push(new THREE.Vector2(180, 640)); + californiaPts.push(new THREE.Vector2(165, 680)); + californiaPts.push(new THREE.Vector2(150, 670)); + californiaPts.push(new THREE.Vector2(90, 737)); + californiaPts.push(new THREE.Vector2(80, 795)); + californiaPts.push(new THREE.Vector2(50, 835)); + californiaPts.push(new THREE.Vector2(64, 870)); + californiaPts.push(new THREE.Vector2(60, 945)); + californiaPts.push(new THREE.Vector2(300, 945)); + californiaPts.push(new THREE.Vector2(300, 743)); + californiaPts.push(new THREE.Vector2(600, 473)); + californiaPts.push(new THREE.Vector2(626, 425)); + californiaPts.push(new THREE.Vector2(600, 370)); + californiaPts.push(new THREE.Vector2(610, 320)); + + for (let i = 0; i < californiaPts.length; i++) californiaPts[i].multiplyScalar(0.25); + + const californiaShape = new THREE.Shape(californiaPts); + + // Triangle + + const triangleShape = new THREE.Shape().moveTo(80, 20).lineTo(40, 80).lineTo(120, 80).lineTo(80, 20); // close path + + // Heart + + const x = 0, + y = 0; + + const heartShape = new THREE.Shape() + .moveTo(x + 25, y + 25) + .bezierCurveTo(x + 25, y + 25, x + 20, y, x, y) + .bezierCurveTo(x - 30, y, x - 30, y + 35, x - 30, y + 35) + .bezierCurveTo(x - 30, y + 55, x - 10, y + 77, x + 25, y + 95) + .bezierCurveTo(x + 60, y + 77, x + 80, y + 55, x + 80, y + 35) + .bezierCurveTo(x + 80, y + 35, x + 80, y, x + 50, y) + .bezierCurveTo(x + 35, y, x + 25, y + 25, x + 25, y + 25); + + // Square + + const sqLength = 80; + + const squareShape = new THREE.Shape() + .moveTo(0, 0) + .lineTo(0, sqLength) + .lineTo(sqLength, sqLength) + .lineTo(sqLength, 0) + .lineTo(0, 0); + + // Rounded rectangle + + const roundedRectShape = new THREE.Shape(); + + (function roundedRect(ctx, x, y, width, height, radius) { + ctx.moveTo(x, y + radius); + ctx.lineTo(x, y + height - radius); + ctx.quadraticCurveTo(x, y + height, x + radius, y + height); + ctx.lineTo(x + width - radius, y + height); + ctx.quadraticCurveTo(x + width, y + height, x + width, y + height - radius); + ctx.lineTo(x + width, y + radius); + ctx.quadraticCurveTo(x + width, y, x + width - radius, y); + ctx.lineTo(x + radius, y); + ctx.quadraticCurveTo(x, y, x, y + radius); + })(roundedRectShape, 0, 0, 50, 50, 20); + + // Track + + const trackShape = new THREE.Shape() + .moveTo(40, 40) + .lineTo(40, 160) + .absarc(60, 160, 20, Math.PI, 0, true) + .lineTo(80, 40) + .absarc(60, 40, 20, 2 * Math.PI, Math.PI, true); + + // Circle + + const circleRadius = 40; + const circleShape = new THREE.Shape() + .moveTo(0, circleRadius) + .quadraticCurveTo(circleRadius, circleRadius, circleRadius, 0) + .quadraticCurveTo(circleRadius, -circleRadius, 0, -circleRadius) + .quadraticCurveTo(-circleRadius, -circleRadius, -circleRadius, 0) + .quadraticCurveTo(-circleRadius, circleRadius, 0, circleRadius); + + // Fish + + const fishShape = new THREE.Shape() + .moveTo(x, y) + .quadraticCurveTo(x + 50, y - 80, x + 90, y - 10) + .quadraticCurveTo(x + 100, y - 10, x + 115, y - 40) + .quadraticCurveTo(x + 115, y, x + 115, y + 40) + .quadraticCurveTo(x + 100, y + 10, x + 90, y + 10) + .quadraticCurveTo(x + 50, y + 80, x, y); + + // Arc circle + + const arcShape = new THREE.Shape().moveTo(50, 10).absarc(10, 10, 40, 0, Math.PI * 2, false); + + const holePath = new THREE.Path().moveTo(20, 10).absarc(10, 10, 10, 0, Math.PI * 2, true); + + arcShape.holes.push(holePath); + + // Smiley + + const smileyShape = new THREE.Shape().moveTo(80, 40).absarc(40, 40, 40, 0, Math.PI * 2, false); + + const smileyEye1Path = new THREE.Path().moveTo(35, 20).absellipse(25, 20, 10, 10, 0, Math.PI * 2, true); + + const smileyEye2Path = new THREE.Path().moveTo(65, 20).absarc(55, 20, 10, 0, Math.PI * 2, true); + + const smileyMouthPath = new THREE.Path() + .moveTo(20, 40) + .quadraticCurveTo(40, 60, 60, 40) + .bezierCurveTo(70, 45, 70, 50, 60, 60) + .quadraticCurveTo(40, 80, 20, 60) + .quadraticCurveTo(5, 50, 20, 40); + + smileyShape.holes.push(smileyEye1Path); + smileyShape.holes.push(smileyEye2Path); + smileyShape.holes.push(smileyMouthPath); + + // Spline shape + + const splinepts = []; + splinepts.push(new THREE.Vector2(70, 20)); + splinepts.push(new THREE.Vector2(80, 90)); + splinepts.push(new THREE.Vector2(-30, 70)); + splinepts.push(new THREE.Vector2(0, 0)); + + const splineShape = new THREE.Shape().moveTo(0, 0).splineThru(splinepts); + + const extrudeSettings = { + depth: 8, + bevelEnabled: true, + bevelSegments: 2, + steps: 2, + bevelSize: 1, + bevelThickness: 1, + }; + + // addShape( shape, color, x, y, z, rx, ry,rz, s ); + + addShape(californiaShape, extrudeSettings, 0xf08000, -300, -100, 0, 0, 0, 0, 1); + addShape(triangleShape, extrudeSettings, 0x8080f0, -180, 0, 0, 0, 0, 0, 1); + addShape(roundedRectShape, extrudeSettings, 0x008000, -150, 150, 0, 0, 0, 0, 1); + addShape(trackShape, extrudeSettings, 0x008080, 200, -100, 0, 0, 0, 0, 1); + addShape(squareShape, extrudeSettings, 0x0040f0, 150, 100, 0, 0, 0, 0, 1); + addShape(heartShape, extrudeSettings, 0xf00000, 60, 100, 0, 0, 0, Math.PI, 1); + addShape(circleShape, extrudeSettings, 0x00f000, 120, 250, 0, 0, 0, 0, 1); + addShape(fishShape, extrudeSettings, 0x404040, -60, 200, 0, 0, 0, 0, 1); + addShape(smileyShape, extrudeSettings, 0xf000f0, -200, 250, 0, 0, 0, Math.PI, 1); + addShape(arcShape, extrudeSettings, 0x804000, 150, 0, 0, 0, 0, 0, 1); + addShape(splineShape, extrudeSettings, 0x808080, -50, -100, 0, 0, 0, 0, 1); + + addLineShape(arcShape.holes[0], 0x804000, 150, 0, 0, 0, 0, 0, 1); + + for (let i = 0; i < smileyShape.holes.length; i += 1) { + addLineShape(smileyShape.holes[i], 0xf000f0, -200, 250, 0, 0, 0, Math.PI, 1); + } + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + stats = new Stats(); + container.appendChild(stats.dom); + + container.style.touchAction = 'none'; + container.addEventListener('pointerdown', onPointerDown); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + windowHalfX = window.innerWidth / 2; + + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function onPointerDown(event) { + if (event.isPrimary === false) return; + + pointerXOnPointerDown = event.clientX - windowHalfX; + targetRotationOnPointerDown = targetRotation; + + document.addEventListener('pointermove', onPointerMove); + document.addEventListener('pointerup', onPointerUp); +} + +function onPointerMove(event) { + if (event.isPrimary === false) return; + + pointerX = event.clientX - windowHalfX; + + targetRotation = targetRotationOnPointerDown + (pointerX - pointerXOnPointerDown) * 0.02; +} + +function onPointerUp(event) { + if (event.isPrimary === false) return; + + document.removeEventListener('pointermove', onPointerMove); + document.removeEventListener('pointerup', onPointerUp); +} + +// + +function animate() { + render(); + stats.update(); +} + +function render() { + group.rotation.y += (targetRotation - group.rotation.y) * 0.05; + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_geometry_teapot.ts b/examples-testing/examples/webgl_geometry_teapot.ts new file mode 100644 index 000000000..2516d660c --- /dev/null +++ b/examples-testing/examples/webgl_geometry_teapot.ts @@ -0,0 +1,185 @@ +import * as THREE from 'three'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { TeapotGeometry } from 'three/addons/geometries/TeapotGeometry.js'; + +let camera, scene, renderer; +let cameraControls; +let effectController; +const teapotSize = 300; +let ambientLight, light; + +let tess = -1; // force initialization +let bBottom; +let bLid; +let bBody; +let bFitLid; +let bNonBlinn; +let shading; + +let teapot, textureCube; +const materials = {}; + +init(); +render(); + +function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + const canvasWidth = window.innerWidth; + const canvasHeight = window.innerHeight; + + // CAMERA + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 80000); + camera.position.set(-600, 550, 1300); + + // LIGHTS + ambientLight = new THREE.AmbientLight(0x7c7c7c, 2.0); + + light = new THREE.DirectionalLight(0xffffff, 2.0); + light.position.set(0.32, 0.39, 0.7); + + // RENDERER + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(canvasWidth, canvasHeight); + container.appendChild(renderer.domElement); + + // EVENTS + window.addEventListener('resize', onWindowResize); + + // CONTROLS + cameraControls = new OrbitControls(camera, renderer.domElement); + cameraControls.addEventListener('change', render); + + // TEXTURE MAP + const textureMap = new THREE.TextureLoader().load('textures/uv_grid_opengl.jpg'); + textureMap.wrapS = textureMap.wrapT = THREE.RepeatWrapping; + textureMap.anisotropy = 16; + textureMap.colorSpace = THREE.SRGBColorSpace; + + // REFLECTION MAP + const path = 'textures/cube/pisa/'; + const urls = ['px.png', 'nx.png', 'py.png', 'ny.png', 'pz.png', 'nz.png']; + + textureCube = new THREE.CubeTextureLoader().setPath(path).load(urls); + + materials['wireframe'] = new THREE.MeshBasicMaterial({ wireframe: true }); + materials['flat'] = new THREE.MeshPhongMaterial({ specular: 0x000000, flatShading: true, side: THREE.DoubleSide }); + materials['smooth'] = new THREE.MeshLambertMaterial({ side: THREE.DoubleSide }); + materials['glossy'] = new THREE.MeshPhongMaterial({ + color: 0xc0c0c0, + specular: 0x404040, + shininess: 300, + side: THREE.DoubleSide, + }); + materials['textured'] = new THREE.MeshPhongMaterial({ map: textureMap, side: THREE.DoubleSide }); + materials['reflective'] = new THREE.MeshPhongMaterial({ envMap: textureCube, side: THREE.DoubleSide }); + + // scene itself + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xaaaaaa); + + scene.add(ambientLight); + scene.add(light); + + // GUI + setupGui(); +} + +// EVENT HANDLERS + +function onWindowResize() { + const canvasWidth = window.innerWidth; + const canvasHeight = window.innerHeight; + + renderer.setSize(canvasWidth, canvasHeight); + + camera.aspect = canvasWidth / canvasHeight; + camera.updateProjectionMatrix(); + + render(); +} + +function setupGui() { + effectController = { + newTess: 15, + bottom: true, + lid: true, + body: true, + fitLid: false, + nonblinn: false, + newShading: 'glossy', + }; + + const gui = new GUI(); + gui.add(effectController, 'newTess', [2, 3, 4, 5, 6, 8, 10, 15, 20, 30, 40, 50]) + .name('Tessellation Level') + .onChange(render); + gui.add(effectController, 'lid').name('display lid').onChange(render); + gui.add(effectController, 'body').name('display body').onChange(render); + gui.add(effectController, 'bottom').name('display bottom').onChange(render); + gui.add(effectController, 'fitLid').name('snug lid').onChange(render); + gui.add(effectController, 'nonblinn').name('original scale').onChange(render); + gui.add(effectController, 'newShading', ['wireframe', 'flat', 'smooth', 'glossy', 'textured', 'reflective']) + .name('Shading') + .onChange(render); +} + +// + +function render() { + if ( + effectController.newTess !== tess || + effectController.bottom !== bBottom || + effectController.lid !== bLid || + effectController.body !== bBody || + effectController.fitLid !== bFitLid || + effectController.nonblinn !== bNonBlinn || + effectController.newShading !== shading + ) { + tess = effectController.newTess; + bBottom = effectController.bottom; + bLid = effectController.lid; + bBody = effectController.body; + bFitLid = effectController.fitLid; + bNonBlinn = effectController.nonblinn; + shading = effectController.newShading; + + createNewTeapot(); + } + + // skybox is rendered separately, so that it is always behind the teapot. + if (shading === 'reflective') { + scene.background = textureCube; + } else { + scene.background = null; + } + + renderer.render(scene, camera); +} + +// Whenever the teapot changes, the scene is rebuilt from scratch (not much to it). +function createNewTeapot() { + if (teapot !== undefined) { + teapot.geometry.dispose(); + scene.remove(teapot); + } + + const geometry = new TeapotGeometry( + teapotSize, + tess, + effectController.bottom, + effectController.lid, + effectController.body, + effectController.fitLid, + !effectController.nonblinn, + ); + + teapot = new THREE.Mesh(geometry, materials[shading]); + + scene.add(teapot); +} diff --git a/examples-testing/examples/webgl_geometry_terrain.ts b/examples-testing/examples/webgl_geometry_terrain.ts new file mode 100644 index 000000000..55b4aa474 --- /dev/null +++ b/examples-testing/examples/webgl_geometry_terrain.ts @@ -0,0 +1,174 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { FirstPersonControls } from 'three/addons/controls/FirstPersonControls.js'; +import { ImprovedNoise } from 'three/addons/math/ImprovedNoise.js'; + +let container, stats; +let camera, controls, scene, renderer; +let mesh, texture; + +const worldWidth = 256, + worldDepth = 256; +const timer = new THREE.Timer(); +timer.connect(document); + +init(); + +function init() { + container = document.getElementById('container'); + + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 10000); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xefd1b5); + scene.fog = new THREE.FogExp2(0xefd1b5, 0.0025); + + const data = generateHeight(worldWidth, worldDepth); + + camera.position.set(100, 800, -800); + camera.lookAt(-100, 810, -800); + + const geometry = new THREE.PlaneGeometry(7500, 7500, worldWidth - 1, worldDepth - 1); + geometry.rotateX(-Math.PI / 2); + + const vertices = geometry.attributes.position.array; + + for (let i = 0, j = 0, l = vertices.length; i < l; i++, j += 3) { + vertices[j + 1] = data[i] * 10; + } + + texture = new THREE.CanvasTexture(generateTexture(data, worldWidth, worldDepth)); + texture.wrapS = THREE.ClampToEdgeWrapping; + texture.wrapT = THREE.ClampToEdgeWrapping; + texture.colorSpace = THREE.SRGBColorSpace; + + mesh = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ map: texture })); + scene.add(mesh); + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + controls = new FirstPersonControls(camera, renderer.domElement); + controls.movementSpeed = 150; + controls.lookSpeed = 0.1; + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function generateHeight(width, height) { + let seed = Math.PI / 4; + window.Math.random = function () { + const x = Math.sin(seed++) * 10000; + return x - Math.floor(x); + }; + + const size = width * height, + data = new Uint8Array(size); + const perlin = new ImprovedNoise(), + z = Math.random() * 100; + + let quality = 1; + + for (let j = 0; j < 4; j++) { + for (let i = 0; i < size; i++) { + const x = i % width, + y = ~~(i / width); + data[i] += Math.abs(perlin.noise(x / quality, y / quality, z) * quality * 1.75); + } + + quality *= 5; + } + + return data; +} + +function generateTexture(data, width, height) { + let context, image, imageData, shade; + + const vector3 = new THREE.Vector3(0, 0, 0); + + const sun = new THREE.Vector3(1, 1, 1); + sun.normalize(); + + const canvas = document.createElement('canvas'); + canvas.width = width; + canvas.height = height; + + context = canvas.getContext('2d'); + context.fillStyle = '#000'; + context.fillRect(0, 0, width, height); + + image = context.getImageData(0, 0, canvas.width, canvas.height); + imageData = image.data; + + for (let i = 0, j = 0, l = imageData.length; i < l; i += 4, j++) { + vector3.x = data[j - 2] - data[j + 2]; + vector3.y = 2; + vector3.z = data[j - width * 2] - data[j + width * 2]; + vector3.normalize(); + + shade = vector3.dot(sun); + + imageData[i] = (96 + shade * 128) * (0.5 + data[j] * 0.007); + imageData[i + 1] = (32 + shade * 96) * (0.5 + data[j] * 0.007); + imageData[i + 2] = shade * 96 * (0.5 + data[j] * 0.007); + } + + context.putImageData(image, 0, 0); + + // Scaled 4x + + const canvasScaled = document.createElement('canvas'); + canvasScaled.width = width * 4; + canvasScaled.height = height * 4; + + context = canvasScaled.getContext('2d'); + context.scale(4, 4); + context.drawImage(canvas, 0, 0); + + image = context.getImageData(0, 0, canvasScaled.width, canvasScaled.height); + imageData = image.data; + + for (let i = 0, l = imageData.length; i < l; i += 4) { + const v = ~~(Math.random() * 5); + + imageData[i] += v; + imageData[i + 1] += v; + imageData[i + 2] += v; + } + + context.putImageData(image, 0, 0); + + return canvasScaled; +} + +// + +function animate() { + timer.update(); + + render(); + stats.update(); +} + +function render() { + controls.update(timer.getDelta()); + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_geometry_terrain_raycast.ts b/examples-testing/examples/webgl_geometry_terrain_raycast.ts new file mode 100644 index 000000000..f1383c138 --- /dev/null +++ b/examples-testing/examples/webgl_geometry_terrain_raycast.ts @@ -0,0 +1,206 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { ImprovedNoise } from 'three/addons/math/ImprovedNoise.js'; + +let container, stats; + +let camera, controls, scene, renderer; + +let mesh, texture; + +const worldWidth = 256, + worldDepth = 256, + worldHalfWidth = worldWidth / 2, + worldHalfDepth = worldDepth / 2; + +let helper; + +const raycaster = new THREE.Raycaster(); +const pointer = new THREE.Vector2(); + +init(); + +function init() { + container = document.getElementById('container'); + container.innerHTML = ''; + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xbfd1e5); + + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 10, 20000); + + controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 1000; + controls.maxDistance = 10000; + controls.maxPolarAngle = Math.PI / 2; + + // + + const data = generateHeight(worldWidth, worldDepth); + + controls.target.y = data[worldHalfWidth + worldHalfDepth * worldWidth] + 500; + camera.position.y = controls.target.y + 2000; + camera.position.x = 2000; + controls.update(); + + const geometry = new THREE.PlaneGeometry(7500, 7500, worldWidth - 1, worldDepth - 1); + geometry.rotateX(-Math.PI / 2); + + const vertices = geometry.attributes.position.array; + + for (let i = 0, j = 0, l = vertices.length; i < l; i++, j += 3) { + vertices[j + 1] = data[i] * 10; + } + + // + + texture = new THREE.CanvasTexture(generateTexture(data, worldWidth, worldDepth)); + texture.wrapS = THREE.ClampToEdgeWrapping; + texture.wrapT = THREE.ClampToEdgeWrapping; + texture.colorSpace = THREE.SRGBColorSpace; + + mesh = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ map: texture })); + scene.add(mesh); + + const geometryHelper = new THREE.ConeGeometry(20, 100, 3); + geometryHelper.translate(0, 50, 0); + geometryHelper.rotateX(Math.PI / 2); + helper = new THREE.Mesh(geometryHelper, new THREE.MeshNormalMaterial()); + scene.add(helper); + + container.addEventListener('pointermove', onPointerMove); + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function generateHeight(width, height) { + const size = width * height, + data = new Uint8Array(size), + perlin = new ImprovedNoise(), + z = Math.random() * 100; + + let quality = 1; + + for (let j = 0; j < 4; j++) { + for (let i = 0; i < size; i++) { + const x = i % width, + y = ~~(i / width); + data[i] += Math.abs(perlin.noise(x / quality, y / quality, z) * quality * 1.75); + } + + quality *= 5; + } + + return data; +} + +function generateTexture(data, width, height) { + // bake lighting into texture + + let context, image, imageData, shade; + + const vector3 = new THREE.Vector3(0, 0, 0); + + const sun = new THREE.Vector3(1, 1, 1); + sun.normalize(); + + const canvas = document.createElement('canvas'); + canvas.width = width; + canvas.height = height; + + context = canvas.getContext('2d'); + context.fillStyle = '#000'; + context.fillRect(0, 0, width, height); + + image = context.getImageData(0, 0, canvas.width, canvas.height); + imageData = image.data; + + for (let i = 0, j = 0, l = imageData.length; i < l; i += 4, j++) { + vector3.x = data[j - 2] - data[j + 2]; + vector3.y = 2; + vector3.z = data[j - width * 2] - data[j + width * 2]; + vector3.normalize(); + + shade = vector3.dot(sun); + + imageData[i] = (96 + shade * 128) * (0.5 + data[j] * 0.007); + imageData[i + 1] = (32 + shade * 96) * (0.5 + data[j] * 0.007); + imageData[i + 2] = shade * 96 * (0.5 + data[j] * 0.007); + } + + context.putImageData(image, 0, 0); + + // Scaled 4x + + const canvasScaled = document.createElement('canvas'); + canvasScaled.width = width * 4; + canvasScaled.height = height * 4; + + context = canvasScaled.getContext('2d'); + context.scale(4, 4); + context.drawImage(canvas, 0, 0); + + image = context.getImageData(0, 0, canvasScaled.width, canvasScaled.height); + imageData = image.data; + + for (let i = 0, l = imageData.length; i < l; i += 4) { + const v = ~~(Math.random() * 5); + + imageData[i] += v; + imageData[i + 1] += v; + imageData[i + 2] += v; + } + + context.putImageData(image, 0, 0); + + return canvasScaled; +} + +// + +function animate() { + render(); + stats.update(); +} + +function render() { + renderer.render(scene, camera); +} + +function onPointerMove(event) { + pointer.x = (event.clientX / renderer.domElement.clientWidth) * 2 - 1; + pointer.y = -(event.clientY / renderer.domElement.clientHeight) * 2 + 1; + raycaster.setFromCamera(pointer, camera); + + // See if the ray from the camera into the world hits one of our meshes + const intersects = raycaster.intersectObject(mesh); + + // Toggle rotation bool for meshes that we clicked + if (intersects.length > 0) { + helper.position.set(0, 0, 0); + helper.lookAt(intersects[0].face.normal); + + helper.position.copy(intersects[0].point); + } +} diff --git a/examples-testing/examples/webgl_geometry_text.ts b/examples-testing/examples/webgl_geometry_text.ts new file mode 100644 index 000000000..1a9d00d06 --- /dev/null +++ b/examples-testing/examples/webgl_geometry_text.ts @@ -0,0 +1,312 @@ +import * as THREE from 'three'; + +import { FontLoader } from 'three/addons/loaders/FontLoader.js'; +import { TextGeometry } from 'three/addons/geometries/TextGeometry.js'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +THREE.Cache.enabled = true; + +let container; + +let camera, cameraTarget, scene, renderer; + +let group, textMesh1, textMesh2, textGeo, materials; + +let firstLetter = true; + +let text = 'three.js', + bevelEnabled = true, + font = undefined, + fontName = 'optimer', // helvetiker, optimer, gentilis, droid sans, droid serif + fontWeight = 'bold'; // normal bold + +const depth = 20, + size = 70, + hover = 30, + curveSegments = 4, + bevelThickness = 2, + bevelSize = 1.5; + +const mirror = true; + +const fontMap = { + helvetiker: 0, + optimer: 1, + gentilis: 2, + 'droid/droid_sans': 3, + 'droid/droid_serif': 4, +}; + +const weightMap = { + regular: 0, + bold: 1, +}; + +const reverseFontMap = []; +const reverseWeightMap = []; + +for (const i in fontMap) reverseFontMap[fontMap[i]] = i; +for (const i in weightMap) reverseWeightMap[weightMap[i]] = i; + +let targetRotation = 0; +let targetRotationOnPointerDown = 0; + +let pointerX = 0; +let pointerXOnPointerDown = 0; + +let windowHalfX = window.innerWidth / 2; + +let fontIndex = 1; + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + // CAMERA + + camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 1500); + camera.position.set(0, 400, 700); + + cameraTarget = new THREE.Vector3(0, 150, 0); + + // SCENE + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x000000); + scene.fog = new THREE.Fog(0x000000, 250, 1400); + + // LIGHTS + + const dirLight = new THREE.DirectionalLight(0xffffff, 0.4); + dirLight.position.set(0, 0, 1).normalize(); + scene.add(dirLight); + + const pointLight = new THREE.PointLight(0xffffff, 4.5, 0, 0); + pointLight.color.setHSL(Math.random(), 1, 0.5); + pointLight.position.set(0, 100, 90); + scene.add(pointLight); + + materials = [ + new THREE.MeshPhongMaterial({ color: 0xffffff, flatShading: true }), // front + new THREE.MeshPhongMaterial({ color: 0xffffff }), // side + ]; + + group = new THREE.Group(); + group.position.y = 100; + + scene.add(group); + + loadFont(); + + const plane = new THREE.Mesh( + new THREE.PlaneGeometry(10000, 10000), + new THREE.MeshBasicMaterial({ color: 0xffffff, opacity: 0.5, transparent: true }), + ); + plane.position.y = 100; + plane.rotation.x = -Math.PI / 2; + scene.add(plane); + + // RENDERER + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + // EVENTS + + container.style.touchAction = 'none'; + container.addEventListener('pointerdown', onPointerDown); + + document.addEventListener('keypress', onDocumentKeyPress); + document.addEventListener('keydown', onDocumentKeyDown); + + // + + const params = { + changeColor: function () { + pointLight.color.setHSL(Math.random(), 1, 0.5); + }, + changeFont: function () { + fontIndex++; + + fontName = reverseFontMap[fontIndex % reverseFontMap.length]; + + loadFont(); + }, + changeWeight: function () { + if (fontWeight === 'bold') { + fontWeight = 'regular'; + } else { + fontWeight = 'bold'; + } + + loadFont(); + }, + changeBevel: function () { + bevelEnabled = !bevelEnabled; + + refreshText(); + }, + }; + + // + + const gui = new GUI(); + + gui.add(params, 'changeColor').name('change color'); + gui.add(params, 'changeFont').name('change font'); + gui.add(params, 'changeWeight').name('change weight'); + gui.add(params, 'changeBevel').name('change bevel'); + gui.open(); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + windowHalfX = window.innerWidth / 2; + + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function onDocumentKeyDown(event) { + if (firstLetter) { + firstLetter = false; + text = ''; + } + + const keyCode = event.keyCode; + + // backspace + + if (keyCode == 8) { + event.preventDefault(); + + text = text.substring(0, text.length - 1); + refreshText(); + + return false; + } +} + +function onDocumentKeyPress(event) { + const keyCode = event.which; + + // backspace + + if (keyCode == 8) { + event.preventDefault(); + } else { + const ch = String.fromCharCode(keyCode); + text += ch; + + refreshText(); + } +} + +function loadFont() { + const loader = new FontLoader(); + loader.load('fonts/' + fontName + '_' + fontWeight + '.typeface.json', function (response) { + font = response; + + refreshText(); + }); +} + +function createText() { + textGeo = new TextGeometry(text, { + font: font, + + size: size, + depth: depth, + curveSegments: curveSegments, + + bevelThickness: bevelThickness, + bevelSize: bevelSize, + bevelEnabled: bevelEnabled, + }); + + textGeo.computeBoundingBox(); + + const centerOffset = -0.5 * (textGeo.boundingBox.max.x - textGeo.boundingBox.min.x); + + textMesh1 = new THREE.Mesh(textGeo, materials); + + textMesh1.position.x = centerOffset; + textMesh1.position.y = hover; + textMesh1.position.z = 0; + + textMesh1.rotation.x = 0; + textMesh1.rotation.y = Math.PI * 2; + + group.add(textMesh1); + + if (mirror) { + textMesh2 = new THREE.Mesh(textGeo, materials); + + textMesh2.position.x = centerOffset; + textMesh2.position.y = -hover; + textMesh2.position.z = depth; + + textMesh2.rotation.x = Math.PI; + textMesh2.rotation.y = Math.PI * 2; + + group.add(textMesh2); + } +} + +function refreshText() { + group.remove(textMesh1); + if (mirror) group.remove(textMesh2); + + if (!text) return; + + createText(); +} + +function onPointerDown(event) { + if (event.isPrimary === false) return; + + pointerXOnPointerDown = event.clientX - windowHalfX; + targetRotationOnPointerDown = targetRotation; + + document.addEventListener('pointermove', onPointerMove); + document.addEventListener('pointerup', onPointerUp); +} + +function onPointerMove(event) { + if (event.isPrimary === false) return; + + pointerX = event.clientX - windowHalfX; + + targetRotation = targetRotationOnPointerDown + (pointerX - pointerXOnPointerDown) * 0.02; +} + +function onPointerUp(event) { + if (event.isPrimary === false) return; + + document.removeEventListener('pointermove', onPointerMove); + document.removeEventListener('pointerup', onPointerUp); +} + +// + +function animate() { + group.rotation.y += (targetRotation - group.rotation.y) * 0.05; + + camera.lookAt(cameraTarget); + + renderer.clear(); + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_geometry_text_shapes.ts b/examples-testing/examples/webgl_geometry_text_shapes.ts new file mode 100644 index 000000000..d9633cd15 --- /dev/null +++ b/examples-testing/examples/webgl_geometry_text_shapes.ts @@ -0,0 +1,112 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { FontLoader } from 'three/addons/loaders/FontLoader.js'; + +let camera, scene, renderer; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000); + camera.position.set(0, -400, 600); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xf0f0f0); + + const loader = new FontLoader(); + loader.load('fonts/helvetiker_regular.typeface.json', function (font) { + const color = 0x006699; + + const matDark = new THREE.LineBasicMaterial({ + color: color, + side: THREE.DoubleSide, + }); + + const matLite = new THREE.MeshBasicMaterial({ + color: color, + transparent: true, + opacity: 0.4, + side: THREE.DoubleSide, + }); + + const message = ' Three.js\nSimple text.'; + + const shapes = font.generateShapes(message, 100); + + const geometry = new THREE.ShapeGeometry(shapes); + + geometry.computeBoundingBox(); + + const xMid = -0.5 * (geometry.boundingBox.max.x - geometry.boundingBox.min.x); + + geometry.translate(xMid, 0, 0); + + // make shape ( N.B. edge view not visible ) + + const text = new THREE.Mesh(geometry, matLite); + text.position.z = -150; + scene.add(text); + + // make line shape ( N.B. edge view remains visible ) + + const holeShapes = []; + + for (let i = 0; i < shapes.length; i++) { + const shape = shapes[i]; + + if (shape.holes && shape.holes.length > 0) { + for (let j = 0; j < shape.holes.length; j++) { + const hole = shape.holes[j]; + holeShapes.push(hole); + } + } + } + + shapes.push(...holeShapes); + + const lineText = new THREE.Object3D(); + + for (let i = 0; i < shapes.length; i++) { + const shape = shapes[i]; + + const points = shape.getPoints(); + const geometry = new THREE.BufferGeometry().setFromPoints(points); + + geometry.translate(xMid, 0, 0); + + const lineMesh = new THREE.Line(geometry, matDark); + lineText.add(lineMesh); + } + + scene.add(lineText); + + render(); + }); //end load function + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + document.body.appendChild(renderer.domElement); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, 0, 0); + controls.update(); + + controls.addEventListener('change', render); + + window.addEventListener('resize', onWindowResize); +} // end init + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + render(); +} + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_geometry_text_stroke.ts b/examples-testing/examples/webgl_geometry_text_stroke.ts new file mode 100644 index 000000000..373892c74 --- /dev/null +++ b/examples-testing/examples/webgl_geometry_text_stroke.ts @@ -0,0 +1,152 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { SVGLoader } from 'three/addons/loaders/SVGLoader.js'; +import { Font } from 'three/addons/loaders/FontLoader.js'; +import { unzipSync, strFromU8 } from 'three/addons/libs/fflate.module.js'; + +let camera, scene, renderer; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000); + camera.position.set(0, -400, 1000); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xf0f0f0); + + new THREE.FileLoader() + .setResponseType('arraybuffer') + .load('fonts/MPLUSRounded1c/MPLUSRounded1c-Regular.typeface.json.zip', function (data) { + const zip = unzipSync(new Uint8Array(data)); + const strArray = strFromU8(new Uint8Array(zip['MPLUSRounded1c-Regular.typeface.json'].buffer)); + + const font = new Font(JSON.parse(strArray)); + const color = new THREE.Color(0x006699); + + const matDark = new THREE.MeshBasicMaterial({ + color: color, + side: THREE.DoubleSide, + }); + + const matLite = new THREE.MeshBasicMaterial({ + color: color, + transparent: true, + opacity: 0.4, + side: THREE.DoubleSide, + }); + + const material = { + dark: matDark, + lite: matLite, + color: color, + }; + + const english = ' Three.js\nStroke text.'; // Left to right + + const hebrew = 'טקסט קו'; // Right to left + + const chinese = '文字描邊'; // Top to bottom + + const message1 = generateStrokeText(font, material, english, 80, 'ltr'); + + const message2 = generateStrokeText(font, material, hebrew, 80, 'rtl'); + + const message3 = generateStrokeText(font, material, chinese, 80, 'tb'); + + message1.position.x = -100; + + message2.position.x = -100; + message2.position.y = -300; + + message3.position.x = 300; + message3.position.y = -300; + + scene.add(message1, message2, message3); + + render(); + }); //end load function + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + document.body.appendChild(renderer.domElement); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, 0, 0); + controls.update(); + + controls.addEventListener('change', render); + + window.addEventListener('resize', onWindowResize); +} // end init + +function generateStrokeText(font, material, message, size, direction = 'ltr') { + const shapes = font.generateShapes(message, size, direction); + + const geometry = new THREE.ShapeGeometry(shapes); + + const strokeText = new THREE.Group(); + + geometry.computeBoundingBox(); + + const xMid = -0.5 * (geometry.boundingBox.max.x - geometry.boundingBox.min.x); + + geometry.translate(xMid, 0, 0); + + // make shape ( N.B. edge view not visible ) + + const text = new THREE.Mesh(geometry, material.lite); + + text.position.z = -150; + + strokeText.add(text); + + // make line shape ( N.B. edge view remains visible ) + + const holeShapes = []; + + for (let i = 0; i < shapes.length; i++) { + const shape = shapes[i]; + + if (shape.holes && shape.holes.length > 0) { + for (let j = 0; j < shape.holes.length; j++) { + const hole = shape.holes[j]; + holeShapes.push(hole); + } + } + } + + shapes.push(...holeShapes); + + const style = SVGLoader.getStrokeStyle(5, material.color.getStyle()); + + for (let i = 0; i < shapes.length; i++) { + const shape = shapes[i]; + + const points = shape.getPoints(); + + const geometry = SVGLoader.pointsToStroke(points, style); + + geometry.translate(xMid, 0, 0); + + const strokeMesh = new THREE.Mesh(geometry, material.dark); + strokeText.add(strokeMesh); + } + + return strokeText; +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + render(); +} + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_gpgpu_birds.ts b/examples-testing/examples/webgl_gpgpu_birds.ts new file mode 100644 index 000000000..20a5e0d97 --- /dev/null +++ b/examples-testing/examples/webgl_gpgpu_birds.ts @@ -0,0 +1,313 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { GPUComputationRenderer } from 'three/addons/misc/GPUComputationRenderer.js'; + +/* TEXTURE WIDTH FOR SIMULATION */ +const WIDTH = 32; + +const BIRDS = WIDTH * WIDTH; + +// Custom Geometry - using 3 triangles each. No UVs, no normals currently. +class BirdGeometry extends THREE.BufferGeometry { + constructor() { + super(); + + const trianglesPerBird = 3; + const triangles = BIRDS * trianglesPerBird; + const points = triangles * 3; + + const vertices = new THREE.BufferAttribute(new Float32Array(points * 3), 3); + const birdColors = new THREE.BufferAttribute(new Float32Array(points * 3), 3); + const references = new THREE.BufferAttribute(new Float32Array(points * 2), 2); + const birdVertex = new THREE.BufferAttribute(new Float32Array(points), 1); + + this.setAttribute('position', vertices); + this.setAttribute('birdColor', birdColors); + this.setAttribute('reference', references); + this.setAttribute('birdVertex', birdVertex); + + // this.setAttribute( 'normal', new Float32Array( points * 3 ), 3 ); + + let v = 0; + + function verts_push() { + for (let i = 0; i < arguments.length; i++) { + vertices.array[v++] = arguments[i]; + } + } + + const wingsSpan = 20; + + for (let f = 0; f < BIRDS; f++) { + // Body + + verts_push(0, -0, -20, 0, 4, -20, 0, 0, 30); + + // Wings + + verts_push(0, 0, -15, -wingsSpan, 0, 0, 0, 0, 15); + + verts_push(0, 0, 15, wingsSpan, 0, 0, 0, 0, -15); + } + + for (let v = 0; v < triangles * 3; v++) { + const triangleIndex = ~~(v / 3); + const birdIndex = ~~(triangleIndex / trianglesPerBird); + const x = (birdIndex % WIDTH) / WIDTH; + const y = ~~(birdIndex / WIDTH) / WIDTH; + + const c = new THREE.Color(0x666666 + (~~(v / 9) / BIRDS) * 0x666666); + + birdColors.array[v * 3 + 0] = c.r; + birdColors.array[v * 3 + 1] = c.g; + birdColors.array[v * 3 + 2] = c.b; + + references.array[v * 2] = x; + references.array[v * 2 + 1] = y; + + birdVertex.array[v] = v % 9; + } + + this.scale(0.2, 0.2, 0.2); + } +} + +// + +let container, stats; +let camera, scene, renderer; +let mouseX = 0, + mouseY = 0; + +let windowHalfX = window.innerWidth / 2; +let windowHalfY = window.innerHeight / 2; + +const BOUNDS = 800, + BOUNDS_HALF = BOUNDS / 2; + +let last = performance.now(); + +let gpuCompute; +let velocityVariable; +let positionVariable; +let positionUniforms; +let velocityUniforms; +let birdUniforms; + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 3000); + camera.position.z = 350; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xffffff); + scene.fog = new THREE.Fog(0xffffff, 100, 1000); + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + initComputeRenderer(); + + stats = new Stats(); + container.appendChild(stats.dom); + + container.style.touchAction = 'none'; + container.addEventListener('pointermove', onPointerMove); + + // + + window.addEventListener('resize', onWindowResize); + + const gui = new GUI(); + + const effectController = { + separation: 20.0, + alignment: 20.0, + cohesion: 20.0, + freedom: 0.75, + }; + + const valuesChanger = function () { + velocityUniforms['separationDistance'].value = effectController.separation; + velocityUniforms['alignmentDistance'].value = effectController.alignment; + velocityUniforms['cohesionDistance'].value = effectController.cohesion; + velocityUniforms['freedomFactor'].value = effectController.freedom; + }; + + valuesChanger(); + + gui.add(effectController, 'separation', 0.0, 100.0, 1.0).onChange(valuesChanger); + gui.add(effectController, 'alignment', 0.0, 100, 0.001).onChange(valuesChanger); + gui.add(effectController, 'cohesion', 0.0, 100, 0.025).onChange(valuesChanger); + gui.close(); + + initBirds(); +} + +function initComputeRenderer() { + gpuCompute = new GPUComputationRenderer(WIDTH, WIDTH, renderer); + + const dtPosition = gpuCompute.createTexture(); + const dtVelocity = gpuCompute.createTexture(); + fillPositionTexture(dtPosition); + fillVelocityTexture(dtVelocity); + + velocityVariable = gpuCompute.addVariable( + 'textureVelocity', + document.getElementById('fragmentShaderVelocity').textContent, + dtVelocity, + ); + positionVariable = gpuCompute.addVariable( + 'texturePosition', + document.getElementById('fragmentShaderPosition').textContent, + dtPosition, + ); + + gpuCompute.setVariableDependencies(velocityVariable, [positionVariable, velocityVariable]); + gpuCompute.setVariableDependencies(positionVariable, [positionVariable, velocityVariable]); + + positionUniforms = positionVariable.material.uniforms; + velocityUniforms = velocityVariable.material.uniforms; + + positionUniforms['time'] = { value: 0.0 }; + positionUniforms['delta'] = { value: 0.0 }; + velocityUniforms['time'] = { value: 1.0 }; + velocityUniforms['delta'] = { value: 0.0 }; + velocityUniforms['testing'] = { value: 1.0 }; + velocityUniforms['separationDistance'] = { value: 1.0 }; + velocityUniforms['alignmentDistance'] = { value: 1.0 }; + velocityUniforms['cohesionDistance'] = { value: 1.0 }; + velocityUniforms['freedomFactor'] = { value: 1.0 }; + velocityUniforms['predator'] = { value: new THREE.Vector3() }; + velocityVariable.material.defines.BOUNDS = BOUNDS.toFixed(2); + + velocityVariable.wrapS = THREE.RepeatWrapping; + velocityVariable.wrapT = THREE.RepeatWrapping; + positionVariable.wrapS = THREE.RepeatWrapping; + positionVariable.wrapT = THREE.RepeatWrapping; + + const error = gpuCompute.init(); + + if (error !== null) { + console.error(error); + } +} + +function initBirds() { + const geometry = new BirdGeometry(); + + // For Vertex and Fragment + birdUniforms = { + color: { value: new THREE.Color(0xff2200) }, + texturePosition: { value: null }, + textureVelocity: { value: null }, + time: { value: 1.0 }, + delta: { value: 0.0 }, + }; + + // THREE.ShaderMaterial + const material = new THREE.ShaderMaterial({ + uniforms: birdUniforms, + vertexShader: document.getElementById('birdVS').textContent, + fragmentShader: document.getElementById('birdFS').textContent, + side: THREE.DoubleSide, + }); + + const birdMesh = new THREE.Mesh(geometry, material); + birdMesh.rotation.y = Math.PI / 2; + birdMesh.matrixAutoUpdate = false; + birdMesh.updateMatrix(); + + scene.add(birdMesh); +} + +function fillPositionTexture(texture) { + const theArray = texture.image.data; + + for (let k = 0, kl = theArray.length; k < kl; k += 4) { + const x = Math.random() * BOUNDS - BOUNDS_HALF; + const y = Math.random() * BOUNDS - BOUNDS_HALF; + const z = Math.random() * BOUNDS - BOUNDS_HALF; + + theArray[k + 0] = x; + theArray[k + 1] = y; + theArray[k + 2] = z; + theArray[k + 3] = 1; + } +} + +function fillVelocityTexture(texture) { + const theArray = texture.image.data; + + for (let k = 0, kl = theArray.length; k < kl; k += 4) { + const x = Math.random() - 0.5; + const y = Math.random() - 0.5; + const z = Math.random() - 0.5; + + theArray[k + 0] = x * 10; + theArray[k + 1] = y * 10; + theArray[k + 2] = z * 10; + theArray[k + 3] = 1; + } +} + +function onWindowResize() { + windowHalfX = window.innerWidth / 2; + windowHalfY = window.innerHeight / 2; + + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function onPointerMove(event) { + if (event.isPrimary === false) return; + + mouseX = event.clientX - windowHalfX; + mouseY = event.clientY - windowHalfY; +} + +// + +function animate() { + render(); + stats.update(); +} + +function render() { + const now = performance.now(); + let delta = (now - last) / 1000; + + if (delta > 1) delta = 1; // safety cap on large deltas + last = now; + + positionUniforms['time'].value = now; + positionUniforms['delta'].value = delta; + velocityUniforms['time'].value = now; + velocityUniforms['delta'].value = delta; + birdUniforms['time'].value = now; + birdUniforms['delta'].value = delta; + + velocityUniforms['predator'].value.set((0.5 * mouseX) / windowHalfX, (-0.5 * mouseY) / windowHalfY, 0); + + mouseX = 10000; + mouseY = 10000; + + gpuCompute.compute(); + + birdUniforms['texturePosition'].value = gpuCompute.getCurrentRenderTarget(positionVariable).texture; + birdUniforms['textureVelocity'].value = gpuCompute.getCurrentRenderTarget(velocityVariable).texture; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_gpgpu_birds_gltf.ts b/examples-testing/examples/webgl_gpgpu_birds_gltf.ts new file mode 100644 index 000000000..05f81a869 --- /dev/null +++ b/examples-testing/examples/webgl_gpgpu_birds_gltf.ts @@ -0,0 +1,413 @@ +import * as THREE from 'three'; +import Stats from 'three/addons/libs/stats.module.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { GPUComputationRenderer } from 'three/addons/misc/GPUComputationRenderer.js'; + +/* TEXTURE WIDTH FOR SIMULATION */ +const WIDTH = 64; +const BIRDS = WIDTH * WIDTH; + +/* BAKE ANIMATION INTO TEXTURE and CREATE GEOMETRY FROM BASE MODEL */ +const BirdGeometry = new THREE.BufferGeometry(); +let textureAnimation, durationAnimation, birdMesh, materialShader, indicesPerBird; + +function nextPowerOf2(n) { + return Math.pow(2, Math.ceil(Math.log(n) / Math.log(2))); +} + +const gltfs = ['models/gltf/Parrot.glb', 'models/gltf/Flamingo.glb']; +const colors = [0xccffff, 0xffdeff]; +const sizes = [0.2, 0.1]; +const selectModel = Math.floor(Math.random() * gltfs.length); +new GLTFLoader().load(gltfs[selectModel], function (gltf) { + const animations = gltf.animations; + durationAnimation = Math.round(animations[0].duration * 60); + const birdGeo = gltf.scene.children[0].geometry; + const morphAttributes = birdGeo.morphAttributes.position; + const tHeight = nextPowerOf2(durationAnimation); + const tWidth = nextPowerOf2(birdGeo.getAttribute('position').count); + indicesPerBird = birdGeo.index.count; + const tData = new Float32Array(4 * tWidth * tHeight); + + for (let i = 0; i < tWidth; i++) { + for (let j = 0; j < tHeight; j++) { + const offset = j * tWidth * 4; + + const curMorph = Math.floor((j / durationAnimation) * morphAttributes.length); + const nextMorph = + (Math.floor((j / durationAnimation) * morphAttributes.length) + 1) % morphAttributes.length; + const lerpAmount = ((j / durationAnimation) * morphAttributes.length) % 1; + + if (j < durationAnimation) { + let d0, d1; + + d0 = morphAttributes[curMorph].array[i * 3]; + d1 = morphAttributes[nextMorph].array[i * 3]; + + if (d0 !== undefined && d1 !== undefined) + tData[offset + i * 4] = THREE.MathUtils.lerp(d0, d1, lerpAmount); + + d0 = morphAttributes[curMorph].array[i * 3 + 1]; + d1 = morphAttributes[nextMorph].array[i * 3 + 1]; + + if (d0 !== undefined && d1 !== undefined) + tData[offset + i * 4 + 1] = THREE.MathUtils.lerp(d0, d1, lerpAmount); + + d0 = morphAttributes[curMorph].array[i * 3 + 2]; + d1 = morphAttributes[nextMorph].array[i * 3 + 2]; + + if (d0 !== undefined && d1 !== undefined) + tData[offset + i * 4 + 2] = THREE.MathUtils.lerp(d0, d1, lerpAmount); + + tData[offset + i * 4 + 3] = 1; + } + } + } + + textureAnimation = new THREE.DataTexture(tData, tWidth, tHeight, THREE.RGBAFormat, THREE.FloatType); + textureAnimation.needsUpdate = true; + + const vertices = [], + color = [], + reference = [], + seeds = [], + indices = []; + const totalVertices = birdGeo.getAttribute('position').count * 3 * BIRDS; + for (let i = 0; i < totalVertices; i++) { + const bIndex = i % (birdGeo.getAttribute('position').count * 3); + vertices.push(birdGeo.getAttribute('position').array[bIndex]); + color.push(birdGeo.getAttribute('color').array[bIndex]); + } + + let r = Math.random(); + for (let i = 0; i < birdGeo.getAttribute('position').count * BIRDS; i++) { + const bIndex = i % birdGeo.getAttribute('position').count; + const bird = Math.floor(i / birdGeo.getAttribute('position').count); + if (bIndex == 0) r = Math.random(); + const j = ~~bird; + const x = (j % WIDTH) / WIDTH; + const y = ~~(j / WIDTH) / WIDTH; + reference.push(x, y, bIndex / tWidth, durationAnimation / tHeight); + seeds.push(bird, r, Math.random(), Math.random()); + } + + for (let i = 0; i < birdGeo.index.array.length * BIRDS; i++) { + const offset = Math.floor(i / birdGeo.index.array.length) * birdGeo.getAttribute('position').count; + indices.push(birdGeo.index.array[i % birdGeo.index.array.length] + offset); + } + + BirdGeometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(vertices), 3)); + BirdGeometry.setAttribute('birdColor', new THREE.BufferAttribute(new Float32Array(color), 3)); + BirdGeometry.setAttribute('color', new THREE.BufferAttribute(new Float32Array(color), 3)); + BirdGeometry.setAttribute('reference', new THREE.BufferAttribute(new Float32Array(reference), 4)); + BirdGeometry.setAttribute('seeds', new THREE.BufferAttribute(new Float32Array(seeds), 4)); + + BirdGeometry.setIndex(indices); + + init(); +}); + +let container, stats; +let camera, scene, renderer; +let mouseX = 0, + mouseY = 0; + +let windowHalfX = window.innerWidth / 2; +let windowHalfY = window.innerHeight / 2; + +const BOUNDS = 800, + BOUNDS_HALF = BOUNDS / 2; + +let last = performance.now(); + +let gpuCompute; +let velocityVariable; +let positionVariable; +let positionUniforms; +let velocityUniforms; + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 3000); + camera.position.z = 350; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(colors[selectModel]); + scene.fog = new THREE.Fog(colors[selectModel], 100, 1000); + + // LIGHTS + + const hemiLight = new THREE.HemisphereLight(colors[selectModel], 0xffffff, 4.5); + hemiLight.color.setHSL(0.6, 1, 0.6, THREE.SRGBColorSpace); + hemiLight.groundColor.setHSL(0.095, 1, 0.75, THREE.SRGBColorSpace); + hemiLight.position.set(0, 50, 0); + scene.add(hemiLight); + + const dirLight = new THREE.DirectionalLight(0x00ced1, 2.0); + dirLight.color.setHSL(0.1, 1, 0.95, THREE.SRGBColorSpace); + dirLight.position.set(-1, 1.75, 1); + dirLight.position.multiplyScalar(30); + scene.add(dirLight); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + initComputeRenderer(); + + stats = new Stats(); + container.appendChild(stats.dom); + + container.style.touchAction = 'none'; + container.addEventListener('pointermove', onPointerMove); + + window.addEventListener('resize', onWindowResize); + + const gui = new GUI(); + + const effectController = { + separation: 20.0, + alignment: 20.0, + cohesion: 20.0, + freedom: 0.75, + size: sizes[selectModel], + count: Math.floor(BIRDS / 4), + }; + + const valuesChanger = function () { + velocityUniforms['separationDistance'].value = effectController.separation; + velocityUniforms['alignmentDistance'].value = effectController.alignment; + velocityUniforms['cohesionDistance'].value = effectController.cohesion; + velocityUniforms['freedomFactor'].value = effectController.freedom; + if (materialShader) materialShader.uniforms['size'].value = effectController.size; + BirdGeometry.setDrawRange(0, indicesPerBird * effectController.count); + }; + + valuesChanger(); + + gui.add(effectController, 'separation', 0.0, 100.0, 1.0).onChange(valuesChanger); + gui.add(effectController, 'alignment', 0.0, 100, 0.001).onChange(valuesChanger); + gui.add(effectController, 'cohesion', 0.0, 100, 0.025).onChange(valuesChanger); + gui.add(effectController, 'size', 0, 1, 0.01).onChange(valuesChanger); + gui.add(effectController, 'count', 0, BIRDS, 1).onChange(valuesChanger); + gui.close(); + + initBirds(effectController); +} + +function initComputeRenderer() { + gpuCompute = new GPUComputationRenderer(WIDTH, WIDTH, renderer); + + const dtPosition = gpuCompute.createTexture(); + const dtVelocity = gpuCompute.createTexture(); + fillPositionTexture(dtPosition); + fillVelocityTexture(dtVelocity); + + velocityVariable = gpuCompute.addVariable( + 'textureVelocity', + document.getElementById('fragmentShaderVelocity').textContent, + dtVelocity, + ); + positionVariable = gpuCompute.addVariable( + 'texturePosition', + document.getElementById('fragmentShaderPosition').textContent, + dtPosition, + ); + + gpuCompute.setVariableDependencies(velocityVariable, [positionVariable, velocityVariable]); + gpuCompute.setVariableDependencies(positionVariable, [positionVariable, velocityVariable]); + + positionUniforms = positionVariable.material.uniforms; + velocityUniforms = velocityVariable.material.uniforms; + + positionUniforms['time'] = { value: 0.0 }; + positionUniforms['delta'] = { value: 0.0 }; + velocityUniforms['time'] = { value: 1.0 }; + velocityUniforms['delta'] = { value: 0.0 }; + velocityUniforms['testing'] = { value: 1.0 }; + velocityUniforms['separationDistance'] = { value: 1.0 }; + velocityUniforms['alignmentDistance'] = { value: 1.0 }; + velocityUniforms['cohesionDistance'] = { value: 1.0 }; + velocityUniforms['freedomFactor'] = { value: 1.0 }; + velocityUniforms['predator'] = { value: new THREE.Vector3() }; + velocityVariable.material.defines.BOUNDS = BOUNDS.toFixed(2); + + velocityVariable.wrapS = THREE.RepeatWrapping; + velocityVariable.wrapT = THREE.RepeatWrapping; + positionVariable.wrapS = THREE.RepeatWrapping; + positionVariable.wrapT = THREE.RepeatWrapping; + + const error = gpuCompute.init(); + + if (error !== null) { + console.error(error); + } +} + +function initBirds(effectController) { + const geometry = BirdGeometry; + + const m = new THREE.MeshStandardMaterial({ + vertexColors: true, + flatShading: true, + roughness: 1, + metalness: 0, + }); + + m.onBeforeCompile = shader => { + shader.uniforms.texturePosition = { value: null }; + shader.uniforms.textureVelocity = { value: null }; + shader.uniforms.textureAnimation = { value: textureAnimation }; + shader.uniforms.time = { value: 1.0 }; + shader.uniforms.size = { value: effectController.size }; + shader.uniforms.delta = { value: 0.0 }; + + let token = '#define STANDARD'; + + let insert = /* glsl */ ` + attribute vec4 reference; + attribute vec4 seeds; + attribute vec3 birdColor; + uniform sampler2D texturePosition; + uniform sampler2D textureVelocity; + uniform sampler2D textureAnimation; + uniform float size; + uniform float time; + `; + + shader.vertexShader = shader.vertexShader.replace(token, token + insert); + + token = '#include '; + + insert = /* glsl */ ` + vec4 tmpPos = texture2D( texturePosition, reference.xy ); + + vec3 pos = tmpPos.xyz; + vec3 velocity = normalize(texture2D( textureVelocity, reference.xy ).xyz); + vec3 aniPos = texture2D( textureAnimation, vec2( reference.z, mod( time + ( seeds.x ) * ( ( 0.0004 + seeds.y / 10000.0) + normalize( velocity ) / 20000.0 ), reference.w ) ) ).xyz; + vec3 newPosition = position; + + newPosition = mat3( modelMatrix ) * ( newPosition + aniPos ); + newPosition *= size + seeds.y * size * 0.2; + + velocity.z *= -1.; + float xz = length( velocity.xz ); + float xyz = 1.; + float x = sqrt( 1. - velocity.y * velocity.y ); + + float cosry = velocity.x / xz; + float sinry = velocity.z / xz; + + float cosrz = x / xyz; + float sinrz = velocity.y / xyz; + + mat3 maty = mat3( cosry, 0, -sinry, 0 , 1, 0 , sinry, 0, cosry ); + mat3 matz = mat3( cosrz , sinrz, 0, -sinrz, cosrz, 0, 0 , 0 , 1 ); + + newPosition = maty * matz * newPosition; + newPosition += pos; + + vec3 transformed = vec3( newPosition ); + `; + + shader.vertexShader = shader.vertexShader.replace(token, insert); + + materialShader = shader; + }; + + birdMesh = new THREE.Mesh(geometry, m); + birdMesh.rotation.y = Math.PI / 2; + + birdMesh.castShadow = true; + birdMesh.receiveShadow = true; + + scene.add(birdMesh); +} + +function fillPositionTexture(texture) { + const theArray = texture.image.data; + + for (let k = 0, kl = theArray.length; k < kl; k += 4) { + const x = Math.random() * BOUNDS - BOUNDS_HALF; + const y = Math.random() * BOUNDS - BOUNDS_HALF; + const z = Math.random() * BOUNDS - BOUNDS_HALF; + + theArray[k + 0] = x; + theArray[k + 1] = y; + theArray[k + 2] = z; + theArray[k + 3] = 1; + } +} + +function fillVelocityTexture(texture) { + const theArray = texture.image.data; + + for (let k = 0, kl = theArray.length; k < kl; k += 4) { + const x = Math.random() - 0.5; + const y = Math.random() - 0.5; + const z = Math.random() - 0.5; + + theArray[k + 0] = x * 10; + theArray[k + 1] = y * 10; + theArray[k + 2] = z * 10; + theArray[k + 3] = 1; + } +} + +function onWindowResize() { + windowHalfX = window.innerWidth / 2; + windowHalfY = window.innerHeight / 2; + + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function onPointerMove(event) { + if (event.isPrimary === false) return; + + mouseX = event.clientX - windowHalfX; + mouseY = event.clientY - windowHalfY; +} + +// + +function animate() { + render(); + stats.update(); +} + +function render() { + const now = performance.now(); + let delta = (now - last) / 1000; + + if (delta > 1) delta = 1; // safety cap on large deltas + last = now; + + positionUniforms['time'].value = now; + positionUniforms['delta'].value = delta; + velocityUniforms['time'].value = now; + velocityUniforms['delta'].value = delta; + if (materialShader) materialShader.uniforms['time'].value = now / 1000; + if (materialShader) materialShader.uniforms['delta'].value = delta; + + velocityUniforms['predator'].value.set((0.5 * mouseX) / windowHalfX, (-0.5 * mouseY) / windowHalfY, 0); + + mouseX = 10000; + mouseY = 10000; + + gpuCompute.compute(); + + if (materialShader) + materialShader.uniforms['texturePosition'].value = gpuCompute.getCurrentRenderTarget(positionVariable).texture; + if (materialShader) + materialShader.uniforms['textureVelocity'].value = gpuCompute.getCurrentRenderTarget(velocityVariable).texture; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_gpgpu_protoplanet.ts b/examples-testing/examples/webgl_gpgpu_protoplanet.ts new file mode 100644 index 000000000..30444ddba --- /dev/null +++ b/examples-testing/examples/webgl_gpgpu_protoplanet.ts @@ -0,0 +1,280 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GPUComputationRenderer } from 'three/addons/misc/GPUComputationRenderer.js'; + +// Texture width for simulation (each texel is a debris particle) +const WIDTH = 64; + +let container, stats; +let camera, scene, renderer, geometry; + +const PARTICLES = WIDTH * WIDTH; + +let gpuCompute; +let velocityVariable; +let positionVariable; +let velocityUniforms; +let particleUniforms; +let effectController; + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 5, 15000); + camera.position.y = 120; + camera.position.z = 400; + + scene = new THREE.Scene(); + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 100; + controls.maxDistance = 1000; + + effectController = { + // Can be changed dynamically + gravityConstant: 100.0, + density: 0.45, + + // Must restart simulation + radius: 300, + height: 8, + exponent: 0.4, + maxMass: 15.0, + velocity: 70, + velocityExponent: 0.2, + randVelocity: 0.001, + }; + + initComputeRenderer(); + + stats = new Stats(); + container.appendChild(stats.dom); + + window.addEventListener('resize', onWindowResize); + + initGUI(); + + initProtoplanets(); + + dynamicValuesChanger(); +} + +function initComputeRenderer() { + gpuCompute = new GPUComputationRenderer(WIDTH, WIDTH, renderer); + + const dtPosition = gpuCompute.createTexture(); + const dtVelocity = gpuCompute.createTexture(); + + fillTextures(dtPosition, dtVelocity); + + velocityVariable = gpuCompute.addVariable( + 'textureVelocity', + document.getElementById('computeShaderVelocity').textContent, + dtVelocity, + ); + positionVariable = gpuCompute.addVariable( + 'texturePosition', + document.getElementById('computeShaderPosition').textContent, + dtPosition, + ); + + gpuCompute.setVariableDependencies(velocityVariable, [positionVariable, velocityVariable]); + gpuCompute.setVariableDependencies(positionVariable, [positionVariable, velocityVariable]); + + velocityUniforms = velocityVariable.material.uniforms; + + velocityUniforms['gravityConstant'] = { value: 0.0 }; + velocityUniforms['density'] = { value: 0.0 }; + + const error = gpuCompute.init(); + + if (error !== null) { + console.error(error); + } +} + +function restartSimulation() { + const dtPosition = gpuCompute.createTexture(); + const dtVelocity = gpuCompute.createTexture(); + + fillTextures(dtPosition, dtVelocity); + + gpuCompute.renderTexture(dtPosition, positionVariable.renderTargets[0]); + gpuCompute.renderTexture(dtPosition, positionVariable.renderTargets[1]); + gpuCompute.renderTexture(dtVelocity, velocityVariable.renderTargets[0]); + gpuCompute.renderTexture(dtVelocity, velocityVariable.renderTargets[1]); +} + +function initProtoplanets() { + geometry = new THREE.BufferGeometry(); + + const positions = new Float32Array(PARTICLES * 3); + let p = 0; + + for (let i = 0; i < PARTICLES; i++) { + positions[p++] = (Math.random() * 2 - 1) * effectController.radius; + positions[p++] = 0; //( Math.random() * 2 - 1 ) * effectController.radius; + positions[p++] = (Math.random() * 2 - 1) * effectController.radius; + } + + const uvs = new Float32Array(PARTICLES * 2); + p = 0; + + for (let j = 0; j < WIDTH; j++) { + for (let i = 0; i < WIDTH; i++) { + uvs[p++] = i / (WIDTH - 1); + uvs[p++] = j / (WIDTH - 1); + } + } + + geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); + geometry.setAttribute('uv', new THREE.BufferAttribute(uvs, 2)); + + particleUniforms = { + texturePosition: { value: null }, + textureVelocity: { value: null }, + cameraConstant: { value: getCameraConstant(camera) }, + density: { value: 0.0 }, + }; + + // THREE.ShaderMaterial + const material = new THREE.ShaderMaterial({ + uniforms: particleUniforms, + vertexShader: document.getElementById('particleVertexShader').textContent, + fragmentShader: document.getElementById('particleFragmentShader').textContent, + }); + + const particles = new THREE.Points(geometry, material); + particles.matrixAutoUpdate = false; + particles.updateMatrix(); + + scene.add(particles); +} + +function fillTextures(texturePosition, textureVelocity) { + const posArray = texturePosition.image.data; + const velArray = textureVelocity.image.data; + + const radius = effectController.radius; + const height = effectController.height; + const exponent = effectController.exponent; + const maxMass = (effectController.maxMass * 1024) / PARTICLES; + const maxVel = effectController.velocity; + const velExponent = effectController.velocityExponent; + const randVel = effectController.randVelocity; + + for (let k = 0, kl = posArray.length; k < kl; k += 4) { + // Position + let x, z, rr; + + do { + x = Math.random() * 2 - 1; + z = Math.random() * 2 - 1; + rr = x * x + z * z; + } while (rr > 1); + + rr = Math.sqrt(rr); + + const rExp = radius * Math.pow(rr, exponent); + + // Velocity + const vel = maxVel * Math.pow(rr, velExponent); + + const vx = vel * z + (Math.random() * 2 - 1) * randVel; + const vy = (Math.random() * 2 - 1) * randVel * 0.05; + const vz = -vel * x + (Math.random() * 2 - 1) * randVel; + + x *= rExp; + z *= rExp; + const y = (Math.random() * 2 - 1) * height; + + const mass = Math.random() * maxMass + 1; + + // Fill in texture values + posArray[k + 0] = x; + posArray[k + 1] = y; + posArray[k + 2] = z; + posArray[k + 3] = 1; + + velArray[k + 0] = vx; + velArray[k + 1] = vy; + velArray[k + 2] = vz; + velArray[k + 3] = mass; + } +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + particleUniforms['cameraConstant'].value = getCameraConstant(camera); +} + +function dynamicValuesChanger() { + velocityUniforms['gravityConstant'].value = effectController.gravityConstant; + velocityUniforms['density'].value = effectController.density; + particleUniforms['density'].value = effectController.density; +} + +function initGUI() { + const gui = new GUI({ width: 280 }); + + const folder1 = gui.addFolder('Dynamic parameters'); + + folder1.add(effectController, 'gravityConstant', 0.0, 1000.0, 0.05).onChange(dynamicValuesChanger); + folder1.add(effectController, 'density', 0.0, 10.0, 0.001).onChange(dynamicValuesChanger); + + const folder2 = gui.addFolder('Static parameters'); + + folder2.add(effectController, 'radius', 10.0, 1000.0, 1.0); + folder2.add(effectController, 'height', 0.0, 50.0, 0.01); + folder2.add(effectController, 'exponent', 0.0, 2.0, 0.001); + folder2.add(effectController, 'maxMass', 1.0, 50.0, 0.1); + folder2.add(effectController, 'velocity', 0.0, 150.0, 0.1); + folder2.add(effectController, 'velocityExponent', 0.0, 1.0, 0.01); + folder2.add(effectController, 'randVelocity', 0.0, 50.0, 0.1); + + const buttonRestart = { + restartSimulation: function () { + restartSimulation(); + }, + }; + + folder2.add(buttonRestart, 'restartSimulation'); + + folder1.open(); + folder2.open(); +} + +function getCameraConstant(camera) { + return window.innerHeight / (Math.tan(THREE.MathUtils.DEG2RAD * 0.5 * camera.fov) / camera.zoom); +} + +function animate() { + render(); + stats.update(); +} + +function render() { + gpuCompute.compute(); + + particleUniforms['texturePosition'].value = gpuCompute.getCurrentRenderTarget(positionVariable).texture; + particleUniforms['textureVelocity'].value = gpuCompute.getCurrentRenderTarget(velocityVariable).texture; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_gpgpu_water.ts b/examples-testing/examples/webgl_gpgpu_water.ts new file mode 100644 index 000000000..3210a298a --- /dev/null +++ b/examples-testing/examples/webgl_gpgpu_water.ts @@ -0,0 +1,583 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { GPUComputationRenderer } from 'three/addons/misc/GPUComputationRenderer.js'; +import { SimplexNoise } from 'three/addons/math/SimplexNoise.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; +import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; + +// Texture width for simulation +const WIDTH = 128; + +// Water size in system units +const BOUNDS = 6; +const BOUNDS_HALF = BOUNDS * 0.5; + +let tmpHeightmap = null; +const tmpQuat = new THREE.Quaternion(); +const tmpQuatX = new THREE.Quaternion(); +const tmpQuatZ = new THREE.Quaternion(); +let duckModel = null; + +let container, stats; +let camera, scene, renderer, controls; +let mousedown = false; +const mouseCoords = new THREE.Vector2(); +const raycaster = new THREE.Raycaster(); + +let sun; +let waterMesh; +let poolBorder; +let meshRay; +let gpuCompute; +let heightmapVariable; +// let smoothShader; +let readWaterLevelShader; +let readWaterLevelRenderTarget; +let readWaterLevelImage; +const waterNormal = new THREE.Vector3(); + +const NUM_DUCK = 12; +const ducks = []; +let ducksEnabled = true; + +const simplex = new SimplexNoise(); + +let frame = 0; + +const effectController = { + mouseSize: 0.2, + mouseDeep: 0.01, + viscosity: 0.93, + speed: 5, + ducksEnabled: ducksEnabled, + wireframe: false, + shadow: false, +}; + +init(); + +async function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); + camera.position.set(0, 2.0, 4); + camera.lookAt(0, 0, 0); + + scene = new THREE.Scene(); + + sun = new THREE.DirectionalLight(0xffffff, 4.0); + sun.position.set(-1, 2.6, 1.4); + scene.add(sun); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = 0.5; + container.appendChild(renderer.domElement); + + controls = new OrbitControls(camera, container); + + stats = new Stats(); + container.appendChild(stats.dom); + + container.style.touchAction = 'none'; + container.addEventListener('pointermove', onPointerMove); + container.addEventListener('pointerdown', onPointerDown); + container.addEventListener('pointerup', onPointerUp); + + window.addEventListener('resize', onWindowResize); + + const hdrLoader = new HDRLoader().setPath('./textures/equirectangular/'); + const glbloader = new GLTFLoader().setPath('models/gltf/'); + glbloader.setDRACOLoader(new DRACOLoader().setDecoderPath('jsm/libs/draco/gltf/')); + + const [env, model] = await Promise.all([ + hdrLoader.loadAsync('blouberg_sunrise_2_1k.hdr'), + glbloader.loadAsync('duck.glb'), + ]); + env.mapping = THREE.EquirectangularReflectionMapping; + scene.environment = env; + scene.background = env; + scene.backgroundBlurriness = 0.3; + scene.environmentIntensity = 1.25; + + duckModel = model.scene.children[0]; + duckModel.receiveShadow = true; + duckModel.castShadow = true; + + const gui = new GUI(); + gui.domElement.style.right = '0px'; + + const valuesChanger = function () { + heightmapVariable.material.uniforms['mouseSize'].value = effectController.mouseSize; + heightmapVariable.material.uniforms['deep'].value = effectController.mouseDeep; + heightmapVariable.material.uniforms['viscosity'].value = effectController.viscosity; + ducksEnabled = effectController.ducksEnabled; + + let i = NUM_DUCK; + while (i--) { + if (ducks[i]) ducks[i].visible = ducksEnabled; + } + }; + + gui.add(effectController, 'mouseSize', 0.1, 1.0, 0.1).onChange(valuesChanger); + gui.add(effectController, 'mouseDeep', 0.01, 1.0, 0.01).onChange(valuesChanger); + gui.add(effectController, 'viscosity', 0.9, 0.999, 0.001).onChange(valuesChanger); + gui.add(effectController, 'speed', 1, 6, 1); + gui.add(effectController, 'ducksEnabled').onChange(valuesChanger); + gui.add(effectController, 'wireframe').onChange(v => { + waterMesh.material.wireframe = v; + poolBorder.material.wireframe = v; + }); + gui.add(effectController, 'shadow').onChange(addShadow); + + //const buttonSmooth = { smoothWater: function () {smoothWater();} }; + //gui.add( buttonSmooth, 'smoothWater' ); + + initWater(); + + createducks(); + + valuesChanger(); + + renderer.setAnimationLoop(animate); +} + +function initWater() { + const geometry = new THREE.PlaneGeometry(BOUNDS, BOUNDS, WIDTH - 1, WIDTH - 1); + + const material = new WaterMaterial({ + color: 0x9bd2ec, + metalness: 0.9, + roughness: 0, + transparent: true, + opacity: 0.8, + side: THREE.DoubleSide, + }); + + waterMesh = new THREE.Mesh(geometry, material); + waterMesh.rotation.x = -Math.PI * 0.5; + waterMesh.matrixAutoUpdate = false; + waterMesh.updateMatrix(); + + waterMesh.receiveShadow = true; + waterMesh.castShadow = true; + + scene.add(waterMesh); + + // pool border + const borderGeom = new THREE.TorusGeometry(4.2, 0.1, 12, 4); + borderGeom.rotateX(Math.PI * 0.5); + borderGeom.rotateY(Math.PI * 0.25); + poolBorder = new THREE.Mesh(borderGeom, new THREE.MeshStandardMaterial({ color: 0x908877, roughness: 0.2 })); + scene.add(poolBorder); + poolBorder.receiveShadow = true; + poolBorder.castShadow = true; + + // THREE.Mesh just for mouse raycasting + const geometryRay = new THREE.PlaneGeometry(BOUNDS, BOUNDS, 1, 1); + meshRay = new THREE.Mesh(geometryRay, new THREE.MeshBasicMaterial({ color: 0xffffff, visible: false })); + meshRay.rotation.x = -Math.PI / 2; + meshRay.matrixAutoUpdate = false; + meshRay.updateMatrix(); + scene.add(meshRay); + + // Creates the gpu computation class and sets it up + + gpuCompute = new GPUComputationRenderer(WIDTH, WIDTH, renderer); + + const heightmap0 = gpuCompute.createTexture(); + + fillTexture(heightmap0); + + heightmapVariable = gpuCompute.addVariable('heightmap', shaderChange.heightmap_frag, heightmap0); + + gpuCompute.setVariableDependencies(heightmapVariable, [heightmapVariable]); + + heightmapVariable.material.uniforms['mousePos'] = { value: new THREE.Vector2(10000, 10000) }; + heightmapVariable.material.uniforms['mouseSize'] = { value: 0.2 }; + heightmapVariable.material.uniforms['viscosity'] = { value: 0.93 }; + heightmapVariable.material.uniforms['deep'] = { value: 0.01 }; + heightmapVariable.material.defines.BOUNDS = BOUNDS.toFixed(1); + + const error = gpuCompute.init(); + if (error !== null) console.error(error); + + // Create compute shader to smooth the water surface and velocity + //smoothShader = gpuCompute.createShaderMaterial( document.getElementById( 'smoothFragmentShader' ).textContent, { smoothTexture: { value: null } } ); + + // Create compute shader to read water level + readWaterLevelShader = gpuCompute.createShaderMaterial( + document.getElementById('readWaterLevelFragmentShader').textContent, + { + point1: { value: new THREE.Vector2() }, + levelTexture: { value: null }, + }, + ); + readWaterLevelShader.defines.WIDTH = WIDTH.toFixed(1); + readWaterLevelShader.defines.BOUNDS = BOUNDS.toFixed(1); + + // Create a 4x1 pixel image and a render target (Uint8, 4 channels, 1 byte per channel) to read water height and orientation + readWaterLevelImage = new Uint8Array(4 * 1 * 4); + + readWaterLevelRenderTarget = new THREE.WebGLRenderTarget(4, 1, { + wrapS: THREE.ClampToEdgeWrapping, + wrapT: THREE.ClampToEdgeWrapping, + minFilter: THREE.NearestFilter, + magFilter: THREE.NearestFilter, + format: THREE.RGBAFormat, + type: THREE.UnsignedByteType, + depthBuffer: false, + }); +} + +function fillTexture(texture) { + const waterMaxHeight = 0.1; + + function noise(x, y) { + let multR = waterMaxHeight; + let mult = 0.025; + let r = 0; + for (let i = 0; i < 15; i++) { + r += multR * simplex.noise(x * mult, y * mult); + multR *= 0.53 + 0.025 * i; + mult *= 1.25; + } + + return r; + } + + const pixels = texture.image.data; + + let p = 0; + for (let j = 0; j < WIDTH; j++) { + for (let i = 0; i < WIDTH; i++) { + const x = (i * 128) / WIDTH; + const y = (j * 128) / WIDTH; + + pixels[p + 0] = noise(x, y); + pixels[p + 1] = pixels[p + 0]; + pixels[p + 2] = 0; + pixels[p + 3] = 1; + + p += 4; + } + } +} + +function addShadow(v) { + renderer.shadowMap.enabled = v; + sun.castShadow = v; + + if (v) { + renderer.shadowMap.type = THREE.VSMShadowMap; + const shadow = sun.shadow; + shadow.mapSize.width = shadow.mapSize.height = 2048; + shadow.radius = 2; + shadow.bias = -0.0005; + const shadowCam = shadow.camera, + s = 5; + shadowCam.near = 0.1; + shadowCam.far = 6; + shadowCam.right = shadowCam.top = s; + shadowCam.left = shadowCam.bottom = -s; + } else { + if (sun.shadow) sun.shadow.dispose(); + } + + // debug shadow + //scene.add( new THREE.CameraHelper(shadowCam) ); +} + +// function smoothWater() { + +// const currentRenderTarget = gpuCompute.getCurrentRenderTarget( heightmapVariable ); +// const alternateRenderTarget = gpuCompute.getAlternateRenderTarget( heightmapVariable ); + +// for ( let i = 0; i < 10; i ++ ) { + +// smoothShader.uniforms[ 'smoothTexture' ].value = currentRenderTarget.texture; +// gpuCompute.doRenderTarget( smoothShader, alternateRenderTarget ); + +// smoothShader.uniforms[ 'smoothTexture' ].value = alternateRenderTarget.texture; +// gpuCompute.doRenderTarget( smoothShader, currentRenderTarget ); + +// } + +// } + +function createducks() { + for (let i = 0; i < NUM_DUCK; i++) { + let sphere = duckModel; + if (i < NUM_DUCK - 1) { + sphere = duckModel.clone(); + } + + sphere.position.x = (Math.random() - 0.5) * BOUNDS * 0.7; + sphere.position.z = (Math.random() - 0.5) * BOUNDS * 0.7; + + sphere.userData.velocity = new THREE.Vector3(); + + scene.add(sphere); + + ducks[i] = sphere; + } +} + +function duckDynamics() { + readWaterLevelShader.uniforms['levelTexture'].value = tmpHeightmap; + + for (let i = 0; i < NUM_DUCK; i++) { + const sphere = ducks[i]; + + if (sphere) { + // Read water level and orientation + const u = (0.5 * sphere.position.x) / BOUNDS_HALF + 0.5; + const v = 1 - ((0.5 * sphere.position.z) / BOUNDS_HALF + 0.5); + readWaterLevelShader.uniforms['point1'].value.set(u, v); + gpuCompute.doRenderTarget(readWaterLevelShader, readWaterLevelRenderTarget); + + renderer.readRenderTargetPixels(readWaterLevelRenderTarget, 0, 0, 4, 1, readWaterLevelImage); + const pixels = new Float32Array(readWaterLevelImage.buffer); + + // Get orientation + waterNormal.set(pixels[1], 0, -pixels[2]); + + const pos = sphere.position; + + const startPos = pos.clone(); + + // Set height + pos.y = pixels[0]; + + // Move sphere + waterNormal.multiplyScalar(0.01); + sphere.userData.velocity.add(waterNormal); + sphere.userData.velocity.multiplyScalar(0.998); + pos.add(sphere.userData.velocity); + + const decal = 0.001; + const limit = BOUNDS_HALF - 0.2; + + if (pos.x < -limit) { + pos.x = -limit + decal; + sphere.userData.velocity.x *= -0.3; + } else if (pos.x > limit) { + pos.x = limit - decal; + sphere.userData.velocity.x *= -0.3; + } + + if (pos.z < -limit) { + pos.z = -limit + decal; + sphere.userData.velocity.z *= -0.3; + } else if (pos.z > limit) { + pos.z = limit - decal; + sphere.userData.velocity.z *= -0.3; + } + + // duck orientation test + + const startNormal = new THREE.Vector3(pixels[1], 1, -pixels[2]).normalize(); + + const dir = startPos.sub(pos); + dir.y = 0; + dir.normalize(); + + const yAxis = new THREE.Vector3(0, 1, 0); + const zAxis = new THREE.Vector3(0, 0, -1); + tmpQuatX.setFromUnitVectors(zAxis, dir); + tmpQuatZ.setFromUnitVectors(yAxis, startNormal); + tmpQuat.multiplyQuaternions(tmpQuatZ, tmpQuatX); + sphere.quaternion.slerp(tmpQuat, 0.017); + } + } +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function onPointerDown() { + mousedown = true; +} + +function onPointerUp() { + mousedown = false; + controls.enabled = true; +} + +function onPointerMove(event) { + const dom = renderer.domElement; + mouseCoords.set((event.clientX / dom.clientWidth) * 2 - 1, -(event.clientY / dom.clientHeight) * 2 + 1); +} + +function raycast() { + // Set uniforms: mouse interaction + const uniforms = heightmapVariable.material.uniforms; + if (mousedown) { + raycaster.setFromCamera(mouseCoords, camera); + + const intersects = raycaster.intersectObject(meshRay); + + if (intersects.length > 0) { + const point = intersects[0].point; + uniforms['mousePos'].value.set(point.x, point.z); + if (controls.enabled) controls.enabled = false; + } else { + uniforms['mousePos'].value.set(10000, 10000); + } + } else { + uniforms['mousePos'].value.set(10000, 10000); + } +} + +function animate() { + render(); + stats.update(); +} + +function render() { + raycast(); + + frame++; + + if (frame >= 7 - effectController.speed) { + // Do the gpu computation + gpuCompute.compute(); + tmpHeightmap = gpuCompute.getCurrentRenderTarget(heightmapVariable).texture; + + if (ducksEnabled) duckDynamics(); + + // Get compute output in custom uniform + if (waterMesh) waterMesh.material.heightmap = tmpHeightmap; + + frame = 0; + } + + // Render + renderer.render(scene, camera); +} + +//---------------------- + +class WaterMaterial extends THREE.MeshStandardMaterial { + constructor(parameters) { + super(); + + this.defines = { + STANDARD: '', + USE_UV: '', + WIDTH: WIDTH.toFixed(1), + BOUNDS: BOUNDS.toFixed(1), + }; + + this.extra = {}; + + this.addParameter('heightmap', null); + + this.setValues(parameters); + } + + addParameter(name, value) { + this.extra[name] = value; + Object.defineProperty(this, name, { + get: () => this.extra[name], + set: v => { + this.extra[name] = v; + if (this.userData.shader) this.userData.shader.uniforms[name].value = this.extra[name]; + }, + }); + } + + onBeforeCompile(shader) { + for (const name in this.extra) { + shader.uniforms[name] = { value: this.extra[name] }; + } + + shader.vertexShader = shader.vertexShader.replace('#include ', shaderChange.common); + //shader.vertexShader = 'uniform sampler2D heightmap;\n' + shader.vertexShader; + shader.vertexShader = shader.vertexShader.replace( + '#include ', + shaderChange.beginnormal_vertex, + ); + shader.vertexShader = shader.vertexShader.replace('#include ', shaderChange.begin_vertex); + + this.userData.shader = shader; + } +} + +const shaderChange = { + heightmap_frag: /* glsl */ ` + #include + + uniform vec2 mousePos; + uniform float mouseSize; + uniform float viscosity; + uniform float deep; + + void main() { + + vec2 cellSize = 1.0 / resolution.xy; + + vec2 uv = gl_FragCoord.xy * cellSize; + + // heightmapValue.x == height from previous frame + // heightmapValue.y == height from penultimate frame + // heightmapValue.z, heightmapValue.w not used + vec4 heightmapValue = texture2D( heightmap, uv ); + + // Get neighbours + vec4 north = texture2D( heightmap, uv + vec2( 0.0, cellSize.y ) ); + vec4 south = texture2D( heightmap, uv + vec2( 0.0, - cellSize.y ) ); + vec4 east = texture2D( heightmap, uv + vec2( cellSize.x, 0.0 ) ); + vec4 west = texture2D( heightmap, uv + vec2( - cellSize.x, 0.0 ) ); + + //float newHeight = ( ( north.x + south.x + east.x + west.x ) * 0.5 - heightmapValue.y ) * viscosity; + float newHeight = ( ( north.x + south.x + east.x + west.x ) * 0.5 - (heightmapValue.y) ) * viscosity; + + + // Mouse influence + float mousePhase = clamp( length( ( uv - vec2( 0.5 ) ) * BOUNDS - vec2( mousePos.x, - mousePos.y ) ) * PI / mouseSize, 0.0, PI ); + //newHeight += ( cos( mousePhase ) + 1.0 ) * 0.28 * 10.0; + newHeight -= ( cos( mousePhase ) + 1.0 ) * deep; + + heightmapValue.y = heightmapValue.x; + heightmapValue.x = newHeight; + + gl_FragColor = heightmapValue; + + } + `, + // FOR MATERIAL + common: /* glsl */ ` + #include + uniform sampler2D heightmap; + `, + beginnormal_vertex: /* glsl */ ` + vec2 cellSize = vec2( 1.0 / WIDTH, 1.0 / WIDTH ); + vec3 objectNormal = vec3( + ( texture2D( heightmap, uv + vec2( - cellSize.x, 0 ) ).x - texture2D( heightmap, uv + vec2( cellSize.x, 0 ) ).x ) * WIDTH / BOUNDS, + ( texture2D( heightmap, uv + vec2( 0, - cellSize.y ) ).x - texture2D( heightmap, uv + vec2( 0, cellSize.y ) ).x ) * WIDTH / BOUNDS, + 1.0 ); + #ifdef USE_TANGENT + vec3 objectTangent = vec3( tangent.xyz ); + #endif + `, + begin_vertex: /* glsl */ ` + float heightValue = texture2D( heightmap, uv ).x; + vec3 transformed = vec3( position.x, position.y, heightValue ); + #ifdef USE_ALPHAHASH + vPosition = vec3( position ); + #endif + `, +}; diff --git a/examples-testing/examples/webgl_helpers.ts b/examples-testing/examples/webgl_helpers.ts new file mode 100644 index 000000000..a8c3b9773 --- /dev/null +++ b/examples-testing/examples/webgl_helpers.ts @@ -0,0 +1,117 @@ +import * as THREE from 'three'; + +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +import { VertexNormalsHelper } from 'three/addons/helpers/VertexNormalsHelper.js'; +import { VertexTangentsHelper } from 'three/addons/helpers/VertexTangentsHelper.js'; + +let scene, renderer; +let camera, light; +let vnh; +let vth; + +init(); + +function init() { + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // + + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.z = 400; + + scene = new THREE.Scene(); + + light = new THREE.PointLight(); + light.position.set(200, 100, 150); + scene.add(light); + + scene.add(new THREE.PointLightHelper(light, 15)); + + const gridHelper = new THREE.GridHelper(400, 40, 0x0000ff, 0x808080); + gridHelper.position.y = -150; + gridHelper.position.x = -150; + scene.add(gridHelper); + + const polarGridHelper = new THREE.PolarGridHelper(200, 16, 8, 64, 0x0000ff, 0x808080); + polarGridHelper.position.y = -150; + polarGridHelper.position.x = 200; + scene.add(polarGridHelper); + + const loader = new GLTFLoader(); + loader.load('models/gltf/LeePerrySmith/LeePerrySmith.glb', function (gltf) { + const mesh = gltf.scene.children[0]; + + mesh.geometry.computeTangents(); // generates bad data due to degenerate UVs + + const group = new THREE.Group(); + group.scale.multiplyScalar(50); + scene.add(group); + + // To make sure that the matrixWorld is up to date for the boxhelpers + group.updateMatrixWorld(true); + + group.add(mesh); + + vnh = new VertexNormalsHelper(mesh, 5); + scene.add(vnh); + + vth = new VertexTangentsHelper(mesh, 5); + scene.add(vth); + + scene.add(new THREE.BoxHelper(mesh)); + + const wireframe = new THREE.WireframeGeometry(mesh.geometry); + let line = new THREE.LineSegments(wireframe); + line.material.depthTest = false; + line.material.opacity = 0.25; + line.material.transparent = true; + line.position.x = 4; + group.add(line); + scene.add(new THREE.BoxHelper(line)); + + const edges = new THREE.EdgesGeometry(mesh.geometry); + line = new THREE.LineSegments(edges); + line.material.depthTest = false; + line.material.opacity = 0.25; + line.material.transparent = true; + line.position.x = -4; + group.add(line); + scene.add(new THREE.BoxHelper(line)); + + scene.add(new THREE.BoxHelper(group)); + scene.add(new THREE.BoxHelper(scene)); + }); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + const time = -performance.now() * 0.0003; + + camera.position.x = 400 * Math.cos(time); + camera.position.z = 400 * Math.sin(time); + camera.lookAt(scene.position); + + light.position.x = Math.sin(time * 1.7) * 300; + light.position.y = Math.cos(time * 1.5) * 400; + light.position.z = Math.cos(time * 1.3) * 300; + + if (vnh) vnh.update(); + if (vth) vth.update(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_instancing_dynamic.ts b/examples-testing/examples/webgl_instancing_dynamic.ts new file mode 100644 index 000000000..bc4a88662 --- /dev/null +++ b/examples-testing/examples/webgl_instancing_dynamic.ts @@ -0,0 +1,175 @@ +import * as THREE from 'three'; + +import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; +import TWEEN from 'three/addons/libs/tween.module.js'; + +let camera, scene, renderer, timer, mesh; + +const amount = 100; + +const count = Math.pow(amount, 2); +const dummy = new THREE.Object3D(); + +const seeds = []; +const baseColors = []; + +const color = new THREE.Color(); +const colors = [new THREE.Color(0x00ffff), new THREE.Color(0xffff00), new THREE.Color(0xff00ff)]; +const animation = { t: 0 }; +let currentColorIndex = 0; +let nextColorIndex = 1; + +const maxDistance = 75; +const cameraTarget = new THREE.Vector3(); + +init(); + +function init() { + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.toneMapping = THREE.NeutralToneMapping; + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(10, 10, 10); + camera.lookAt(0, 0, 0); + + const pmremGenerator = new THREE.PMREMGenerator(renderer); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xadd8e6); + scene.environment = pmremGenerator.fromScene(new RoomEnvironment(), 0.04).texture; + + timer = new THREE.Timer(); + timer.connect(document); + + const loader = new THREE.TextureLoader(); + const texture = loader.load('textures/edge3.jpg'); + texture.colorSpace = THREE.SRGBColorSpace; + + const geometry = new THREE.BoxGeometry(); + const material = new THREE.MeshStandardMaterial({ map: texture }); + + mesh = new THREE.InstancedMesh(geometry, material, count); + mesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage); // will be updated every frame + scene.add(mesh); + + let i = 0; + const offset = (amount - 1) / 2; + + for (let x = 0; x < amount; x++) { + for (let z = 0; z < amount; z++) { + dummy.position.set(offset - x, 0, offset - z); + dummy.scale.set(1, 2, 1); + + dummy.updateMatrix(); + + color.setHSL(1, 0.5 + Math.random() * 0.5, 0.5 + Math.random() * 0.5); + baseColors.push(color.getHex()); + + mesh.setMatrixAt(i, dummy.matrix); + mesh.setColorAt(i, color.multiply(colors[0])); + + i++; + + seeds.push(Math.random()); + } + } + + // + + window.addEventListener('resize', onWindowResize); + + setInterval(startTween, 3000); +} + +function startTween() { + // tween for animating color transition + + new TWEEN.Tween(animation) + .to( + { + t: 1, + }, + 2000, + ) + .easing(TWEEN.Easing.Sinusoidal.In) + .onComplete(() => { + animation.t = 0; + + currentColorIndex = nextColorIndex; + nextColorIndex++; + + if (nextColorIndex >= colors.length) nextColorIndex = 0; + }) + .start(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + timer.update(); + + const time = timer.getElapsed(); + + TWEEN.update(); + + // animate camera + + camera.position.x = Math.sin(time / 4) * 10; + camera.position.z = Math.cos(time / 4) * 10; + camera.position.y = 8 + Math.cos(time / 2) * 2; + + cameraTarget.x = Math.sin(time / 4) * -8; + cameraTarget.z = Math.cos(time / 2) * -8; + + camera.lookAt(cameraTarget); + + camera.up.x = Math.sin(time / 400); + + // animate instance positions and colors + + for (let i = 0; i < mesh.count; i++) { + mesh.getMatrixAt(i, dummy.matrix); + dummy.matrix.decompose(dummy.position, dummy.quaternion, dummy.scale); + + dummy.position.y = Math.abs(Math.sin((time + seeds[i]) * 2 + seeds[i])); + + dummy.updateMatrix(); + + mesh.setMatrixAt(i, dummy.matrix); + + // colors + + if (animation.t > 0) { + const currentColor = colors[currentColorIndex]; + const nextColor = colors[nextColorIndex]; + + const f = dummy.position.length() / maxDistance; + + if (f <= animation.t) { + color.set(baseColors[i]).multiply(nextColor); + } else { + color.set(baseColors[i]).multiply(currentColor); + } + + mesh.setColorAt(i, color); + } + } + + mesh.instanceMatrix.needsUpdate = true; + if (animation.t > 0) mesh.instanceColor.needsUpdate = true; + + mesh.computeBoundingSphere(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_instancing_morph.ts b/examples-testing/examples/webgl_instancing_morph.ts new file mode 100644 index 000000000..70e89199c --- /dev/null +++ b/examples-testing/examples/webgl_instancing_morph.ts @@ -0,0 +1,150 @@ +import * as THREE from 'three'; + +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +import Stats from 'three/addons/libs/stats.module.js'; + +let camera, scene, renderer, stats, mesh, mixer, dummy; + +const offset = 5000; + +const timeOffsets = new Float32Array(1024); + +for (let i = 0; i < 1024; i++) { + timeOffsets[i] = Math.random() * 3; +} + +const timer = new THREE.Timer(); +timer.connect(document); + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 100, 10000); + + scene = new THREE.Scene(); + + scene.background = new THREE.Color(0x99ddff); + + scene.fog = new THREE.Fog(0x99ddff, 5000, 10000); + + const light = new THREE.DirectionalLight(0xffffff, 1); + + light.position.set(200, 1000, 50); + + light.castShadow = true; + + light.shadow.camera.left = -5000; + light.shadow.camera.right = 5000; + light.shadow.camera.top = 5000; + light.shadow.camera.bottom = -5000; + light.shadow.camera.far = 2000; + + light.shadow.bias = -0.01; + + light.shadow.camera.updateProjectionMatrix(); + + scene.add(light); + + const hemi = new THREE.HemisphereLight(0x99ddff, 0x669933, 1 / 3); + + scene.add(hemi); + + const ground = new THREE.Mesh( + new THREE.PlaneGeometry(1000000, 1000000), + new THREE.MeshStandardMaterial({ color: 0x669933, depthWrite: true }), + ); + + ground.rotation.x = -Math.PI / 2; + + ground.receiveShadow = true; + + scene.add(ground); + + const loader = new GLTFLoader(); + + loader.load('models/gltf/Horse.glb', function (glb) { + dummy = glb.scene.children[0]; + + mesh = new THREE.InstancedMesh(dummy.geometry, dummy.material, 1024); + + mesh.castShadow = true; + + for (let x = 0, i = 0; x < 32; x++) { + for (let y = 0; y < 32; y++) { + dummy.position.set(offset - 300 * x + 200 * Math.random(), 0, offset - 300 * y); + + dummy.updateMatrix(); + + mesh.setMatrixAt(i, dummy.matrix); + + mesh.setColorAt(i, new THREE.Color(`hsl(${Math.random() * 360}, 50%, 66%)`)); + + i++; + } + } + + scene.add(mesh); + + mixer = new THREE.AnimationMixer(glb.scene); + + const action = mixer.clipAction(glb.animations[0]); + + action.play(); + }); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + renderer.shadowMap.enabled = true; + renderer.shadowMap.type = THREE.VSMShadowMap; + // + + stats = new Stats(); + document.body.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + timer.update(); + + render(); + + stats.update(); +} + +function render() { + const time = timer.getElapsed(); + + const r = 3000; + camera.position.set(Math.sin(time / 10) * r, 1500 + 1000 * Math.cos(time / 5), Math.cos(time / 10) * r); + camera.lookAt(0, 0, 0); + + if (mesh) { + for (let i = 0; i < 1024; i++) { + mixer.setTime(time + timeOffsets[i]); + + mesh.setMorphAt(i, dummy); + } + + mesh.morphTexture.needsUpdate = true; + } + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_instancing_performance.ts b/examples-testing/examples/webgl_instancing_performance.ts new file mode 100644 index 000000000..bf1deabad --- /dev/null +++ b/examples-testing/examples/webgl_instancing_performance.ts @@ -0,0 +1,262 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js'; + +let container, stats, gui, guiStatsEl; +let camera, controls, scene, renderer, material; + +// gui + +const Method = { + INSTANCED: 'INSTANCED', + MERGED: 'MERGED', + NAIVE: 'NAIVE', +}; + +const api = { + method: Method.INSTANCED, + count: 1000, +}; + +// + +init(); +initMesh(); + +// + +function clean() { + const meshes = []; + + scene.traverse(function (object) { + if (object.isMesh) meshes.push(object); + }); + + for (let i = 0; i < meshes.length; i++) { + const mesh = meshes[i]; + mesh.material.dispose(); + mesh.geometry.dispose(); + + scene.remove(mesh); + } +} + +const randomizeMatrix = (function () { + const position = new THREE.Vector3(); + const quaternion = new THREE.Quaternion(); + const scale = new THREE.Vector3(); + + return function (matrix) { + position.x = Math.random() * 40 - 20; + position.y = Math.random() * 40 - 20; + position.z = Math.random() * 40 - 20; + + quaternion.random(); + + scale.x = scale.y = scale.z = Math.random() * 1; + + matrix.compose(position, quaternion, scale); + }; +})(); + +function initMesh() { + clean(); + + // make instances + new THREE.BufferGeometryLoader().setPath('models/json/').load('suzanne_buffergeometry.json', function (geometry) { + material = new THREE.MeshNormalMaterial(); + + geometry.computeVertexNormals(); + + console.time(api.method + ' (build)'); + + switch (api.method) { + case Method.INSTANCED: + makeInstanced(geometry); + break; + + case Method.MERGED: + makeMerged(geometry); + break; + + case Method.NAIVE: + makeNaive(geometry); + break; + } + + console.timeEnd(api.method + ' (build)'); + }); +} + +function makeInstanced(geometry) { + const matrix = new THREE.Matrix4(); + const mesh = new THREE.InstancedMesh(geometry, material, api.count); + + for (let i = 0; i < api.count; i++) { + randomizeMatrix(matrix); + mesh.setMatrixAt(i, matrix); + } + + scene.add(mesh); + + // + + const geometryByteLength = getGeometryByteLength(geometry); + + guiStatsEl.innerHTML = [ + 'GPU draw calls: 1', + 'GPU memory: ' + formatBytes(api.count * 16 + geometryByteLength, 2), + ].join('
'); +} + +function makeMerged(geometry) { + const geometries = []; + const matrix = new THREE.Matrix4(); + + for (let i = 0; i < api.count; i++) { + randomizeMatrix(matrix); + + const instanceGeometry = geometry.clone(); + instanceGeometry.applyMatrix4(matrix); + + geometries.push(instanceGeometry); + } + + const mergedGeometry = BufferGeometryUtils.mergeGeometries(geometries); + + scene.add(new THREE.Mesh(mergedGeometry, material)); + + // + + guiStatsEl.innerHTML = [ + 'GPU draw calls: 1', + 'GPU memory: ' + formatBytes(getGeometryByteLength(mergedGeometry), 2), + ].join('
'); +} + +function makeNaive(geometry) { + const matrix = new THREE.Matrix4(); + + for (let i = 0; i < api.count; i++) { + randomizeMatrix(matrix); + + const mesh = new THREE.Mesh(geometry, material); + mesh.applyMatrix4(matrix); + + scene.add(mesh); + } + + // + + const geometryByteLength = getGeometryByteLength(geometry); + + guiStatsEl.innerHTML = [ + 'GPU draw calls: ' + api.count, + 'GPU memory: ' + formatBytes(api.count * 16 + geometryByteLength, 2), + ].join('
'); +} + +function init() { + const width = window.innerWidth; + const height = window.innerHeight; + + // camera + + camera = new THREE.PerspectiveCamera(70, width / height, 1, 100); + camera.position.z = 30; + + // renderer + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(width, height); + renderer.setAnimationLoop(animate); + container = document.getElementById('container'); + container.appendChild(renderer.domElement); + + // scene + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xffffff); + + // controls + + controls = new OrbitControls(camera, renderer.domElement); + controls.autoRotate = true; + + // stats + + stats = new Stats(); + container.appendChild(stats.dom); + + // gui + + gui = new GUI(); + gui.add(api, 'method', Method).onChange(initMesh); + gui.add(api, 'count', 1, 10000).step(1).onChange(initMesh); + + const perfFolder = gui.addFolder('Performance'); + + guiStatsEl = document.createElement('div'); + guiStatsEl.classList.add('gui-stats'); + + perfFolder.$children.appendChild(guiStatsEl); + perfFolder.open(); + + // listeners + + window.addEventListener('resize', onWindowResize); + + Object.assign(window, { scene }); +} + +// + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); +} + +function animate() { + controls.update(); + + renderer.render(scene, camera); + + stats.update(); +} + +// + +function getGeometryByteLength(geometry) { + let total = 0; + + if (geometry.index) total += geometry.index.array.byteLength; + + for (const name in geometry.attributes) { + total += geometry.attributes[name].array.byteLength; + } + + return total; +} + +// Source: https://stackoverflow.com/a/18650828/1314762 +function formatBytes(bytes, decimals) { + if (bytes === 0) return '0 bytes'; + + const k = 1024; + const dm = decimals < 0 ? 0 : decimals; + const sizes = ['bytes', 'KB', 'MB']; + + const i = Math.floor(Math.log(bytes) / Math.log(k)); + + return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; +} diff --git a/examples-testing/examples/webgl_instancing_raycast.ts b/examples-testing/examples/webgl_instancing_raycast.ts new file mode 100644 index 000000000..371ea070b --- /dev/null +++ b/examples-testing/examples/webgl_instancing_raycast.ts @@ -0,0 +1,116 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let camera, scene, renderer, controls, stats; + +let mesh; +const amount = parseInt(window.location.search.slice(1)) || 10; +const count = Math.pow(amount, 3); + +const raycaster = new THREE.Raycaster(); +const mouse = new THREE.Vector2(1, 1); + +const color = new THREE.Color(); +const white = new THREE.Color().setHex(0xffffff); + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(amount, amount, amount); + camera.lookAt(0, 0, 0); + + scene = new THREE.Scene(); + + const light = new THREE.HemisphereLight(0xffffff, 0x888888, 3); + light.position.set(0, 1, 0); + scene.add(light); + + const geometry = new THREE.IcosahedronGeometry(0.5, 3); + const material = new THREE.MeshPhongMaterial({ color: 0xffffff }); + + mesh = new THREE.InstancedMesh(geometry, material, count); + + let i = 0; + const offset = (amount - 1) / 2; + + const matrix = new THREE.Matrix4(); + + for (let x = 0; x < amount; x++) { + for (let y = 0; y < amount; y++) { + for (let z = 0; z < amount; z++) { + matrix.setPosition(offset - x, offset - y, offset - z); + + mesh.setMatrixAt(i, matrix); + mesh.setColorAt(i, color); + + i++; + } + } + } + + scene.add(mesh); + + // + + const gui = new GUI(); + gui.add(mesh, 'count', 0, count); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + controls.enableZoom = false; + controls.enablePan = false; + + stats = new Stats(); + document.body.appendChild(stats.dom); + + window.addEventListener('resize', onWindowResize); + document.addEventListener('mousemove', onMouseMove); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function onMouseMove(event) { + event.preventDefault(); + + mouse.x = (event.clientX / window.innerWidth) * 2 - 1; + mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; +} + +function animate() { + controls.update(); + + raycaster.setFromCamera(mouse, camera); + + const intersection = raycaster.intersectObject(mesh); + + if (intersection.length > 0) { + const instanceId = intersection[0].instanceId; + + mesh.getColorAt(instanceId, color); + + if (color.equals(white)) { + mesh.setColorAt(instanceId, color.setHex(Math.random() * 0xffffff)); + + mesh.instanceColor.needsUpdate = true; + } + } + + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/webgl_instancing_scatter.ts b/examples-testing/examples/webgl_instancing_scatter.ts new file mode 100644 index 000000000..fc3b9cc9f --- /dev/null +++ b/examples-testing/examples/webgl_instancing_scatter.ts @@ -0,0 +1,257 @@ +import * as THREE from 'three'; + +import { MeshSurfaceSampler } from 'three/addons/math/MeshSurfaceSampler.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import Stats from 'three/addons/libs/stats.module.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let camera, scene, renderer, stats; + +const api = { + count: 2000, + distribution: 'random', + resample: resample, + surfaceColor: 0xfff784, + backgroundColor: 0xe39469, +}; + +let stemMesh, blossomMesh; +let stemGeometry, blossomGeometry; +let stemMaterial, blossomMaterial; + +let sampler; +const count = api.count; +const ages = new Float32Array(count); +const scales = new Float32Array(count); +const dummy = new THREE.Object3D(); + +const _position = new THREE.Vector3(); +const _normal = new THREE.Vector3(); +const _scale = new THREE.Vector3(); + +// let surfaceGeometry = new THREE.BoxGeometry( 10, 10, 10 ).toNonIndexed(); +const surfaceGeometry = new THREE.TorusKnotGeometry(10, 3, 100, 16).toNonIndexed(); +const surfaceMaterial = new THREE.MeshLambertMaterial({ color: api.surfaceColor, wireframe: false }); +const surface = new THREE.Mesh(surfaceGeometry, surfaceMaterial); + +// Source: https://gist.github.com/gre/1650294 +const easeOutCubic = function (t) { + return --t * t * t + 1; +}; + +// Scaling curve causes particles to grow quickly, ease gradually into full scale, then +// disappear quickly. More of the particle's lifetime is spent around full scale. +const scaleCurve = function (t) { + return Math.abs(easeOutCubic((t > 0.5 ? 1 - t : t) * 2)); +}; + +const loader = new GLTFLoader(); + +loader.load('./models/gltf/Flower/Flower.glb', function (gltf) { + const _stemMesh = gltf.scene.getObjectByName('Stem'); + const _blossomMesh = gltf.scene.getObjectByName('Blossom'); + + stemGeometry = _stemMesh.geometry.clone(); + blossomGeometry = _blossomMesh.geometry.clone(); + + const defaultTransform = new THREE.Matrix4() + .makeRotationX(Math.PI) + .multiply(new THREE.Matrix4().makeScale(7, 7, 7)); + + stemGeometry.applyMatrix4(defaultTransform); + blossomGeometry.applyMatrix4(defaultTransform); + + stemMaterial = _stemMesh.material; + blossomMaterial = _blossomMesh.material; + + stemMesh = new THREE.InstancedMesh(stemGeometry, stemMaterial, count); + blossomMesh = new THREE.InstancedMesh(blossomGeometry, blossomMaterial, count); + + // Assign random colors to the blossoms. + const color = new THREE.Color(); + const blossomPalette = [0xf20587, 0xf2d479, 0xf2c879, 0xf2b077, 0xf24405]; + + for (let i = 0; i < count; i++) { + color.setHex(blossomPalette[Math.floor(Math.random() * blossomPalette.length)]); + blossomMesh.setColorAt(i, color); + } + + // Instance matrices will be updated every frame. + stemMesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage); + blossomMesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage); + + resample(); + + init(); +}); + +function init() { + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(25, 25, 25); + camera.lookAt(0, 0, 0); + + // + + scene = new THREE.Scene(); + scene.background = new THREE.Color(api.backgroundColor); + + const pointLight = new THREE.PointLight(0xaa8899, 2.5, 0, 0); + pointLight.position.set(50, -25, 75); + scene.add(pointLight); + + scene.add(new THREE.AmbientLight(0xffffff, 3)); + + // + + scene.add(stemMesh); + scene.add(blossomMesh); + + scene.add(surface); + + // + + const gui = new GUI(); + gui.add(api, 'count', 0, count).onChange(function () { + stemMesh.count = api.count; + blossomMesh.count = api.count; + }); + + // gui.addColor( api, 'backgroundColor' ).onChange( function () { + + // scene.background.setHex( api.backgroundColor ); + + // } ); + + // gui.addColor( api, 'surfaceColor' ).onChange( function () { + + // surfaceMaterial.color.setHex( api.surfaceColor ); + + // } ); + + gui.add(api, 'distribution').options(['random', 'weighted']).onChange(resample); + gui.add(api, 'resample'); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // + + stats = new Stats(); + document.body.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); +} + +function resample() { + const vertexCount = surface.geometry.getAttribute('position').count; + + console.info('Sampling ' + count + ' points from a surface with ' + vertexCount + ' vertices...'); + + // + + console.time('.build()'); + + sampler = new MeshSurfaceSampler(surface).setWeightAttribute(api.distribution === 'weighted' ? 'uv' : null).build(); + + console.timeEnd('.build()'); + + // + + console.time('.sample()'); + + for (let i = 0; i < count; i++) { + ages[i] = Math.random(); + scales[i] = scaleCurve(ages[i]); + + resampleParticle(i); + } + + console.timeEnd('.sample()'); + + stemMesh.instanceMatrix.needsUpdate = true; + blossomMesh.instanceMatrix.needsUpdate = true; +} + +function resampleParticle(i) { + sampler.sample(_position, _normal); + _normal.add(_position); + + dummy.position.copy(_position); + dummy.scale.set(scales[i], scales[i], scales[i]); + dummy.lookAt(_normal); + dummy.updateMatrix(); + + stemMesh.setMatrixAt(i, dummy.matrix); + blossomMesh.setMatrixAt(i, dummy.matrix); +} + +function updateParticle(i) { + // Update lifecycle. + + ages[i] += 0.005; + + if (ages[i] >= 1) { + ages[i] = 0.001; + scales[i] = scaleCurve(ages[i]); + + resampleParticle(i); + + return; + } + + // Update scale. + + const prevScale = scales[i]; + scales[i] = scaleCurve(ages[i]); + _scale.set(scales[i] / prevScale, scales[i] / prevScale, scales[i] / prevScale); + + // Update transform. + + stemMesh.getMatrixAt(i, dummy.matrix); + dummy.matrix.scale(_scale); + stemMesh.setMatrixAt(i, dummy.matrix); + blossomMesh.setMatrixAt(i, dummy.matrix); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + render(); + + stats.update(); +} + +function render() { + if (stemMesh && blossomMesh) { + const time = Date.now() * 0.001; + + scene.rotation.x = Math.sin(time / 4); + scene.rotation.y = Math.sin(time / 2); + + for (let i = 0; i < api.count; i++) { + updateParticle(i); + } + + stemMesh.instanceMatrix.needsUpdate = true; + blossomMesh.instanceMatrix.needsUpdate = true; + + stemMesh.computeBoundingSphere(); + blossomMesh.computeBoundingSphere(); + } + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_interactive_buffergeometry.ts b/examples-testing/examples/webgl_interactive_buffergeometry.ts new file mode 100644 index 000000000..1d6608b13 --- /dev/null +++ b/examples-testing/examples/webgl_interactive_buffergeometry.ts @@ -0,0 +1,244 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +let container, stats; + +let camera, scene, renderer; + +let raycaster, pointer; + +let mesh, line; + +init(); + +function init() { + container = document.getElementById('container'); + + // + + camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 1, 3500); + camera.position.z = 2750; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x050505); + scene.fog = new THREE.Fog(0x050505, 2000, 3500); + + // + + scene.add(new THREE.AmbientLight(0x444444, 3)); + + const light1 = new THREE.DirectionalLight(0xffffff, 1.5); + light1.position.set(1, 1, 1); + scene.add(light1); + + const light2 = new THREE.DirectionalLight(0xffffff, 4.5); + light2.position.set(0, -1, 0); + scene.add(light2); + + // + + const triangles = 5000; + + let geometry = new THREE.BufferGeometry(); + + const positions = new Float32Array(triangles * 3 * 3); + const normals = new Float32Array(triangles * 3 * 3); + const colors = new Float32Array(triangles * 3 * 3); + + const color = new THREE.Color(); + + const n = 800, + n2 = n / 2; // triangles spread in the cube + const d = 120, + d2 = d / 2; // individual triangle size + + const pA = new THREE.Vector3(); + const pB = new THREE.Vector3(); + const pC = new THREE.Vector3(); + + const cb = new THREE.Vector3(); + const ab = new THREE.Vector3(); + + for (let i = 0; i < positions.length; i += 9) { + // positions + + const x = Math.random() * n - n2; + const y = Math.random() * n - n2; + const z = Math.random() * n - n2; + + const ax = x + Math.random() * d - d2; + const ay = y + Math.random() * d - d2; + const az = z + Math.random() * d - d2; + + const bx = x + Math.random() * d - d2; + const by = y + Math.random() * d - d2; + const bz = z + Math.random() * d - d2; + + const cx = x + Math.random() * d - d2; + const cy = y + Math.random() * d - d2; + const cz = z + Math.random() * d - d2; + + positions[i] = ax; + positions[i + 1] = ay; + positions[i + 2] = az; + + positions[i + 3] = bx; + positions[i + 4] = by; + positions[i + 5] = bz; + + positions[i + 6] = cx; + positions[i + 7] = cy; + positions[i + 8] = cz; + + // flat face normals + + pA.set(ax, ay, az); + pB.set(bx, by, bz); + pC.set(cx, cy, cz); + + cb.subVectors(pC, pB); + ab.subVectors(pA, pB); + cb.cross(ab); + + cb.normalize(); + + const nx = cb.x; + const ny = cb.y; + const nz = cb.z; + + normals[i] = nx; + normals[i + 1] = ny; + normals[i + 2] = nz; + + normals[i + 3] = nx; + normals[i + 4] = ny; + normals[i + 5] = nz; + + normals[i + 6] = nx; + normals[i + 7] = ny; + normals[i + 8] = nz; + + // colors + + const vx = x / n + 0.5; + const vy = y / n + 0.5; + const vz = z / n + 0.5; + + color.setRGB(vx, vy, vz); + + colors[i] = color.r; + colors[i + 1] = color.g; + colors[i + 2] = color.b; + + colors[i + 3] = color.r; + colors[i + 4] = color.g; + colors[i + 5] = color.b; + + colors[i + 6] = color.r; + colors[i + 7] = color.g; + colors[i + 8] = color.b; + } + + geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); + geometry.setAttribute('normal', new THREE.BufferAttribute(normals, 3)); + geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3)); + + geometry.computeBoundingSphere(); + + let material = new THREE.MeshPhongMaterial({ + color: 0xaaaaaa, + specular: 0xffffff, + shininess: 250, + side: THREE.DoubleSide, + vertexColors: true, + }); + + mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + // + + raycaster = new THREE.Raycaster(); + + pointer = new THREE.Vector2(); + + geometry = new THREE.BufferGeometry(); + geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(4 * 3), 3)); + + material = new THREE.LineBasicMaterial({ color: 0xffffff, transparent: true }); + + line = new THREE.Line(geometry, material); + scene.add(line); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + // + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); + document.addEventListener('pointermove', onPointerMove); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function onPointerMove(event) { + pointer.x = (event.clientX / window.innerWidth) * 2 - 1; + pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; +} + +// + +function animate() { + render(); + stats.update(); +} + +function render() { + const time = Date.now() * 0.001; + + mesh.rotation.x = time * 0.15; + mesh.rotation.y = time * 0.25; + + raycaster.setFromCamera(pointer, camera); + + const intersects = raycaster.intersectObject(mesh); + + if (intersects.length > 0) { + const intersect = intersects[0]; + const face = intersect.face; + + const linePosition = line.geometry.attributes.position; + const meshPosition = mesh.geometry.attributes.position; + + linePosition.copyAt(0, meshPosition, face.a); + linePosition.copyAt(1, meshPosition, face.b); + linePosition.copyAt(2, meshPosition, face.c); + linePosition.copyAt(3, meshPosition, face.a); + + mesh.updateMatrix(); + + line.geometry.applyMatrix4(mesh.matrix); + + line.visible = true; + } else { + line.visible = false; + } + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_interactive_cubes.ts b/examples-testing/examples/webgl_interactive_cubes.ts new file mode 100644 index 000000000..adfcfddf8 --- /dev/null +++ b/examples-testing/examples/webgl_interactive_cubes.ts @@ -0,0 +1,114 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +let stats; +let camera, scene, raycaster, renderer; + +let INTERSECTED; +let theta = 0; + +const pointer = new THREE.Vector2(); +const radius = 5; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 100); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xf0f0f0); + + const light = new THREE.DirectionalLight(0xffffff, 3); + light.position.set(1, 1, 1).normalize(); + scene.add(light); + + const geometry = new THREE.BoxGeometry(); + + for (let i = 0; i < 2000; i++) { + const object = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({ color: Math.random() * 0xffffff })); + + object.position.x = Math.random() * 40 - 20; + object.position.y = Math.random() * 40 - 20; + object.position.z = Math.random() * 40 - 20; + + object.rotation.x = Math.random() * 2 * Math.PI; + object.rotation.y = Math.random() * 2 * Math.PI; + object.rotation.z = Math.random() * 2 * Math.PI; + + object.scale.x = Math.random() + 0.5; + object.scale.y = Math.random() + 0.5; + object.scale.z = Math.random() + 0.5; + + scene.add(object); + } + + raycaster = new THREE.Raycaster(); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + stats = new Stats(); + document.body.appendChild(stats.dom); + + document.addEventListener('mousemove', onPointerMove); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function onPointerMove(event) { + pointer.x = (event.clientX / window.innerWidth) * 2 - 1; + pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; +} + +// + +function animate() { + render(); + stats.update(); +} + +function render() { + theta += 0.1; + + camera.position.x = radius * Math.sin(THREE.MathUtils.degToRad(theta)); + camera.position.y = radius * Math.sin(THREE.MathUtils.degToRad(theta)); + camera.position.z = radius * Math.cos(THREE.MathUtils.degToRad(theta)); + camera.lookAt(scene.position); + + camera.updateMatrixWorld(); + + // find intersections + + raycaster.setFromCamera(pointer, camera); + + const intersects = raycaster.intersectObjects(scene.children, false); + + if (intersects.length > 0) { + if (INTERSECTED != intersects[0].object) { + if (INTERSECTED) INTERSECTED.material.emissive.setHex(INTERSECTED.currentHex); + + INTERSECTED = intersects[0].object; + INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex(); + INTERSECTED.material.emissive.setHex(0xff0000); + } + } else { + if (INTERSECTED) INTERSECTED.material.emissive.setHex(INTERSECTED.currentHex); + + INTERSECTED = null; + } + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_interactive_cubes_gpu.ts b/examples-testing/examples/webgl_interactive_cubes_gpu.ts new file mode 100644 index 000000000..5b19d2085 --- /dev/null +++ b/examples-testing/examples/webgl_interactive_cubes_gpu.ts @@ -0,0 +1,231 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { TrackballControls } from 'three/addons/controls/TrackballControls.js'; +import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js'; + +let container, stats; +let camera, controls, scene, renderer; +let pickingTexture, pickingScene; +let highlightBox; + +const pickingData = []; + +const pointer = new THREE.Vector2(); +const offset = new THREE.Vector3(10, 10, 10); +const clearColor = new THREE.Color(); + +init(); + +function init() { + container = document.getElementById('container'); + + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 10000); + camera.position.z = 1000; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xffffff); + + scene.add(new THREE.AmbientLight(0xcccccc)); + + const light = new THREE.DirectionalLight(0xffffff, 3); + light.position.set(0, 500, 2000); + scene.add(light); + + const defaultMaterial = new THREE.MeshPhongMaterial({ + color: 0xffffff, + flatShading: true, + vertexColors: true, + shininess: 0, + }); + + // set up the picking texture to use a 32 bit integer so we can write and read integer ids from it + pickingScene = new THREE.Scene(); + pickingTexture = new THREE.WebGLRenderTarget(1, 1, { + type: THREE.IntType, + format: THREE.RGBAIntegerFormat, + internalFormat: 'RGBA32I', + }); + const pickingMaterial = new THREE.ShaderMaterial({ + glslVersion: THREE.GLSL3, + + vertexShader: /* glsl */ ` + attribute int id; + flat varying int vid; + void main() { + + vid = id; + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + + } + `, + + fragmentShader: /* glsl */ ` + layout(location = 0) out int out_id; + flat varying int vid; + + void main() { + + out_id = vid; + + } + `, + }); + + function applyId(geometry, id) { + const position = geometry.attributes.position; + const array = new Int16Array(position.count); + array.fill(id); + + const bufferAttribute = new THREE.Int16BufferAttribute(array, 1, false); + bufferAttribute.gpuType = THREE.IntType; + geometry.setAttribute('id', bufferAttribute); + } + + function applyVertexColors(geometry, color) { + const position = geometry.attributes.position; + const colors = []; + + for (let i = 0; i < position.count; i++) { + colors.push(color.r, color.g, color.b); + } + + geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); + } + + const geometries = []; + const matrix = new THREE.Matrix4(); + const quaternion = new THREE.Quaternion(); + const color = new THREE.Color(); + + for (let i = 0; i < 5000; i++) { + const geometry = new THREE.BoxGeometry(); + + const position = new THREE.Vector3(); + position.x = Math.random() * 10000 - 5000; + position.y = Math.random() * 6000 - 3000; + position.z = Math.random() * 8000 - 4000; + + const rotation = new THREE.Euler(); + rotation.x = Math.random() * 2 * Math.PI; + rotation.y = Math.random() * 2 * Math.PI; + rotation.z = Math.random() * 2 * Math.PI; + + const scale = new THREE.Vector3(); + scale.x = Math.random() * 200 + 100; + scale.y = Math.random() * 200 + 100; + scale.z = Math.random() * 200 + 100; + + quaternion.setFromEuler(rotation); + matrix.compose(position, quaternion, scale); + + geometry.applyMatrix4(matrix); + + // give the geometry's vertices a random color to be displayed and an integer + // identifier as a vertex attribute so boxes can be identified after being merged. + applyVertexColors(geometry, color.setHex(Math.random() * 0xffffff)); + applyId(geometry, i); + + geometries.push(geometry); + + pickingData[i] = { + position: position, + rotation: rotation, + scale: scale, + }; + } + + const mergedGeometry = BufferGeometryUtils.mergeGeometries(geometries); + scene.add(new THREE.Mesh(mergedGeometry, defaultMaterial)); + pickingScene.add(new THREE.Mesh(mergedGeometry, pickingMaterial)); + + highlightBox = new THREE.Mesh(new THREE.BoxGeometry(), new THREE.MeshLambertMaterial({ color: 0xffff00 })); + scene.add(highlightBox); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + controls = new TrackballControls(camera, renderer.domElement); + controls.rotateSpeed = 1.0; + controls.zoomSpeed = 1.2; + controls.panSpeed = 0.8; + controls.noZoom = false; + controls.noPan = false; + controls.staticMoving = true; + controls.dynamicDampingFactor = 0.3; + + stats = new Stats(); + container.appendChild(stats.dom); + + renderer.domElement.addEventListener('pointermove', onPointerMove); +} + +// + +function onPointerMove(e) { + pointer.x = e.clientX; + pointer.y = e.clientY; +} + +function animate() { + render(); + stats.update(); +} + +function pick() { + // render the picking scene off-screen + // set the view offset to represent just a single pixel under the mouse + const dpr = window.devicePixelRatio; + camera.setViewOffset( + renderer.domElement.width, + renderer.domElement.height, + Math.floor(pointer.x * dpr), + Math.floor(pointer.y * dpr), + 1, + 1, + ); + + // render the scene + renderer.setRenderTarget(pickingTexture); + + // clear the background to - 1 meaning no item was hit + clearColor.setRGB(-1, -1, -1); + renderer.setClearColor(clearColor); + renderer.render(pickingScene, camera); + + // Restore active render target to canvas + renderer.setRenderTarget(null); + + // clear the view offset so rendering returns to normal + camera.clearViewOffset(); + + // create buffer for reading single pixel + const pixelBuffer = new Int32Array(4); + + // read the pixel + renderer.readRenderTargetPixelsAsync(pickingTexture, 0, 0, 1, 1, pixelBuffer).then(() => { + const id = pixelBuffer[0]; + if (id !== -1) { + // move our highlightBox so that it surrounds the picked object + const data = pickingData[id]; + highlightBox.position.copy(data.position); + highlightBox.rotation.copy(data.rotation); + highlightBox.scale.copy(data.scale).add(offset); + highlightBox.visible = true; + } else { + highlightBox.visible = false; + } + }); +} + +function render() { + controls.update(); + + pick(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_interactive_cubes_ortho.ts b/examples-testing/examples/webgl_interactive_cubes_ortho.ts new file mode 100644 index 000000000..520674b5f --- /dev/null +++ b/examples-testing/examples/webgl_interactive_cubes_ortho.ts @@ -0,0 +1,129 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +let stats; +let camera, scene, raycaster, renderer; + +let theta = 0; +let INTERSECTED; + +const pointer = new THREE.Vector2(); +const radius = 25; +const frustumSize = 50; + +init(); + +function init() { + const aspect = window.innerWidth / window.innerHeight; + camera = new THREE.OrthographicCamera( + (frustumSize * aspect) / -2, + (frustumSize * aspect) / 2, + frustumSize / 2, + frustumSize / -2, + 0.1, + 100, + ); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xf0f0f0); + + const light = new THREE.DirectionalLight(0xffffff, 3); + light.position.set(1, 1, 1).normalize(); + scene.add(light); + + const geometry = new THREE.BoxGeometry(); + + for (let i = 0; i < 2000; i++) { + const object = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({ color: Math.random() * 0xffffff })); + + object.position.x = Math.random() * 40 - 20; + object.position.y = Math.random() * 40 - 20; + object.position.z = Math.random() * 40 - 20; + + object.rotation.x = Math.random() * 2 * Math.PI; + object.rotation.y = Math.random() * 2 * Math.PI; + object.rotation.z = Math.random() * 2 * Math.PI; + + object.scale.x = Math.random() + 0.5; + object.scale.y = Math.random() + 0.5; + object.scale.z = Math.random() + 0.5; + + scene.add(object); + } + + raycaster = new THREE.Raycaster(); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + stats = new Stats(); + document.body.appendChild(stats.dom); + + document.addEventListener('pointermove', onPointerMove); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + const aspect = window.innerWidth / window.innerHeight; + + camera.left = (-frustumSize * aspect) / 2; + camera.right = (frustumSize * aspect) / 2; + camera.top = frustumSize / 2; + camera.bottom = -frustumSize / 2; + + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function onPointerMove(event) { + pointer.x = (event.clientX / window.innerWidth) * 2 - 1; + pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; +} + +// + +function animate() { + render(); + stats.update(); +} + +function render() { + theta += 0.1; + + camera.position.x = radius * Math.sin(THREE.MathUtils.degToRad(theta)); + camera.position.y = radius * Math.sin(THREE.MathUtils.degToRad(theta)); + camera.position.z = radius * Math.cos(THREE.MathUtils.degToRad(theta)); + camera.lookAt(scene.position); + + camera.updateMatrixWorld(); + + // find intersections + + raycaster.setFromCamera(pointer, camera); + + const intersects = raycaster.intersectObjects(scene.children, false); + + if (intersects.length > 0) { + if (INTERSECTED != intersects[0].object) { + if (INTERSECTED) INTERSECTED.material.emissive.setHex(INTERSECTED.currentHex); + + INTERSECTED = intersects[0].object; + INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex(); + INTERSECTED.material.emissive.setHex(0xff0000); + } + } else { + if (INTERSECTED) INTERSECTED.material.emissive.setHex(INTERSECTED.currentHex); + + INTERSECTED = null; + } + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_interactive_lines.ts b/examples-testing/examples/webgl_interactive_lines.ts new file mode 100644 index 000000000..b137c5501 --- /dev/null +++ b/examples-testing/examples/webgl_interactive_lines.ts @@ -0,0 +1,160 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +let container, stats; +let camera, scene, raycaster, renderer, parentTransform, sphereInter; + +const pointer = new THREE.Vector2(); +const radius = 100; +let theta = 0; + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + const info = document.createElement('div'); + info.style.position = 'absolute'; + info.style.top = '10px'; + info.style.width = '100%'; + info.style.textAlign = 'center'; + info.innerHTML = + 'three.js webgl - interactive lines'; + container.appendChild(info); + + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 10000); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xf0f0f0); + + const geometry = new THREE.SphereGeometry(5); + const material = new THREE.MeshBasicMaterial({ color: 0xff0000 }); + + sphereInter = new THREE.Mesh(geometry, material); + sphereInter.visible = false; + scene.add(sphereInter); + + const lineGeometry = new THREE.BufferGeometry(); + const points = []; + + const point = new THREE.Vector3(); + const direction = new THREE.Vector3(); + + for (let i = 0; i < 50; i++) { + direction.x += Math.random() - 0.5; + direction.y += Math.random() - 0.5; + direction.z += Math.random() - 0.5; + direction.normalize().multiplyScalar(10); + + point.add(direction); + points.push(point.x, point.y, point.z); + } + + lineGeometry.setAttribute('position', new THREE.Float32BufferAttribute(points, 3)); + + parentTransform = new THREE.Object3D(); + parentTransform.position.x = Math.random() * 40 - 20; + parentTransform.position.y = Math.random() * 40 - 20; + parentTransform.position.z = Math.random() * 40 - 20; + + parentTransform.rotation.x = Math.random() * 2 * Math.PI; + parentTransform.rotation.y = Math.random() * 2 * Math.PI; + parentTransform.rotation.z = Math.random() * 2 * Math.PI; + + parentTransform.scale.x = Math.random() + 0.5; + parentTransform.scale.y = Math.random() + 0.5; + parentTransform.scale.z = Math.random() + 0.5; + + for (let i = 0; i < 50; i++) { + let object; + + const lineMaterial = new THREE.LineBasicMaterial({ color: Math.random() * 0xffffff }); + + if (Math.random() > 0.5) { + object = new THREE.Line(lineGeometry, lineMaterial); + } else { + object = new THREE.LineSegments(lineGeometry, lineMaterial); + } + + object.position.x = Math.random() * 400 - 200; + object.position.y = Math.random() * 400 - 200; + object.position.z = Math.random() * 400 - 200; + + object.rotation.x = Math.random() * 2 * Math.PI; + object.rotation.y = Math.random() * 2 * Math.PI; + object.rotation.z = Math.random() * 2 * Math.PI; + + object.scale.x = Math.random() + 0.5; + object.scale.y = Math.random() + 0.5; + object.scale.z = Math.random() + 0.5; + + parentTransform.add(object); + } + + scene.add(parentTransform); + + raycaster = new THREE.Raycaster(); + raycaster.params.Line.threshold = 3; + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + stats = new Stats(); + container.appendChild(stats.dom); + + document.addEventListener('pointermove', onPointerMove); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function onPointerMove(event) { + pointer.x = (event.clientX / window.innerWidth) * 2 - 1; + pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; +} + +// + +function animate() { + render(); + stats.update(); +} + +function render() { + theta += 0.1; + + camera.position.x = radius * Math.sin(THREE.MathUtils.degToRad(theta)); + camera.position.y = radius * Math.sin(THREE.MathUtils.degToRad(theta)); + camera.position.z = radius * Math.cos(THREE.MathUtils.degToRad(theta)); + camera.lookAt(scene.position); + + camera.updateMatrixWorld(); + + // find intersections + + raycaster.setFromCamera(pointer, camera); + + const intersects = raycaster.intersectObjects(parentTransform.children, true); + + if (intersects.length > 0) { + sphereInter.visible = true; + sphereInter.position.copy(intersects[0].point); + } else { + sphereInter.visible = false; + } + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_interactive_points.ts b/examples-testing/examples/webgl_interactive_points.ts new file mode 100644 index 000000000..b6be0df05 --- /dev/null +++ b/examples-testing/examples/webgl_interactive_points.ts @@ -0,0 +1,143 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js'; + +let renderer, scene, camera, stats; + +let particles; + +const PARTICLE_SIZE = 20; + +let raycaster, intersects; +let pointer, INTERSECTED; + +init(); + +function init() { + const container = document.getElementById('container'); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000); + camera.position.z = 250; + + // + + let boxGeometry = new THREE.BoxGeometry(200, 200, 200, 16, 16, 16); + + // if normal and uv attributes are not removed, mergeVertices() can't consolidate identical vertices with different normal/uv data + + boxGeometry.deleteAttribute('normal'); + boxGeometry.deleteAttribute('uv'); + + boxGeometry = BufferGeometryUtils.mergeVertices(boxGeometry); + + // + + const positionAttribute = boxGeometry.getAttribute('position'); + + const colors = []; + const sizes = []; + + const color = new THREE.Color(); + + for (let i = 0, l = positionAttribute.count; i < l; i++) { + color.setHSL(0.01 + 0.1 * (i / l), 1.0, 0.5); + color.toArray(colors, i * 3); + + sizes[i] = PARTICLE_SIZE * 0.5; + } + + const geometry = new THREE.BufferGeometry(); + geometry.setAttribute('position', positionAttribute); + geometry.setAttribute('customColor', new THREE.Float32BufferAttribute(colors, 3)); + geometry.setAttribute('size', new THREE.Float32BufferAttribute(sizes, 1)); + + // + + const material = new THREE.ShaderMaterial({ + uniforms: { + color: { value: new THREE.Color(0xffffff) }, + pointTexture: { value: new THREE.TextureLoader().load('textures/sprites/disc.png') }, + alphaTest: { value: 0.9 }, + }, + vertexShader: document.getElementById('vertexshader').textContent, + fragmentShader: document.getElementById('fragmentshader').textContent, + }); + + // + + particles = new THREE.Points(geometry, material); + scene.add(particles); + + // + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + // + + raycaster = new THREE.Raycaster(); + pointer = new THREE.Vector2(); + + // + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); + document.addEventListener('pointermove', onPointerMove); +} + +function onPointerMove(event) { + pointer.x = (event.clientX / window.innerWidth) * 2 - 1; + pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + render(); + stats.update(); +} + +function render() { + particles.rotation.x += 0.0005; + particles.rotation.y += 0.001; + + const geometry = particles.geometry; + const attributes = geometry.attributes; + + raycaster.setFromCamera(pointer, camera); + + intersects = raycaster.intersectObject(particles); + + if (intersects.length > 0) { + if (INTERSECTED != intersects[0].index) { + attributes.size.array[INTERSECTED] = PARTICLE_SIZE; + + INTERSECTED = intersects[0].index; + + attributes.size.array[INTERSECTED] = PARTICLE_SIZE * 1.25; + attributes.size.needsUpdate = true; + } + } else if (INTERSECTED !== null) { + attributes.size.array[INTERSECTED] = PARTICLE_SIZE; + attributes.size.needsUpdate = true; + INTERSECTED = null; + } + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_interactive_raycasting_points.ts b/examples-testing/examples/webgl_interactive_raycasting_points.ts new file mode 100644 index 000000000..50b8d1e1d --- /dev/null +++ b/examples-testing/examples/webgl_interactive_raycasting_points.ts @@ -0,0 +1,223 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +let renderer, scene, camera, stats; +let pointclouds; +let raycaster; +let intersection = null; +let spheresIndex = 0; +let timer; +let toggle = 0; + +const pointer = new THREE.Vector2(); +const spheres = []; + +const threshold = 0.1; +const pointSize = 0.05; +const width = 80; +const length = 160; +const rotateY = new THREE.Matrix4().makeRotationY(0.005); + +init(); + +function generatePointCloudGeometry(color, width, length) { + const geometry = new THREE.BufferGeometry(); + const numPoints = width * length; + + const positions = new Float32Array(numPoints * 3); + const colors = new Float32Array(numPoints * 3); + + let k = 0; + + for (let i = 0; i < width; i++) { + for (let j = 0; j < length; j++) { + const u = i / width; + const v = j / length; + const x = u - 0.5; + const y = (Math.cos(u * Math.PI * 4) + Math.sin(v * Math.PI * 8)) / 20; + const z = v - 0.5; + + positions[3 * k] = x; + positions[3 * k + 1] = y; + positions[3 * k + 2] = z; + + const intensity = (y + 0.1) * 5; + colors[3 * k] = color.r * intensity; + colors[3 * k + 1] = color.g * intensity; + colors[3 * k + 2] = color.b * intensity; + + k++; + } + } + + geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); + geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3)); + geometry.computeBoundingBox(); + + return geometry; +} + +function generatePointcloud(color, width, length) { + const geometry = generatePointCloudGeometry(color, width, length); + const material = new THREE.PointsMaterial({ size: pointSize, vertexColors: true }); + + return new THREE.Points(geometry, material); +} + +function generateIndexedPointcloud(color, width, length) { + const geometry = generatePointCloudGeometry(color, width, length); + const numPoints = width * length; + const indices = new Uint16Array(numPoints); + + let k = 0; + + for (let i = 0; i < width; i++) { + for (let j = 0; j < length; j++) { + indices[k] = k; + k++; + } + } + + geometry.setIndex(new THREE.BufferAttribute(indices, 1)); + + const material = new THREE.PointsMaterial({ size: pointSize, vertexColors: true }); + + return new THREE.Points(geometry, material); +} + +function generateIndexedWithOffsetPointcloud(color, width, length) { + const geometry = generatePointCloudGeometry(color, width, length); + const numPoints = width * length; + const indices = new Uint16Array(numPoints); + + let k = 0; + + for (let i = 0; i < width; i++) { + for (let j = 0; j < length; j++) { + indices[k] = k; + k++; + } + } + + geometry.setIndex(new THREE.BufferAttribute(indices, 1)); + geometry.addGroup(0, indices.length); + + const material = new THREE.PointsMaterial({ size: pointSize, vertexColors: true }); + + return new THREE.Points(geometry, material); +} + +function init() { + const container = document.getElementById('container'); + + scene = new THREE.Scene(); + + timer = new THREE.Timer(); + timer.connect(document); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000); + camera.position.set(10, 10, 10); + camera.lookAt(scene.position); + camera.updateMatrix(); + + // + + const pcBuffer = generatePointcloud(new THREE.Color(1, 0, 0), width, length); + pcBuffer.scale.set(5, 10, 10); + pcBuffer.position.set(-5, 0, 0); + scene.add(pcBuffer); + + const pcIndexed = generateIndexedPointcloud(new THREE.Color(0, 1, 0), width, length); + pcIndexed.scale.set(5, 10, 10); + pcIndexed.position.set(0, 0, 0); + scene.add(pcIndexed); + + const pcIndexedOffset = generateIndexedWithOffsetPointcloud(new THREE.Color(0, 1, 1), width, length); + pcIndexedOffset.scale.set(5, 10, 10); + pcIndexedOffset.position.set(5, 0, 0); + scene.add(pcIndexedOffset); + + pointclouds = [pcBuffer, pcIndexed, pcIndexedOffset]; + + // + + const sphereGeometry = new THREE.SphereGeometry(0.1, 32, 32); + const sphereMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 }); + + for (let i = 0; i < 40; i++) { + const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial); + scene.add(sphere); + spheres.push(sphere); + } + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + // + + raycaster = new THREE.Raycaster(); + raycaster.params.Points.threshold = threshold; + + // + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); + document.addEventListener('pointermove', onPointerMove); +} + +function onPointerMove(event) { + pointer.x = (event.clientX / window.innerWidth) * 2 - 1; + pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + render(); + stats.update(); +} + +function render() { + camera.applyMatrix4(rotateY); + camera.updateMatrixWorld(); + + raycaster.setFromCamera(pointer, camera); + + const intersections = raycaster.intersectObjects(pointclouds, false); + intersection = intersections.length > 0 ? intersections[0] : null; + + if (toggle > 0.02 && intersection !== null) { + spheres[spheresIndex].position.copy(intersection.point); + spheres[spheresIndex].scale.set(1, 1, 1); + spheresIndex = (spheresIndex + 1) % spheres.length; + + toggle = 0; + } + + for (let i = 0; i < spheres.length; i++) { + const sphere = spheres[i]; + sphere.scale.multiplyScalar(0.98); + sphere.scale.clampScalar(0.01, 1); + } + + toggle += timer.getDelta(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_interactive_voxelpainter.ts b/examples-testing/examples/webgl_interactive_voxelpainter.ts new file mode 100644 index 000000000..48b16f3b7 --- /dev/null +++ b/examples-testing/examples/webgl_interactive_voxelpainter.ts @@ -0,0 +1,158 @@ +import * as THREE from 'three'; + +let camera, scene, renderer; +let plane; +let pointer, + raycaster, + isShiftDown = false; + +let rollOverMesh, rollOverMaterial; +let cubeGeo, cubeMaterial; + +const objects = []; + +init(); +render(); + +function init() { + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000); + camera.position.set(500, 800, 1300); + camera.lookAt(0, 0, 0); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xf0f0f0); + + // roll-over helpers + + const rollOverGeo = new THREE.BoxGeometry(50, 50, 50); + rollOverMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000, opacity: 0.5, transparent: true }); + rollOverMesh = new THREE.Mesh(rollOverGeo, rollOverMaterial); + scene.add(rollOverMesh); + + // cubes + + const map = new THREE.TextureLoader().load('textures/square-outline-textured.png'); + map.colorSpace = THREE.SRGBColorSpace; + cubeGeo = new THREE.BoxGeometry(50, 50, 50); + cubeMaterial = new THREE.MeshLambertMaterial({ color: 0xfeb74c, map: map }); + + // grid + + const gridHelper = new THREE.GridHelper(1000, 20); + scene.add(gridHelper); + + // + + raycaster = new THREE.Raycaster(); + pointer = new THREE.Vector2(); + + const geometry = new THREE.PlaneGeometry(1000, 1000); + geometry.rotateX(-Math.PI / 2); + + plane = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ visible: false })); + scene.add(plane); + + objects.push(plane); + + // lights + + const ambientLight = new THREE.AmbientLight(0x606060, 3); + scene.add(ambientLight); + + const directionalLight = new THREE.DirectionalLight(0xffffff, 3); + directionalLight.position.set(1, 0.75, 0.5).normalize(); + scene.add(directionalLight); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + document.body.appendChild(renderer.domElement); + + document.addEventListener('pointermove', onPointerMove); + document.addEventListener('pointerdown', onPointerDown); + document.addEventListener('keydown', onDocumentKeyDown); + document.addEventListener('keyup', onDocumentKeyUp); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + render(); +} + +function onPointerMove(event) { + pointer.set((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1); + + raycaster.setFromCamera(pointer, camera); + + const intersects = raycaster.intersectObjects(objects, false); + + if (intersects.length > 0) { + const intersect = intersects[0]; + + rollOverMesh.position.copy(intersect.point).add(intersect.face.normal); + rollOverMesh.position.divideScalar(50).floor().multiplyScalar(50).addScalar(25); + + render(); + } +} + +function onPointerDown(event) { + pointer.set((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1); + + raycaster.setFromCamera(pointer, camera); + + const intersects = raycaster.intersectObjects(objects, false); + + if (intersects.length > 0) { + const intersect = intersects[0]; + + // delete cube + + if (isShiftDown) { + if (intersect.object !== plane) { + scene.remove(intersect.object); + + objects.splice(objects.indexOf(intersect.object), 1); + } + + // create cube + } else { + const voxel = new THREE.Mesh(cubeGeo, cubeMaterial); + voxel.position.copy(intersect.point).add(intersect.face.normal); + voxel.position.divideScalar(50).floor().multiplyScalar(50).addScalar(25); + scene.add(voxel); + + objects.push(voxel); + } + + render(); + } +} + +function onDocumentKeyDown(event) { + switch (event.keyCode) { + case 16: + isShiftDown = true; + break; + } +} + +function onDocumentKeyUp(event) { + switch (event.keyCode) { + case 16: + isShiftDown = false; + break; + } +} + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_lensflares.ts b/examples-testing/examples/webgl_lensflares.ts new file mode 100644 index 000000000..0a55bec42 --- /dev/null +++ b/examples-testing/examples/webgl_lensflares.ts @@ -0,0 +1,140 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { FlyControls } from 'three/addons/controls/FlyControls.js'; +import { Lensflare, LensflareElement } from 'three/addons/objects/Lensflare.js'; + +let container, stats; + +let camera, scene, renderer; +let controls; + +const timer = new THREE.Timer(); +timer.connect(document); + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + // camera + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 15000); + camera.position.z = 250; + + // scene + + scene = new THREE.Scene(); + scene.background = new THREE.Color().setHSL(0.51, 0.4, 0.01, THREE.SRGBColorSpace); + scene.fog = new THREE.Fog(scene.background, 3500, 15000); + + // world + + const s = 250; + + const geometry = new THREE.BoxGeometry(s, s, s); + const material = new THREE.MeshPhongMaterial({ color: 0xffffff, specular: 0xffffff, shininess: 50 }); + + for (let i = 0; i < 3000; i++) { + const mesh = new THREE.Mesh(geometry, material); + + mesh.position.x = 8000 * (2.0 * Math.random() - 1.0); + mesh.position.y = 8000 * (2.0 * Math.random() - 1.0); + mesh.position.z = 8000 * (2.0 * Math.random() - 1.0); + + mesh.rotation.x = Math.random() * Math.PI; + mesh.rotation.y = Math.random() * Math.PI; + mesh.rotation.z = Math.random() * Math.PI; + + mesh.matrixAutoUpdate = false; + mesh.updateMatrix(); + + scene.add(mesh); + } + + // lights + + const dirLight = new THREE.DirectionalLight(0xffffff, 0.15); + dirLight.position.set(0, -1, 0).normalize(); + dirLight.color.setHSL(0.1, 0.7, 0.5); + scene.add(dirLight); + + // lensflares + const textureLoader = new THREE.TextureLoader(); + + const textureFlare0 = textureLoader.load('textures/lensflare/lensflare0.png'); + const textureFlare3 = textureLoader.load('textures/lensflare/lensflare3.png'); + + addLight(0.55, 0.9, 0.5, 5000, 0, -1000); + addLight(0.08, 0.8, 0.5, 0, 0, -1000); + addLight(0.995, 0.5, 0.9, 5000, 5000, -1000); + + function addLight(h, s, l, x, y, z) { + const light = new THREE.PointLight(0xffffff, 1.5, 2000, 0); + light.color.setHSL(h, s, l); + light.position.set(x, y, z); + scene.add(light); + + const lensflare = new Lensflare(); + lensflare.addElement(new LensflareElement(textureFlare0, 700, 0, light.color)); + lensflare.addElement(new LensflareElement(textureFlare3, 60, 0.6)); + lensflare.addElement(new LensflareElement(textureFlare3, 70, 0.7)); + lensflare.addElement(new LensflareElement(textureFlare3, 120, 0.9)); + lensflare.addElement(new LensflareElement(textureFlare3, 70, 1)); + light.add(lensflare); + } + + // renderer + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + // + + controls = new FlyControls(camera, renderer.domElement); + + controls.movementSpeed = 2500; + controls.domElement = container; + controls.rollSpeed = Math.PI / 6; + controls.autoForward = false; + controls.dragToLook = false; + + // stats + + stats = new Stats(); + container.appendChild(stats.dom); + + // events + + window.addEventListener('resize', onWindowResize); +} + +// + +function onWindowResize() { + renderer.setSize(window.innerWidth, window.innerHeight); + + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); +} + +// + +function animate() { + timer.update(); + + render(); + stats.update(); +} + +function render() { + const delta = timer.getDelta(); + + controls.update(delta); + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_lightprobe.ts b/examples-testing/examples/webgl_lightprobe.ts new file mode 100644 index 000000000..58f021e6d --- /dev/null +++ b/examples-testing/examples/webgl_lightprobe.ts @@ -0,0 +1,142 @@ +import * as THREE from 'three'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { LightProbeGenerator } from 'three/addons/lights/LightProbeGenerator.js'; + +import { LightProbeHelper } from 'three/addons/helpers/LightProbeHelper.js'; + +let mesh, renderer, scene, camera; + +let gui; + +let lightProbe; +let directionalLight; + +// linear color space +const API = { + lightProbeIntensity: 1.0, + directionalLightIntensity: 0.6, + envMapIntensity: 1, +}; + +init(); + +function init() { + // renderer + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + document.body.appendChild(renderer.domElement); + + // tone mapping + renderer.toneMapping = THREE.NoToneMapping; + + // scene + scene = new THREE.Scene(); + + // camera + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(0, 0, 30); + + // controls + const controls = new OrbitControls(camera, renderer.domElement); + controls.addEventListener('change', render); + controls.minDistance = 10; + controls.maxDistance = 50; + controls.enablePan = false; + + // probe + lightProbe = new THREE.LightProbe(); + scene.add(lightProbe); + + // light + directionalLight = new THREE.DirectionalLight(0xffffff, API.directionalLightIntensity); + directionalLight.position.set(10, 10, 10); + scene.add(directionalLight); + + // envmap + const genCubeUrls = function (prefix, postfix) { + return [ + prefix + 'px' + postfix, + prefix + 'nx' + postfix, + prefix + 'py' + postfix, + prefix + 'ny' + postfix, + prefix + 'pz' + postfix, + prefix + 'nz' + postfix, + ]; + }; + + const urls = genCubeUrls('textures/cube/pisa/', '.png'); + + new THREE.CubeTextureLoader().load(urls, function (cubeTexture) { + scene.background = cubeTexture; + + lightProbe.copy(LightProbeGenerator.fromCubeTexture(cubeTexture)); + lightProbe.intensity = API.lightProbeIntensity; + lightProbe.position.set(-10, 0, 0); // position not used in scene lighting calculations (helper honors the position, however) + + const geometry = new THREE.SphereGeometry(5, 64, 32); + //const geometry = new THREE.TorusKnotGeometry( 4, 1.5, 256, 32, 2, 3 ); + + const material = new THREE.MeshStandardMaterial({ + color: 0xffffff, + metalness: 0, + roughness: 0, + envMap: cubeTexture, + envMapIntensity: API.envMapIntensity, + }); + + // mesh + mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + // helper + const helper = new LightProbeHelper(lightProbe, 1); + scene.add(helper); + + render(); + }); + + // gui + gui = new GUI({ title: 'Intensity' }); + + gui.add(API, 'lightProbeIntensity', 0, 1, 0.02) + .name('light probe') + .onChange(function () { + lightProbe.intensity = API.lightProbeIntensity; + render(); + }); + + gui.add(API, 'directionalLightIntensity', 0, 1, 0.02) + .name('directional light') + .onChange(function () { + directionalLight.intensity = API.directionalLightIntensity; + render(); + }); + + gui.add(API, 'envMapIntensity', 0, 1, 0.02) + .name('envMap') + .onChange(function () { + mesh.material.envMapIntensity = API.envMapIntensity; + render(); + }); + + // listener + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + renderer.setSize(window.innerWidth, window.innerHeight); + + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + render(); +} + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_lightprobe_cubecamera.ts b/examples-testing/examples/webgl_lightprobe_cubecamera.ts new file mode 100644 index 000000000..65425d4e7 --- /dev/null +++ b/examples-testing/examples/webgl_lightprobe_cubecamera.ts @@ -0,0 +1,85 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { LightProbeHelper } from 'three/addons/helpers/LightProbeHelper.js'; +import { LightProbeGenerator } from 'three/addons/lights/LightProbeGenerator.js'; + +let renderer, scene, camera, cubeCamera; + +let lightProbe; + +init(); + +function init() { + // renderer + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + document.body.appendChild(renderer.domElement); + + // scene + scene = new THREE.Scene(); + + // camera + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(0, 0, 30); + + const cubeRenderTarget = new THREE.WebGLCubeRenderTarget(256); + + cubeCamera = new THREE.CubeCamera(1, 1000, cubeRenderTarget); + + // controls + const controls = new OrbitControls(camera, renderer.domElement); + controls.addEventListener('change', render); + controls.minDistance = 10; + controls.maxDistance = 50; + controls.enablePan = false; + + // probe + lightProbe = new THREE.LightProbe(); + scene.add(lightProbe); + + // envmap + const genCubeUrls = function (prefix, postfix) { + return [ + prefix + 'px' + postfix, + prefix + 'nx' + postfix, + prefix + 'py' + postfix, + prefix + 'ny' + postfix, + prefix + 'pz' + postfix, + prefix + 'nz' + postfix, + ]; + }; + + const urls = genCubeUrls('textures/cube/pisa/', '.png'); + + new THREE.CubeTextureLoader().load(urls, async function (cubeTexture) { + scene.background = cubeTexture; + + cubeCamera.update(renderer, scene); + + const probe = await LightProbeGenerator.fromCubeRenderTarget(renderer, cubeRenderTarget); + + lightProbe.copy(probe); + + scene.add(new LightProbeHelper(lightProbe, 5)); + + render(); + }); + + // listener + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + renderer.setSize(window.innerWidth, window.innerHeight); + + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + render(); +} + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_lights_hemisphere.ts b/examples-testing/examples/webgl_lights_hemisphere.ts new file mode 100644 index 000000000..2910b15de --- /dev/null +++ b/examples-testing/examples/webgl_lights_hemisphere.ts @@ -0,0 +1,191 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +let camera, scene, renderer; +const mixers = []; +let stats; + +const timer = new THREE.Timer(); +timer.connect(document); + +init(); + +function init() { + const container = document.getElementById('container'); + + camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 5000); + camera.position.set(0, 0, 250); + + scene = new THREE.Scene(); + scene.background = new THREE.Color().setHSL(0.6, 0, 1); + scene.fog = new THREE.Fog(scene.background, 1, 5000); + + // LIGHTS + + const hemiLight = new THREE.HemisphereLight(0xffffff, 0xffffff, 2); + hemiLight.color.setHSL(0.6, 1, 0.6); + hemiLight.groundColor.setHSL(0.095, 1, 0.75); + hemiLight.position.set(0, 50, 0); + scene.add(hemiLight); + + const hemiLightHelper = new THREE.HemisphereLightHelper(hemiLight, 10); + scene.add(hemiLightHelper); + + // + + const dirLight = new THREE.DirectionalLight(0xffffff, 3); + dirLight.color.setHSL(0.1, 1, 0.95); + dirLight.position.set(-1, 1.75, 1); + dirLight.position.multiplyScalar(30); + scene.add(dirLight); + + dirLight.castShadow = true; + + dirLight.shadow.mapSize.width = 2048; + dirLight.shadow.mapSize.height = 2048; + + const d = 50; + + dirLight.shadow.camera.left = -d; + dirLight.shadow.camera.right = d; + dirLight.shadow.camera.top = d; + dirLight.shadow.camera.bottom = -d; + + dirLight.shadow.camera.far = 3500; + dirLight.shadow.bias = -0.0001; + + const dirLightHelper = new THREE.DirectionalLightHelper(dirLight, 10); + scene.add(dirLightHelper); + + // GROUND + + const groundGeo = new THREE.PlaneGeometry(10000, 10000); + const groundMat = new THREE.MeshLambertMaterial({ color: 0xffffff }); + groundMat.color.setHSL(0.095, 1, 0.75); + + const ground = new THREE.Mesh(groundGeo, groundMat); + ground.position.y = -33; + ground.rotation.x = -Math.PI / 2; + ground.receiveShadow = true; + scene.add(ground); + + // SKYDOME + + const vertexShader = document.getElementById('vertexShader').textContent; + const fragmentShader = document.getElementById('fragmentShader').textContent; + const uniforms = { + topColor: { value: new THREE.Color(0x0077ff) }, + bottomColor: { value: new THREE.Color(0xffffff) }, + offset: { value: 33 }, + exponent: { value: 0.6 }, + }; + uniforms['topColor'].value.copy(hemiLight.color); + + scene.fog.color.copy(uniforms['bottomColor'].value); + + const skyGeo = new THREE.SphereGeometry(4000, 32, 15); + const skyMat = new THREE.ShaderMaterial({ + uniforms: uniforms, + vertexShader: vertexShader, + fragmentShader: fragmentShader, + side: THREE.BackSide, + }); + + const sky = new THREE.Mesh(skyGeo, skyMat); + scene.add(sky); + + // MODEL + + const loader = new GLTFLoader(); + + loader.load('models/gltf/Flamingo.glb', function (gltf) { + const mesh = gltf.scene.children[0]; + + const s = 0.35; + mesh.scale.set(s, s, s); + mesh.position.y = 15; + mesh.rotation.y = -1; + + mesh.castShadow = true; + mesh.receiveShadow = true; + + scene.add(mesh); + + const mixer = new THREE.AnimationMixer(mesh); + mixer.clipAction(gltf.animations[0]).setDuration(1).play(); + mixers.push(mixer); + }); + + // RENDERER + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + renderer.shadowMap.enabled = true; + + // STATS + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + const params = { + toggleHemisphereLight: function () { + hemiLight.visible = !hemiLight.visible; + hemiLightHelper.visible = !hemiLightHelper.visible; + }, + toggleDirectionalLight: function () { + dirLight.visible = !dirLight.visible; + dirLightHelper.visible = !dirLightHelper.visible; + }, + shadowIntensity: 1, + }; + + const gui = new GUI(); + + gui.add(params, 'toggleHemisphereLight').name('toggle hemisphere light'); + gui.add(params, 'toggleDirectionalLight').name('toggle directional light'); + gui.add(params, 'shadowIntensity', 0, 1) + .name('shadow intensity') + .onChange(value => { + dirLight.shadow.intensity = value; + }); + gui.open(); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + timer.update(); + + render(); + stats.update(); +} + +function render() { + const delta = timer.getDelta(); + + for (let i = 0; i < mixers.length; i++) { + mixers[i].update(delta); + } + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_lights_physical.ts b/examples-testing/examples/webgl_lights_physical.ts new file mode 100644 index 000000000..707ef200e --- /dev/null +++ b/examples-testing/examples/webgl_lights_physical.ts @@ -0,0 +1,237 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let camera, scene, renderer, bulbLight, bulbMat, hemiLight, stats; +let ballMat, cubeMat, floorMat; + +let previousShadowMap = false; + +// ref for lumens: http://www.power-sure.com/lumens.htm +const bulbLuminousPowers = { + '110000 lm (1000W)': 110000, + '3500 lm (300W)': 3500, + '1700 lm (100W)': 1700, + '800 lm (60W)': 800, + '400 lm (40W)': 400, + '180 lm (25W)': 180, + '20 lm (4W)': 20, + Off: 0, +}; + +// ref for solar irradiances: https://en.wikipedia.org/wiki/Lux +const hemiLuminousIrradiances = { + '0.0001 lx (Moonless Night)': 0.0001, + '0.002 lx (Night Airglow)': 0.002, + '0.5 lx (Full Moon)': 0.5, + '3.4 lx (City Twilight)': 3.4, + '50 lx (Living Room)': 50, + '100 lx (Very Overcast)': 100, + '350 lx (Office Room)': 350, + '400 lx (Sunrise/Sunset)': 400, + '1000 lx (Overcast)': 1000, + '18000 lx (Daylight)': 18000, + '50000 lx (Direct Sun)': 50000, +}; + +const params = { + shadows: true, + exposure: 0.68, + bulbPower: Object.keys(bulbLuminousPowers)[4], + hemiIrradiance: Object.keys(hemiLuminousIrradiances)[0], +}; + +init(); + +function init() { + const container = document.getElementById('container'); + + stats = new Stats(); + container.appendChild(stats.dom); + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.x = -4; + camera.position.z = 4; + camera.position.y = 2; + + scene = new THREE.Scene(); + + const bulbGeometry = new THREE.SphereGeometry(0.02, 16, 8); + bulbLight = new THREE.PointLight(0xffee88, 1, 100, 2); + + bulbMat = new THREE.MeshStandardMaterial({ + emissive: 0xffffee, + emissiveIntensity: 1, + color: 0x000000, + }); + bulbLight.add(new THREE.Mesh(bulbGeometry, bulbMat)); + bulbLight.position.set(0, 2, 0); + bulbLight.castShadow = true; + scene.add(bulbLight); + + hemiLight = new THREE.HemisphereLight(0xddeeff, 0x0f0e0d, 0.02); + scene.add(hemiLight); + + floorMat = new THREE.MeshStandardMaterial({ + roughness: 0.8, + color: 0xffffff, + metalness: 0.2, + bumpScale: 1, + }); + const textureLoader = new THREE.TextureLoader(); + textureLoader.load('textures/hardwood2_diffuse.jpg', function (map) { + map.wrapS = THREE.RepeatWrapping; + map.wrapT = THREE.RepeatWrapping; + map.anisotropy = 4; + map.repeat.set(10, 24); + map.colorSpace = THREE.SRGBColorSpace; + floorMat.map = map; + floorMat.needsUpdate = true; + }); + textureLoader.load('textures/hardwood2_bump.jpg', function (map) { + map.wrapS = THREE.RepeatWrapping; + map.wrapT = THREE.RepeatWrapping; + map.anisotropy = 4; + map.repeat.set(10, 24); + floorMat.bumpMap = map; + floorMat.needsUpdate = true; + }); + textureLoader.load('textures/hardwood2_roughness.jpg', function (map) { + map.wrapS = THREE.RepeatWrapping; + map.wrapT = THREE.RepeatWrapping; + map.anisotropy = 4; + map.repeat.set(10, 24); + floorMat.roughnessMap = map; + floorMat.needsUpdate = true; + }); + + cubeMat = new THREE.MeshStandardMaterial({ + roughness: 0.7, + color: 0xffffff, + bumpScale: 1, + metalness: 0.2, + }); + textureLoader.load('textures/brick_diffuse.jpg', function (map) { + map.wrapS = THREE.RepeatWrapping; + map.wrapT = THREE.RepeatWrapping; + map.anisotropy = 4; + map.repeat.set(1, 1); + map.colorSpace = THREE.SRGBColorSpace; + cubeMat.map = map; + cubeMat.needsUpdate = true; + }); + textureLoader.load('textures/brick_bump.jpg', function (map) { + map.wrapS = THREE.RepeatWrapping; + map.wrapT = THREE.RepeatWrapping; + map.anisotropy = 4; + map.repeat.set(1, 1); + cubeMat.bumpMap = map; + cubeMat.needsUpdate = true; + }); + + ballMat = new THREE.MeshStandardMaterial({ + color: 0xffffff, + roughness: 0.5, + metalness: 1.0, + }); + textureLoader.load('textures/planets/earth_atmos_2048.jpg', function (map) { + map.anisotropy = 4; + map.colorSpace = THREE.SRGBColorSpace; + ballMat.map = map; + ballMat.needsUpdate = true; + }); + textureLoader.load('textures/planets/earth_specular_2048.jpg', function (map) { + map.anisotropy = 4; + map.colorSpace = THREE.SRGBColorSpace; + ballMat.metalnessMap = map; + ballMat.needsUpdate = true; + }); + + const floorGeometry = new THREE.PlaneGeometry(20, 20); + const floorMesh = new THREE.Mesh(floorGeometry, floorMat); + floorMesh.receiveShadow = true; + floorMesh.rotation.x = -Math.PI / 2.0; + scene.add(floorMesh); + + const ballGeometry = new THREE.SphereGeometry(0.25, 32, 32); + const ballMesh = new THREE.Mesh(ballGeometry, ballMat); + ballMesh.position.set(1, 0.25, 1); + ballMesh.rotation.y = Math.PI; + ballMesh.castShadow = true; + scene.add(ballMesh); + + const boxGeometry = new THREE.BoxGeometry(0.5, 0.5, 0.5); + const boxMesh = new THREE.Mesh(boxGeometry, cubeMat); + boxMesh.position.set(-0.5, 0.25, -1); + boxMesh.castShadow = true; + scene.add(boxMesh); + + const boxMesh2 = new THREE.Mesh(boxGeometry, cubeMat); + boxMesh2.position.set(0, 0.25, -5); + boxMesh2.castShadow = true; + scene.add(boxMesh2); + + const boxMesh3 = new THREE.Mesh(boxGeometry, cubeMat); + boxMesh3.position.set(7, 0.25, 0); + boxMesh3.castShadow = true; + scene.add(boxMesh3); + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.shadowMap.enabled = true; + renderer.toneMapping = THREE.ReinhardToneMapping; + container.appendChild(renderer.domElement); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 1; + controls.maxDistance = 20; + + window.addEventListener('resize', onWindowResize); + + const gui = new GUI(); + + gui.add(params, 'hemiIrradiance', Object.keys(hemiLuminousIrradiances)); + gui.add(params, 'bulbPower', Object.keys(bulbLuminousPowers)); + gui.add(params, 'exposure', 0, 1); + gui.add(params, 'shadows'); + gui.open(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + renderer.toneMappingExposure = Math.pow(params.exposure, 5.0); // to allow for very bright scenes. + renderer.shadowMap.enabled = params.shadows; + bulbLight.castShadow = params.shadows; + + if (params.shadows !== previousShadowMap) { + ballMat.needsUpdate = true; + cubeMat.needsUpdate = true; + floorMat.needsUpdate = true; + previousShadowMap = params.shadows; + } + + bulbLight.power = bulbLuminousPowers[params.bulbPower]; + bulbMat.emissiveIntensity = bulbLight.intensity / Math.pow(0.02, 2.0); // convert from intensity to irradiance at bulb surface + + hemiLight.intensity = hemiLuminousIrradiances[params.hemiIrradiance]; + const time = Date.now() * 0.0005; + + bulbLight.position.y = Math.cos(time) * 0.75 + 1.25; + + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/webgl_lights_rectarealight.ts b/examples-testing/examples/webgl_lights_rectarealight.ts new file mode 100644 index 000000000..621d183d8 --- /dev/null +++ b/examples-testing/examples/webgl_lights_rectarealight.ts @@ -0,0 +1,110 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { RectAreaLightHelper } from 'three/addons/helpers/RectAreaLightHelper.js'; +import { RectAreaLightUniformsLib } from 'three/addons/lights/RectAreaLightUniformsLib.js'; + +let renderer, scene, camera; +let rectLight1, rectLight2, rectLight3; +let timer, stats; + +init(); + +function init() { + timer = new THREE.Timer(); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animation); + document.body.appendChild(renderer.domElement); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(0, 5, -15); + + scene = new THREE.Scene(); + + RectAreaLightUniformsLib.init(); + + rectLight1 = new THREE.RectAreaLight(0xff0000, 5, 4, 10); + rectLight1.position.set(-5, 6, 5); + scene.add(rectLight1); + + rectLight2 = new THREE.RectAreaLight(0x00ff00, 5, 4, 10); + rectLight2.position.set(0, 6, 5); + scene.add(rectLight2); + + rectLight3 = new THREE.RectAreaLight(0x0000ff, 5, 4, 10); + rectLight3.position.set(5, 6, 5); + scene.add(rectLight3); + + scene.add(new RectAreaLightHelper(rectLight1)); + scene.add(new RectAreaLightHelper(rectLight2)); + scene.add(new RectAreaLightHelper(rectLight3)); + + const geoFloor = new THREE.BoxGeometry(2000, 0.1, 2000); + const matStdFloor = new THREE.MeshStandardMaterial({ color: 0x444444 }); + matStdFloor.roughnessMap = createCheckerTexture(400); + const mshStdFloor = new THREE.Mesh(geoFloor, matStdFloor); + scene.add(mshStdFloor); + + const geoKnot = new THREE.TorusKnotGeometry(1.5, 0.5, 200, 16); + const matKnot = new THREE.MeshStandardMaterial({ color: 0xffffff, roughness: 0, metalness: 0 }); + const meshKnot = new THREE.Mesh(geoKnot, matKnot); + meshKnot.position.set(0, 5.5, 0); + scene.add(meshKnot); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.target.copy(meshKnot.position); + controls.update(); + + // + + window.addEventListener('resize', onWindowResize); + + stats = new Stats(); + document.body.appendChild(stats.dom); +} + +function createCheckerTexture(repeat = 1) { + const canvas = document.createElement('canvas'); + canvas.width = 2; + canvas.height = 2; + + const ctx = canvas.getContext('2d'); + ctx.fillStyle = '#000'; + ctx.fillRect(0, 0, 2, 2); + ctx.fillStyle = '#fff'; + ctx.fillRect(0, 0, 1, 1); + ctx.fillRect(1, 1, 1, 1); + + const texture = new THREE.CanvasTexture(canvas); + texture.repeat.set(repeat, repeat); + texture.magFilter = THREE.NearestFilter; + texture.wrapS = THREE.RepeatWrapping; + texture.wrapT = THREE.RepeatWrapping; + + return texture; +} + +function onWindowResize() { + renderer.setSize(window.innerWidth, window.innerHeight); + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); +} + +function animation() { + timer.update(); + + const delta = timer.getDelta(); + + rectLight1.rotation.y += -delta; + rectLight2.rotation.y += delta * 0.5; + rectLight3.rotation.y += delta; + + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/webgl_lights_spotlight.ts b/examples-testing/examples/webgl_lights_spotlight.ts new file mode 100644 index 000000000..78fec9d4e --- /dev/null +++ b/examples-testing/examples/webgl_lights_spotlight.ts @@ -0,0 +1,202 @@ +import * as THREE from 'three'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { PLYLoader } from 'three/addons/loaders/PLYLoader.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let renderer, scene, camera; + +let spotLight; + +init(); + +function init() { + // Renderer + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + renderer.toneMapping = THREE.NeutralToneMapping; + renderer.toneMappingExposure = 1; + + renderer.shadowMap.enabled = true; + renderer.shadowMap.type = THREE.PCFShadowMap; + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(7, 4, 1); + + // Controls + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 2; + controls.maxDistance = 10; + controls.maxPolarAngle = Math.PI / 2; + controls.target.set(0, 1, 0); + controls.update(); + + // Textures + + const loader = new THREE.TextureLoader().setPath('textures/'); + const filenames = ['disturb.jpg', 'colors.png', 'uv_grid_opengl.jpg']; + + const textures = { none: null }; + + for (let i = 0; i < filenames.length; i++) { + const filename = filenames[i]; + + const texture = loader.load(filename); + texture.minFilter = THREE.LinearFilter; + texture.magFilter = THREE.LinearFilter; + texture.generateMipmaps = false; + texture.colorSpace = THREE.SRGBColorSpace; + + textures[filename] = texture; + } + + // Lights + + const ambient = new THREE.HemisphereLight(0xffffff, 0x8d8d8d, 0.25); + scene.add(ambient); + + spotLight = new THREE.SpotLight(0xffffff, 100); + spotLight.name = 'spotLight'; + spotLight.map = textures['disturb.jpg']; + spotLight.position.set(2.5, 5, 2.5); + spotLight.angle = Math.PI / 6; + spotLight.penumbra = 1; + spotLight.decay = 2; + spotLight.distance = 0; + + spotLight.castShadow = true; + spotLight.shadow.mapSize.width = 1024; + spotLight.shadow.mapSize.height = 1024; + spotLight.shadow.camera.near = 2; + spotLight.shadow.camera.far = 10; + spotLight.shadow.focus = 1; + spotLight.shadow.bias = -0.003; + spotLight.shadow.intensity = 1; + scene.add(spotLight); + + spotLight.lightHelper = new THREE.SpotLightHelper(spotLight); + spotLight.lightHelper.visible = false; + scene.add(spotLight.lightHelper); + + spotLight.shadowCameraHelper = new THREE.CameraHelper(spotLight.shadow.camera); // colored lines + spotLight.shadowCameraHelper.visible = false; + scene.add(spotLight.shadowCameraHelper); + + // + + const geometry = new THREE.PlaneGeometry(10, 10); + const material = new THREE.MeshLambertMaterial({ color: 0xbcbcbc }); + + const mesh = new THREE.Mesh(geometry, material); + mesh.position.set(0, -1, 0); + mesh.rotation.x = -Math.PI / 2; + mesh.receiveShadow = true; + scene.add(mesh); + + // Model + + new PLYLoader().load('models/ply/binary/Lucy100k.ply', function (geometry) { + geometry.scale(0.0024, 0.0024, 0.0024); + geometry.computeVertexNormals(); + + const material = new THREE.MeshLambertMaterial(); + + const mesh = new THREE.Mesh(geometry, material); + mesh.rotation.y = -Math.PI / 2; + mesh.position.y = 0.8; + mesh.castShadow = true; + mesh.receiveShadow = true; + scene.add(mesh); + }); + + // + + window.addEventListener('resize', onWindowResize); + + // GUI + + const gui = new GUI(); + + const params = { + map: textures['disturb.jpg'], + color: spotLight.color.getHex(), + intensity: spotLight.intensity, + distance: spotLight.distance, + angle: spotLight.angle, + penumbra: spotLight.penumbra, + decay: spotLight.decay, + focus: spotLight.shadow.focus, + shadowIntensity: spotLight.shadow.intensity, + helpers: false, + }; + + gui.add(params, 'map', textures).onChange(function (val) { + spotLight.map = val; + }); + + gui.addColor(params, 'color').onChange(function (val) { + spotLight.color.setHex(val); + }); + + gui.add(params, 'intensity', 0, 500).onChange(function (val) { + spotLight.intensity = val; + }); + + gui.add(params, 'distance', 0, 20).onChange(function (val) { + spotLight.distance = val; + }); + + gui.add(params, 'angle', 0, Math.PI / 3).onChange(function (val) { + spotLight.angle = val; + }); + + gui.add(params, 'penumbra', 0, 1).onChange(function (val) { + spotLight.penumbra = val; + }); + + gui.add(params, 'decay', 1, 2).onChange(function (val) { + spotLight.decay = val; + }); + + gui.add(params, 'focus', 0, 1).onChange(function (val) { + spotLight.shadow.focus = val; + }); + + gui.add(params, 'shadowIntensity', 0, 1).onChange(function (val) { + spotLight.shadow.intensity = val; + }); + + gui.add(params, 'helpers').onChange(function (val) { + spotLight.lightHelper.visible = val; + spotLight.shadowCameraHelper.visible = val; + }); + + gui.open(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + const time = performance.now() / 3000; + + spotLight.position.x = Math.cos(time) * 2.5; + spotLight.position.z = Math.sin(time) * 2.5; + + spotLight.lightHelper.update(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_lights_spotlights.ts b/examples-testing/examples/webgl_lights_spotlights.ts new file mode 100644 index 000000000..be8dfd69d --- /dev/null +++ b/examples-testing/examples/webgl_lights_spotlights.ts @@ -0,0 +1,133 @@ +import * as THREE from 'three'; +import TWEEN from 'three/addons/libs/tween.module.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +const renderer = new THREE.WebGLRenderer({ antialias: true }); +renderer.setPixelRatio(window.devicePixelRatio); +renderer.setSize(window.innerWidth, window.innerHeight); +renderer.setAnimationLoop(animate); + +const camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 0.1, 100); + +const controls = new OrbitControls(camera, renderer.domElement); + +const scene = new THREE.Scene(); + +const matFloor = new THREE.MeshPhongMaterial({ color: 0x808080 }); +const matBox = new THREE.MeshPhongMaterial({ color: 0xaaaaaa }); + +const geoFloor = new THREE.PlaneGeometry(100, 100); +const geoBox = new THREE.BoxGeometry(0.3, 0.1, 0.2); + +const mshFloor = new THREE.Mesh(geoFloor, matFloor); +mshFloor.rotation.x = -Math.PI * 0.5; +const mshBox = new THREE.Mesh(geoBox, matBox); + +const ambient = new THREE.AmbientLight(0x444444); + +const spotLight1 = createSpotlight(0xff7f00); +const spotLight2 = createSpotlight(0x00ff7f); +const spotLight3 = createSpotlight(0x7f00ff); + +let lightHelper1, lightHelper2, lightHelper3; + +function init() { + renderer.shadowMap.enabled = true; + renderer.shadowMap.type = THREE.PCFShadowMap; + + camera.position.set(4.6, 2.2, -2.1); + + spotLight1.position.set(1.5, 4, 4.5); + spotLight2.position.set(0, 4, 3.5); + spotLight3.position.set(-1.5, 4, 4.5); + + lightHelper1 = new THREE.SpotLightHelper(spotLight1); + lightHelper2 = new THREE.SpotLightHelper(spotLight2); + lightHelper3 = new THREE.SpotLightHelper(spotLight3); + + mshFloor.receiveShadow = true; + mshFloor.position.set(0, -0.05, 0); + + mshBox.castShadow = true; + mshBox.receiveShadow = true; + mshBox.position.set(0, 0.5, 0); + + scene.add(mshFloor); + scene.add(mshBox); + scene.add(ambient); + scene.add(spotLight1, spotLight2, spotLight3); + scene.add(lightHelper1, lightHelper2, lightHelper3); + + document.body.appendChild(renderer.domElement); + window.addEventListener('resize', onWindowResize); + + controls.target.set(0, 0.5, 0); + controls.maxPolarAngle = Math.PI / 2; + controls.minDistance = 1; + controls.maxDistance = 10; + controls.update(); +} + +function createSpotlight(color) { + const newObj = new THREE.SpotLight(color, 10); + + newObj.castShadow = true; + newObj.angle = 0.3; + newObj.penumbra = 0.2; + newObj.decay = 2; + newObj.distance = 50; + + return newObj; +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function tween(light) { + new TWEEN.Tween(light) + .to( + { + angle: Math.random() * 0.7 + 0.1, + penumbra: Math.random() + 1, + }, + Math.random() * 3000 + 2000, + ) + .easing(TWEEN.Easing.Quadratic.Out) + .start(); + + new TWEEN.Tween(light.position) + .to( + { + x: Math.random() * 3 - 1.5, + y: Math.random() * 1 + 1.5, + z: Math.random() * 3 - 1.5, + }, + Math.random() * 3000 + 2000, + ) + .easing(TWEEN.Easing.Quadratic.Out) + .start(); +} + +function updateTweens() { + tween(spotLight1); + tween(spotLight2); + tween(spotLight3); + + setTimeout(updateTweens, 5000); +} + +function animate() { + TWEEN.update(); + + if (lightHelper1) lightHelper1.update(); + if (lightHelper2) lightHelper2.update(); + if (lightHelper3) lightHelper3.update(); + + renderer.render(scene, camera); +} + +init(); +updateTweens(); diff --git a/examples-testing/examples/webgl_lines_colors.ts b/examples-testing/examples/webgl_lines_colors.ts new file mode 100644 index 000000000..9da19ee2e --- /dev/null +++ b/examples-testing/examples/webgl_lines_colors.ts @@ -0,0 +1,181 @@ +import * as THREE from 'three'; + +import * as GeometryUtils from 'three/addons/utils/GeometryUtils.js'; + +let mouseX = 0, + mouseY = 0; + +let windowHalfX = window.innerWidth / 2; +let windowHalfY = window.innerHeight / 2; + +let camera, scene, renderer; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(33, window.innerWidth / window.innerHeight, 1, 10000); + camera.position.z = 1000; + + scene = new THREE.Scene(); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // + + const hilbertPoints = GeometryUtils.hilbert3D(new THREE.Vector3(0, 0, 0), 200.0, 1, 0, 1, 2, 3, 4, 5, 6, 7); + + const geometry1 = new THREE.BufferGeometry(); + const geometry2 = new THREE.BufferGeometry(); + const geometry3 = new THREE.BufferGeometry(); + + const subdivisions = 6; + + let vertices = []; + let colors1 = []; + let colors2 = []; + let colors3 = []; + + const point = new THREE.Vector3(); + const color = new THREE.Color(); + + const spline = new THREE.CatmullRomCurve3(hilbertPoints); + + for (let i = 0; i < hilbertPoints.length * subdivisions; i++) { + const t = i / (hilbertPoints.length * subdivisions); + spline.getPoint(t, point); + + vertices.push(point.x, point.y, point.z); + + color.setHSL(0.6, 1.0, Math.max(0, -point.x / 200) + 0.5, THREE.SRGBColorSpace); + colors1.push(color.r, color.g, color.b); + + color.setHSL(0.9, 1.0, Math.max(0, -point.y / 200) + 0.5, THREE.SRGBColorSpace); + colors2.push(color.r, color.g, color.b); + + color.setHSL(i / (hilbertPoints.length * subdivisions), 1.0, 0.5, THREE.SRGBColorSpace); + colors3.push(color.r, color.g, color.b); + } + + geometry1.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); + geometry2.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); + geometry3.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); + + geometry1.setAttribute('color', new THREE.Float32BufferAttribute(colors1, 3)); + geometry2.setAttribute('color', new THREE.Float32BufferAttribute(colors2, 3)); + geometry3.setAttribute('color', new THREE.Float32BufferAttribute(colors3, 3)); + + // + + const geometry4 = new THREE.BufferGeometry(); + const geometry5 = new THREE.BufferGeometry(); + const geometry6 = new THREE.BufferGeometry(); + + vertices = []; + colors1 = []; + colors2 = []; + colors3 = []; + + for (let i = 0; i < hilbertPoints.length; i++) { + const point = hilbertPoints[i]; + + vertices.push(point.x, point.y, point.z); + + color.setHSL(0.6, 1.0, Math.max(0, (200 - hilbertPoints[i].x) / 400) * 0.5 + 0.5, THREE.SRGBColorSpace); + colors1.push(color.r, color.g, color.b); + + color.setHSL(0.3, 1.0, Math.max(0, (200 + hilbertPoints[i].x) / 400) * 0.5, THREE.SRGBColorSpace); + colors2.push(color.r, color.g, color.b); + + color.setHSL(i / hilbertPoints.length, 1.0, 0.5, THREE.SRGBColorSpace); + colors3.push(color.r, color.g, color.b); + } + + geometry4.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); + geometry5.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); + geometry6.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); + + geometry4.setAttribute('color', new THREE.Float32BufferAttribute(colors1, 3)); + geometry5.setAttribute('color', new THREE.Float32BufferAttribute(colors2, 3)); + geometry6.setAttribute('color', new THREE.Float32BufferAttribute(colors3, 3)); + + // Create lines and add to scene + + const material = new THREE.LineBasicMaterial({ color: 0xffffff, vertexColors: true }); + + let line, p; + const scale = 0.3, + d = 225; + + const parameters = [ + [material, scale * 1.5, [-d, -d / 2, 0], geometry1], + [material, scale * 1.5, [0, -d / 2, 0], geometry2], + [material, scale * 1.5, [d, -d / 2, 0], geometry3], + + [material, scale * 1.5, [-d, d / 2, 0], geometry4], + [material, scale * 1.5, [0, d / 2, 0], geometry5], + [material, scale * 1.5, [d, d / 2, 0], geometry6], + ]; + + for (let i = 0; i < parameters.length; i++) { + p = parameters[i]; + line = new THREE.Line(p[3], p[0]); + line.scale.x = line.scale.y = line.scale.z = p[1]; + line.position.x = p[2][0]; + line.position.y = p[2][1]; + line.position.z = p[2][2]; + scene.add(line); + } + + // + + document.body.style.touchAction = 'none'; + document.body.addEventListener('pointermove', onPointerMove); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + windowHalfX = window.innerWidth / 2; + windowHalfY = window.innerHeight / 2; + + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function onPointerMove(event) { + if (event.isPrimary === false) return; + + mouseX = event.clientX - windowHalfX; + mouseY = event.clientY - windowHalfY; +} + +// + +function animate() { + camera.position.x += (mouseX - camera.position.x) * 0.05; + camera.position.y += (-mouseY + 200 - camera.position.y) * 0.05; + + camera.lookAt(scene.position); + + const time = Date.now() * 0.0005; + + for (let i = 0; i < scene.children.length; i++) { + const object = scene.children[i]; + + if (object.isLine) { + object.rotation.y = time * (i % 2 ? 1 : -1); + } + } + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_lines_dashed.ts b/examples-testing/examples/webgl_lines_dashed.ts new file mode 100644 index 000000000..3e3ee3041 --- /dev/null +++ b/examples-testing/examples/webgl_lines_dashed.ts @@ -0,0 +1,186 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import * as GeometryUtils from 'three/addons/utils/GeometryUtils.js'; + +let renderer, scene, camera, stats; +const objects = []; + +const WIDTH = window.innerWidth, + HEIGHT = window.innerHeight; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(60, WIDTH / HEIGHT, 1, 200); + camera.position.z = 150; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x111111); + scene.fog = new THREE.Fog(0x111111, 150, 200); + + const subdivisions = 6; + const recursion = 1; + + const points = GeometryUtils.hilbert3D(new THREE.Vector3(0, 0, 0), 25.0, recursion, 0, 1, 2, 3, 4, 5, 6, 7); + const spline = new THREE.CatmullRomCurve3(points); + + const samples = spline.getPoints(points.length * subdivisions); + const geometrySpline = new THREE.BufferGeometry().setFromPoints(samples); + + const line = new THREE.Line( + geometrySpline, + new THREE.LineDashedMaterial({ color: 0xffffff, dashSize: 1, gapSize: 0.5 }), + ); + line.computeLineDistances(); + + objects.push(line); + scene.add(line); + + const geometryBox = box(50, 50, 50); + + const lineSegments = new THREE.LineSegments( + geometryBox, + new THREE.LineDashedMaterial({ color: 0xffaa00, dashSize: 3, gapSize: 1 }), + ); + lineSegments.computeLineDistances(); + + objects.push(lineSegments); + scene.add(lineSegments); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(WIDTH, HEIGHT); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + stats = new Stats(); + document.body.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); +} + +function box(width, height, depth) { + ((width = width * 0.5), (height = height * 0.5), (depth = depth * 0.5)); + + const geometry = new THREE.BufferGeometry(); + const position = []; + + position.push( + -width, + -height, + -depth, + -width, + height, + -depth, + + -width, + height, + -depth, + width, + height, + -depth, + + width, + height, + -depth, + width, + -height, + -depth, + + width, + -height, + -depth, + -width, + -height, + -depth, + + -width, + -height, + depth, + -width, + height, + depth, + + -width, + height, + depth, + width, + height, + depth, + + width, + height, + depth, + width, + -height, + depth, + + width, + -height, + depth, + -width, + -height, + depth, + + -width, + -height, + -depth, + -width, + -height, + depth, + + -width, + height, + -depth, + -width, + height, + depth, + + width, + height, + -depth, + width, + height, + depth, + + width, + -height, + -depth, + width, + -height, + depth, + ); + + geometry.setAttribute('position', new THREE.Float32BufferAttribute(position, 3)); + + return geometry; +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + render(); + stats.update(); +} + +function render() { + const time = Date.now() * 0.001; + + scene.traverse(function (object) { + if (object.isLine) { + object.rotation.x = 0.25 * time; + object.rotation.y = 0.25 * time; + } + }); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_lines_fat.ts b/examples-testing/examples/webgl_lines_fat.ts new file mode 100644 index 000000000..b5b66bbb1 --- /dev/null +++ b/examples-testing/examples/webgl_lines_fat.ts @@ -0,0 +1,235 @@ +import * as THREE from 'three'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { Line2 } from 'three/addons/lines/Line2.js'; +import { LineMaterial } from 'three/addons/lines/LineMaterial.js'; +import { LineGeometry } from 'three/addons/lines/LineGeometry.js'; +import * as GeometryUtils from 'three/addons/utils/GeometryUtils.js'; + +let line, renderer, scene, camera, camera2, controls; +let line1; +let matLine, matLineBasic, matLineDashed; +let gui; + +// viewport +let insetWidth; +let insetHeight; + +init(); + +function init() { + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setClearColor(0x000000, 0.0); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(-40, 0, 60); + + camera2 = new THREE.PerspectiveCamera(40, 1, 1, 1000); + camera2.position.copy(camera.position); + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + controls.minDistance = 10; + controls.maxDistance = 500; + + // Position and THREE.Color Data + + const positions = []; + const colors = []; + + const points = GeometryUtils.hilbert3D(new THREE.Vector3(0, 0, 0), 20.0, 1, 0, 1, 2, 3, 4, 5, 6, 7); + + const spline = new THREE.CatmullRomCurve3(points); + const divisions = Math.round(12 * points.length); + const point = new THREE.Vector3(); + const color = new THREE.Color(); + + for (let i = 0, l = divisions; i < l; i++) { + const t = i / l; + + spline.getPoint(t, point); + positions.push(point.x, point.y, point.z); + + color.setHSL(t, 1.0, 0.5, THREE.SRGBColorSpace); + colors.push(color.r, color.g, color.b); + } + + // Line2 ( LineGeometry, LineMaterial ) + + const geometry = new LineGeometry(); + geometry.setPositions(positions); + geometry.setColors(colors); + + matLine = new LineMaterial({ + color: 0xffffff, + linewidth: 5, // in world units with size attenuation, pixels otherwise + vertexColors: true, + + dashed: false, + alphaToCoverage: true, + }); + + line = new Line2(geometry, matLine); + line.computeLineDistances(); + line.scale.set(1, 1, 1); + scene.add(line); + + // THREE.Line ( THREE.BufferGeometry, THREE.LineBasicMaterial ) - rendered with gl.LINE_STRIP + + const geo = new THREE.BufferGeometry(); + geo.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)); + geo.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); + + matLineBasic = new THREE.LineBasicMaterial({ vertexColors: true }); + matLineDashed = new THREE.LineDashedMaterial({ vertexColors: true, scale: 2, dashSize: 1, gapSize: 1 }); + + line1 = new THREE.Line(geo, matLineBasic); + line1.computeLineDistances(); + line1.visible = false; + scene.add(line1); + + // + + window.addEventListener('resize', onWindowResize); + onWindowResize(); + + initGui(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + insetWidth = window.innerHeight / 4; // square + insetHeight = window.innerHeight / 4; + + camera2.aspect = insetWidth / insetHeight; + camera2.updateProjectionMatrix(); +} + +function animate() { + // main scene + + renderer.setClearColor(0x000000, 0); + + renderer.setViewport(0, 0, window.innerWidth, window.innerHeight); + + controls.update(); + + renderer.render(scene, camera); + + // inset scene + + renderer.setClearColor(0x222222, 1); + + renderer.clearDepth(); // important! + + renderer.setScissorTest(true); + + renderer.setScissor(20, 20, insetWidth, insetHeight); + + renderer.setViewport(20, 20, insetWidth, insetHeight); + + camera2.position.copy(camera.position); + camera2.quaternion.copy(camera.quaternion); + + renderer.render(scene, camera2); + + renderer.setScissorTest(false); +} + +// + +function initGui() { + gui = new GUI(); + + const param = { + 'line type': 0, + 'world units': false, + width: 5, + alphaToCoverage: true, + dashed: false, + 'dash scale': 1, + 'dash / gap': 1, + }; + + gui.add(param, 'line type', { LineGeometry: 0, 'gl.LINE': 1 }).onChange(function (val) { + switch (val) { + case 0: + line.visible = true; + + line1.visible = false; + + break; + + case 1: + line.visible = false; + + line1.visible = true; + + break; + } + }); + + gui.add(param, 'world units').onChange(function (val) { + matLine.worldUnits = val; + }); + + gui.add(param, 'width', 1, 10).onChange(function (val) { + matLine.linewidth = val; + }); + + gui.add(param, 'alphaToCoverage').onChange(function (val) { + matLine.alphaToCoverage = val; + }); + + gui.add(param, 'dashed').onChange(function (val) { + matLine.dashed = val; + line1.material = val ? matLineDashed : matLineBasic; + }); + + gui.add(param, 'dash scale', 0.5, 2, 0.1).onChange(function (val) { + matLine.dashScale = val; + matLineDashed.scale = val; + }); + + gui.add(param, 'dash / gap', { '2 : 1': 0, '1 : 1': 1, '1 : 2': 2 }).onChange(function (val) { + switch (val) { + case 0: + matLine.dashSize = 2; + matLine.gapSize = 1; + + matLineDashed.dashSize = 2; + matLineDashed.gapSize = 1; + + break; + + case 1: + matLine.dashSize = 1; + matLine.gapSize = 1; + + matLineDashed.dashSize = 1; + matLineDashed.gapSize = 1; + + break; + + case 2: + matLine.dashSize = 1; + matLine.gapSize = 2; + + matLineDashed.dashSize = 1; + matLineDashed.gapSize = 2; + + break; + } + }); +} diff --git a/examples-testing/examples/webgl_lines_fat_raycasting.ts b/examples-testing/examples/webgl_lines_fat_raycasting.ts new file mode 100644 index 000000000..ac9400da3 --- /dev/null +++ b/examples-testing/examples/webgl_lines_fat_raycasting.ts @@ -0,0 +1,280 @@ +import * as THREE from 'three'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { LineMaterial } from 'three/addons/lines/LineMaterial.js'; +import { LineSegments2 } from 'three/addons/lines/LineSegments2.js'; +import { LineSegmentsGeometry } from 'three/addons/lines/LineSegmentsGeometry.js'; +import { Line2 } from 'three/addons/lines/Line2.js'; +import { LineGeometry } from 'three/addons/lines/LineGeometry.js'; + +let line, thresholdLine, segments, thresholdSegments; +let renderer, scene, camera, controls; +let sphereInter, sphereOnLine; +let gui; +let timer; + +const color = new THREE.Color(); + +const pointer = new THREE.Vector2(Infinity, Infinity); + +const raycaster = new THREE.Raycaster(); + +raycaster.params.Line2 = {}; +raycaster.params.Line2.threshold = 0; + +const matLine = new LineMaterial({ + color: 0xffffff, + linewidth: 1, // in world units with size attenuation, pixels otherwise + worldUnits: true, + vertexColors: true, + + alphaToCoverage: true, +}); + +const matThresholdLine = new LineMaterial({ + color: 0xffffff, + linewidth: matLine.linewidth, // in world units with size attenuation, pixels otherwise + worldUnits: true, + // vertexColors: true, + transparent: true, + opacity: 0.2, + depthTest: false, + visible: false, +}); + +const params = { + 'line type': 0, + 'world units': matLine.worldUnits, + 'visualize threshold': matThresholdLine.visible, + width: matLine.linewidth, + alphaToCoverage: matLine.alphaToCoverage, + threshold: raycaster.params.Line2.threshold, + translation: 0, + animate: true, +}; + +init(); + +function init() { + timer = new THREE.Timer(); + timer.connect(document); + + renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setClearColor(0x000000, 0.0); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(-40, 0, 60); + + controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 10; + controls.maxDistance = 500; + + const sphereGeometry = new THREE.SphereGeometry(0.25, 8, 4); + const sphereInterMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000, depthTest: false }); + const sphereOnLineMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00, depthTest: false }); + + sphereInter = new THREE.Mesh(sphereGeometry, sphereInterMaterial); + sphereOnLine = new THREE.Mesh(sphereGeometry, sphereOnLineMaterial); + sphereInter.visible = false; + sphereOnLine.visible = false; + sphereInter.renderOrder = 10; + sphereOnLine.renderOrder = 10; + scene.add(sphereInter); + scene.add(sphereOnLine); + + // Position and THREE.Color Data + + const positions = []; + const colors = []; + const points = []; + for (let i = -50; i < 50; i++) { + const t = i / 3; + points.push(new THREE.Vector3(t * Math.sin(2 * t), t, t * Math.cos(2 * t))); + } + + const spline = new THREE.CatmullRomCurve3(points); + const divisions = Math.round(3 * points.length); + const point = new THREE.Vector3(); + const color = new THREE.Color(); + + for (let i = 0, l = divisions; i < l; i++) { + const t = i / l; + + spline.getPoint(t, point); + positions.push(point.x, point.y, point.z); + + color.setHSL(t, 1.0, 0.5, THREE.SRGBColorSpace); + colors.push(color.r, color.g, color.b); + } + + const lineGeometry = new LineGeometry(); + lineGeometry.setPositions(positions); + lineGeometry.setColors(colors); + + const segmentsGeometry = new LineSegmentsGeometry(); + segmentsGeometry.setPositions(positions); + segmentsGeometry.setColors(colors); + + segments = new LineSegments2(segmentsGeometry, matLine); + segments.computeLineDistances(); + segments.scale.set(1, 1, 1); + scene.add(segments); + segments.visible = false; + + thresholdSegments = new LineSegments2(segmentsGeometry, matThresholdLine); + thresholdSegments.computeLineDistances(); + thresholdSegments.scale.set(1, 1, 1); + scene.add(thresholdSegments); + thresholdSegments.visible = false; + + line = new Line2(lineGeometry, matLine); + line.computeLineDistances(); + line.scale.set(1, 1, 1); + scene.add(line); + + thresholdLine = new Line2(lineGeometry, matThresholdLine); + thresholdLine.computeLineDistances(); + thresholdLine.scale.set(1, 1, 1); + scene.add(thresholdLine); + + const geo = new THREE.BufferGeometry(); + geo.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)); + geo.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); + + // + + document.addEventListener('pointermove', onPointerMove); + window.addEventListener('resize', onWindowResize); + onWindowResize(); + + initGui(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function onPointerMove(event) { + pointer.x = (event.clientX / window.innerWidth) * 2 - 1; + pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; +} + +function animate() { + timer.update(); + + const delta = timer.getDelta(); + + const obj = line.visible ? line : segments; + thresholdLine.position.copy(line.position); + thresholdLine.quaternion.copy(line.quaternion); + thresholdSegments.position.copy(segments.position); + thresholdSegments.quaternion.copy(segments.quaternion); + + if (params.animate) { + line.rotation.y += delta * 0.1; + + segments.rotation.y = line.rotation.y; + } + + raycaster.setFromCamera(pointer, camera); + + const intersects = raycaster.intersectObject(obj); + + if (intersects.length > 0) { + sphereInter.visible = true; + sphereOnLine.visible = true; + + sphereInter.position.copy(intersects[0].point); + sphereOnLine.position.copy(intersects[0].pointOnLine); + + const index = intersects[0].faceIndex; + const colors = obj.geometry.getAttribute('instanceColorStart'); + + color.fromBufferAttribute(colors, index); + + sphereInter.material.color.copy(color).offsetHSL(0.3, 0, 0); + sphereOnLine.material.color.copy(color).offsetHSL(0.7, 0, 0); + + renderer.domElement.style.cursor = 'crosshair'; + } else { + sphereInter.visible = false; + sphereOnLine.visible = false; + renderer.domElement.style.cursor = ''; + } + + renderer.render(scene, camera); +} + +// + +function switchLine(val) { + switch (val) { + case 0: + line.visible = true; + thresholdLine.visible = true; + + segments.visible = false; + thresholdSegments.visible = false; + + break; + + case 1: + line.visible = false; + thresholdLine.visible = false; + + segments.visible = true; + thresholdSegments.visible = true; + + break; + } +} + +function initGui() { + gui = new GUI(); + + gui.add(params, 'line type', { LineGeometry: 0, LineSegmentsGeometry: 1 }) + .onChange(function (val) { + switchLine(val); + }) + .setValue(1); + + gui.add(params, 'world units').onChange(function (val) { + matLine.worldUnits = val; + matThresholdLine.worldUnits = val; + }); + + gui.add(params, 'visualize threshold').onChange(function (val) { + matThresholdLine.visible = val; + }); + + gui.add(params, 'width', 1, 10).onChange(function (val) { + matLine.linewidth = val; + matThresholdLine.linewidth = matLine.linewidth + raycaster.params.Line2.threshold; + }); + + gui.add(params, 'alphaToCoverage').onChange(function (val) { + matLine.alphaToCoverage = val; + }); + + gui.add(params, 'threshold', 0, 10).onChange(function (val) { + raycaster.params.Line2.threshold = val; + matThresholdLine.linewidth = matLine.linewidth + raycaster.params.Line2.threshold; + }); + + gui.add(params, 'translation', 0, 10).onChange(function (val) { + line.position.x = val; + segments.position.x = val; + }); + + gui.add(params, 'animate'); +} diff --git a/examples-testing/examples/webgl_lines_fat_wireframe.ts b/examples-testing/examples/webgl_lines_fat_wireframe.ts new file mode 100644 index 000000000..59660ad7e --- /dev/null +++ b/examples-testing/examples/webgl_lines_fat_wireframe.ts @@ -0,0 +1,210 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { LineMaterial } from 'three/addons/lines/LineMaterial.js'; +import { Wireframe } from 'three/addons/lines/Wireframe.js'; +import { WireframeGeometry2 } from 'three/addons/lines/WireframeGeometry2.js'; + +let wireframe, renderer, scene, camera, camera2, controls; +let wireframe1; +let matLine, matLineBasic, matLineDashed; +let stats; +let gui; + +// viewport +let insetWidth; +let insetHeight; + +init(); + +function init() { + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setClearColor(0x000000, 0.0); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(-50, 0, 50); + + camera2 = new THREE.PerspectiveCamera(40, 1, 1, 1000); + camera2.position.copy(camera.position); + + controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 10; + controls.maxDistance = 500; + + // Wireframe ( WireframeGeometry2, LineMaterial ) + + let geo = new THREE.IcosahedronGeometry(20, 1); + + const geometry = new WireframeGeometry2(geo); + + matLine = new LineMaterial({ + color: 0x4080ff, + linewidth: 5, // in pixels + dashed: false, + }); + + wireframe = new Wireframe(geometry, matLine); + wireframe.computeLineDistances(); + wireframe.scale.set(1, 1, 1); + scene.add(wireframe); + + // Line ( THREE.WireframeGeometry, THREE.LineBasicMaterial ) - rendered with gl.LINE + + geo = new THREE.WireframeGeometry(geo); + + matLineBasic = new THREE.LineBasicMaterial({ color: 0x4080ff }); + matLineDashed = new THREE.LineDashedMaterial({ scale: 2, dashSize: 1, gapSize: 1 }); + + wireframe1 = new THREE.LineSegments(geo, matLineBasic); + wireframe1.computeLineDistances(); + wireframe1.visible = false; + scene.add(wireframe1); + + // + + window.addEventListener('resize', onWindowResize); + onWindowResize(); + + stats = new Stats(); + document.body.appendChild(stats.dom); + + initGui(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + insetWidth = window.innerHeight / 4; // square + insetHeight = window.innerHeight / 4; + + camera2.aspect = insetWidth / insetHeight; + camera2.updateProjectionMatrix(); +} + +function animate() { + // main scene + + renderer.setClearColor(0x000000, 0); + + renderer.setViewport(0, 0, window.innerWidth, window.innerHeight); + + renderer.render(scene, camera); + + // inset scene + + renderer.setClearColor(0x222222, 1); + + renderer.clearDepth(); // important! + + renderer.setScissorTest(true); + + renderer.setScissor(20, 20, insetWidth, insetHeight); + + renderer.setViewport(20, 20, insetWidth, insetHeight); + + camera2.position.copy(camera.position); + camera2.quaternion.copy(camera.quaternion); + + renderer.render(scene, camera2); + + renderer.setScissorTest(false); + + stats.update(); +} + +// + +function initGui() { + gui = new GUI(); + + const param = { + 'line type': 0, + 'width (px)': 5, + dashed: false, + 'dash scale': 1, + 'dash / gap': 1, + }; + + gui.add(param, 'line type', { LineGeometry: 0, 'gl.LINE': 1 }).onChange(function (val) { + switch (val) { + case 0: + wireframe.visible = true; + + wireframe1.visible = false; + + break; + + case 1: + wireframe.visible = false; + + wireframe1.visible = true; + + break; + } + }); + + gui.add(param, 'width (px)', 1, 10).onChange(function (val) { + matLine.linewidth = val; + }); + + gui.add(param, 'dashed').onChange(function (val) { + matLine.dashed = val; + + // dashed is implemented as a defines -- not as a uniform. this could be changed. + // ... or THREE.LineDashedMaterial could be implemented as a separate material + // temporary hack - renderer should do this eventually + if (val) matLine.defines.USE_DASH = ''; + else delete matLine.defines.USE_DASH; + matLine.needsUpdate = true; + + wireframe1.material = val ? matLineDashed : matLineBasic; + }); + + gui.add(param, 'dash scale', 0.5, 1, 0.1).onChange(function (val) { + matLine.dashScale = val; + matLineDashed.scale = val; + }); + + gui.add(param, 'dash / gap', { '2 : 1': 0, '1 : 1': 1, '1 : 2': 2 }).onChange(function (val) { + switch (val) { + case 0: + matLine.dashSize = 2; + matLine.gapSize = 1; + + matLineDashed.dashSize = 2; + matLineDashed.gapSize = 1; + + break; + + case 1: + matLine.dashSize = 1; + matLine.gapSize = 1; + + matLineDashed.dashSize = 1; + matLineDashed.gapSize = 1; + + break; + + case 2: + matLine.dashSize = 1; + matLine.gapSize = 2; + + matLineDashed.dashSize = 1; + matLineDashed.gapSize = 2; + + break; + } + }); +} diff --git a/examples-testing/examples/webgl_loader_3dm.ts b/examples-testing/examples/webgl_loader_3dm.ts new file mode 100644 index 000000000..7570306fd --- /dev/null +++ b/examples-testing/examples/webgl_loader_3dm.ts @@ -0,0 +1,95 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { Rhino3dmLoader } from 'three/addons/loaders/3DMLoader.js'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let camera, scene, renderer; +let controls, gui; + +init(); + +function init() { + THREE.Object3D.DEFAULT_UP.set(0, 0, 1); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(26, -40, 5); + + scene = new THREE.Scene(); + + const directionalLight = new THREE.DirectionalLight(0xffffff, 6); + directionalLight.position.set(0, 0, 2); + scene.add(directionalLight); + + const loader = new Rhino3dmLoader(); + //generally, use this for the Library Path: https://cdn.jsdelivr.net/npm/rhino3dm@8.0.1 + loader.setLibraryPath('jsm/libs/rhino3dm/'); + loader.load( + 'models/3dm/Rhino_Logo.3dm', + function (object) { + scene.add(object); + initGUI(object.userData.layers); + + // hide spinner + document.getElementById('loader').style.display = 'none'; + }, + function (progress) { + console.log((progress.loaded / progress.total) * 100 + '%'); + }, + function (error) { + console.log(error); + }, + ); + + controls = new OrbitControls(camera, renderer.domElement); + + window.addEventListener('resize', resize); +} + +function resize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); +} + +function animate() { + controls.update(); + renderer.render(scene, camera); +} + +function initGUI(layers) { + gui = new GUI({ title: 'layers' }); + + for (let i = 0; i < layers.length; i++) { + const layer = layers[i]; + gui.add(layer, 'visible') + .name(layer.name) + .onChange(function (val) { + const name = this.object.name; + + scene.traverse(function (child) { + if (child.userData.hasOwnProperty('attributes')) { + if ('layerIndex' in child.userData.attributes) { + const layerName = layers[child.userData.attributes.layerIndex].name; + + if (layerName === name) { + child.visible = val; + layer.visible = val; + } + } + } + }); + }); + } +} diff --git a/examples-testing/examples/webgl_loader_3ds.ts b/examples-testing/examples/webgl_loader_3ds.ts new file mode 100644 index 000000000..10ce34076 --- /dev/null +++ b/examples-testing/examples/webgl_loader_3ds.ts @@ -0,0 +1,62 @@ +import * as THREE from 'three'; + +import { TrackballControls } from 'three/addons/controls/TrackballControls.js'; +import { TDSLoader } from 'three/addons/loaders/TDSLoader.js'; + +let container, controls; +let camera, scene, renderer; + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 10); + camera.position.z = 2; + + scene = new THREE.Scene(); + scene.add(new THREE.AmbientLight(0xffffff, 3)); + + const directionalLight = new THREE.DirectionalLight(0xffeedd, 3); + directionalLight.position.set(0, 0, 2); + scene.add(directionalLight); + + //3ds files dont store normal maps + const normal = new THREE.TextureLoader().load('models/3ds/portalgun/textures/normal.jpg'); + + const loader = new TDSLoader(); + loader.setResourcePath('models/3ds/portalgun/textures/'); + loader.load('models/3ds/portalgun/portalgun.3ds', function (object) { + object.traverse(function (child) { + if (child.isMesh) { + child.material.specular.setScalar(0.1); + child.material.normalMap = normal; + } + }); + + scene.add(object); + }); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + controls = new TrackballControls(camera, renderer.domElement); + + window.addEventListener('resize', resize); +} + +function resize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + controls.update(); + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_loader_3dtiles.ts b/examples-testing/examples/webgl_loader_3dtiles.ts new file mode 100644 index 000000000..bae36bc84 --- /dev/null +++ b/examples-testing/examples/webgl_loader_3dtiles.ts @@ -0,0 +1,78 @@ +import * as THREE from 'three'; +import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js'; +import { TilesRenderer, GlobeControls } from '3d-tiles-renderer'; +import { + CesiumIonAuthPlugin, + GLTFExtensionsPlugin, + TilesFadePlugin, + UpdateOnChangePlugin, +} from '3d-tiles-renderer/plugins'; + +// Ion key provided by Cesium for use on threejs.org +// A personal Cesium Ion key can be used for development. +const ION_KEY = + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJiMTFiZTRmZS1mMWIxLTQ5YzYtYjA4Zi0xYTE0MjFmYzQ5OGYiLCJpZCI6MjY3NzgzLCJpYXQiOjE3MzY0NzQxMDh9.ppGPgpse1lq7QeNyljX7THUyK5w1x_4HksSHSlhe5sY'; + +let camera, scene, renderer; +let tiles, controls; + +init(); + +function init() { + // camera + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 100); + camera.position.set(-1, 1, 1).normalize().multiplyScalar(10); + camera.position.set(-8000000, 10000000, -14720000); + camera.lookAt(0, 0, 0); + + // scene + scene = new THREE.Scene(); + + // renderer + renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // loader + const dracoLoader = new DRACOLoader(); + dracoLoader.setDecoderPath('jsm/libs/draco/'); + dracoLoader.setDecoderConfig({ type: 'js' }); + + // tiles + tiles = new TilesRenderer(); + tiles.registerPlugin(new CesiumIonAuthPlugin({ apiToken: ION_KEY, assetId: '2275207', autoRefreshToken: true })); + tiles.registerPlugin(new GLTFExtensionsPlugin({ dracoLoader })); + tiles.registerPlugin(new TilesFadePlugin()); + tiles.registerPlugin(new UpdateOnChangePlugin()); + tiles.setCamera(camera); + tiles.setResolutionFromRenderer(camera, renderer); + scene.add(tiles.group); + + // rotate the globe so the north pole is up + tiles.group.rotation.x = -Math.PI / 2; + + // controls + controls = new GlobeControls(scene, camera, renderer.domElement, tiles); + controls.enableDamping = true; + + window.addEventListener('resize', onWindowResize); + onWindowResize(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + tiles.setResolutionFromRenderer(camera, renderer); +} + +function animate() { + controls.update(); + tiles.update(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_loader_3mf.ts b/examples-testing/examples/webgl_loader_3mf.ts new file mode 100644 index 000000000..c31e32196 --- /dev/null +++ b/examples-testing/examples/webgl_loader_3mf.ts @@ -0,0 +1,105 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { ThreeMFLoader } from 'three/addons/loaders/3MFLoader.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let camera, scene, renderer, object, loader, controls; + +const params = { + asset: 'cube_gears', +}; + +const assets = ['cube_gears', 'facecolors', 'multipletextures', 'vertexcolors']; + +init(); + +function init() { + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + document.body.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x333333); + + camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 500); + + // Z is up for objects intended to be 3D printed. + + camera.up.set(0, 0, 1); + camera.position.set(-100, -250, 100); + scene.add(camera); + + controls = new OrbitControls(camera, renderer.domElement); + controls.addEventListener('change', render); + controls.minDistance = 50; + controls.maxDistance = 400; + controls.enablePan = false; + controls.update(); + + scene.add(new THREE.AmbientLight(0xffffff, 0.6)); + + const light = new THREE.DirectionalLight(0xffffff, 2); + light.position.set(-1, -2.5, 1); + scene.add(light); + + const manager = new THREE.LoadingManager(); + + manager.onLoad = function () { + const aabb = new THREE.Box3().setFromObject(object); + const center = aabb.getCenter(new THREE.Vector3()); + + object.position.x += object.position.x - center.x; + object.position.y += object.position.y - center.y; + object.position.z += object.position.z - center.z; + + controls.reset(); + + scene.add(object); + render(); + }; + + loader = new ThreeMFLoader(manager); + loadAsset(params.asset); + + window.addEventListener('resize', onWindowResize); + + // + + const gui = new GUI(); + gui.add(params, 'asset', assets).onChange(function (value) { + loadAsset(value); + }); +} + +function loadAsset(asset) { + loader.load('models/3mf/' + asset + '.3mf', function (group) { + if (object) { + object.traverse(function (child) { + if (child.material) child.material.dispose(); + if (child.material && child.material.map) child.material.map.dispose(); + if (child.geometry) child.geometry.dispose(); + }); + + scene.remove(object); + } + + // + + object = group; + }); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + render(); +} + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_loader_3mf_materials.ts b/examples-testing/examples/webgl_loader_3mf_materials.ts new file mode 100644 index 000000000..0dea01391 --- /dev/null +++ b/examples-testing/examples/webgl_loader_3mf_materials.ts @@ -0,0 +1,106 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { ThreeMFLoader } from 'three/addons/loaders/3MFLoader.js'; + +let camera, scene, renderer; + +init(); + +function init() { + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xa0a0a0); + scene.fog = new THREE.Fog(0xa0a0a0, 10, 500); + + camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 500); + camera.position.set(-50, 40, 50); + scene.add(camera); + + // + + const hemiLight = new THREE.HemisphereLight(0xffffff, 0x8d8d8d, 3); + hemiLight.position.set(0, 100, 0); + scene.add(hemiLight); + + const dirLight = new THREE.DirectionalLight(0xffffff, 3); + dirLight.position.set(-0, 40, 50); + dirLight.castShadow = true; + dirLight.shadow.camera.top = 50; + dirLight.shadow.camera.bottom = -25; + dirLight.shadow.camera.left = -25; + dirLight.shadow.camera.right = 25; + dirLight.shadow.camera.near = 0.1; + dirLight.shadow.camera.far = 200; + dirLight.shadow.mapSize.set(1024, 1024); + scene.add(dirLight); + + // scene.add( new THREE.CameraHelper( dirLight.shadow.camera ) ); + + // + + const manager = new THREE.LoadingManager(); + + const loader = new ThreeMFLoader(manager); + loader.load('./models/3mf/truck.3mf', function (object) { + object.rotation.set(-Math.PI / 2, 0, 0); // z-up conversion + + object.traverse(function (child) { + child.castShadow = true; + }); + + scene.add(object); + }); + + // + + manager.onLoad = function () { + render(); + }; + + // + + const ground = new THREE.Mesh( + new THREE.PlaneGeometry(1000, 1000), + new THREE.MeshPhongMaterial({ color: 0xcbcbcb, depthWrite: false }), + ); + ground.rotation.x = -Math.PI / 2; + ground.position.y = 11; + ground.receiveShadow = true; + scene.add(ground); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.shadowMap.enabled = true; + renderer.shadowMap.type = THREE.PCFShadowMap; + document.body.appendChild(renderer.domElement); + + // + + const controls = new OrbitControls(camera, renderer.domElement); + controls.addEventListener('change', render); + controls.minDistance = 50; + controls.maxDistance = 200; + controls.enablePan = false; + controls.target.set(0, 20, 0); + controls.update(); + + window.addEventListener('resize', onWindowResize); + + render(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + render(); +} + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_loader_amf.ts b/examples-testing/examples/webgl_loader_amf.ts new file mode 100644 index 000000000..ee576e04f --- /dev/null +++ b/examples-testing/examples/webgl_loader_amf.ts @@ -0,0 +1,62 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { AMFLoader } from 'three/addons/loaders/AMFLoader.js'; + +let camera, scene, renderer; + +init(); + +function init() { + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x999999); + + scene.add(new THREE.AmbientLight(0x999999)); + + camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 500); + + // Z is up for objects intended to be 3D printed. + + camera.up.set(0, 0, 1); + camera.position.set(0, -9, 6); + + camera.add(new THREE.PointLight(0xffffff, 250)); + + scene.add(camera); + + const grid = new THREE.GridHelper(50, 50, 0xffffff, 0x555555); + grid.rotateOnAxis(new THREE.Vector3(1, 0, 0), 90 * (Math.PI / 180)); + scene.add(grid); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + document.body.appendChild(renderer.domElement); + + const loader = new AMFLoader(); + loader.load('./models/amf/rook.amf', function (amfobject) { + scene.add(amfobject); + render(); + }); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.addEventListener('change', render); + controls.target.set(0, 0, 2); + controls.enableZoom = false; + controls.update(); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + render(); +} + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_loader_bvh.ts b/examples-testing/examples/webgl_loader_bvh.ts new file mode 100644 index 000000000..70cec7e6a --- /dev/null +++ b/examples-testing/examples/webgl_loader_bvh.ts @@ -0,0 +1,64 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { BVHLoader } from 'three/addons/loaders/BVHLoader.js'; + +const timer = new THREE.Timer(); +timer.connect(document); + +let camera, controls, scene, renderer; +let mixer; + +init(); + +const loader = new BVHLoader(); +loader.load('models/bvh/pirouette.bvh', function (result) { + const skeletonHelper = new THREE.SkeletonHelper(result.skeleton.bones[0]); + + scene.add(result.skeleton.bones[0]); + scene.add(skeletonHelper); + + // play animation + mixer = new THREE.AnimationMixer(result.skeleton.bones[0]); + mixer.clipAction(result.clip).play(); +}); + +function init() { + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(0, 200, 300); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xeeeeee); + + scene.add(new THREE.GridHelper(400, 10)); + + // renderer + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 300; + controls.maxDistance = 700; + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + const delta = timer.getDelta(); + + if (mixer) mixer.update(delta); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_loader_collada.ts b/examples-testing/examples/webgl_loader_collada.ts new file mode 100644 index 000000000..3106369c8 --- /dev/null +++ b/examples-testing/examples/webgl_loader_collada.ts @@ -0,0 +1,86 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { ColladaLoader } from 'three/addons/loaders/ColladaLoader.js'; + +let container, stats, timer; +let camera, scene, renderer, elf; + +init(); + +function init() { + container = document.getElementById('container'); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 2000); + camera.position.set(8, 10, 8); + camera.lookAt(0, 3, 0); + + scene = new THREE.Scene(); + + timer = new THREE.Timer(); + timer.connect(document); + + // loading manager + + const loadingManager = new THREE.LoadingManager(function () { + scene.add(elf); + }); + + // collada + + const loader = new ColladaLoader(loadingManager); + loader.load('./models/collada/elf/elf.dae', function (collada) { + elf = collada.scene; + }); + + // + + const ambientLight = new THREE.AmbientLight(0xffffff); + scene.add(ambientLight); + + const directionalLight = new THREE.DirectionalLight(0xffffff, 2.5); + directionalLight.position.set(1, 1, 0).normalize(); + scene.add(directionalLight); + + // + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + // + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + render(); + stats.update(); +} + +function render() { + const delta = timer.getDelta(); + + if (elf !== undefined) { + elf.rotation.z += delta * 0.5; + } + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_loader_collada_skinning.ts b/examples-testing/examples/webgl_loader_collada_skinning.ts new file mode 100644 index 000000000..bbbd0ac3e --- /dev/null +++ b/examples-testing/examples/webgl_loader_collada_skinning.ts @@ -0,0 +1,100 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { ColladaLoader } from 'three/addons/loaders/ColladaLoader.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let container, stats, timer, controls; +let camera, scene, renderer, mixer; + +init(); + +function init() { + container = document.getElementById('container'); + + camera = new THREE.PerspectiveCamera(25, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(15, 10, -15); + + scene = new THREE.Scene(); + + timer = new THREE.Timer(); + timer.connect(document); + + // collada + + const loader = new ColladaLoader(); + loader.load('./models/collada/stormtrooper/stormtrooper.dae', function (collada) { + const avatar = collada.scene; + const animations = avatar.animations; + + mixer = new THREE.AnimationMixer(avatar); + mixer.clipAction(animations[0]).play(); + + scene.add(avatar); + }); + + // + + const gridHelper = new THREE.GridHelper(10, 20, 0xc1c1c1, 0x8d8d8d); + scene.add(gridHelper); + + // + + const ambientLight = new THREE.AmbientLight(0xffffff, 0.6); + scene.add(ambientLight); + + const directionalLight = new THREE.DirectionalLight(0xffffff, 3); + directionalLight.position.set(1.5, 1, -1.5); + scene.add(directionalLight); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + // + + controls = new OrbitControls(camera, renderer.domElement); + controls.screenSpacePanning = true; + controls.minDistance = 5; + controls.maxDistance = 40; + controls.target.set(0, 2, 0); + controls.update(); + + // + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + render(); + stats.update(); +} + +function render() { + const delta = timer.getDelta(); + + if (mixer !== undefined) { + mixer.update(delta); + } + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_loader_draco.ts b/examples-testing/examples/webgl_loader_draco.ts new file mode 100644 index 000000000..c1df7100d --- /dev/null +++ b/examples-testing/examples/webgl_loader_draco.ts @@ -0,0 +1,83 @@ +import * as THREE from 'three'; + +import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; + +let camera, scene, renderer; + +const container = document.querySelector('#container'); + +// Configure and create Draco decoder. +const dracoLoader = new DRACOLoader(); +dracoLoader.setDecoderPath('jsm/libs/draco/'); +dracoLoader.setDecoderConfig({ type: 'js' }); + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 0.1, 15); + camera.position.set(3, 0.25, 3); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x443333); + scene.fog = new THREE.Fog(0x443333, 1, 4); + + // Ground + const plane = new THREE.Mesh(new THREE.PlaneGeometry(8, 8), new THREE.MeshLambertMaterial({ color: 0xcbcbcb })); + plane.rotation.x = -Math.PI / 2; + plane.position.y = 0.03; + plane.receiveShadow = true; + scene.add(plane); + + // Lights + const hemiLight = new THREE.HemisphereLight(0x8d7c7c, 0x494966, 3); + scene.add(hemiLight); + + const spotLight = new THREE.SpotLight(); + spotLight.intensity = 7; + spotLight.angle = Math.PI / 16; + spotLight.penumbra = 0.5; + spotLight.castShadow = true; + spotLight.shadow.radius = 8; + spotLight.position.set(-1, 1, 1); + scene.add(spotLight); + + dracoLoader.load('models/draco/bunny.drc', function (geometry) { + geometry.computeVertexNormals(); + + const material = new THREE.MeshStandardMaterial({ color: 0xa5a5a5 }); + const mesh = new THREE.Mesh(geometry, material); + mesh.castShadow = true; + mesh.receiveShadow = true; + scene.add(mesh); + + // Release decoder resources. + dracoLoader.dispose(); + }); + + // renderer + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.shadowMap.enabled = true; + container.appendChild(renderer.domElement); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + const timer = Date.now() * 0.0003; + + camera.position.x = Math.sin(timer) * 0.5; + camera.position.z = Math.cos(timer) * 0.5; + camera.lookAt(0, 0.1, 0); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_loader_fbx.ts b/examples-testing/examples/webgl_loader_fbx.ts new file mode 100644 index 000000000..bcf26ae95 --- /dev/null +++ b/examples-testing/examples/webgl_loader_fbx.ts @@ -0,0 +1,169 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { FBXLoader } from 'three/addons/loaders/FBXLoader.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +const manager = new THREE.LoadingManager(); + +let camera, scene, renderer, stats, object, loader, guiMorphsFolder; +let mixer; + +const timer = new THREE.Timer(); +timer.connect(document); + +const params = { + asset: 'Samba Dancing', +}; + +const assets = ['Samba Dancing', 'morph_test', 'monkey', 'monkey_embedded_texture', 'vCube']; + +init(); + +function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000); + camera.position.set(100, 200, 300); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xa0a0a0); + scene.fog = new THREE.Fog(0xa0a0a0, 200, 1000); + + const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444, 5); + hemiLight.position.set(0, 200, 0); + scene.add(hemiLight); + + const dirLight = new THREE.DirectionalLight(0xffffff, 5); + dirLight.position.set(0, 200, 100); + dirLight.castShadow = true; + dirLight.shadow.camera.top = 180; + dirLight.shadow.camera.bottom = -100; + dirLight.shadow.camera.left = -120; + dirLight.shadow.camera.right = 120; + scene.add(dirLight); + + // scene.add( new THREE.CameraHelper( dirLight.shadow.camera ) ); + + // ground + const mesh = new THREE.Mesh( + new THREE.PlaneGeometry(2000, 2000), + new THREE.MeshPhongMaterial({ color: 0x999999, depthWrite: false }), + ); + mesh.rotation.x = -Math.PI / 2; + mesh.receiveShadow = true; + scene.add(mesh); + + const grid = new THREE.GridHelper(2000, 20, 0x000000, 0x000000); + grid.material.opacity = 0.2; + grid.material.transparent = true; + scene.add(grid); + + loader = new FBXLoader(manager); + loadAsset(params.asset); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.shadowMap.enabled = true; + container.appendChild(renderer.domElement); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, 100, 0); + controls.update(); + + window.addEventListener('resize', onWindowResize); + + // stats + stats = new Stats(); + container.appendChild(stats.dom); + + const gui = new GUI(); + gui.add(params, 'asset', assets).onChange(function (value) { + loadAsset(value); + }); + + guiMorphsFolder = gui.addFolder('Morphs').hide(); +} + +function loadAsset(asset) { + loader.load('models/fbx/' + asset + '.fbx', function (group) { + if (object) { + object.traverse(function (child) { + if (child.isSkinnedMesh) { + child.skeleton.dispose(); + } + + if (child.material) { + const materials = Array.isArray(child.material) ? child.material : [child.material]; + materials.forEach(material => { + if (material.map) material.map.dispose(); + material.dispose(); + }); + } + + if (child.geometry) child.geometry.dispose(); + }); + + scene.remove(object); + } + + // + + object = group; + + if (object.animations && object.animations.length) { + mixer = new THREE.AnimationMixer(object); + + const action = mixer.clipAction(object.animations[0]); + action.play(); + } else { + mixer = null; + } + + guiMorphsFolder.children.forEach(child => child.destroy()); + guiMorphsFolder.hide(); + + object.traverse(function (child) { + if (child.isMesh) { + child.castShadow = true; + child.receiveShadow = true; + + if (child.morphTargetDictionary) { + guiMorphsFolder.show(); + const meshFolder = guiMorphsFolder.addFolder(child.name || child.uuid); + Object.keys(child.morphTargetDictionary).forEach(key => { + meshFolder.add(child.morphTargetInfluences, child.morphTargetDictionary[key], 0, 1, 0.01); + }); + } + } + }); + + scene.add(object); + }); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + timer.update(); + + const delta = timer.getDelta(); + + if (mixer) mixer.update(delta); + + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/webgl_loader_fbx_nurbs.ts b/examples-testing/examples/webgl_loader_fbx_nurbs.ts new file mode 100644 index 000000000..f2e45bcb5 --- /dev/null +++ b/examples-testing/examples/webgl_loader_fbx_nurbs.ts @@ -0,0 +1,61 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { FBXLoader } from 'three/addons/loaders/FBXLoader.js'; + +let camera, scene, renderer, stats; + +init(); + +function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000); + camera.position.set(2, 18, 28); + + scene = new THREE.Scene(); + + // grid + const gridHelper = new THREE.GridHelper(28, 28, 0x303030, 0x303030); + scene.add(gridHelper); + + // stats + stats = new Stats(); + container.appendChild(stats.dom); + + // model + const loader = new FBXLoader(); + loader.load('models/fbx/nurbs.fbx', function (object) { + scene.add(object); + }); + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, 12, 0); + controls.update(); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/webgl_loader_gcode.ts b/examples-testing/examples/webgl_loader_gcode.ts new file mode 100644 index 000000000..16dfb92e2 --- /dev/null +++ b/examples-testing/examples/webgl_loader_gcode.ts @@ -0,0 +1,81 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GCodeLoader } from 'three/addons/loaders/GCodeLoader.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let camera, scene, renderer, controls, loader; + +let model; + +const params = { + asset: 'benchy', +}; + +const assets = ['benchy', 'test_m82', 'test_m83']; + +const positions = [new THREE.Vector3(-100, -20, 100), new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 0, 0)]; + +init(); +render(); + +function init() { + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(0, 0, 70); + + scene = new THREE.Scene(); + + loader = new GCodeLoader(); + loadAsset(params.asset); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + document.body.appendChild(renderer.domElement); + + controls = new OrbitControls(camera, renderer.domElement); + controls.addEventListener('change', render); // use if there is no animation loop + controls.minDistance = 10; + controls.maxDistance = 100; + + window.addEventListener('resize', resize); + + const gui = new GUI(); + + gui.add(params, 'asset', assets).onChange(function (value) { + if (model) { + model.traverse(function (object) { + if (object.material) object.material.dispose(); + if (object.geometry) object.geometry.dispose(); + }); + + scene.remove(model); + } + + loadAsset(value); + }); + + gui.open(); +} + +function loadAsset(asset) { + loader.load('models/gcode/' + asset + '.gcode', function (object) { + model = object; + model.position.copy(positions[assets.indexOf(asset)]); + scene.add(model); + controls.reset(); + }); +} + +function resize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + render(); +} + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_loader_gltf.ts b/examples-testing/examples/webgl_loader_gltf.ts new file mode 100644 index 000000000..e0cb68546 --- /dev/null +++ b/examples-testing/examples/webgl_loader_gltf.ts @@ -0,0 +1,167 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let camera, scene, renderer, controls; +let currentModel, mixer; +let currentLoadId = 0; + +const timer = new THREE.Timer(); + +init(); + +function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); + camera.position.set(-1.8, 0.6, 2.7); + + scene = new THREE.Scene(); + + new UltraHDRLoader().setPath('textures/equirectangular/').load('royal_esplanade_2k.hdr.jpg', function (texture) { + texture.mapping = THREE.EquirectangularReflectionMapping; + + scene.background = texture; + scene.environment = texture; + + render(); + + // model + + fetch('https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Assets/main/Models/model-index.json') + .then(response => response.json()) + .then(models => { + const gui = new GUI(); + const modelNames = models.map(m => m.name); + const params = { model: 'DamagedHelmet' }; + + if (!modelNames.includes(params.model) && modelNames.length > 0) { + params.model = modelNames[0]; + } + + gui.add(params, 'model', modelNames).onChange(name => { + const modelInfo = models.find(m => m.name === name); + loadModel(modelInfo); + }); + + gui.add(scene, 'backgroundBlurriness', 0, 1); + + const initialModel = models.find(m => m.name === params.model); + if (initialModel) loadModel(initialModel); + }); + }); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(render); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + container.appendChild(renderer.domElement); + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + controls.minDistance = 2; + controls.maxDistance = 10; + controls.target.set(0, 0, -0.2); + controls.update(); + + window.addEventListener('resize', onWindowResize); +} + +function loadModel(modelInfo) { + const variants = modelInfo.variants; + const variant = variants['glTF-Binary'] || variants['glTF']; + const url = `https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Assets/main/Models/${modelInfo.name}/${variant.endsWith('.glb') ? 'glTF-Binary' : 'glTF'}/${variant}`; + + if (currentModel) { + scene.remove(currentModel); + currentModel = null; + } + + if (mixer) { + mixer.stopAllAction(); + mixer = null; + } + + const loadId = ++currentLoadId; + + const loader = new GLTFLoader(); + loader.load(url, async function (gltf) { + if (loadId !== currentLoadId) return; + + currentModel = gltf.scene; + + // wait until the model can be added to the scene without blocking due to shader compilation + + await renderer.compileAsync(currentModel, camera, scene); + + if (loadId !== currentLoadId) return; + + scene.add(currentModel); + + fitCameraToSelection(camera, controls, currentModel); + + // animations + + if (gltf.animations.length > 0) { + mixer = new THREE.AnimationMixer(currentModel); + + for (const animation of gltf.animations) { + mixer.clipAction(animation).play(); + } + } + }); +} + +function fitCameraToSelection(camera, controls, selection, fitOffset = 1.3) { + const box = new THREE.Box3(); + box.setFromObject(selection); + + const size = box.getSize(new THREE.Vector3()); + const center = box.getCenter(new THREE.Vector3()); + + const maxSize = Math.max(size.x, size.y, size.z); + const fitHeightDistance = maxSize / (2 * Math.atan((Math.PI * camera.fov) / 360)); + // const fitWidthDistance = fitHeightDistance / camera.aspect; + // const distance = fitOffset * Math.max( fitHeightDistance, fitWidthDistance ); + const distance = fitOffset * fitHeightDistance; + + const direction = controls.target.clone().sub(camera.position).normalize().multiplyScalar(distance); + + controls.maxDistance = distance * 10; + controls.minDistance = distance / 10; + controls.target.copy(center); + + camera.near = distance / 100; + camera.far = distance * 100; + camera.updateProjectionMatrix(); + + camera.position.copy(controls.target).sub(direction); + + controls.update(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + render(); +} + +// + +function render() { + timer.update(); + + controls.update(); + + if (mixer) mixer.update(timer.getDelta()); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_loader_gltf_animation_pointer.ts b/examples-testing/examples/webgl_loader_gltf_animation_pointer.ts new file mode 100644 index 000000000..443df047b --- /dev/null +++ b/examples-testing/examples/webgl_loader_gltf_animation_pointer.ts @@ -0,0 +1,91 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; + +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; +import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js'; +import { GLTFAnimationPointerExtension } from '@needle-tools/three-animation-pointer'; + +let mixer; + +const timer = new THREE.Timer(); +timer.connect(document); +const container = document.getElementById('container'); + +const stats = new Stats(); +container.appendChild(stats.dom); + +const renderer = new THREE.WebGLRenderer({ antialias: true }); +renderer.setPixelRatio(window.devicePixelRatio); +renderer.setSize(window.innerWidth, window.innerHeight); +container.appendChild(renderer.domElement); + +const pmremGenerator = new THREE.PMREMGenerator(renderer); + +const scene = new THREE.Scene(); +scene.background = new THREE.Color(0xbfe3dd); +scene.environment = pmremGenerator.fromScene(new RoomEnvironment(), 0.04).texture; + +const camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.2, 100); +camera.position.set(-3, 2, 6); + +const controls = new OrbitControls(camera, renderer.domElement); +controls.target.set(0, 0.5, 0); +controls.update(); +controls.enablePan = false; +controls.enableDamping = true; + +const dracoLoader = new DRACOLoader(); +dracoLoader.setDecoderPath('jsm/libs/draco/gltf/'); + +const ktx2Loader = new KTX2Loader().setTranscoderPath('jsm/libs/basis/').detectSupport(renderer); + +const loader = new GLTFLoader(); +loader.setDRACOLoader(dracoLoader); +loader.setKTX2Loader(ktx2Loader); + +loader.register(p => { + return new GLTFAnimationPointerExtension(p); +}); + +loader.load( + 'https://cloud.needle.tools/-/assets/Z23hmXB27L6Db-optimized/file', + function (gltf) { + const model = gltf.scene; + scene.add(model); + + mixer = new THREE.AnimationMixer(model); + mixer.clipAction(gltf.animations[0]).play(); + + renderer.setAnimationLoop(animate); + }, + undefined, + function (e) { + console.error(e); + }, +); + +window.onresize = function () { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +}; + +function animate() { + timer.update(); + + const delta = timer.getDelta(); + + mixer.update(delta); + + controls.update(); + + stats.update(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_loader_gltf_anisotropy.ts b/examples-testing/examples/webgl_loader_gltf_anisotropy.ts new file mode 100644 index 000000000..f0dd05cfe --- /dev/null +++ b/examples-testing/examples/webgl_loader_gltf_anisotropy.ts @@ -0,0 +1,68 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; + +let renderer, scene, camera, controls; + +init(); + +async function init() { + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = 1.35; + document.body.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.01, 10); + camera.position.set(-0.35, -0.2, 0.35); + + controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, -0.08, 0.11); + controls.minDistance = 0.1; + controls.maxDistance = 2; + controls.addEventListener('change', render); + controls.update(); + + const hdrLoader = new UltraHDRLoader().setPath('textures/equirectangular/'); + const gltfLoader = new GLTFLoader().setPath('models/gltf/'); + + const [texture, gltf] = await Promise.all([ + hdrLoader.loadAsync('royal_esplanade_2k.hdr.jpg'), + gltfLoader.loadAsync('AnisotropyBarnLamp.glb'), + ]); + + // environment + + texture.mapping = THREE.EquirectangularReflectionMapping; + + scene.background = texture; + scene.backgroundBlurriness = 0.5; + scene.environment = texture; + + // model + + scene.add(gltf.scene); + + render(); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + render(); +} + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_loader_gltf_avif.ts b/examples-testing/examples/webgl_loader_gltf_avif.ts new file mode 100644 index 000000000..37d63859e --- /dev/null +++ b/examples-testing/examples/webgl_loader_gltf_avif.ts @@ -0,0 +1,61 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; + +let camera, scene, renderer; + +init(); +render(); + +function init() { + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(1.5, 4, 9); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xf6eedc); + + // + + const dracoLoader = new DRACOLoader(); + dracoLoader.setDecoderPath('jsm/libs/draco/gltf/'); + + const loader = new GLTFLoader(); + loader.setDRACOLoader(dracoLoader); + loader.setPath('models/gltf/AVIFTest/'); + loader.load('forest_house.glb', function (gltf) { + scene.add(gltf.scene); + + render(); + }); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + document.body.appendChild(renderer.domElement); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.addEventListener('change', render); + controls.target.set(0, 2, 0); + controls.update(); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + render(); +} + +// + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_loader_gltf_compressed.ts b/examples-testing/examples/webgl_loader_gltf_compressed.ts new file mode 100644 index 000000000..235d9b3d0 --- /dev/null +++ b/examples-testing/examples/webgl_loader_gltf_compressed.ts @@ -0,0 +1,83 @@ +import * as THREE from 'three'; + +import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js'; +import { MeshoptDecoder } from 'three/addons/libs/meshopt_decoder.module.js'; + +let camera, scene, renderer; + +init(); +render(); + +function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = 1; + container.appendChild(renderer.domElement); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000); + camera.position.set(0, 100, 0); + + const environment = new RoomEnvironment(); + const pmremGenerator = new THREE.PMREMGenerator(renderer); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xbbbbbb); + scene.environment = pmremGenerator.fromScene(environment, 0.04).texture; + environment.dispose(); + + const grid = new THREE.GridHelper(500, 10, 0xffffff, 0xffffff); + grid.material.opacity = 0.5; + grid.material.depthWrite = false; + grid.material.transparent = true; + scene.add(grid); + + const ktx2Loader = new KTX2Loader().setTranscoderPath('jsm/libs/basis/').detectSupport(renderer); + + const loader = new GLTFLoader().setPath('models/gltf/'); + loader.setKTX2Loader(ktx2Loader); + loader.setMeshoptDecoder(MeshoptDecoder); + loader.load('coffeemat.glb', function (gltf) { + // coffeemat.glb was produced from the source scene using gltfpack: + // gltfpack -i coffeemat/scene.gltf -o coffeemat.glb -cc -tc + // The resulting model uses EXT_meshopt_compression (for geometry) and KHR_texture_basisu (for texture compression using ETC1S/BasisLZ) + + gltf.scene.position.y = 8; + + scene.add(gltf.scene); + + render(); + }); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.addEventListener('change', render); // use if there is no animation loop + controls.minDistance = 400; + controls.maxDistance = 1000; + controls.target.set(10, 90, -16); + controls.update(); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + render(); +} + +// + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_loader_gltf_dispersion.ts b/examples-testing/examples/webgl_loader_gltf_dispersion.ts new file mode 100644 index 000000000..0f1d7e57a --- /dev/null +++ b/examples-testing/examples/webgl_loader_gltf_dispersion.ts @@ -0,0 +1,66 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; + +let camera, scene, renderer; + +init().then(render); + +async function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.01, 5); + camera.position.set(0.1, 0.05, 0.15); + + scene = new THREE.Scene(); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.toneMapping = THREE.NeutralToneMapping; + renderer.toneMappingExposure = 1; + container.appendChild(renderer.domElement); + + const environment = new RoomEnvironment(); + const pmremGenerator = new THREE.PMREMGenerator(renderer); + + scene = new THREE.Scene(); + scene.backgroundBlurriness = 0.5; + + const env = pmremGenerator.fromScene(environment, 0.04).texture; + scene.background = env; + scene.environment = env; + environment.dispose(); + + const loader = new GLTFLoader(); + const gltf = await loader.loadAsync('models/gltf/DispersionTest.glb'); + + scene.add(gltf.scene); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.addEventListener('change', render); // use if there is no animation loop + controls.minDistance = 0.1; + controls.maxDistance = 10; + controls.target.set(0, 0, 0); + controls.update(); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + render(); +} + +// + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_loader_gltf_instancing.ts b/examples-testing/examples/webgl_loader_gltf_instancing.ts new file mode 100644 index 000000000..6acb140ec --- /dev/null +++ b/examples-testing/examples/webgl_loader_gltf_instancing.ts @@ -0,0 +1,69 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; + +let camera, scene, renderer; + +init(); +render(); + +function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); + camera.position.set(-0.9, 0.41, -0.89); + + scene = new THREE.Scene(); + + new UltraHDRLoader().setPath('textures/equirectangular/').load('royal_esplanade_2k.hdr.jpg', function (texture) { + texture.mapping = THREE.EquirectangularReflectionMapping; + + scene.background = texture; + scene.environment = texture; + + render(); + + // model + + const loader = new GLTFLoader().setPath('models/gltf/DamagedHelmet/glTF-instancing/'); + loader.load('DamagedHelmetGpuInstancing.gltf', function (gltf) { + scene.add(gltf.scene); + + render(); + }); + }); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = 1; + container.appendChild(renderer.domElement); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.addEventListener('change', render); // use if there is no animation loop + controls.minDistance = 0.2; + controls.maxDistance = 10; + controls.target.set(0, 0.25, 0); + controls.update(); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + render(); +} + +// + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_loader_gltf_iridescence.ts b/examples-testing/examples/webgl_loader_gltf_iridescence.ts new file mode 100644 index 000000000..5519835fe --- /dev/null +++ b/examples-testing/examples/webgl_loader_gltf_iridescence.ts @@ -0,0 +1,66 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; + +let renderer, scene, camera, controls; + +init().catch(function (err) { + console.error(err); +}); + +async function init() { + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setAnimationLoop(animate); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + document.body.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.05, 20); + camera.position.set(0.35, 0.05, 0.35); + + controls = new OrbitControls(camera, renderer.domElement); + controls.autoRotate = true; + controls.autoRotateSpeed = -0.5; + controls.target.set(0, 0.2, 0); + controls.update(); + + const hdrLoader = new HDRLoader().setPath('textures/equirectangular/'); + + const gltfLoader = new GLTFLoader().setPath('models/gltf/'); + + const [texture, gltf] = await Promise.all([ + hdrLoader.loadAsync('venice_sunset_1k.hdr'), + gltfLoader.loadAsync('IridescenceLamp.glb'), + ]); + + // environment + + texture.mapping = THREE.EquirectangularReflectionMapping; + + scene.background = texture; + scene.environment = texture; + + // model + + scene.add(gltf.scene); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + controls.update(); + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_loader_gltf_progressive_lod.ts b/examples-testing/examples/webgl_loader_gltf_progressive_lod.ts new file mode 100644 index 000000000..8d1c60480 --- /dev/null +++ b/examples-testing/examples/webgl_loader_gltf_progressive_lod.ts @@ -0,0 +1,143 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; +import { useNeedleProgressive } from '@needle-tools/gltf-progressive'; + +let camera, scene, renderer, mixer; +let airshipModel; + +init(); + +function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + scene = new THREE.Scene(); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = 1; + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.1, 40); + camera.position.set(-9, 2, -13); + + const fog = new THREE.Fog('#131055', 15, 50); + scene.fog = fog; + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 0.1; + controls.maxDistance = 20; + controls.target.set(-1, 2.1, 0); + controls.update(); + + new HDRLoader().setPath('textures/equirectangular/').load('quarry_01_1k.hdr', function (texture) { + texture.mapping = THREE.EquirectangularReflectionMapping; + + scene.background = new THREE.Color('#192022'); + scene.backgroundBlurriness = 0.5; + scene.environment = texture; + scene.environmentRotation = new THREE.Euler(0, Math.PI / -2, 0, 'XYZ'); + }); + + mixer = new THREE.AnimationMixer(scene); + + const loader = new GLTFLoader(); + + useNeedleProgressive(loader, renderer); + + loader.load('https://cloud.needle.tools/-/assets/Z23hmXBZ2sPRdk-world/file', function (gltf) { + const model = gltf.scene; + + model.scale.multiplyScalar(0.1); + + scene.add(model); + + const animations = gltf.animations; + if (animations && animations.length) { + for (const animation of animations) { + mixer.clipAction(animation).play(); + } + } + }); + + loader.load('https://cloud.needle.tools/-/assets/Z23hmXBZnlceI-ZnlceI-world/file', function (gltf) { + const model = gltf.scene; + + model.scale.multiplyScalar(0.0005); + + model.position.set(1.6, 6, 7); + + model.rotation.set(0, Math.PI * 1.4, 0); + + scene.add(model); + + airshipModel = model; + + const animations = gltf.animations; + + if (animations && animations.length) { + for (const animation of animations) { + mixer.clipAction(animation).play(); + } + } + }); + + loader.load('https://cloud.needle.tools/-/assets/Z23hmXBZ21QnG-Z21QnG-product/file', function (gltf) { + const model = gltf.scene; + + model.scale.multiplyScalar(0.5); + + model.position.set(2, 5.15, 2.3); + + model.rotation.set(0, Math.PI * 1, 0); + + scene.add(model); + + const animations = gltf.animations; + if (animations && animations.length) { + for (const animation of animations) { + mixer.clipAction(animation).play(); + } + } + }); + + container.appendChild(renderer.domElement); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +const timer = new THREE.Timer(); +timer.connect(document); +let time = 0; + +function animate() { + timer.update(); + + const dt = timer.getDelta(); + time += dt; + + mixer.update(dt); + + if (airshipModel) { + airshipModel.position.y += Math.sin(time) * 0.002; + } + + renderer.render(scene, camera); + + window.requestAnimationFrame(animate); +} + +animate(); diff --git a/examples-testing/examples/webgl_loader_gltf_sheen.ts b/examples-testing/examples/webgl_loader_gltf_sheen.ts new file mode 100644 index 000000000..b058f1e27 --- /dev/null +++ b/examples-testing/examples/webgl_loader_gltf_sheen.ts @@ -0,0 +1,72 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let camera, scene, renderer, controls; + +init(); + +function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 20); + camera.position.set(-0.75, 0.7, 1.25); + + scene = new THREE.Scene(); + + // model + + new GLTFLoader().setPath('models/gltf/').load('SheenChair.glb', function (gltf) { + scene.add(gltf.scene); + + const object = gltf.scene.getObjectByName('SheenChair_fabric'); + + const gui = new GUI(); + + gui.add(object.material, 'sheen', 0, 1); + gui.open(); + }); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = 1; + container.appendChild(renderer.domElement); + + const environment = new RoomEnvironment(); + const pmremGenerator = new THREE.PMREMGenerator(renderer); + + scene.background = new THREE.Color(0xbbbbbb); + scene.environment = pmremGenerator.fromScene(environment, 0.04).texture; + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + controls.minDistance = 1; + controls.maxDistance = 10; + controls.target.set(0, 0.35, 0); + controls.update(); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + controls.update(); // required if damping enabled + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_loader_gltf_transmission.ts b/examples-testing/examples/webgl_loader_gltf_transmission.ts new file mode 100644 index 000000000..e8ea0a927 --- /dev/null +++ b/examples-testing/examples/webgl_loader_gltf_transmission.ts @@ -0,0 +1,83 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; + +import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; + +let camera, scene, renderer, controls, timer, mixer; + +init(); + +function init() { + timer = new THREE.Timer(); + timer.connect(document); + + const container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); + camera.position.set(0, 0.4, 0.7); + + scene = new THREE.Scene(); + + new UltraHDRLoader().setPath('textures/equirectangular/').load('royal_esplanade_2k.hdr.jpg', function (texture) { + texture.mapping = THREE.EquirectangularReflectionMapping; + + scene.background = texture; + scene.backgroundBlurriness = 0.35; + + scene.environment = texture; + + // model + + new GLTFLoader() + .setPath('models/gltf/') + .setDRACOLoader(new DRACOLoader().setDecoderPath('jsm/libs/draco/gltf/')) + .load('IridescentDishWithOlives.glb', function (gltf) { + mixer = new THREE.AnimationMixer(gltf.scene); + mixer.clipAction(gltf.animations[0]).play(); + + scene.add(gltf.scene); + }); + }); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = 1; + container.appendChild(renderer.domElement); + + controls = new OrbitControls(camera, renderer.domElement); + controls.autoRotate = true; + controls.autoRotateSpeed = -0.75; + controls.enableDamping = true; + controls.minDistance = 0.5; + controls.maxDistance = 1; + controls.target.set(0, 0.1, 0); + controls.update(); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + timer.update(); + + if (mixer) mixer.update(timer.getDelta()); + + controls.update(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_loader_imagebitmap.ts b/examples-testing/examples/webgl_loader_imagebitmap.ts new file mode 100644 index 000000000..c02a03e17 --- /dev/null +++ b/examples-testing/examples/webgl_loader_imagebitmap.ts @@ -0,0 +1,113 @@ +import * as THREE from 'three'; + +let camera, scene, renderer; +let group, cubes; + +init(); + +function addImageBitmap() { + new THREE.ImageBitmapLoader().setOptions({ imageOrientation: 'flipY' }).load( + 'textures/planets/earth_atmos_2048.jpg?' + performance.now(), + function (imageBitmap) { + const texture = new THREE.CanvasTexture(imageBitmap); + texture.colorSpace = THREE.SRGBColorSpace; + const material = new THREE.MeshBasicMaterial({ map: texture }); + + // ImageBitmap should be disposed when done with it. + + texture.onUpdate = disposeImageBitmap; + + addCube(material); + }, + function (p) { + console.log(p); + }, + function (e) { + console.log(e); + }, + ); +} + +function addImage() { + new THREE.ImageLoader() + .setCrossOrigin('*') + .load('textures/planets/earth_atmos_2048.jpg?' + performance.now(), function (image) { + const texture = new THREE.CanvasTexture(image); + texture.colorSpace = THREE.SRGBColorSpace; + const material = new THREE.MeshBasicMaterial({ color: 0xff8888, map: texture }); + addCube(material); + }); +} + +const geometry = new THREE.BoxGeometry(); + +function addCube(material) { + const cube = new THREE.Mesh(geometry, material); + cube.position.set(Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1); + cube.rotation.set(Math.random() * 2 * Math.PI, Math.random() * 2 * Math.PI, Math.random() * 2 * Math.PI); + cubes.add(cube); +} + +function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + // CAMERA + + camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 1500); + camera.position.set(0, 4, 7); + camera.lookAt(0, 0, 0); + + // SCENE + + scene = new THREE.Scene(); + + // + + group = new THREE.Group(); + scene.add(group); + + group.add(new THREE.GridHelper(4, 12, 0x888888, 0x444444)); + + cubes = new THREE.Group(); + group.add(cubes); + + // RENDERER + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + // TESTS + + setTimeout(addImage, 300); + setTimeout(addImage, 600); + setTimeout(addImage, 900); + setTimeout(addImageBitmap, 1300); + setTimeout(addImageBitmap, 1600); + setTimeout(addImageBitmap, 1900); + + // EVENTS + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + group.rotation.y = performance.now() / 3000; + + renderer.render(scene, camera); +} + +function disposeImageBitmap(texture) { + texture.source.data.close(); + texture.onUpdate = null; // make sure this callback is executed only once per texture +} diff --git a/examples-testing/examples/webgl_loader_kmz.ts b/examples-testing/examples/webgl_loader_kmz.ts new file mode 100644 index 000000000..f93555e41 --- /dev/null +++ b/examples-testing/examples/webgl_loader_kmz.ts @@ -0,0 +1,59 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { KMZLoader } from 'three/addons/loaders/KMZLoader.js'; + +let camera, scene, renderer; + +init(); + +function init() { + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x999999); + + const light = new THREE.DirectionalLight(0xffffff, 3); + light.position.set(0.5, 1.0, 0.5).normalize(); + + scene.add(light); + + camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 500); + + camera.position.y = 5; + camera.position.z = 10; + + scene.add(camera); + + const grid = new THREE.GridHelper(50, 50, 0xffffff, 0x7b7b7b); + scene.add(grid); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + document.body.appendChild(renderer.domElement); + + const loader = new KMZLoader(); + loader.load('./models/kmz/Box.kmz', function (kmz) { + kmz.scene.position.y = 0.5; + scene.add(kmz.scene); + render(); + }); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.addEventListener('change', render); + controls.update(); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + render(); +} + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_loader_lwo.ts b/examples-testing/examples/webgl_loader_lwo.ts new file mode 100644 index 000000000..fb10c8340 --- /dev/null +++ b/examples-testing/examples/webgl_loader_lwo.ts @@ -0,0 +1,69 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { LWOLoader } from 'three/addons/loaders/LWOLoader.js'; + +let camera, scene, renderer; + +init(); + +function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 200); + camera.position.set(0.7, 14.6, -43.2); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xa0a0a0); + + const ambientLight = new THREE.AmbientLight(0xbbbbbb); + scene.add(ambientLight); + + const light1 = new THREE.DirectionalLight(0xc1c1c1, 3); + light1.position.set(0, 200, -100); + scene.add(light1); + + const grid = new THREE.GridHelper(200, 20, 0x000000, 0x000000); + grid.material.opacity = 0.3; + grid.material.transparent = true; + scene.add(grid); + + const loader = new LWOLoader(); + loader.load('models/lwo/Objects/LWO3/Demo.lwo', function (object) { + const phong = object.meshes[0]; + phong.position.set(2, 12, 0); + + const standard = object.meshes[1]; + standard.position.set(-2, 12, 0); + + const rocket = object.meshes[2]; + rocket.position.set(0, 10.5, 1); + + scene.add(phong, standard, rocket); + }); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + container.appendChild(renderer.domElement); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(-1.33, 10, 6.7); + controls.update(); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_loader_md2_control.ts b/examples-testing/examples/webgl_loader_md2_control.ts new file mode 100644 index 000000000..56b7dc3c4 --- /dev/null +++ b/examples-testing/examples/webgl_loader_md2_control.ts @@ -0,0 +1,292 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { MD2CharacterComplex } from 'three/addons/misc/MD2CharacterComplex.js'; +import { Gyroscope } from 'three/addons/misc/Gyroscope.js'; + +let SCREEN_WIDTH = window.innerWidth; +let SCREEN_HEIGHT = window.innerHeight; + +let container, stats; +let camera, scene, renderer; + +const characters = []; +let nCharacters = 0; + +let cameraControls; + +const controls = { + moveForward: false, + moveBackward: false, + moveLeft: false, + moveRight: false, +}; + +const timer = new THREE.Timer(); +timer.connect(document); + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + // CAMERA + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 4000); + camera.position.set(0, 150, 1300); + + // SCENE + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xffffff); + scene.fog = new THREE.Fog(0xffffff, 1000, 4000); + + scene.add(camera); + + // LIGHTS + + scene.add(new THREE.AmbientLight(0x666666, 3)); + + const light = new THREE.DirectionalLight(0xffffff, 7); + light.position.set(200, 450, 500); + + light.castShadow = true; + + light.shadow.mapSize.width = 1024; + light.shadow.mapSize.height = 512; + + light.shadow.camera.near = 100; + light.shadow.camera.far = 1200; + + light.shadow.camera.left = -1000; + light.shadow.camera.right = 1000; + light.shadow.camera.top = 350; + light.shadow.camera.bottom = -350; + + scene.add(light); + // scene.add( new THREE.CameraHelper( light.shadow.camera ) ); + + // GROUND + + const gt = new THREE.TextureLoader().load('textures/terrain/grasslight-big.jpg'); + const gg = new THREE.PlaneGeometry(16000, 16000); + const gm = new THREE.MeshPhongMaterial({ color: 0xffffff, map: gt }); + + const ground = new THREE.Mesh(gg, gm); + ground.rotation.x = -Math.PI / 2; + ground.material.map.repeat.set(64, 64); + ground.material.map.wrapS = THREE.RepeatWrapping; + ground.material.map.wrapT = THREE.RepeatWrapping; + ground.material.map.colorSpace = THREE.SRGBColorSpace; + // note that because the ground does not cast a shadow, .castShadow is left false + ground.receiveShadow = true; + + scene.add(ground); + + // RENDERER + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + // + + renderer.shadowMap.enabled = true; + renderer.shadowMap.type = THREE.PCFShadowMap; + + // STATS + + stats = new Stats(); + container.appendChild(stats.dom); + + // EVENTS + + window.addEventListener('resize', onWindowResize); + document.addEventListener('keydown', onKeyDown); + document.addEventListener('keyup', onKeyUp); + + // CONTROLS + + cameraControls = new OrbitControls(camera, renderer.domElement); + cameraControls.target.set(0, 50, 0); + cameraControls.update(); + + // CHARACTER + + const configOgro = { + baseUrl: 'models/md2/ogro/', + + body: 'ogro.md2', + skins: [ + 'grok.jpg', + 'ogrobase.png', + 'arboshak.png', + 'ctf_r.png', + 'ctf_b.png', + 'darkam.png', + 'freedom.png', + 'gib.png', + 'gordogh.png', + 'igdosh.png', + 'khorne.png', + 'nabogro.png', + 'sharokh.png', + ], + weapons: [['weapon.md2', 'weapon.jpg']], + animations: { + move: 'run', + idle: 'stand', + jump: 'jump', + attack: 'attack', + crouchMove: 'cwalk', + crouchIdle: 'cstand', + crouchAttach: 'crattack', + }, + + walkSpeed: 350, + crouchSpeed: 175, + }; + + const nRows = 1; + const nSkins = configOgro.skins.length; + + nCharacters = nSkins * nRows; + + for (let i = 0; i < nCharacters; i++) { + const character = new MD2CharacterComplex(); + character.scale = 3; + character.controls = controls; + characters.push(character); + } + + const baseCharacter = new MD2CharacterComplex(); + baseCharacter.scale = 3; + + baseCharacter.onLoadComplete = function () { + let k = 0; + + for (let j = 0; j < nRows; j++) { + for (let i = 0; i < nSkins; i++) { + const cloneCharacter = characters[k]; + + cloneCharacter.shareParts(baseCharacter); + + // cast and receive shadows + cloneCharacter.enableShadows(true); + + cloneCharacter.setWeapon(0); + cloneCharacter.setSkin(i); + + cloneCharacter.root.position.x = (i - nSkins / 2) * 150; + cloneCharacter.root.position.z = j * 250; + + scene.add(cloneCharacter.root); + + k++; + } + } + + const gyro = new Gyroscope(); + gyro.add(camera); + gyro.add(light, light.target); + + characters[Math.floor(nSkins / 2)].root.add(gyro); + }; + + baseCharacter.loadParts(configOgro); +} + +// EVENT HANDLERS + +function onWindowResize() { + SCREEN_WIDTH = window.innerWidth; + SCREEN_HEIGHT = window.innerHeight; + + renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); + + camera.aspect = SCREEN_WIDTH / SCREEN_HEIGHT; + camera.updateProjectionMatrix(); +} + +function onKeyDown(event) { + switch (event.code) { + case 'ArrowUp': + case 'KeyW': + controls.moveForward = true; + break; + + case 'ArrowDown': + case 'KeyS': + controls.moveBackward = true; + break; + + case 'ArrowLeft': + case 'KeyA': + controls.moveLeft = true; + break; + + case 'ArrowRight': + case 'KeyD': + controls.moveRight = true; + break; + + // case 'KeyC': controls.crouch = true; break; + // case 'Space': controls.jump = true; break; + // case 'ControlLeft': + // case 'ControlRight': controls.attack = true; break; + } +} + +function onKeyUp(event) { + switch (event.code) { + case 'ArrowUp': + case 'KeyW': + controls.moveForward = false; + break; + + case 'ArrowDown': + case 'KeyS': + controls.moveBackward = false; + break; + + case 'ArrowLeft': + case 'KeyA': + controls.moveLeft = false; + break; + + case 'ArrowRight': + case 'KeyD': + controls.moveRight = false; + break; + + // case 'KeyC': controls.crouch = false; break; + // case 'Space': controls.jump = false; break; + // case 'ControlLeft': + // case 'ControlRight': controls.attack = false; break; + } +} + +// + +function animate() { + timer.update(); + + render(); + + stats.update(); +} + +function render() { + const delta = timer.getDelta(); + + for (let i = 0; i < nCharacters; i++) { + characters[i].update(delta); + } + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_loader_mdd.ts b/examples-testing/examples/webgl_loader_mdd.ts new file mode 100644 index 000000000..16e49221c --- /dev/null +++ b/examples-testing/examples/webgl_loader_mdd.ts @@ -0,0 +1,65 @@ +import * as THREE from 'three'; + +import { MDDLoader } from 'three/addons/loaders/MDDLoader.js'; + +let camera, scene, renderer, mixer, timer; + +init(); + +function init() { + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(8, 8, 8); + camera.lookAt(scene.position); + + timer = new THREE.Timer(); + timer.connect(document); + + // + + const loader = new MDDLoader(); + loader.load('models/mdd/cube.mdd', function (result) { + const morphTargets = result.morphTargets; + const clip = result.clip; + // clip.optimize(); // optional + + const geometry = new THREE.BoxGeometry(); + geometry.morphAttributes.position = morphTargets; // apply morph targets + + const material = new THREE.MeshNormalMaterial(); + + const mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + mixer = new THREE.AnimationMixer(mesh); + mixer.clipAction(clip).play(); // use clip + }); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + const delta = timer.getDelta(); + + if (mixer) mixer.update(delta); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_loader_obj.ts b/examples-testing/examples/webgl_loader_obj.ts new file mode 100644 index 000000000..42f1f3257 --- /dev/null +++ b/examples-testing/examples/webgl_loader_obj.ts @@ -0,0 +1,72 @@ +import * as THREE from 'three'; + +import { MTLLoader } from 'three/addons/loaders/MTLLoader.js'; +import { OBJLoader } from 'three/addons/loaders/OBJLoader.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let camera, scene, renderer, controls; + +init(); + +async function init() { + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 20); + camera.position.z = 2.5; + + // scene + + scene = new THREE.Scene(); + + const ambientLight = new THREE.AmbientLight(0xffffff); + scene.add(ambientLight); + + const pointLight = new THREE.PointLight(0xffffff, 15); + camera.add(pointLight); + scene.add(camera); + + // model + + const mtlLoader = new MTLLoader().setPath('models/obj/male02/'); + const materials = await mtlLoader.loadAsync('male02.mtl'); + materials.preload(); + + const objLoader = new OBJLoader().setPath('models/obj/male02/'); + objLoader.setMaterials(materials); // optional since OBJ assets can be loaded without an accompanying MTL file + + const object = await objLoader.loadAsync('male02.obj'); + + object.position.y = -0.95; + object.scale.setScalar(0.01); + scene.add(object); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + controls.minDistance = 2; + controls.maxDistance = 5; + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + controls.update(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_loader_pcd.ts b/examples-testing/examples/webgl_loader_pcd.ts new file mode 100644 index 000000000..dd0f0b0f5 --- /dev/null +++ b/examples-testing/examples/webgl_loader_pcd.ts @@ -0,0 +1,78 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { PCDLoader } from 'three/addons/loaders/PCDLoader.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let camera, scene, renderer; + +init(); +render(); + +function init() { + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + document.body.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 0.01, 40); + camera.position.set(0, 0, 1); + scene.add(camera); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.addEventListener('change', render); // use if there is no animation loop + controls.minDistance = 0.5; + controls.maxDistance = 10; + + //scene.add( new THREE.AxesHelper( 1 ) ); + + const loader = new PCDLoader(); + + const loadPointCloud = function (file) { + loader.load('./models/pcd/' + file, function (points) { + points.geometry.center(); + points.geometry.rotateX(Math.PI); + points.name = file; + scene.add(points); + + const gui = new GUI(); + + gui.add(points.material, 'size', 0.001, 0.01).onChange(render); + gui.addColor(points.material, 'color').onChange(render); + gui.add(points, 'name', [ + 'ascii/simple.pcd', + 'binary/Zaghetto.pcd', + 'binary/Zaghetto_8bit.pcd', + 'binary_compressed/pcl_logo.pcd', + ]) + .name('type') + .onChange(e => { + gui.destroy(); + scene.remove(points); + loadPointCloud(e); + }); + gui.open(); + + render(); + }); + }; + + loadPointCloud('binary/Zaghetto.pcd'); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + render(); +} + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_loader_pdb.ts b/examples-testing/examples/webgl_loader_pdb.ts new file mode 100644 index 000000000..b560efa73 --- /dev/null +++ b/examples-testing/examples/webgl_loader_pdb.ts @@ -0,0 +1,208 @@ +import * as THREE from 'three'; + +import { TrackballControls } from 'three/addons/controls/TrackballControls.js'; +import { PDBLoader } from 'three/addons/loaders/PDBLoader.js'; +import { CSS2DRenderer, CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let camera, scene, renderer, labelRenderer; +let controls; + +let root; + +const MOLECULES = { + Ethanol: 'ethanol.pdb', + Aspirin: 'aspirin.pdb', + Caffeine: 'caffeine.pdb', + Nicotine: 'nicotine.pdb', + LSD: 'lsd.pdb', + Cocaine: 'cocaine.pdb', + Cholesterol: 'cholesterol.pdb', + Lycopene: 'lycopene.pdb', + Glucose: 'glucose.pdb', + 'Aluminium oxide': 'Al2O3.pdb', + Cubane: 'cubane.pdb', + Copper: 'cu.pdb', + Fluorite: 'caf2.pdb', + Salt: 'nacl.pdb', + 'YBCO superconductor': 'ybco.pdb', + Buckyball: 'buckyball.pdb', + Graphite: 'graphite.pdb', +}; + +const params = { + molecule: 'caffeine.pdb', +}; + +const loader = new PDBLoader(); +const offset = new THREE.Vector3(); + +init(); + +function init() { + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x050505); + + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 5000); + camera.position.z = 1000; + scene.add(camera); + + const light1 = new THREE.DirectionalLight(0xffffff, 2.5); + light1.position.set(1, 1, 1); + scene.add(light1); + + const light2 = new THREE.DirectionalLight(0xffffff, 1.5); + light2.position.set(-1, -1, 1); + scene.add(light2); + + root = new THREE.Group(); + scene.add(root); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.getElementById('container').appendChild(renderer.domElement); + + labelRenderer = new CSS2DRenderer(); + labelRenderer.setSize(window.innerWidth, window.innerHeight); + labelRenderer.domElement.style.position = 'absolute'; + labelRenderer.domElement.style.top = '0px'; + labelRenderer.domElement.style.pointerEvents = 'none'; + document.getElementById('container').appendChild(labelRenderer.domElement); + + // + + controls = new TrackballControls(camera, renderer.domElement); + controls.minDistance = 500; + controls.maxDistance = 2000; + + // + + loadMolecule(params.molecule); + + // + + window.addEventListener('resize', onWindowResize); + + // + + const gui = new GUI(); + + gui.add(params, 'molecule', MOLECULES).onChange(loadMolecule); + gui.open(); +} + +// + +function loadMolecule(model) { + const url = 'models/pdb/' + model; + + while (root.children.length > 0) { + const object = root.children[0]; + object.parent.remove(object); + } + + loader.load(url, function (pdb) { + const geometryAtoms = pdb.geometryAtoms; + const geometryBonds = pdb.geometryBonds; + const json = pdb.json; + + const boxGeometry = new THREE.BoxGeometry(1, 1, 1); + const sphereGeometry = new THREE.IcosahedronGeometry(1, 3); + + geometryAtoms.computeBoundingBox(); + geometryAtoms.boundingBox.getCenter(offset).negate(); + + geometryAtoms.translate(offset.x, offset.y, offset.z); + geometryBonds.translate(offset.x, offset.y, offset.z); + + let positions = geometryAtoms.getAttribute('position'); + const colors = geometryAtoms.getAttribute('color'); + + const position = new THREE.Vector3(); + const color = new THREE.Color(); + + for (let i = 0; i < positions.count; i++) { + position.x = positions.getX(i); + position.y = positions.getY(i); + position.z = positions.getZ(i); + + color.r = colors.getX(i); + color.g = colors.getY(i); + color.b = colors.getZ(i); + + const material = new THREE.MeshPhongMaterial({ color: color }); + + const object = new THREE.Mesh(sphereGeometry, material); + object.position.copy(position); + object.position.multiplyScalar(75); + object.scale.multiplyScalar(25); + root.add(object); + + const atom = json.atoms[i]; + + const text = document.createElement('div'); + text.className = 'label'; + text.style.color = 'rgb(' + atom[3][0] + ',' + atom[3][1] + ',' + atom[3][2] + ')'; + text.textContent = atom[4]; + + const label = new CSS2DObject(text); + label.position.copy(object.position); + root.add(label); + } + + positions = geometryBonds.getAttribute('position'); + + const start = new THREE.Vector3(); + const end = new THREE.Vector3(); + + for (let i = 0; i < positions.count; i += 2) { + start.x = positions.getX(i); + start.y = positions.getY(i); + start.z = positions.getZ(i); + + end.x = positions.getX(i + 1); + end.y = positions.getY(i + 1); + end.z = positions.getZ(i + 1); + + start.multiplyScalar(75); + end.multiplyScalar(75); + + const object = new THREE.Mesh(boxGeometry, new THREE.MeshPhongMaterial({ color: 0xffffff })); + object.position.copy(start); + object.position.lerp(end, 0.5); + object.scale.set(5, 5, start.distanceTo(end)); + object.lookAt(end); + root.add(object); + } + }); +} + +// + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + labelRenderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + controls.update(); + + const time = Date.now() * 0.0004; + + root.rotation.x = time; + root.rotation.y = time * 0.7; + + render(); +} + +function render() { + renderer.render(scene, camera); + labelRenderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_loader_ply.ts b/examples-testing/examples/webgl_loader_ply.ts new file mode 100644 index 000000000..0f4042b7d --- /dev/null +++ b/examples-testing/examples/webgl_loader_ply.ts @@ -0,0 +1,146 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { PLYLoader } from 'three/addons/loaders/PLYLoader.js'; + +let container, stats; + +let camera, cameraTarget, scene, renderer; + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 15); + camera.position.set(3, 0.15, 3); + + cameraTarget = new THREE.Vector3(0, -0.1, 0); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x72645b); + scene.fog = new THREE.Fog(0x72645b, 2, 15); + + // Ground + + const plane = new THREE.Mesh( + new THREE.PlaneGeometry(40, 40), + new THREE.MeshPhongMaterial({ color: 0xcbcbcb, specular: 0x474747 }), + ); + plane.rotation.x = -Math.PI / 2; + plane.position.y = -0.5; + scene.add(plane); + + plane.receiveShadow = true; + + // PLY file + + const loader = new PLYLoader(); + loader.load('./models/ply/ascii/dolphins.ply', function (geometry) { + geometry.computeVertexNormals(); + + const material = new THREE.MeshStandardMaterial({ color: 0x009cff, flatShading: true }); + const mesh = new THREE.Mesh(geometry, material); + + mesh.position.y = -0.2; + mesh.position.z = 0.3; + mesh.rotation.x = -Math.PI / 2; + mesh.scale.multiplyScalar(0.001); + + mesh.castShadow = true; + mesh.receiveShadow = true; + + scene.add(mesh); + }); + + loader.load('./models/ply/binary/Lucy100k.ply', function (geometry) { + geometry.computeVertexNormals(); + + const material = new THREE.MeshStandardMaterial({ color: 0x009cff, flatShading: true }); + const mesh = new THREE.Mesh(geometry, material); + + mesh.position.x = -0.2; + mesh.position.y = -0.02; + mesh.position.z = -0.2; + mesh.scale.multiplyScalar(0.0006); + + mesh.castShadow = true; + mesh.receiveShadow = true; + + scene.add(mesh); + }); + + // Lights + + scene.add(new THREE.HemisphereLight(0x8d7c7c, 0x494966, 3)); + + addShadowedLight(1, 1, 1, 0xffffff, 3.5); + addShadowedLight(0.5, 1, -1, 0xffd500, 3); + + // renderer + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + + renderer.shadowMap.enabled = true; + + container.appendChild(renderer.domElement); + + // stats + + stats = new Stats(); + container.appendChild(stats.dom); + + // resize + + window.addEventListener('resize', onWindowResize); +} + +function addShadowedLight(x, y, z, color, intensity) { + const directionalLight = new THREE.DirectionalLight(color, intensity); + directionalLight.position.set(x, y, z); + scene.add(directionalLight); + + directionalLight.castShadow = true; + + const d = 1; + directionalLight.shadow.camera.left = -d; + directionalLight.shadow.camera.right = d; + directionalLight.shadow.camera.top = d; + directionalLight.shadow.camera.bottom = -d; + + directionalLight.shadow.camera.near = 1; + directionalLight.shadow.camera.far = 4; + + directionalLight.shadow.mapSize.width = 1024; + directionalLight.shadow.mapSize.height = 1024; + + directionalLight.shadow.bias = -0.001; +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + render(); + stats.update(); +} + +function render() { + const timer = Date.now() * 0.0005; + + camera.position.x = Math.sin(timer) * 2.5; + camera.position.z = Math.cos(timer) * 2.5; + + camera.lookAt(cameraTarget); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_loader_svg.ts b/examples-testing/examples/webgl_loader_svg.ts new file mode 100644 index 000000000..a04b79de2 --- /dev/null +++ b/examples-testing/examples/webgl_loader_svg.ts @@ -0,0 +1,206 @@ +import * as THREE from 'three'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { SVGLoader } from 'three/addons/loaders/SVGLoader.js'; + +let renderer, scene, camera, gui, guiData; + +init(); + +// + +function init() { + const container = document.getElementById('container'); + + // + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(0, 0, 200); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + container.appendChild(renderer.domElement); + + // + + const controls = new OrbitControls(camera, renderer.domElement); + controls.addEventListener('change', render); + controls.screenSpacePanning = true; + + // + + window.addEventListener('resize', onWindowResize); + + guiData = { + currentURL: 'models/svg/tiger.svg', + drawFillShapes: true, + drawStrokes: true, + fillShapesWireframe: false, + strokesWireframe: false, + }; + + loadSVG(guiData.currentURL); + + createGUI(); +} + +function createGUI() { + if (gui) gui.destroy(); + + gui = new GUI(); + + gui.add(guiData, 'currentURL', { + Tiger: 'models/svg/tiger.svg', + 'Joins and caps': 'models/svg/lineJoinsAndCaps.svg', + Hexagon: 'models/svg/hexagon.svg', + Energy: 'models/svg/energy.svg', + 'Test 1': 'models/svg/tests/1.svg', + 'Test 2': 'models/svg/tests/2.svg', + 'Test 3': 'models/svg/tests/3.svg', + 'Test 4': 'models/svg/tests/4.svg', + 'Test 5': 'models/svg/tests/5.svg', + 'Test 6': 'models/svg/tests/6.svg', + 'Test 7': 'models/svg/tests/7.svg', + 'Test 8': 'models/svg/tests/8.svg', + 'Test 9': 'models/svg/tests/9.svg', + Units: 'models/svg/tests/units.svg', + Ordering: 'models/svg/tests/ordering.svg', + Defs: 'models/svg/tests/testDefs/Svg-defs.svg', + Defs2: 'models/svg/tests/testDefs/Svg-defs2.svg', + Defs3: 'models/svg/tests/testDefs/Wave-defs.svg', + Defs4: 'models/svg/tests/testDefs/defs4.svg', + Defs5: 'models/svg/tests/testDefs/defs5.svg', + 'Style CSS inside defs': 'models/svg/style-css-inside-defs.svg', + 'Multiple CSS classes': 'models/svg/multiple-css-classes.svg', + 'Zero Radius': 'models/svg/zero-radius.svg', + 'Styles in svg tag': 'models/svg/tests/styles.svg', + 'Round join': 'models/svg/tests/roundJoinPrecisionIssue.svg', + 'Ellipse Transformations': 'models/svg/tests/ellipseTransform.svg', + singlePointTest: 'models/svg/singlePointTest.svg', + singlePointTest2: 'models/svg/singlePointTest2.svg', + singlePointTest3: 'models/svg/singlePointTest3.svg', + emptyPath: 'models/svg/emptyPath.svg', + }) + .name('SVG File') + .onChange(update); + + gui.add(guiData, 'drawStrokes').name('Draw strokes').onChange(update); + + gui.add(guiData, 'drawFillShapes').name('Draw fill shapes').onChange(update); + + gui.add(guiData, 'strokesWireframe').name('Wireframe strokes').onChange(update); + + gui.add(guiData, 'fillShapesWireframe').name('Wireframe fill shapes').onChange(update); + + function update() { + loadSVG(guiData.currentURL); + } +} + +function loadSVG(url) { + // + + if (scene) disposeScene(scene); + + // + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xb0b0b0); + + // + + const helper = new THREE.GridHelper(160, 10, 0x8d8d8d, 0xc1c1c1); + helper.rotation.x = Math.PI / 2; + scene.add(helper); + + // + + const loader = new SVGLoader(); + + loader.load(url, function (data) { + const group = new THREE.Group(); + group.scale.multiplyScalar(0.25); + group.position.x = -70; + group.position.y = 70; + group.scale.y *= -1; + + let renderOrder = 0; + + for (const path of data.paths) { + const fillColor = path.userData.style.fill; + + if (guiData.drawFillShapes && fillColor !== undefined && fillColor !== 'none') { + const material = new THREE.MeshBasicMaterial({ + color: new THREE.Color().setStyle(fillColor), + opacity: path.userData.style.fillOpacity, + transparent: true, + side: THREE.DoubleSide, + depthWrite: false, + wireframe: guiData.fillShapesWireframe, + }); + + const shapes = SVGLoader.createShapes(path); + + for (const shape of shapes) { + const geometry = new THREE.ShapeGeometry(shape); + const mesh = new THREE.Mesh(geometry, material); + mesh.renderOrder = renderOrder++; + + group.add(mesh); + } + } + + const strokeColor = path.userData.style.stroke; + + if (guiData.drawStrokes && strokeColor !== undefined && strokeColor !== 'none') { + const material = new THREE.MeshBasicMaterial({ + color: new THREE.Color().setStyle(strokeColor), + opacity: path.userData.style.strokeOpacity, + transparent: true, + side: THREE.DoubleSide, + depthWrite: false, + wireframe: guiData.strokesWireframe, + }); + + for (const subPath of path.subPaths) { + const geometry = SVGLoader.pointsToStroke(subPath.getPoints(), path.userData.style); + + if (geometry) { + const mesh = new THREE.Mesh(geometry, material); + mesh.renderOrder = renderOrder++; + + group.add(mesh); + } + } + } + } + + scene.add(group); + + render(); + }); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + render(); +} + +function render() { + renderer.render(scene, camera); +} + +function disposeScene(scene) { + scene.traverse(function (object) { + if (object.isMesh || object.isLine) { + object.geometry.dispose(); + object.material.dispose(); + } + }); +} diff --git a/examples-testing/examples/webgl_loader_texture_dds.ts b/examples-testing/examples/webgl_loader_texture_dds.ts new file mode 100644 index 000000000..ba9b18e9d --- /dev/null +++ b/examples-testing/examples/webgl_loader_texture_dds.ts @@ -0,0 +1,218 @@ +import * as THREE from 'three'; + +import { DDSLoader } from 'three/addons/loaders/DDSLoader.js'; + +let camera, scene, renderer; +const meshes = []; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 100); + camera.position.y = -2; + camera.position.z = 16; + + scene = new THREE.Scene(); + + const geometry = new THREE.BoxGeometry(2, 2, 2); + + /* + This is how compressed textures are supposed to be used: + + DXT1 - RGB - opaque textures + DXT3 - RGBA - transparent textures with sharp alpha transitions + DXT5 - RGBA - transparent textures with full alpha range + */ + + const loader = new DDSLoader(); + + const map1 = loader.load('textures/compressed/disturb_dxt1_nomip.dds'); + map1.minFilter = map1.magFilter = THREE.LinearFilter; + map1.anisotropy = 4; + map1.colorSpace = THREE.SRGBColorSpace; + + const map2 = loader.load('textures/compressed/disturb_dxt1_mip.dds'); + map2.anisotropy = 4; + map2.colorSpace = THREE.SRGBColorSpace; + + const map3 = loader.load('textures/compressed/hepatica_dxt3_mip.dds'); + map3.anisotropy = 4; + map3.colorSpace = THREE.SRGBColorSpace; + + const map4 = loader.load('textures/compressed/explosion_dxt5_mip.dds'); + map4.anisotropy = 4; + map4.colorSpace = THREE.SRGBColorSpace; + + const map5 = loader.load('textures/compressed/disturb_argb_nomip.dds'); + map5.minFilter = map5.magFilter = THREE.LinearFilter; + map5.anisotropy = 4; + map5.colorSpace = THREE.SRGBColorSpace; + + const map6 = loader.load('textures/compressed/disturb_argb_mip.dds'); + map6.anisotropy = 4; + map6.colorSpace = THREE.SRGBColorSpace; + + const map7 = loader.load('textures/compressed/disturb_dx10_bc6h_signed_nomip.dds'); + map7.anisotropy = 4; + + const map8 = loader.load('textures/compressed/disturb_dx10_bc6h_signed_mip.dds'); + map8.anisotropy = 4; + + const map9 = loader.load('textures/compressed/disturb_dx10_bc6h_unsigned_nomip.dds'); + map9.anisotropy = 4; + + const map10 = loader.load('textures/compressed/disturb_dx10_bc6h_unsigned_mip.dds'); + map10.anisotropy = 4; + + const map11 = loader.load('textures/wave_normals_24bit_uncompressed.dds'); + map11.anisotropy = 4; + + const cubemap1 = loader.load('textures/compressed/Mountains.dds', function (texture) { + texture.magFilter = THREE.LinearFilter; + texture.minFilter = THREE.LinearFilter; + texture.mapping = THREE.CubeReflectionMapping; + texture.colorSpace = THREE.SRGBColorSpace; + material1.needsUpdate = true; + }); + + const cubemap2 = loader.load('textures/compressed/Mountains_argb_mip.dds', function (texture) { + texture.magFilter = THREE.LinearFilter; + texture.minFilter = THREE.LinearFilter; + texture.mapping = THREE.CubeReflectionMapping; + texture.colorSpace = THREE.SRGBColorSpace; + material5.needsUpdate = true; + }); + + const cubemap3 = loader.load('textures/compressed/Mountains_argb_nomip.dds', function (texture) { + texture.magFilter = THREE.LinearFilter; + texture.minFilter = THREE.LinearFilter; + texture.mapping = THREE.CubeReflectionMapping; + texture.colorSpace = THREE.SRGBColorSpace; + material6.needsUpdate = true; + }); + + const material1 = new THREE.MeshBasicMaterial({ map: map1, envMap: cubemap1 }); + const material2 = new THREE.MeshBasicMaterial({ map: map2 }); + const material3 = new THREE.MeshBasicMaterial({ map: map3, alphaTest: 0.5, side: THREE.DoubleSide }); + const material4 = new THREE.MeshBasicMaterial({ + map: map4, + side: THREE.DoubleSide, + blending: THREE.AdditiveBlending, + depthTest: false, + transparent: true, + }); + const material5 = new THREE.MeshBasicMaterial({ envMap: cubemap2 }); + const material6 = new THREE.MeshBasicMaterial({ envMap: cubemap3 }); + const material7 = new THREE.MeshBasicMaterial({ map: map5 }); + const material8 = new THREE.MeshBasicMaterial({ map: map6 }); + const material9 = new THREE.MeshBasicMaterial({ map: map7 }); + const material10 = new THREE.MeshBasicMaterial({ map: map8 }); + const material11 = new THREE.MeshBasicMaterial({ map: map9 }); + const material12 = new THREE.MeshBasicMaterial({ map: map10 }); + const material13 = new THREE.MeshBasicMaterial({ map: map11, transparent: true }); + + let mesh = new THREE.Mesh(new THREE.TorusGeometry(), material1); + mesh.position.x = -10; + mesh.position.y = -2; + scene.add(mesh); + meshes.push(mesh); + + mesh = new THREE.Mesh(geometry, material2); + mesh.position.x = -6; + mesh.position.y = -2; + scene.add(mesh); + meshes.push(mesh); + + mesh = new THREE.Mesh(geometry, material3); + mesh.position.x = -6; + mesh.position.y = 2; + scene.add(mesh); + meshes.push(mesh); + + mesh = new THREE.Mesh(geometry, material4); + mesh.position.x = -10; + mesh.position.y = 2; + scene.add(mesh); + meshes.push(mesh); + + mesh = new THREE.Mesh(geometry, material5); + mesh.position.x = -2; + mesh.position.y = 2; + scene.add(mesh); + meshes.push(mesh); + + mesh = new THREE.Mesh(geometry, material6); + mesh.position.x = -2; + mesh.position.y = -2; + scene.add(mesh); + meshes.push(mesh); + + mesh = new THREE.Mesh(geometry, material7); + mesh.position.x = 2; + mesh.position.y = -2; + scene.add(mesh); + meshes.push(mesh); + + mesh = new THREE.Mesh(geometry, material8); + mesh.position.x = 2; + mesh.position.y = 2; + scene.add(mesh); + meshes.push(mesh); + + mesh = new THREE.Mesh(geometry, material9); + mesh.position.x = 6; + mesh.position.y = -2; + scene.add(mesh); + meshes.push(mesh); + + mesh = new THREE.Mesh(geometry, material10); + mesh.position.x = 6; + mesh.position.y = 2; + scene.add(mesh); + meshes.push(mesh); + + mesh = new THREE.Mesh(geometry, material11); + mesh.position.x = 10; + mesh.position.y = -2; + scene.add(mesh); + meshes.push(mesh); + + mesh = new THREE.Mesh(geometry, material12); + mesh.position.x = 10; + mesh.position.y = 2; + scene.add(mesh); + meshes.push(mesh); + + mesh = new THREE.Mesh(geometry, material13); + mesh.position.x = -10; + mesh.position.y = -6; + scene.add(mesh); + meshes.push(mesh); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + const time = Date.now() * 0.001; + + for (let i = 0; i < meshes.length; i++) { + const mesh = meshes[i]; + mesh.rotation.x = time; + mesh.rotation.y = time; + } + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_loader_texture_ktx.ts b/examples-testing/examples/webgl_loader_texture_ktx.ts new file mode 100644 index 000000000..a4e749301 --- /dev/null +++ b/examples-testing/examples/webgl_loader_texture_ktx.ts @@ -0,0 +1,165 @@ +import * as THREE from 'three'; + +import { KTXLoader } from 'three/addons/loaders/KTXLoader.js'; + +/* + This is how compressed textures are supposed to be used: + + best for desktop: + BC1(DXT1) - opaque textures + BC3(DXT5) - transparent textures with full alpha range + + best for iOS: + PVR2, PVR4 - opaque textures or alpha + + best for Android: + ETC1 - opaque textures + ASTC_4x4, ASTC8x8 - transparent textures with full alpha range + */ + +let camera, scene, renderer; +const meshes = []; + +init(); + +function init() { + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + const formats = { + astc: renderer.extensions.has('WEBGL_compressed_texture_astc'), + etc1: renderer.extensions.has('WEBGL_compressed_texture_etc1'), + etc2: renderer.extensions.has('WEBGL_compressed_texture_etc'), + s3tc: renderer.extensions.has('WEBGL_compressed_texture_s3tc'), + pvrtc: renderer.extensions.has('WEBGL_compressed_texture_pvrtc'), + }; + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 2000); + camera.position.z = 1000; + + scene = new THREE.Scene(); + + const ambientLight = new THREE.AmbientLight(0xffffff, 0.02); + scene.add(ambientLight); + + const pointLight = new THREE.PointLight(0xffffff, 2, 0, 0); + pointLight.position.z = 300; + scene.add(pointLight); + + const geometry = new THREE.BoxGeometry(200, 200, 200); + let material1, material2, material3; + + // TODO: add cubemap support + const loader = new KTXLoader(); + + if (formats.pvrtc) { + material1 = new THREE.MeshBasicMaterial({ + map: loader.load('textures/compressed/disturb_PVR2bpp.ktx'), + }); + material1.map.colorSpace = THREE.SRGBColorSpace; + material2 = new THREE.MeshBasicMaterial({ + map: loader.load('textures/compressed/lensflare_PVR4bpp.ktx'), + depthTest: false, + transparent: true, + side: THREE.DoubleSide, + }); + material2.map.colorSpace = THREE.SRGBColorSpace; + + meshes.push(new THREE.Mesh(geometry, material1)); + meshes.push(new THREE.Mesh(geometry, material2)); + } + + if (formats.s3tc) { + material1 = new THREE.MeshBasicMaterial({ + map: loader.load('textures/compressed/disturb_BC1.ktx'), + }); + material1.map.colorSpace = THREE.SRGBColorSpace; + material2 = new THREE.MeshBasicMaterial({ + map: loader.load('textures/compressed/lensflare_BC3.ktx'), + depthTest: false, + transparent: true, + side: THREE.DoubleSide, + }); + material2.map.colorSpace = THREE.SRGBColorSpace; + material3 = new THREE.MeshStandardMaterial({ + normalMap: loader.load('textures/compressed/normal.bc5.ktx'), + }); + + meshes.push(new THREE.Mesh(geometry, material1)); + meshes.push(new THREE.Mesh(geometry, material2)); + meshes.push(new THREE.Mesh(geometry, material3)); + } + + if (formats.etc1) { + material1 = new THREE.MeshBasicMaterial({ + map: loader.load('textures/compressed/disturb_ETC1.ktx'), + }); + + meshes.push(new THREE.Mesh(geometry, material1)); + } + + if (formats.etc2) { + material1 = new THREE.MeshStandardMaterial({ + normalMap: loader.load('textures/compressed/normal.eac_rg.ktx'), + }); + + meshes.push(new THREE.Mesh(geometry, material1)); + } + + if (formats.astc) { + material1 = new THREE.MeshBasicMaterial({ + map: loader.load('textures/compressed/disturb_ASTC4x4.ktx'), + }); + material1.map.colorSpace = THREE.SRGBColorSpace; + material2 = new THREE.MeshBasicMaterial({ + map: loader.load('textures/compressed/lensflare_ASTC8x8.ktx'), + depthTest: false, + transparent: true, + side: THREE.DoubleSide, + }); + material2.map.colorSpace = THREE.SRGBColorSpace; + + meshes.push(new THREE.Mesh(geometry, material1)); + meshes.push(new THREE.Mesh(geometry, material2)); + } + + let x0 = (-Math.min(4, meshes.length) * 300) / 2 + 150; + for (let i = 0; i < Math.min(4, meshes.length); ++i, x0 += 300) { + const mesh = meshes[i]; + mesh.position.x = x0; + mesh.position.y = 150; + scene.add(mesh); + } + + let x1 = (-(meshes.length - 4) * 300) / 2 + 150; + for (let i = 4; i < meshes.length; ++i, x1 += 300) { + const mesh = meshes[i]; + mesh.position.x = x1; + mesh.position.y = -150; + scene.add(mesh); + } + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + const time = Date.now() * 0.001; + + for (let i = 0; i < meshes.length; i++) { + const mesh = meshes[i]; + mesh.rotation.x = time; + mesh.rotation.y = time; + } + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_loader_texture_tga.ts b/examples-testing/examples/webgl_loader_texture_tga.ts new file mode 100644 index 000000000..c4f65b79a --- /dev/null +++ b/examples-testing/examples/webgl_loader_texture_tga.ts @@ -0,0 +1,90 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { TGALoader } from 'three/addons/loaders/TGALoader.js'; + +let camera, scene, renderer, stats; + +init(); + +function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(0, 1, 5); + + scene = new THREE.Scene(); + + // + + const loader = new TGALoader(); + const geometry = new THREE.BoxGeometry(); + + // add box 1 - grey8 texture + + const texture1 = loader.load('textures/crate_grey8.tga'); + texture1.colorSpace = THREE.SRGBColorSpace; + const material1 = new THREE.MeshPhongMaterial({ color: 0xffffff, map: texture1 }); + + const mesh1 = new THREE.Mesh(geometry, material1); + mesh1.position.x = -1; + + scene.add(mesh1); + + // add box 2 - tga texture + + const texture2 = loader.load('textures/crate_color8.tga'); + texture2.colorSpace = THREE.SRGBColorSpace; + const material2 = new THREE.MeshPhongMaterial({ color: 0xffffff, map: texture2 }); + + const mesh2 = new THREE.Mesh(geometry, material2); + mesh2.position.x = 1; + + scene.add(mesh2); + + // + + const ambientLight = new THREE.AmbientLight(0xffffff, 1.5); + scene.add(ambientLight); + + const light = new THREE.DirectionalLight(0xffffff, 2.5); + light.position.set(1, 1, 1); + scene.add(light); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + // + + const controls = new OrbitControls(camera, renderer.domElement); + controls.enableZoom = false; + + // + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + renderer.render(scene, camera); + stats.update(); +} diff --git a/examples-testing/examples/webgl_loader_texture_tiff.ts b/examples-testing/examples/webgl_loader_texture_tiff.ts new file mode 100644 index 000000000..f097774aa --- /dev/null +++ b/examples-testing/examples/webgl_loader_texture_tiff.ts @@ -0,0 +1,87 @@ +import * as THREE from 'three'; + +import { TIFFLoader } from 'three/addons/loaders/TIFFLoader.js'; + +let renderer, scene, camera; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.01, 10); + camera.position.set(0, 0, 4); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + document.body.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + + const loader = new TIFFLoader(); + + const geometry = new THREE.PlaneGeometry(); + + // uncompressed + + loader.load('textures/tiff/crate_uncompressed.tif', function (texture) { + texture.colorSpace = THREE.SRGBColorSpace; + + const material = new THREE.MeshBasicMaterial({ map: texture }); + + const mesh = new THREE.Mesh(geometry, material); + mesh.position.set(-1.5, 0, 0); + + scene.add(mesh); + + render(); + }); + + // LZW + + loader.load('textures/tiff/crate_lzw.tif', function (texture) { + texture.colorSpace = THREE.SRGBColorSpace; + + const material = new THREE.MeshBasicMaterial({ map: texture }); + + const mesh = new THREE.Mesh(geometry, material); + mesh.position.set(0, 0, 0); + + scene.add(mesh); + + render(); + }); + + // JPEG + + loader.load('textures/tiff/crate_jpeg.tif', function (texture) { + texture.colorSpace = THREE.SRGBColorSpace; + + const material = new THREE.MeshBasicMaterial({ map: texture }); + + const mesh = new THREE.Mesh(geometry, material); + mesh.position.set(1.5, 0, 0); + + scene.add(mesh); + + render(); + }); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + render(); +} + +// + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_loader_texture_ultrahdr.ts b/examples-testing/examples/webgl_loader_texture_ultrahdr.ts new file mode 100644 index 000000000..c8bce4bf9 --- /dev/null +++ b/examples-testing/examples/webgl_loader_texture_ultrahdr.ts @@ -0,0 +1,101 @@ +import * as THREE from 'three'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +const params = { + autoRotate: true, + metalness: 1.0, + roughness: 0.0, + exposure: 1.0, + resolution: '2k', + type: 'HalfFloatType', +}; + +let renderer, scene, camera, controls, torusMesh, loader; + +init(); + +function init() { + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + document.body.appendChild(renderer.domElement); + + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = params.exposure; + + renderer.setAnimationLoop(render); + + scene = new THREE.Scene(); + + torusMesh = new THREE.Mesh( + new THREE.TorusKnotGeometry(1, 0.4, 128, 128, 1, 3), + new THREE.MeshStandardMaterial({ roughness: params.roughness, metalness: params.metalness }), + ); + scene.add(torusMesh); + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 500); + camera.position.set(0.0, 0.0, -6.0); + + controls = new OrbitControls(camera, renderer.domElement); + + loader = new UltraHDRLoader(); + loader.setDataType(THREE.FloatType); + + const loadEnvironment = function (resolution = '2k', type = 'HalfFloatType') { + loader.setDataType(THREE[type]); + + loader.load(`textures/equirectangular/spruit_sunrise_${resolution}.hdr.jpg`, function (texture) { + texture.mapping = THREE.EquirectangularReflectionMapping; + texture.needsUpdate = true; + + scene.background = texture; + scene.environment = texture; + }); + }; + + loadEnvironment(params.resolution, params.type); + + const gui = new GUI(); + + gui.add(params, 'autoRotate'); + gui.add(params, 'metalness', 0, 1, 0.01); + gui.add(params, 'roughness', 0, 1, 0.01); + gui.add(params, 'exposure', 0, 4, 0.01); + gui.add(params, 'resolution', ['2k', '4k']).onChange(value => { + loadEnvironment(value, params.type); + }); + gui.add(params, 'type', ['HalfFloatType', 'FloatType']).onChange(value => { + loadEnvironment(params.resolution, value); + }); + + gui.open(); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function render() { + torusMesh.material.roughness = params.roughness; + torusMesh.material.metalness = params.metalness; + + if (params.autoRotate) { + torusMesh.rotation.y += 0.005; + } + + renderer.toneMappingExposure = params.exposure; + + controls.update(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_loader_ttf.ts b/examples-testing/examples/webgl_loader_ttf.ts new file mode 100644 index 000000000..b54b8c290 --- /dev/null +++ b/examples-testing/examples/webgl_loader_ttf.ts @@ -0,0 +1,231 @@ +import * as THREE from 'three'; + +import { TTFLoader } from 'three/addons/loaders/TTFLoader.js'; +import { Font } from 'three/addons/loaders/FontLoader.js'; +import { TextGeometry } from 'three/addons/geometries/TextGeometry.js'; + +let container; +let camera, cameraTarget, scene, renderer; +let group, textMesh1, textMesh2, textGeo, material; +let firstLetter = true; + +let text = 'three.js'; +const depth = 20, + size = 70, + hover = 30, + curveSegments = 4, + bevelThickness = 2, + bevelSize = 1.5; + +let font = null; +const mirror = true; + +let targetRotation = 0; +let targetRotationOnPointerDown = 0; + +let pointerX = 0; +let pointerXOnPointerDown = 0; + +let windowHalfX = window.innerWidth / 2; + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + // CAMERA + + camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 1500); + camera.position.set(0, 400, 700); + + cameraTarget = new THREE.Vector3(0, 150, 0); + + // SCENE + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x000000); + scene.fog = new THREE.Fog(0x000000, 250, 1400); + + // LIGHTS + + const dirLight1 = new THREE.DirectionalLight(0xffffff, 0.4); + dirLight1.position.set(0, 0, 1).normalize(); + scene.add(dirLight1); + + const dirLight2 = new THREE.DirectionalLight(0xffffff, 2); + dirLight2.position.set(0, hover, 10).normalize(); + dirLight2.color.setHSL(Math.random(), 1, 0.5, THREE.SRGBColorSpace); + scene.add(dirLight2); + + material = new THREE.MeshPhongMaterial({ color: 0xffffff, flatShading: true }); + + group = new THREE.Group(); + group.position.y = 100; + + scene.add(group); + + const loader = new TTFLoader(); + + loader.load('fonts/ttf/kenpixel.ttf', function (json) { + font = new Font(json); + createText(); + }); + + const plane = new THREE.Mesh( + new THREE.PlaneGeometry(10000, 10000), + new THREE.MeshBasicMaterial({ color: 0xffffff, opacity: 0.5, transparent: true }), + ); + plane.position.y = 100; + plane.rotation.x = -Math.PI / 2; + scene.add(plane); + + // RENDERER + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + // EVENTS + + container.style.touchAction = 'none'; + container.addEventListener('pointerdown', onPointerDown); + + document.addEventListener('keypress', onDocumentKeyPress); + document.addEventListener('keydown', onDocumentKeyDown); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + windowHalfX = window.innerWidth / 2; + + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function onDocumentKeyDown(event) { + if (firstLetter) { + firstLetter = false; + text = ''; + } + + const keyCode = event.keyCode; + + // backspace + + if (keyCode === 8) { + event.preventDefault(); + + text = text.substring(0, text.length - 1); + refreshText(); + + return false; + } +} + +function onDocumentKeyPress(event) { + const keyCode = event.which; + + // backspace + + if (keyCode === 8) { + event.preventDefault(); + } else { + const ch = String.fromCharCode(keyCode); + text += ch; + + refreshText(); + } +} + +function createText() { + textGeo = new TextGeometry(text, { + font: font, + + size: size, + depth: depth, + curveSegments: curveSegments, + + bevelThickness: bevelThickness, + bevelSize: bevelSize, + bevelEnabled: true, + }); + + textGeo.computeBoundingBox(); + textGeo.computeVertexNormals(); + + const centerOffset = -0.5 * (textGeo.boundingBox.max.x - textGeo.boundingBox.min.x); + + textMesh1 = new THREE.Mesh(textGeo, material); + + textMesh1.position.x = centerOffset; + textMesh1.position.y = hover; + textMesh1.position.z = 0; + + textMesh1.rotation.x = 0; + textMesh1.rotation.y = Math.PI * 2; + + group.add(textMesh1); + + if (mirror) { + textMesh2 = new THREE.Mesh(textGeo, material); + + textMesh2.position.x = centerOffset; + textMesh2.position.y = -hover; + textMesh2.position.z = depth; + + textMesh2.rotation.x = Math.PI; + textMesh2.rotation.y = Math.PI * 2; + + group.add(textMesh2); + } +} + +function refreshText() { + group.remove(textMesh1); + if (mirror) group.remove(textMesh2); + + if (!text) return; + + createText(); +} + +function onPointerDown(event) { + if (event.isPrimary === false) return; + + pointerXOnPointerDown = event.clientX - windowHalfX; + targetRotationOnPointerDown = targetRotation; + + document.addEventListener('pointermove', onPointerMove); + document.addEventListener('pointerup', onPointerUp); +} + +function onPointerMove(event) { + if (event.isPrimary === false) return; + + pointerX = event.clientX - windowHalfX; + + targetRotation = targetRotationOnPointerDown + (pointerX - pointerXOnPointerDown) * 0.02; +} + +function onPointerUp(event) { + if (event.isPrimary === false) return; + + document.removeEventListener('pointermove', onPointerMove); + document.removeEventListener('pointerup', onPointerUp); +} + +// + +function animate() { + group.rotation.y += (targetRotation - group.rotation.y) * 0.05; + + camera.lookAt(cameraTarget); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_loader_usdz.ts b/examples-testing/examples/webgl_loader_usdz.ts new file mode 100644 index 000000000..409c6b597 --- /dev/null +++ b/examples-testing/examples/webgl_loader_usdz.ts @@ -0,0 +1,68 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; +import { USDLoader } from 'three/addons/loaders/USDLoader.js'; + +let camera, scene, renderer; + +init(); + +async function init() { + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(0, 0.75, -1.5); + + scene = new THREE.Scene(); + + const hdrLoader = new HDRLoader().setPath('textures/equirectangular/'); + + const usdzLoader = new USDLoader().setPath('models/usdz/'); + + const [texture, model] = await Promise.all([ + hdrLoader.loadAsync('venice_sunset_1k.hdr'), + usdzLoader.loadAsync('saeukkang.usdz'), + ]); + + // environment + + texture.mapping = THREE.EquirectangularReflectionMapping; + + scene.background = texture; + scene.backgroundBlurriness = 0.5; + scene.environment = texture; + + // model + + model.position.y = 0.25; + model.position.z = -0.25; + scene.add(model); + + // renderer + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = 2.0; + document.body.appendChild(renderer.domElement); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 1; + controls.maxDistance = 8; + // controls.target.y = 15; + // controls.update(); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_loader_vox.ts b/examples-testing/examples/webgl_loader_vox.ts new file mode 100644 index 000000000..87e0b2f81 --- /dev/null +++ b/examples-testing/examples/webgl_loader_vox.ts @@ -0,0 +1,99 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { VOXLoader } from 'three/addons/loaders/VOXLoader.js'; + +let camera, controls, scene, renderer; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.01, 10); + camera.position.set(0.175, 0.075, 0.175); + + scene = new THREE.Scene(); + scene.add(camera); + + // light + + const hemiLight = new THREE.HemisphereLight(0xcccccc, 0x444444, 3); + scene.add(hemiLight); + + const dirLight = new THREE.DirectionalLight(0xffffff, 2.5); + dirLight.position.set(1.5, 3, 2.5); + scene.add(dirLight); + + const dirLight2 = new THREE.DirectionalLight(0xffffff, 1.5); + dirLight2.position.set(-1.5, -3, -2.5); + scene.add(dirLight2); + + const loader = new VOXLoader(); + loader.load('models/vox/monu10.vox', function (result) { + const mesh = result.scene.children[0]; + mesh.position.y = 0; + mesh.scale.setScalar(0.0015); + scene.add(mesh); + }); + + // renderer + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // controls + + controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 0.1; + controls.maxDistance = 0.5; + + // + + window.addEventListener('resize', onWindowResize); +} + +/* + function displayPalette( palette ) { + + const canvas = document.createElement( 'canvas' ); + canvas.width = 8; + canvas.height = 32; + canvas.style.position = 'absolute'; + canvas.style.top = '0'; + canvas.style.width = '100px'; + canvas.style.imageRendering = 'pixelated'; + document.body.appendChild( canvas ); + + const context = canvas.getContext( '2d' ); + + for ( let c = 0; c < 256; c ++ ) { + + const x = c % 8; + const y = Math.floor( c / 8 ); + + const hex = palette[ c + 1 ]; + const r = hex >> 0 & 0xff; + const g = hex >> 8 & 0xff; + const b = hex >> 16 & 0xff; + context.fillStyle = `rgba(${r},${g},${b},1)`; + context.fillRect( x, 31 - y, 1, 1 ); + + } + + } + */ + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + controls.update(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_loader_vrml.ts b/examples-testing/examples/webgl_loader_vrml.ts new file mode 100644 index 000000000..1dda79f2b --- /dev/null +++ b/examples-testing/examples/webgl_loader_vrml.ts @@ -0,0 +1,119 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { VRMLLoader } from 'three/addons/loaders/VRMLLoader.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let camera, scene, renderer, stats, controls, loader; + +const params = { + asset: 'house', +}; + +const assets = [ + 'creaseAngle', + 'crystal', + 'house', + 'elevationGrid1', + 'elevationGrid2', + 'extrusion1', + 'extrusion2', + 'extrusion3', + 'lines', + 'linesTransparent', + 'meshWithLines', + 'meshWithTexture', + 'pixelTexture', + 'points', + 'camera', +]; + +let vrmlScene; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1e10); + camera.position.set(-10, 5, 10); + + scene = new THREE.Scene(); + scene.add(camera); + + // light + + const ambientLight = new THREE.AmbientLight(0xffffff, 1.2); + scene.add(ambientLight); + + const dirLight = new THREE.DirectionalLight(0xffffff, 2.0); + dirLight.position.set(200, 200, 200); + scene.add(dirLight); + + loader = new VRMLLoader(); + loadAsset(params.asset); + + // renderer + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // controls + + controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 1; + controls.maxDistance = 200; + controls.enableDamping = true; + + // + + stats = new Stats(); + document.body.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); + + // + + const gui = new GUI(); + gui.add(params, 'asset', assets).onChange(function (value) { + if (vrmlScene) { + vrmlScene.traverse(function (object) { + if (object.material) object.material.dispose(); + if (object.material && object.material.map) object.material.map.dispose(); + if (object.geometry) object.geometry.dispose(); + }); + + scene.remove(vrmlScene); + } + + loadAsset(value); + }); +} + +function loadAsset(asset) { + loader.load('models/vrml/' + asset + '.wrl', function (object) { + vrmlScene = object; + scene.add(object); + controls.reset(); + }); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + controls.update(); // to support damping + + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/webgl_loader_vtk.ts b/examples-testing/examples/webgl_loader_vtk.ts new file mode 100644 index 000000000..dfc798657 --- /dev/null +++ b/examples-testing/examples/webgl_loader_vtk.ts @@ -0,0 +1,123 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { TrackballControls } from 'three/addons/controls/TrackballControls.js'; +import { VTKLoader } from 'three/addons/loaders/VTKLoader.js'; + +let stats; + +let camera, controls, scene, renderer; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.01, 100); + camera.position.z = 0.2; + + scene = new THREE.Scene(); + + scene.add(camera); + + // light + + const hemiLight = new THREE.HemisphereLight(0xffffff, 0x000000, 3); + scene.add(hemiLight); + + const dirLight = new THREE.DirectionalLight(0xffffff, 1.5); + dirLight.position.set(2, 2, 2); + scene.add(dirLight); + + const loader = new VTKLoader(); + loader.load('models/vtk/bunny.vtk', function (geometry) { + geometry.center(); + geometry.computeVertexNormals(); + + const material = new THREE.MeshLambertMaterial({ color: 0xffffff }); + const mesh = new THREE.Mesh(geometry, material); + mesh.position.set(-0.075, 0.005, 0); + mesh.scale.multiplyScalar(0.2); + scene.add(mesh); + }); + + const loader1 = new VTKLoader(); + loader1.load('models/vtk/cube_ascii.vtp', function (geometry) { + geometry.computeVertexNormals(); + geometry.center(); + + const material = new THREE.MeshLambertMaterial({ color: 0x00ff00 }); + const mesh = new THREE.Mesh(geometry, material); + + mesh.position.set(-0.025, 0, 0); + mesh.scale.multiplyScalar(0.01); + + scene.add(mesh); + }); + + const loader2 = new VTKLoader(); + loader2.load('models/vtk/cube_binary.vtp', function (geometry) { + geometry.computeVertexNormals(); + geometry.center(); + + const material = new THREE.MeshLambertMaterial({ color: 0x0000ff }); + const mesh = new THREE.Mesh(geometry, material); + + mesh.position.set(0.025, 0, 0); + mesh.scale.multiplyScalar(0.01); + + scene.add(mesh); + }); + + const loader3 = new VTKLoader(); + loader3.load('models/vtk/cube_no_compression.vtp', function (geometry) { + geometry.computeVertexNormals(); + geometry.center(); + + const material = new THREE.MeshLambertMaterial({ color: 0xff0000 }); + const mesh = new THREE.Mesh(geometry, material); + + mesh.position.set(0.075, 0, 0); + mesh.scale.multiplyScalar(0.01); + + scene.add(mesh); + }); + + // renderer + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // controls + + controls = new TrackballControls(camera, renderer.domElement); + controls.minDistance = 0.1; + controls.maxDistance = 0.5; + controls.rotateSpeed = 5.0; + + stats = new Stats(); + document.body.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + controls.handleResize(); +} + +function animate() { + controls.update(); + + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/webgl_loader_xyz.ts b/examples-testing/examples/webgl_loader_xyz.ts new file mode 100644 index 000000000..315c6de39 --- /dev/null +++ b/examples-testing/examples/webgl_loader_xyz.ts @@ -0,0 +1,65 @@ +import * as THREE from 'three'; + +import { XYZLoader } from 'three/addons/loaders/XYZLoader.js'; + +let camera, scene, renderer, timer; + +let points; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(10, 7, 10); + + scene = new THREE.Scene(); + scene.add(camera); + camera.lookAt(scene.position); + + timer = new THREE.Timer(); + timer.connect(document); + + const loader = new XYZLoader(); + loader.load('models/xyz/helix_201.xyz', function (geometry) { + geometry.center(); + + const vertexColors = geometry.hasAttribute('color') === true; + + const material = new THREE.PointsMaterial({ size: 0.1, vertexColors: vertexColors }); + + points = new THREE.Points(geometry, material); + scene.add(points); + }); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + const delta = timer.getDelta(); + + if (points) { + points.rotation.x += delta * 0.2; + points.rotation.y += delta * 0.5; + } + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_lod.ts b/examples-testing/examples/webgl_lod.ts new file mode 100644 index 000000000..d957efbbd --- /dev/null +++ b/examples-testing/examples/webgl_lod.ts @@ -0,0 +1,91 @@ +import * as THREE from 'three'; + +import { FlyControls } from 'three/addons/controls/FlyControls.js'; + +let container; + +let camera, scene, renderer, controls; + +const timer = new THREE.Timer(); +timer.connect(document); + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 15000); + camera.position.z = 1000; + + scene = new THREE.Scene(); + scene.fog = new THREE.Fog(0x000000, 1, 15000); + + const pointLight = new THREE.PointLight(0xff2200, 3, 0, 0); + pointLight.position.set(0, 0, 0); + scene.add(pointLight); + + const dirLight = new THREE.DirectionalLight(0xffffff, 3); + dirLight.position.set(0, 0, 1).normalize(); + scene.add(dirLight); + + const geometry = [ + [new THREE.IcosahedronGeometry(100, 16), 50], + [new THREE.IcosahedronGeometry(100, 8), 300], + [new THREE.IcosahedronGeometry(100, 4), 1000], + [new THREE.IcosahedronGeometry(100, 2), 2000], + [new THREE.IcosahedronGeometry(100, 1), 8000], + ]; + + const material = new THREE.MeshLambertMaterial({ color: 0xffffff, wireframe: true }); + + for (let j = 0; j < 1000; j++) { + const lod = new THREE.LOD(); + + for (let i = 0; i < geometry.length; i++) { + const mesh = new THREE.Mesh(geometry[i][0], material); + mesh.scale.set(1.5, 1.5, 1.5); + mesh.updateMatrix(); + mesh.matrixAutoUpdate = false; + lod.addLevel(mesh, geometry[i][1]); + } + + lod.position.x = 10000 * (0.5 - Math.random()); + lod.position.y = 7500 * (0.5 - Math.random()); + lod.position.z = 10000 * (0.5 - Math.random()); + lod.updateMatrix(); + lod.matrixAutoUpdate = false; + scene.add(lod); + } + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + // + + controls = new FlyControls(camera, renderer.domElement); + controls.movementSpeed = 1000; + controls.rollSpeed = Math.PI / 10; + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + controls.update(timer.getDelta()); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_marchingcubes.ts b/examples-testing/examples/webgl_marchingcubes.ts new file mode 100644 index 000000000..ea96c0f9f --- /dev/null +++ b/examples-testing/examples/webgl_marchingcubes.ts @@ -0,0 +1,314 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { MarchingCubes } from 'three/addons/objects/MarchingCubes.js'; +import { ToonShader1, ToonShader2, ToonShaderHatching, ToonShaderDotted } from 'three/addons/shaders/ToonShader.js'; + +let container, stats; + +let camera, scene, renderer; + +let materials, current_material; + +let light, pointLight, ambientLight; + +let effect, resolution; + +let effectController; + +let time = 0; + +const timer = new THREE.Timer(); +timer.connect(document); + +init(); + +function init() { + container = document.getElementById('container'); + + // CAMERA + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000); + camera.position.set(-500, 500, 1500); + + // SCENE + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x050505); + + // LIGHTS + + light = new THREE.DirectionalLight(0xffffff, 3); + light.position.set(0.5, 0.5, 1); + scene.add(light); + + pointLight = new THREE.PointLight(0xff7c00, 3, 0, 0); + pointLight.position.set(0, 0, 100); + scene.add(pointLight); + + ambientLight = new THREE.AmbientLight(0x323232, 3); + scene.add(ambientLight); + + // MATERIALS + + materials = generateMaterials(); + current_material = 'shiny'; + + // MARCHING CUBES + + resolution = 28; + + effect = new MarchingCubes(resolution, materials[current_material], true, true, 100000); + effect.position.set(0, 0, 0); + effect.scale.set(700, 700, 700); + + effect.enableUvs = false; + effect.enableColors = false; + + scene.add(effect); + + // RENDERER + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + // CONTROLS + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 500; + controls.maxDistance = 5000; + + // STATS + + stats = new Stats(); + container.appendChild(stats.dom); + + // GUI + + setupGui(); + + // EVENTS + + window.addEventListener('resize', onWindowResize); +} + +// + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function generateMaterials() { + // environment map + + const path = 'textures/cube/SwedishRoyalCastle/'; + const format = '.jpg'; + const urls = [ + path + 'px' + format, + path + 'nx' + format, + path + 'py' + format, + path + 'ny' + format, + path + 'pz' + format, + path + 'nz' + format, + ]; + + const cubeTextureLoader = new THREE.CubeTextureLoader(); + + const reflectionCube = cubeTextureLoader.load(urls); + const refractionCube = cubeTextureLoader.load(urls); + refractionCube.mapping = THREE.CubeRefractionMapping; + + // toons + + const toonMaterial1 = createShaderMaterial(ToonShader1, light, ambientLight); + const toonMaterial2 = createShaderMaterial(ToonShader2, light, ambientLight); + const hatchingMaterial = createShaderMaterial(ToonShaderHatching, light, ambientLight); + const dottedMaterial = createShaderMaterial(ToonShaderDotted, light, ambientLight); + + const texture = new THREE.TextureLoader().load('textures/uv_grid_opengl.jpg'); + texture.wrapS = THREE.RepeatWrapping; + texture.wrapT = THREE.RepeatWrapping; + texture.colorSpace = THREE.SRGBColorSpace; + + const materials = { + shiny: new THREE.MeshStandardMaterial({ + color: 0x9c0000, + envMap: reflectionCube, + roughness: 0.1, + metalness: 1.0, + }), + chrome: new THREE.MeshLambertMaterial({ color: 0xffffff, envMap: reflectionCube }), + liquid: new THREE.MeshLambertMaterial({ color: 0xffffff, envMap: refractionCube, refractionRatio: 0.85 }), + matte: new THREE.MeshPhongMaterial({ specular: 0x494949, shininess: 1 }), + flat: new THREE.MeshLambertMaterial({ + /*TODO flatShading: true */ + }), + textured: new THREE.MeshPhongMaterial({ color: 0xffffff, specular: 0x111111, shininess: 1, map: texture }), + colors: new THREE.MeshPhongMaterial({ color: 0xffffff, specular: 0xffffff, shininess: 2, vertexColors: true }), + multiColors: new THREE.MeshPhongMaterial({ shininess: 2, vertexColors: true }), + plastic: new THREE.MeshPhongMaterial({ specular: 0xc1c1c1, shininess: 250 }), + toon1: toonMaterial1, + toon2: toonMaterial2, + hatching: hatchingMaterial, + dotted: dottedMaterial, + }; + + return materials; +} + +function createShaderMaterial(shader, light, ambientLight) { + const u = THREE.UniformsUtils.clone(shader.uniforms); + + const vs = shader.vertexShader; + const fs = shader.fragmentShader; + + const material = new THREE.ShaderMaterial({ uniforms: u, vertexShader: vs, fragmentShader: fs }); + + material.uniforms['uDirLightPos'].value = light.position; + material.uniforms['uDirLightColor'].value = light.color; + + material.uniforms['uAmbientLightColor'].value = ambientLight.color; + + return material; +} + +// + +function setupGui() { + const createHandler = function (id) { + return function () { + current_material = id; + + effect.material = materials[id]; + effect.enableUvs = current_material === 'textured' ? true : false; + effect.enableColors = current_material === 'colors' || current_material === 'multiColors' ? true : false; + }; + }; + + effectController = { + material: 'shiny', + + speed: 1.0, + numBlobs: 10, + resolution: 28, + isolation: 80, + + floor: true, + wallx: false, + wallz: false, + + dummy: function () {}, + }; + + let h; + + const gui = new GUI(); + + // material (type) + + h = gui.addFolder('Materials'); + + for (const m in materials) { + effectController[m] = createHandler(m); + h.add(effectController, m).name(m); + } + + // simulation + + h = gui.addFolder('Simulation'); + + h.add(effectController, 'speed', 0.1, 8.0, 0.05); + h.add(effectController, 'numBlobs', 1, 50, 1); + h.add(effectController, 'resolution', 14, 100, 1); + h.add(effectController, 'isolation', 10, 300, 1); + + h.add(effectController, 'floor'); + h.add(effectController, 'wallx'); + h.add(effectController, 'wallz'); +} + +// this controls content of marching cubes voxel field + +function updateCubes(object, time, numblobs, floor, wallx, wallz) { + object.reset(); + + // fill the field with some metaballs + + const rainbow = [ + new THREE.Color(0xff0000), + new THREE.Color(0xffbb00), + new THREE.Color(0xffff00), + new THREE.Color(0x00ff00), + new THREE.Color(0x0000ff), + new THREE.Color(0x9400bd), + new THREE.Color(0xc800eb), + ]; + const subtract = 12; + const strength = 1.2 / ((Math.sqrt(numblobs) - 1) / 4 + 1); + + for (let i = 0; i < numblobs; i++) { + const ballx = Math.sin(i + 1.26 * time * (1.03 + 0.5 * Math.cos(0.21 * i))) * 0.27 + 0.5; + const bally = Math.abs(Math.cos(i + 1.12 * time * Math.cos(1.22 + 0.1424 * i))) * 0.77; // dip into the floor + const ballz = Math.cos(i + 1.32 * time * 0.1 * Math.sin(0.92 + 0.53 * i)) * 0.27 + 0.5; + + if (current_material === 'multiColors') { + object.addBall(ballx, bally, ballz, strength, subtract, rainbow[i % 7]); + } else { + object.addBall(ballx, bally, ballz, strength, subtract); + } + } + + if (floor) object.addPlaneY(2, 12); + if (wallz) object.addPlaneZ(2, 12); + if (wallx) object.addPlaneX(2, 12); + + object.update(); +} + +// + +function animate() { + timer.update(); + + render(); + stats.update(); +} + +function render() { + const delta = timer.getDelta(); + + time += delta * effectController.speed * 0.5; + + // marching cubes + + if (effectController.resolution !== resolution) { + resolution = effectController.resolution; + effect.init(Math.floor(resolution)); + } + + if (effectController.isolation !== effect.isolation) { + effect.isolation = effectController.isolation; + } + + updateCubes( + effect, + time, + effectController.numBlobs, + effectController.floor, + effectController.wallx, + effectController.wallz, + ); + + // render + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_materials_alphahash.ts b/examples-testing/examples/webgl_materials_alphahash.ts new file mode 100644 index 000000000..790e31be4 --- /dev/null +++ b/examples-testing/examples/webgl_materials_alphahash.ts @@ -0,0 +1,178 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; + +import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; +import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; +import { TAARenderPass } from 'three/addons/postprocessing/TAARenderPass.js'; +import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; + +let camera, scene, renderer, controls, stats, mesh, material; + +let composer, renderPass, taaRenderPass, outputPass; + +let needsUpdate = false; + +const amount = parseInt(window.location.search.slice(1)) || 3; +const count = Math.pow(amount, 3); + +const color = new THREE.Color(); + +const params = { + alpha: 0.5, + alphaHash: true, + taa: true, + sampleLevel: 2, +}; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(amount, amount, amount); + camera.lookAt(0, 0, 0); + + scene = new THREE.Scene(); + + const geometry = new THREE.IcosahedronGeometry(0.5, 3); + + material = new THREE.MeshStandardMaterial({ + color: 0xffffff, + alphaHash: params.alphaHash, + opacity: params.alpha, + }); + + mesh = new THREE.InstancedMesh(geometry, material, count); + + let i = 0; + const offset = (amount - 1) / 2; + + const matrix = new THREE.Matrix4(); + + for (let x = 0; x < amount; x++) { + for (let y = 0; y < amount; y++) { + for (let z = 0; z < amount; z++) { + matrix.setPosition(offset - x, offset - y, offset - z); + + mesh.setMatrixAt(i, matrix); + mesh.setColorAt(i, color.setHex(Math.random() * 0xffffff)); + + i++; + } + } + } + + scene.add(mesh); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // + + const environment = new RoomEnvironment(); + const pmremGenerator = new THREE.PMREMGenerator(renderer); + + scene.environment = pmremGenerator.fromScene(environment, 0.04).texture; + environment.dispose(); + + // + + composer = new EffectComposer(renderer); + + renderPass = new RenderPass(scene, camera); + renderPass.enabled = false; + + taaRenderPass = new TAARenderPass(scene, camera); + + outputPass = new OutputPass(); + + composer.addPass(renderPass); + composer.addPass(taaRenderPass); + composer.addPass(outputPass); + + // + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableZoom = false; + controls.enablePan = false; + + controls.addEventListener('change', () => (needsUpdate = true)); + + // + + const gui = new GUI(); + + gui.add(params, 'alpha', 0, 1).onChange(onMaterialUpdate); + gui.add(params, 'alphaHash').onChange(onMaterialUpdate); + + const taaFolder = gui.addFolder('Temporal Anti-Aliasing'); + + taaFolder + .add(params, 'taa') + .name('enabled') + .onChange(() => { + renderPass.enabled = !params.taa; + taaRenderPass.enabled = params.taa; + + sampleLevelCtrl.enable(params.taa); + + needsUpdate = true; + }); + + const sampleLevelCtrl = taaFolder.add(params, 'sampleLevel', 0, 6, 1).onChange(() => (needsUpdate = true)); + + // + + stats = new Stats(); + document.body.appendChild(stats.dom); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + composer.setSize(window.innerWidth, window.innerHeight); + + needsUpdate = true; +} + +function onMaterialUpdate() { + material.opacity = params.alpha; + material.alphaHash = params.alphaHash; + material.transparent = !params.alphaHash; + material.depthWrite = params.alphaHash; + + material.needsUpdate = true; + needsUpdate = true; +} + +function animate() { + render(); + + stats.update(); +} + +function render() { + if (needsUpdate) { + taaRenderPass.accumulate = false; + taaRenderPass.sampleLevel = 0; + + needsUpdate = false; + } else { + taaRenderPass.accumulate = true; + taaRenderPass.sampleLevel = params.sampleLevel; + } + + composer.render(); +} diff --git a/examples-testing/examples/webgl_materials_blending.ts b/examples-testing/examples/webgl_materials_blending.ts new file mode 100644 index 000000000..fb2e6a91e --- /dev/null +++ b/examples-testing/examples/webgl_materials_blending.ts @@ -0,0 +1,149 @@ +import * as THREE from 'three'; + +let camera, scene, renderer; +let mapBg; + +const textureLoader = new THREE.TextureLoader(); + +init(); + +function init() { + // CAMERA + + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.z = 600; + + // SCENE + + scene = new THREE.Scene(); + + // BACKGROUND + + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); + canvas.width = canvas.height = 128; + ctx.fillStyle = '#ddd'; + ctx.fillRect(0, 0, 128, 128); + ctx.fillStyle = '#555'; + ctx.fillRect(0, 0, 64, 64); + ctx.fillStyle = '#999'; + ctx.fillRect(32, 32, 32, 32); + ctx.fillStyle = '#555'; + ctx.fillRect(64, 64, 64, 64); + ctx.fillStyle = '#777'; + ctx.fillRect(96, 96, 32, 32); + + mapBg = new THREE.CanvasTexture(canvas); + mapBg.colorSpace = THREE.SRGBColorSpace; + mapBg.wrapS = mapBg.wrapT = THREE.RepeatWrapping; + mapBg.repeat.set(64, 32); + + scene.background = mapBg; + + // OBJECTS + + const blendings = [ + { name: 'No', constant: THREE.NoBlending }, + { name: 'Normal', constant: THREE.NormalBlending }, + { name: 'Additive', constant: THREE.AdditiveBlending }, + { name: 'Subtractive', constant: THREE.SubtractiveBlending }, + { name: 'Multiply', constant: THREE.MultiplyBlending }, + ]; + + const assignSRGB = texture => { + texture.colorSpace = THREE.SRGBColorSpace; + }; + + const map0 = textureLoader.load('textures/uv_grid_opengl.jpg', assignSRGB); + const map1 = textureLoader.load('textures/sprite0.jpg', assignSRGB); + const map2 = textureLoader.load('textures/sprite0.png', assignSRGB); + const map3 = textureLoader.load('textures/lensflare/lensflare0.png', assignSRGB); + const map4 = textureLoader.load('textures/lensflare/lensflare0_alpha.png', assignSRGB); + + const geo1 = new THREE.PlaneGeometry(100, 100); + const geo2 = new THREE.PlaneGeometry(100, 25); + + addImageRow(map0, 300); + addImageRow(map1, 150); + addImageRow(map2, 0); + addImageRow(map3, -150); + addImageRow(map4, -300); + + function addImageRow(map, y) { + for (let i = 0; i < blendings.length; i++) { + const blending = blendings[i]; + + const material = new THREE.MeshBasicMaterial({ map: map }); + material.transparent = true; + material.blending = blending.constant; + + material.premultipliedAlpha = true; + + const x = (i - blendings.length / 2) * 110; + const z = 0; + + let mesh = new THREE.Mesh(geo1, material); + mesh.position.set(x, y, z); + scene.add(mesh); + + mesh = new THREE.Mesh(geo2, generateLabelMaterial(blending.name)); + mesh.position.set(x, y - 75, z); + scene.add(mesh); + } + } + + // RENDERER + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // EVENTS + + window.addEventListener('resize', onWindowResize); +} + +// + +function onWindowResize() { + const SCREEN_WIDTH = window.innerWidth; + const SCREEN_HEIGHT = window.innerHeight; + + renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); + + camera.aspect = SCREEN_WIDTH / SCREEN_HEIGHT; + camera.updateProjectionMatrix(); +} + +function generateLabelMaterial(text) { + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); + canvas.width = 128; + canvas.height = 32; + + ctx.fillStyle = 'rgba( 0, 0, 0, 0.95 )'; + ctx.fillRect(0, 0, 128, 32); + + ctx.fillStyle = 'white'; + ctx.font = 'bold 12pt arial'; + ctx.fillText(text, 10, 22); + + const map = new THREE.CanvasTexture(canvas); + map.colorSpace = THREE.SRGBColorSpace; + + const material = new THREE.MeshBasicMaterial({ map: map, transparent: true }); + + return material; +} + +function animate() { + const time = Date.now() * 0.00025; + const ox = (time * -0.01 * mapBg.repeat.x) % 1; + const oy = (time * -0.01 * mapBg.repeat.y) % 1; + + mapBg.offset.set(ox, oy); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_materials_blending_custom.ts b/examples-testing/examples/webgl_materials_blending_custom.ts new file mode 100644 index 000000000..072447426 --- /dev/null +++ b/examples-testing/examples/webgl_materials_blending_custom.ts @@ -0,0 +1,214 @@ +import * as THREE from 'three'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let camera, scene, renderer; + +let mapBg; +const materials = []; + +const params = { + blendEquation: THREE.AddEquation, +}; + +const equations = { + Add: THREE.AddEquation, + Subtract: THREE.SubtractEquation, + ReverseSubtract: THREE.ReverseSubtractEquation, + Min: THREE.MinEquation, + Max: THREE.MaxEquation, +}; + +init(); + +function init() { + // CAMERA + + camera = new THREE.PerspectiveCamera(80, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.z = 700; + + // SCENE + + scene = new THREE.Scene(); + + // BACKGROUND + + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); + canvas.width = canvas.height = 128; + ctx.fillStyle = '#ddd'; + ctx.fillRect(0, 0, 128, 128); + ctx.fillStyle = '#555'; + ctx.fillRect(0, 0, 64, 64); + ctx.fillStyle = '#999'; + ctx.fillRect(32, 32, 32, 32); + ctx.fillStyle = '#555'; + ctx.fillRect(64, 64, 64, 64); + ctx.fillStyle = '#777'; + ctx.fillRect(96, 96, 32, 32); + + mapBg = new THREE.CanvasTexture(canvas); + mapBg.colorSpace = THREE.SRGBColorSpace; + mapBg.wrapS = mapBg.wrapT = THREE.RepeatWrapping; + mapBg.repeat.set(64, 32); + + scene.background = mapBg; + + // FOREGROUND OBJECTS + + const src = [ + { name: 'Zero', constant: THREE.ZeroFactor }, + { name: 'One', constant: THREE.OneFactor }, + { name: 'SrcColor', constant: THREE.SrcColorFactor }, + { name: 'OneMinusSrcColor', constant: THREE.OneMinusSrcColorFactor }, + { name: 'SrcAlpha', constant: THREE.SrcAlphaFactor }, + { name: 'OneMinusSrcAlpha', constant: THREE.OneMinusSrcAlphaFactor }, + { name: 'DstAlpha', constant: THREE.DstAlphaFactor }, + { name: 'OneMinusDstAlpha', constant: THREE.OneMinusDstAlphaFactor }, + { name: 'DstColor', constant: THREE.DstColorFactor }, + { name: 'OneMinusDstColor', constant: THREE.OneMinusDstColorFactor }, + { name: 'SrcAlphaSaturate', constant: THREE.SrcAlphaSaturateFactor }, + ]; + + const dst = [ + { name: 'Zero', constant: THREE.ZeroFactor }, + { name: 'One', constant: THREE.OneFactor }, + { name: 'SrcColor', constant: THREE.SrcColorFactor }, + { name: 'OneMinusSrcColor', constant: THREE.OneMinusSrcColorFactor }, + { name: 'SrcAlpha', constant: THREE.SrcAlphaFactor }, + { name: 'OneMinusSrcAlpha', constant: THREE.OneMinusSrcAlphaFactor }, + { name: 'DstAlpha', constant: THREE.DstAlphaFactor }, + { name: 'OneMinusDstAlpha', constant: THREE.OneMinusDstAlphaFactor }, + { name: 'DstColor', constant: THREE.DstColorFactor }, + { name: 'OneMinusDstColor', constant: THREE.OneMinusDstColorFactor }, + ]; + + const geo1 = new THREE.PlaneGeometry(100, 100); + const geo2 = new THREE.PlaneGeometry(100, 25); + + const texture = new THREE.TextureLoader().load('textures/lensflare/lensflare0_alpha.png'); + texture.colorSpace = THREE.SRGBColorSpace; + + for (let i = 0; i < dst.length; i++) { + const blendDst = dst[i]; + + for (let j = 0; j < src.length; j++) { + const blendSrc = src[j]; + + const material = new THREE.MeshBasicMaterial({ map: texture }); + material.transparent = true; + + material.blending = THREE.CustomBlending; + material.blendSrc = blendSrc.constant; + material.blendDst = blendDst.constant; + material.blendEquation = THREE.AddEquation; + + const x = (j - src.length / 2) * 110; + const z = 0; + const y = (i - dst.length / 2) * 110 + 50; + + const mesh = new THREE.Mesh(geo1, material); + mesh.position.set(x, -y, z); + mesh.matrixAutoUpdate = false; + mesh.updateMatrix(); + scene.add(mesh); + + materials.push(material); + } + } + + for (let j = 0; j < src.length; j++) { + const blendSrc = src[j]; + + const x = (j - src.length / 2) * 110; + const z = 0; + const y = (0 - dst.length / 2) * 110 + 50; + + const mesh = new THREE.Mesh(geo2, generateLabelMaterial(blendSrc.name, 'rgba( 0, 150, 0, 1 )')); + mesh.position.set(x, -(y - 70), z); + mesh.matrixAutoUpdate = false; + mesh.updateMatrix(); + scene.add(mesh); + } + + for (let i = 0; i < dst.length; i++) { + const blendDst = dst[i]; + + const x = (0 - src.length / 2) * 110 - 125; + const z = 0; + const y = (i - dst.length / 2) * 110 + 165; + + const mesh = new THREE.Mesh(geo2, generateLabelMaterial(blendDst.name, 'rgba( 150, 0, 0, 1 )')); + mesh.position.set(x, -(y - 120), z); + mesh.matrixAutoUpdate = false; + mesh.updateMatrix(); + scene.add(mesh); + } + + // RENDERER + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // EVENTS + + window.addEventListener('resize', onWindowResize); + + // GUI + + // + const gui = new GUI({ width: 300 }); + + gui.add(params, 'blendEquation', equations).onChange(updateBlendEquation); + gui.open(); +} + +// + +function onWindowResize() { + renderer.setSize(window.innerWidth, window.innerHeight); + + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); +} + +// + +function generateLabelMaterial(text, bg) { + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); + canvas.width = 128; + canvas.height = 32; + + ctx.fillStyle = bg; + ctx.fillRect(0, 0, 128, 32); + + ctx.fillStyle = 'white'; + ctx.font = 'bold 11pt arial'; + ctx.fillText(text, 8, 22); + + const map = new THREE.CanvasTexture(canvas); + map.colorSpace = THREE.SRGBColorSpace; + + const material = new THREE.MeshBasicMaterial({ map: map, transparent: true }); + return material; +} + +function updateBlendEquation(value) { + for (const material of materials) { + material.blendEquation = value; + } +} + +function animate() { + const time = Date.now() * 0.00025; + const ox = (time * -0.01 * mapBg.repeat.x) % 1; + const oy = (time * -0.01 * mapBg.repeat.y) % 1; + + mapBg.offset.set(ox, oy); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_materials_bumpmap.ts b/examples-testing/examples/webgl_materials_bumpmap.ts new file mode 100644 index 000000000..a09c21573 --- /dev/null +++ b/examples-testing/examples/webgl_materials_bumpmap.ts @@ -0,0 +1,134 @@ +import * as THREE from 'three'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let container, loader; + +let camera, scene, renderer, controls; + +let mesh; + +let spotLight; + +const params = { + enableBumpMap: true, +}; + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + // + + camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.z = 12; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x060708); + + // LIGHTS + + scene.add(new THREE.HemisphereLight(0x8d7c7c, 0x494966, 3)); + + spotLight = new THREE.SpotLight(0xffffde, 200); + spotLight.position.set(3.5, 0, 7); + scene.add(spotLight); + + spotLight.castShadow = true; + + spotLight.shadow.mapSize.width = 2048; + spotLight.shadow.mapSize.height = 2048; + + spotLight.shadow.camera.near = 2; + spotLight.shadow.camera.far = 15; + + spotLight.shadow.camera.fov = 40; + + spotLight.shadow.bias = -0.005; + + // + + const mapHeight = new THREE.TextureLoader().load( + 'models/gltf/LeePerrySmith/Infinite-Level_02_Disp_NoSmoothUV-4096.jpg', + ); + + const material = new THREE.MeshPhongMaterial({ + color: 0x9c6e49, + specular: 0x666666, + shininess: 25, + bumpMap: mapHeight, + bumpScale: 10, + }); + + loader = new GLTFLoader(); + loader.load('models/gltf/LeePerrySmith/LeePerrySmith.glb', function (gltf) { + createScene(gltf.scene.children[0].geometry, 1, material); + }); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + renderer.shadowMap.enabled = true; + + // EVENTS + + window.addEventListener('resize', onWindowResize); + + // GUI + + const gui = new GUI(); + + gui.add(params, 'enableBumpMap') + .name('enable bump map') + .onChange(value => { + mesh.material.bumpMap = value === true ? mapHeight : null; + mesh.material.needsUpdate = true; + }); + gui.add(material, 'bumpScale', 0, 40).name('bump scale'); + gui.open(); + + // CONTROLS + + controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 8; + controls.maxDistance = 50; + controls.enablePan = false; + controls.enableDamping = true; +} + +function createScene(geometry, scale, material) { + mesh = new THREE.Mesh(geometry, material); + + mesh.position.y = -0.5; + mesh.scale.set(scale, scale, scale); + + mesh.castShadow = true; + mesh.receiveShadow = true; + + scene.add(mesh); +} + +// + +function onWindowResize() { + renderer.setSize(window.innerWidth, window.innerHeight); + + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); +} + +// + +function animate() { + controls.update(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_materials_car.ts b/examples-testing/examples/webgl_materials_car.ts new file mode 100644 index 000000000..44be6e0ee --- /dev/null +++ b/examples-testing/examples/webgl_materials_car.ts @@ -0,0 +1,168 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; +import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; + +let camera, scene, renderer; +let stats; + +let grid; +let controls; + +const wheels = []; + +function init() { + const container = document.getElementById('container'); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = 0.85; + container.appendChild(renderer.domElement); + + window.addEventListener('resize', onWindowResize); + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(4.25, 1.4, -4.5); + + controls = new OrbitControls(camera, container); + controls.maxDistance = 9; + controls.maxPolarAngle = THREE.MathUtils.degToRad(90); + controls.target.set(0, 0.5, 0); + controls.update(); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x333333); + scene.environment = new HDRLoader().load('textures/equirectangular/venice_sunset_1k.hdr'); + scene.environment.mapping = THREE.EquirectangularReflectionMapping; + scene.fog = new THREE.Fog(0x333333, 10, 15); + + grid = new THREE.GridHelper(20, 40, 0xffffff, 0xffffff); + grid.material.opacity = 0.2; + grid.material.depthWrite = false; + grid.material.transparent = true; + scene.add(grid); + + // materials + + const bodyMaterial = new THREE.MeshPhysicalMaterial({ + color: 0xff0000, + metalness: 1.0, + roughness: 0.5, + clearcoat: 1.0, + clearcoatRoughness: 0.03, + }); + + const detailsMaterial = new THREE.MeshStandardMaterial({ + color: 0xffffff, + metalness: 1.0, + roughness: 0.5, + }); + + const glassMaterial = new THREE.MeshPhysicalMaterial({ + color: 0xffffff, + metalness: 0.25, + roughness: 0, + transmission: 1.0, + }); + + const bodyColorInput = document.getElementById('body-color'); + bodyColorInput.addEventListener('input', function () { + bodyMaterial.color.set(this.value); + }); + + const detailsColorInput = document.getElementById('details-color'); + detailsColorInput.addEventListener('input', function () { + detailsMaterial.color.set(this.value); + }); + + const glassColorInput = document.getElementById('glass-color'); + glassColorInput.addEventListener('input', function () { + glassMaterial.color.set(this.value); + }); + + // Car + + const shadow = new THREE.TextureLoader().load('models/gltf/ferrari_ao.png'); + + const dracoLoader = new DRACOLoader(); + dracoLoader.setDecoderPath('jsm/libs/draco/gltf/'); + + const loader = new GLTFLoader(); + loader.setDRACOLoader(dracoLoader); + + loader.load('models/gltf/ferrari.glb', function (gltf) { + const carModel = gltf.scene.children[0]; + + carModel.getObjectByName('body').material = bodyMaterial; + + carModel.getObjectByName('rim_fl').material = detailsMaterial; + carModel.getObjectByName('rim_fr').material = detailsMaterial; + carModel.getObjectByName('rim_rr').material = detailsMaterial; + carModel.getObjectByName('rim_rl').material = detailsMaterial; + carModel.getObjectByName('trim').material = detailsMaterial; + + carModel.getObjectByName('glass').material = glassMaterial; + + wheels.push( + carModel.getObjectByName('wheel_fl'), + carModel.getObjectByName('wheel_fr'), + carModel.getObjectByName('wheel_rl'), + carModel.getObjectByName('wheel_rr'), + ); + + // shadow + const mesh = new THREE.Mesh( + new THREE.PlaneGeometry(0.655 * 4, 1.3 * 4), + new THREE.MeshBasicMaterial({ + map: shadow, + blending: THREE.MultiplyBlending, + toneMapped: false, + transparent: true, + premultipliedAlpha: true, + }), + ); + mesh.rotation.x = -Math.PI / 2; + mesh.renderOrder = 2; + carModel.add(mesh); + + scene.add(carModel); + }); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + controls.update(); + + const time = -performance.now() / 1000; + + for (let i = 0; i < wheels.length; i++) { + wheels[i].rotation.x = time * Math.PI * 2; + } + + grid.position.z = -time % 1; + + renderer.render(scene, camera); + + stats.update(); +} + +init(); diff --git a/examples-testing/examples/webgl_materials_cubemap.ts b/examples-testing/examples/webgl_materials_cubemap.ts new file mode 100644 index 000000000..5f2692751 --- /dev/null +++ b/examples-testing/examples/webgl_materials_cubemap.ts @@ -0,0 +1,115 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { OBJLoader } from 'three/addons/loaders/OBJLoader.js'; + +let container, stats; + +let camera, scene, renderer; + +let pointLight; + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.z = 13; + + //cubemap + const path = 'textures/cube/SwedishRoyalCastle/'; + const format = '.jpg'; + const urls = [ + path + 'px' + format, + path + 'nx' + format, + path + 'py' + format, + path + 'ny' + format, + path + 'pz' + format, + path + 'nz' + format, + ]; + + const reflectionCube = new THREE.CubeTextureLoader().load(urls); + const refractionCube = new THREE.CubeTextureLoader().load(urls); + refractionCube.mapping = THREE.CubeRefractionMapping; + + scene = new THREE.Scene(); + scene.background = reflectionCube; + + //lights + const ambient = new THREE.AmbientLight(0xffffff, 3); + scene.add(ambient); + + pointLight = new THREE.PointLight(0xffffff, 200); + scene.add(pointLight); + + //materials + const cubeMaterial3 = new THREE.MeshLambertMaterial({ + color: 0xffaa00, + envMap: reflectionCube, + combine: THREE.MixOperation, + reflectivity: 0.3, + }); + const cubeMaterial2 = new THREE.MeshLambertMaterial({ + color: 0xfff700, + envMap: refractionCube, + refractionRatio: 0.95, + }); + const cubeMaterial1 = new THREE.MeshLambertMaterial({ color: 0xffffff, envMap: reflectionCube }); + + //models + const objLoader = new OBJLoader(); + + objLoader.setPath('models/obj/walt/'); + objLoader.load('WaltHead.obj', function (object) { + const head = object.children[0]; + head.scale.setScalar(0.1); + head.position.y = -3; + head.material = cubeMaterial1; + + const head2 = head.clone(); + head2.position.x = -6; + head2.material = cubeMaterial2; + + const head3 = head.clone(); + head3.position.x = 6; + head3.material = cubeMaterial3; + + scene.add(head, head2, head3); + }); + + //renderer + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + //controls + const controls = new OrbitControls(camera, renderer.domElement); + controls.enableZoom = false; + controls.enablePan = false; + controls.minPolarAngle = Math.PI / 4; + controls.maxPolarAngle = Math.PI / 1.5; + + //stats + stats = new Stats(); + container.appendChild(stats.dom); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + renderer.render(scene, camera); + stats.update(); +} diff --git a/examples-testing/examples/webgl_materials_cubemap_dynamic.ts b/examples-testing/examples/webgl_materials_cubemap_dynamic.ts new file mode 100644 index 000000000..301835dda --- /dev/null +++ b/examples-testing/examples/webgl_materials_cubemap_dynamic.ts @@ -0,0 +1,115 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import Stats from 'three/addons/libs/stats.module.js'; + +let camera, scene, renderer, stats; +let cube, sphere, torus, material; + +let cubeCamera, cubeRenderTarget; + +let controls; + +init(); + +function init() { + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + document.body.appendChild(renderer.domElement); + + window.addEventListener('resize', onWindowResized); + + stats = new Stats(); + document.body.appendChild(stats.dom); + + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.z = 75; + + scene = new THREE.Scene(); + scene.rotation.y = 0.5; // avoid flying objects occluding the sun + + new HDRLoader().setPath('textures/equirectangular/').load('quarry_01_1k.hdr', function (texture) { + texture.mapping = THREE.EquirectangularReflectionMapping; + + scene.background = texture; + scene.environment = texture; + }); + + // + + cubeRenderTarget = new THREE.WebGLCubeRenderTarget(256); + cubeRenderTarget.texture.type = THREE.HalfFloatType; + + cubeCamera = new THREE.CubeCamera(1, 1000, cubeRenderTarget); + + // + + material = new THREE.MeshStandardMaterial({ + envMap: cubeRenderTarget.texture, + roughness: 0.05, + metalness: 1, + }); + + const gui = new GUI(); + gui.add(material, 'roughness', 0, 1); + gui.add(material, 'metalness', 0, 1); + gui.add(renderer, 'toneMappingExposure', 0, 2).name('exposure'); + + sphere = new THREE.Mesh(new THREE.IcosahedronGeometry(15, 8), material); + scene.add(sphere); + + const material2 = new THREE.MeshStandardMaterial({ + roughness: 0.1, + metalness: 0, + }); + + cube = new THREE.Mesh(new THREE.BoxGeometry(15, 15, 15), material2); + scene.add(cube); + + torus = new THREE.Mesh(new THREE.TorusKnotGeometry(8, 3, 128, 16), material2); + scene.add(torus); + + // + + controls = new OrbitControls(camera, renderer.domElement); + controls.autoRotate = true; +} + +function onWindowResized() { + renderer.setSize(window.innerWidth, window.innerHeight); + + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); +} + +function animate(msTime) { + const time = msTime / 1000; + + cube.position.x = Math.cos(time) * 30; + cube.position.y = Math.sin(time) * 30; + cube.position.z = Math.sin(time) * 30; + + cube.rotation.x += 0.02; + cube.rotation.y += 0.03; + + torus.position.x = Math.cos(time + 10) * 30; + torus.position.y = Math.sin(time + 10) * 30; + torus.position.z = Math.sin(time + 10) * 30; + + torus.rotation.x += 0.02; + torus.rotation.y += 0.03; + + cubeCamera.update(renderer, scene); + + controls.update(); + + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/webgl_materials_cubemap_mipmaps.ts b/examples-testing/examples/webgl_materials_cubemap_mipmaps.ts new file mode 100644 index 000000000..944f4c18e --- /dev/null +++ b/examples-testing/examples/webgl_materials_cubemap_mipmaps.ts @@ -0,0 +1,119 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let container; + +let camera, scene, renderer; + +init(); + +//load customized cube texture +async function loadCubeTextureWithMipmaps() { + const path = 'textures/cube/angus/'; + const format = '.jpg'; + const mipmaps = []; + const maxLevel = 8; + + async function loadCubeTexture(urls) { + return new Promise(function (resolve) { + new THREE.CubeTextureLoader().load(urls, function (cubeTexture) { + resolve(cubeTexture); + }); + }); + } + + // load mipmaps + const pendings = []; + + for (let level = 0; level <= maxLevel; ++level) { + const urls = []; + + for (let face = 0; face < 6; ++face) { + urls.push(path + 'cube_m0' + level + '_c0' + face + format); + } + + const mipmapLevel = level; + + pendings.push( + loadCubeTexture(urls).then(function (cubeTexture) { + mipmaps[mipmapLevel] = cubeTexture; + }), + ); + } + + await Promise.all(pendings); + + const customizedCubeTexture = mipmaps.shift(); + customizedCubeTexture.mipmaps = mipmaps; + customizedCubeTexture.colorSpace = THREE.SRGBColorSpace; + customizedCubeTexture.minFilter = THREE.LinearMipMapLinearFilter; + customizedCubeTexture.magFilter = THREE.LinearFilter; + customizedCubeTexture.generateMipmaps = false; + customizedCubeTexture.needsUpdate = true; + + return customizedCubeTexture; +} + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000); + camera.position.z = 500; + + scene = new THREE.Scene(); + + loadCubeTextureWithMipmaps().then(function (cubeTexture) { + //model + const sphere = new THREE.SphereGeometry(100, 128, 128); + + //manual mipmaps + let material = new THREE.MeshBasicMaterial({ color: 0xffffff, envMap: cubeTexture }); + material.name = 'manual mipmaps'; + + let mesh = new THREE.Mesh(sphere, material); + mesh.position.set(100, 0, 0); + scene.add(mesh); + + //webgl mipmaps + material = material.clone(); + material.name = 'auto mipmaps'; + + const autoCubeTexture = cubeTexture.clone(); + autoCubeTexture.mipmaps = []; + autoCubeTexture.generateMipmaps = true; + autoCubeTexture.needsUpdate = true; + + material.envMap = autoCubeTexture; + + mesh = new THREE.Mesh(sphere, material); + mesh.position.set(-100, 0, 0); + scene.add(mesh); + }); + + //renderer + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + //controls + const controls = new OrbitControls(camera, renderer.domElement); + controls.minPolarAngle = Math.PI / 4; + controls.maxPolarAngle = Math.PI / 1.5; + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_materials_cubemap_refraction.ts b/examples-testing/examples/webgl_materials_cubemap_refraction.ts new file mode 100644 index 000000000..8c025071f --- /dev/null +++ b/examples-testing/examples/webgl_materials_cubemap_refraction.ts @@ -0,0 +1,126 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { PLYLoader } from 'three/addons/loaders/PLYLoader.js'; + +let container, stats; + +let camera, scene, renderer; + +let mouseX = 0, + mouseY = 0; + +let windowHalfX = window.innerWidth / 2; +let windowHalfY = window.innerHeight / 2; + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 100000); + camera.position.z = -4000; + + // + + const r = 'textures/cube/Park3Med/'; + + const urls = [r + 'px.jpg', r + 'nx.jpg', r + 'py.jpg', r + 'ny.jpg', r + 'pz.jpg', r + 'nz.jpg']; + + const textureCube = new THREE.CubeTextureLoader().load(urls); + textureCube.mapping = THREE.CubeRefractionMapping; + + scene = new THREE.Scene(); + scene.background = textureCube; + + // LIGHTS + + const ambient = new THREE.AmbientLight(0xffffff, 3.5); + scene.add(ambient); + + // material samples + + const cubeMaterial3 = new THREE.MeshPhongMaterial({ + color: 0xccddff, + envMap: textureCube, + refractionRatio: 0.98, + reflectivity: 0.9, + }); + const cubeMaterial2 = new THREE.MeshPhongMaterial({ color: 0xccfffd, envMap: textureCube, refractionRatio: 0.985 }); + const cubeMaterial1 = new THREE.MeshPhongMaterial({ color: 0xffffff, envMap: textureCube, refractionRatio: 0.98 }); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + stats = new Stats(); + container.appendChild(stats.dom); + + const loader = new PLYLoader(); + loader.load('models/ply/binary/Lucy100k.ply', function (geometry) { + createScene(geometry, cubeMaterial1, cubeMaterial2, cubeMaterial3); + }); + + document.addEventListener('mousemove', onDocumentMouseMove); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + windowHalfX = window.innerWidth / 2; + windowHalfY = window.innerHeight / 2; + + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function createScene(geometry, m1, m2, m3) { + geometry.computeVertexNormals(); + + const s = 1.5; + + let mesh = new THREE.Mesh(geometry, m1); + mesh.scale.x = mesh.scale.y = mesh.scale.z = s; + scene.add(mesh); + + mesh = new THREE.Mesh(geometry, m2); + mesh.position.x = -1500; + mesh.scale.x = mesh.scale.y = mesh.scale.z = s; + scene.add(mesh); + + mesh = new THREE.Mesh(geometry, m3); + mesh.position.x = 1500; + mesh.scale.x = mesh.scale.y = mesh.scale.z = s; + scene.add(mesh); +} + +function onDocumentMouseMove(event) { + mouseX = (event.clientX - windowHalfX) * 4; + mouseY = (event.clientY - windowHalfY) * 4; +} + +// + +function animate() { + render(); + stats.update(); +} + +function render() { + camera.position.x += (mouseX - camera.position.x) * 0.05; + camera.position.y += (-mouseY - camera.position.y) * 0.05; + + camera.lookAt(scene.position); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_materials_cubemap_render_to_mipmaps.ts b/examples-testing/examples/webgl_materials_cubemap_render_to_mipmaps.ts new file mode 100644 index 000000000..599a1369b --- /dev/null +++ b/examples-testing/examples/webgl_materials_cubemap_render_to_mipmaps.ts @@ -0,0 +1,183 @@ +import * as THREE from 'three'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let container; +let camera, scene, renderer; + +const CubemapFilterShader = { + name: 'CubemapFilterShader', + + uniforms: { + cubeTexture: { value: null }, + mipIndex: { value: 0 }, + }, + + vertexShader: /* glsl */ ` + + varying vec3 vWorldDirection; + + #include + + void main() { + vWorldDirection = transformDirection(position, modelMatrix); + #include + #include + gl_Position.z = gl_Position.w; // set z to camera.far + } + + `, + + fragmentShader: /* glsl */ ` + + uniform samplerCube cubeTexture; + varying vec3 vWorldDirection; + + uniform float mipIndex; + + #include + + void main() { + vec3 cubeCoordinates = normalize(vWorldDirection); + + // Colorize mip levels + vec4 color = vec4(1.0, 0.0, 0.0, 1.0); + if (mipIndex == 0.0) color.rgb = vec3(1.0, 1.0, 1.0); + else if (mipIndex == 1.0) color.rgb = vec3(0.0, 0.0, 1.0); + else if (mipIndex == 2.0) color.rgb = vec3(0.0, 1.0, 1.0); + else if (mipIndex == 3.0) color.rgb = vec3(0.0, 1.0, 0.0); + else if (mipIndex == 4.0) color.rgb = vec3(1.0, 1.0, 0.0); + + gl_FragColor = textureCube(cubeTexture, cubeCoordinates, 0.0) * color; + } + + `, +}; + +init(); + +async function loadCubeTexture(urls) { + return new Promise(function (resolve) { + new THREE.CubeTextureLoader().load(urls, function (cubeTexture) { + resolve(cubeTexture); + }); + }); +} + +function allocateCubemapRenderTarget(cubeMapSize) { + const params = { + magFilter: THREE.LinearFilter, + minFilter: THREE.LinearMipMapLinearFilter, + generateMipmaps: false, + type: THREE.HalfFloatType, + format: THREE.RGBAFormat, + colorSpace: THREE.LinearSRGBColorSpace, + depthBuffer: false, + }; + + const rt = new THREE.WebGLCubeRenderTarget(cubeMapSize, params); + + const mipLevels = Math.log(cubeMapSize) * Math.LOG2E + 1.0; + for (let i = 0; i < mipLevels; i++) rt.texture.mipmaps.push({}); + + rt.texture.mapping = THREE.CubeReflectionMapping; + return rt; +} + +function renderToCubeTexture(cubeMapRenderTarget, sourceCubeTexture) { + const geometry = new THREE.BoxGeometry(5, 5, 5); + + const material = new THREE.ShaderMaterial({ + name: CubemapFilterShader.name, + uniforms: THREE.UniformsUtils.clone(CubemapFilterShader.uniforms), + vertexShader: CubemapFilterShader.vertexShader, + fragmentShader: CubemapFilterShader.fragmentShader, + side: THREE.BackSide, + blending: THREE.NoBlending, + }); + + material.uniforms.cubeTexture.value = sourceCubeTexture; + + const mesh = new THREE.Mesh(geometry, material); + const cubeCamera = new THREE.CubeCamera(1, 10, cubeMapRenderTarget); + const mipmapCount = Math.floor(Math.log2(Math.max(cubeMapRenderTarget.width, cubeMapRenderTarget.height))); + + for (let mipmap = 0; mipmap < mipmapCount; mipmap++) { + material.uniforms.mipIndex.value = mipmap; + material.needsUpdate = true; + + cubeMapRenderTarget.viewport.set( + 0, + 0, + cubeMapRenderTarget.width >> mipmap, + cubeMapRenderTarget.height >> mipmap, + ); + + cubeCamera.activeMipmapLevel = mipmap; + cubeCamera.update(renderer, mesh); + } + + mesh.geometry.dispose(); + mesh.material.dispose(); +} + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + // Create renderer + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000); + camera.position.z = 500; + + // Create controls + const controls = new OrbitControls(camera, renderer.domElement); + controls.minPolarAngle = Math.PI / 4; + controls.maxPolarAngle = Math.PI / 1.5; + + window.addEventListener('resize', onWindowResize); + + // Load a cube texture + const r = 'textures/cube/Park3Med/'; + const urls = [r + 'px.jpg', r + 'nx.jpg', r + 'py.jpg', r + 'ny.jpg', r + 'pz.jpg', r + 'nz.jpg']; + + loadCubeTexture(urls).then(cubeTexture => { + // Allocate a cube map render target + const cubeMapRenderTarget = allocateCubemapRenderTarget(512); + + // Render to all the mip levels of cubeMapRenderTarget + renderToCubeTexture(cubeMapRenderTarget, cubeTexture); + + // Create geometry + const sphere = new THREE.SphereGeometry(100, 128, 128); + let material = new THREE.MeshBasicMaterial({ color: 0xffffff, envMap: cubeTexture }); + + let mesh = new THREE.Mesh(sphere, material); + mesh.position.set(-100, 0, 0); + scene.add(mesh); + + material = material.clone(); + material.envMap = cubeMapRenderTarget.texture; + + mesh = new THREE.Mesh(sphere, material); + mesh.position.set(100, 0, 0); + scene.add(mesh); + }); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_materials_displacementmap.ts b/examples-testing/examples/webgl_materials_displacementmap.ts new file mode 100644 index 000000000..fd0be9a5e --- /dev/null +++ b/examples-testing/examples/webgl_materials_displacementmap.ts @@ -0,0 +1,224 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { OBJLoader } from 'three/addons/loaders/OBJLoader.js'; + +let stats; +let camera, scene, renderer, controls; + +const settings = { + metalness: 1.0, + roughness: 0.4, + ambientIntensity: 0.2, + aoMapIntensity: 1.0, + envMapIntensity: 1.0, + displacementScale: 2.436143, // from original model + normalScale: 1.0, +}; + +let mesh, material; + +let pointLight, ambientLight; + +const height = 500; // of camera frustum + +let r = 0.0; + +init(); +initGui(); + +// Init gui +function initGui() { + const gui = new GUI(); + //let gui = gui.addFolder( "Material" ); + gui.add(settings, 'metalness') + .min(0) + .max(1) + .onChange(function (value) { + material.metalness = value; + }); + + gui.add(settings, 'roughness') + .min(0) + .max(1) + .onChange(function (value) { + material.roughness = value; + }); + + gui.add(settings, 'aoMapIntensity') + .min(0) + .max(1) + .onChange(function (value) { + material.aoMapIntensity = value; + }); + + gui.add(settings, 'ambientIntensity') + .min(0) + .max(1) + .onChange(function (value) { + ambientLight.intensity = value; + }); + + gui.add(settings, 'envMapIntensity') + .min(0) + .max(3) + .onChange(function (value) { + material.envMapIntensity = value; + }); + + gui.add(settings, 'displacementScale') + .min(0) + .max(3.0) + .onChange(function (value) { + material.displacementScale = value; + }); + + gui.add(settings, 'normalScale') + .min(-1) + .max(1) + .onChange(function (value) { + material.normalScale.set(1, -1).multiplyScalar(value); + }); +} + +function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + // + + scene = new THREE.Scene(); + + const aspect = window.innerWidth / window.innerHeight; + camera = new THREE.OrthographicCamera(-height * aspect, height * aspect, height, -height, 1, 10000); + camera.position.z = 1500; + scene.add(camera); + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableZoom = false; + controls.enableDamping = true; + + // lights + + ambientLight = new THREE.AmbientLight(0xffffff, settings.ambientIntensity); + scene.add(ambientLight); + + pointLight = new THREE.PointLight(0xff0000, 1.5, 0, 0); + pointLight.position.z = 2500; + scene.add(pointLight); + + const pointLight2 = new THREE.PointLight(0xff6666, 3, 0, 0); + camera.add(pointLight2); + + const pointLight3 = new THREE.PointLight(0x0000ff, 1.5, 0, 0); + pointLight3.position.x = -1000; + pointLight3.position.z = 1000; + scene.add(pointLight3); + + // env map + + const path = 'textures/cube/SwedishRoyalCastle/'; + const format = '.jpg'; + const urls = [ + path + 'px' + format, + path + 'nx' + format, + path + 'py' + format, + path + 'ny' + format, + path + 'pz' + format, + path + 'nz' + format, + ]; + + const reflectionCube = new THREE.CubeTextureLoader().load(urls); + + // textures + + const textureLoader = new THREE.TextureLoader(); + const normalMap = textureLoader.load('models/obj/ninja/normal.png'); + const aoMap = textureLoader.load('models/obj/ninja/ao.jpg'); + const displacementMap = textureLoader.load('models/obj/ninja/displacement.jpg'); + + // material + + material = new THREE.MeshStandardMaterial({ + color: 0xc1c1c1, + roughness: settings.roughness, + metalness: settings.metalness, + + normalMap: normalMap, + normalScale: new THREE.Vector2(1, -1), // why does the normal map require negation in this case? + + aoMap: aoMap, + aoMapIntensity: 1, + + displacementMap: displacementMap, + displacementScale: settings.displacementScale, + displacementBias: -0.428408, // from original model + + envMap: reflectionCube, + envMapIntensity: settings.envMapIntensity, + + side: THREE.DoubleSide, + }); + + // + + const loader = new OBJLoader(); + loader.load('models/obj/ninja/ninjaHead_Low.obj', function (group) { + const geometry = group.children[0].geometry; + geometry.center(); + + mesh = new THREE.Mesh(geometry, material); + mesh.scale.multiplyScalar(25); + scene.add(mesh); + }); + + // + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + const aspect = window.innerWidth / window.innerHeight; + + camera.left = -height * aspect; + camera.right = height * aspect; + camera.top = height; + camera.bottom = -height; + + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + controls.update(); + + stats.begin(); + render(); + stats.end(); +} + +function render() { + pointLight.position.x = 2500 * Math.cos(r); + pointLight.position.z = 2500 * Math.sin(r); + + r += 0.01; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_materials_envmaps.ts b/examples-testing/examples/webgl_materials_envmaps.ts new file mode 100644 index 000000000..18a5542ed --- /dev/null +++ b/examples-testing/examples/webgl_materials_envmaps.ts @@ -0,0 +1,131 @@ +import * as THREE from 'three'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let controls, camera, scene, renderer; +let textureEquirec, textureCube; +let sphereMesh, sphereMaterial, params; + +init(); + +function init() { + // CAMERAS + + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(0, 0, 2.5); + + // SCENE + + scene = new THREE.Scene(); + + // Textures + + const loader = new THREE.CubeTextureLoader(); + loader.setPath('textures/cube/Bridge2/'); + + textureCube = loader.load(['posx.jpg', 'negx.jpg', 'posy.jpg', 'negy.jpg', 'posz.jpg', 'negz.jpg']); + + const textureLoader = new THREE.TextureLoader(); + + textureEquirec = textureLoader.load('textures/2294472375_24a3b8ef46_o.jpg'); + textureEquirec.mapping = THREE.EquirectangularReflectionMapping; + textureEquirec.colorSpace = THREE.SRGBColorSpace; + + scene.background = textureCube; + + // + + const geometry = new THREE.IcosahedronGeometry(1, 15); + sphereMaterial = new THREE.MeshBasicMaterial({ envMap: textureCube }); + sphereMesh = new THREE.Mesh(geometry, sphereMaterial); + scene.add(sphereMesh); + + // + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // + + controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 1.5; + controls.maxDistance = 6; + + // + + params = { + Cube: function () { + scene.background = textureCube; + + sphereMaterial.envMap = textureCube; + sphereMaterial.needsUpdate = true; + }, + Equirectangular: function () { + scene.background = textureEquirec; + + sphereMaterial.envMap = textureEquirec; + sphereMaterial.needsUpdate = true; + }, + Refraction: false, + backgroundRotationX: false, + backgroundRotationY: false, + backgroundRotationZ: false, + syncMaterial: false, + }; + + const gui = new GUI({ width: 300 }); + gui.add(params, 'Cube'); + gui.add(params, 'Equirectangular'); + gui.add(params, 'Refraction').onChange(function (value) { + if (value) { + textureEquirec.mapping = THREE.EquirectangularRefractionMapping; + textureCube.mapping = THREE.CubeRefractionMapping; + } else { + textureEquirec.mapping = THREE.EquirectangularReflectionMapping; + textureCube.mapping = THREE.CubeReflectionMapping; + } + + sphereMaterial.needsUpdate = true; + }); + gui.add(params, 'backgroundRotationX'); + gui.add(params, 'backgroundRotationY'); + gui.add(params, 'backgroundRotationZ'); + gui.add(params, 'syncMaterial'); + gui.open(); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + if (params.backgroundRotationX) { + scene.backgroundRotation.x += 0.001; + } + + if (params.backgroundRotationY) { + scene.backgroundRotation.y += 0.001; + } + + if (params.backgroundRotationZ) { + scene.backgroundRotation.z += 0.001; + } + + if (params.syncMaterial) { + sphereMesh.material.envMapRotation.copy(scene.backgroundRotation); + } + + camera.lookAt(scene.position); + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_materials_envmaps_exr.ts b/examples-testing/examples/webgl_materials_envmaps_exr.ts new file mode 100644 index 000000000..c3f3f4f7d --- /dev/null +++ b/examples-testing/examples/webgl_materials_envmaps_exr.ts @@ -0,0 +1,153 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { EXRLoader } from 'three/addons/loaders/EXRLoader.js'; + +const params = { + envMap: 'EXR', + roughness: 0.0, + metalness: 0.0, + exposure: 1.0, + debug: false, +}; + +let container, stats; +let camera, scene, renderer, controls; +let torusMesh, planeMesh; +let pngCubeRenderTarget, exrCubeRenderTarget; +let pngBackground, exrBackground; + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(0, 0, 120); + + scene = new THREE.Scene(); + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + + container.appendChild(renderer.domElement); + + renderer.toneMapping = THREE.ACESFilmicToneMapping; + + // + + let geometry = new THREE.TorusKnotGeometry(18, 8, 150, 20); + let material = new THREE.MeshStandardMaterial({ + metalness: params.metalness, + roughness: params.roughness, + envMapIntensity: 1.0, + }); + + torusMesh = new THREE.Mesh(geometry, material); + scene.add(torusMesh); + + geometry = new THREE.PlaneGeometry(200, 200); + material = new THREE.MeshBasicMaterial(); + + planeMesh = new THREE.Mesh(geometry, material); + planeMesh.position.y = -50; + planeMesh.rotation.x = -Math.PI * 0.5; + scene.add(planeMesh); + + THREE.DefaultLoadingManager.onLoad = function () { + pmremGenerator.dispose(); + }; + + new EXRLoader().load('textures/piz_compressed.exr', function (texture) { + texture.mapping = THREE.EquirectangularReflectionMapping; + + exrCubeRenderTarget = pmremGenerator.fromEquirectangular(texture); + exrBackground = texture; + }); + + new THREE.TextureLoader().load('textures/equirectangular.png', function (texture) { + texture.mapping = THREE.EquirectangularReflectionMapping; + texture.colorSpace = THREE.SRGBColorSpace; + + pngCubeRenderTarget = pmremGenerator.fromEquirectangular(texture); + pngBackground = texture; + }); + + const pmremGenerator = new THREE.PMREMGenerator(renderer); + pmremGenerator.compileEquirectangularShader(); + + stats = new Stats(); + container.appendChild(stats.dom); + + controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 50; + controls.maxDistance = 300; + + window.addEventListener('resize', onWindowResize); + + const gui = new GUI(); + + gui.add(params, 'envMap', ['EXR', 'PNG']); + gui.add(params, 'roughness', 0, 1, 0.01); + gui.add(params, 'metalness', 0, 1, 0.01); + gui.add(params, 'exposure', 0, 2, 0.01); + gui.add(params, 'debug'); + gui.open(); +} + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); +} + +function animate() { + stats.begin(); + render(); + stats.end(); +} + +function render() { + torusMesh.material.roughness = params.roughness; + torusMesh.material.metalness = params.metalness; + + let newEnvMap = torusMesh.material.envMap; + let background = scene.background; + + switch (params.envMap) { + case 'EXR': + newEnvMap = exrCubeRenderTarget ? exrCubeRenderTarget.texture : null; + background = exrBackground; + break; + case 'PNG': + newEnvMap = pngCubeRenderTarget ? pngCubeRenderTarget.texture : null; + background = pngBackground; + break; + } + + if (newEnvMap !== torusMesh.material.envMap) { + torusMesh.material.envMap = newEnvMap; + torusMesh.material.needsUpdate = true; + + planeMesh.material.map = newEnvMap; + planeMesh.material.needsUpdate = true; + } + + torusMesh.rotation.y += 0.005; + planeMesh.visible = params.debug; + + scene.background = background; + renderer.toneMappingExposure = params.exposure; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_materials_envmaps_fasthdr.ts b/examples-testing/examples/webgl_materials_envmaps_fasthdr.ts new file mode 100644 index 000000000..de8715eb7 --- /dev/null +++ b/examples-testing/examples/webgl_materials_envmaps_fasthdr.ts @@ -0,0 +1,169 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js'; + +const params = { + image: 'ballroom', + fov: 40, + exposure: 1.0, + backgroundBlurriness: 0.0, +}; + +let container, stats; +let camera, scene, renderer, controls; + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(params.fov, window.innerWidth / window.innerHeight, 0.1, 50); + camera.position.set(7, 0, 0); + + scene = new THREE.Scene(); + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + + container.appendChild(renderer.domElement); + + renderer.toneMapping = THREE.ACESFilmicToneMapping; + + // + + const sphereGeometry = new THREE.SphereGeometry(0.45, 64, 32); + + const sphere01 = new THREE.Mesh( + sphereGeometry, + new THREE.MeshPhysicalMaterial({ + transmission: 1.0, + thickness: 2.0, + metalness: 0.0, + roughness: 0.0, + }), + ); + sphere01.position.z += 2; + scene.add(sphere01); + + const sphere02 = new THREE.Mesh( + sphereGeometry, + new THREE.MeshStandardMaterial({ + metalness: 0.0, + roughness: 1.0, + }), + ); + sphere02.position.z += 1; + scene.add(sphere02); + + const sphere03 = new THREE.Mesh( + sphereGeometry, + new THREE.MeshStandardMaterial({ + metalness: 1.0, + roughness: 0.0, + }), + ); + sphere03.position.z += 0; + scene.add(sphere03); + + const sphere04 = new THREE.Mesh( + sphereGeometry, + new THREE.MeshStandardMaterial({ + metalness: 1.0, + roughness: 0.5, + color: 0x888888, + }), + ); + sphere04.position.z -= 1; + scene.add(sphere04); + + const sphere05 = new THREE.Mesh( + sphereGeometry, + new THREE.MeshStandardMaterial({ + metalness: 0.0, + roughness: 0.0, + color: 0x6ab440, + }), + ); + sphere05.position.z -= 2; + scene.add(sphere05); + + const loader = new KTX2Loader().setTranscoderPath('jsm/libs/basis/').detectSupport(renderer); + + function loadTexture(url) { + loader.load(url, texture => { + texture.mapping = THREE.CubeUVReflectionMapping; + scene.environment = texture; + scene.background = texture; + }); + } + + loadTexture('https://cdn.needle.tools/static/hdris/ballroom_2k.pmrem.ktx2'); + + stats = new Stats(); + container.appendChild(stats.dom); + + controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 0.1; + controls.maxDistance = 20; + controls.enableDamping = true; + + window.addEventListener('resize', onWindowResize); + + const gui = new GUI(); + + gui.add(params, 'image', { + ballroom: 'https://cdn.needle.tools/static/hdris/ballroom_2k.pmrem.ktx2', + 'brown photostudio': 'https://cdn.needle.tools/static/hdris/brown_photostudio_02_2k.pmrem.ktx2', + 'cape hill': 'https://cdn.needle.tools/static/hdris/cape_hill_2k.pmrem.ktx2', + cannon: 'https://cdn.needle.tools/static/hdris/cannon_2k.pmrem.ktx2', + 'metro noord': 'https://cdn.needle.tools/static/hdris/metro_noord_2k.pmrem.ktx2', + 'the sky is on fire': 'https://cdn.needle.tools/static/hdris/the_sky_is_on_fire_2k.pmrem.ktx2', + 'studio small 09': 'https://cdn.needle.tools/static/hdris/studio_small_09_2k.pmrem.ktx2', + 'wide street 01': 'https://cdn.needle.tools/static/hdris/wide_street_01_2k.pmrem.ktx2', + }).onChange(() => { + loadTexture(params.image); + }); + + gui.add(params, 'exposure', 0, 2, 0.01); + + gui.add(params, 'fov', 10, 100).onChange(() => { + camera.fov = params.fov; + camera.updateProjectionMatrix(); + }); + + gui.add(params, 'backgroundBlurriness', 0, 1, 0.01).name('background blurriness'); + + gui.open(); +} + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); +} + +function animate() { + stats.begin(); + render(); + stats.end(); +} + +function render() { + renderer.toneMappingExposure = params.exposure; + scene.backgroundBlurriness = params.backgroundBlurriness; + + controls.update(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_materials_envmaps_groundprojected.ts b/examples-testing/examples/webgl_materials_envmaps_groundprojected.ts new file mode 100644 index 000000000..c1e0ed83b --- /dev/null +++ b/examples-testing/examples/webgl_materials_envmaps_groundprojected.ts @@ -0,0 +1,151 @@ +import * as THREE from 'three'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GroundedSkybox } from 'three/addons/objects/GroundedSkybox.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; +import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; + +const params = { + height: 15, + radius: 100, + enabled: true, +}; + +let camera, scene, renderer, skybox; + +init().then(render); + +async function init() { + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(-20, 7, 20); + camera.lookAt(0, 4, 0); + + scene = new THREE.Scene(); + + const hdrLoader = new HDRLoader(); + const envMap = await hdrLoader.loadAsync('textures/equirectangular/blouberg_sunrise_2_1k.hdr'); + envMap.mapping = THREE.EquirectangularReflectionMapping; + + skybox = new GroundedSkybox(envMap, params.height, params.radius); + skybox.position.y = params.height - 0.01; + scene.add(skybox); + + scene.environment = envMap; + + const dracoLoader = new DRACOLoader(); + dracoLoader.setDecoderPath('jsm/libs/draco/gltf/'); + + const loader = new GLTFLoader(); + loader.setDRACOLoader(dracoLoader); + + const shadow = new THREE.TextureLoader().load('models/gltf/ferrari_ao.png'); + + loader.load('models/gltf/ferrari.glb', function (gltf) { + const bodyMaterial = new THREE.MeshPhysicalMaterial({ + color: 0x000000, + metalness: 1.0, + roughness: 0.8, + clearcoat: 1.0, + clearcoatRoughness: 0.2, + }); + + const detailsMaterial = new THREE.MeshStandardMaterial({ + color: 0xffffff, + metalness: 1.0, + roughness: 0.5, + }); + + const glassMaterial = new THREE.MeshPhysicalMaterial({ + color: 0xffffff, + metalness: 0.25, + roughness: 0, + transmission: 1.0, + }); + + const carModel = gltf.scene.children[0]; + carModel.scale.multiplyScalar(4); + carModel.rotation.y = Math.PI; + + carModel.getObjectByName('body').material = bodyMaterial; + + carModel.getObjectByName('rim_fl').material = detailsMaterial; + carModel.getObjectByName('rim_fr').material = detailsMaterial; + carModel.getObjectByName('rim_rr').material = detailsMaterial; + carModel.getObjectByName('rim_rl').material = detailsMaterial; + carModel.getObjectByName('trim').material = detailsMaterial; + + carModel.getObjectByName('glass').material = glassMaterial; + + // shadow + const mesh = new THREE.Mesh( + new THREE.PlaneGeometry(0.655 * 4, 1.3 * 4), + new THREE.MeshBasicMaterial({ + map: shadow, + blending: THREE.MultiplyBlending, + toneMapped: false, + transparent: true, + premultipliedAlpha: true, + }), + ); + mesh.rotation.x = -Math.PI / 2; + carModel.add(mesh); + + scene.add(carModel); + + render(); + }); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + + // + + const controls = new OrbitControls(camera, renderer.domElement); + controls.addEventListener('change', render); + controls.target.set(0, 2, 0); + controls.maxPolarAngle = THREE.MathUtils.degToRad(90); + controls.maxDistance = 80; + controls.minDistance = 20; + controls.enablePan = false; + controls.update(); + + document.body.appendChild(renderer.domElement); + window.addEventListener('resize', onWindowResize); + + const gui = new GUI(); + + gui.add(params, 'enabled') + .name('Grounded') + .onChange(function (value) { + if (value) { + scene.add(skybox); + scene.background = null; + } else { + scene.remove(skybox); + scene.background = scene.environment; + } + + render(); + }); + + gui.open(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + render(); +} + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_materials_envmaps_hdr.ts b/examples-testing/examples/webgl_materials_envmaps_hdr.ts new file mode 100644 index 000000000..2dc2b808a --- /dev/null +++ b/examples-testing/examples/webgl_materials_envmaps_hdr.ts @@ -0,0 +1,163 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { HDRCubeTextureLoader } from 'three/addons/loaders/HDRCubeTextureLoader.js'; +import { DebugEnvironment } from 'three/addons/environments/DebugEnvironment.js'; + +const params = { + envMap: 'HDR', + roughness: 0.0, + metalness: 0.0, + exposure: 1.0, + debug: false, +}; + +let container, stats; +let camera, scene, renderer, controls; +let torusMesh, planeMesh; +let generatedCubeRenderTarget, ldrCubeRenderTarget, hdrCubeRenderTarget; +let ldrCubeMap, hdrCubeMap; + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(0, 0, 120); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x000000); + + renderer = new THREE.WebGLRenderer(); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + + // + + let geometry = new THREE.TorusKnotGeometry(18, 8, 150, 20); + // let geometry = new THREE.SphereGeometry( 26, 64, 32 ); + let material = new THREE.MeshStandardMaterial({ + color: 0xffffff, + metalness: params.metalness, + roughness: params.roughness, + }); + + torusMesh = new THREE.Mesh(geometry, material); + scene.add(torusMesh); + + geometry = new THREE.PlaneGeometry(200, 200); + material = new THREE.MeshBasicMaterial(); + + planeMesh = new THREE.Mesh(geometry, material); + planeMesh.position.y = -50; + planeMesh.rotation.x = -Math.PI * 0.5; + scene.add(planeMesh); + + THREE.DefaultLoadingManager.onLoad = function () { + pmremGenerator.dispose(); + }; + + const hdrUrls = ['px.hdr', 'nx.hdr', 'py.hdr', 'ny.hdr', 'pz.hdr', 'nz.hdr']; + hdrCubeMap = new HDRCubeTextureLoader().setPath('./textures/cube/pisaHDR/').load(hdrUrls, function () { + hdrCubeRenderTarget = pmremGenerator.fromCubemap(hdrCubeMap); + + hdrCubeMap.magFilter = THREE.LinearFilter; + hdrCubeMap.needsUpdate = true; + }); + + const ldrUrls = ['px.png', 'nx.png', 'py.png', 'ny.png', 'pz.png', 'nz.png']; + ldrCubeMap = new THREE.CubeTextureLoader().setPath('./textures/cube/pisa/').load(ldrUrls, function () { + ldrCubeRenderTarget = pmremGenerator.fromCubemap(ldrCubeMap); + }); + + const pmremGenerator = new THREE.PMREMGenerator(renderer); + pmremGenerator.compileCubemapShader(); + + const envScene = new DebugEnvironment(); + generatedCubeRenderTarget = pmremGenerator.fromScene(envScene); + + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + //renderer.toneMapping = ReinhardToneMapping; + + stats = new Stats(); + container.appendChild(stats.dom); + + controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 50; + controls.maxDistance = 300; + + window.addEventListener('resize', onWindowResize); + + const gui = new GUI(); + + gui.add(params, 'envMap', ['Generated', 'LDR', 'HDR']); + gui.add(params, 'roughness', 0, 1, 0.01); + gui.add(params, 'metalness', 0, 1, 0.01); + gui.add(params, 'exposure', 0, 2, 0.01); + gui.add(params, 'debug'); + gui.open(); +} + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); +} + +function animate() { + stats.begin(); + render(); + stats.end(); +} + +function render() { + torusMesh.material.roughness = params.roughness; + torusMesh.material.metalness = params.metalness; + + let renderTarget, cubeMap; + + switch (params.envMap) { + case 'Generated': + renderTarget = generatedCubeRenderTarget; + cubeMap = generatedCubeRenderTarget.texture; + break; + case 'LDR': + renderTarget = ldrCubeRenderTarget; + cubeMap = ldrCubeMap; + break; + case 'HDR': + renderTarget = hdrCubeRenderTarget; + cubeMap = hdrCubeMap; + break; + } + + const newEnvMap = renderTarget ? renderTarget.texture : null; + + if (newEnvMap && newEnvMap !== torusMesh.material.envMap) { + torusMesh.material.envMap = newEnvMap; + torusMesh.material.needsUpdate = true; + + planeMesh.material.map = newEnvMap; + planeMesh.material.needsUpdate = true; + } + + torusMesh.rotation.y += 0.005; + planeMesh.visible = params.debug; + + scene.background = cubeMap; + renderer.toneMappingExposure = params.exposure; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_materials_modified.ts b/examples-testing/examples/webgl_materials_modified.ts new file mode 100644 index 000000000..de36aeb7d --- /dev/null +++ b/examples-testing/examples/webgl_materials_modified.ts @@ -0,0 +1,115 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +let camera, scene, renderer, stats; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.z = 20; + + scene = new THREE.Scene(); + + const loader = new GLTFLoader(); + loader.load('models/gltf/LeePerrySmith/LeePerrySmith.glb', function (gltf) { + const geometry = gltf.scene.children[0].geometry; + + let mesh = new THREE.Mesh(geometry, buildTwistMaterial(2.0)); + mesh.position.x = -3.5; + mesh.position.y = -0.5; + scene.add(mesh); + + mesh = new THREE.Mesh(geometry, buildTwistMaterial(-2.0)); + mesh.position.x = 3.5; + mesh.position.y = -0.5; + scene.add(mesh); + }); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 10; + controls.maxDistance = 50; + + // + + stats = new Stats(); + document.body.appendChild(stats.dom); + + // EVENTS + + window.addEventListener('resize', onWindowResize); +} + +function buildTwistMaterial(amount) { + const material = new THREE.MeshNormalMaterial(); + material.onBeforeCompile = function (shader) { + shader.uniforms.time = { value: 0 }; + + shader.vertexShader = 'uniform float time;\n' + shader.vertexShader; + shader.vertexShader = shader.vertexShader.replace( + '#include ', + [ + `float theta = sin( time + position.y ) / ${amount.toFixed(1)};`, + 'float c = cos( theta );', + 'float s = sin( theta );', + 'mat3 m = mat3( c, 0, s, 0, 1, 0, -s, 0, c );', + 'vec3 transformed = vec3( position ) * m;', + 'vNormal = vNormal * m;', + ].join('\n'), + ); + + material.userData.shader = shader; + }; + + // Make sure WebGLRenderer doesn't reuse a single program + + material.customProgramCacheKey = function () { + return amount.toFixed(1); + }; + + return material; +} + +// + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); +} + +// + +function animate() { + render(); + + stats.update(); +} + +function render() { + scene.traverse(function (child) { + if (child.isMesh) { + const shader = child.material.userData.shader; + + if (shader) { + shader.uniforms.time.value = performance.now() / 1000; + } + } + }); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_materials_normalmap_object_space.ts b/examples-testing/examples/webgl_materials_normalmap_object_space.ts new file mode 100644 index 000000000..1fc6f8066 --- /dev/null +++ b/examples-testing/examples/webgl_materials_normalmap_object_space.ts @@ -0,0 +1,82 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +let renderer, scene, camera; + +init(); + +function init() { + // renderer + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setSize(window.innerWidth, window.innerHeight); + document.body.appendChild(renderer.domElement); + + // scene + scene = new THREE.Scene(); + + // camera + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(-10, 0, 23); + scene.add(camera); + + // controls + const controls = new OrbitControls(camera, renderer.domElement); + controls.addEventListener('change', render); + controls.minDistance = 10; + controls.maxDistance = 50; + controls.enablePan = false; + + // ambient + scene.add(new THREE.AmbientLight(0xffffff, 0.6)); + + // light + const light = new THREE.PointLight(0xffffff, 4.5, 0, 0); + camera.add(light); + + // model + new GLTFLoader().load('models/gltf/Nefertiti/Nefertiti.glb', function (gltf) { + gltf.scene.traverse(function (child) { + if (child.isMesh) { + // glTF currently supports only tangent-space normal maps. + // this model has been modified to demonstrate the use of an object-space normal map. + + child.material.normalMapType = THREE.ObjectSpaceNormalMap; + + // attribute normals are not required with an object-space normal map. remove them. + + child.geometry.deleteAttribute('normal'); + + // + + child.material.side = THREE.DoubleSide; + + child.scale.multiplyScalar(0.5); + + // recenter + + new THREE.Box3().setFromObject(child).getCenter(child.position).multiplyScalar(-1); + + scene.add(child); + } + }); + + render(); + }); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + renderer.setSize(window.innerWidth, window.innerHeight); + + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + render(); +} + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_materials_physical_clearcoat.ts b/examples-testing/examples/webgl_materials_physical_clearcoat.ts new file mode 100644 index 000000000..408fd9921 --- /dev/null +++ b/examples-testing/examples/webgl_materials_physical_clearcoat.ts @@ -0,0 +1,208 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { HDRCubeTextureLoader } from 'three/addons/loaders/HDRCubeTextureLoader.js'; + +import { FlakesTexture } from 'three/addons/textures/FlakesTexture.js'; + +let container, stats; + +let camera, scene, renderer; + +let particleLight; +let group; + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 0.25, 50); + camera.position.z = 10; + + scene = new THREE.Scene(); + + group = new THREE.Group(); + scene.add(group); + + new HDRCubeTextureLoader() + .setPath('textures/cube/pisaHDR/') + .load(['px.hdr', 'nx.hdr', 'py.hdr', 'ny.hdr', 'pz.hdr', 'nz.hdr'], function (texture) { + const geometry = new THREE.SphereGeometry(0.8, 64, 32); + + const textureLoader = new THREE.TextureLoader(); + + const diffuse = textureLoader.load('textures/carbon/Carbon.png'); + diffuse.colorSpace = THREE.SRGBColorSpace; + diffuse.wrapS = THREE.RepeatWrapping; + diffuse.wrapT = THREE.RepeatWrapping; + diffuse.repeat.x = 10; + diffuse.repeat.y = 10; + + const normalMap = textureLoader.load('textures/carbon/Carbon_Normal.png'); + normalMap.wrapS = THREE.RepeatWrapping; + normalMap.wrapT = THREE.RepeatWrapping; + normalMap.repeat.x = 10; + normalMap.repeat.y = 10; + + const normalMap2 = textureLoader.load('textures/water/Water_1_M_Normal.jpg'); + + const normalMap3 = new THREE.CanvasTexture(new FlakesTexture()); + normalMap3.wrapS = THREE.RepeatWrapping; + normalMap3.wrapT = THREE.RepeatWrapping; + normalMap3.repeat.x = 10; + normalMap3.repeat.y = 6; + normalMap3.anisotropy = 16; + + const normalMap4 = textureLoader.load('textures/golfball.jpg'); + + const clearcoatNormalMap = textureLoader.load( + 'textures/pbr/Scratched_gold/Scratched_gold_01_1K_Normal.png', + ); + + // car paint + + let material = new THREE.MeshPhysicalMaterial({ + clearcoat: 1.0, + clearcoatRoughness: 0.1, + metalness: 0.9, + roughness: 0.5, + color: 0x0000ff, + normalMap: normalMap3, + normalScale: new THREE.Vector2(0.15, 0.15), + }); + + let mesh = new THREE.Mesh(geometry, material); + mesh.position.x = -1; + mesh.position.y = 1; + group.add(mesh); + + // fibers + + material = new THREE.MeshPhysicalMaterial({ + roughness: 0.5, + clearcoat: 1.0, + clearcoatRoughness: 0.1, + map: diffuse, + normalMap: normalMap, + }); + mesh = new THREE.Mesh(geometry, material); + mesh.position.x = 1; + mesh.position.y = 1; + group.add(mesh); + + // golf + + material = new THREE.MeshPhysicalMaterial({ + metalness: 0.0, + roughness: 0.1, + clearcoat: 1.0, + normalMap: normalMap4, + clearcoatNormalMap: clearcoatNormalMap, + + // y scale is negated to compensate for normal map handedness. + clearcoatNormalScale: new THREE.Vector2(2.0, -2.0), + }); + mesh = new THREE.Mesh(geometry, material); + mesh.position.x = -1; + mesh.position.y = -1; + group.add(mesh); + + // clearcoat + normalmap + + material = new THREE.MeshPhysicalMaterial({ + clearcoat: 1.0, + metalness: 1.0, + color: 0xff0000, + normalMap: normalMap2, + normalScale: new THREE.Vector2(0.15, 0.15), + clearcoatNormalMap: clearcoatNormalMap, + + // y scale is negated to compensate for normal map handedness. + clearcoatNormalScale: new THREE.Vector2(2.0, -2.0), + }); + mesh = new THREE.Mesh(geometry, material); + mesh.position.x = 1; + mesh.position.y = -1; + group.add(mesh); + + // + + scene.background = texture; + scene.environment = texture; + }); + + // LIGHTS + + particleLight = new THREE.Mesh( + new THREE.SphereGeometry(0.05, 8, 8), + new THREE.MeshBasicMaterial({ color: 0xffffff }), + ); + scene.add(particleLight); + + particleLight.add(new THREE.PointLight(0xffffff, 30)); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + // + + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = 1.25; + + // + + // + + stats = new Stats(); + container.appendChild(stats.dom); + + // EVENTS + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 3; + controls.maxDistance = 30; + + window.addEventListener('resize', onWindowResize); +} + +// + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); +} + +// + +function animate() { + render(); + + stats.update(); +} + +function render() { + const timer = Date.now() * 0.00025; + + particleLight.position.x = Math.sin(timer * 7) * 3; + particleLight.position.y = Math.cos(timer * 5) * 4; + particleLight.position.z = Math.cos(timer * 3) * 3; + + for (let i = 0; i < group.children.length; i++) { + const child = group.children[i]; + child.rotation.y += 0.005; + } + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_materials_physical_transmission.ts b/examples-testing/examples/webgl_materials_physical_transmission.ts new file mode 100644 index 000000000..08ee08cac --- /dev/null +++ b/examples-testing/examples/webgl_materials_physical_transmission.ts @@ -0,0 +1,192 @@ +import * as THREE from 'three'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; + +const params = { + color: 0xffffff, + transmission: 1, + opacity: 1, + metalness: 0, + roughness: 0, + ior: 1.5, + thickness: 0.01, + specularIntensity: 1, + specularColor: 0xffffff, + envMapIntensity: 1, + lightIntensity: 1, + exposure: 1, + transmissionResolutionScale: 1, +}; + +let camera, scene, renderer; + +let mesh; + +const hdrEquirect = new UltraHDRLoader() + .setPath('textures/equirectangular/') + .load('royal_esplanade_2k.hdr.jpg', function () { + hdrEquirect.mapping = THREE.EquirectangularReflectionMapping; + + init(); + render(); + }); + +function init() { + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.shadowMap.enabled = true; + document.body.appendChild(renderer.domElement); + + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = params.exposure; + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 2000); + camera.position.set(0, 0, 120); + + // + + scene.background = hdrEquirect; + + // + + const geometry = new THREE.SphereGeometry(20, 64, 32); + + const texture = new THREE.CanvasTexture(generateTexture()); + texture.magFilter = THREE.NearestFilter; + texture.wrapT = THREE.RepeatWrapping; + texture.wrapS = THREE.RepeatWrapping; + texture.repeat.set(1, 3.5); + + const material = new THREE.MeshPhysicalMaterial({ + color: params.color, + metalness: params.metalness, + roughness: params.roughness, + ior: params.ior, + alphaMap: texture, + envMap: hdrEquirect, + envMapIntensity: params.envMapIntensity, + transmission: params.transmission, // use material.transmission for glass materials + specularIntensity: params.specularIntensity, + specularColor: params.specularColor, + opacity: params.opacity, + side: THREE.DoubleSide, + transparent: true, + }); + + mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + // + + const controls = new OrbitControls(camera, renderer.domElement); + controls.addEventListener('change', render); // use if there is no animation loop + controls.minDistance = 10; + controls.maxDistance = 150; + + window.addEventListener('resize', onWindowResize); + + // + + const gui = new GUI(); + + gui.addColor(params, 'color').onChange(function () { + material.color.set(params.color); + render(); + }); + + gui.add(params, 'transmission', 0, 1, 0.01).onChange(function () { + material.transmission = params.transmission; + render(); + }); + + gui.add(params, 'opacity', 0, 1, 0.01).onChange(function () { + material.opacity = params.opacity; + render(); + }); + + gui.add(params, 'metalness', 0, 1, 0.01).onChange(function () { + material.metalness = params.metalness; + render(); + }); + + gui.add(params, 'roughness', 0, 1, 0.01).onChange(function () { + material.roughness = params.roughness; + render(); + }); + + gui.add(params, 'ior', 1, 2, 0.01).onChange(function () { + material.ior = params.ior; + render(); + }); + + gui.add(params, 'thickness', 0, 5, 0.01).onChange(function () { + material.thickness = params.thickness; + render(); + }); + + gui.add(params, 'specularIntensity', 0, 1, 0.01).onChange(function () { + material.specularIntensity = params.specularIntensity; + render(); + }); + + gui.addColor(params, 'specularColor').onChange(function () { + material.specularColor.set(params.specularColor); + render(); + }); + + gui.add(params, 'envMapIntensity', 0, 1, 0.01) + .name('envMap intensity') + .onChange(function () { + material.envMapIntensity = params.envMapIntensity; + render(); + }); + + gui.add(params, 'exposure', 0, 1, 0.01).onChange(function () { + renderer.toneMappingExposure = params.exposure; + render(); + }); + + gui.add(params, 'transmissionResolutionScale', 0.01, 1, 0.01) + .name('transmission resolution') + .onChange(function () { + renderer.transmissionResolutionScale = params.transmissionResolutionScale; + render(); + }); + + gui.open(); +} + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); + + render(); +} + +// + +function generateTexture() { + const canvas = document.createElement('canvas'); + canvas.width = 2; + canvas.height = 2; + + const context = canvas.getContext('2d'); + context.fillStyle = 'white'; + context.fillRect(0, 1, 2, 1); + + return canvas; +} + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_materials_physical_transmission_alpha.ts b/examples-testing/examples/webgl_materials_physical_transmission_alpha.ts new file mode 100644 index 000000000..6318d7844 --- /dev/null +++ b/examples-testing/examples/webgl_materials_physical_transmission_alpha.ts @@ -0,0 +1,194 @@ +import * as THREE from 'three'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +const params = { + color: 0xffffff, + transmission: 1, + opacity: 1, + metalness: 0, + roughness: 0, + ior: 1.5, + thickness: 0.01, + attenuationColor: 0xffffff, + attenuationDistance: 1, + specularIntensity: 1, + specularColor: 0xffffff, + envMapIntensity: 1, + lightIntensity: 1, + exposure: 1, +}; + +let camera, scene, renderer; + +let mesh, material; + +const hdrEquirect = new UltraHDRLoader() + .setPath('textures/equirectangular/') + .load('royal_esplanade_2k.hdr.jpg', function () { + hdrEquirect.mapping = THREE.EquirectangularReflectionMapping; + + new GLTFLoader().setPath('models/gltf/').load('DragonAttenuation.glb', function (gltf) { + gltf.scene.traverse(function (child) { + if (child.isMesh && child.material.isMeshPhysicalMaterial) { + mesh = child; + material = mesh.material; + + const color = new THREE.Color(); + + params.color = color.copy(mesh.material.color).getHex(); + params.roughness = mesh.material.roughness; + params.metalness = mesh.material.metalness; + + params.ior = mesh.material.ior; + params.specularIntensity = mesh.material.specularIntensity; + + params.transmission = mesh.material.transmission; + params.thickness = mesh.material.thickness; + params.attenuationColor = color.copy(mesh.material.attenuationColor).getHex(); + params.attenuationDistance = mesh.material.attenuationDistance; + } + }); + + init(); + + scene.add(gltf.scene); + + scene.environment = hdrEquirect; + //scene.background = hdrEquirect; + + render(); + }); + }); + +function init() { + renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.shadowMap.enabled = true; + document.body.appendChild(renderer.domElement); + + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = params.exposure; + + // accommodate CSS table + renderer.domElement.style.position = 'absolute'; + renderer.domElement.style.top = 0; + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 2000); + camera.position.set(-5, 0.5, 0); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.addEventListener('change', render); // use if there is no animation loop + controls.minDistance = 5; + controls.maxDistance = 20; + controls.target.y = 0.5; + controls.update(); + + window.addEventListener('resize', onWindowResize); + + // + + const gui = new GUI(); + + gui.addColor(params, 'color').onChange(function () { + material.color.set(params.color); + render(); + }); + + gui.add(params, 'transmission', 0, 1, 0.01).onChange(function () { + material.transmission = params.transmission; + render(); + }); + + gui.add(params, 'opacity', 0, 1, 0.01).onChange(function () { + material.opacity = params.opacity; + const transparent = params.opacity < 1; + + if (transparent !== material.transparent) { + material.transparent = transparent; + material.needsUpdate = true; + } + + render(); + }); + + gui.add(params, 'metalness', 0, 1, 0.01).onChange(function () { + material.metalness = params.metalness; + render(); + }); + + gui.add(params, 'roughness', 0, 1, 0.01).onChange(function () { + material.roughness = params.roughness; + render(); + }); + + gui.add(params, 'ior', 1, 2, 0.01).onChange(function () { + material.ior = params.ior; + render(); + }); + + gui.add(params, 'thickness', 0, 5, 0.01).onChange(function () { + material.thickness = params.thickness; + render(); + }); + + gui.addColor(params, 'attenuationColor') + .name('attenuation color') + .onChange(function () { + material.attenuationColor.set(params.attenuationColor); + render(); + }); + + gui.add(params, 'attenuationDistance', 0, 1, 0.01).onChange(function () { + material.attenuationDistance = params.attenuationDistance; + render(); + }); + + gui.add(params, 'specularIntensity', 0, 1, 0.01).onChange(function () { + material.specularIntensity = params.specularIntensity; + render(); + }); + + gui.addColor(params, 'specularColor').onChange(function () { + material.specularColor.set(params.specularColor); + render(); + }); + + gui.add(params, 'envMapIntensity', 0, 1, 0.01) + .name('envMap intensity') + .onChange(function () { + material.envMapIntensity = params.envMapIntensity; + render(); + }); + + gui.add(params, 'exposure', 0, 1, 0.01).onChange(function () { + renderer.toneMappingExposure = params.exposure; + render(); + }); + + gui.open(); +} + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); + + render(); +} + +// + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_materials_texture_anisotropy.ts b/examples-testing/examples/webgl_materials_texture_anisotropy.ts new file mode 100644 index 000000000..1e030d64d --- /dev/null +++ b/examples-testing/examples/webgl_materials_texture_anisotropy.ts @@ -0,0 +1,143 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +const SCREEN_WIDTH = window.innerWidth; +const SCREEN_HEIGHT = window.innerHeight; + +let container, stats; + +let camera, scene1, scene2, renderer; + +let mouseX = 0, + mouseY = 0; + +const windowHalfX = window.innerWidth / 2; +const windowHalfY = window.innerHeight / 2; + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + + // + + camera = new THREE.PerspectiveCamera(35, SCREEN_WIDTH / SCREEN_HEIGHT, 1, 25000); + camera.position.z = 1500; + + scene1 = new THREE.Scene(); + scene1.background = new THREE.Color(0xf2f7ff); + scene1.fog = new THREE.Fog(0xf2f7ff, 1, 25000); + + scene2 = new THREE.Scene(); + scene2.background = new THREE.Color(0xf2f7ff); + scene2.fog = new THREE.Fog(0xf2f7ff, 1, 25000); + + scene1.add(new THREE.AmbientLight(0xeef0ff, 3)); + scene2.add(new THREE.AmbientLight(0xeef0ff, 3)); + + const light1 = new THREE.DirectionalLight(0xffffff, 6); + light1.position.set(1, 1, 1); + scene1.add(light1); + + const light2 = new THREE.DirectionalLight(0xffffff, 6); + light2.position.set(1, 1, 1); + scene2.add(light2); + + // GROUND + + const textureLoader = new THREE.TextureLoader(); + + const maxAnisotropy = renderer.capabilities.getMaxAnisotropy(); + + const texture1 = textureLoader.load('textures/crate.gif'); + const material1 = new THREE.MeshPhongMaterial({ color: 0xffffff, map: texture1 }); + + texture1.colorSpace = THREE.SRGBColorSpace; + texture1.anisotropy = maxAnisotropy; + texture1.wrapS = texture1.wrapT = THREE.RepeatWrapping; + texture1.repeat.set(512, 512); + + const texture2 = textureLoader.load('textures/crate.gif'); + const material2 = new THREE.MeshPhongMaterial({ color: 0xffffff, map: texture2 }); + + texture2.colorSpace = THREE.SRGBColorSpace; + texture2.anisotropy = 1; + texture2.wrapS = texture2.wrapT = THREE.RepeatWrapping; + texture2.repeat.set(512, 512); + + if (maxAnisotropy > 0) { + document.getElementById('val_left').innerHTML = texture1.anisotropy; + document.getElementById('val_right').innerHTML = texture2.anisotropy; + } else { + document.getElementById('val_left').innerHTML = 'not supported'; + document.getElementById('val_right').innerHTML = 'not supported'; + } + + // + + const geometry = new THREE.PlaneGeometry(100, 100); + + const mesh1 = new THREE.Mesh(geometry, material1); + mesh1.rotation.x = -Math.PI / 2; + mesh1.scale.set(1000, 1000, 1000); + + const mesh2 = new THREE.Mesh(geometry, material2); + mesh2.rotation.x = -Math.PI / 2; + mesh2.scale.set(1000, 1000, 1000); + + scene1.add(mesh1); + scene2.add(mesh2); + + // RENDERER + + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); + renderer.setAnimationLoop(animate); + renderer.autoClear = false; + + renderer.domElement.style.position = 'relative'; + container.appendChild(renderer.domElement); + + // STATS1 + + stats = new Stats(); + container.appendChild(stats.dom); + + document.addEventListener('mousemove', onDocumentMouseMove); +} + +function onDocumentMouseMove(event) { + mouseX = event.clientX - windowHalfX; + mouseY = event.clientY - windowHalfY; +} + +function animate() { + render(); + stats.update(); +} + +function render() { + camera.position.x += (mouseX - camera.position.x) * 0.05; + camera.position.y = THREE.MathUtils.clamp( + camera.position.y + (-(mouseY - 200) - camera.position.y) * 0.05, + 50, + 1000, + ); + + camera.lookAt(scene1.position); + + renderer.clear(); + renderer.setScissorTest(true); + + renderer.setScissor(0, 0, SCREEN_WIDTH / 2 - 2, SCREEN_HEIGHT); + renderer.render(scene1, camera); + + renderer.setScissor(SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 2 - 2, SCREEN_HEIGHT); + renderer.render(scene2, camera); + + renderer.setScissorTest(false); +} diff --git a/examples-testing/examples/webgl_materials_texture_canvas.ts b/examples-testing/examples/webgl_materials_texture_canvas.ts new file mode 100644 index 000000000..d23c68436 --- /dev/null +++ b/examples-testing/examples/webgl_materials_texture_canvas.ts @@ -0,0 +1,92 @@ +import * as THREE from 'three'; + +let camera, scene, renderer, mesh, material; +const drawStartPos = new THREE.Vector2(); + +init(); +setupCanvasDrawing(); + +function init() { + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 2000); + camera.position.z = 500; + + scene = new THREE.Scene(); + + material = new THREE.MeshBasicMaterial(); + + mesh = new THREE.Mesh(new THREE.BoxGeometry(200, 200, 200), material); + scene.add(mesh); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + window.addEventListener('resize', onWindowResize); +} + +// Sets up the drawing canvas and adds it as the material map + +function setupCanvasDrawing() { + // get canvas and context + + const drawingCanvas = document.getElementById('drawing-canvas'); + const drawingContext = drawingCanvas.getContext('2d'); + + // draw white background + + drawingContext.fillStyle = '#FFFFFF'; + drawingContext.fillRect(0, 0, 128, 128); + + // set canvas as material.map (this could be done to any map, bump, displacement etc.) + + material.map = new THREE.CanvasTexture(drawingCanvas); + + // set the variable to keep track of when to draw + + let paint = false; + + // add canvas event listeners + drawingCanvas.addEventListener('pointerdown', function (e) { + paint = true; + drawStartPos.set(e.offsetX, e.offsetY); + }); + + drawingCanvas.addEventListener('pointermove', function (e) { + if (paint) draw(drawingContext, e.offsetX, e.offsetY); + }); + + drawingCanvas.addEventListener('pointerup', function () { + paint = false; + }); + + drawingCanvas.addEventListener('pointerleave', function () { + paint = false; + }); +} + +function draw(drawContext, x, y) { + drawContext.moveTo(drawStartPos.x, drawStartPos.y); + drawContext.strokeStyle = '#000000'; + drawContext.lineTo(x, y); + drawContext.stroke(); + // reset drawing start position to current position. + drawStartPos.set(x, y); + // need to flag the map as needing updating. + material.map.needsUpdate = true; +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + mesh.rotation.x += 0.01; + mesh.rotation.y += 0.01; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_materials_texture_filters.ts b/examples-testing/examples/webgl_materials_texture_filters.ts new file mode 100644 index 000000000..77b254684 --- /dev/null +++ b/examples-testing/examples/webgl_materials_texture_filters.ts @@ -0,0 +1,165 @@ +import * as THREE from 'three'; + +const SCREEN_WIDTH = window.innerWidth; +const SCREEN_HEIGHT = window.innerHeight; + +let container; + +let camera, scene, scene2, renderer; + +let mouseX = 0, + mouseY = 0; + +const windowHalfX = window.innerWidth / 2; +const windowHalfY = window.innerHeight / 2; + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(35, SCREEN_WIDTH / SCREEN_HEIGHT, 1, 5000); + camera.position.z = 1500; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x000000); + scene.fog = new THREE.Fog(0x000000, 1500, 4000); + + scene2 = new THREE.Scene(); + scene2.background = new THREE.Color(0x000000); + scene2.fog = new THREE.Fog(0x000000, 1500, 4000); + + // GROUND + + const imageCanvas = document.createElement('canvas'); + const context = imageCanvas.getContext('2d'); + + imageCanvas.width = imageCanvas.height = 128; + + context.fillStyle = '#444'; + context.fillRect(0, 0, 128, 128); + + context.fillStyle = '#fff'; + context.fillRect(0, 0, 64, 64); + context.fillRect(64, 64, 64, 64); + + const textureCanvas = new THREE.CanvasTexture(imageCanvas); + textureCanvas.colorSpace = THREE.SRGBColorSpace; + textureCanvas.repeat.set(1000, 1000); + textureCanvas.wrapS = THREE.RepeatWrapping; + textureCanvas.wrapT = THREE.RepeatWrapping; + + const textureCanvas2 = textureCanvas.clone(); + textureCanvas2.magFilter = THREE.NearestFilter; + textureCanvas2.minFilter = THREE.NearestFilter; + textureCanvas2.generateMipmaps = false; + + const materialCanvas = new THREE.MeshBasicMaterial({ map: textureCanvas }); + const materialCanvas2 = new THREE.MeshBasicMaterial({ color: 0xffccaa, map: textureCanvas2 }); + + const geometry = new THREE.PlaneGeometry(100, 100); + + const meshCanvas = new THREE.Mesh(geometry, materialCanvas); + meshCanvas.rotation.x = -Math.PI / 2; + meshCanvas.scale.set(1000, 1000, 1000); + + const meshCanvas2 = new THREE.Mesh(geometry, materialCanvas2); + meshCanvas2.rotation.x = -Math.PI / 2; + meshCanvas2.scale.set(1000, 1000, 1000); + + // PAINTING + + const callbackPainting = function () { + const image = texturePainting.image; + + texturePainting2.image = image; + texturePainting2.needsUpdate = true; + + scene.add(meshCanvas); + scene2.add(meshCanvas2); + + const geometry = new THREE.PlaneGeometry(100, 100); + const mesh = new THREE.Mesh(geometry, materialPainting); + const mesh2 = new THREE.Mesh(geometry, materialPainting2); + + addPainting(scene, mesh); + addPainting(scene2, mesh2); + + function addPainting(zscene, zmesh) { + zmesh.scale.x = image.width / 100; + zmesh.scale.y = image.height / 100; + + zscene.add(zmesh); + + const meshFrame = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ color: 0x000000 })); + meshFrame.position.z = -10.0; + meshFrame.scale.x = (1.1 * image.width) / 100; + meshFrame.scale.y = (1.1 * image.height) / 100; + zscene.add(meshFrame); + + const meshShadow = new THREE.Mesh( + geometry, + new THREE.MeshBasicMaterial({ color: 0x000000, opacity: 0.75, transparent: true }), + ); + meshShadow.position.y = (-1.1 * image.height) / 2; + meshShadow.position.z = (-1.1 * image.height) / 2; + meshShadow.rotation.x = -Math.PI / 2; + meshShadow.scale.x = (1.1 * image.width) / 100; + meshShadow.scale.y = (1.1 * image.height) / 100; + zscene.add(meshShadow); + + const floorHeight = (-1.117 * image.height) / 2; + meshCanvas.position.y = meshCanvas2.position.y = floorHeight; + } + }; + + const texturePainting = new THREE.TextureLoader().load( + 'textures/758px-Canestra_di_frutta_(Caravaggio).jpg', + callbackPainting, + ); + const texturePainting2 = new THREE.Texture(); + + const materialPainting = new THREE.MeshBasicMaterial({ color: 0xffffff, map: texturePainting }); + const materialPainting2 = new THREE.MeshBasicMaterial({ color: 0xffccaa, map: texturePainting2 }); + + texturePainting.colorSpace = THREE.SRGBColorSpace; + texturePainting2.colorSpace = THREE.SRGBColorSpace; + texturePainting2.minFilter = texturePainting2.magFilter = THREE.NearestFilter; + texturePainting.minFilter = texturePainting.magFilter = THREE.LinearFilter; + texturePainting.mapping = THREE.UVMapping; + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); + renderer.setAnimationLoop(animate); + renderer.autoClear = false; + + renderer.domElement.style.position = 'relative'; + container.appendChild(renderer.domElement); + + document.addEventListener('mousemove', onDocumentMouseMove); +} + +function onDocumentMouseMove(event) { + mouseX = event.clientX - windowHalfX; + mouseY = event.clientY - windowHalfY; +} + +function animate() { + camera.position.x += (mouseX - camera.position.x) * 0.05; + camera.position.y += (-(mouseY - 200) - camera.position.y) * 0.05; + + camera.lookAt(scene.position); + + renderer.clear(); + renderer.setScissorTest(true); + + renderer.setScissor(0, 0, SCREEN_WIDTH / 2 - 2, SCREEN_HEIGHT); + renderer.render(scene, camera); + + renderer.setScissor(SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 2 - 2, SCREEN_HEIGHT); + renderer.render(scene2, camera); + + renderer.setScissorTest(false); +} diff --git a/examples-testing/examples/webgl_materials_texture_manualmipmap.ts b/examples-testing/examples/webgl_materials_texture_manualmipmap.ts new file mode 100644 index 000000000..24bd4eb9f --- /dev/null +++ b/examples-testing/examples/webgl_materials_texture_manualmipmap.ts @@ -0,0 +1,175 @@ +import * as THREE from 'three'; + +const SCREEN_WIDTH = window.innerWidth; +const SCREEN_HEIGHT = window.innerHeight; + +let container; + +let camera, scene1, scene2, renderer; + +let mouseX = 0, + mouseY = 0; + +const windowHalfX = window.innerWidth / 2; +const windowHalfY = window.innerHeight / 2; + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(35, SCREEN_WIDTH / SCREEN_HEIGHT, 1, 5000); + camera.position.z = 1500; + + scene1 = new THREE.Scene(); + scene1.background = new THREE.Color(0x000000); + scene1.fog = new THREE.Fog(0x000000, 1500, 4000); + + scene2 = new THREE.Scene(); + scene2.background = new THREE.Color(0x000000); + scene2.fog = new THREE.Fog(0x000000, 1500, 4000); + + // GROUND + + function mipmap(size, color) { + const imageCanvas = document.createElement('canvas'); + const context = imageCanvas.getContext('2d'); + + imageCanvas.width = imageCanvas.height = size; + + context.fillStyle = '#444'; + context.fillRect(0, 0, size, size); + + context.fillStyle = color; + context.fillRect(0, 0, size / 2, size / 2); + context.fillRect(size / 2, size / 2, size / 2, size / 2); + return imageCanvas; + } + + const canvas = mipmap(128, '#f00'); + const textureCanvas1 = new THREE.CanvasTexture(canvas); + textureCanvas1.mipmaps[0] = canvas; + textureCanvas1.mipmaps[1] = mipmap(64, '#0f0'); + textureCanvas1.mipmaps[2] = mipmap(32, '#00f'); + textureCanvas1.mipmaps[3] = mipmap(16, '#400'); + textureCanvas1.mipmaps[4] = mipmap(8, '#040'); + textureCanvas1.mipmaps[5] = mipmap(4, '#004'); + textureCanvas1.mipmaps[6] = mipmap(2, '#044'); + textureCanvas1.mipmaps[7] = mipmap(1, '#404'); + textureCanvas1.colorSpace = THREE.SRGBColorSpace; + textureCanvas1.repeat.set(1000, 1000); + textureCanvas1.wrapS = THREE.RepeatWrapping; + textureCanvas1.wrapT = THREE.RepeatWrapping; + + const textureCanvas2 = textureCanvas1.clone(); + textureCanvas2.magFilter = THREE.NearestFilter; + textureCanvas2.minFilter = THREE.NearestMipmapNearestFilter; + + const materialCanvas1 = new THREE.MeshBasicMaterial({ map: textureCanvas1 }); + const materialCanvas2 = new THREE.MeshBasicMaterial({ color: 0xffccaa, map: textureCanvas2 }); + + const geometry = new THREE.PlaneGeometry(100, 100); + + const meshCanvas1 = new THREE.Mesh(geometry, materialCanvas1); + meshCanvas1.rotation.x = -Math.PI / 2; + meshCanvas1.scale.set(1000, 1000, 1000); + + const meshCanvas2 = new THREE.Mesh(geometry, materialCanvas2); + meshCanvas2.rotation.x = -Math.PI / 2; + meshCanvas2.scale.set(1000, 1000, 1000); + + // PAINTING + + const callbackPainting = function () { + const image = texturePainting1.image; + + texturePainting2.image = image; + texturePainting2.needsUpdate = true; + + scene1.add(meshCanvas1); + scene2.add(meshCanvas2); + + const geometry = new THREE.PlaneGeometry(100, 100); + const mesh1 = new THREE.Mesh(geometry, materialPainting1); + const mesh2 = new THREE.Mesh(geometry, materialPainting2); + + addPainting(scene1, mesh1); + addPainting(scene2, mesh2); + + function addPainting(zscene, zmesh) { + zmesh.scale.x = image.width / 100; + zmesh.scale.y = image.height / 100; + + zscene.add(zmesh); + + const meshFrame = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ color: 0x000000 })); + meshFrame.position.z = -10.0; + meshFrame.scale.x = (1.1 * image.width) / 100; + meshFrame.scale.y = (1.1 * image.height) / 100; + zscene.add(meshFrame); + + const meshShadow = new THREE.Mesh( + geometry, + new THREE.MeshBasicMaterial({ color: 0x000000, opacity: 0.75, transparent: true }), + ); + meshShadow.position.y = (-1.1 * image.height) / 2; + meshShadow.position.z = (-1.1 * image.height) / 2; + meshShadow.rotation.x = -Math.PI / 2; + meshShadow.scale.x = (1.1 * image.width) / 100; + meshShadow.scale.y = (1.1 * image.height) / 100; + zscene.add(meshShadow); + + const floorHeight = (-1.117 * image.height) / 2; + meshCanvas1.position.y = meshCanvas2.position.y = floorHeight; + } + }; + + const texturePainting1 = new THREE.TextureLoader().load( + 'textures/758px-Canestra_di_frutta_(Caravaggio).jpg', + callbackPainting, + ); + const texturePainting2 = new THREE.Texture(); + const materialPainting1 = new THREE.MeshBasicMaterial({ color: 0xffffff, map: texturePainting1 }); + const materialPainting2 = new THREE.MeshBasicMaterial({ color: 0xffccaa, map: texturePainting2 }); + + texturePainting1.colorSpace = THREE.SRGBColorSpace; + texturePainting2.colorSpace = THREE.SRGBColorSpace; + texturePainting2.minFilter = texturePainting2.magFilter = THREE.NearestFilter; + texturePainting1.minFilter = texturePainting1.magFilter = THREE.LinearFilter; + texturePainting1.mapping = THREE.UVMapping; + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); + renderer.setAnimationLoop(animate); + renderer.autoClear = false; + + renderer.domElement.style.position = 'relative'; + container.appendChild(renderer.domElement); + + document.addEventListener('mousemove', onDocumentMouseMove); +} + +function onDocumentMouseMove(event) { + mouseX = event.clientX - windowHalfX; + mouseY = event.clientY - windowHalfY; +} + +function animate() { + camera.position.x += (mouseX - camera.position.x) * 0.05; + camera.position.y += (-(mouseY - 200) - camera.position.y) * 0.05; + + camera.lookAt(scene1.position); + + renderer.clear(); + renderer.setScissorTest(true); + + renderer.setScissor(0, 0, SCREEN_WIDTH / 2 - 2, SCREEN_HEIGHT); + renderer.render(scene1, camera); + + renderer.setScissor(SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 2 - 2, SCREEN_HEIGHT); + renderer.render(scene2, camera); + + renderer.setScissorTest(false); +} diff --git a/examples-testing/examples/webgl_materials_texture_partialupdate.ts b/examples-testing/examples/webgl_materials_texture_partialupdate.ts new file mode 100644 index 000000000..cae161939 --- /dev/null +++ b/examples-testing/examples/webgl_materials_texture_partialupdate.ts @@ -0,0 +1,104 @@ +import * as THREE from 'three'; + +let camera, scene, renderer, timer, dataTexture, diffuseMap; + +let last = 0; +const position = new THREE.Vector2(); +const color = new THREE.Color(); + +init(); + +async function init() { + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 10); + camera.position.z = 2; + + scene = new THREE.Scene(); + + timer = new THREE.Timer(); + timer.connect(document); + + const loader = new THREE.TextureLoader(); + diffuseMap = await loader.loadAsync('textures/floors/FloorsCheckerboard_S_Diffuse.jpg'); + diffuseMap.colorSpace = THREE.SRGBColorSpace; + diffuseMap.minFilter = THREE.LinearFilter; + diffuseMap.generateMipmaps = false; + + const geometry = new THREE.PlaneGeometry(2, 2); + const material = new THREE.MeshBasicMaterial({ map: diffuseMap }); + + const mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + // + + const width = 32; + const height = 32; + + const data = new Uint8Array(width * height * 4); + dataTexture = new THREE.DataTexture(data, width, height); + dataTexture.colorSpace = THREE.SRGBColorSpace; + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + const elapsedTime = timer.getElapsed(); + + if (elapsedTime - last > 0.1) { + last = elapsedTime; + + position.x = 32 * THREE.MathUtils.randInt(1, 16) - 32; + position.y = 32 * THREE.MathUtils.randInt(1, 16) - 32; + + // generate new color data + + updateDataTexture(dataTexture); + + // perform copy from src to dest texture to a random position + + renderer.copyTextureToTexture(dataTexture, diffuseMap, null, position); + } + + renderer.render(scene, camera); +} + +function updateDataTexture(texture) { + const size = texture.image.width * texture.image.height; + const data = texture.image.data; + + // generate a random color and update texture data + + color.setHex(Math.random() * 0xffffff); + + const r = Math.floor(color.r * 255); + const g = Math.floor(color.g * 255); + const b = Math.floor(color.b * 255); + + for (let i = 0; i < size; i++) { + const stride = i * 4; + + data[stride] = r; + data[stride + 1] = g; + data[stride + 2] = b; + data[stride + 3] = 1; + } +} diff --git a/examples-testing/examples/webgl_materials_texture_rotation.ts b/examples-testing/examples/webgl_materials_texture_rotation.ts new file mode 100644 index 000000000..eedc80c6f --- /dev/null +++ b/examples-testing/examples/webgl_materials_texture_rotation.ts @@ -0,0 +1,112 @@ +import * as THREE from 'three'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let mesh, renderer, scene, camera; + +let gui; + +const API = { + offsetX: 0, + offsetY: 0, + repeatX: 0.25, + repeatY: 0.25, + rotation: Math.PI / 4, // positive is counterclockwise + centerX: 0.5, + centerY: 0.5, +}; + +init(); + +function init() { + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + document.body.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(10, 15, 25); + scene.add(camera); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.addEventListener('change', render); + controls.minDistance = 20; + controls.maxDistance = 50; + controls.maxPolarAngle = Math.PI / 2; + + const geometry = new THREE.BoxGeometry(10, 10, 10); + + new THREE.TextureLoader().load('textures/uv_grid_opengl.jpg', function (texture) { + texture.wrapS = texture.wrapT = THREE.RepeatWrapping; + texture.anisotropy = renderer.capabilities.getMaxAnisotropy(); + texture.colorSpace = THREE.SRGBColorSpace; + + //texture.matrixAutoUpdate = false; // default is true; set to false to update texture.matrix manually + + const material = new THREE.MeshBasicMaterial({ map: texture }); + + mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + updateUvTransform(); + + initGui(); + + render(); + }); + + window.addEventListener('resize', onWindowResize); +} + +function render() { + renderer.render(scene, camera); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + render(); +} + +function updateUvTransform() { + const texture = mesh.material.map; + + if (texture.matrixAutoUpdate === true) { + texture.offset.set(API.offsetX, API.offsetY); + texture.repeat.set(API.repeatX, API.repeatY); + texture.center.set(API.centerX, API.centerY); + texture.rotation = API.rotation; // rotation is around center + } else { + // setting the matrix uv transform directly + texture.matrix.setUvTransform( + API.offsetX, + API.offsetY, + API.repeatX, + API.repeatY, + API.rotation, + API.centerX, + API.centerY, + ); + } + + render(); +} + +function initGui() { + gui = new GUI(); + + gui.add(API, 'offsetX', 0.0, 1.0).name('offset.x').onChange(updateUvTransform); + gui.add(API, 'offsetY', 0.0, 1.0).name('offset.y').onChange(updateUvTransform); + gui.add(API, 'repeatX', 0.25, 2.0).name('repeat.x').onChange(updateUvTransform); + gui.add(API, 'repeatY', 0.25, 2.0).name('repeat.y').onChange(updateUvTransform); + gui.add(API, 'rotation', -2.0, 2.0).name('rotation').onChange(updateUvTransform); + gui.add(API, 'centerX', 0.0, 1.0).name('center.x').onChange(updateUvTransform); + gui.add(API, 'centerY', 0.0, 1.0).name('center.y').onChange(updateUvTransform); +} diff --git a/examples-testing/examples/webgl_materials_toon.ts b/examples-testing/examples/webgl_materials_toon.ts new file mode 100644 index 000000000..46c6a7e93 --- /dev/null +++ b/examples-testing/examples/webgl_materials_toon.ts @@ -0,0 +1,152 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { OutlineEffect } from 'three/addons/effects/OutlineEffect.js'; +import { FontLoader } from 'three/addons/loaders/FontLoader.js'; +import { TextGeometry } from 'three/addons/geometries/TextGeometry.js'; + +let container, stats; + +let camera, scene, renderer, effect; +let particleLight; + +const loader = new FontLoader(); +loader.load('fonts/gentilis_regular.typeface.json', function (font) { + init(font); +}); + +function init(font) { + container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 2500); + camera.position.set(0.0, 400, 400 * 3.5); + + // + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x444488); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + // Materials + + const cubeWidth = 400; + const numberOfSpheresPerSide = 5; + const sphereRadius = (cubeWidth / numberOfSpheresPerSide) * 0.8 * 0.5; + const stepSize = 1.0 / numberOfSpheresPerSide; + + const geometry = new THREE.SphereGeometry(sphereRadius, 32, 16); + + for (let alpha = 0, alphaIndex = 0; alpha <= 1.0; alpha += stepSize, alphaIndex++) { + const colors = new Uint8Array(alphaIndex + 2); + + for (let c = 0; c <= colors.length; c++) { + colors[c] = (c / colors.length) * 256; + } + + const gradientMap = new THREE.DataTexture(colors, colors.length, 1, THREE.RedFormat); + gradientMap.needsUpdate = true; + + for (let beta = 0; beta <= 1.0; beta += stepSize) { + for (let gamma = 0; gamma <= 1.0; gamma += stepSize) { + // basic monochromatic energy preservation + const diffuseColor = new THREE.Color() + .setHSL(alpha, 0.5, gamma * 0.5 + 0.1) + .multiplyScalar(1 - beta * 0.2); + + const material = new THREE.MeshToonMaterial({ + color: diffuseColor, + gradientMap: gradientMap, + }); + + const mesh = new THREE.Mesh(geometry, material); + + mesh.position.x = alpha * 400 - 200; + mesh.position.y = beta * 400 - 200; + mesh.position.z = gamma * 400 - 200; + + scene.add(mesh); + } + } + } + + function addLabel(name, location) { + const textGeo = new TextGeometry(name, { + font: font, + + size: 20, + depth: 1, + curveSegments: 1, + }); + + const textMaterial = new THREE.MeshBasicMaterial(); + const textMesh = new THREE.Mesh(textGeo, textMaterial); + textMesh.position.copy(location); + scene.add(textMesh); + } + + addLabel('-gradientMap', new THREE.Vector3(-350, 0, 0)); + addLabel('+gradientMap', new THREE.Vector3(350, 0, 0)); + + addLabel('-diffuse', new THREE.Vector3(0, 0, -300)); + addLabel('+diffuse', new THREE.Vector3(0, 0, 300)); + + particleLight = new THREE.Mesh(new THREE.SphereGeometry(4, 8, 8), new THREE.MeshBasicMaterial({ color: 0xffffff })); + scene.add(particleLight); + + // Lights + + scene.add(new THREE.AmbientLight(0xc1c1c1, 3)); + + const pointLight = new THREE.PointLight(0xffffff, 2, 800, 0); + particleLight.add(pointLight); + + // + + effect = new OutlineEffect(renderer); + + // + + stats = new Stats(); + container.appendChild(stats.dom); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 200; + controls.maxDistance = 2000; + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + stats.begin(); + render(); + stats.end(); +} + +function render() { + const timer = Date.now() * 0.00025; + + particleLight.position.x = Math.sin(timer * 7) * 300; + particleLight.position.y = Math.cos(timer * 5) * 400; + particleLight.position.z = Math.cos(timer * 3) * 300; + + effect.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_materials_video.ts b/examples-testing/examples/webgl_materials_video.ts new file mode 100644 index 000000000..4f0d26a18 --- /dev/null +++ b/examples-testing/examples/webgl_materials_video.ts @@ -0,0 +1,208 @@ +import * as THREE from 'three'; + +import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; +import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; +import { BloomPass } from 'three/addons/postprocessing/BloomPass.js'; +import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; + +let container; + +let camera, scene, renderer; + +let video, texture, material, mesh; + +let composer; + +let mouseX = 0; +let mouseY = 0; + +let windowHalfX = window.innerWidth / 2; +let windowHalfY = window.innerHeight / 2; + +let cube_count; + +const meshes = [], + materials = [], + xgrid = 20, + ygrid = 10; + +const startButton = document.getElementById('startButton'); +startButton.addEventListener('click', function () { + init(); +}); + +function init() { + const overlay = document.getElementById('overlay'); + overlay.remove(); + + container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 10000); + camera.position.z = 500; + + scene = new THREE.Scene(); + + const light = new THREE.DirectionalLight(0xffffff, 3); + light.position.set(0.5, 1, 1).normalize(); + scene.add(light); + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + video = document.getElementById('video'); + video.play(); + video.addEventListener('play', function () { + this.currentTime = 3; + }); + + texture = new THREE.VideoTexture(video); + texture.colorSpace = THREE.SRGBColorSpace; + + // + + let i, j, ox, oy, geometry; + + const ux = 1 / xgrid; + const uy = 1 / ygrid; + + const xsize = 480 / xgrid; + const ysize = 204 / ygrid; + + const parameters = { color: 0xffffff, map: texture }; + + cube_count = 0; + + for (i = 0; i < xgrid; i++) { + for (j = 0; j < ygrid; j++) { + ox = i; + oy = j; + + geometry = new THREE.BoxGeometry(xsize, ysize, xsize); + + change_uvs(geometry, ux, uy, ox, oy); + + materials[cube_count] = new THREE.MeshLambertMaterial(parameters); + + material = materials[cube_count]; + + material.hue = i / xgrid; + material.saturation = 1 - j / ygrid; + + material.color.setHSL(material.hue, material.saturation, 0.5); + + mesh = new THREE.Mesh(geometry, material); + + mesh.position.x = (i - xgrid / 2) * xsize; + mesh.position.y = (j - ygrid / 2) * ysize; + mesh.position.z = 0; + + mesh.scale.x = mesh.scale.y = mesh.scale.z = 1; + + scene.add(mesh); + + mesh.dx = 0.001 * (0.5 - Math.random()); + mesh.dy = 0.001 * (0.5 - Math.random()); + + meshes[cube_count] = mesh; + + cube_count += 1; + } + } + + renderer.autoClear = false; + + document.addEventListener('mousemove', onDocumentMouseMove); + + // postprocessing + + const renderPass = new RenderPass(scene, camera); + const bloomPass = new BloomPass(1.3); + const outputPass = new OutputPass(); + + composer = new EffectComposer(renderer); + + composer.addPass(renderPass); + composer.addPass(bloomPass); + composer.addPass(outputPass); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + windowHalfX = window.innerWidth / 2; + windowHalfY = window.innerHeight / 2; + + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + composer.setSize(window.innerWidth, window.innerHeight); +} + +function change_uvs(geometry, unitx, unity, offsetx, offsety) { + const uvs = geometry.attributes.uv.array; + + for (let i = 0; i < uvs.length; i += 2) { + uvs[i] = (uvs[i] + offsetx) * unitx; + uvs[i + 1] = (uvs[i + 1] + offsety) * unity; + } +} + +function onDocumentMouseMove(event) { + mouseX = event.clientX - windowHalfX; + mouseY = (event.clientY - windowHalfY) * 0.3; +} + +// + +let h, + counter = 1; + +function animate() { + const time = Date.now() * 0.00005; + + camera.position.x += (mouseX - camera.position.x) * 0.05; + camera.position.y += (-mouseY - camera.position.y) * 0.05; + + camera.lookAt(scene.position); + + for (let i = 0; i < cube_count; i++) { + material = materials[i]; + + h = ((360 * (material.hue + time)) % 360) / 360; + material.color.setHSL(h, material.saturation, 0.5); + } + + if (counter % 1000 > 200) { + for (let i = 0; i < cube_count; i++) { + mesh = meshes[i]; + + mesh.rotation.x += 10 * mesh.dx; + mesh.rotation.y += 10 * mesh.dy; + + mesh.position.x -= 150 * mesh.dx; + mesh.position.y += 150 * mesh.dy; + mesh.position.z += 300 * mesh.dx; + } + } + + if (counter % 1000 === 0) { + for (let i = 0; i < cube_count; i++) { + mesh = meshes[i]; + + mesh.dx *= -1; + mesh.dy *= -1; + } + } + + counter++; + + renderer.clear(); + composer.render(); +} diff --git a/examples-testing/examples/webgl_materials_video_webcam.ts b/examples-testing/examples/webgl_materials_video_webcam.ts new file mode 100644 index 000000000..cf6f8d50c --- /dev/null +++ b/examples-testing/examples/webgl_materials_video_webcam.ts @@ -0,0 +1,79 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let camera, scene, renderer, video; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.z = 0.01; + + scene = new THREE.Scene(); + + video = document.getElementById('video'); + + const texture = new THREE.VideoTexture(video); + texture.colorSpace = THREE.SRGBColorSpace; + + const geometry = new THREE.PlaneGeometry(16, 9); + geometry.scale(0.5, 0.5, 0.5); + const material = new THREE.MeshBasicMaterial({ map: texture }); + + const count = 128; + const radius = 32; + + for (let i = 1, l = count; i <= l; i++) { + const phi = Math.acos(-1 + (2 * i) / l); + const theta = Math.sqrt(l * Math.PI) * phi; + + const mesh = new THREE.Mesh(geometry, material); + mesh.position.setFromSphericalCoords(radius, phi, theta); + mesh.lookAt(camera.position); + scene.add(mesh); + } + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.enableZoom = false; + controls.enablePan = false; + + window.addEventListener('resize', onWindowResize); + + // + + if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { + const constraints = { video: { width: 1280, height: 720, facingMode: 'user' } }; + + navigator.mediaDevices + .getUserMedia(constraints) + .then(function (stream) { + // apply the stream to the video element used in the texture + + video.srcObject = stream; + video.play(); + }) + .catch(function (error) { + console.error('Unable to access the camera/webcam.', error); + }); + } else { + console.error('MediaDevices interface not available.'); + } +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_materials_wireframe.ts b/examples-testing/examples/webgl_materials_wireframe.ts new file mode 100644 index 000000000..8adbd71d6 --- /dev/null +++ b/examples-testing/examples/webgl_materials_wireframe.ts @@ -0,0 +1,107 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +const API = { + thickness: 1, +}; + +let renderer, scene, camera, mesh2; + +init(); + +function init() { + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + document.body.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 500); + camera.position.z = 200; + + const controls = new OrbitControls(camera, renderer.domElement); + controls.addEventListener('change', render); // use if there is no animation loop + controls.enablePan = false; + controls.enableZoom = false; + + new THREE.BufferGeometryLoader().load('models/json/WaltHeadLo_buffergeometry.json', function (geometry) { + geometry.deleteAttribute('normal'); + geometry.deleteAttribute('uv'); + + setupAttributes(geometry); + + // left + + const material1 = new THREE.MeshBasicMaterial({ + color: 0xe0e0ff, + wireframe: true, + }); + + const mesh1 = new THREE.Mesh(geometry, material1); + mesh1.position.set(-40, 0, 0); + + scene.add(mesh1); + + // right + + const material2 = new THREE.ShaderMaterial({ + uniforms: { thickness: { value: API.thickness } }, + vertexShader: document.getElementById('vertexShader').textContent, + fragmentShader: document.getElementById('fragmentShader').textContent, + side: THREE.DoubleSide, + alphaToCoverage: true, // only works when WebGLRenderer's "antialias" is set to "true" + }); + + mesh2 = new THREE.Mesh(geometry, material2); + mesh2.position.set(40, 0, 0); + + scene.add(mesh2); + + // + + render(); + }); + + // + + const gui = new GUI(); + + gui.add(API, 'thickness', 0, 4).onChange(function () { + mesh2.material.uniforms.thickness.value = API.thickness; + render(); + }); + + gui.open(); + + // + + window.addEventListener('resize', onWindowResize); +} + +function setupAttributes(geometry) { + const vectors = [new THREE.Vector3(1, 0, 0), new THREE.Vector3(0, 1, 0), new THREE.Vector3(0, 0, 1)]; + + const position = geometry.attributes.position; + const centers = new Float32Array(position.count * 3); + + for (let i = 0, l = position.count; i < l; i++) { + vectors[i % 3].toArray(centers, i * 3); + } + + geometry.setAttribute('center', new THREE.BufferAttribute(centers, 3)); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_math_obb.ts b/examples-testing/examples/webgl_math_obb.ts new file mode 100644 index 000000000..71cb5a916 --- /dev/null +++ b/examples-testing/examples/webgl_math_obb.ts @@ -0,0 +1,192 @@ +import * as THREE from 'three'; + +import { OBB } from 'three/addons/math/OBB.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import Stats from 'three/addons/libs/stats.module.js'; + +let camera, scene, renderer, timer, controls, stats, raycaster, hitbox; + +const objects = [], + mouse = new THREE.Vector2(); + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(0, 0, 75); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xffffff); + + timer = new THREE.Timer(); + timer.connect(document); + + raycaster = new THREE.Raycaster(); + + const hemiLight = new THREE.HemisphereLight(0xffffff, 0x222222, 4); + hemiLight.position.set(1, 1, 1); + scene.add(hemiLight); + + const size = new THREE.Vector3(10, 5, 6); + const geometry = new THREE.BoxGeometry(size.x, size.y, size.z); + + // setup OBB on geometry level (doing this manually for now) + + geometry.userData.obb = new OBB(); + geometry.userData.obb.halfSize.copy(size).multiplyScalar(0.5); + + for (let i = 0; i < 100; i++) { + const object = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({ color: 0x00ff00 })); + object.matrixAutoUpdate = false; + + object.position.x = Math.random() * 80 - 40; + object.position.y = Math.random() * 80 - 40; + object.position.z = Math.random() * 80 - 40; + + object.rotation.x = Math.random() * 2 * Math.PI; + object.rotation.y = Math.random() * 2 * Math.PI; + object.rotation.z = Math.random() * 2 * Math.PI; + + object.scale.x = Math.random() + 0.5; + object.scale.y = Math.random() + 0.5; + object.scale.z = Math.random() + 0.5; + + scene.add(object); + + // bounding volume on object level (this will reflect the current world transform) + + object.userData.obb = new OBB(); + + objects.push(object); + } + + // + + hitbox = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ color: 0x000000, wireframe: true })); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + + // + + stats = new Stats(); + document.body.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); + + document.addEventListener('click', onClick); +} + +function onClick(event) { + event.preventDefault(); + + mouse.x = (event.clientX / window.innerWidth) * 2 - 1; + mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; + + raycaster.setFromCamera(mouse, camera); + + const intersectionPoint = new THREE.Vector3(); + const intersections = []; + + for (let i = 0, il = objects.length; i < il; i++) { + const object = objects[i]; + const obb = object.userData.obb; + + const ray = raycaster.ray; + + if (obb.intersectRay(ray, intersectionPoint) !== null) { + const distance = ray.origin.distanceTo(intersectionPoint); + intersections.push({ distance: distance, object: object }); + } + } + + if (intersections.length > 0) { + // determine closest intersection and highlight the respective 3D object + + intersections.sort(sortIntersections); + + intersections[0].object.add(hitbox); + } else { + const parent = hitbox.parent; + + if (parent) parent.remove(hitbox); + } +} + +function sortIntersections(a, b) { + return a.distance - b.distance; +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + timer.update(); + + controls.update(); + + // transform cubes + + const delta = timer.getDelta(); + + for (let i = 0, il = objects.length; i < il; i++) { + const object = objects[i]; + + object.rotation.x += delta * Math.PI * 0.2; + object.rotation.y += delta * Math.PI * 0.1; + + object.updateMatrix(); + object.updateMatrixWorld(); + + // update OBB + + object.userData.obb.copy(object.geometry.userData.obb); + object.userData.obb.applyMatrix4(object.matrixWorld); + + // reset + + object.material.color.setHex(0x00ff00); + } + + // collision detection + + for (let i = 0, il = objects.length; i < il; i++) { + const object = objects[i]; + const obb = object.userData.obb; + + for (let j = i + 1, jl = objects.length; j < jl; j++) { + const objectToTest = objects[j]; + const obbToTest = objectToTest.userData.obb; + + // now perform intersection test + + if (obb.intersectsOBB(obbToTest) === true) { + object.material.color.setHex(0xff0000); + objectToTest.material.color.setHex(0xff0000); + } + } + } + + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/webgl_math_orientation_transform.ts b/examples-testing/examples/webgl_math_orientation_transform.ts new file mode 100644 index 000000000..1d66997c4 --- /dev/null +++ b/examples-testing/examples/webgl_math_orientation_transform.ts @@ -0,0 +1,120 @@ +import * as THREE from 'three'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let camera, scene, renderer, mesh, target; + +const spherical = new THREE.Spherical(); +const rotationMatrix = new THREE.Matrix4(); +const targetQuaternion = new THREE.Quaternion(); +const timer = new THREE.Timer(); +timer.connect(document); +const speed = Math.PI / 2; + +const params = { + useLookAt: false, +}; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 10); + camera.position.z = 5; + + scene = new THREE.Scene(); + + const geometry = new THREE.ConeGeometry(0.1, 0.5, 8); + geometry.rotateX(Math.PI * 0.5); + const material = new THREE.MeshNormalMaterial(); + + mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + // + + const targetGeometry = new THREE.SphereGeometry(0.05); + const targetMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 }); + target = new THREE.Mesh(targetGeometry, targetMaterial); + scene.add(target); + + // + + const sphereGeometry = new THREE.SphereGeometry(2, 32, 32); + const sphereMaterial = new THREE.MeshBasicMaterial({ + color: 0xcccccc, + wireframe: true, + transparent: true, + opacity: 0.3, + }); + const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial); + scene.add(sphere); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // + + window.addEventListener('resize', onWindowResize); + + // + + const gui = new GUI(); + + gui.add(params, 'useLookAt'); + gui.open(); + + // + + generateTarget(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + const delta = timer.getDelta(); + + if (mesh.quaternion.equals(targetQuaternion) === false) { + if (params.useLookAt === true) { + // using lookAt() will make the mesh instantly look at the target + + mesh.lookAt(target.position); + } else { + // using rotateTowards() will gradually rotate the mesh towards the target + // the "speed" variable represents the rotation speed in radians per seconds + + const step = speed * delta; + mesh.quaternion.rotateTowards(targetQuaternion, step); + } + } + + renderer.render(scene, camera); +} + +function generateTarget() { + // generate a random point on a sphere + + spherical.theta = Math.random() * Math.PI * 2; + spherical.phi = Math.acos(2 * Math.random() - 1); + spherical.radius = 2; + + target.position.setFromSpherical(spherical); + + // compute target rotation + + rotationMatrix.lookAt(target.position, mesh.position, mesh.up); + targetQuaternion.setFromRotationMatrix(rotationMatrix); + + setTimeout(generateTarget, 2000); +} diff --git a/examples-testing/examples/webgl_mesh_batch.ts b/examples-testing/examples/webgl_mesh_batch.ts new file mode 100644 index 000000000..e238e50a3 --- /dev/null +++ b/examples-testing/examples/webgl_mesh_batch.ts @@ -0,0 +1,347 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { radixSort } from 'three/addons/utils/SortUtils.js'; + +let stats, gui, guiStatsEl; +let camera, controls, scene, renderer; +let geometries, mesh; +const ids = []; +const matrix = new THREE.Matrix4(); + +// + +const position = new THREE.Vector3(); +const rotation = new THREE.Euler(); +const quaternion = new THREE.Quaternion(); +const scale = new THREE.Vector3(); + +// + +const MAX_GEOMETRY_COUNT = 20000; + +const Method = { + BATCHED: 'BATCHED', + NAIVE: 'NAIVE', +}; + +const api = { + method: Method.BATCHED, + count: 256, + dynamic: 16, + + transparent: true, + sortObjects: true, + perObjectFrustumCulled: true, + useCustomSort: true, +}; + +init(); +initGeometries(); +initMesh(); + +// + +function randomizeMatrix(matrix) { + position.x = Math.random() * 40 - 20; + position.y = Math.random() * 40 - 20; + position.z = Math.random() * 40 - 20; + + rotation.x = Math.random() * 2 * Math.PI; + rotation.y = Math.random() * 2 * Math.PI; + rotation.z = Math.random() * 2 * Math.PI; + + quaternion.setFromEuler(rotation); + + scale.x = scale.y = scale.z = 0.5 + Math.random() * 0.5; + + return matrix.compose(position, quaternion, scale); +} + +function randomizeRotationSpeed(rotation) { + rotation.x = Math.random() * 0.01; + rotation.y = Math.random() * 0.01; + rotation.z = Math.random() * 0.01; + return rotation; +} + +function randomizeColor(color) { + return color.setHSL(Math.random() * 0.5, 0.6, 0.5); +} + +function randomizeAlpha() { + // make ~20% of all objects transparent + return Math.random() > 0.8 ? 0.5 : 1.0; +} + +function initGeometries() { + const cone = new THREE.ConeGeometry(1.0, 2.0); + const box = new THREE.BoxGeometry(2.0, 2.0, 2.0); + const sphere = new THREE.SphereGeometry(1.0, 16, 8); + + geometries = [cone, box, sphere]; + + for (const geometry of geometries) { + // add vertex colors for testing + const count = geometry.getAttribute('position').count; + const attribute = new THREE.BufferAttribute(new Float32Array(count * 3), 3); + geometry.setAttribute('color', attribute); + + for (let i = 0, l = attribute.array.length; i < l; ++i) { + attribute.array[i] = 1.0; + } + } +} + +function createMaterial() { + return new THREE.MeshPhongMaterial({ + vertexColors: true, + transparent: api.transparent, + depthWrite: !api.transparent, + }); +} + +function cleanup() { + if (mesh) { + mesh.traverse(node => { + if (node instanceof THREE.Mesh) { + node.material.dispose(); + } + }); + + mesh.parent.remove(mesh); + + if (mesh.dispose) { + mesh.dispose(); + } + } +} + +function initMesh() { + cleanup(); + + if (api.method === Method.BATCHED) { + initBatchedMesh(); + } else { + initRegularMesh(); + } +} + +function initRegularMesh() { + mesh = new THREE.Group(); + + for (let i = 0; i < api.count; i++) { + const material = createMaterial(); + randomizeColor(material.color); + material.opacity = randomizeAlpha(); + + const child = new THREE.Mesh(geometries[i % geometries.length], material); + randomizeMatrix(child.matrix); + child.matrix.decompose(child.position, child.quaternion, child.scale); + child.userData.rotationSpeed = randomizeRotationSpeed(new THREE.Euler()); + mesh.add(child); + } + + scene.add(mesh); +} + +function initBatchedMesh() { + const geometryCount = api.count; + const vertexCount = geometries.length * 512; + const indexCount = geometries.length * 1024; + + const euler = new THREE.Euler(); + const matrix = new THREE.Matrix4(); + const color = new THREE.Color(); + const colorWithAlpha = new THREE.Vector4(); + mesh = new THREE.BatchedMesh(geometryCount, vertexCount, indexCount, createMaterial()); + mesh.userData.rotationSpeeds = []; + + // disable full-object frustum culling since all of the objects can be dynamic. + mesh.frustumCulled = false; + + ids.length = 0; + + const geometryIds = [ + mesh.addGeometry(geometries[0]), + mesh.addGeometry(geometries[1]), + mesh.addGeometry(geometries[2]), + ]; + + for (let i = 0; i < api.count; i++) { + randomizeColor(color); + colorWithAlpha.set(color.r, color.g, color.b, randomizeAlpha()); + + const id = mesh.addInstance(geometryIds[i % geometryIds.length]); + mesh.setMatrixAt(id, randomizeMatrix(matrix)); + mesh.setColorAt(id, colorWithAlpha); + + const rotationMatrix = new THREE.Matrix4(); + rotationMatrix.makeRotationFromEuler(randomizeRotationSpeed(euler)); + mesh.userData.rotationSpeeds.push(rotationMatrix); + + ids.push(id); + } + + scene.add(mesh); +} + +function init() { + const width = window.innerWidth; + const height = window.innerHeight; + + // camera + + camera = new THREE.PerspectiveCamera(70, width / height, 1, 100); + camera.position.z = 30; + + // renderer + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(width, height); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // scene + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xffffff); + + // controls + + controls = new OrbitControls(camera, renderer.domElement); + controls.autoRotate = true; + controls.autoRotateSpeed = 1.0; + + // light + + const ambientLight = new THREE.AmbientLight(0xffffff, 2); + const directionalLight = new THREE.DirectionalLight(0xffffff, 2); + directionalLight.position.set(1, 1, 1); + scene.add(directionalLight, ambientLight); + + // stats + + stats = new Stats(); + document.body.appendChild(stats.dom); + + // gui + + gui = new GUI(); + gui.add(api, 'count', 1, MAX_GEOMETRY_COUNT).step(1).onChange(initMesh); + gui.add(api, 'dynamic', 0, MAX_GEOMETRY_COUNT).step(1); + gui.add(api, 'method', Method).onChange(initMesh); + + gui.add(api, 'transparent').onChange(v => + mesh.traverse(node => { + if (node instanceof THREE.Mesh) { + const material = node.material; + material.transparent = v; + material.depthWrite = !v; + material.needsUpdate = true; + } + }), + ); + + gui.add(api, 'sortObjects'); + gui.add(api, 'perObjectFrustumCulled'); + gui.add(api, 'useCustomSort'); + + guiStatsEl = document.createElement('li'); + guiStatsEl.classList.add('gui-stats'); + + // listeners + + window.addEventListener('resize', onWindowResize); +} + +// + +function sortFunction(list) { + // initialize options + this._options = this._options || { + get: el => el.z, + aux: new Array(this.maxInstanceCount), + }; + + const options = this._options; + options.reversed = this.material.transparent; + + let minZ = Infinity; + let maxZ = -Infinity; + for (let i = 0, l = list.length; i < l; i++) { + const z = list[i].z; + if (z > maxZ) maxZ = z; + if (z < minZ) minZ = z; + } + + // convert depth to unsigned 32 bit range + const depthDelta = maxZ - minZ; + const factor = (2 ** 32 - 1) / depthDelta; // UINT32_MAX / z range + for (let i = 0, l = list.length; i < l; i++) { + list[i].z -= minZ; + list[i].z *= factor; + } + + // perform a fast-sort using the hybrid radix sort function + radixSort(list, options); +} + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); +} + +function animate() { + animateMeshes(); + + controls.update(); + stats.update(); + + render(); +} + +function animateMeshes() { + const loopNum = Math.min(api.count, api.dynamic); + + if (api.method === Method.BATCHED) { + for (let i = 0; i < loopNum; i++) { + const rotationMatrix = mesh.userData.rotationSpeeds[i]; + const id = ids[i]; + + mesh.getMatrixAt(id, matrix); + matrix.multiply(rotationMatrix); + mesh.setMatrixAt(id, matrix); + } + } else { + for (let i = 0; i < loopNum; i++) { + const child = mesh.children[i]; + const rotationSpeed = child.userData.rotationSpeed; + + child.rotation.set( + child.rotation.x + rotationSpeed.x, + child.rotation.y + rotationSpeed.y, + child.rotation.z + rotationSpeed.z, + ); + } + } +} + +function render() { + if (mesh.isBatchedMesh) { + mesh.sortObjects = api.sortObjects; + mesh.perObjectFrustumCulled = api.perObjectFrustumCulled; + mesh.setCustomSort(api.useCustomSort ? sortFunction : null); + } + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_mirror.ts b/examples-testing/examples/webgl_mirror.ts new file mode 100644 index 000000000..4f1f17f7d --- /dev/null +++ b/examples-testing/examples/webgl_mirror.ts @@ -0,0 +1,193 @@ +import * as THREE from 'three'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { Reflector } from 'three/addons/objects/Reflector.js'; + +let camera, scene, renderer; + +let cameraControls; + +let sphereGroup, smallSphere; + +let groundMirror, verticalMirror; + +let resolutionScale = 1; // render target scale factor in [ 0, 1 ] + +const size = new THREE.Vector2(); + +init(); + +function init() { + const container = document.getElementById('container'); + + // renderer + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + // scene + scene = new THREE.Scene(); + + // camera + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 500); + camera.position.set(0, 75, 160); + + cameraControls = new OrbitControls(camera, renderer.domElement); + cameraControls.target.set(0, 40, 0); + cameraControls.maxDistance = 400; + cameraControls.minDistance = 10; + cameraControls.update(); + + // + + const planeGeo = new THREE.PlaneGeometry(100.1, 100.1); + + // reflectors/mirrors + + let geometry, material; + + renderer.getDrawingBufferSize(size); + size.multiplyScalar(resolutionScale).round(); + + geometry = new THREE.CircleGeometry(40, 64); + groundMirror = new Reflector(geometry, { + clipBias: 0.003, + textureWidth: size.width, + textureHeight: size.height, + color: 0xb5b5b5, + }); + groundMirror.position.y = 0.5; + groundMirror.rotateX(-Math.PI / 2); + scene.add(groundMirror); + + geometry = new THREE.PlaneGeometry(100, 100); + verticalMirror = new Reflector(geometry, { + clipBias: 0.003, + textureWidth: size.width, + textureHeight: size.height, + color: 0xc1cbcb, + }); + verticalMirror.position.y = 50; + verticalMirror.position.z = -50; + scene.add(verticalMirror); + + sphereGroup = new THREE.Object3D(); + scene.add(sphereGroup); + + geometry = new THREE.CylinderGeometry(0.1, 15 * Math.cos((Math.PI / 180) * 30), 0.1, 24, 1); + material = new THREE.MeshPhongMaterial({ color: 0xffffff, emissive: 0x8d8d8d }); + const sphereCap = new THREE.Mesh(geometry, material); + sphereCap.position.y = -15 * Math.sin((Math.PI / 180) * 30) - 0.05; + sphereCap.rotateX(-Math.PI); + + geometry = new THREE.SphereGeometry(15, 24, 24, Math.PI / 2, Math.PI * 2, 0, (Math.PI / 180) * 120); + const halfSphere = new THREE.Mesh(geometry, material); + halfSphere.add(sphereCap); + halfSphere.rotateX((-Math.PI / 180) * 135); + halfSphere.rotateZ((-Math.PI / 180) * 20); + halfSphere.position.y = 7.5 + 15 * Math.sin((Math.PI / 180) * 30); + + sphereGroup.add(halfSphere); + + geometry = new THREE.IcosahedronGeometry(5, 0); + material = new THREE.MeshPhongMaterial({ color: 0xffffff, emissive: 0x7b7b7b, flatShading: true }); + smallSphere = new THREE.Mesh(geometry, material); + scene.add(smallSphere); + + // walls + const planeTop = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xffffff })); + planeTop.position.y = 100; + planeTop.rotateX(Math.PI / 2); + scene.add(planeTop); + + const planeBottom = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xffffff })); + planeBottom.rotateX(-Math.PI / 2); + scene.add(planeBottom); + + const planeFront = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0x7f7fff })); + planeFront.position.z = 50; + planeFront.position.y = 50; + planeFront.rotateY(Math.PI); + scene.add(planeFront); + + const planeRight = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0x00ff00 })); + planeRight.position.x = 50; + planeRight.position.y = 50; + planeRight.rotateY(-Math.PI / 2); + scene.add(planeRight); + + const planeLeft = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xff0000 })); + planeLeft.position.x = -50; + planeLeft.position.y = 50; + planeLeft.rotateY(Math.PI / 2); + scene.add(planeLeft); + + // lights + const mainLight = new THREE.PointLight(0xe7e7e7, 2.5, 250, 0); + mainLight.position.y = 60; + scene.add(mainLight); + + const greenLight = new THREE.PointLight(0x00ff00, 0.5, 1000, 0); + greenLight.position.set(550, 50, 0); + scene.add(greenLight); + + const redLight = new THREE.PointLight(0xff0000, 0.5, 1000, 0); + redLight.position.set(-550, 50, 0); + scene.add(redLight); + + const blueLight = new THREE.PointLight(0xbbbbfe, 0.5, 1000, 0); + blueLight.position.set(0, 50, 550); + scene.add(blueLight); + + // GUI + + const params = { + resolution: resolutionScale, + }; + + const gui = new GUI(); + + const folder = gui.addFolder('Mirrors'); + + folder.add(params, 'resolution', 0.2, 1, 0.1).onChange(function (val) { + resolutionScale = val; + onWindowResize(); + }); + + folder.open(); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + renderer.getDrawingBufferSize(size); + size.multiplyScalar(resolutionScale).round(); + + groundMirror.getRenderTarget().setSize(size.width, size.height); + verticalMirror.getRenderTarget().setSize(size.width, size.height); +} + +function animate() { + const timer = Date.now() * 0.01; + + sphereGroup.rotation.y -= 0.002; + + smallSphere.position.set( + Math.cos(timer * 0.1) * 30, + Math.abs(Math.cos(timer * 0.2)) * 20 + 5, + Math.sin(timer * 0.1) * 30, + ); + smallSphere.rotation.y = Math.PI / 2 - timer * 0.1; + smallSphere.rotation.z = timer * 0.8; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_modifier_edgesplit.ts b/examples-testing/examples/webgl_modifier_edgesplit.ts new file mode 100644 index 000000000..4725eff62 --- /dev/null +++ b/examples-testing/examples/webgl_modifier_edgesplit.ts @@ -0,0 +1,136 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { OBJLoader } from 'three/addons/loaders/OBJLoader.js'; +import { EdgeSplitModifier } from 'three/addons/modifiers/EdgeSplitModifier.js'; +import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let renderer, scene, camera; +let modifier, mesh, baseGeometry; +let map; + +const params = { + smoothShading: true, + edgeSplit: true, + cutOffAngle: 20, + showMap: false, + tryKeepNormals: true, +}; + +init(); + +function init() { + const info = document.createElement('div'); + info.style.position = 'absolute'; + info.style.top = '10px'; + info.style.width = '100%'; + info.style.textAlign = 'center'; + info.innerHTML = 'three.js - Edge Split modifier'; + document.body.appendChild(info); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + document.body.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.addEventListener('change', render); // use if there is no animation loop + controls.enableDamping = true; + controls.dampingFactor = 0.25; + controls.rotateSpeed = 0.35; + controls.minZoom = 1; + camera.position.set(0, 0, 4); + + scene.add(new THREE.HemisphereLight(0xffffff, 0x444444, 3)); + + new OBJLoader().load('./models/obj/cerberus/Cerberus.obj', function (group) { + const cerberus = group.children[0]; + const modelGeometry = cerberus.geometry; + + modifier = new EdgeSplitModifier(); + baseGeometry = BufferGeometryUtils.mergeVertices(modelGeometry); + + mesh = new THREE.Mesh(getGeometry(), new THREE.MeshStandardMaterial()); + mesh.material.flatShading = !params.smoothShading; + mesh.rotateY(-Math.PI / 2); + mesh.scale.set(3.5, 3.5, 3.5); + mesh.translateZ(1.5); + scene.add(mesh); + + if (map !== undefined && params.showMap) { + mesh.material.map = map; + mesh.material.needsUpdate = true; + } + + render(); + }); + + window.addEventListener('resize', onWindowResize); + + new THREE.TextureLoader().load('./models/obj/cerberus/Cerberus_A.jpg', function (texture) { + map = texture; + map.colorSpace = THREE.SRGBColorSpace; + + if (mesh !== undefined && params.showMap) { + mesh.material.map = map; + mesh.material.needsUpdate = true; + } + }); + + const gui = new GUI({ title: 'Edge split modifier parameters' }); + + gui.add(params, 'showMap').onFinishChange(updateMesh); + gui.add(params, 'smoothShading').onFinishChange(updateMesh); + gui.add(params, 'edgeSplit').onFinishChange(updateMesh); + gui.add(params, 'cutOffAngle').min(0).max(180).onFinishChange(updateMesh); + gui.add(params, 'tryKeepNormals').onFinishChange(updateMesh); +} + +function onWindowResize() { + renderer.setSize(window.innerWidth, window.innerHeight); + + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + render(); +} + +function getGeometry() { + let geometry; + + if (params.edgeSplit) { + geometry = modifier.modify(baseGeometry, (params.cutOffAngle * Math.PI) / 180, params.tryKeepNormals); + } else { + geometry = baseGeometry; + } + + return geometry; +} + +function updateMesh() { + if (mesh !== undefined) { + mesh.geometry = getGeometry(); + + let needsUpdate = mesh.material.flatShading === params.smoothShading; + mesh.material.flatShading = params.smoothShading === false; + + if (map !== undefined) { + needsUpdate = needsUpdate || mesh.material.map !== (params.showMap ? map : null); + mesh.material.map = params.showMap ? map : null; + } + + mesh.material.needsUpdate = needsUpdate; + + render(); + } +} + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_modifier_simplifier.ts b/examples-testing/examples/webgl_modifier_simplifier.ts new file mode 100644 index 000000000..e6ea453b3 --- /dev/null +++ b/examples-testing/examples/webgl_modifier_simplifier.ts @@ -0,0 +1,77 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { SimplifyModifier } from 'three/addons/modifiers/SimplifyModifier.js'; + +let renderer, scene, camera; + +init(); + +function init() { + const info = document.createElement('div'); + info.style.position = 'absolute'; + info.style.top = '10px'; + info.style.width = '100%'; + info.style.textAlign = 'center'; + info.innerHTML = + 'three.js - Vertex Reduction using SimplifyModifier'; + document.body.appendChild(info); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + document.body.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.z = 15; + + const controls = new OrbitControls(camera, renderer.domElement); + controls.addEventListener('change', render); // use if there is no animation loop + controls.enablePan = false; + controls.enableZoom = false; + + scene.add(new THREE.AmbientLight(0xffffff, 0.6)); + + const light = new THREE.PointLight(0xffffff, 400); + camera.add(light); + scene.add(camera); + + new GLTFLoader().load('models/gltf/LeePerrySmith/LeePerrySmith.glb', function (gltf) { + const mesh = gltf.scene.children[0]; + mesh.position.x = -3; + mesh.rotation.y = Math.PI / 2; + scene.add(mesh); + + const modifier = new SimplifyModifier(); + + const simplified = mesh.clone(); + simplified.material = simplified.material.clone(); + simplified.material.flatShading = true; + const count = Math.floor(simplified.geometry.attributes.position.count * 0.875); // number of vertices to remove + simplified.geometry = modifier.modify(simplified.geometry, count); + + simplified.position.x = 3; + simplified.rotation.y = -Math.PI / 2; + scene.add(simplified); + + render(); + }); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + renderer.setSize(window.innerWidth, window.innerHeight); + + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + render(); +} + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_modifier_tessellation.ts b/examples-testing/examples/webgl_modifier_tessellation.ts new file mode 100644 index 000000000..4600fc6cb --- /dev/null +++ b/examples-testing/examples/webgl_modifier_tessellation.ts @@ -0,0 +1,142 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { TrackballControls } from 'three/addons/controls/TrackballControls.js'; +import { TessellateModifier } from 'three/addons/modifiers/TessellateModifier.js'; +import { FontLoader } from 'three/addons/loaders/FontLoader.js'; +import { TextGeometry } from 'three/addons/geometries/TextGeometry.js'; + +let renderer, scene, camera, stats; + +let controls; + +let mesh, uniforms; + +const WIDTH = window.innerWidth; +const HEIGHT = window.innerHeight; + +const loader = new FontLoader(); +loader.load('fonts/helvetiker_bold.typeface.json', function (font) { + init(font); +}); + +function init(font) { + camera = new THREE.PerspectiveCamera(40, WIDTH / HEIGHT, 1, 10000); + camera.position.set(-100, 100, 200); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x050505); + + // + + let geometry = new TextGeometry('THREE.JS', { + font: font, + + size: 40, + depth: 5, + curveSegments: 3, + + bevelThickness: 2, + bevelSize: 1, + bevelEnabled: true, + }); + + geometry.center(); + + const tessellateModifier = new TessellateModifier(8, 6); + + geometry = tessellateModifier.modify(geometry); + + // + + const numFaces = geometry.attributes.position.count / 3; + + const colors = new Float32Array(numFaces * 3 * 3); + const displacement = new Float32Array(numFaces * 3 * 3); + + const color = new THREE.Color(); + + for (let f = 0; f < numFaces; f++) { + const index = 9 * f; + + const h = 0.2 * Math.random(); + const s = 0.5 + 0.5 * Math.random(); + const l = 0.5 + 0.5 * Math.random(); + + color.setHSL(h, s, l); + + const d = 10 * (0.5 - Math.random()); + + for (let i = 0; i < 3; i++) { + colors[index + 3 * i] = color.r; + colors[index + 3 * i + 1] = color.g; + colors[index + 3 * i + 2] = color.b; + + displacement[index + 3 * i] = d; + displacement[index + 3 * i + 1] = d; + displacement[index + 3 * i + 2] = d; + } + } + + geometry.setAttribute('customColor', new THREE.BufferAttribute(colors, 3)); + geometry.setAttribute('displacement', new THREE.BufferAttribute(displacement, 3)); + + // + + uniforms = { + amplitude: { value: 0.0 }, + }; + + const shaderMaterial = new THREE.ShaderMaterial({ + uniforms: uniforms, + vertexShader: document.getElementById('vertexshader').textContent, + fragmentShader: document.getElementById('fragmentshader').textContent, + }); + + // + + mesh = new THREE.Mesh(geometry, shaderMaterial); + + scene.add(mesh); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(WIDTH, HEIGHT); + renderer.setAnimationLoop(animate); + + const container = document.getElementById('container'); + container.appendChild(renderer.domElement); + + controls = new TrackballControls(camera, renderer.domElement); + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + render(); + + stats.update(); +} + +function render() { + const time = Date.now() * 0.001; + + uniforms.amplitude.value = 1.0 + Math.sin(time * 0.5); + + controls.update(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_morphtargets.ts b/examples-testing/examples/webgl_morphtargets.ts new file mode 100644 index 000000000..d8a4bbe8d --- /dev/null +++ b/examples-testing/examples/webgl_morphtargets.ts @@ -0,0 +1,116 @@ +import * as THREE from 'three'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let container, camera, scene, renderer, mesh; + +init(); + +function init() { + container = document.getElementById('container'); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x8fbcd4); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 20); + camera.position.z = 10; + scene.add(camera); + + scene.add(new THREE.AmbientLight(0x8fbcd4, 1.5)); + + const pointLight = new THREE.PointLight(0xffffff, 200); + camera.add(pointLight); + + const geometry = createGeometry(); + + const material = new THREE.MeshPhongMaterial({ + color: 0xff0000, + flatShading: true, + }); + + mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + initGUI(); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(function () { + renderer.render(scene, camera); + }); + container.appendChild(renderer.domElement); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.enableZoom = false; + + window.addEventListener('resize', onWindowResize); +} + +function createGeometry() { + const geometry = new THREE.BoxGeometry(2, 2, 2, 32, 32, 32); + + // create an empty array to hold targets for the attribute we want to morph + // morphing positions and normals is supported + geometry.morphAttributes.position = []; + + // the original positions of the cube's vertices + const positionAttribute = geometry.attributes.position; + + // for the first morph target we'll move the cube's vertices onto the surface of a sphere + const spherePositions = []; + + // for the second morph target, we'll twist the cubes vertices + const twistPositions = []; + const direction = new THREE.Vector3(1, 0, 0); + const vertex = new THREE.Vector3(); + + for (let i = 0; i < positionAttribute.count; i++) { + const x = positionAttribute.getX(i); + const y = positionAttribute.getY(i); + const z = positionAttribute.getZ(i); + + spherePositions.push( + x * Math.sqrt(1 - (y * y) / 2 - (z * z) / 2 + (y * y * z * z) / 3), + y * Math.sqrt(1 - (z * z) / 2 - (x * x) / 2 + (z * z * x * x) / 3), + z * Math.sqrt(1 - (x * x) / 2 - (y * y) / 2 + (x * x * y * y) / 3), + ); + + // stretch along the x-axis so we can see the twist better + vertex.set(x * 2, y, z); + + vertex.applyAxisAngle(direction, (Math.PI * x) / 2).toArray(twistPositions, twistPositions.length); + } + + // add the spherical positions as the first morph target + geometry.morphAttributes.position[0] = new THREE.Float32BufferAttribute(spherePositions, 3); + + // add the twisted positions as the second morph target + geometry.morphAttributes.position[1] = new THREE.Float32BufferAttribute(twistPositions, 3); + + return geometry; +} + +function initGUI() { + // Set up dat.GUI to control targets + const params = { + Spherify: 0, + Twist: 0, + }; + const gui = new GUI({ title: 'Morph Targets' }); + + gui.add(params, 'Spherify', 0, 1, 0.01).onChange(function (value) { + mesh.morphTargetInfluences[0] = value; + }); + gui.add(params, 'Twist', 0, 1, 0.01).onChange(function (value) { + mesh.morphTargetInfluences[1] = value; + }); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} diff --git a/examples-testing/examples/webgl_morphtargets_face.ts b/examples-testing/examples/webgl_morphtargets_face.ts new file mode 100644 index 000000000..7f348c8a4 --- /dev/null +++ b/examples-testing/examples/webgl_morphtargets_face.ts @@ -0,0 +1,108 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js'; +import { MeshoptDecoder } from 'three/addons/libs/meshopt_decoder.module.js'; + +import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let camera, scene, renderer, stats, mixer, timer, controls; + +init(); + +function init() { + timer = new THREE.Timer(); + timer.connect(document); + + const container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 20); + camera.position.set(-1.8, 0.8, 3); + + scene = new THREE.Scene(); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + + container.appendChild(renderer.domElement); + + const ktx2Loader = new KTX2Loader().setTranscoderPath('jsm/libs/basis/').detectSupport(renderer); + + new GLTFLoader() + .setKTX2Loader(ktx2Loader) + .setMeshoptDecoder(MeshoptDecoder) + .load('models/gltf/facecap.glb', gltf => { + const mesh = gltf.scene.children[0]; + + scene.add(mesh); + + mixer = new THREE.AnimationMixer(mesh); + + mixer.clipAction(gltf.animations[0]).play(); + + // GUI + + const head = mesh.getObjectByName('mesh_2'); + const influences = head.morphTargetInfluences; + + const gui = new GUI(); + gui.close(); + + for (const [key, value] of Object.entries(head.morphTargetDictionary)) { + gui.add(influences, value, 0, 1, 0.01).name(key.replace('blendShape1.', '')).listen(); + } + }); + + const environment = new RoomEnvironment(); + const pmremGenerator = new THREE.PMREMGenerator(renderer); + + scene.background = new THREE.Color(0x666666); + scene.environment = pmremGenerator.fromScene(environment, 0.04).texture; + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + controls.minDistance = 2.5; + controls.maxDistance = 5; + controls.minAzimuthAngle = -Math.PI / 2; + controls.maxAzimuthAngle = Math.PI / 2; + controls.maxPolarAngle = Math.PI / 1.8; + controls.target.set(0, 0.15, -0.2); + + stats = new Stats(); + container.appendChild(stats.dom); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + const delta = timer.getDelta(); + + if (mixer) { + mixer.update(delta); + } + + renderer.render(scene, camera); + + controls.update(); + + stats.update(); +} diff --git a/examples-testing/examples/webgl_morphtargets_horse.ts b/examples-testing/examples/webgl_morphtargets_horse.ts new file mode 100644 index 000000000..2c29e9c0e --- /dev/null +++ b/examples-testing/examples/webgl_morphtargets_horse.ts @@ -0,0 +1,100 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +let container, stats; +let camera, scene, renderer; +let mesh, mixer; + +const radius = 600; +let theta = 0; +let prevTime = Date.now(); + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + // + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000); + camera.position.y = 300; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xf0f0f0); + + // + + const light1 = new THREE.DirectionalLight(0xefefff, 5); + light1.position.set(1, 1, 1).normalize(); + scene.add(light1); + + const light2 = new THREE.DirectionalLight(0xffefef, 5); + light2.position.set(-1, -1, -1).normalize(); + scene.add(light2); + + const loader = new GLTFLoader(); + loader.load('models/gltf/Horse.glb', function (gltf) { + mesh = gltf.scene.children[0]; + mesh.scale.set(1.5, 1.5, 1.5); + scene.add(mesh); + + mixer = new THREE.AnimationMixer(mesh); + + mixer.clipAction(gltf.animations[0]).setDuration(1).play(); + }); + + // + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + + container.appendChild(renderer.domElement); + + // + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + render(); + stats.update(); +} + +function render() { + theta += 0.1; + + camera.position.x = radius * Math.sin(THREE.MathUtils.degToRad(theta)); + camera.position.z = radius * Math.cos(THREE.MathUtils.degToRad(theta)); + + camera.lookAt(0, 150, 0); + + if (mixer) { + const time = Date.now(); + + mixer.update((time - prevTime) * 0.001); + + prevTime = time; + } + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_morphtargets_sphere.ts b/examples-testing/examples/webgl_morphtargets_sphere.ts new file mode 100644 index 000000000..3e36f002c --- /dev/null +++ b/examples-testing/examples/webgl_morphtargets_sphere.ts @@ -0,0 +1,105 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +let camera, scene, renderer, timer; + +let mesh; + +let sign = 1; +const speed = 0.5; + +init(); + +function init() { + const container = document.getElementById('container'); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.2, 100); + camera.position.set(0, 5, 5); + + scene = new THREE.Scene(); + + timer = new THREE.Timer(); + timer.connect(document); + + const light1 = new THREE.PointLight(0xff2200, 50000); + light1.position.set(100, 100, 100); + scene.add(light1); + + const light2 = new THREE.PointLight(0x22ff00, 10000); + light2.position.set(-100, -100, -100); + scene.add(light2); + + scene.add(new THREE.AmbientLight(0x111111)); + + const loader = new GLTFLoader(); + loader.load('models/gltf/AnimatedMorphSphere/glTF/AnimatedMorphSphere.gltf', function (gltf) { + mesh = gltf.scene.getObjectByName('AnimatedMorphSphere'); + mesh.rotation.z = Math.PI / 2; + scene.add(mesh); + + // + + const pointsMaterial = new THREE.PointsMaterial({ + size: 10, + sizeAttenuation: false, + map: new THREE.TextureLoader().load('textures/sprites/disc.png'), + alphaTest: 0.5, + }); + + const points = new THREE.Points(mesh.geometry, pointsMaterial); + points.morphTargetInfluences = mesh.morphTargetInfluences; + points.morphTargetDictionary = mesh.morphTargetDictionary; + mesh.add(points); + }); + + // + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + + container.appendChild(renderer.domElement); + + // + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 1; + controls.maxDistance = 20; + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + render(); +} + +function render() { + const delta = timer.getDelta(); + + if (mesh !== undefined) { + const step = delta * speed; + + mesh.rotation.y += step; + + mesh.morphTargetInfluences[1] = mesh.morphTargetInfluences[1] + step * sign; + + if (mesh.morphTargetInfluences[1] <= 0 || mesh.morphTargetInfluences[1] >= 1) { + sign *= -1; + } + } + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_multiple_elements.ts b/examples-testing/examples/webgl_multiple_elements.ts new file mode 100644 index 000000000..64f8a9c5f --- /dev/null +++ b/examples-testing/examples/webgl_multiple_elements.ts @@ -0,0 +1,139 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let canvas, renderer; + +const scenes = []; + +init(); + +function init() { + canvas = document.getElementById('c'); + + const geometries = [ + new THREE.BoxGeometry(1, 1, 1), + new THREE.SphereGeometry(0.5, 12, 8), + new THREE.DodecahedronGeometry(0.5), + new THREE.CylinderGeometry(0.5, 0.5, 1, 12), + ]; + + const content = document.getElementById('content'); + + for (let i = 0; i < 40; i++) { + const scene = new THREE.Scene(); + + // make a list item + const element = document.createElement('div'); + element.className = 'list-item'; + + const sceneElement = document.createElement('div'); + element.appendChild(sceneElement); + + const descriptionElement = document.createElement('div'); + descriptionElement.innerText = 'Scene ' + (i + 1); + element.appendChild(descriptionElement); + + // the element that represents the area we want to render the scene + scene.userData.element = sceneElement; + content.appendChild(element); + + const camera = new THREE.PerspectiveCamera(50, 1, 1, 10); + camera.position.z = 2; + scene.userData.camera = camera; + + const controls = new OrbitControls(scene.userData.camera, scene.userData.element); + controls.minDistance = 2; + controls.maxDistance = 5; + controls.enablePan = false; + controls.enableZoom = false; + scene.userData.controls = controls; + + // add one random mesh to each scene + const geometry = geometries[(geometries.length * Math.random()) | 0]; + + const material = new THREE.MeshStandardMaterial({ + color: new THREE.Color().setHSL(Math.random(), 1, 0.75, THREE.SRGBColorSpace), + roughness: 0.5, + metalness: 0, + flatShading: true, + }); + + scene.add(new THREE.Mesh(geometry, material)); + + scene.add(new THREE.HemisphereLight(0xaaaaaa, 0x444444, 3)); + + const light = new THREE.DirectionalLight(0xffffff, 1.5); + light.position.set(1, 1, 1); + scene.add(light); + + scenes.push(scene); + } + + renderer = new THREE.WebGLRenderer({ canvas: canvas, antialias: true }); + renderer.setClearColor(0xffffff, 1); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setAnimationLoop(animate); +} + +function updateSize() { + const width = canvas.clientWidth; + const height = canvas.clientHeight; + + if (canvas.width !== width || canvas.height !== height) { + renderer.setSize(width, height, false); + } +} + +function animate() { + updateSize(); + + canvas.style.transform = `translateY(${window.scrollY}px)`; + + renderer.setClearColor(0xffffff); + renderer.setScissorTest(false); + renderer.clear(); + + renderer.setClearColor(0xe0e0e0); + renderer.setScissorTest(true); + + scenes.forEach(function (scene) { + // so something moves + scene.children[0].rotation.y = Date.now() * 0.001; + + // get the element that is a place holder for where we want to + // draw the scene + const element = scene.userData.element; + + // get its position relative to the page's viewport + const rect = element.getBoundingClientRect(); + + // check if it's offscreen. If so skip it + if ( + rect.bottom < 0 || + rect.top > renderer.domElement.clientHeight || + rect.right < 0 || + rect.left > renderer.domElement.clientWidth + ) { + return; // it's off screen + } + + // set the viewport + const width = rect.right - rect.left; + const height = rect.bottom - rect.top; + const left = rect.left; + const bottom = renderer.domElement.clientHeight - rect.bottom; + + renderer.setViewport(left, bottom, width, height); + renderer.setScissor(left, bottom, width, height); + + const camera = scene.userData.camera; + + //camera.aspect = width / height; // not changing in this example + //camera.updateProjectionMatrix(); + + //scene.userData.controls.update(); + + renderer.render(scene, camera); + }); +} diff --git a/examples-testing/examples/webgl_multiple_rendertargets.ts b/examples-testing/examples/webgl_multiple_rendertargets.ts new file mode 100644 index 000000000..86708082b --- /dev/null +++ b/examples-testing/examples/webgl_multiple_rendertargets.ts @@ -0,0 +1,133 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let camera, scene, renderer, controls; +let renderTarget; +let postScene, postCamera; + +const parameters = { + samples: 4, + wireframe: false, +}; + +const gui = new GUI(); +gui.add(parameters, 'samples', 0, 4).step(1); +gui.add(parameters, 'wireframe'); +gui.onChange(render); + +init(); + +function init() { + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + document.body.appendChild(renderer.domElement); + + // Create a multi render target with Float buffers + + renderTarget = new THREE.WebGLRenderTarget( + window.innerWidth * window.devicePixelRatio, + window.innerHeight * window.devicePixelRatio, + { + count: 2, + minFilter: THREE.NearestFilter, + magFilter: THREE.NearestFilter, + }, + ); + + // Name our G-Buffer attachments for debugging + + renderTarget.textures[0].name = 'diffuse'; + renderTarget.textures[1].name = 'normal'; + + // Scene setup + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x222222); + + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 50); + camera.position.z = 4; + + const loader = new THREE.TextureLoader(); + + const diffuse = loader.load('textures/hardwood2_diffuse.jpg', render); + diffuse.wrapS = THREE.RepeatWrapping; + diffuse.wrapT = THREE.RepeatWrapping; + diffuse.colorSpace = THREE.SRGBColorSpace; + + scene.add( + new THREE.Mesh( + new THREE.TorusKnotGeometry(1, 0.3, 128, 32), + new THREE.RawShaderMaterial({ + name: 'G-Buffer Shader', + vertexShader: document.querySelector('#gbuffer-vert').textContent.trim(), + fragmentShader: document.querySelector('#gbuffer-frag').textContent.trim(), + uniforms: { + tDiffuse: { value: diffuse }, + repeat: { value: new THREE.Vector2(5, 0.5) }, + }, + glslVersion: THREE.GLSL3, + }), + ), + ); + + // PostProcessing setup + + postScene = new THREE.Scene(); + postCamera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1); + + postScene.add( + new THREE.Mesh( + new THREE.PlaneGeometry(2, 2), + new THREE.RawShaderMaterial({ + name: 'Post-FX Shader', + vertexShader: document.querySelector('#render-vert').textContent.trim(), + fragmentShader: document.querySelector('#render-frag').textContent.trim(), + uniforms: { + tDiffuse: { value: renderTarget.textures[0] }, + tNormal: { value: renderTarget.textures[1] }, + }, + glslVersion: THREE.GLSL3, + }), + ), + ); + + // Controls + + controls = new OrbitControls(camera, renderer.domElement); + controls.addEventListener('change', render); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + const dpr = renderer.getPixelRatio(); + renderTarget.setSize(window.innerWidth * dpr, window.innerHeight * dpr); + + render(); +} + +function render() { + renderTarget.samples = parameters.samples; + + scene.traverse(function (child) { + if (child.material !== undefined) { + child.material.wireframe = parameters.wireframe; + } + }); + + // render scene into target + renderer.setRenderTarget(renderTarget); + renderer.render(scene, camera); + + // render post FX + renderer.setRenderTarget(null); + renderer.render(postScene, postCamera); +} diff --git a/examples-testing/examples/webgl_multiple_scenes_comparison.ts b/examples-testing/examples/webgl_multiple_scenes_comparison.ts new file mode 100644 index 000000000..41a5130d4 --- /dev/null +++ b/examples-testing/examples/webgl_multiple_scenes_comparison.ts @@ -0,0 +1,98 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let container, camera, renderer, controls; +let sceneL, sceneR; + +let sliderPos = window.innerWidth / 2; + +init(); + +function init() { + container = document.querySelector('.container'); + + sceneL = new THREE.Scene(); + sceneL.background = new THREE.Color(0xbcd48f); + + sceneR = new THREE.Scene(); + sceneR.background = new THREE.Color(0x8fbcd4); + + camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.z = 6; + + controls = new OrbitControls(camera, container); + + const light = new THREE.HemisphereLight(0xffffff, 0x444444, 3); + light.position.set(-2, 2, 2); + sceneL.add(light.clone()); + sceneR.add(light.clone()); + + initMeshes(); + initSlider(); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setScissorTest(true); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + window.addEventListener('resize', onWindowResize); +} + +function initMeshes() { + const geometry = new THREE.IcosahedronGeometry(1, 3); + + const meshL = new THREE.Mesh(geometry, new THREE.MeshStandardMaterial()); + sceneL.add(meshL); + + const meshR = new THREE.Mesh(geometry, new THREE.MeshStandardMaterial({ wireframe: true })); + sceneR.add(meshR); +} + +function initSlider() { + const slider = document.querySelector('.slider'); + + function onPointerDown() { + if (event.isPrimary === false) return; + + controls.enabled = false; + + window.addEventListener('pointermove', onPointerMove); + window.addEventListener('pointerup', onPointerUp); + } + + function onPointerUp() { + controls.enabled = true; + + window.removeEventListener('pointermove', onPointerMove); + window.removeEventListener('pointerup', onPointerUp); + } + + function onPointerMove(e) { + if (event.isPrimary === false) return; + + sliderPos = Math.max(0, Math.min(window.innerWidth, e.pageX)); + + slider.style.left = sliderPos - slider.offsetWidth / 2 + 'px'; + } + + slider.style.touchAction = 'none'; // disable touch scroll + slider.addEventListener('pointerdown', onPointerDown); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + renderer.setScissor(0, 0, sliderPos, window.innerHeight); + renderer.render(sceneL, camera); + + renderer.setScissor(sliderPos, 0, window.innerWidth, window.innerHeight); + renderer.render(sceneR, camera); +} diff --git a/examples-testing/examples/webgl_multiple_views.ts b/examples-testing/examples/webgl_multiple_views.ts new file mode 100644 index 000000000..672846f9f --- /dev/null +++ b/examples-testing/examples/webgl_multiple_views.ts @@ -0,0 +1,235 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +let stats; + +let scene, renderer; + +let mouseX = 0; + +let windowWidth, windowHeight; + +const views = [ + { + left: 0, + bottom: 0, + width: 0.5, + height: 1.0, + background: new THREE.Color().setRGB(0.5, 0.5, 0.7, THREE.SRGBColorSpace), + eye: [0, 300, 1800], + up: [0, 1, 0], + fov: 30, + updateCamera: function (camera, scene, mouseX) { + camera.position.x += mouseX * 0.05; + camera.position.x = Math.max(Math.min(camera.position.x, 2000), -2000); + camera.lookAt(scene.position); + }, + }, + { + left: 0.5, + bottom: 0, + width: 0.5, + height: 0.5, + background: new THREE.Color().setRGB(0.7, 0.5, 0.5, THREE.SRGBColorSpace), + eye: [0, 1800, 0], + up: [0, 0, 1], + fov: 45, + updateCamera: function (camera, scene, mouseX) { + camera.position.x -= mouseX * 0.05; + camera.position.x = Math.max(Math.min(camera.position.x, 2000), -2000); + camera.lookAt(camera.position.clone().setY(0)); + }, + }, + { + left: 0.5, + bottom: 0.5, + width: 0.5, + height: 0.5, + background: new THREE.Color().setRGB(0.5, 0.7, 0.7, THREE.SRGBColorSpace), + eye: [1400, 800, 1400], + up: [0, 1, 0], + fov: 60, + updateCamera: function (camera, scene, mouseX) { + camera.position.y -= mouseX * 0.05; + camera.position.y = Math.max(Math.min(camera.position.y, 1600), -1600); + camera.lookAt(scene.position); + }, + }, +]; + +init(); + +function init() { + const container = document.getElementById('container'); + + for (let ii = 0; ii < views.length; ++ii) { + const view = views[ii]; + const camera = new THREE.PerspectiveCamera(view.fov, window.innerWidth / window.innerHeight, 1, 10000); + camera.position.fromArray(view.eye); + camera.up.fromArray(view.up); + view.camera = camera; + } + + scene = new THREE.Scene(); + + const light = new THREE.DirectionalLight(0xffffff, 3); + light.position.set(0, 0, 1); + scene.add(light); + + // shadow + + const canvas = document.createElement('canvas'); + canvas.width = 128; + canvas.height = 128; + + const context = canvas.getContext('2d'); + const gradient = context.createRadialGradient( + canvas.width / 2, + canvas.height / 2, + 0, + canvas.width / 2, + canvas.height / 2, + canvas.width / 2, + ); + gradient.addColorStop(0.1, 'rgba(0,0,0,0.15)'); + gradient.addColorStop(1, 'rgba(0,0,0,0)'); + + context.fillStyle = gradient; + context.fillRect(0, 0, canvas.width, canvas.height); + + const shadowTexture = new THREE.CanvasTexture(canvas); + + const shadowMaterial = new THREE.MeshBasicMaterial({ map: shadowTexture, transparent: true }); + const shadowGeo = new THREE.PlaneGeometry(300, 300, 1, 1); + + let shadowMesh; + + shadowMesh = new THREE.Mesh(shadowGeo, shadowMaterial); + shadowMesh.position.y = -250; + shadowMesh.rotation.x = -Math.PI / 2; + scene.add(shadowMesh); + + shadowMesh = new THREE.Mesh(shadowGeo, shadowMaterial); + shadowMesh.position.x = -400; + shadowMesh.position.y = -250; + shadowMesh.rotation.x = -Math.PI / 2; + scene.add(shadowMesh); + + shadowMesh = new THREE.Mesh(shadowGeo, shadowMaterial); + shadowMesh.position.x = 400; + shadowMesh.position.y = -250; + shadowMesh.rotation.x = -Math.PI / 2; + scene.add(shadowMesh); + + const radius = 200; + + const geometry1 = new THREE.IcosahedronGeometry(radius, 1); + + const count = geometry1.attributes.position.count; + geometry1.setAttribute('color', new THREE.BufferAttribute(new Float32Array(count * 3), 3)); + + const geometry2 = geometry1.clone(); + const geometry3 = geometry1.clone(); + + const color = new THREE.Color(); + const positions1 = geometry1.attributes.position; + const positions2 = geometry2.attributes.position; + const positions3 = geometry3.attributes.position; + const colors1 = geometry1.attributes.color; + const colors2 = geometry2.attributes.color; + const colors3 = geometry3.attributes.color; + + for (let i = 0; i < count; i++) { + color.setHSL((positions1.getY(i) / radius + 1) / 2, 1.0, 0.5, THREE.SRGBColorSpace); + colors1.setXYZ(i, color.r, color.g, color.b); + + color.setHSL(0, (positions2.getY(i) / radius + 1) / 2, 0.5, THREE.SRGBColorSpace); + colors2.setXYZ(i, color.r, color.g, color.b); + + color.setRGB(1, 0.8 - (positions3.getY(i) / radius + 1) / 2, 0, THREE.SRGBColorSpace); + colors3.setXYZ(i, color.r, color.g, color.b); + } + + const material = new THREE.MeshPhongMaterial({ + color: 0xffffff, + flatShading: true, + vertexColors: true, + shininess: 0, + }); + + const wireframeMaterial = new THREE.MeshBasicMaterial({ color: 0x000000, wireframe: true, transparent: true }); + + let mesh = new THREE.Mesh(geometry1, material); + let wireframe = new THREE.Mesh(geometry1, wireframeMaterial); + mesh.add(wireframe); + mesh.position.x = -400; + mesh.rotation.x = -1.87; + scene.add(mesh); + + mesh = new THREE.Mesh(geometry2, material); + wireframe = new THREE.Mesh(geometry2, wireframeMaterial); + mesh.add(wireframe); + mesh.position.x = 400; + scene.add(mesh); + + mesh = new THREE.Mesh(geometry3, material); + wireframe = new THREE.Mesh(geometry3, wireframeMaterial); + mesh.add(wireframe); + scene.add(mesh); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + stats = new Stats(); + container.appendChild(stats.dom); + + document.addEventListener('mousemove', onDocumentMouseMove); +} + +function onDocumentMouseMove(event) { + mouseX = event.clientX - windowWidth / 2; +} + +function updateSize() { + if (windowWidth != window.innerWidth || windowHeight != window.innerHeight) { + windowWidth = window.innerWidth; + windowHeight = window.innerHeight; + + renderer.setSize(windowWidth, windowHeight); + } +} + +function animate() { + render(); + stats.update(); +} + +function render() { + updateSize(); + + for (let ii = 0; ii < views.length; ++ii) { + const view = views[ii]; + const camera = view.camera; + + view.updateCamera(camera, scene, mouseX); + + const left = Math.floor(windowWidth * view.left); + const bottom = Math.floor(windowHeight * view.bottom); + const width = Math.floor(windowWidth * view.width); + const height = Math.floor(windowHeight * view.height); + + renderer.setViewport(left, bottom, width, height); + renderer.setScissor(left, bottom, width, height); + renderer.setScissorTest(true); + renderer.setClearColor(view.background); + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.render(scene, camera); + } +} diff --git a/examples-testing/examples/webgl_multisampled_renderbuffers.ts b/examples-testing/examples/webgl_multisampled_renderbuffers.ts new file mode 100644 index 000000000..df84fb144 --- /dev/null +++ b/examples-testing/examples/webgl_multisampled_renderbuffers.ts @@ -0,0 +1,133 @@ +import * as THREE from 'three'; + +import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; +import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; +import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let camera, renderer, group, container; + +let composer1, composer2; + +const params = { + animate: true, +}; + +init(); + +function init() { + container = document.getElementById('container'); + + camera = new THREE.PerspectiveCamera(45, container.offsetWidth / container.offsetHeight, 10, 2000); + camera.position.z = 500; + + const scene = new THREE.Scene(); + scene.background = new THREE.Color(0xffffff); + scene.fog = new THREE.Fog(0xcccccc, 100, 1500); + + // + + const hemiLight = new THREE.HemisphereLight(0xffffff, 0x222222, 5); + hemiLight.position.set(1, 1, 1); + scene.add(hemiLight); + + // + + group = new THREE.Group(); + + const geometry = new THREE.SphereGeometry(10, 64, 40); + const material = new THREE.MeshLambertMaterial({ + color: 0xee0808, + polygonOffset: true, + polygonOffsetFactor: 1, // positive value pushes polygon further away + polygonOffsetUnits: 1, + }); + const material2 = new THREE.MeshBasicMaterial({ color: 0xffffff, wireframe: true }); + + for (let i = 0; i < 50; i++) { + const mesh = new THREE.Mesh(geometry, material); + mesh.position.x = Math.random() * 600 - 300; + mesh.position.y = Math.random() * 600 - 300; + mesh.position.z = Math.random() * 600 - 300; + mesh.rotation.x = Math.random(); + mesh.rotation.z = Math.random(); + mesh.scale.setScalar(Math.random() * 5 + 5); + group.add(mesh); + + const mesh2 = new THREE.Mesh(geometry, material2); + mesh2.position.copy(mesh.position); + mesh2.rotation.copy(mesh.rotation); + mesh2.scale.copy(mesh.scale); + group.add(mesh2); + } + + scene.add(group); + + // + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(container.offsetWidth, container.offsetHeight); + renderer.setAnimationLoop(animate); + renderer.autoClear = false; + container.appendChild(renderer.domElement); + + // + + const size = renderer.getDrawingBufferSize(new THREE.Vector2()); + const renderTarget = new THREE.WebGLRenderTarget(size.width, size.height, { + samples: 4, + type: THREE.HalfFloatType, + }); + + const renderPass = new RenderPass(scene, camera); + const outputPass = new OutputPass(); + + // + + composer1 = new EffectComposer(renderer); + composer1.addPass(renderPass); + composer1.addPass(outputPass); + + // + + composer2 = new EffectComposer(renderer, renderTarget); + composer2.addPass(renderPass); + composer2.addPass(outputPass); + + // + + const gui = new GUI(); + gui.add(params, 'animate'); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = container.offsetWidth / container.offsetHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(container.offsetWidth, container.offsetHeight); + composer1.setSize(container.offsetWidth, container.offsetHeight); + composer2.setSize(container.offsetWidth, container.offsetHeight); +} + +function animate() { + const halfWidth = container.offsetWidth / 2; + + if (params.animate) { + group.rotation.y += 0.002; + } + + renderer.setScissorTest(true); + + renderer.setScissor(0, 0, halfWidth - 1, container.offsetHeight); + composer1.render(); + + renderer.setScissor(halfWidth, 0, halfWidth, container.offsetHeight); + composer2.render(); + + renderer.setScissorTest(false); +} diff --git a/examples-testing/examples/webgl_panorama_cube.ts b/examples-testing/examples/webgl_panorama_cube.ts new file mode 100644 index 000000000..efd09cfc5 --- /dev/null +++ b/examples-testing/examples/webgl_panorama_cube.ts @@ -0,0 +1,83 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let camera, controls; +let renderer; +let scene; + +init(); + +function init() { + const container = document.getElementById('container'); + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(90, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.z = 0.01; + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableZoom = false; + controls.enablePan = false; + controls.enableDamping = true; + controls.rotateSpeed = -0.25; + + const textures = getTexturesFromAtlasFile('textures/cube/sun_temple_stripe.jpg', 6); + + const materials = []; + + for (let i = 0; i < 6; i++) { + materials.push(new THREE.MeshBasicMaterial({ map: textures[i] })); + } + + const skyBox = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), materials); + skyBox.geometry.scale(1, 1, -1); + scene.add(skyBox); + + window.addEventListener('resize', onWindowResize); +} + +function getTexturesFromAtlasFile(atlasImgUrl, tilesNum) { + const textures = []; + + for (let i = 0; i < tilesNum; i++) { + textures[i] = new THREE.Texture(); + } + + new THREE.ImageLoader().load(atlasImgUrl, image => { + let canvas, context; + const tileWidth = image.height; + + for (let i = 0; i < textures.length; i++) { + canvas = document.createElement('canvas'); + context = canvas.getContext('2d'); + canvas.height = tileWidth; + canvas.width = tileWidth; + context.drawImage(image, tileWidth * i, 0, tileWidth, tileWidth, 0, 0, tileWidth, tileWidth); + textures[i].colorSpace = THREE.SRGBColorSpace; + textures[i].image = canvas; + textures[i].needsUpdate = true; + } + }); + + return textures; +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + controls.update(); // required when damping is enabled + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_panorama_equirectangular.ts b/examples-testing/examples/webgl_panorama_equirectangular.ts new file mode 100644 index 000000000..35949ee6f --- /dev/null +++ b/examples-testing/examples/webgl_panorama_equirectangular.ts @@ -0,0 +1,112 @@ +import * as THREE from 'three'; + +let camera, scene, renderer; + +let isUserInteracting = false, + onPointerDownMouseX = 0, + onPointerDownMouseY = 0, + lon = 0, + onPointerDownLon = 0, + lat = 0, + onPointerDownLat = 0, + phi = 0, + theta = 0; + +init(); + +function init() { + const container = document.getElementById('container'); + + camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1100); + + scene = new THREE.Scene(); + + const geometry = new THREE.SphereGeometry(500, 60, 40); + // invert the geometry on the x-axis so that all of the faces point inward + geometry.scale(-1, 1, 1); + + const texture = new THREE.TextureLoader().load('textures/2294472375_24a3b8ef46_o.jpg'); + texture.colorSpace = THREE.SRGBColorSpace; + const material = new THREE.MeshBasicMaterial({ map: texture }); + + const mesh = new THREE.Mesh(geometry, material); + + scene.add(mesh); + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + container.style.touchAction = 'none'; + container.addEventListener('pointerdown', onPointerDown); + + document.addEventListener('wheel', onDocumentMouseWheel); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function onPointerDown(event) { + if (event.isPrimary === false) return; + + isUserInteracting = true; + + onPointerDownMouseX = event.clientX; + onPointerDownMouseY = event.clientY; + + onPointerDownLon = lon; + onPointerDownLat = lat; + + document.addEventListener('pointermove', onPointerMove); + document.addEventListener('pointerup', onPointerUp); +} + +function onPointerMove(event) { + if (event.isPrimary === false) return; + + lon = (onPointerDownMouseX - event.clientX) * 0.1 + onPointerDownLon; + lat = (event.clientY - onPointerDownMouseY) * 0.1 + onPointerDownLat; +} + +function onPointerUp(event) { + if (event.isPrimary === false) return; + + isUserInteracting = false; + + document.removeEventListener('pointermove', onPointerMove); + document.removeEventListener('pointerup', onPointerUp); +} + +function onDocumentMouseWheel(event) { + const fov = camera.fov + event.deltaY * 0.05; + + camera.fov = THREE.MathUtils.clamp(fov, 10, 75); + + camera.updateProjectionMatrix(); +} + +function animate() { + if (isUserInteracting === false) { + lon += 0.1; + } + + lat = Math.max(-85, Math.min(85, lat)); + phi = THREE.MathUtils.degToRad(90 - lat); + theta = THREE.MathUtils.degToRad(lon); + + const x = 500 * Math.sin(phi) * Math.cos(theta); + const y = 500 * Math.cos(phi); + const z = 500 * Math.sin(phi) * Math.sin(theta); + + camera.lookAt(x, y, z); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_performance.ts b/examples-testing/examples/webgl_performance.ts new file mode 100644 index 000000000..697ea36fb --- /dev/null +++ b/examples-testing/examples/webgl_performance.ts @@ -0,0 +1,77 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; + +let camera, scene, renderer, stats; + +init(); + +function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(60, 60, 60); + + scene = new THREE.Scene(); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = 1; + + renderer.setAnimationLoop(render); + container.appendChild(renderer.domElement); + + // + + stats = new Stats(); + document.body.appendChild(stats.dom); + + new UltraHDRLoader().setPath('textures/equirectangular/').load('royal_esplanade_2k.hdr.jpg', function (texture) { + texture.mapping = THREE.EquirectangularReflectionMapping; + + scene.environment = texture; + + // model + + const loader = new GLTFLoader().setPath('models/gltf/'); + loader.load('dungeon_warkarma.glb', async function (gltf) { + const model = gltf.scene; + + // wait until the model can be added to the scene without blocking due to shader compilation + + await renderer.compileAsync(model, camera, scene); + + scene.add(model); + }); + }); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 2; + controls.maxDistance = 60; + controls.target.set(0, 0, -0.2); + controls.update(); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function render() { + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/webgl_pmrem_cubemap.ts b/examples-testing/examples/webgl_pmrem_cubemap.ts new file mode 100644 index 000000000..fb5bdafc1 --- /dev/null +++ b/examples-testing/examples/webgl_pmrem_cubemap.ts @@ -0,0 +1,76 @@ +import * as THREE from 'three'; + +import { HDRCubeTextureLoader } from 'three/addons/loaders/HDRCubeTextureLoader.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let camera, scene, renderer; + +init(); + +async function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); + camera.position.set(0, 0, 8); + + scene = new THREE.Scene(); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(render); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + + container.appendChild(renderer.domElement); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 2; + controls.maxDistance = 10; + controls.update(); + + new HDRCubeTextureLoader() + .setPath('./textures/cube/pisaHDR/') + .load(['px.hdr', 'nx.hdr', 'py.hdr', 'ny.hdr', 'pz.hdr', 'nz.hdr'], function (map) { + const pmremGenerator = new THREE.PMREMGenerator(renderer); + const envMap = pmremGenerator.fromCubemap(map).texture; + + scene.background = envMap; + scene.backgroundBlurriness = 0.5; + + pmremGenerator.dispose(); + + const geometry = new THREE.SphereGeometry(0.4, 64, 64); + + for (let i = 0; i < 6; i++) { + for (let j = 0; j < 5; j++) { + const material = new THREE.MeshPhysicalMaterial({ + roughness: i / 5, + metalness: j / 4, + envMap: envMap, + }); + + const mesh = new THREE.Mesh(geometry, material); + mesh.position.x = i - 2.5; + mesh.position.y = j - 2; + scene.add(mesh); + } + } + }); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_pmrem_equirectangular.ts b/examples-testing/examples/webgl_pmrem_equirectangular.ts new file mode 100644 index 000000000..e7b8e76df --- /dev/null +++ b/examples-testing/examples/webgl_pmrem_equirectangular.ts @@ -0,0 +1,76 @@ +import * as THREE from 'three'; + +import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let camera, scene, renderer; + +init(); + +async function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); + camera.position.set(0, 0, 8); + + scene = new THREE.Scene(); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(render); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + + container.appendChild(renderer.domElement); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 2; + controls.maxDistance = 10; + controls.update(); + + new UltraHDRLoader().setPath('textures/equirectangular/').load('royal_esplanade_2k.hdr.jpg', function (map) { + map.mapping = THREE.EquirectangularReflectionMapping; + + const pmremGenerator = new THREE.PMREMGenerator(renderer); + const envMap = pmremGenerator.fromEquirectangular(map).texture; + + scene.background = envMap; + scene.backgroundBlurriness = 0.5; + + pmremGenerator.dispose(); + + const geometry = new THREE.SphereGeometry(0.4, 64, 64); + + for (let i = 0; i < 6; i++) { + for (let j = 0; j < 5; j++) { + const material = new THREE.MeshPhysicalMaterial({ + roughness: i / 5, + metalness: j / 4, + envMap: envMap, + }); + + const mesh = new THREE.Mesh(geometry, material); + mesh.position.x = i - 2.5; + mesh.position.y = j - 2; + scene.add(mesh); + } + } + }); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_pmrem_test.ts b/examples-testing/examples/webgl_pmrem_test.ts new file mode 100644 index 000000000..3c482338f --- /dev/null +++ b/examples-testing/examples/webgl_pmrem_test.ts @@ -0,0 +1,141 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let scene, camera, controls, renderer; + +function init() { + const width = window.innerWidth; + const height = window.innerHeight; + const aspect = width / height; + + // renderer + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(width, height); + + // tonemapping + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = 1; + + document.body.appendChild(renderer.domElement); + + window.addEventListener('resize', onWindowResize); + + // scene + + scene = new THREE.Scene(); + + // camera + + camera = new THREE.PerspectiveCamera(40, aspect, 1, 30); + updateCamera(); + camera.position.set(0, 0, 16); + + // controls + + controls = new OrbitControls(camera, renderer.domElement); + controls.addEventListener('change', render); // use if there is no animation loop + controls.minDistance = 4; + controls.maxDistance = 20; + + // light + + const directionalLight = new THREE.DirectionalLight(0xffffff, 0); // set intensity to 0 to start + const x = 597; + const y = 213; + const theta = ((x + 0.5) * Math.PI) / 512; + const phi = ((y + 0.5) * Math.PI) / 512; + + directionalLight.position.setFromSphericalCoords(100, -phi, Math.PI / 2 - theta); + + scene.add(directionalLight); + // scene.add( new THREE.DirectionalLightHelper( directionalLight ) ); + + // The spot1Lux HDR environment map is expressed in nits (lux / sr). The directional light has units of lux, + // so to match a 1 lux light, we set a single pixel with a value equal to 1 divided by the solid + // angle of the pixel in steradians. This image is 1024 x 512, + // so the value is 1 / ( sin( phi ) * ( pi / 512 ) ^ 2 ) = 27,490 nits. + + const gui = new GUI(); + gui.add({ enabled: true }, 'enabled') + .name('PMREM') + .onChange(value => { + directionalLight.intensity = value ? 0 : 1; + + scene.traverse(function (child) { + if (child.isMesh) { + child.material.envMapIntensity = 1 - directionalLight.intensity; + } + }); + + render(); + }); +} + +function createObjects() { + let radianceMap = null; + new HDRLoader() + // .setDataType( THREE.FloatType ) + .setPath('textures/equirectangular/') + .load('spot1Lux.hdr', function (texture) { + radianceMap = pmremGenerator.fromEquirectangular(texture).texture; + pmremGenerator.dispose(); + + scene.background = radianceMap; + + const geometry = new THREE.SphereGeometry(0.4, 32, 32); + + for (let x = 0; x <= 10; x++) { + for (let y = 0; y <= 2; y++) { + const material = new THREE.MeshPhysicalMaterial({ + roughness: x / 10, + metalness: y < 1 ? 1 : 0, + color: y < 2 ? 0xffffff : 0x000000, + envMap: radianceMap, + envMapIntensity: 1, + }); + + const mesh = new THREE.Mesh(geometry, material); + mesh.position.x = x - 5; + mesh.position.y = 1 - y; + scene.add(mesh); + } + } + + render(); + }); + + const pmremGenerator = new THREE.PMREMGenerator(renderer); + pmremGenerator.compileEquirectangularShader(); +} + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + updateCamera(); + + renderer.setSize(width, height); + + render(); +} + +function updateCamera() { + const horizontalFoV = 40; + const verticalFoV = + (2 * Math.atan(Math.tan(((horizontalFoV / 2) * Math.PI) / 180) / camera.aspect) * 180) / Math.PI; + camera.fov = verticalFoV; + camera.updateProjectionMatrix(); +} + +function render() { + renderer.render(scene, camera); +} + +Promise.resolve().then(init).then(createObjects).then(render); diff --git a/examples-testing/examples/webgl_points_billboards.ts b/examples-testing/examples/webgl_points_billboards.ts new file mode 100644 index 000000000..24d4de1a9 --- /dev/null +++ b/examples-testing/examples/webgl_points_billboards.ts @@ -0,0 +1,120 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let camera, scene, renderer, stats, material; +let mouseX = 0, + mouseY = 0; + +let windowHalfX = window.innerWidth / 2; +let windowHalfY = window.innerHeight / 2; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight, 2, 2000); + camera.position.z = 1000; + + scene = new THREE.Scene(); + scene.fog = new THREE.FogExp2(0x000000, 0.001); + + const geometry = new THREE.BufferGeometry(); + const vertices = []; + + const sprite = new THREE.TextureLoader().load('textures/sprites/disc.png'); + sprite.colorSpace = THREE.SRGBColorSpace; + + for (let i = 0; i < 10000; i++) { + const x = 2000 * Math.random() - 1000; + const y = 2000 * Math.random() - 1000; + const z = 2000 * Math.random() - 1000; + + vertices.push(x, y, z); + } + + geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); + + material = new THREE.PointsMaterial({ + size: 35, + sizeAttenuation: true, + map: sprite, + alphaTest: 0.5, + transparent: true, + }); + material.color.setHSL(1.0, 0.3, 0.7, THREE.SRGBColorSpace); + + const particles = new THREE.Points(geometry, material); + scene.add(particles); + + // + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // + + stats = new Stats(); + document.body.appendChild(stats.dom); + + // + + const gui = new GUI(); + + gui.add(material, 'sizeAttenuation').onChange(function () { + material.needsUpdate = true; + }); + + gui.open(); + + // + + document.body.style.touchAction = 'none'; + document.body.addEventListener('pointermove', onPointerMove); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + windowHalfX = window.innerWidth / 2; + windowHalfY = window.innerHeight / 2; + + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function onPointerMove(event) { + if (event.isPrimary === false) return; + + mouseX = event.clientX - windowHalfX; + mouseY = event.clientY - windowHalfY; +} + +// + +function animate() { + render(); + stats.update(); +} + +function render() { + const time = Date.now() * 0.00005; + + camera.position.x += (mouseX - camera.position.x) * 0.05; + camera.position.y += (-mouseY - camera.position.y) * 0.05; + + camera.lookAt(scene.position); + + const h = ((360 * (1.0 + time)) % 360) / 360; + material.color.setHSL(h, 0.5, 0.5); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_points_sprites.ts b/examples-testing/examples/webgl_points_sprites.ts new file mode 100644 index 000000000..31b9e2ce1 --- /dev/null +++ b/examples-testing/examples/webgl_points_sprites.ts @@ -0,0 +1,167 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let camera, scene, renderer, stats, parameters; +let mouseX = 0, + mouseY = 0; + +let windowHalfX = window.innerWidth / 2; +let windowHalfY = window.innerHeight / 2; + +const materials = []; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 2000); + camera.position.z = 1000; + + scene = new THREE.Scene(); + scene.fog = new THREE.FogExp2(0x000000, 0.0008); + + const geometry = new THREE.BufferGeometry(); + const vertices = []; + + const textureLoader = new THREE.TextureLoader(); + + const assignSRGB = texture => { + texture.colorSpace = THREE.SRGBColorSpace; + }; + + const sprite1 = textureLoader.load('textures/sprites/snowflake1.png', assignSRGB); + const sprite2 = textureLoader.load('textures/sprites/snowflake2.png', assignSRGB); + const sprite3 = textureLoader.load('textures/sprites/snowflake3.png', assignSRGB); + const sprite4 = textureLoader.load('textures/sprites/snowflake4.png', assignSRGB); + const sprite5 = textureLoader.load('textures/sprites/snowflake5.png', assignSRGB); + + for (let i = 0; i < 10000; i++) { + const x = Math.random() * 2000 - 1000; + const y = Math.random() * 2000 - 1000; + const z = Math.random() * 2000 - 1000; + + vertices.push(x, y, z); + } + + geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); + + parameters = [ + [[1.0, 0.2, 0.5], sprite2, 20], + [[0.95, 0.1, 0.5], sprite3, 15], + [[0.9, 0.05, 0.5], sprite1, 10], + [[0.85, 0, 0.5], sprite5, 8], + [[0.8, 0, 0.5], sprite4, 5], + ]; + + for (let i = 0; i < parameters.length; i++) { + const color = parameters[i][0]; + const sprite = parameters[i][1]; + const size = parameters[i][2]; + + materials[i] = new THREE.PointsMaterial({ + size: size, + map: sprite, + blending: THREE.AdditiveBlending, + depthTest: false, + transparent: true, + }); + materials[i].color.setHSL(color[0], color[1], color[2], THREE.SRGBColorSpace); + + const particles = new THREE.Points(geometry, materials[i]); + + particles.rotation.x = Math.random() * 6; + particles.rotation.y = Math.random() * 6; + particles.rotation.z = Math.random() * 6; + + scene.add(particles); + } + + // + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // + + stats = new Stats(); + document.body.appendChild(stats.dom); + + // + + const gui = new GUI(); + + const params = { + texture: true, + }; + + gui.add(params, 'texture').onChange(function (value) { + for (let i = 0; i < materials.length; i++) { + materials[i].map = value === true ? parameters[i][1] : null; + materials[i].needsUpdate = true; + } + }); + + gui.open(); + + document.body.style.touchAction = 'none'; + document.body.addEventListener('pointermove', onPointerMove); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + windowHalfX = window.innerWidth / 2; + windowHalfY = window.innerHeight / 2; + + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function onPointerMove(event) { + if (event.isPrimary === false) return; + + mouseX = event.clientX - windowHalfX; + mouseY = event.clientY - windowHalfY; +} + +// + +function animate() { + render(); + stats.update(); +} + +function render() { + const time = Date.now() * 0.00005; + + camera.position.x += (mouseX - camera.position.x) * 0.05; + camera.position.y += (-mouseY - camera.position.y) * 0.05; + + camera.lookAt(scene.position); + + for (let i = 0; i < scene.children.length; i++) { + const object = scene.children[i]; + + if (object instanceof THREE.Points) { + object.rotation.y = time * (i < 4 ? i + 1 : -(i + 1)); + } + } + + for (let i = 0; i < materials.length; i++) { + const color = parameters[i][0]; + + const h = ((360 * (color[0] + time)) % 360) / 360; + materials[i].color.setHSL(h, color[1], color[2], THREE.SRGBColorSpace); + } + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_points_waves.ts b/examples-testing/examples/webgl_points_waves.ts new file mode 100644 index 000000000..91986e9e9 --- /dev/null +++ b/examples-testing/examples/webgl_points_waves.ts @@ -0,0 +1,145 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +const SEPARATION = 100, + AMOUNTX = 50, + AMOUNTY = 50; + +let container, stats; +let camera, scene, renderer; + +let particles, + count = 0; + +let mouseX = 0, + mouseY = 0; + +let windowHalfX = window.innerWidth / 2; +let windowHalfY = window.innerHeight / 2; + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000); + camera.position.z = 1000; + + scene = new THREE.Scene(); + + // + + const numParticles = AMOUNTX * AMOUNTY; + + const positions = new Float32Array(numParticles * 3); + const scales = new Float32Array(numParticles); + + let i = 0, + j = 0; + + for (let ix = 0; ix < AMOUNTX; ix++) { + for (let iy = 0; iy < AMOUNTY; iy++) { + positions[i] = ix * SEPARATION - (AMOUNTX * SEPARATION) / 2; // x + positions[i + 1] = 0; // y + positions[i + 2] = iy * SEPARATION - (AMOUNTY * SEPARATION) / 2; // z + + scales[j] = 1; + + i += 3; + j++; + } + } + + const geometry = new THREE.BufferGeometry(); + geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); + geometry.setAttribute('scale', new THREE.BufferAttribute(scales, 1)); + + const material = new THREE.ShaderMaterial({ + uniforms: { + color: { value: new THREE.Color(0xffffff) }, + }, + vertexShader: document.getElementById('vertexshader').textContent, + fragmentShader: document.getElementById('fragmentshader').textContent, + }); + + // + + particles = new THREE.Points(geometry, material); + scene.add(particles); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + stats = new Stats(); + container.appendChild(stats.dom); + + container.style.touchAction = 'none'; + container.addEventListener('pointermove', onPointerMove); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + windowHalfX = window.innerWidth / 2; + windowHalfY = window.innerHeight / 2; + + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function onPointerMove(event) { + if (event.isPrimary === false) return; + + mouseX = event.clientX - windowHalfX; + mouseY = event.clientY - windowHalfY; +} + +// + +function animate() { + render(); + stats.update(); +} + +function render() { + camera.position.x += (mouseX - camera.position.x) * 0.05; + camera.position.y += (-mouseY - camera.position.y) * 0.05; + camera.lookAt(scene.position); + + const positions = particles.geometry.attributes.position.array; + const scales = particles.geometry.attributes.scale.array; + + let i = 0, + j = 0; + + for (let ix = 0; ix < AMOUNTX; ix++) { + for (let iy = 0; iy < AMOUNTY; iy++) { + positions[i + 1] = Math.sin((ix + count) * 0.3) * 50 + Math.sin((iy + count) * 0.5) * 50; + + scales[j] = (Math.sin((ix + count) * 0.3) + 1) * 20 + (Math.sin((iy + count) * 0.5) + 1) * 20; + + i += 3; + j++; + } + } + + particles.geometry.attributes.position.needsUpdate = true; + particles.geometry.attributes.scale.needsUpdate = true; + + renderer.render(scene, camera); + + count += 0.1; +} diff --git a/examples-testing/examples/webgl_portal.ts b/examples-testing/examples/webgl_portal.ts new file mode 100644 index 000000000..4bc59593f --- /dev/null +++ b/examples-testing/examples/webgl_portal.ts @@ -0,0 +1,218 @@ +import * as THREE from 'three'; + +import * as CameraUtils from 'three/addons/utils/CameraUtils.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let camera, scene, renderer; + +let cameraControls; + +let smallSphereOne, smallSphereTwo; + +let portalCamera, + leftPortal, + rightPortal, + leftPortalTexture, + reflectedPosition, + rightPortalTexture, + bottomLeftCorner, + bottomRightCorner, + topLeftCorner; + +init(); + +function init() { + const container = document.getElementById('container'); + + // renderer + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.localClippingEnabled = true; + renderer.toneMapping = THREE.ACESFilmicToneMapping; + container.appendChild(renderer.domElement); + + // scene + scene = new THREE.Scene(); + + // camera + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 5000); + camera.position.set(0, 75, 160); + + cameraControls = new OrbitControls(camera, renderer.domElement); + cameraControls.target.set(0, 40, 0); + cameraControls.maxDistance = 400; + cameraControls.minDistance = 10; + cameraControls.update(); + + // + + const planeGeo = new THREE.PlaneGeometry(100.1, 100.1); + + // bouncing icosphere + const portalPlane = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0.0); + const geometry = new THREE.IcosahedronGeometry(5, 0); + const material = new THREE.MeshPhongMaterial({ + color: 0xffffff, + emissive: 0x333333, + flatShading: true, + clippingPlanes: [portalPlane], + clipShadows: true, + }); + smallSphereOne = new THREE.Mesh(geometry, material); + scene.add(smallSphereOne); + smallSphereTwo = new THREE.Mesh(geometry, material); + scene.add(smallSphereTwo); + + // portals + portalCamera = new THREE.PerspectiveCamera(45, 1.0, 0.1, 500.0); + scene.add(portalCamera); + //frustumHelper = new THREE.CameraHelper( portalCamera ); + //scene.add( frustumHelper ); + bottomLeftCorner = new THREE.Vector3(); + bottomRightCorner = new THREE.Vector3(); + topLeftCorner = new THREE.Vector3(); + reflectedPosition = new THREE.Vector3(); + + leftPortalTexture = new THREE.WebGLRenderTarget(256, 256); + leftPortal = new THREE.Mesh(planeGeo, new THREE.MeshBasicMaterial({ map: leftPortalTexture.texture })); + leftPortal.position.x = -30; + leftPortal.position.y = 20; + leftPortal.scale.set(0.35, 0.35, 0.35); + scene.add(leftPortal); + + rightPortalTexture = new THREE.WebGLRenderTarget(256, 256); + rightPortal = new THREE.Mesh(planeGeo, new THREE.MeshBasicMaterial({ map: rightPortalTexture.texture })); + rightPortal.position.x = 30; + rightPortal.position.y = 20; + rightPortal.scale.set(0.35, 0.35, 0.35); + scene.add(rightPortal); + + // walls + const planeTop = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xffffff })); + planeTop.position.y = 100; + planeTop.rotateX(Math.PI / 2); + scene.add(planeTop); + + const planeBottom = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xffffff })); + planeBottom.rotateX(-Math.PI / 2); + scene.add(planeBottom); + + const planeFront = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0x7f7fff })); + planeFront.position.z = 50; + planeFront.position.y = 50; + planeFront.rotateY(Math.PI); + scene.add(planeFront); + + const planeBack = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xff7fff })); + planeBack.position.z = -50; + planeBack.position.y = 50; + //planeBack.rotateY( Math.PI ); + scene.add(planeBack); + + const planeRight = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0x00ff00 })); + planeRight.position.x = 50; + planeRight.position.y = 50; + planeRight.rotateY(-Math.PI / 2); + scene.add(planeRight); + + const planeLeft = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xff0000 })); + planeLeft.position.x = -50; + planeLeft.position.y = 50; + planeLeft.rotateY(Math.PI / 2); + scene.add(planeLeft); + + // lights + const mainLight = new THREE.PointLight(0xe7e7e7, 2.5, 250, 0); + mainLight.position.y = 60; + scene.add(mainLight); + + const greenLight = new THREE.PointLight(0x00ff00, 0.5, 1000, 0); + greenLight.position.set(550, 50, 0); + scene.add(greenLight); + + const redLight = new THREE.PointLight(0xff0000, 0.5, 1000, 0); + redLight.position.set(-550, 50, 0); + scene.add(redLight); + + const blueLight = new THREE.PointLight(0xbbbbfe, 0.5, 1000, 0); + blueLight.position.set(0, 50, 550); + scene.add(blueLight); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function renderPortal(thisPortalMesh, otherPortalMesh, thisPortalTexture) { + // set the portal camera position to be reflected about the portal plane + thisPortalMesh.worldToLocal(reflectedPosition.copy(camera.position)); + reflectedPosition.x *= -1.0; + reflectedPosition.z *= -1.0; + otherPortalMesh.localToWorld(reflectedPosition); + portalCamera.position.copy(reflectedPosition); + + // grab the corners of the other portal + // - note: the portal is viewed backwards; flip the left/right coordinates + otherPortalMesh.localToWorld(bottomLeftCorner.set(50.05, -50.05, 0.0)); + otherPortalMesh.localToWorld(bottomRightCorner.set(-50.05, -50.05, 0.0)); + otherPortalMesh.localToWorld(topLeftCorner.set(50.05, 50.05, 0.0)); + // set the projection matrix to encompass the portal's frame + CameraUtils.frameCorners(portalCamera, bottomLeftCorner, bottomRightCorner, topLeftCorner, false); + + // render the portal + thisPortalTexture.texture.colorSpace = renderer.outputColorSpace; + renderer.setRenderTarget(thisPortalTexture); + renderer.state.buffers.depth.setMask(true); // make sure the depth buffer is writable so it can be properly cleared, see #18897 + if (renderer.autoClear === false) renderer.clear(); + thisPortalMesh.visible = false; // hide this portal from its own rendering + renderer.render(scene, portalCamera); + thisPortalMesh.visible = true; // re-enable this portal's visibility for general rendering +} + +function animate() { + // move the bouncing sphere(s) + const timerOne = Date.now() * 0.01; + const timerTwo = timerOne + Math.PI * 10.0; + + smallSphereOne.position.set( + Math.cos(timerOne * 0.1) * 30, + Math.abs(Math.cos(timerOne * 0.2)) * 20 + 5, + Math.sin(timerOne * 0.1) * 30, + ); + smallSphereOne.rotation.y = Math.PI / 2 - timerOne * 0.1; + smallSphereOne.rotation.z = timerOne * 0.8; + + smallSphereTwo.position.set( + Math.cos(timerTwo * 0.1) * 30, + Math.abs(Math.cos(timerTwo * 0.2)) * 20 + 5, + Math.sin(timerTwo * 0.1) * 30, + ); + smallSphereTwo.rotation.y = Math.PI / 2 - timerTwo * 0.1; + smallSphereTwo.rotation.z = timerTwo * 0.8; + + // save the original camera properties + const currentRenderTarget = renderer.getRenderTarget(); + const currentXrEnabled = renderer.xr.enabled; + const currentShadowAutoUpdate = renderer.shadowMap.autoUpdate; + renderer.xr.enabled = false; // Avoid camera modification + renderer.shadowMap.autoUpdate = false; // Avoid re-computing shadows + + // render the portal effect + renderPortal(leftPortal, rightPortal, leftPortalTexture); + renderPortal(rightPortal, leftPortal, rightPortalTexture); + + // restore the original rendering properties + renderer.xr.enabled = currentXrEnabled; + renderer.shadowMap.autoUpdate = currentShadowAutoUpdate; + renderer.setRenderTarget(currentRenderTarget); + + // render the main scene + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_postprocessing.ts b/examples-testing/examples/webgl_postprocessing.ts new file mode 100644 index 000000000..ecc9b28ee --- /dev/null +++ b/examples-testing/examples/webgl_postprocessing.ts @@ -0,0 +1,86 @@ +import * as THREE from 'three'; + +import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; +import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; +import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js'; + +import { RGBShiftShader } from 'three/addons/shaders/RGBShiftShader.js'; +import { DotScreenShader } from 'three/addons/shaders/DotScreenShader.js'; +import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; + +let camera, renderer, composer; +let object; + +init(); + +function init() { + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // + + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.z = 400; + + const scene = new THREE.Scene(); + scene.fog = new THREE.Fog(0x000000, 1, 1000); + + object = new THREE.Object3D(); + scene.add(object); + + const geometry = new THREE.SphereGeometry(1, 4, 4); + const material = new THREE.MeshPhongMaterial({ color: 0xffffff, flatShading: true }); + + for (let i = 0; i < 100; i++) { + const mesh = new THREE.Mesh(geometry, material); + mesh.position.set(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5).normalize(); + mesh.position.multiplyScalar(Math.random() * 400); + mesh.rotation.set(Math.random() * 2, Math.random() * 2, Math.random() * 2); + mesh.scale.x = mesh.scale.y = mesh.scale.z = Math.random() * 50; + object.add(mesh); + } + + scene.add(new THREE.AmbientLight(0xcccccc)); + + const light = new THREE.DirectionalLight(0xffffff, 3); + light.position.set(1, 1, 1); + scene.add(light); + + // postprocessing + + composer = new EffectComposer(renderer); + composer.addPass(new RenderPass(scene, camera)); + + const effect1 = new ShaderPass(DotScreenShader); + effect1.uniforms['scale'].value = 4; + composer.addPass(effect1); + + const effect2 = new ShaderPass(RGBShiftShader); + effect2.uniforms['amount'].value = 0.0015; + composer.addPass(effect2); + + const effect3 = new OutputPass(); + composer.addPass(effect3); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + composer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + object.rotation.x += 0.005; + object.rotation.y += 0.01; + + composer.render(); +} diff --git a/examples-testing/examples/webgl_postprocessing_advanced.ts b/examples-testing/examples/webgl_postprocessing_advanced.ts new file mode 100644 index 000000000..82fc39be3 --- /dev/null +++ b/examples-testing/examples/webgl_postprocessing_advanced.ts @@ -0,0 +1,304 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; +import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; +import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js'; +import { BloomPass } from 'three/addons/postprocessing/BloomPass.js'; +import { FilmPass } from 'three/addons/postprocessing/FilmPass.js'; +import { DotScreenPass } from 'three/addons/postprocessing/DotScreenPass.js'; +import { MaskPass, ClearMaskPass } from 'three/addons/postprocessing/MaskPass.js'; +import { TexturePass } from 'three/addons/postprocessing/TexturePass.js'; + +import { BleachBypassShader } from 'three/addons/shaders/BleachBypassShader.js'; +import { ColorifyShader } from 'three/addons/shaders/ColorifyShader.js'; +import { HorizontalBlurShader } from 'three/addons/shaders/HorizontalBlurShader.js'; +import { VerticalBlurShader } from 'three/addons/shaders/VerticalBlurShader.js'; +import { SepiaShader } from 'three/addons/shaders/SepiaShader.js'; +import { VignetteShader } from 'three/addons/shaders/VignetteShader.js'; +import { GammaCorrectionShader } from 'three/addons/shaders/GammaCorrectionShader.js'; + +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +let container, stats; + +let composerScene, composer1, composer2, composer3, composer4; + +let cameraOrtho, cameraPerspective, sceneModel, sceneBG, renderer, mesh, directionalLight; + +const width = window.innerWidth || 2; +const height = window.innerHeight || 2; + +let halfWidth = width / 2; +let halfHeight = height / 2; + +let quadBG, quadMask, renderScene; + +const delta = 0.01; + +init(); + +function init() { + container = document.getElementById('container'); + + // + + cameraOrtho = new THREE.OrthographicCamera(-halfWidth, halfWidth, halfHeight, -halfHeight, -10000, 10000); + cameraOrtho.position.z = 100; + + cameraPerspective = new THREE.PerspectiveCamera(50, width / height, 1, 10000); + cameraPerspective.position.z = 900; + + // + + sceneModel = new THREE.Scene(); + sceneBG = new THREE.Scene(); + + // + + directionalLight = new THREE.DirectionalLight(0xffffff, 3); + directionalLight.position.set(0, -0.1, 1).normalize(); + sceneModel.add(directionalLight); + + const loader = new GLTFLoader(); + loader.load('models/gltf/LeePerrySmith/LeePerrySmith.glb', function (gltf) { + createMesh(gltf.scene.children[0].geometry, sceneModel, 100); + }); + + // + + const diffuseMap = new THREE.TextureLoader().load('textures/cube/SwedishRoyalCastle/pz.jpg'); + diffuseMap.colorSpace = THREE.SRGBColorSpace; + + const materialColor = new THREE.MeshBasicMaterial({ + map: diffuseMap, + depthTest: false, + }); + + quadBG = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), materialColor); + quadBG.position.z = -500; + quadBG.scale.set(width, height, 1); + sceneBG.add(quadBG); + + // + + const sceneMask = new THREE.Scene(); + + quadMask = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), new THREE.MeshBasicMaterial({ color: 0xffaa00 })); + quadMask.position.z = -300; + quadMask.scale.set(width / 2, height / 2, 1); + sceneMask.add(quadMask); + + // + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(width, height); + renderer.setAnimationLoop(animate); + renderer.autoClear = false; + + // + + container.appendChild(renderer.domElement); + + // + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + const shaderBleach = BleachBypassShader; + const shaderSepia = SepiaShader; + const shaderVignette = VignetteShader; + + const effectBleach = new ShaderPass(shaderBleach); + const effectSepia = new ShaderPass(shaderSepia); + const effectVignette = new ShaderPass(shaderVignette); + const gammaCorrection = new ShaderPass(GammaCorrectionShader); + + effectBleach.uniforms['opacity'].value = 0.95; + + effectSepia.uniforms['amount'].value = 0.9; + + effectVignette.uniforms['offset'].value = 1.6; + effectVignette.uniforms['darkness'].value = 0.95; + + const effectBloom = new BloomPass(0.5); + const effectFilm = new FilmPass(0.35); + const effectFilmBW = new FilmPass(0.35, true); + const effectDotScreen = new DotScreenPass(new THREE.Vector2(0, 0), 0.5, 0.8); + + const effectHBlur = new ShaderPass(HorizontalBlurShader); + const effectVBlur = new ShaderPass(VerticalBlurShader); + effectHBlur.uniforms['h'].value = 2 / (width / 2); + effectVBlur.uniforms['v'].value = 2 / (height / 2); + + const effectColorify1 = new ShaderPass(ColorifyShader); + const effectColorify2 = new ShaderPass(ColorifyShader); + effectColorify1.uniforms['color'] = new THREE.Uniform(new THREE.Color(1, 0.8, 0.8)); + effectColorify2.uniforms['color'] = new THREE.Uniform(new THREE.Color(1, 0.75, 0.5)); + + const clearMask = new ClearMaskPass(); + const renderMask = new MaskPass(sceneModel, cameraPerspective); + const renderMaskInverse = new MaskPass(sceneModel, cameraPerspective); + + renderMaskInverse.inverse = true; + + // + + const rtParameters = { + stencilBuffer: true, + }; + + const rtWidth = width / 2; + const rtHeight = height / 2; + + // + + const renderBackground = new RenderPass(sceneBG, cameraOrtho); + const renderModel = new RenderPass(sceneModel, cameraPerspective); + + renderModel.clear = false; + + composerScene = new EffectComposer(renderer, new THREE.WebGLRenderTarget(rtWidth * 2, rtHeight * 2, rtParameters)); + + composerScene.addPass(renderBackground); + composerScene.addPass(renderModel); + composerScene.addPass(renderMaskInverse); + composerScene.addPass(effectHBlur); + composerScene.addPass(effectVBlur); + composerScene.addPass(clearMask); + + // + + renderScene = new TexturePass(composerScene.renderTarget2.texture); + + // + + composer1 = new EffectComposer(renderer, new THREE.WebGLRenderTarget(rtWidth, rtHeight, rtParameters)); + + composer1.addPass(renderScene); + composer1.addPass(gammaCorrection); + composer1.addPass(effectFilmBW); + composer1.addPass(effectVignette); + + // + + composer2 = new EffectComposer(renderer, new THREE.WebGLRenderTarget(rtWidth, rtHeight, rtParameters)); + + composer2.addPass(renderScene); + composer2.addPass(gammaCorrection); + composer2.addPass(effectDotScreen); + composer2.addPass(renderMask); + composer2.addPass(effectColorify1); + composer2.addPass(clearMask); + composer2.addPass(renderMaskInverse); + composer2.addPass(effectColorify2); + composer2.addPass(clearMask); + composer2.addPass(effectVignette); + + // + + composer3 = new EffectComposer(renderer, new THREE.WebGLRenderTarget(rtWidth, rtHeight, rtParameters)); + + composer3.addPass(renderScene); + composer3.addPass(gammaCorrection); + composer3.addPass(effectSepia); + composer3.addPass(effectFilm); + composer3.addPass(effectVignette); + + // + + composer4 = new EffectComposer(renderer, new THREE.WebGLRenderTarget(rtWidth, rtHeight, rtParameters)); + + composer4.addPass(renderScene); + composer4.addPass(gammaCorrection); + composer4.addPass(effectBloom); + composer4.addPass(effectFilm); + composer4.addPass(effectBleach); + composer4.addPass(effectVignette); + + renderScene.uniforms['tDiffuse'].value = composerScene.renderTarget2.texture; + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + halfWidth = window.innerWidth / 2; + halfHeight = window.innerHeight / 2; + + cameraPerspective.aspect = window.innerWidth / window.innerHeight; + cameraPerspective.updateProjectionMatrix(); + + cameraOrtho.left = -halfWidth; + cameraOrtho.right = halfWidth; + cameraOrtho.top = halfHeight; + cameraOrtho.bottom = -halfHeight; + + cameraOrtho.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + composerScene.setSize(halfWidth * 2, halfHeight * 2); + + composer1.setSize(halfWidth, halfHeight); + composer2.setSize(halfWidth, halfHeight); + composer3.setSize(halfWidth, halfHeight); + composer4.setSize(halfWidth, halfHeight); + + renderScene.uniforms['tDiffuse'].value = composerScene.renderTarget2.texture; + + quadBG.scale.set(window.innerWidth, window.innerHeight, 1); + quadMask.scale.set(window.innerWidth / 2, window.innerHeight / 2, 1); +} + +function createMesh(geometry, scene, scale) { + const diffuseMap = new THREE.TextureLoader().load('models/gltf/LeePerrySmith/Map-COL.jpg'); + diffuseMap.colorSpace = THREE.SRGBColorSpace; + + const mat2 = new THREE.MeshPhongMaterial({ + color: 0xcbcbcb, + specular: 0x080808, + shininess: 20, + map: diffuseMap, + normalMap: new THREE.TextureLoader().load('models/gltf/LeePerrySmith/Infinite-Level_02_Tangent_SmoothUV.jpg'), + normalScale: new THREE.Vector2(0.75, 0.75), + }); + + mesh = new THREE.Mesh(geometry, mat2); + mesh.position.set(0, -50, 0); + mesh.scale.set(scale, scale, scale); + + scene.add(mesh); +} + +// + +function animate() { + stats.begin(); + render(); + stats.end(); +} + +function render() { + const time = Date.now() * 0.0004; + + if (mesh) mesh.rotation.y = -time; + + renderer.setViewport(0, 0, halfWidth, halfHeight); + composerScene.render(delta); + + renderer.setViewport(0, 0, halfWidth, halfHeight); + composer1.render(delta); + + renderer.setViewport(halfWidth, 0, halfWidth, halfHeight); + composer2.render(delta); + + renderer.setViewport(0, halfHeight, halfWidth, halfHeight); + composer3.render(delta); + + renderer.setViewport(halfWidth, halfHeight, halfWidth, halfHeight); + composer4.render(delta); +} diff --git a/examples-testing/examples/webgl_postprocessing_afterimage.ts b/examples-testing/examples/webgl_postprocessing_afterimage.ts new file mode 100644 index 000000000..97353dcd2 --- /dev/null +++ b/examples-testing/examples/webgl_postprocessing_afterimage.ts @@ -0,0 +1,72 @@ +import * as THREE from 'three'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; +import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; +import { AfterimagePass } from 'three/addons/postprocessing/AfterimagePass.js'; +import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; + +let camera, scene, renderer, composer; +let mesh; + +let afterimagePass; + +const params = { + enable: true, +}; + +init(); + +function init() { + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.z = 400; + + scene = new THREE.Scene(); + scene.fog = new THREE.Fog(0x000000, 1, 1000); + + const geometry = new THREE.BoxGeometry(150, 150, 150, 2, 2, 2); + const material = new THREE.MeshNormalMaterial(); + mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + // postprocessing + + composer = new EffectComposer(renderer); + composer.addPass(new RenderPass(scene, camera)); + + afterimagePass = new AfterimagePass(); + composer.addPass(afterimagePass); + + const outputPass = new OutputPass(); + composer.addPass(outputPass); + + window.addEventListener('resize', onWindowResize); + + const gui = new GUI({ title: 'Damp setting' }); + gui.add(afterimagePass, 'damp', 0, 1).step(0.001); + gui.add(params, 'enable'); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + composer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + mesh.rotation.x += 0.005; + mesh.rotation.y += 0.01; + + afterimagePass.enabled = params.enable; + + composer.render(); +} diff --git a/examples-testing/examples/webgl_postprocessing_backgrounds.ts b/examples-testing/examples/webgl_postprocessing_backgrounds.ts new file mode 100644 index 000000000..57a6a2dbd --- /dev/null +++ b/examples-testing/examples/webgl_postprocessing_backgrounds.ts @@ -0,0 +1,214 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; +import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; +import { TexturePass } from 'three/addons/postprocessing/TexturePass.js'; +import { CubeTexturePass } from 'three/addons/postprocessing/CubeTexturePass.js'; +import { ClearPass } from 'three/addons/postprocessing/ClearPass.js'; +import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let scene, renderer, composer; +let clearPass, texturePass, renderPass; +let cameraP, cubeTexturePassP; +let gui, stats; + +const params = { + clearPass: true, + clearColor: 'white', + clearAlpha: 1.0, + + texturePass: true, + texturePassOpacity: 1.0, + + cubeTexturePass: true, + cubeTexturePassOpacity: 1.0, + + renderPass: true, +}; + +init(); + +clearGui(); + +function clearGui() { + if (gui) gui.destroy(); + + gui = new GUI(); + + gui.add(params, 'clearPass'); + gui.add(params, 'clearColor', ['black', 'white', 'blue', 'green', 'red']); + gui.add(params, 'clearAlpha', 0, 1); + + gui.add(params, 'texturePass'); + gui.add(params, 'texturePassOpacity', 0, 1); + + gui.add(params, 'cubeTexturePass'); + gui.add(params, 'cubeTexturePassOpacity', 0, 1); + + gui.add(params, 'renderPass'); + + gui.open(); +} + +function init() { + const container = document.getElementById('container'); + + const width = window.innerWidth || 1; + const height = window.innerHeight || 1; + const aspect = width / height; + const devicePixelRatio = window.devicePixelRatio || 1; + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(devicePixelRatio); + renderer.setSize(width, height); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + cameraP = new THREE.PerspectiveCamera(65, aspect, 1, 10); + cameraP.position.z = 7; + + scene = new THREE.Scene(); + + const group = new THREE.Group(); + scene.add(group); + + const light = new THREE.PointLight(0xefffef, 500); + light.position.z = 10; + light.position.y = -10; + light.position.x = -10; + scene.add(light); + + const light2 = new THREE.PointLight(0xffefef, 500); + light2.position.z = 10; + light2.position.x = -10; + light2.position.y = 10; + scene.add(light2); + + const light3 = new THREE.PointLight(0xefefff, 500); + light3.position.z = 10; + light3.position.x = 10; + light3.position.y = -10; + scene.add(light3); + + const geometry = new THREE.SphereGeometry(1, 48, 24); + + const material = new THREE.MeshStandardMaterial(); + material.roughness = 0.5 * Math.random() + 0.25; + material.metalness = 0; + material.color.setHSL(Math.random(), 1.0, 0.3); + + const mesh = new THREE.Mesh(geometry, material); + group.add(mesh); + + // postprocessing + + const genCubeUrls = function (prefix, postfix) { + return [ + prefix + 'px' + postfix, + prefix + 'nx' + postfix, + prefix + 'py' + postfix, + prefix + 'ny' + postfix, + prefix + 'pz' + postfix, + prefix + 'nz' + postfix, + ]; + }; + + composer = new EffectComposer(renderer); + + clearPass = new ClearPass(params.clearColor, params.clearAlpha); + composer.addPass(clearPass); + + texturePass = new TexturePass(); + composer.addPass(texturePass); + + const textureLoader = new THREE.TextureLoader(); + textureLoader.load('textures/hardwood2_diffuse.jpg', function (map) { + map.colorSpace = THREE.SRGBColorSpace; + texturePass.map = map; + }); + + cubeTexturePassP = null; + + const ldrUrls = genCubeUrls('textures/cube/pisa/', '.png'); + new THREE.CubeTextureLoader().load(ldrUrls, function (ldrCubeMap) { + cubeTexturePassP = new CubeTexturePass(cameraP, ldrCubeMap); + composer.insertPass(cubeTexturePassP, 2); + }); + + renderPass = new RenderPass(scene, cameraP); + renderPass.clear = false; + composer.addPass(renderPass); + + const outputPass = new OutputPass(); + composer.addPass(outputPass); + + const controls = new OrbitControls(cameraP, renderer.domElement); + controls.enableZoom = false; + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + const aspect = width / height; + + cameraP.aspect = aspect; + cameraP.updateProjectionMatrix(); + + renderer.setSize(width, height); + composer.setSize(width, height); +} + +function animate() { + stats.begin(); + + cameraP.updateMatrixWorld(true); + + let newColor = clearPass.clearColor; + + switch (params.clearColor) { + case 'blue': + newColor = 0x0000ff; + break; + case 'red': + newColor = 0xff0000; + break; + case 'green': + newColor = 0x00ff00; + break; + case 'white': + newColor = 0xffffff; + break; + case 'black': + newColor = 0x000000; + break; + } + + clearPass.enabled = params.clearPass; + clearPass.clearColor = newColor; + clearPass.clearAlpha = params.clearAlpha; + + texturePass.enabled = params.texturePass; + texturePass.opacity = params.texturePassOpacity; + + if (cubeTexturePassP !== null) { + cubeTexturePassP.enabled = params.cubeTexturePass; + cubeTexturePassP.opacity = params.cubeTexturePassOpacity; + } + + renderPass.enabled = params.renderPass; + + composer.render(); + + stats.end(); +} diff --git a/examples-testing/examples/webgl_postprocessing_fxaa.ts b/examples-testing/examples/webgl_postprocessing_fxaa.ts new file mode 100644 index 000000000..c5e632ad7 --- /dev/null +++ b/examples-testing/examples/webgl_postprocessing_fxaa.ts @@ -0,0 +1,122 @@ +import * as THREE from 'three'; + +import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; +import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; +import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; +import { FXAAPass } from 'three/addons/postprocessing/FXAAPass.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let camera, scene, renderer, controls, container; + +let composer1, composer2, fxaaPass; + +init(); + +function init() { + container = document.getElementById('container'); + + camera = new THREE.PerspectiveCamera(45, container.offsetWidth / container.offsetHeight, 1, 2000); + camera.position.z = 500; + + scene = new THREE.Scene(); + + // + + const hemiLight = new THREE.HemisphereLight(0xffffff, 0x8d8d8d); + hemiLight.position.set(0, 1000, 0); + scene.add(hemiLight); + + const dirLight = new THREE.DirectionalLight(0xffffff, 3); + dirLight.position.set(-3000, 1000, -1000); + scene.add(dirLight); + + // + + const geometry = new THREE.TetrahedronGeometry(10); + const material = new THREE.MeshStandardMaterial({ color: 0xf73232, flatShading: true }); + + const mesh = new THREE.InstancedMesh(geometry, material, 100); + const dummy = new THREE.Object3D(); + + for (let i = 0; i < 100; i++) { + dummy.position.x = Math.random() * 500 - 250; + dummy.position.y = Math.random() * 500 - 250; + dummy.position.z = Math.random() * 500 - 250; + + dummy.scale.setScalar(Math.random() * 2 + 1); + + dummy.rotation.x = Math.random() * Math.PI; + dummy.rotation.y = Math.random() * Math.PI; + dummy.rotation.z = Math.random() * Math.PI; + + dummy.updateMatrix(); + mesh.setMatrixAt(i, dummy.matrix); + } + + scene.add(mesh); + + // + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(container.offsetWidth, container.offsetHeight); + renderer.setAnimationLoop(animate); + renderer.autoClear = false; + controls = new OrbitControls(camera, renderer.domElement); + controls.autoRotate = true; + container.appendChild(renderer.domElement); + + // + + const renderPass = new RenderPass(scene, camera); + renderPass.clearAlpha = 0; + + // + + fxaaPass = new FXAAPass(); + + const outputPass = new OutputPass(); + + composer1 = new EffectComposer(renderer); + composer1.addPass(renderPass); + composer1.addPass(outputPass); + + // + + composer2 = new EffectComposer(renderer); + composer2.addPass(renderPass); + composer2.addPass(outputPass); + + // FXAA is engineered to be applied towards the end of engine post processing after conversion to low dynamic range and conversion to the sRGB color space for display. + + composer2.addPass(fxaaPass); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = container.offsetWidth / container.offsetHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(container.offsetWidth, container.offsetHeight); + composer1.setSize(container.offsetWidth, container.offsetHeight); + composer2.setSize(container.offsetWidth, container.offsetHeight); +} + +function animate() { + const halfWidth = container.offsetWidth / 2; + + controls.update(); + + renderer.setScissorTest(true); + + renderer.setScissor(0, 0, halfWidth - 1, container.offsetHeight); + composer1.render(); + + renderer.setScissor(halfWidth, 0, halfWidth, container.offsetHeight); + composer2.render(); + + renderer.setScissorTest(false); +} diff --git a/examples-testing/examples/webgl_postprocessing_glitch.ts b/examples-testing/examples/webgl_postprocessing_glitch.ts new file mode 100644 index 000000000..02acda572 --- /dev/null +++ b/examples-testing/examples/webgl_postprocessing_glitch.ts @@ -0,0 +1,108 @@ +import * as THREE from 'three'; + +import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; +import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; +import { GlitchPass } from 'three/addons/postprocessing/GlitchPass.js'; +import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; + +let camera, scene, renderer, composer; +let object, light; + +let glitchPass; + +const button = document.querySelector('#startButton'); +button.addEventListener('click', function () { + const overlay = document.getElementById('overlay'); + overlay.remove(); + + init(); +}); + +function updateOptions() { + const wildGlitch = document.getElementById('wildGlitch'); + glitchPass.goWild = wildGlitch.checked; +} + +function init() { + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // + + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.z = 400; + + scene = new THREE.Scene(); + scene.fog = new THREE.Fog(0x000000, 1, 1000); + + object = new THREE.Object3D(); + scene.add(object); + + const geometry = new THREE.SphereGeometry(1, 4, 4); + const material = new THREE.MeshPhongMaterial({ flatShading: true }); + + const mesh = new THREE.InstancedMesh(geometry, material, 100); + const dummy = new THREE.Object3D(); + const color = new THREE.Color(); + + for (let i = 0; i < 100; i++) { + dummy.position.set(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5).normalize(); + dummy.position.multiplyScalar(Math.random() * 400); + dummy.rotation.set(Math.random() * 2, Math.random() * 2, Math.random() * 2); + + const scale = Math.random() * 50; + dummy.scale.set(scale, scale, scale); + + dummy.updateMatrix(); + mesh.setMatrixAt(i, dummy.matrix); + + color.setHex(0xffffff * Math.random()); + mesh.setColorAt(i, color); + } + + object.add(mesh); + + scene.add(new THREE.AmbientLight(0xcccccc)); + + light = new THREE.DirectionalLight(0xffffff, 3); + light.position.set(1, 1, 1); + scene.add(light); + + // postprocessing + + composer = new EffectComposer(renderer); + composer.addPass(new RenderPass(scene, camera)); + + glitchPass = new GlitchPass(); + composer.addPass(glitchPass); + + const outputPass = new OutputPass(); + composer.addPass(outputPass); + + // + + window.addEventListener('resize', onWindowResize); + + const wildGlitchOption = document.getElementById('wildGlitch'); + wildGlitchOption.addEventListener('change', updateOptions); + + updateOptions(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + composer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + object.rotation.x += 0.005; + object.rotation.y += 0.01; + + composer.render(); +} diff --git a/examples-testing/examples/webgl_postprocessing_godrays.ts b/examples-testing/examples/webgl_postprocessing_godrays.ts new file mode 100644 index 000000000..b7c2c6661 --- /dev/null +++ b/examples-testing/examples/webgl_postprocessing_godrays.ts @@ -0,0 +1,176 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { EffectComposer, RenderPass } from 'postprocessing'; +import { GodraysPass } from 'goodrays'; + +let camera, scene, renderer, composer; +let controls, stats; + +init(); + +async function init() { + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000); + camera.position.set(-175, 50, 0); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x000000); + + // asset + + const loader = new GLTFLoader(); + const gltf = await loader.loadAsync('models/gltf/godrays_demo.glb'); + scene.add(gltf.scene); + + const pillars = gltf.scene.getObjectByName('concrete'); + pillars.material = new THREE.MeshStandardMaterial({ + color: 0x333333, + }); + + const base = gltf.scene.getObjectByName('base'); + base.material = new THREE.MeshStandardMaterial({ + color: 0x333333, + side: THREE.DoubleSide, + }); + + setupBackdrop(); + + // lights + + const lightPos = new THREE.Vector3(0, 50, 0); + const lightSphereMaterial = new THREE.MeshBasicMaterial({ + color: 0xffffff, + }); + const lightSphere = new THREE.Mesh(new THREE.SphereGeometry(0.5, 16, 16), lightSphereMaterial); + lightSphere.position.copy(lightPos); + scene.add(lightSphere); + + scene.add(new THREE.AmbientLight(0xcccccc, 0.4)); + + const pointLight = new THREE.PointLight(0xf6287d, 10000); + pointLight.castShadow = true; + pointLight.shadow.bias = -0.001; + pointLight.shadow.mapSize.width = 1024; + pointLight.shadow.mapSize.height = 1024; + pointLight.position.copy(lightPos); + scene.add(pointLight); + + // shadow setup + + scene.traverse(obj => { + if (obj.isMesh === true) { + obj.castShadow = true; + obj.receiveShadow = true; + } + }); + + lightSphere.castShadow = false; + lightSphere.receiveShadow = false; + + // + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.shadowMap.enabled = true; + document.body.appendChild(renderer.domElement); + + // + + composer = new EffectComposer(renderer, { frameBufferType: THREE.HalfFloatType }); + + const renderPass = new RenderPass(scene, camera); + composer.addPass(renderPass); + + const params = { + density: 1 / 128, + maxDensity: 0.5, + edgeStrength: 2, + edgeRadius: 2, + distanceAttenuation: 2, + color: new THREE.Color(0xf6287d), + raymarchSteps: 60, + blur: true, + gammaCorrection: true, + }; + + const godraysPass = new GodraysPass(pointLight, camera, params); + godraysPass.renderToScreen = true; + composer.addPass(godraysPass); + + // + + controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, 0.5, 0); + controls.enableDamping = true; + controls.maxDistance = 200; + controls.update(); + + // + + stats = new Stats(); + document.body.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); +} + +// + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function setupBackdrop() { + const backdropDistance = 200; + // Add backdrop walls `backdropDistance` units away from the origin + const backdropGeometry = new THREE.PlaneGeometry(400, 200); + const backdropMaterial = new THREE.MeshBasicMaterial({ + color: 0x200808, + side: THREE.DoubleSide, + }); + const backdropLeft = new THREE.Mesh(backdropGeometry, backdropMaterial); + backdropLeft.position.set(-backdropDistance, 100, 0); + backdropLeft.rotateY(Math.PI / 2); + scene.add(backdropLeft); + + const backdropRight = new THREE.Mesh(backdropGeometry, backdropMaterial); + backdropRight.position.set(backdropDistance, 100, 0); + backdropRight.rotateY(Math.PI / 2); + scene.add(backdropRight); + + const backdropFront = new THREE.Mesh(backdropGeometry, backdropMaterial); + backdropFront.position.set(0, 100, -backdropDistance); + scene.add(backdropFront); + + const backdropBack = new THREE.Mesh(backdropGeometry, backdropMaterial); + backdropBack.position.set(0, 100, backdropDistance); + scene.add(backdropBack); + + const backdropTop = new THREE.Mesh(backdropGeometry, backdropMaterial); + backdropTop.position.set(0, 200, 0); + backdropTop.rotateX(Math.PI / 2); + backdropTop.scale.set(3, 6, 1); + scene.add(backdropTop); +} + +function animate() { + controls.update(); + + stats.begin(); + + composer.render(); + + //renderer.render( scene, camera ); + + stats.end(); +} diff --git a/examples-testing/examples/webgl_postprocessing_gtao.ts b/examples-testing/examples/webgl_postprocessing_gtao.ts new file mode 100644 index 000000000..a37d3041b --- /dev/null +++ b/examples-testing/examples/webgl_postprocessing_gtao.ts @@ -0,0 +1,218 @@ +import * as THREE from 'three'; +import Stats from 'three/addons/libs/stats.module.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; +import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; +import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; +import { GTAOPass } from 'three/addons/postprocessing/GTAOPass.js'; +import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; + +let camera, scene, renderer, composer, controls, timer, stats, mixer; + +init(); + +function init() { + const dracoLoader = new DRACOLoader(); + dracoLoader.setDecoderPath('jsm/libs/draco/'); + dracoLoader.setDecoderConfig({ type: 'js' }); + const loader = new GLTFLoader(); + loader.setDRACOLoader(dracoLoader); + loader.setPath('models/gltf/'); + + timer = new THREE.Timer(); + timer.connect(document); + const container = document.createElement('div'); + document.body.appendChild(container); + + stats = new Stats(); + container.appendChild(stats.dom); + + renderer = new THREE.WebGLRenderer(); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + const pmremGenerator = new THREE.PMREMGenerator(renderer); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xbfe3dd); + scene.environment = pmremGenerator.fromScene(new RoomEnvironment(), 0.04).texture; + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 100); + camera.position.set(5, 2, 8); + + controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, 0.5, 0); + controls.update(); + controls.enablePan = false; + controls.enableDamping = true; + + const width = window.innerWidth; + const height = window.innerHeight; + + composer = new EffectComposer(renderer); + + const renderPass = new RenderPass(scene, camera); + composer.addPass(renderPass); + + const gtaoPass = new GTAOPass(scene, camera, width, height); + gtaoPass.output = GTAOPass.OUTPUT.Denoise; + composer.addPass(gtaoPass); + + const outputPass = new OutputPass(); + composer.addPass(outputPass); + + // + + loader.load( + 'LittlestTokyo.glb', + gltf => { + const model = gltf.scene; + model.position.set(1, 1, 0); + model.scale.set(0.01, 0.01, 0.01); + scene.add(model); + + mixer = new THREE.AnimationMixer(model); + mixer.clipAction(gltf.animations[0]).play(); + + const box = new THREE.Box3().setFromObject(scene); + gtaoPass.setSceneClipBox(box); + }, + undefined, + e => console.error(e), + ); + + // Init gui + const gui = new GUI(); + + gui.add(gtaoPass, 'output', { + Default: GTAOPass.OUTPUT.Default, + Diffuse: GTAOPass.OUTPUT.Diffuse, + 'AO Only': GTAOPass.OUTPUT.AO, + 'AO Only + Denoise': GTAOPass.OUTPUT.Denoise, + Depth: GTAOPass.OUTPUT.Depth, + Normal: GTAOPass.OUTPUT.Normal, + }).onChange(function (value) { + gtaoPass.output = value; + }); + + const aoParameters = { + radius: 0.25, + distanceExponent: 1, + thickness: 1, + scale: 1, + samples: 16, + distanceFallOff: 1, + screenSpaceRadius: false, + }; + const pdParameters = { + lumaPhi: 10, + depthPhi: 2, + normalPhi: 3, + radius: 4, + radiusExponent: 1, + rings: 2, + samples: 16, + }; + gtaoPass.updateGtaoMaterial(aoParameters); + gtaoPass.updatePdMaterial(pdParameters); + gui.add(gtaoPass, 'blendIntensity').min(0).max(1).step(0.01); + gui.add(aoParameters, 'radius') + .min(0.01) + .max(1) + .step(0.01) + .onChange(() => gtaoPass.updateGtaoMaterial(aoParameters)); + gui.add(aoParameters, 'distanceExponent') + .min(1) + .max(4) + .step(0.01) + .onChange(() => gtaoPass.updateGtaoMaterial(aoParameters)); + gui.add(aoParameters, 'thickness') + .min(0.01) + .max(10) + .step(0.01) + .onChange(() => gtaoPass.updateGtaoMaterial(aoParameters)); + gui.add(aoParameters, 'distanceFallOff') + .min(0) + .max(1) + .step(0.01) + .onChange(() => gtaoPass.updateGtaoMaterial(aoParameters)); + gui.add(aoParameters, 'scale') + .min(0.01) + .max(2.0) + .step(0.01) + .onChange(() => gtaoPass.updateGtaoMaterial(aoParameters)); + gui.add(aoParameters, 'samples') + .min(2) + .max(32) + .step(1) + .onChange(() => gtaoPass.updateGtaoMaterial(aoParameters)); + gui.add(aoParameters, 'screenSpaceRadius').onChange(() => gtaoPass.updateGtaoMaterial(aoParameters)); + gui.add(pdParameters, 'lumaPhi') + .min(0) + .max(20) + .step(0.01) + .onChange(() => gtaoPass.updatePdMaterial(pdParameters)); + gui.add(pdParameters, 'depthPhi') + .min(0.01) + .max(20) + .step(0.01) + .onChange(() => gtaoPass.updatePdMaterial(pdParameters)); + gui.add(pdParameters, 'normalPhi') + .min(0.01) + .max(20) + .step(0.01) + .onChange(() => gtaoPass.updatePdMaterial(pdParameters)); + gui.add(pdParameters, 'radius') + .min(0) + .max(32) + .step(1) + .onChange(() => gtaoPass.updatePdMaterial(pdParameters)); + gui.add(pdParameters, 'radiusExponent') + .min(0.1) + .max(4) + .step(0.1) + .onChange(() => gtaoPass.updatePdMaterial(pdParameters)); + gui.add(pdParameters, 'rings') + .min(1) + .max(16) + .step(0.125) + .onChange(() => gtaoPass.updatePdMaterial(pdParameters)); + gui.add(pdParameters, 'samples') + .min(2) + .max(32) + .step(1) + .onChange(() => gtaoPass.updatePdMaterial(pdParameters)); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); + composer.setSize(width, height); +} + +function animate() { + timer.update(); + + const delta = timer.getDelta(); + + if (mixer) { + mixer.update(delta); + } + + controls.update(); + + stats.begin(); + composer.render(); + stats.end(); +} diff --git a/examples-testing/examples/webgl_postprocessing_masking.ts b/examples-testing/examples/webgl_postprocessing_masking.ts new file mode 100644 index 000000000..a4d09866d --- /dev/null +++ b/examples-testing/examples/webgl_postprocessing_masking.ts @@ -0,0 +1,101 @@ +import * as THREE from 'three'; + +import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; +import { TexturePass } from 'three/addons/postprocessing/TexturePass.js'; +import { ClearPass } from 'three/addons/postprocessing/ClearPass.js'; +import { MaskPass, ClearMaskPass } from 'three/addons/postprocessing/MaskPass.js'; +import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; + +let camera, composer, renderer; +let box, torus; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.z = 10; + + const scene1 = new THREE.Scene(); + const scene2 = new THREE.Scene(); + + box = new THREE.Mesh(new THREE.BoxGeometry(4, 4, 4)); + scene1.add(box); + + torus = new THREE.Mesh(new THREE.TorusGeometry(3, 1, 16, 32)); + scene2.add(torus); + + renderer = new THREE.WebGLRenderer(); + renderer.setClearColor(0xe0e0e0); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.autoClear = false; + document.body.appendChild(renderer.domElement); + + // + + const clearPass = new ClearPass(); + + const clearMaskPass = new ClearMaskPass(); + + const maskPass1 = new MaskPass(scene1, camera); + const maskPass2 = new MaskPass(scene2, camera); + + const texture1 = new THREE.TextureLoader().load('textures/758px-Canestra_di_frutta_(Caravaggio).jpg'); + texture1.colorSpace = THREE.SRGBColorSpace; + texture1.minFilter = THREE.LinearFilter; + texture1.generateMipmaps = false; + const texture2 = new THREE.TextureLoader().load('textures/2294472375_24a3b8ef46_o.jpg'); + texture2.colorSpace = THREE.SRGBColorSpace; + + const texturePass1 = new TexturePass(texture1); + const texturePass2 = new TexturePass(texture2); + + const outputPass = new OutputPass(); + + const parameters = { + stencilBuffer: true, + }; + + const renderTarget = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight, parameters); + + composer = new EffectComposer(renderer, renderTarget); + composer.addPass(clearPass); + composer.addPass(maskPass1); + composer.addPass(texturePass1); + composer.addPass(clearMaskPass); + composer.addPass(maskPass2); + composer.addPass(texturePass2); + composer.addPass(clearMaskPass); + composer.addPass(outputPass); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); + composer.setSize(width, height); +} + +function animate() { + const time = performance.now() * 0.001 + 6000; + + box.position.x = Math.cos(time / 1.5) * 2; + box.position.y = Math.sin(time) * 2; + box.rotation.x = time; + box.rotation.y = time / 2; + + torus.position.x = Math.cos(time) * 2; + torus.position.y = Math.sin(time / 1.5) * 2; + torus.rotation.x = time; + torus.rotation.y = time / 2; + + renderer.clear(); + composer.render(time); +} diff --git a/examples-testing/examples/webgl_postprocessing_outline.ts b/examples-testing/examples/webgl_postprocessing_outline.ts new file mode 100644 index 000000000..31ef6b9b2 --- /dev/null +++ b/examples-testing/examples/webgl_postprocessing_outline.ts @@ -0,0 +1,282 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { OBJLoader } from 'three/addons/loaders/OBJLoader.js'; +import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; +import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; +import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js'; +import { OutlinePass } from 'three/addons/postprocessing/OutlinePass.js'; +import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; +import { FXAAShader } from 'three/addons/shaders/FXAAShader.js'; + +let container, stats; +let camera, scene, renderer, controls; +let composer, effectFXAA, outlinePass; + +let selectedObjects = []; + +const raycaster = new THREE.Raycaster(); +const mouse = new THREE.Vector2(); + +const obj3d = new THREE.Object3D(); +const group = new THREE.Group(); + +const params = { + edgeStrength: 3.0, + edgeGlow: 0.0, + edgeThickness: 1.0, + pulsePeriod: 0, + rotate: false, + usePatternTexture: false, +}; + +// Init gui + +const gui = new GUI({ width: 280 }); + +gui.add(params, 'edgeStrength', 0.01, 10).onChange(function (value) { + outlinePass.edgeStrength = Number(value); +}); + +gui.add(params, 'edgeGlow', 0.0, 1).onChange(function (value) { + outlinePass.edgeGlow = Number(value); +}); + +gui.add(params, 'edgeThickness', 1, 4).onChange(function (value) { + outlinePass.edgeThickness = Number(value); +}); + +gui.add(params, 'pulsePeriod', 0.0, 5).onChange(function (value) { + outlinePass.pulsePeriod = Number(value); +}); + +gui.add(params, 'rotate'); + +gui.add(params, 'usePatternTexture').onChange(function (value) { + outlinePass.usePatternTexture = value; +}); + +function Configuration() { + this.visibleEdgeColor = '#ffffff'; + this.hiddenEdgeColor = '#190a05'; +} + +const conf = new Configuration(); + +gui.addColor(conf, 'visibleEdgeColor').onChange(function (value) { + outlinePass.visibleEdgeColor.set(value); +}); + +gui.addColor(conf, 'hiddenEdgeColor').onChange(function (value) { + outlinePass.hiddenEdgeColor.set(value); +}); + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + const width = window.innerWidth; + const height = window.innerHeight; + + renderer = new THREE.WebGLRenderer(); + renderer.shadowMap.enabled = true; + // todo - support pixelRatio in this demo + renderer.setSize(width, height); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 100); + camera.position.set(0, 0, 8); + + controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 5; + controls.maxDistance = 20; + controls.enablePan = false; + controls.enableDamping = true; + controls.dampingFactor = 0.05; + + // + + scene.add(new THREE.AmbientLight(0xaaaaaa, 0.6)); + + const light = new THREE.DirectionalLight(0xddffdd, 2); + light.position.set(5, 5, 5); + light.castShadow = true; + light.shadow.mapSize.width = 1024; + light.shadow.mapSize.height = 1024; + + const d = 10; + + light.shadow.camera.left = -d; + light.shadow.camera.right = d; + light.shadow.camera.top = d; + light.shadow.camera.bottom = -d; + light.shadow.camera.far = 25; + + scene.add(light); + + // model + + const loader = new OBJLoader(); + loader.load('models/obj/tree.obj', function (object) { + let scale = 1.0; + + object.traverse(function (child) { + if (child instanceof THREE.Mesh) { + child.geometry.center(); + child.geometry.computeBoundingSphere(); + scale = 0.2 * child.geometry.boundingSphere.radius; + + const phongMaterial = new THREE.MeshPhongMaterial({ + color: 0xffffff, + specular: 0x111111, + shininess: 5, + }); + child.material = phongMaterial; + child.receiveShadow = true; + child.castShadow = true; + } + }); + + object.position.y = 1; + object.scale.divideScalar(scale); + obj3d.add(object); + }); + + scene.add(group); + + group.add(obj3d); + + // + + const geometry = new THREE.SphereGeometry(3, 48, 24); + + for (let i = 0; i < 20; i++) { + const material = new THREE.MeshLambertMaterial(); + material.color.setHSL(Math.random(), 1.0, 0.3); + + const mesh = new THREE.Mesh(geometry, material); + mesh.position.x = Math.random() * 4 - 2; + mesh.position.y = Math.random() * 4 - 2; + mesh.position.z = Math.random() * 4 - 2; + mesh.receiveShadow = true; + mesh.castShadow = true; + mesh.scale.multiplyScalar(Math.random() * 0.3 + 0.1); + group.add(mesh); + } + + const floorMaterial = new THREE.MeshLambertMaterial({ side: THREE.DoubleSide }); + + const floorGeometry = new THREE.PlaneGeometry(12, 12); + const floorMesh = new THREE.Mesh(floorGeometry, floorMaterial); + floorMesh.rotation.x -= Math.PI * 0.5; + floorMesh.position.y -= 1.5; + group.add(floorMesh); + floorMesh.receiveShadow = true; + + const torusGeometry = new THREE.TorusGeometry(1, 0.3, 16, 100); + const torusMaterial = new THREE.MeshPhongMaterial({ color: 0xffaaff }); + const torus = new THREE.Mesh(torusGeometry, torusMaterial); + torus.position.z = -4; + group.add(torus); + torus.receiveShadow = true; + torus.castShadow = true; + + // + + stats = new Stats(); + container.appendChild(stats.dom); + + // postprocessing + + composer = new EffectComposer(renderer); + + const renderPass = new RenderPass(scene, camera); + composer.addPass(renderPass); + + outlinePass = new OutlinePass(new THREE.Vector2(window.innerWidth, window.innerHeight), scene, camera); + composer.addPass(outlinePass); + + const textureLoader = new THREE.TextureLoader(); + textureLoader.load('textures/tri_pattern.jpg', function (texture) { + outlinePass.patternTexture = texture; + texture.wrapS = THREE.RepeatWrapping; + texture.wrapT = THREE.RepeatWrapping; + }); + + const outputPass = new OutputPass(); + composer.addPass(outputPass); + + effectFXAA = new ShaderPass(FXAAShader); + effectFXAA.uniforms['resolution'].value.set(1 / window.innerWidth, 1 / window.innerHeight); + composer.addPass(effectFXAA); + + window.addEventListener('resize', onWindowResize); + + renderer.domElement.style.touchAction = 'none'; + renderer.domElement.addEventListener('pointermove', onPointerMove); + + function onPointerMove(event) { + if (event.isPrimary === false) return; + + mouse.x = (event.clientX / window.innerWidth) * 2 - 1; + mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; + + checkIntersection(); + } + + function addSelectedObject(object) { + selectedObjects = []; + selectedObjects.push(object); + } + + function checkIntersection() { + raycaster.setFromCamera(mouse, camera); + + const intersects = raycaster.intersectObject(scene, true); + + if (intersects.length > 0) { + const selectedObject = intersects[0].object; + addSelectedObject(selectedObject); + outlinePass.selectedObjects = selectedObjects; + } else { + // outlinePass.selectedObjects = []; + } + } +} + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); + composer.setSize(width, height); + + effectFXAA.uniforms['resolution'].value.set(1 / window.innerWidth, 1 / window.innerHeight); +} + +function animate() { + stats.begin(); + + const timer = performance.now(); + + if (params.rotate) { + group.rotation.y = timer * 0.0001; + } + + controls.update(); + + composer.render(); + + stats.end(); +} diff --git a/examples-testing/examples/webgl_postprocessing_pixel.ts b/examples-testing/examples/webgl_postprocessing_pixel.ts new file mode 100644 index 000000000..04aec4816 --- /dev/null +++ b/examples-testing/examples/webgl_postprocessing_pixel.ts @@ -0,0 +1,231 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; +import { RenderPixelatedPass } from 'three/addons/postprocessing/RenderPixelatedPass.js'; +import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let camera, scene, renderer, composer, crystalMesh, timer; +let gui, params; + +init(); + +function init() { + const aspectRatio = window.innerWidth / window.innerHeight; + + camera = new THREE.OrthographicCamera(-aspectRatio, aspectRatio, 1, -1, 0.1, 10); + camera.position.y = 2 * Math.tan(Math.PI / 6); + camera.position.z = 2; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x151729); + + timer = new THREE.Timer(); + timer.connect(document); + + renderer = new THREE.WebGLRenderer(); + renderer.shadowMap.enabled = true; + //renderer.setPixelRatio( window.devicePixelRatio ); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + composer = new EffectComposer(renderer); + const renderPixelatedPass = new RenderPixelatedPass(6, scene, camera); + composer.addPass(renderPixelatedPass); + + const outputPass = new OutputPass(); + composer.addPass(outputPass); + + window.addEventListener('resize', onWindowResize); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.maxZoom = 2; + + // gui + + gui = new GUI(); + params = { pixelSize: 6, normalEdgeStrength: 0.3, depthEdgeStrength: 0.4, pixelAlignedPanning: true }; + gui.add(params, 'pixelSize') + .min(1) + .max(16) + .step(1) + .onChange(() => { + renderPixelatedPass.setPixelSize(params.pixelSize); + }); + gui.add(renderPixelatedPass, 'normalEdgeStrength').min(0).max(2).step(0.05); + gui.add(renderPixelatedPass, 'depthEdgeStrength').min(0).max(1).step(0.05); + gui.add(params, 'pixelAlignedPanning'); + + // textures + + const loader = new THREE.TextureLoader(); + const texChecker = pixelTexture(loader.load('textures/checker.png')); + const texChecker2 = pixelTexture(loader.load('textures/checker.png')); + texChecker.repeat.set(3, 3); + texChecker2.repeat.set(1.5, 1.5); + + // meshes + + const boxMaterial = new THREE.MeshPhongMaterial({ map: texChecker2 }); + + function addBox(boxSideLength, x, z, rotation) { + const mesh = new THREE.Mesh(new THREE.BoxGeometry(boxSideLength, boxSideLength, boxSideLength), boxMaterial); + mesh.castShadow = true; + mesh.receiveShadow = true; + mesh.rotation.y = rotation; + mesh.position.y = boxSideLength / 2; + mesh.position.set(x, boxSideLength / 2 + 0.0001, z); + scene.add(mesh); + return mesh; + } + + addBox(0.4, 0, 0, Math.PI / 4); + addBox(0.5, -0.5, -0.5, Math.PI / 4); + + const planeSideLength = 2; + const planeMesh = new THREE.Mesh( + new THREE.PlaneGeometry(planeSideLength, planeSideLength), + new THREE.MeshPhongMaterial({ map: texChecker }), + ); + planeMesh.receiveShadow = true; + planeMesh.rotation.x = -Math.PI / 2; + scene.add(planeMesh); + + const radius = 0.2; + const geometry = new THREE.IcosahedronGeometry(radius); + crystalMesh = new THREE.Mesh( + geometry, + new THREE.MeshPhongMaterial({ + color: 0x68b7e9, + emissive: 0x4f7e8b, + shininess: 10, + specular: 0xffffff, + }), + ); + crystalMesh.receiveShadow = true; + crystalMesh.castShadow = true; + scene.add(crystalMesh); + + // lights + + scene.add(new THREE.AmbientLight(0x757f8e, 3)); + + const directionalLight = new THREE.DirectionalLight(0xfffecd, 1.5); + directionalLight.position.set(100, 100, 100); + directionalLight.castShadow = true; + directionalLight.shadow.mapSize.set(2048, 2048); + scene.add(directionalLight); + + const spotLight = new THREE.SpotLight(0xffc100, 10, 10, Math.PI / 16, 0.02, 2); + spotLight.position.set(2, 2, 0); + const target = spotLight.target; + scene.add(target); + target.position.set(0, 0, 0); + spotLight.castShadow = true; + scene.add(spotLight); +} + +function onWindowResize() { + const aspectRatio = window.innerWidth / window.innerHeight; + camera.left = -aspectRatio; + camera.right = aspectRatio; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + composer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + const t = timer.getElapsed(); + + crystalMesh.material.emissiveIntensity = Math.sin(t * 3) * 0.5 + 0.5; + crystalMesh.position.y = 0.7 + Math.sin(t * 2) * 0.05; + crystalMesh.rotation.y = stopGoEased(t, 2, 4) * 2 * Math.PI; + + const rendererSize = renderer.getSize(new THREE.Vector2()); + const aspectRatio = rendererSize.x / rendererSize.y; + if (params['pixelAlignedPanning']) { + pixelAlignFrustum( + camera, + aspectRatio, + Math.floor(rendererSize.x / params['pixelSize']), + Math.floor(rendererSize.y / params['pixelSize']), + ); + } else if (camera.left != -aspectRatio || camera.top != 1.0) { + // Reset the Camera Frustum if it has been modified + camera.left = -aspectRatio; + camera.right = aspectRatio; + camera.top = 1.0; + camera.bottom = -1.0; + camera.updateProjectionMatrix(); + } + + composer.render(); +} + +// Helper functions + +function pixelTexture(texture) { + texture.minFilter = THREE.NearestFilter; + texture.magFilter = THREE.NearestFilter; + texture.generateMipmaps = false; + texture.wrapS = THREE.RepeatWrapping; + texture.wrapT = THREE.RepeatWrapping; + texture.colorSpace = THREE.SRGBColorSpace; + return texture; +} + +function easeInOutCubic(x) { + return x ** 2 * 3 - x ** 3 * 2; +} + +function linearStep(x, edge0, edge1) { + const w = edge1 - edge0; + const m = 1 / w; + const y0 = -m * edge0; + return THREE.MathUtils.clamp(y0 + m * x, 0, 1); +} + +function stopGoEased(x, downtime, period) { + const cycle = (x / period) | 0; + const tween = x - cycle * period; + const linStep = easeInOutCubic(linearStep(tween, downtime, period)); + return cycle + linStep; +} + +function pixelAlignFrustum(camera, aspectRatio, pixelsPerScreenWidth, pixelsPerScreenHeight) { + // 0. Get Pixel Grid Units + const worldScreenWidth = (camera.right - camera.left) / camera.zoom; + const worldScreenHeight = (camera.top - camera.bottom) / camera.zoom; + const pixelWidth = worldScreenWidth / pixelsPerScreenWidth; + const pixelHeight = worldScreenHeight / pixelsPerScreenHeight; + + // 1. Project the current camera position along its local rotation bases + const camPos = new THREE.Vector3(); + camera.getWorldPosition(camPos); + const camRot = new THREE.Quaternion(); + camera.getWorldQuaternion(camRot); + const camRight = new THREE.Vector3(1.0, 0.0, 0.0).applyQuaternion(camRot); + const camUp = new THREE.Vector3(0.0, 1.0, 0.0).applyQuaternion(camRot); + const camPosRight = camPos.dot(camRight); + const camPosUp = camPos.dot(camUp); + + // 2. Find how far along its position is along these bases in pixel units + const camPosRightPx = camPosRight / pixelWidth; + const camPosUpPx = camPosUp / pixelHeight; + + // 3. Find the fractional pixel units and convert to world units + const fractX = camPosRightPx - Math.round(camPosRightPx); + const fractY = camPosUpPx - Math.round(camPosUpPx); + + // 4. Add fractional world units to the left/right top/bottom to align with the pixel grid + camera.left = -aspectRatio - fractX * pixelWidth; + camera.right = aspectRatio - fractX * pixelWidth; + camera.top = 1.0 - fractY * pixelHeight; + camera.bottom = -1.0 - fractY * pixelHeight; + camera.updateProjectionMatrix(); +} diff --git a/examples-testing/examples/webgl_postprocessing_procedural.ts b/examples-testing/examples/webgl_postprocessing_procedural.ts new file mode 100644 index 000000000..869824270 --- /dev/null +++ b/examples-testing/examples/webgl_postprocessing_procedural.ts @@ -0,0 +1,77 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let postCamera, postScene, renderer; +let postMaterial, noiseRandom1DMaterial, noiseRandom2DMaterial, noiseRandom3DMaterial, postQuad; +let stats; + +const params = { procedure: 'noiseRandom3D' }; + +init(); + +function init() { + const container = document.getElementById('container'); + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + stats = new Stats(); + container.appendChild(stats.dom); + + // Setup post processing stage + postCamera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1); + noiseRandom1DMaterial = new THREE.ShaderMaterial({ + vertexShader: document.querySelector('#procedural-vert').textContent.trim(), + fragmentShader: document.querySelector('#noiseRandom1D-frag').textContent.trim(), + }); + noiseRandom2DMaterial = new THREE.ShaderMaterial({ + vertexShader: document.querySelector('#procedural-vert').textContent.trim(), + fragmentShader: document.querySelector('#noiseRandom2D-frag').textContent.trim(), + }); + noiseRandom3DMaterial = new THREE.ShaderMaterial({ + vertexShader: document.querySelector('#procedural-vert').textContent.trim(), + fragmentShader: document.querySelector('#noiseRandom3D-frag').textContent.trim(), + }); + postMaterial = noiseRandom3DMaterial; + const postPlane = new THREE.PlaneGeometry(2, 2); + postQuad = new THREE.Mesh(postPlane, postMaterial); + postScene = new THREE.Scene(); + postScene.add(postQuad); + + window.addEventListener('resize', onWindowResize); + + // + + const gui = new GUI(); + gui.add(params, 'procedure', ['noiseRandom1D', 'noiseRandom2D', 'noiseRandom3D']); +} + +function onWindowResize() { + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + switch (params.procedure) { + case 'noiseRandom1D': + postMaterial = noiseRandom1DMaterial; + break; + case 'noiseRandom2D': + postMaterial = noiseRandom2DMaterial; + break; + case 'noiseRandom3D': + postMaterial = noiseRandom3DMaterial; + break; + } + + postQuad.material = postMaterial; + + // render post FX + renderer.render(postScene, postCamera); + + stats.update(); +} diff --git a/examples-testing/examples/webgl_postprocessing_rgb_halftone.ts b/examples-testing/examples/webgl_postprocessing_rgb_halftone.ts new file mode 100644 index 000000000..5a40f9793 --- /dev/null +++ b/examples-testing/examples/webgl_postprocessing_rgb_halftone.ts @@ -0,0 +1,170 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; +import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; +import { HalftonePass } from 'three/addons/postprocessing/HalftonePass.js'; + +let renderer, timer, camera, stats; + +const rotationSpeed = Math.PI / 64; + +let composer, group; + +init(); + +function init() { + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + + timer = new THREE.Timer(); + timer.connect(document); + + camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.z = 12; + + stats = new Stats(); + + document.body.appendChild(renderer.domElement); + document.body.appendChild(stats.dom); + + // camera controls + const controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, 0, 0); + controls.update(); + + // scene + + const scene = new THREE.Scene(); + scene.background = new THREE.Color(0x444444); + + group = new THREE.Group(); + const floor = new THREE.Mesh(new THREE.BoxGeometry(100, 1, 100), new THREE.MeshPhongMaterial({})); + floor.position.y = -10; + const light = new THREE.PointLight(0xffffff, 250); + light.position.y = 2; + group.add(floor, light); + scene.add(group); + + const mat = new THREE.ShaderMaterial({ + uniforms: {}, + + vertexShader: [ + 'varying vec2 vUV;', + 'varying vec3 vNormal;', + + 'void main() {', + + 'vUV = uv;', + 'vNormal = vec3( normal );', + 'gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );', + + '}', + ].join('\n'), + + fragmentShader: [ + 'varying vec2 vUV;', + 'varying vec3 vNormal;', + + 'void main() {', + + 'vec4 c = vec4( abs( vNormal ) + vec3( vUV, 0.0 ), 0.0 );', + 'gl_FragColor = c;', + + '}', + ].join('\n'), + }); + + for (let i = 0; i < 50; ++i) { + // fill scene with coloured cubes + const mesh = new THREE.Mesh(new THREE.BoxGeometry(2, 2, 2), mat); + mesh.position.set(Math.random() * 16 - 8, Math.random() * 16 - 8, Math.random() * 16 - 8); + mesh.rotation.set(Math.random() * Math.PI * 2, Math.random() * Math.PI * 2, Math.random() * Math.PI * 2); + group.add(mesh); + } + + // post-processing + + composer = new EffectComposer(renderer); + const renderPass = new RenderPass(scene, camera); + const params = { + shape: 1, + radius: 4, + rotateR: Math.PI / 12, + rotateB: (Math.PI / 12) * 2, + rotateG: (Math.PI / 12) * 3, + scatter: 0, + blending: 1, + blendingMode: 1, + greyscale: false, + disable: false, + }; + const halftonePass = new HalftonePass(params); + composer.addPass(renderPass); + composer.addPass(halftonePass); + + window.onresize = function () { + // resize composer + renderer.setSize(window.innerWidth, window.innerHeight); + composer.setSize(window.innerWidth, window.innerHeight); + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + }; + + // GUI + + const controller = { + radius: halftonePass.uniforms['radius'].value, + rotateR: halftonePass.uniforms['rotateR'].value / (Math.PI / 180), + rotateG: halftonePass.uniforms['rotateG'].value / (Math.PI / 180), + rotateB: halftonePass.uniforms['rotateB'].value / (Math.PI / 180), + scatter: halftonePass.uniforms['scatter'].value, + shape: halftonePass.uniforms['shape'].value, + greyscale: halftonePass.uniforms['greyscale'].value, + blending: halftonePass.uniforms['blending'].value, + blendingMode: halftonePass.uniforms['blendingMode'].value, + disable: halftonePass.uniforms['disable'].value, + }; + + function onGUIChange() { + // update uniforms + halftonePass.uniforms['radius'].value = controller.radius; + halftonePass.uniforms['rotateR'].value = controller.rotateR * (Math.PI / 180); + halftonePass.uniforms['rotateG'].value = controller.rotateG * (Math.PI / 180); + halftonePass.uniforms['rotateB'].value = controller.rotateB * (Math.PI / 180); + halftonePass.uniforms['scatter'].value = controller.scatter; + halftonePass.uniforms['shape'].value = controller.shape; + halftonePass.uniforms['greyscale'].value = controller.greyscale; + halftonePass.uniforms['blending'].value = controller.blending; + halftonePass.uniforms['blendingMode'].value = controller.blendingMode; + halftonePass.uniforms['disable'].value = controller.disable; + } + + const gui = new GUI(); + gui.add(controller, 'shape', { Dot: 1, Ellipse: 2, Line: 3, Square: 4, Diamond: 5 }).onChange(onGUIChange); + gui.add(controller, 'radius', 1, 25).onChange(onGUIChange); + gui.add(controller, 'rotateR', 0, 90).onChange(onGUIChange); + gui.add(controller, 'rotateG', 0, 90).onChange(onGUIChange); + gui.add(controller, 'rotateB', 0, 90).onChange(onGUIChange); + gui.add(controller, 'scatter', 0, 1, 0.01).onChange(onGUIChange); + gui.add(controller, 'greyscale').onChange(onGUIChange); + gui.add(controller, 'blending', 0, 1, 0.01).onChange(onGUIChange); + gui.add(controller, 'blendingMode', { Linear: 1, Multiply: 2, Add: 3, Lighter: 4, Darker: 5 }).onChange( + onGUIChange, + ); + gui.add(controller, 'disable').onChange(onGUIChange); +} + +function animate() { + timer.update(); + + const delta = timer.getDelta(); + stats.update(); + group.rotation.y += delta * rotationSpeed; + composer.render(delta); +} diff --git a/examples-testing/examples/webgl_postprocessing_sao.ts b/examples-testing/examples/webgl_postprocessing_sao.ts new file mode 100644 index 000000000..0c6298e6b --- /dev/null +++ b/examples-testing/examples/webgl_postprocessing_sao.ts @@ -0,0 +1,146 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; +import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; +import { SAOPass } from 'three/addons/postprocessing/SAOPass.js'; +import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; + +let container, stats; +let camera, scene, renderer; +let composer, renderPass, saoPass; +let group; + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + const width = window.innerWidth; + const height = window.innerHeight; + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(width, height); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + camera = new THREE.PerspectiveCamera(65, width / height, 3, 10); + camera.position.z = 7; + + scene = new THREE.Scene(); + + group = new THREE.Object3D(); + scene.add(group); + + const light = new THREE.PointLight(0xefffef, 500); + light.position.z = 10; + light.position.y = -10; + light.position.x = -10; + scene.add(light); + + const light2 = new THREE.PointLight(0xffefef, 500); + light2.position.z = 10; + light2.position.x = -10; + light2.position.y = 10; + scene.add(light2); + + const light3 = new THREE.PointLight(0xefefff, 500); + light3.position.z = 10; + light3.position.x = 10; + light3.position.y = -10; + scene.add(light3); + + const light4 = new THREE.AmbientLight(0xffffff, 0.2); + scene.add(light4); + + const geometry = new THREE.SphereGeometry(3, 48, 24); + const material = new THREE.MeshStandardMaterial(); + material.roughness = 0.5; + material.metalness = 0; + + const mesh = new THREE.InstancedMesh(geometry, material, 120); + const dummy = new THREE.Object3D(); + const color = new THREE.Color(); + + for (let i = 0; i < 120; i++) { + dummy.position.x = Math.random() * 4 - 2; + dummy.position.y = Math.random() * 4 - 2; + dummy.position.z = Math.random() * 4 - 2; + dummy.rotation.x = Math.random(); + dummy.rotation.y = Math.random(); + dummy.rotation.z = Math.random(); + + const scale = Math.random() * 0.2 + 0.05; + dummy.scale.set(scale, scale, scale); + + dummy.updateMatrix(); + mesh.setMatrixAt(i, dummy.matrix); + + color.setHSL(Math.random(), 1.0, 0.3); + mesh.setColorAt(i, color); + } + + group.add(mesh); + + stats = new Stats(); + container.appendChild(stats.dom); + + composer = new EffectComposer(renderer); + renderPass = new RenderPass(scene, camera); + composer.addPass(renderPass); + saoPass = new SAOPass(scene, camera); + composer.addPass(saoPass); + const outputPass = new OutputPass(); + composer.addPass(outputPass); + + // Init gui + const gui = new GUI(); + gui.add(saoPass.params, 'output', { + Default: SAOPass.OUTPUT.Default, + 'SAO Only': SAOPass.OUTPUT.SAO, + Normal: SAOPass.OUTPUT.Normal, + }).onChange(function (value) { + saoPass.params.output = value; + }); + gui.add(saoPass.params, 'saoBias', -1, 1); + gui.add(saoPass.params, 'saoIntensity', 0, 1); + gui.add(saoPass.params, 'saoScale', 0, 10); + gui.add(saoPass.params, 'saoKernelRadius', 1, 100); + gui.add(saoPass.params, 'saoMinResolution', 0, 1); + gui.add(saoPass.params, 'saoBlur'); + gui.add(saoPass.params, 'saoBlurRadius', 0, 200); + gui.add(saoPass.params, 'saoBlurStdDev', 0.5, 150); + gui.add(saoPass.params, 'saoBlurDepthCutoff', 0.0, 0.1); + gui.add(saoPass, 'enabled'); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + const width = window.innerWidth || 1; + const height = window.innerHeight || 1; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + renderer.setSize(width, height); + + composer.setSize(width, height); +} + +function animate() { + stats.begin(); + render(); + stats.end(); +} + +function render() { + const timer = performance.now(); + group.rotation.x = timer * 0.0002; + group.rotation.y = timer * 0.0001; + + composer.render(); +} diff --git a/examples-testing/examples/webgl_postprocessing_smaa.ts b/examples-testing/examples/webgl_postprocessing_smaa.ts new file mode 100644 index 000000000..9e73d38b0 --- /dev/null +++ b/examples-testing/examples/webgl_postprocessing_smaa.ts @@ -0,0 +1,106 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; +import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; +import { SMAAPass } from 'three/addons/postprocessing/SMAAPass.js'; +import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; + +let camera, scene, renderer, composer, stats, smaaPass; + +const params = { + enabled: true, + autoRotate: true, +}; + +init(); + +function init() { + const container = document.getElementById('container'); + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.z = 300; + + scene = new THREE.Scene(); + + const geometry = new THREE.BoxGeometry(120, 120, 120); + const material1 = new THREE.MeshBasicMaterial({ color: 0xffffff, wireframe: true }); + + const mesh1 = new THREE.Mesh(geometry, material1); + mesh1.position.x = -100; + scene.add(mesh1); + + const texture = new THREE.TextureLoader().load('textures/brick_diffuse.jpg'); + texture.anisotropy = renderer.capabilities.getMaxAnisotropy(); + texture.colorSpace = THREE.SRGBColorSpace; + + const material2 = new THREE.MeshBasicMaterial({ map: texture }); + + const mesh2 = new THREE.Mesh(geometry, material2); + mesh2.position.x = 100; + scene.add(mesh2); + + // postprocessing + + composer = new EffectComposer(renderer); + composer.addPass(new RenderPass(scene, camera)); + + smaaPass = new SMAAPass(); + composer.addPass(smaaPass); + + const outputPass = new OutputPass(); + composer.addPass(outputPass); + + window.addEventListener('resize', onWindowResize); + + const gui = new GUI(); + + const smaaFolder = gui.addFolder('SMAA'); + smaaFolder.add(params, 'enabled'); + + const sceneFolder = gui.addFolder('Scene'); + sceneFolder.add(params, 'autoRotate'); +} + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); + composer.setSize(width, height); +} + +function animate() { + stats.begin(); + + if (params.autoRotate === true) { + for (let i = 0; i < scene.children.length; i++) { + const child = scene.children[i]; + + child.rotation.x += 0.005; + child.rotation.y += 0.01; + } + } + + smaaPass.enabled = params.enabled; + + composer.render(); + + stats.end(); +} diff --git a/examples-testing/examples/webgl_postprocessing_sobel.ts b/examples-testing/examples/webgl_postprocessing_sobel.ts new file mode 100644 index 000000000..55d88dc02 --- /dev/null +++ b/examples-testing/examples/webgl_postprocessing_sobel.ts @@ -0,0 +1,111 @@ +import * as THREE from 'three'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; +import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; +import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js'; + +import { LuminosityShader } from 'three/addons/shaders/LuminosityShader.js'; +import { SobelOperatorShader } from 'three/addons/shaders/SobelOperatorShader.js'; + +let camera, scene, renderer, composer; + +let effectSobel; + +const params = { + enable: true, +}; + +init(); + +function init() { + // + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(0, 1, 3); + camera.lookAt(scene.position); + + // + + const geometry = new THREE.TorusKnotGeometry(1, 0.3, 256, 32); + const material = new THREE.MeshPhongMaterial({ color: 0xffff00 }); + + const mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + // + + const ambientLight = new THREE.AmbientLight(0xe7e7e7); + scene.add(ambientLight); + + const pointLight = new THREE.PointLight(0xffffff, 20); + camera.add(pointLight); + scene.add(camera); + + // + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // postprocessing + + composer = new EffectComposer(renderer); + const renderPass = new RenderPass(scene, camera); + composer.addPass(renderPass); + + // color to grayscale conversion + + const effectGrayScale = new ShaderPass(LuminosityShader); + composer.addPass(effectGrayScale); + + // you might want to use a gaussian blur filter before + // the next pass to improve the result of the Sobel operator + + // Sobel operator + + effectSobel = new ShaderPass(SobelOperatorShader); + effectSobel.uniforms['resolution'].value.x = window.innerWidth * window.devicePixelRatio; + effectSobel.uniforms['resolution'].value.y = window.innerHeight * window.devicePixelRatio; + composer.addPass(effectSobel); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.enableZoom = false; + + // + + const gui = new GUI(); + + gui.add(params, 'enable'); + gui.open(); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + composer.setSize(window.innerWidth, window.innerHeight); + + effectSobel.uniforms['resolution'].value.x = window.innerWidth * window.devicePixelRatio; + effectSobel.uniforms['resolution'].value.y = window.innerHeight * window.devicePixelRatio; +} + +function animate() { + if (params.enable === true) { + composer.render(); + } else { + renderer.render(scene, camera); + } +} diff --git a/examples-testing/examples/webgl_postprocessing_ssaa.ts b/examples-testing/examples/webgl_postprocessing_ssaa.ts new file mode 100644 index 000000000..45d8767b1 --- /dev/null +++ b/examples-testing/examples/webgl_postprocessing_ssaa.ts @@ -0,0 +1,214 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; +import { SSAARenderPass } from 'three/addons/postprocessing/SSAARenderPass.js'; +import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; + +let scene, renderer, composer; +let cameraP, ssaaRenderPassP; +let cameraO, ssaaRenderPassO; +let gui, stats; + +const params = { + sampleLevel: 4, + unbiased: true, + camera: 'perspective', + clearColor: 'black', + clearAlpha: 1.0, + viewOffsetX: 0, + autoRotate: true, +}; + +init(); + +clearGui(); + +function clearGui() { + if (gui) gui.destroy(); + + gui = new GUI(); + + gui.add(params, 'unbiased'); + gui.add(params, 'sampleLevel', { + 'Level 0: 1 Sample': 0, + 'Level 1: 2 Samples': 1, + 'Level 2: 4 Samples': 2, + 'Level 3: 8 Samples': 3, + 'Level 4: 16 Samples': 4, + 'Level 5: 32 Samples': 5, + }); + gui.add(params, 'camera', ['perspective', 'orthographic']); + gui.add(params, 'clearColor', ['black', 'white', 'blue', 'green', 'red']); + gui.add(params, 'clearAlpha', 0, 1); + gui.add(params, 'viewOffsetX', -100, 100); + gui.add(params, 'autoRotate'); + + gui.open(); +} + +function init() { + const container = document.getElementById('container'); + + const width = window.innerWidth || 1; + const height = window.innerHeight || 1; + const aspect = width / height; + const devicePixelRatio = window.devicePixelRatio || 1; + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(devicePixelRatio); + renderer.setSize(width, height); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + stats = new Stats(); + container.appendChild(stats.dom); + + cameraP = new THREE.PerspectiveCamera(65, aspect, 3, 10); + cameraP.position.z = 7; + cameraP.setViewOffset(width, height, params.viewOffsetX, 0, width, height); + + cameraO = new THREE.OrthographicCamera(width / -2, width / 2, height / 2, height / -2, 3, 10); + cameraO.position.z = 7; + + const fov = THREE.MathUtils.degToRad(cameraP.fov); + const hyperfocus = (cameraP.near + cameraP.far) / 2; + const _height = 2 * Math.tan(fov / 2) * hyperfocus; + cameraO.zoom = height / _height; + + scene = new THREE.Scene(); + + const group = new THREE.Group(); + scene.add(group); + + const light = new THREE.PointLight(0xefffef, 500); + light.position.z = 10; + light.position.y = -10; + light.position.x = -10; + scene.add(light); + + const light2 = new THREE.PointLight(0xffefef, 500); + light2.position.z = 10; + light2.position.x = -10; + light2.position.y = 10; + scene.add(light2); + + const light3 = new THREE.PointLight(0xefefff, 500); + light3.position.z = 10; + light3.position.x = 10; + light3.position.y = -10; + scene.add(light3); + + const light4 = new THREE.AmbientLight(0xffffff, 0.2); + scene.add(light4); + + const geometry = new THREE.SphereGeometry(3, 48, 24); + const material = new THREE.MeshStandardMaterial(); + material.roughness = 0.5; + material.metalness = 0; + + const mesh = new THREE.InstancedMesh(geometry, material, 120); + const dummy = new THREE.Object3D(); + const color = new THREE.Color(); + + for (let i = 0; i < 120; i++) { + dummy.position.x = Math.random() * 4 - 2; + dummy.position.y = Math.random() * 4 - 2; + dummy.position.z = Math.random() * 4 - 2; + dummy.rotation.x = Math.random(); + dummy.rotation.y = Math.random(); + dummy.rotation.z = Math.random(); + + dummy.scale.setScalar(Math.random() * 0.2 + 0.05); + + dummy.updateMatrix(); + mesh.setMatrixAt(i, dummy.matrix); + + color.setHSL(Math.random(), 1.0, 0.3); + mesh.setColorAt(i, color); + } + + group.add(mesh); + + // postprocessing + + composer = new EffectComposer(renderer); + composer.setPixelRatio(1); // ensure pixel ratio is always 1 for performance reasons + ssaaRenderPassP = new SSAARenderPass(scene, cameraP); + composer.addPass(ssaaRenderPassP); + ssaaRenderPassO = new SSAARenderPass(scene, cameraO); + composer.addPass(ssaaRenderPassO); + const outputPass = new OutputPass(); + composer.addPass(outputPass); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + const aspect = width / height; + + cameraP.aspect = aspect; + cameraP.setViewOffset(width, height, params.viewOffsetX, 0, width, height); + cameraO.updateProjectionMatrix(); + + cameraO.left = -height * aspect; + cameraO.right = height * aspect; + cameraO.top = height; + cameraO.bottom = -height; + cameraO.updateProjectionMatrix(); + + renderer.setSize(width, height); + composer.setSize(width, height); +} + +function animate() { + stats.begin(); + + if (params.autoRotate) { + for (let i = 0; i < scene.children.length; i++) { + const child = scene.children[i]; + + child.rotation.x += 0.005; + child.rotation.y += 0.01; + } + } + + let newColor = ssaaRenderPassP.clearColor; + + switch (params.clearColor) { + case 'blue': + newColor = 0x0000ff; + break; + case 'red': + newColor = 0xff0000; + break; + case 'green': + newColor = 0x00ff00; + break; + case 'white': + newColor = 0xffffff; + break; + case 'black': + newColor = 0x000000; + break; + } + + ssaaRenderPassP.clearColor = ssaaRenderPassO.clearColor = newColor; + ssaaRenderPassP.clearAlpha = ssaaRenderPassO.clearAlpha = params.clearAlpha; + + ssaaRenderPassP.sampleLevel = ssaaRenderPassO.sampleLevel = params.sampleLevel; + ssaaRenderPassP.unbiased = ssaaRenderPassO.unbiased = params.unbiased; + + ssaaRenderPassP.enabled = params.camera === 'perspective'; + ssaaRenderPassO.enabled = params.camera === 'orthographic'; + + cameraP.view.offsetX = params.viewOffsetX; + + composer.render(); + + stats.end(); +} diff --git a/examples-testing/examples/webgl_postprocessing_ssao.ts b/examples-testing/examples/webgl_postprocessing_ssao.ts new file mode 100644 index 000000000..fd3739af3 --- /dev/null +++ b/examples-testing/examples/webgl_postprocessing_ssao.ts @@ -0,0 +1,125 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; +import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; +import { SSAOPass } from 'three/addons/postprocessing/SSAOPass.js'; +import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; + +let container, stats; +let camera, scene, renderer; +let composer; +let group; + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + renderer = new THREE.WebGLRenderer(); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + camera = new THREE.PerspectiveCamera(65, window.innerWidth / window.innerHeight, 100, 700); + camera.position.z = 500; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xaaaaaa); + + scene.add(new THREE.DirectionalLight(0xffffff, 4)); + scene.add(new THREE.AmbientLight(0xffffff)); + + group = new THREE.Group(); + scene.add(group); + + const geometry = new THREE.BoxGeometry(10, 10, 10); + const material = new THREE.MeshLambertMaterial(); + + const mesh = new THREE.InstancedMesh(geometry, material, 100); + const dummy = new THREE.Object3D(); + const color = new THREE.Color(); + + for (let i = 0; i < 100; i++) { + dummy.position.x = Math.random() * 400 - 200; + dummy.position.y = Math.random() * 400 - 200; + dummy.position.z = Math.random() * 400 - 200; + dummy.rotation.x = Math.random(); + dummy.rotation.y = Math.random(); + dummy.rotation.z = Math.random(); + + dummy.scale.setScalar(Math.random() * 10 + 2); + + dummy.updateMatrix(); + mesh.setMatrixAt(i, dummy.matrix); + + color.setHex(Math.random() * 0xffffff); + mesh.setColorAt(i, color); + } + + group.add(mesh); + + stats = new Stats(); + container.appendChild(stats.dom); + + const width = window.innerWidth; + const height = window.innerHeight; + + composer = new EffectComposer(renderer); + + const renderPass = new RenderPass(scene, camera); + composer.addPass(renderPass); + + const ssaoPass = new SSAOPass(scene, camera, width, height); + composer.addPass(ssaoPass); + + const outputPass = new OutputPass(); + composer.addPass(outputPass); + + // Init gui + const gui = new GUI(); + + gui.add(ssaoPass, 'output', { + Default: SSAOPass.OUTPUT.Default, + 'SSAO Only': SSAOPass.OUTPUT.SSAO, + 'SSAO Only + Blur': SSAOPass.OUTPUT.Blur, + Depth: SSAOPass.OUTPUT.Depth, + Normal: SSAOPass.OUTPUT.Normal, + }).onChange(function (value) { + ssaoPass.output = value; + }); + gui.add(ssaoPass, 'kernelRadius').min(0).max(32); + gui.add(ssaoPass, 'minDistance').min(0.001).max(0.02); + gui.add(ssaoPass, 'maxDistance').min(0.01).max(0.3); + gui.add(ssaoPass, 'enabled'); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); + composer.setSize(width, height); +} + +function animate() { + stats.begin(); + render(); + stats.end(); +} + +function render() { + const timer = performance.now(); + group.rotation.x = timer * 0.0002; + group.rotation.y = timer * 0.0001; + + composer.render(); +} diff --git a/examples-testing/examples/webgl_postprocessing_ssr.ts b/examples-testing/examples/webgl_postprocessing_ssr.ts new file mode 100644 index 000000000..1f5e48d3b --- /dev/null +++ b/examples-testing/examples/webgl_postprocessing_ssr.ts @@ -0,0 +1,262 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; +import { SSRPass } from 'three/addons/postprocessing/SSRPass.js'; +import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; +import { ReflectorForSSRPass } from 'three/addons/objects/ReflectorForSSRPass.js'; + +import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; + +const params = { + enableSSR: true, + autoRotate: true, + otherMeshes: true, + groundReflector: true, +}; +let composer; +let ssrPass; +let gui; +let stats; +let controls; +let camera, scene, renderer; +const otherMeshes = []; +let groundReflector; +const selects = []; + +const container = document.querySelector('#container'); + +// Configure and create Draco decoder. +const dracoLoader = new DRACOLoader(); +dracoLoader.setDecoderPath('jsm/libs/draco/'); +dracoLoader.setDecoderConfig({ type: 'js' }); + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 0.1, 15); + camera.position.set(0.13271600513224902, 0.3489546826045913, 0.43921296427927076); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x443333); + scene.fog = new THREE.Fog(0x443333, 1, 4); + + // Ground + const plane = new THREE.Mesh(new THREE.PlaneGeometry(8, 8), new THREE.MeshPhongMaterial({ color: 0xcbcbcb })); + plane.rotation.x = -Math.PI / 2; + plane.position.y = -0.0001; + // plane.receiveShadow = true; + scene.add(plane); + + // Lights + const hemiLight = new THREE.HemisphereLight(0x8d7c7c, 0x494966, 3); + scene.add(hemiLight); + + const spotLight = new THREE.SpotLight(); + spotLight.intensity = 8; + spotLight.angle = Math.PI / 16; + spotLight.penumbra = 0.5; + // spotLight.castShadow = true; + spotLight.position.set(-1, 1, 1); + scene.add(spotLight); + + dracoLoader.load('models/draco/bunny.drc', function (geometry) { + geometry.computeVertexNormals(); + + const material = new THREE.MeshStandardMaterial({ color: 0xa5a5a5 }); + const mesh = new THREE.Mesh(geometry, material); + mesh.position.y = -0.0365; + scene.add(mesh); + selects.push(mesh); + + // Release decoder resources. + dracoLoader.dispose(); + }); + + let geometry, material, mesh; + + geometry = new THREE.BoxGeometry(0.05, 0.05, 0.05); + material = new THREE.MeshStandardMaterial({ color: 'green' }); + mesh = new THREE.Mesh(geometry, material); + mesh.position.set(-0.12, 0.025, 0.015); + scene.add(mesh); + otherMeshes.push(mesh); + selects.push(mesh); + + geometry = new THREE.IcosahedronGeometry(0.025, 4); + material = new THREE.MeshStandardMaterial({ color: 'cyan' }); + mesh = new THREE.Mesh(geometry, material); + mesh.position.set(-0.05, 0.025, 0.08); + scene.add(mesh); + otherMeshes.push(mesh); + selects.push(mesh); + + geometry = new THREE.ConeGeometry(0.025, 0.05, 64); + material = new THREE.MeshStandardMaterial({ color: 'yellow' }); + mesh = new THREE.Mesh(geometry, material); + mesh.position.set(-0.05, 0.025, -0.055); + scene.add(mesh); + otherMeshes.push(mesh); + selects.push(mesh); + + geometry = new THREE.PlaneGeometry(1, 1); + groundReflector = new ReflectorForSSRPass(geometry, { + clipBias: 0.0003, + textureWidth: window.innerWidth, + textureHeight: window.innerHeight, + color: 0x888888, + useDepthTexture: true, + }); + groundReflector.material.depthWrite = false; + groundReflector.rotation.x = -Math.PI / 2; + groundReflector.visible = false; + scene.add(groundReflector); + + // renderer + renderer = new THREE.WebGLRenderer({ antialias: false }); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + // + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + controls.target.set(0, 0.0635, 0); + controls.update(); + controls.enabled = !params.autoRotate; + + // STATS + + stats = new Stats(); + container.appendChild(stats.dom); + + window.addEventListener('resize', onWindowResize); + + // composer + + composer = new EffectComposer(renderer); + ssrPass = new SSRPass({ + renderer, + scene, + camera, + width: innerWidth, + height: innerHeight, + groundReflector: params.groundReflector ? groundReflector : null, + selects: params.groundReflector ? selects : null, + }); + + composer.addPass(ssrPass); + composer.addPass(new OutputPass()); + + // GUI + + gui = new GUI({ width: 260 }); + gui.add(params, 'enableSSR').name('Enable SSR'); + gui.add(params, 'groundReflector').onChange(() => { + if (params.groundReflector) { + ((ssrPass.groundReflector = groundReflector), (ssrPass.selects = selects)); + } else { + ((ssrPass.groundReflector = null), (ssrPass.selects = null)); + } + }); + ssrPass.thickness = 0.018; + gui.add(ssrPass, 'resolutionScale').min(0).max(1); + gui.add(ssrPass, 'thickness').min(0).max(0.1).step(0.0001); + ssrPass.infiniteThick = false; + gui.add(ssrPass, 'infiniteThick'); + gui.add(params, 'autoRotate').onChange(() => { + controls.enabled = !params.autoRotate; + }); + + const folder = gui.addFolder('more settings'); + folder.add(ssrPass, 'fresnel').onChange(() => { + groundReflector.fresnel = ssrPass.fresnel; + }); + folder.add(ssrPass, 'distanceAttenuation').onChange(() => { + groundReflector.distanceAttenuation = ssrPass.distanceAttenuation; + }); + ssrPass.maxDistance = 0.1; + groundReflector.maxDistance = ssrPass.maxDistance; + folder + .add(ssrPass, 'maxDistance') + .min(0) + .max(0.5) + .step(0.001) + .onChange(() => { + groundReflector.maxDistance = ssrPass.maxDistance; + }); + folder.add(params, 'otherMeshes').onChange(() => { + if (params.otherMeshes) { + otherMeshes.forEach(mesh => (mesh.visible = true)); + } else { + otherMeshes.forEach(mesh => (mesh.visible = false)); + } + }); + folder.add(ssrPass, 'bouncing'); + folder + .add(ssrPass, 'output', { + Default: SSRPass.OUTPUT.Default, + 'SSR Only': SSRPass.OUTPUT.SSR, + Beauty: SSRPass.OUTPUT.Beauty, + Depth: SSRPass.OUTPUT.Depth, + Normal: SSRPass.OUTPUT.Normal, + Metalness: SSRPass.OUTPUT.Metalness, + }) + .onChange(function (value) { + ssrPass.output = value; + }); + ssrPass.opacity = 1; + groundReflector.opacity = ssrPass.opacity; + folder + .add(ssrPass, 'opacity') + .min(0) + .max(1) + .onChange(() => { + groundReflector.opacity = ssrPass.opacity; + }); + folder.add(ssrPass, 'blur'); + // folder.open() + // gui.close() +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + composer.setSize(window.innerWidth, window.innerHeight); + groundReflector.getRenderTarget().setSize(window.innerWidth, window.innerHeight); + groundReflector.resolution.set(window.innerWidth, window.innerHeight); +} + +function animate() { + stats.begin(); + render(); + stats.end(); +} + +function render() { + if (params.autoRotate) { + const timer = Date.now() * 0.0003; + + camera.position.x = Math.sin(timer) * 0.5; + camera.position.y = 0.2135; + camera.position.z = Math.cos(timer) * 0.5; + camera.lookAt(0, 0.0635, 0); + } else { + controls.update(); + } + + if (params.enableSSR) { + // TODO: groundReflector has full ground info, need use it to solve reflection gaps problem on objects when camera near ground. + // TODO: the normal and depth info where groundReflector reflected need to be changed. + composer.render(); + } else { + renderer.render(scene, camera); + } +} diff --git a/examples-testing/examples/webgl_postprocessing_taa.ts b/examples-testing/examples/webgl_postprocessing_taa.ts new file mode 100644 index 000000000..11a986741 --- /dev/null +++ b/examples-testing/examples/webgl_postprocessing_taa.ts @@ -0,0 +1,139 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; +import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; +import { TAARenderPass } from 'three/addons/postprocessing/TAARenderPass.js'; +import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; + +let camera, scene, renderer, composer, taaRenderPass, renderPass; +let gui, stats; +let index = 0; + +const param = { TAAEnabled: '1', TAASampleLevel: 0 }; + +init(); + +clearGui(); + +function clearGui() { + if (gui) gui.destroy(); + + gui = new GUI(); + + gui.add(param, 'TAAEnabled', { + Disabled: '0', + Enabled: '1', + }).onFinishChange(function () { + if (taaRenderPass) { + taaRenderPass.enabled = param.TAAEnabled === '1'; + renderPass.enabled = param.TAAEnabled !== '1'; + } + }); + + gui.add(param, 'TAASampleLevel', { + 'Level 0: 1 Sample': 0, + 'Level 1: 2 Samples': 1, + 'Level 2: 4 Samples': 2, + 'Level 3: 8 Samples': 3, + 'Level 4: 16 Samples': 4, + 'Level 5: 32 Samples': 5, + }).onFinishChange(function () { + if (taaRenderPass) { + taaRenderPass.sampleLevel = param.TAASampleLevel; + } + }); + + gui.open(); +} + +function init() { + const container = document.getElementById('container'); + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.z = 300; + + scene = new THREE.Scene(); + + const geometry = new THREE.BoxGeometry(120, 120, 120); + const material1 = new THREE.MeshBasicMaterial({ color: 0xffffff, wireframe: true }); + + const mesh1 = new THREE.Mesh(geometry, material1); + mesh1.position.x = -100; + scene.add(mesh1); + + const texture = new THREE.TextureLoader().load('textures/brick_diffuse.jpg'); + texture.minFilter = THREE.NearestFilter; + texture.magFilter = THREE.NearestFilter; + texture.anisotropy = 1; + texture.generateMipmaps = false; + texture.colorSpace = THREE.SRGBColorSpace; + + const material2 = new THREE.MeshBasicMaterial({ map: texture }); + + const mesh2 = new THREE.Mesh(geometry, material2); + mesh2.position.x = 100; + scene.add(mesh2); + + // postprocessing + + composer = new EffectComposer(renderer); + + taaRenderPass = new TAARenderPass(scene, camera); + taaRenderPass.unbiased = false; + composer.addPass(taaRenderPass); + + renderPass = new RenderPass(scene, camera); + renderPass.enabled = false; + composer.addPass(renderPass); + + const outputPass = new OutputPass(); + composer.addPass(outputPass); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); + composer.setSize(width, height); +} + +function animate() { + index++; + + if (Math.round(index / 200) % 2 === 0) { + for (let i = 0; i < scene.children.length; i++) { + const child = scene.children[i]; + + child.rotation.x += 0.005; + child.rotation.y += 0.01; + } + + if (taaRenderPass) taaRenderPass.accumulate = false; + } else { + if (taaRenderPass) taaRenderPass.accumulate = true; + } + + composer.render(); + + stats.update(); +} diff --git a/examples-testing/examples/webgl_postprocessing_transition.ts b/examples-testing/examples/webgl_postprocessing_transition.ts new file mode 100644 index 000000000..1cf5dd3cb --- /dev/null +++ b/examples-testing/examples/webgl_postprocessing_transition.ts @@ -0,0 +1,214 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import TWEEN from 'three/addons/libs/tween.module.js'; +import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; +import { RenderTransitionPass } from 'three/addons/postprocessing/RenderTransitionPass.js'; +import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; + +let stats; +let renderer, composer, renderTransitionPass; + +const textures = []; +const timer = new THREE.Timer(); +timer.connect(document); + +const params = { + sceneAnimate: true, + transitionAnimate: true, + transition: 0, + useTexture: true, + texture: 5, + cycle: true, + threshold: 0.1, +}; + +const fxSceneA = new FXScene(new THREE.BoxGeometry(2, 2, 2), new THREE.Vector3(0, -0.4, 0), 0xffffff); +const fxSceneB = new FXScene(new THREE.IcosahedronGeometry(1, 1), new THREE.Vector3(0, 0.2, 0.1), 0x000000); + +init(); + +function init() { + initGUI(); + initTextures(); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + composer = new EffectComposer(renderer); + + stats = new Stats(); + document.body.appendChild(stats.dom); + + renderTransitionPass = new RenderTransitionPass(fxSceneA.scene, fxSceneA.camera, fxSceneB.scene, fxSceneB.camera); + renderTransitionPass.setTexture(textures[0]); + composer.addPass(renderTransitionPass); + + const outputPass = new OutputPass(); + composer.addPass(outputPass); +} + +window.addEventListener('resize', onWindowResize); + +function onWindowResize() { + fxSceneA.resize(); + fxSceneB.resize(); + renderer.setSize(window.innerWidth, window.innerHeight); + composer.setSize(window.innerWidth, window.innerHeight); +} + +new TWEEN.Tween(params) + .to({ transition: 1 }, 1500) + .onUpdate(function () { + renderTransitionPass.setTransition(params.transition); + + // Change the current alpha texture after each transition + if (params.cycle) { + if (params.transition == 0 || params.transition == 1) { + params.texture = (params.texture + 1) % textures.length; + renderTransitionPass.setTexture(textures[params.texture]); + } + } + }) + .repeat(Infinity) + .delay(2000) + .yoyo(true) + .start(); + +function animate() { + timer.update(); + + // Transition animation + if (params.transitionAnimate) TWEEN.update(); + + const delta = timer.getDelta(); + fxSceneA.update(delta); + fxSceneB.update(delta); + + render(); + stats.update(); +} + +function initTextures() { + const loader = new THREE.TextureLoader(); + + for (let i = 0; i < 6; i++) { + textures[i] = loader.load('textures/transition/transition' + (i + 1) + '.png'); + } +} + +function initGUI() { + const gui = new GUI(); + + gui.add(params, 'sceneAnimate').name('Animate scene'); + gui.add(params, 'transitionAnimate').name('Animate transition'); + gui.add(params, 'transition', 0, 1, 0.01) + .onChange(function (value) { + renderTransitionPass.setTransition(value); + }) + .listen(); + + gui.add(params, 'useTexture').onChange(function (value) { + renderTransitionPass.useTexture(value); + }); + + gui.add(params, 'texture', { Perlin: 0, Squares: 1, Cells: 2, Distort: 3, Gradient: 4, Radial: 5 }) + .onChange(function (value) { + renderTransitionPass.setTexture(textures[value]); + }) + .listen(); + + gui.add(params, 'cycle'); + + gui.add(params, 'threshold', 0, 1, 0.01).onChange(function (value) { + renderTransitionPass.setTextureThreshold(value); + }); +} + +function render() { + // Prevent render both scenes when it's not necessary + if (params.transition === 0) { + renderer.render(fxSceneB.scene, fxSceneB.camera); + } else if (params.transition === 1) { + renderer.render(fxSceneA.scene, fxSceneA.camera); + } else { + // When 0 < transition < 1 render transition between two scenes + composer.render(); + } +} + +function FXScene(geometry, rotationSpeed, backgroundColor) { + const camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.z = 20; + + // Setup scene + const scene = new THREE.Scene(); + scene.background = new THREE.Color(backgroundColor); + scene.add(new THREE.AmbientLight(0xaaaaaa, 3)); + + const light = new THREE.DirectionalLight(0xffffff, 3); + light.position.set(0, 1, 4); + scene.add(light); + + this.rotationSpeed = rotationSpeed; + + const color = geometry.type === 'BoxGeometry' ? 0x0000ff : 0xff0000; + const material = new THREE.MeshPhongMaterial({ color: color, flatShading: true }); + const mesh = generateInstancedMesh(geometry, material, 500); + scene.add(mesh); + + this.scene = scene; + this.camera = camera; + this.mesh = mesh; + + this.update = function (delta) { + if (params.sceneAnimate) { + mesh.rotation.x += this.rotationSpeed.x * delta; + mesh.rotation.y += this.rotationSpeed.y * delta; + mesh.rotation.z += this.rotationSpeed.z * delta; + } + }; + + this.resize = function () { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + }; +} + +function generateInstancedMesh(geometry, material, count) { + const mesh = new THREE.InstancedMesh(geometry, material, count); + + const dummy = new THREE.Object3D(); + const color = new THREE.Color(); + + for (let i = 0; i < count; i++) { + dummy.position.x = Math.random() * 100 - 50; + dummy.position.y = Math.random() * 60 - 30; + dummy.position.z = Math.random() * 80 - 40; + + dummy.rotation.x = Math.random() * 2 * Math.PI; + dummy.rotation.y = Math.random() * 2 * Math.PI; + dummy.rotation.z = Math.random() * 2 * Math.PI; + + dummy.scale.x = Math.random() * 2 + 1; + + if (geometry.type === 'BoxGeometry') { + dummy.scale.y = Math.random() * 2 + 1; + dummy.scale.z = Math.random() * 2 + 1; + } else { + dummy.scale.y = dummy.scale.x; + dummy.scale.z = dummy.scale.x; + } + + dummy.updateMatrix(); + + mesh.setMatrixAt(i, dummy.matrix); + mesh.setColorAt(i, color.setScalar(0.1 + 0.9 * Math.random())); + } + + return mesh; +} diff --git a/examples-testing/examples/webgl_postprocessing_unreal_bloom.ts b/examples-testing/examples/webgl_postprocessing_unreal_bloom.ts new file mode 100644 index 000000000..b5e2ee0f9 --- /dev/null +++ b/examples-testing/examples/webgl_postprocessing_unreal_bloom.ts @@ -0,0 +1,139 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; +import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; +import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js'; +import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; + +let camera, stats; +let composer, renderer, mixer, timer; + +const params = { + threshold: 0, + strength: 1, + radius: 0.5, + exposure: 1, +}; + +init(); + +async function init() { + const container = document.getElementById('container'); + + timer = new THREE.Timer(); + timer.connect(document); + + const scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 100); + camera.position.set(-5, 2.5, -3.5); + scene.add(camera); + + scene.add(new THREE.AmbientLight(0xcccccc)); + + const pointLight = new THREE.PointLight(0xffffff, 100); + camera.add(pointLight); + + const loader = new GLTFLoader(); + const gltf = await loader.loadAsync('models/gltf/PrimaryIonDrive.glb'); + + const model = gltf.scene; + scene.add(model); + + mixer = new THREE.AnimationMixer(model); + const clip = gltf.animations[0]; + mixer.clipAction(clip.optimize()).play(); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + container.appendChild(renderer.domElement); + + // + + const renderScene = new RenderPass(scene, camera); + + const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85); + bloomPass.threshold = params.threshold; + bloomPass.strength = params.strength; + bloomPass.radius = params.radius; + + const outputPass = new OutputPass(); + + composer = new EffectComposer(renderer); + composer.addPass(renderScene); + composer.addPass(bloomPass); + composer.addPass(outputPass); + + // + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + const controls = new OrbitControls(camera, renderer.domElement); + controls.maxPolarAngle = Math.PI * 0.5; + controls.minDistance = 3; + controls.maxDistance = 8; + + // + + const gui = new GUI(); + + const bloomFolder = gui.addFolder('bloom'); + + bloomFolder.add(params, 'threshold', 0.0, 1.0).onChange(function (value) { + bloomPass.threshold = Number(value); + }); + + bloomFolder.add(params, 'strength', 0.0, 3.0).onChange(function (value) { + bloomPass.strength = Number(value); + }); + + gui.add(params, 'radius', 0.0, 1.0) + .step(0.01) + .onChange(function (value) { + bloomPass.radius = Number(value); + }); + + const toneMappingFolder = gui.addFolder('tone mapping'); + + toneMappingFolder.add(params, 'exposure', 0.1, 2).onChange(function (value) { + renderer.toneMappingExposure = Math.pow(value, 4.0); + }); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); + composer.setSize(width, height); +} + +function animate() { + timer.update(); + + const delta = timer.getDelta(); + + mixer.update(delta); + + stats.update(); + + composer.render(); +} diff --git a/examples-testing/examples/webgl_postprocessing_unreal_bloom_selective.ts b/examples-testing/examples/webgl_postprocessing_unreal_bloom_selective.ts new file mode 100644 index 000000000..288b4477d --- /dev/null +++ b/examples-testing/examples/webgl_postprocessing_unreal_bloom_selective.ts @@ -0,0 +1,208 @@ +import * as THREE from 'three'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; +import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; +import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js'; +import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js'; +import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; +import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; + +const BLOOM_SCENE = 1; + +const bloomLayer = new THREE.Layers(); +bloomLayer.set(BLOOM_SCENE); + +const params = { + threshold: 0, + strength: 1, + radius: 0.5, + exposure: 1, +}; + +const darkMaterial = new THREE.MeshBasicMaterial({ color: 'black' }); +const materials = {}; + +const renderer = new THREE.WebGLRenderer({ antialias: false }); +renderer.setPixelRatio(window.devicePixelRatio); +renderer.setSize(window.innerWidth, window.innerHeight); +renderer.toneMapping = THREE.NeutralToneMapping; +document.body.appendChild(renderer.domElement); + +const scene = new THREE.Scene(); +const pmremGenerator = new THREE.PMREMGenerator(renderer); +scene.environment = pmremGenerator.fromScene(new RoomEnvironment(), 0.04).texture; + +const camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 200); +camera.position.set(0, 0, 20); +camera.lookAt(0, 0, 0); + +const controls = new OrbitControls(camera, renderer.domElement); +controls.maxPolarAngle = Math.PI * 0.5; +controls.minDistance = 1; +controls.maxDistance = 100; +controls.addEventListener('change', render); + +const renderScene = new RenderPass(scene, camera); + +const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85); +bloomPass.threshold = params.threshold; +bloomPass.strength = params.strength; +bloomPass.radius = params.radius; + +const bloomRenderTarget = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight, { + type: THREE.HalfFloatType, +}); +const bloomComposer = new EffectComposer(renderer, bloomRenderTarget); +bloomComposer.renderToScreen = false; +bloomComposer.addPass(renderScene); +bloomComposer.addPass(bloomPass); + +const mixPass = new ShaderPass( + new THREE.ShaderMaterial({ + uniforms: { + baseTexture: { value: null }, + bloomTexture: { value: bloomComposer.renderTarget2.texture }, + bloomStrength: { value: params.strength }, + }, + vertexShader: document.getElementById('vertexshader').textContent, + fragmentShader: document.getElementById('fragmentshader').textContent, + defines: {}, + }), + 'baseTexture', +); +mixPass.needsSwap = true; + +const outputPass = new OutputPass(); + +const finalRenderTarget = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight, { + type: THREE.HalfFloatType, + samples: 4, +}); +const finalComposer = new EffectComposer(renderer, finalRenderTarget); +finalComposer.addPass(renderScene); +finalComposer.addPass(mixPass); +finalComposer.addPass(outputPass); + +const raycaster = new THREE.Raycaster(); + +const mouse = new THREE.Vector2(); + +window.addEventListener('pointerdown', onPointerDown); + +const gui = new GUI(); + +const bloomFolder = gui.addFolder('bloom'); + +bloomFolder.add(params, 'threshold', 0.0, 1.0).onChange(function (value) { + bloomPass.threshold = Number(value); + render(); +}); + +bloomFolder.add(params, 'strength', 0.0, 3).onChange(function (value) { + bloomPass.strength = Number(value); + mixPass.material.uniforms.bloomStrength.value = bloomPass.strength; + + render(); +}); + +bloomFolder + .add(params, 'radius', 0.0, 1.0) + .step(0.01) + .onChange(function (value) { + bloomPass.radius = Number(value); + render(); + }); + +const toneMappingFolder = gui.addFolder('tone mapping'); + +toneMappingFolder.add(params, 'exposure', 0.1, 2).onChange(function (value) { + renderer.toneMappingExposure = Math.pow(value, 4.0); + render(); +}); + +setupScene(); + +function onPointerDown(event) { + mouse.x = (event.clientX / window.innerWidth) * 2 - 1; + mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; + + raycaster.setFromCamera(mouse, camera); + const intersects = raycaster.intersectObjects(scene.children, false); + if (intersects.length > 0) { + const object = intersects[0].object; + object.layers.toggle(BLOOM_SCENE); + render(); + } +} + +window.onresize = function () { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); + + bloomComposer.setSize(width, height); + finalComposer.setSize(width, height); + + render(); +}; + +function setupScene() { + scene.traverse(disposeMaterial); + scene.children.length = 0; + + const geometry = new THREE.IcosahedronGeometry(1, 15); + + for (let i = 0; i < 50; i++) { + const color = new THREE.Color(); + color.setHSL(Math.random(), 0.7, Math.random() * 0.2 + 0.05); + + const material = new THREE.MeshStandardMaterial({ color: color, roughness: 1, metalness: 1 }); + const sphere = new THREE.Mesh(geometry, material); + sphere.position.x = Math.random() * 10 - 5; + sphere.position.y = Math.random() * 10 - 5; + sphere.position.z = Math.random() * 10 - 5; + sphere.position.normalize().multiplyScalar(Math.random() * 4.0 + 2.0); + sphere.scale.setScalar(Math.random() * Math.random() + 0.5); + scene.add(sphere); + + if (Math.random() < 0.25) sphere.layers.enable(BLOOM_SCENE); + } + + render(); +} + +function disposeMaterial(obj) { + if (obj.material) { + obj.material.dispose(); + } +} + +function render() { + scene.traverse(darkenNonBloomed); + bloomComposer.render(); + scene.traverse(restoreMaterial); + + // render the entire scene, then render bloom scene on top + finalComposer.render(); +} + +function darkenNonBloomed(obj) { + if (obj.isMesh && bloomLayer.test(obj.layers) === false) { + materials[obj.uuid] = obj.material; + obj.material = darkMaterial; + } +} + +function restoreMaterial(obj) { + if (materials[obj.uuid]) { + obj.material = materials[obj.uuid]; + delete materials[obj.uuid]; + } +} diff --git a/examples-testing/examples/webgl_random_uv.ts b/examples-testing/examples/webgl_random_uv.ts new file mode 100644 index 000000000..fea6b3478 --- /dev/null +++ b/examples-testing/examples/webgl_random_uv.ts @@ -0,0 +1,338 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; +import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let camera, scene, renderer, dirLight, ground, gui, material, materialIn, uniforms, uniformsIn; + +init(); +render(); + +function init() { + const container = document.getElementById('container'); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 20); + camera.position.set(-0.8, 0.6, 1.5); + + scene = new THREE.Scene(); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = 0.7; + renderer.shadowMap.enabled = true; + renderer.shadowMap.type = THREE.VSMShadowMap; + container.appendChild(renderer.domElement); + + dirLight = new THREE.DirectionalLight(0xffffff, 3); + dirLight.position.set(-0.5, 1, 0.8); + dirLight.castShadow = true; + scene.add(dirLight); + const shadow = dirLight.shadow; + shadow.mapSize.width = shadow.mapSize.height = 1024; + shadow.radius = 16; + shadow.bias = -0.0005; + const shadowCam = shadow.camera, + s = 2; + shadowCam.near = 0.5; + shadowCam.far = 3; + shadowCam.right = shadowCam.top = s; + shadowCam.left = shadowCam.bottom = -s; + // debug shadow + //scene.add( new THREE.CameraHelper(shadowCam) ); + + // add ground plane + const plane = new THREE.PlaneGeometry(2, 2); + plane.rotateX(-Math.PI * 0.5); + ground = new THREE.Mesh(plane, new THREE.ShadowMaterial({ opacity: 0.5 })); + ground.receiveShadow = true; + ground.position.z = -0.5; + scene.add(ground); + + const map = new THREE.TextureLoader().load('textures/jade.jpg'); + map.colorSpace = THREE.SRGBColorSpace; + map.wrapS = map.wrapT = THREE.RepeatWrapping; + map.repeat.set(20, 20); + map.flipY = false; + + const disolveMap = new THREE.TextureLoader().load('textures/shaderball_ds.jpg'); + disolveMap.flipY = false; + + const noise = new THREE.TextureLoader().load('textures/noise.png'); + + new HDRLoader().setPath('textures/equirectangular/').load('lobe.hdr', function (texture) { + texture.mapping = THREE.EquirectangularReflectionMapping; + + scene.background = texture; + scene.environment = texture; + scene.backgroundBlurriness = 0.5; + scene.backgroundIntensity = 1.0; + scene.environmentIntensity = 1.5; + + render(); + + // model + + const loader = new GLTFLoader().setPath('models/gltf/'); + loader.setDRACOLoader(new DRACOLoader().setDecoderPath('jsm/libs/draco/gltf/')); + loader.load('ShaderBall2.glb', function (gltf) { + const shaderBall = gltf.scene.children[0]; + + // shaderBall is a group with 3 children : base, inside and logo + // ao map is include in model + + let i = shaderBall.children.length, + n = 0; + + while (i--) { + shaderBall.children[i].receiveShadow = true; + shaderBall.children[i].castShadow = true; + shaderBall.children[i].renderOrder = n++; + } + + material = shaderBall.children[0].material; + material.map = map; + material.alphaMap = disolveMap; + material.transparent = true; + + materialIn = shaderBall.children[1].material; + materialIn.alphaMap = disolveMap; + materialIn.transparent = true; + + material.onBeforeCompile = function (shader) { + shader.uniforms['disolve'] = { value: 0 }; + shader.uniforms['threshold'] = { value: 0.2 }; + + shader.uniforms['noiseMap'] = { value: noise }; + shader.uniforms['enableRandom'] = { value: 1 }; + shader.uniforms['useNoiseMap'] = { value: 1 }; + shader.uniforms['useSuslikMethod'] = { value: 0 }; + shader.uniforms['debugNoise'] = { value: 0 }; + + shader.fragmentShader = shader.fragmentShader.replace( + '#include ', + '#include ' + randomUV, + ); + shader.fragmentShader = shader.fragmentShader.replace('#include ', map_fragment); + + // for disolve + shader.fragmentShader = shader.fragmentShader.replace( + '#include ', + alphamap_pars_fragment, + ); + shader.fragmentShader = shader.fragmentShader.replace( + '#include ', + alphamap_fragment, + ); + + uniforms = shader.uniforms; + }; + + materialIn.onBeforeCompile = function (shader) { + shader.uniforms['disolve'] = { value: 0 }; + shader.uniforms['threshold'] = { value: 0.2 }; + // for disolve + shader.fragmentShader = shader.fragmentShader.replace( + '#include ', + alphamap_pars_fragment, + ); + shader.fragmentShader = shader.fragmentShader.replace( + '#include ', + alphamap_fragment, + ); + + uniformsIn = shader.uniforms; + }; + + scene.add(shaderBall); + + render(); + + createGUI(); + }); + }); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.addEventListener('change', render); // use if there is no animation loop + controls.minDistance = 0.3; + controls.maxDistance = 10; + controls.target.set(0, 0.4, 0); + controls.update(); + + window.addEventListener('resize', onWindowResize); +} + +function createGUI() { + const setting = { + get Enabled() { + return uniforms.enableRandom.value ? true : false; + }, + set Enabled(v) { + uniforms.enableRandom.value = v ? 1 : 0; + render(); + }, + + get UseNoiseMap() { + return uniforms.useNoiseMap.value ? true : false; + }, + set UseNoiseMap(v) { + uniforms.useNoiseMap.value = v ? 1 : 0; + render(); + }, + + get SuslikMethod() { + return uniforms.useSuslikMethod.value ? true : false; + }, + set SuslikMethod(v) { + uniforms.useSuslikMethod.value = v ? 1 : 0; + render(); + }, + + get DebugNoise() { + return uniforms.debugNoise.value ? true : false; + }, + set DebugNoise(v) { + uniforms.debugNoise.value = v ? 1 : 0; + render(); + }, + + // disolve + get disolve() { + return uniforms.disolve.value; + }, + set disolve(v) { + uniforms.disolve.value = v; + uniformsIn.disolve.value = v; + ground.material.opacity = (1 - v) * 0.5; + render(); + }, + + get threshold() { + return uniforms.threshold.value; + }, + set threshold(v) { + uniforms.threshold.value = v; + uniformsIn.threshold.value = v; + render(); + }, + }; + + gui = new GUI(); + gui.add(material, 'roughness', 0, 1, 0.01).onChange(render); + gui.add(material, 'metalness', 0, 1, 0.01).onChange(render); + gui.add(setting, 'disolve', 0, 1, 0.01).onChange(render); + gui.add(setting, 'threshold', 0, 1, 0.01).onChange(render); + gui.add(setting, 'Enabled'); + gui.add(setting, 'UseNoiseMap'); + gui.add(setting, 'SuslikMethod'); + gui.add(setting, 'DebugNoise'); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + renderer.setSize(window.innerWidth, window.innerHeight); + + render(); +} + +// + +function render() { + renderer.render(scene, camera); +} + +const randomUV = /* glsl */ ` + + uniform sampler2D noiseMap; + uniform float enableRandom; + uniform float useNoiseMap; + uniform float debugNoise; + uniform float useSuslikMethod; + + float directNoise(vec2 p){ + vec2 ip = floor(p); + vec2 u = fract(p); + u = u*u*(3.0-2.0*u); + + float res = mix( + mix(rand(ip),rand(ip+vec2(1.0,0.0)),u.x), + mix(rand(ip+vec2(0.0,1.0)),rand(ip+vec2(1.0,1.0)),u.x),u.y); + return res*res; + } + + float sum( vec4 v ) { return v.x+v.y+v.z; } + + vec4 textureNoTile( sampler2D mapper, in vec2 uv ){ + + // sample variation pattern + float k = 0.0; + if( useNoiseMap == 1.0 ) k = texture2D( noiseMap, 0.005*uv ).x; + else k = directNoise( uv ); + + // compute index + float index = k*8.0; + float f = fract( index ); + float ia = 0.0; + float ib = 0.0; + + if( useSuslikMethod == 1.0 ){ + ia = floor(index+0.5); + ib = floor(index); + f = min(f, 1.0-f)*2.0; + } else { + ia = floor( index ); + ib = ia + 1.0; + } + + // offsets for the different virtual patterns + vec2 offa = sin(vec2(3.0,7.0)*ia); // can replace with any other hash + vec2 offb = sin(vec2(3.0,7.0)*ib); // can replace with any other hash + + // compute derivatives for mip-mapping + vec2 dx = dFdx(uv); + vec2 dy = dFdy(uv); + + // sample the two closest virtual patterns + vec4 cola = textureGrad( mapper, uv + offa, dx, dy ); + vec4 colb = textureGrad( mapper, uv + offb, dx, dy ); + if( debugNoise == 1.0 ){ + cola = vec4( 0.1,0.0,0.0,1.0 ); + colb = vec4( 0.0,0.0,1.0,1.0 ); + } + + // interpolate between the two virtual patterns + return mix( cola, colb, smoothstep(0.2,0.8,f-0.1*sum(cola-colb)) ); + + }`; + +const map_fragment = /* glsl */ ` + #ifdef USE_MAP + + if( enableRandom == 1.0 ) diffuseColor *= textureNoTile( map, vMapUv ); + else diffuseColor *= texture2D( map, vMapUv ); + + #endif + `; + +const alphamap_pars_fragment = /* glsl */ ` + #ifdef USE_ALPHAMAP + uniform sampler2D alphaMap; + uniform float disolve; + uniform float threshold; + #endif + `; + +const alphamap_fragment = /* glsl */ ` + #ifdef USE_ALPHAMAP + float vv = texture2D( alphaMap, vAlphaMapUv ).g; + float r = disolve * (1.0 + threshold * 2.0) - threshold; + float mixf = clamp((vv - r)*(1.0/threshold), 0.0, 1.0); + diffuseColor.a = mixf; + #endif + `; diff --git a/examples-testing/examples/webgl_raycaster_sprite.ts b/examples-testing/examples/webgl_raycaster_sprite.ts new file mode 100644 index 000000000..f35d5de17 --- /dev/null +++ b/examples-testing/examples/webgl_raycaster_sprite.ts @@ -0,0 +1,103 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let renderer, scene, camera; +let group; + +let selectedObject = null; +const raycaster = new THREE.Raycaster(); +const pointer = new THREE.Vector2(); + +init(); + +function init() { + // init renderer + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // init scene + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xffffff); + + group = new THREE.Group(); + scene.add(group); + + // init camera + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(15, 15, 15); + camera.lookAt(scene.position); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 15; + controls.maxDistance = 250; + + // add sprites + + const sprite1 = new THREE.Sprite(new THREE.SpriteMaterial({ color: '#69f' })); + sprite1.position.set(6, 5, 5); + sprite1.scale.set(2, 5, 1); + group.add(sprite1); + + const sprite2 = new THREE.Sprite(new THREE.SpriteMaterial({ color: '#69f', sizeAttenuation: false })); + sprite2.material.rotation = (Math.PI / 3) * 4; + sprite2.position.set(8, -2, 2); + sprite2.center.set(0.5, 0); + sprite2.scale.set(0.1, 0.5, 0.1); + group.add(sprite2); + + const group2 = new THREE.Object3D(); + group2.scale.set(1, 2, 1); + group2.position.set(-5, 0, 0); + group2.rotation.set(Math.PI / 2, 0, 0); + group.add(group2); + + const sprite3 = new THREE.Sprite(new THREE.SpriteMaterial({ color: '#69f' })); + sprite3.position.set(0, 2, 5); + sprite3.scale.set(10, 2, 3); + sprite3.center.set(-0.1, 0); + sprite3.material.rotation = Math.PI / 3; + group2.add(sprite3); + + window.addEventListener('resize', onWindowResize); + document.addEventListener('pointermove', onPointerMove); +} + +function animate() { + renderer.render(scene, camera); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function onPointerMove(event) { + if (selectedObject) { + selectedObject.material.color.set('#69f'); + selectedObject = null; + } + + pointer.x = (event.clientX / window.innerWidth) * 2 - 1; + pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; + + raycaster.setFromCamera(pointer, camera); + + const intersects = raycaster.intersectObject(group, true); + + if (intersects.length > 0) { + const res = intersects.filter(function (res) { + return res && res.object; + })[0]; + + if (res && res.object) { + selectedObject = res.object; + selectedObject.material.color.set('#f00'); + } + } +} diff --git a/examples-testing/examples/webgl_raycaster_texture.ts b/examples-testing/examples/webgl_raycaster_texture.ts new file mode 100644 index 000000000..72c7054dc --- /dev/null +++ b/examples-testing/examples/webgl_raycaster_texture.ts @@ -0,0 +1,286 @@ +import * as THREE from 'three'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +const WRAPPING = { + RepeatWrapping: THREE.RepeatWrapping, + ClampToEdgeWrapping: THREE.ClampToEdgeWrapping, + MirroredRepeatWrapping: THREE.MirroredRepeatWrapping, +}; + +const params = { + wrapS: THREE.RepeatWrapping, + wrapT: THREE.RepeatWrapping, + offsetX: 0, + offsetY: 0, + repeatX: 1, + repeatY: 1, + rotation: 0, +}; + +function CanvasTexture(parentTexture) { + this._canvas = document.createElement('canvas'); + this._canvas.width = this._canvas.height = 1024; + this._context2D = this._canvas.getContext('2d'); + + if (parentTexture) { + this._parentTexture.push(parentTexture); + parentTexture.image = this._canvas; + } + + const that = this; + this._background = document.createElement('img'); + this._background.addEventListener('load', function () { + that._canvas.width = that._background.naturalWidth; + that._canvas.height = that._background.naturalHeight; + + that._crossRadius = Math.ceil(Math.min(that._canvas.width, that._canvas.height / 30)); + that._crossMax = Math.ceil(0.70710678 * that._crossRadius); + that._crossMin = Math.ceil(that._crossMax / 10); + that._crossThickness = Math.ceil(that._crossMax / 10); + + that._draw(); + }); + this._background.crossOrigin = ''; + this._background.src = 'textures/uv_grid_opengl.jpg'; + + this._draw(); +} + +CanvasTexture.prototype = { + constructor: CanvasTexture, + + _canvas: null, + _context2D: null, + _xCross: 0, + _yCross: 0, + + _crossRadius: 57, + _crossMax: 40, + _crossMin: 4, + _crossThickness: 4, + + _parentTexture: [], + + addParent: function (parentTexture) { + if (this._parentTexture.indexOf(parentTexture) === -1) { + this._parentTexture.push(parentTexture); + parentTexture.image = this._canvas; + } + }, + + setCrossPosition: function (x, y) { + this._xCross = x * this._canvas.width; + this._yCross = y * this._canvas.height; + + this._draw(); + }, + + _draw: function () { + if (!this._context2D) return; + + this._context2D.clearRect(0, 0, this._canvas.width, this._canvas.height); + + // Background. + this._context2D.drawImage(this._background, 0, 0); + + // Yellow cross. + this._context2D.lineWidth = this._crossThickness * 3; + this._context2D.strokeStyle = '#FFFF00'; + + this._context2D.beginPath(); + this._context2D.moveTo(this._xCross - this._crossMax - 2, this._yCross - this._crossMax - 2); + this._context2D.lineTo(this._xCross - this._crossMin, this._yCross - this._crossMin); + + this._context2D.moveTo(this._xCross + this._crossMin, this._yCross + this._crossMin); + this._context2D.lineTo(this._xCross + this._crossMax + 2, this._yCross + this._crossMax + 2); + + this._context2D.moveTo(this._xCross - this._crossMax - 2, this._yCross + this._crossMax + 2); + this._context2D.lineTo(this._xCross - this._crossMin, this._yCross + this._crossMin); + + this._context2D.moveTo(this._xCross + this._crossMin, this._yCross - this._crossMin); + this._context2D.lineTo(this._xCross + this._crossMax + 2, this._yCross - this._crossMax - 2); + + this._context2D.stroke(); + + for (let i = 0; i < this._parentTexture.length; i++) { + this._parentTexture[i].needsUpdate = true; + } + }, +}; + +const width = window.innerWidth; +const height = window.innerHeight; + +let canvas; +let planeTexture, cubeTexture, circleTexture; + +let container; + +let camera, scene, renderer; + +const raycaster = new THREE.Raycaster(); +const mouse = new THREE.Vector2(); +const onClickPosition = new THREE.Vector2(); + +init(); + +function init() { + container = document.getElementById('container'); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xeeeeee); + + camera = new THREE.PerspectiveCamera(45, width / height, 1, 1000); + camera.position.x = -30; + camera.position.y = 40; + camera.position.z = 50; + camera.lookAt(scene.position); + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(width, height); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + // A cube, in the middle. + cubeTexture = new THREE.Texture(undefined, THREE.UVMapping, THREE.RepeatWrapping, THREE.RepeatWrapping); + cubeTexture.colorSpace = THREE.SRGBColorSpace; + canvas = new CanvasTexture(cubeTexture); + const cubeMaterial = new THREE.MeshBasicMaterial({ map: cubeTexture }); + const cubeGeometry = new THREE.BoxGeometry(20, 20, 20); + let uvs = cubeGeometry.attributes.uv.array; + // Set a specific texture mapping. + for (let i = 0; i < uvs.length; i++) { + uvs[i] *= 2; + } + + const cube = new THREE.Mesh(cubeGeometry, cubeMaterial); + cube.position.x = 4; + cube.position.y = -5; + cube.position.z = 0; + scene.add(cube); + + // A plane on the left + + planeTexture = new THREE.Texture( + undefined, + THREE.UVMapping, + THREE.MirroredRepeatWrapping, + THREE.MirroredRepeatWrapping, + ); + planeTexture.colorSpace = THREE.SRGBColorSpace; + canvas.addParent(planeTexture); + const planeMaterial = new THREE.MeshBasicMaterial({ map: planeTexture }); + const planeGeometry = new THREE.PlaneGeometry(25, 25, 1, 1); + uvs = planeGeometry.attributes.uv.array; + + // Set a specific texture mapping. + + for (let i = 0; i < uvs.length; i++) { + uvs[i] *= 2; + } + + const plane = new THREE.Mesh(planeGeometry, planeMaterial); + plane.position.x = -16; + plane.position.y = -5; + plane.position.z = 0; + scene.add(plane); + + // A circle on the right. + + circleTexture = new THREE.Texture(undefined, THREE.UVMapping, THREE.RepeatWrapping, THREE.RepeatWrapping); + circleTexture.colorSpace = THREE.SRGBColorSpace; + canvas.addParent(circleTexture); + const circleMaterial = new THREE.MeshBasicMaterial({ map: circleTexture }); + const circleGeometry = new THREE.CircleGeometry(25, 40, 0, Math.PI * 2); + uvs = circleGeometry.attributes.uv.array; + + // Set a specific texture mapping. + + for (let i = 0; i < uvs.length; i++) { + uvs[i] = (uvs[i] - 0.25) * 2; + } + + const circle = new THREE.Mesh(circleGeometry, circleMaterial); + circle.position.x = 24; + circle.position.y = -5; + circle.position.z = 0; + scene.add(circle); + + window.addEventListener('resize', onWindowResize); + container.addEventListener('mousemove', onMouseMove); + + // + + const gui = new GUI(); + gui.title('Circle Texture Settings'); + + gui.add(params, 'wrapS', WRAPPING).onChange(setwrapS); + gui.add(params, 'wrapT', WRAPPING).onChange(setwrapT); + gui.add(params, 'offsetX', 0, 5); + gui.add(params, 'offsetY', 0, 5); + gui.add(params, 'repeatX', 0, 5); + gui.add(params, 'repeatY', 0, 5); + gui.add(params, 'rotation', 0, 2 * Math.PI); + gui.open(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function onMouseMove(evt) { + evt.preventDefault(); + + const array = getMousePosition(container, evt.clientX, evt.clientY); + onClickPosition.fromArray(array); + + const intersects = getIntersects(onClickPosition, scene.children); + + if (intersects.length > 0 && intersects[0].uv) { + const uv = intersects[0].uv; + intersects[0].object.material.map.transformUv(uv); + canvas.setCrossPosition(uv.x, uv.y); + } +} + +function getMousePosition(dom, x, y) { + const rect = dom.getBoundingClientRect(); + return [(x - rect.left) / rect.width, (y - rect.top) / rect.height]; +} + +function getIntersects(point, objects) { + mouse.set(point.x * 2 - 1, -(point.y * 2) + 1); + + raycaster.setFromCamera(mouse, camera); + + return raycaster.intersectObjects(objects, false); +} + +function animate() { + // update texture parameters + + circleTexture.offset.x = params.offsetX; + circleTexture.offset.y = params.offsetY; + circleTexture.repeat.x = params.repeatX; + circleTexture.repeat.y = params.repeatY; + circleTexture.rotation = params.rotation; + + // + + renderer.render(scene, camera); +} + +function setwrapS(value) { + circleTexture.wrapS = value; + circleTexture.needsUpdate = true; +} + +function setwrapT(value) { + circleTexture.wrapT = value; + circleTexture.needsUpdate = true; +} diff --git a/examples-testing/examples/webgl_read_float_buffer.ts b/examples-testing/examples/webgl_read_float_buffer.ts new file mode 100644 index 000000000..69f847729 --- /dev/null +++ b/examples-testing/examples/webgl_read_float_buffer.ts @@ -0,0 +1,153 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +let container, stats; + +let cameraRTT, sceneRTT, sceneScreen, renderer, zmesh1, zmesh2; + +let mouseX = 0, + mouseY = 0; + +const windowHalfX = window.innerWidth / 2; +const windowHalfY = window.innerHeight / 2; + +let rtTexture, material, quad; + +let delta = 0.01; +let valueNode; + +init(); + +function init() { + container = document.getElementById('container'); + + cameraRTT = new THREE.OrthographicCamera( + window.innerWidth / -2, + window.innerWidth / 2, + window.innerHeight / 2, + window.innerHeight / -2, + 1, + 1000, + ); + cameraRTT.position.z = 500; + + // + + sceneRTT = new THREE.Scene(); + sceneScreen = new THREE.Scene(); + + let light = new THREE.DirectionalLight(0xffffff, 3); + light.position.set(0, 0, 1).normalize(); + sceneRTT.add(light); + + light = new THREE.DirectionalLight(0xffd5d5, 4.5); + light.position.set(0, 0, -1).normalize(); + sceneRTT.add(light); + + rtTexture = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight, { + minFilter: THREE.LinearFilter, + magFilter: THREE.NearestFilter, + format: THREE.RGBAFormat, + type: THREE.FloatType, + }); + + material = new THREE.ShaderMaterial({ + uniforms: { time: { value: 0.0 } }, + vertexShader: document.getElementById('vertexShader').textContent, + fragmentShader: document.getElementById('fragment_shader_pass_1').textContent, + }); + + const materialScreen = new THREE.ShaderMaterial({ + uniforms: { tDiffuse: { value: rtTexture.texture } }, + vertexShader: document.getElementById('vertexShader').textContent, + fragmentShader: document.getElementById('fragment_shader_screen').textContent, + + depthWrite: false, + }); + + const plane = new THREE.PlaneGeometry(window.innerWidth, window.innerHeight); + + quad = new THREE.Mesh(plane, material); + quad.position.z = -100; + sceneRTT.add(quad); + + const geometry = new THREE.TorusGeometry(100, 25, 15, 30); + + const mat1 = new THREE.MeshPhongMaterial({ color: 0x9c9c9c, specular: 0xffaa00, shininess: 5 }); + const mat2 = new THREE.MeshPhongMaterial({ color: 0x9c0000, specular: 0xff2200, shininess: 5 }); + + zmesh1 = new THREE.Mesh(geometry, mat1); + zmesh1.position.set(0, 0, 100); + zmesh1.scale.set(1.5, 1.5, 1.5); + sceneRTT.add(zmesh1); + + zmesh2 = new THREE.Mesh(geometry, mat2); + zmesh2.position.set(0, 150, 100); + zmesh2.scale.set(0.75, 0.75, 0.75); + sceneRTT.add(zmesh2); + + quad = new THREE.Mesh(plane, materialScreen); + quad.position.z = -100; + sceneScreen.add(quad); + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.autoClear = false; + + container.appendChild(renderer.domElement); + + stats = new Stats(); + container.appendChild(stats.dom); + + valueNode = document.getElementById('values'); + + document.addEventListener('mousemove', onDocumentMouseMove); +} + +function onDocumentMouseMove(event) { + mouseX = event.clientX - windowHalfX; + mouseY = event.clientY - windowHalfY; +} + +// + +function animate() { + render(); + stats.update(); +} + +function render() { + const time = Date.now() * 0.0015; + + if (zmesh1 && zmesh2) { + zmesh1.rotation.y = -time; + zmesh2.rotation.y = -time + Math.PI / 2; + } + + if (material.uniforms['time'].value > 1 || material.uniforms['time'].value < 0) { + delta *= -1; + } + + material.uniforms['time'].value += delta; + + renderer.clear(); + + // Render first scene into texture + + renderer.setRenderTarget(rtTexture); + renderer.clear(); + renderer.render(sceneRTT, cameraRTT); + + // Render full screen quad with generated texture + + renderer.setRenderTarget(null); + renderer.render(sceneScreen, cameraRTT); + + const read = new Float32Array(4); + renderer.readRenderTargetPixels(rtTexture, windowHalfX + mouseX, windowHalfY - mouseY, 1, 1, read); + + valueNode.innerHTML = 'r:' + read[0] + '
g:' + read[1] + '
b:' + read[2]; +} diff --git a/examples-testing/examples/webgl_refraction.ts b/examples-testing/examples/webgl_refraction.ts new file mode 100644 index 000000000..b8ef7143d --- /dev/null +++ b/examples-testing/examples/webgl_refraction.ts @@ -0,0 +1,138 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { Refractor } from 'three/addons/objects/Refractor.js'; +import { WaterRefractionShader } from 'three/addons/shaders/WaterRefractionShader.js'; + +let camera, scene, renderer, timer; + +let refractor, smallSphere; + +init(); + +async function init() { + const container = document.getElementById('container'); + + timer = new THREE.Timer(); + timer.connect(document); + + // scene + scene = new THREE.Scene(); + + // camera + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 500); + camera.position.set(0, 75, 160); + + // refractor + + const refractorGeometry = new THREE.PlaneGeometry(90, 90); + + refractor = new Refractor(refractorGeometry, { + color: 0xcbcbcb, + textureWidth: 1024, + textureHeight: 1024, + shader: WaterRefractionShader, + }); + + refractor.position.set(0, 50, 0); + + scene.add(refractor); + + // load dudv map for distortion effect + + const loader = new THREE.TextureLoader(); + const dudvMap = await loader.loadAsync('textures/waterdudv.jpg'); + + dudvMap.wrapS = dudvMap.wrapT = THREE.RepeatWrapping; + refractor.material.uniforms.tDudv.value = dudvMap; + + // + + const geometry = new THREE.IcosahedronGeometry(5, 0); + const material = new THREE.MeshPhongMaterial({ color: 0xffffff, emissive: 0x333333, flatShading: true }); + smallSphere = new THREE.Mesh(geometry, material); + scene.add(smallSphere); + + // walls + const planeGeo = new THREE.PlaneGeometry(100.1, 100.1); + + const planeTop = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xffffff })); + planeTop.position.y = 100; + planeTop.rotateX(Math.PI / 2); + scene.add(planeTop); + + const planeBottom = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xffffff })); + planeBottom.rotateX(-Math.PI / 2); + scene.add(planeBottom); + + const planeBack = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0x7f7fff })); + planeBack.position.z = -50; + planeBack.position.y = 50; + scene.add(planeBack); + + const planeRight = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0x00ff00 })); + planeRight.position.x = 50; + planeRight.position.y = 50; + planeRight.rotateY(-Math.PI / 2); + scene.add(planeRight); + + const planeLeft = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xff0000 })); + planeLeft.position.x = -50; + planeLeft.position.y = 50; + planeLeft.rotateY(Math.PI / 2); + scene.add(planeLeft); + + // lights + const mainLight = new THREE.PointLight(0xe7e7e7, 2.5, 250, 0); + mainLight.position.y = 60; + scene.add(mainLight); + + const greenLight = new THREE.PointLight(0x00ff00, 0.5, 1000, 0); + greenLight.position.set(550, 50, 0); + scene.add(greenLight); + + const redLight = new THREE.PointLight(0xff0000, 0.5, 1000, 0); + redLight.position.set(-550, 50, 0); + scene.add(redLight); + + const blueLight = new THREE.PointLight(0xbbbbfe, 0.5, 1000, 0); + blueLight.position.set(0, 50, 550); + scene.add(blueLight); + + // renderer + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + // controls + const controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, 40, 0); + controls.maxDistance = 400; + controls.minDistance = 10; + controls.update(); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + const time = timer.getElapsed(); + + refractor.material.uniforms.time.value = time; + + smallSphere.position.set(Math.cos(time) * 30, Math.abs(Math.cos(time * 2)) * 20 + 5, Math.sin(time) * 30); + smallSphere.rotation.y = Math.PI / 2 - time; + smallSphere.rotation.z = time * 8; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_rtt.ts b/examples-testing/examples/webgl_rtt.ts new file mode 100644 index 000000000..b80e78ed3 --- /dev/null +++ b/examples-testing/examples/webgl_rtt.ts @@ -0,0 +1,171 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +let container, stats; + +let cameraRTT, camera, sceneRTT, sceneScreen, scene, renderer, zmesh1, zmesh2; + +let mouseX = 0, + mouseY = 0; + +const windowHalfX = window.innerWidth / 2; +const windowHalfY = window.innerHeight / 2; + +let rtTexture, material, quad; + +let delta = 0.01; + +init(); + +function init() { + container = document.getElementById('container'); + + camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 10000); + camera.position.z = 100; + + cameraRTT = new THREE.OrthographicCamera( + window.innerWidth / -2, + window.innerWidth / 2, + window.innerHeight / 2, + window.innerHeight / -2, + 1, + 1000, + ); + cameraRTT.position.z = 500; + + // + + scene = new THREE.Scene(); + sceneRTT = new THREE.Scene(); + sceneScreen = new THREE.Scene(); + + let light = new THREE.DirectionalLight(0xffffff, 3); + light.position.set(0, 0, 1).normalize(); + sceneRTT.add(light); + + light = new THREE.DirectionalLight(0xffd5d5, 4.5); + light.position.set(0, 0, -1).normalize(); + sceneRTT.add(light); + + rtTexture = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight); + + material = new THREE.ShaderMaterial({ + uniforms: { time: { value: 0.0 } }, + vertexShader: document.getElementById('vertexShader').textContent, + fragmentShader: document.getElementById('fragment_shader_pass_1').textContent, + }); + + const materialScreen = new THREE.ShaderMaterial({ + uniforms: { tDiffuse: { value: rtTexture.texture } }, + vertexShader: document.getElementById('vertexShader').textContent, + fragmentShader: document.getElementById('fragment_shader_screen').textContent, + + depthWrite: false, + }); + + const plane = new THREE.PlaneGeometry(window.innerWidth, window.innerHeight); + + quad = new THREE.Mesh(plane, material); + quad.position.z = -100; + sceneRTT.add(quad); + + const torusGeometry = new THREE.TorusGeometry(100, 25, 15, 30); + + const mat1 = new THREE.MeshPhongMaterial({ color: 0x9c9c9c, specular: 0xffaa00, shininess: 5 }); + const mat2 = new THREE.MeshPhongMaterial({ color: 0x9c0000, specular: 0xff2200, shininess: 5 }); + + zmesh1 = new THREE.Mesh(torusGeometry, mat1); + zmesh1.position.set(0, 0, 100); + zmesh1.scale.set(1.5, 1.5, 1.5); + sceneRTT.add(zmesh1); + + zmesh2 = new THREE.Mesh(torusGeometry, mat2); + zmesh2.position.set(0, 150, 100); + zmesh2.scale.set(0.75, 0.75, 0.75); + sceneRTT.add(zmesh2); + + quad = new THREE.Mesh(plane, materialScreen); + quad.position.z = -100; + sceneScreen.add(quad); + + const n = 5, + geometry = new THREE.SphereGeometry(10, 64, 32), + material2 = new THREE.MeshBasicMaterial({ color: 0xffffff, map: rtTexture.texture }); + + for (let j = 0; j < n; j++) { + for (let i = 0; i < n; i++) { + const mesh = new THREE.Mesh(geometry, material2); + + mesh.position.x = (i - (n - 1) / 2) * 20; + mesh.position.y = (j - (n - 1) / 2) * 20; + mesh.position.z = 0; + + mesh.rotation.y = -Math.PI / 2; + + scene.add(mesh); + } + } + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.autoClear = false; + + container.appendChild(renderer.domElement); + + stats = new Stats(); + container.appendChild(stats.dom); + + document.addEventListener('mousemove', onDocumentMouseMove); +} + +function onDocumentMouseMove(event) { + mouseX = event.clientX - windowHalfX; + mouseY = event.clientY - windowHalfY; +} + +// + +function animate() { + render(); + stats.update(); +} + +function render() { + const time = Date.now() * 0.0015; + + camera.position.x += (mouseX - camera.position.x) * 0.05; + camera.position.y += (-mouseY - camera.position.y) * 0.05; + + camera.lookAt(scene.position); + + if (zmesh1 && zmesh2) { + zmesh1.rotation.y = -time; + zmesh2.rotation.y = -time + Math.PI / 2; + } + + if (material.uniforms['time'].value > 1 || material.uniforms['time'].value < 0) { + delta *= -1; + } + + material.uniforms['time'].value += delta; + + // Render first scene into texture + + renderer.setRenderTarget(rtTexture); + renderer.clear(); + renderer.render(sceneRTT, cameraRTT); + + // Render full screen quad with generated texture + + renderer.setRenderTarget(null); + renderer.clear(); + renderer.render(sceneScreen, cameraRTT); + + // Render second scene to screen + // (using first scene as regular texture) + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_shader.ts b/examples-testing/examples/webgl_shader.ts new file mode 100644 index 000000000..47a6c7ece --- /dev/null +++ b/examples-testing/examples/webgl_shader.ts @@ -0,0 +1,50 @@ +import * as THREE from 'three'; + +let camera, scene, renderer; + +let uniforms; + +init(); + +function init() { + const container = document.getElementById('container'); + + camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1); + + scene = new THREE.Scene(); + + const geometry = new THREE.PlaneGeometry(2, 2); + + uniforms = { + time: { value: 1.0 }, + }; + + const material = new THREE.ShaderMaterial({ + uniforms: uniforms, + vertexShader: document.getElementById('vertexShader').textContent, + fragmentShader: document.getElementById('fragmentShader').textContent, + }); + + const mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + uniforms['time'].value = performance.now() / 1000; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_shader_lava.ts b/examples-testing/examples/webgl_shader_lava.ts new file mode 100644 index 000000000..0c974f5b9 --- /dev/null +++ b/examples-testing/examples/webgl_shader_lava.ts @@ -0,0 +1,104 @@ +import * as THREE from 'three'; + +import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; +import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; +import { BloomPass } from 'three/addons/postprocessing/BloomPass.js'; +import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; + +let camera, renderer, composer, timer; + +let uniforms, mesh; + +init(); + +function init() { + const container = document.getElementById('container'); + + camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 3000); + camera.position.z = 4; + + const scene = new THREE.Scene(); + + timer = new THREE.Timer(); + timer.connect(document); + + const textureLoader = new THREE.TextureLoader(); + + const cloudTexture = textureLoader.load('textures/lava/cloud.png'); + const lavaTexture = textureLoader.load('textures/lava/lavatile.jpg'); + + lavaTexture.colorSpace = THREE.SRGBColorSpace; + + cloudTexture.wrapS = cloudTexture.wrapT = THREE.RepeatWrapping; + lavaTexture.wrapS = lavaTexture.wrapT = THREE.RepeatWrapping; + + uniforms = { + fogDensity: { value: 0.45 }, + fogColor: { value: new THREE.Vector3(0, 0, 0) }, + time: { value: 1.0 }, + uvScale: { value: new THREE.Vector2(3.0, 1.0) }, + texture1: { value: cloudTexture }, + texture2: { value: lavaTexture }, + }; + + const size = 0.65; + + const material = new THREE.ShaderMaterial({ + uniforms: uniforms, + vertexShader: document.getElementById('vertexShader').textContent, + fragmentShader: document.getElementById('fragmentShader').textContent, + }); + + mesh = new THREE.Mesh(new THREE.TorusGeometry(size, 0.3, 30, 30), material); + mesh.rotation.x = 0.3; + scene.add(mesh); + + // + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.autoClear = false; + container.appendChild(renderer.domElement); + + // + + const renderModel = new RenderPass(scene, camera); + const effectBloom = new BloomPass(1.25); + const outputPass = new OutputPass(); + + composer = new EffectComposer(renderer); + + composer.addPass(renderModel); + composer.addPass(effectBloom); + composer.addPass(outputPass); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + composer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + timer.update(); + + const delta = 5 * timer.getDelta(); + + uniforms['time'].value += 0.2 * delta; + + mesh.rotation.y += 0.0125 * delta; + mesh.rotation.x += 0.05 * delta; + + renderer.clear(); + composer.render(0.01); +} diff --git a/examples-testing/examples/webgl_shaders_ocean.ts b/examples-testing/examples/webgl_shaders_ocean.ts new file mode 100644 index 000000000..e0e26ed33 --- /dev/null +++ b/examples-testing/examples/webgl_shaders_ocean.ts @@ -0,0 +1,195 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { Water } from 'three/addons/objects/Water.js'; +import { Sky } from 'three/addons/objects/Sky.js'; +import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js'; + +let container, stats; +let camera, scene, renderer; +let controls, water, sun, sky, mesh, bloomPass; + +init(); + +function init() { + container = document.getElementById('container'); + + // + + renderer = new THREE.WebGLRenderer({ outputBufferType: THREE.HalfFloatType }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = 0.1; + container.appendChild(renderer.domElement); + + bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85); + bloomPass.threshold = 0; + bloomPass.strength = 0.1; + bloomPass.radius = 0; + renderer.setEffects([bloomPass]); + + // + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight, 1, 20000); + camera.position.set(30, 30, 100); + + // + + sun = new THREE.Vector3(); + + // Water + + const waterGeometry = new THREE.PlaneGeometry(10000, 10000); + + water = new Water(waterGeometry, { + textureWidth: 512, + textureHeight: 512, + waterNormals: new THREE.TextureLoader().load('textures/waternormals.jpg', function (texture) { + texture.wrapS = texture.wrapT = THREE.RepeatWrapping; + }), + sunDirection: new THREE.Vector3(), + sunColor: 0xffffff, + waterColor: 0x001e0f, + distortionScale: 3.7, + fog: scene.fog !== undefined, + }); + + water.rotation.x = -Math.PI / 2; + + scene.add(water); + + // Skybox + + sky = new Sky(); + sky.scale.setScalar(10000); + scene.add(sky); + + const skyUniforms = sky.material.uniforms; + + skyUniforms['turbidity'].value = 10; + skyUniforms['rayleigh'].value = 2; + skyUniforms['mieCoefficient'].value = 0.005; + skyUniforms['mieDirectionalG'].value = 0.8; + skyUniforms['cloudCoverage'].value = 0.4; + skyUniforms['cloudDensity'].value = 0.5; + skyUniforms['cloudElevation'].value = 0.5; + + const parameters = { + elevation: 2, + azimuth: 180, + exposure: 0.1, + }; + + const pmremGenerator = new THREE.PMREMGenerator(renderer); + const sceneEnv = new THREE.Scene(); + + let renderTarget; + + function updateSun() { + const phi = THREE.MathUtils.degToRad(90 - parameters.elevation); + const theta = THREE.MathUtils.degToRad(parameters.azimuth); + + sun.setFromSphericalCoords(1, phi, theta); + + sky.material.uniforms['sunPosition'].value.copy(sun); + water.material.uniforms['sunDirection'].value.copy(sun).normalize(); + + if (renderTarget !== undefined) renderTarget.dispose(); + + sceneEnv.add(sky); + renderTarget = pmremGenerator.fromScene(sceneEnv); + scene.add(sky); + + scene.environment = renderTarget.texture; + } + + updateSun(); + + // + + const geometry = new THREE.BoxGeometry(30, 30, 30); + const material = new THREE.MeshStandardMaterial({ roughness: 0 }); + + mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + // + + controls = new OrbitControls(camera, renderer.domElement); + controls.maxPolarAngle = Math.PI * 0.495; + controls.target.set(0, 10, 0); + controls.minDistance = 40.0; + controls.maxDistance = 200.0; + controls.update(); + + // + + stats = new Stats(); + container.appendChild(stats.dom); + + // GUI + + const gui = new GUI(); + + const folderSky = gui.addFolder('Sky'); + folderSky.add(parameters, 'elevation', 0, 90, 0.1).onChange(updateSun); + folderSky.add(parameters, 'azimuth', -180, 180, 0.1).onChange(updateSun); + folderSky.add(parameters, 'exposure', 0, 1, 0.0001).onChange(function (value) { + renderer.toneMappingExposure = value; + }); + folderSky.open(); + + const waterUniforms = water.material.uniforms; + + const folderWater = gui.addFolder('Water'); + folderWater.add(waterUniforms.distortionScale, 'value', 0, 8, 0.1).name('distortionScale'); + folderWater.add(waterUniforms.size, 'value', 0.1, 10, 0.1).name('size'); + folderWater.open(); + + const folderBloom = gui.addFolder('Bloom'); + folderBloom.add(bloomPass, 'strength', 0, 3, 0.01); + folderBloom.add(bloomPass, 'radius', 0, 1, 0.01); + folderBloom.open(); + + const folderClouds = gui.addFolder('Clouds'); + folderClouds.add(skyUniforms.cloudCoverage, 'value', 0, 1, 0.01).name('coverage'); + folderClouds.add(skyUniforms.cloudDensity, 'value', 0, 1, 0.01).name('density'); + folderClouds.add(skyUniforms.cloudElevation, 'value', 0, 1, 0.01).name('elevation'); + folderClouds.open(); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + render(); + stats.update(); +} + +function render() { + const time = performance.now() * 0.001; + + mesh.position.y = Math.sin(time) * 20 + 5; + mesh.rotation.x = time * 0.5; + mesh.rotation.z = time * 0.51; + + water.material.uniforms['time'].value += 1.0 / 60.0; + sky.material.uniforms['time'].value = time; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_shaders_sky.ts b/examples-testing/examples/webgl_shaders_sky.ts new file mode 100644 index 000000000..01cdddec4 --- /dev/null +++ b/examples-testing/examples/webgl_shaders_sky.ts @@ -0,0 +1,114 @@ +import * as THREE from 'three'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { Sky } from 'three/addons/objects/Sky.js'; + +let camera, scene, renderer; + +let sky, sun; + +init(); + +function initSky() { + // Add Sky + sky = new Sky(); + sky.scale.setScalar(450000); + scene.add(sky); + + sun = new THREE.Vector3(); + + /// GUI + + const effectController = { + turbidity: 10, + rayleigh: 3, + mieCoefficient: 0.005, + mieDirectionalG: 0.7, + elevation: 2, + azimuth: 180, + exposure: renderer.toneMappingExposure, + cloudCoverage: 0.4, + cloudDensity: 0.4, + cloudElevation: 0.5, + showSunDisc: true, + }; + + function guiChanged() { + const uniforms = sky.material.uniforms; + uniforms['turbidity'].value = effectController.turbidity; + uniforms['rayleigh'].value = effectController.rayleigh; + uniforms['mieCoefficient'].value = effectController.mieCoefficient; + uniforms['mieDirectionalG'].value = effectController.mieDirectionalG; + uniforms['cloudCoverage'].value = effectController.cloudCoverage; + uniforms['cloudDensity'].value = effectController.cloudDensity; + uniforms['cloudElevation'].value = effectController.cloudElevation; + uniforms['showSunDisc'].value = effectController.showSunDisc; + + const phi = THREE.MathUtils.degToRad(90 - effectController.elevation); + const theta = THREE.MathUtils.degToRad(effectController.azimuth); + + sun.setFromSphericalCoords(1, phi, theta); + + uniforms['sunPosition'].value.copy(sun); + + renderer.toneMappingExposure = effectController.exposure; + } + + const gui = new GUI(); + + gui.add(effectController, 'turbidity', 0.0, 20.0, 0.1).onChange(guiChanged); + gui.add(effectController, 'rayleigh', 0.0, 4, 0.001).onChange(guiChanged); + gui.add(effectController, 'mieCoefficient', 0.0, 0.1, 0.001).onChange(guiChanged); + gui.add(effectController, 'mieDirectionalG', 0.0, 1, 0.001).onChange(guiChanged); + gui.add(effectController, 'elevation', 0, 90, 0.1).onChange(guiChanged); + gui.add(effectController, 'azimuth', -180, 180, 0.1).onChange(guiChanged); + gui.add(effectController, 'exposure', 0, 1, 0.0001).onChange(guiChanged); + gui.add(effectController, 'showSunDisc').onChange(guiChanged); + + const folderClouds = gui.addFolder('Clouds'); + folderClouds.add(effectController, 'cloudCoverage', 0, 1, 0.01).name('coverage').onChange(guiChanged); + folderClouds.add(effectController, 'cloudDensity', 0, 1, 0.01).name('density').onChange(guiChanged); + folderClouds.add(effectController, 'cloudElevation', 0, 1, 0.01).name('elevation').onChange(guiChanged); + + guiChanged(); +} + +function init() { + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 100, 2000000); + camera.position.set(0, 100, 2000); + + scene = new THREE.Scene(); + + const helper = new THREE.GridHelper(10000, 2, 0xffffff, 0xffffff); + scene.add(helper); + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = 0.5; + document.body.appendChild(renderer.domElement); + + const controls = new OrbitControls(camera, renderer.domElement); + //controls.maxPolarAngle = Math.PI / 2; + controls.enableZoom = false; + controls.enablePan = false; + + initSky(); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + sky.material.uniforms['time'].value = performance.now() * 0.001; + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_shadow_contact.ts b/examples-testing/examples/webgl_shadow_contact.ts new file mode 100644 index 000000000..f402fa20d --- /dev/null +++ b/examples-testing/examples/webgl_shadow_contact.ts @@ -0,0 +1,272 @@ +import * as THREE from 'three'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import Stats from 'three/addons/libs/stats.module.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import { HorizontalBlurShader } from 'three/addons/shaders/HorizontalBlurShader.js'; +import { VerticalBlurShader } from 'three/addons/shaders/VerticalBlurShader.js'; + +let camera, scene, renderer, stats, gui; + +const meshes = []; + +const PLANE_WIDTH = 2.5; +const PLANE_HEIGHT = 2.5; +const CAMERA_HEIGHT = 0.3; + +const state = { + shadow: { + blur: 3.5, + darkness: 1, + opacity: 1, + }, + plane: { + color: '#ffffff', + opacity: 1, + }, + showWireframe: false, +}; + +let shadowGroup, + renderTarget, + renderTargetBlur, + shadowCamera, + cameraHelper, + depthMaterial, + horizontalBlurMaterial, + verticalBlurMaterial; + +let plane, blurPlane, fillPlane; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(0.5, 1, 2); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xffffff); + + stats = new Stats(); + document.body.appendChild(stats.dom); + + window.addEventListener('resize', onWindowResize); + + // add the example meshes + + const geometries = [ + new THREE.BoxGeometry(0.4, 0.4, 0.4), + new THREE.IcosahedronGeometry(0.3), + new THREE.TorusKnotGeometry(0.4, 0.05, 256, 24, 1, 3), + ]; + + const material = new THREE.MeshNormalMaterial(); + + for (let i = 0, l = geometries.length; i < l; i++) { + const angle = (i / l) * Math.PI * 2; + + const geometry = geometries[i]; + const mesh = new THREE.Mesh(geometry, material); + mesh.position.y = 0.1; + mesh.position.x = Math.cos(angle) / 2.0; + mesh.position.z = Math.sin(angle) / 2.0; + scene.add(mesh); + meshes.push(mesh); + } + + // the container, if you need to move the plane just move this + shadowGroup = new THREE.Group(); + shadowGroup.position.y = -0.3; + scene.add(shadowGroup); + + // the render target that will show the shadows in the plane texture + renderTarget = new THREE.WebGLRenderTarget(512, 512); + renderTarget.texture.generateMipmaps = false; + + // the render target that we will use to blur the first render target + renderTargetBlur = new THREE.WebGLRenderTarget(512, 512); + renderTargetBlur.texture.generateMipmaps = false; + + // make a plane and make it face up + const planeGeometry = new THREE.PlaneGeometry(PLANE_WIDTH, PLANE_HEIGHT).rotateX(Math.PI / 2); + const planeMaterial = new THREE.MeshBasicMaterial({ + map: renderTarget.texture, + opacity: state.shadow.opacity, + transparent: true, + depthWrite: false, + }); + plane = new THREE.Mesh(planeGeometry, planeMaterial); + // make sure it's rendered after the fillPlane + plane.renderOrder = 1; + shadowGroup.add(plane); + + // the y from the texture is flipped! + plane.scale.y = -1; + + // the plane onto which to blur the texture + blurPlane = new THREE.Mesh(planeGeometry); + blurPlane.visible = false; + shadowGroup.add(blurPlane); + + // the plane with the color of the ground + const fillPlaneMaterial = new THREE.MeshBasicMaterial({ + color: state.plane.color, + opacity: state.plane.opacity, + transparent: true, + depthWrite: false, + }); + fillPlane = new THREE.Mesh(planeGeometry, fillPlaneMaterial); + fillPlane.rotateX(Math.PI); + shadowGroup.add(fillPlane); + + // the camera to render the depth material from + shadowCamera = new THREE.OrthographicCamera( + -PLANE_WIDTH / 2, + PLANE_WIDTH / 2, + PLANE_HEIGHT / 2, + -PLANE_HEIGHT / 2, + 0, + CAMERA_HEIGHT, + ); + shadowCamera.rotation.x = Math.PI / 2; // get the camera to look up + shadowGroup.add(shadowCamera); + + cameraHelper = new THREE.CameraHelper(shadowCamera); + + // like MeshDepthMaterial, but goes from black to transparent + depthMaterial = new THREE.MeshDepthMaterial(); + depthMaterial.userData.darkness = { value: state.shadow.darkness }; + depthMaterial.onBeforeCompile = function (shader) { + shader.uniforms.darkness = depthMaterial.userData.darkness; + shader.fragmentShader = /* glsl */ ` + uniform float darkness; + ${shader.fragmentShader.replace( + 'gl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity );', + 'gl_FragColor = vec4( vec3( 0.0 ), ( 1.0 - fragCoordZ ) * darkness );', + )} + `; + }; + + depthMaterial.depthTest = false; + depthMaterial.depthWrite = false; + + horizontalBlurMaterial = new THREE.ShaderMaterial(HorizontalBlurShader); + horizontalBlurMaterial.depthTest = false; + + verticalBlurMaterial = new THREE.ShaderMaterial(VerticalBlurShader); + verticalBlurMaterial.depthTest = false; + + // + + gui = new GUI(); + const shadowFolder = gui.addFolder('shadow'); + shadowFolder.open(); + const planeFolder = gui.addFolder('plane'); + planeFolder.open(); + + shadowFolder.add(state.shadow, 'blur', 0, 15, 0.1); + shadowFolder.add(state.shadow, 'darkness', 1, 5, 0.1).onChange(function () { + depthMaterial.userData.darkness.value = state.shadow.darkness; + }); + shadowFolder.add(state.shadow, 'opacity', 0, 1, 0.01).onChange(function () { + plane.material.opacity = state.shadow.opacity; + }); + planeFolder.addColor(state.plane, 'color').onChange(function () { + fillPlane.material.color = new THREE.Color(state.plane.color); + }); + planeFolder.add(state.plane, 'opacity', 0, 1, 0.01).onChange(function () { + fillPlane.material.opacity = state.plane.opacity; + }); + + gui.add(state, 'showWireframe').onChange(function () { + if (state.showWireframe) { + scene.add(cameraHelper); + } else { + scene.remove(cameraHelper); + } + }); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // + + new OrbitControls(camera, renderer.domElement); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// renderTarget --> blurPlane (horizontalBlur) --> renderTargetBlur --> blurPlane (verticalBlur) --> renderTarget +function blurShadow(amount) { + blurPlane.visible = true; + + // blur horizontally and draw in the renderTargetBlur + blurPlane.material = horizontalBlurMaterial; + blurPlane.material.uniforms.tDiffuse.value = renderTarget.texture; + horizontalBlurMaterial.uniforms.h.value = (amount * 1) / 256; + + renderer.setRenderTarget(renderTargetBlur); + renderer.render(blurPlane, shadowCamera); + + // blur vertically and draw in the main renderTarget + blurPlane.material = verticalBlurMaterial; + blurPlane.material.uniforms.tDiffuse.value = renderTargetBlur.texture; + verticalBlurMaterial.uniforms.v.value = (amount * 1) / 256; + + renderer.setRenderTarget(renderTarget); + renderer.render(blurPlane, shadowCamera); + + blurPlane.visible = false; +} + +function animate() { + meshes.forEach(mesh => { + mesh.rotation.x += 0.01; + mesh.rotation.y += 0.02; + }); + + // + + // remove the background + const initialBackground = scene.background; + scene.background = null; + + // force the depthMaterial to everything + cameraHelper.visible = false; + scene.overrideMaterial = depthMaterial; + + // set renderer clear alpha + const initialClearAlpha = renderer.getClearAlpha(); + renderer.setClearAlpha(0); + + // render to the render target to get the depths + renderer.setRenderTarget(renderTarget); + renderer.render(scene, shadowCamera); + + // and reset the override material + scene.overrideMaterial = null; + cameraHelper.visible = true; + + blurShadow(state.shadow.blur); + + // a second pass to reduce the artifacts + // (0.4 is the minimum blur amount so that the artifacts are gone) + blurShadow(state.shadow.blur * 0.4); + + // reset and render the normal scene + renderer.setRenderTarget(null); + renderer.setClearAlpha(initialClearAlpha); + scene.background = initialBackground; + + renderer.render(scene, camera); + stats.update(); +} diff --git a/examples-testing/examples/webgl_shadowmap.ts b/examples-testing/examples/webgl_shadowmap.ts new file mode 100644 index 000000000..9557bb051 --- /dev/null +++ b/examples-testing/examples/webgl_shadowmap.ts @@ -0,0 +1,310 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { FontLoader } from 'three/addons/loaders/FontLoader.js'; +import { TextGeometry } from 'three/addons/geometries/TextGeometry.js'; +import { ShadowMapViewer } from 'three/addons/utils/ShadowMapViewer.js'; + +const SHADOW_MAP_WIDTH = 2048, + SHADOW_MAP_HEIGHT = 1024; + +const SCREEN_WIDTH = window.innerWidth; +const SCREEN_HEIGHT = window.innerHeight; +const FLOOR = -250; + +let camera, controls, scene, renderer; +let container, stats; + +const NEAR = 10, + FAR = 3000; + +let mixer; + +const morphs = []; + +let light; +let lightShadowMapViewer; + +const timer = new THREE.Timer(); +timer.connect(document); + +let showHUD = false; + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + // CAMERA + + camera = new THREE.PerspectiveCamera(23, SCREEN_WIDTH / SCREEN_HEIGHT, NEAR, FAR); + camera.position.set(700, 50, 1900); + + // SCENE + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x59472b); + scene.fog = new THREE.Fog(0x59472b, 1000, FAR); + + // LIGHTS + + const ambient = new THREE.AmbientLight(0xffffff); + scene.add(ambient); + + light = new THREE.DirectionalLight(0xffffff, 3); + light.position.set(0, 1500, 1000); + light.castShadow = true; + light.shadow.camera.top = 2000; + light.shadow.camera.bottom = -2000; + light.shadow.camera.left = -2000; + light.shadow.camera.right = 2000; + light.shadow.camera.near = 1200; + light.shadow.camera.far = 2500; + light.shadow.bias = 0.0001; + + light.shadow.mapSize.width = SHADOW_MAP_WIDTH; + light.shadow.mapSize.height = SHADOW_MAP_HEIGHT; + + scene.add(light); + + createHUD(); + createScene(); + + // RENDERER + + renderer = new THREE.WebGLRenderer({ antialias: true, reversedDepthBuffer: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + renderer.autoClear = false; + + // + + renderer.shadowMap.enabled = true; + renderer.shadowMap.type = THREE.PCFShadowMap; + + // CONTROLS + + controls = new OrbitControls(camera, renderer.domElement); + controls.enablePan = false; + controls.maxPolarAngle = Math.PI / 2; + controls.minDistance = 200; + controls.maxDistance = 2200; + + controls.target.set(0, -75, 25); + controls.update(); + + // STATS + + stats = new Stats(); + //container.appendChild( stats.dom ); + + // + + window.addEventListener('resize', onWindowResize); + window.addEventListener('keydown', onKeyDown); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function onKeyDown(event) { + switch (event.keyCode) { + case 84 /*t*/: + showHUD = !showHUD; + break; + } +} + +function createHUD() { + lightShadowMapViewer = new ShadowMapViewer(light); + lightShadowMapViewer.position.x = 10; + lightShadowMapViewer.position.y = SCREEN_HEIGHT - SHADOW_MAP_HEIGHT / 4 - 10; + lightShadowMapViewer.size.width = SHADOW_MAP_WIDTH / 4; + lightShadowMapViewer.size.height = SHADOW_MAP_HEIGHT / 4; + lightShadowMapViewer.update(); +} + +function createScene() { + // GROUND + + const geometry = new THREE.PlaneGeometry(100, 100); + const planeMaterial = new THREE.MeshPhongMaterial({ color: 0xffdd99 }); + + const ground = new THREE.Mesh(geometry, planeMaterial); + + ground.position.set(0, FLOOR, 0); + ground.rotation.x = -Math.PI / 2; + ground.scale.set(100, 100, 100); + + ground.castShadow = false; + ground.receiveShadow = true; + + scene.add(ground); + + // TEXT + + const loader = new FontLoader(); + loader.load('fonts/helvetiker_bold.typeface.json', function (font) { + const textGeo = new TextGeometry('THREE.JS', { + font: font, + + size: 200, + depth: 50, + curveSegments: 12, + + bevelThickness: 2, + bevelSize: 5, + bevelEnabled: true, + }); + + textGeo.computeBoundingBox(); + const centerOffset = -0.5 * (textGeo.boundingBox.max.x - textGeo.boundingBox.min.x); + + const textMaterial = new THREE.MeshPhongMaterial({ color: 0xff0000, specular: 0xffffff }); + + const mesh = new THREE.Mesh(textGeo, textMaterial); + mesh.position.x = centerOffset; + mesh.position.y = FLOOR + 67; + + mesh.castShadow = true; + mesh.receiveShadow = true; + + scene.add(mesh); + }); + + // CUBES + + const cubes1 = new THREE.Mesh(new THREE.BoxGeometry(1500, 220, 150), planeMaterial); + + cubes1.position.y = FLOOR - 50; + cubes1.position.z = 20; + + cubes1.castShadow = true; + cubes1.receiveShadow = true; + + scene.add(cubes1); + + const cubes2 = new THREE.Mesh(new THREE.BoxGeometry(1600, 170, 250), planeMaterial); + + cubes2.position.y = FLOOR - 50; + cubes2.position.z = 20; + + cubes2.castShadow = true; + cubes2.receiveShadow = true; + + scene.add(cubes2); + + // MORPHS + + mixer = new THREE.AnimationMixer(scene); + + function addMorph(mesh, clip, speed, duration, x, y, z, fudgeColor) { + mesh = mesh.clone(); + mesh.material = mesh.material.clone(); + + if (fudgeColor) { + mesh.material.color.offsetHSL(0, Math.random() * 0.5 - 0.25, Math.random() * 0.5 - 0.25); + } + + mesh.speed = speed; + + mixer + .clipAction(clip, mesh) + .setDuration(duration) + // to shift the playback out of phase: + .startAt(-duration * Math.random()) + .play(); + + mesh.position.set(x, y, z); + mesh.rotation.y = Math.PI / 2; + + mesh.castShadow = true; + mesh.receiveShadow = true; + + scene.add(mesh); + + morphs.push(mesh); + } + + const gltfloader = new GLTFLoader(); + + gltfloader.load('models/gltf/Horse.glb', function (gltf) { + const mesh = gltf.scene.children[0]; + + const clip = gltf.animations[0]; + + addMorph(mesh, clip, 550, 1, 100 - Math.random() * 1000, FLOOR, 300, true); + addMorph(mesh, clip, 550, 1, 100 - Math.random() * 1000, FLOOR, 450, true); + addMorph(mesh, clip, 550, 1, 100 - Math.random() * 1000, FLOOR, 600, true); + + addMorph(mesh, clip, 550, 1, 100 - Math.random() * 1000, FLOOR, -300, true); + addMorph(mesh, clip, 550, 1, 100 - Math.random() * 1000, FLOOR, -450, true); + addMorph(mesh, clip, 550, 1, 100 - Math.random() * 1000, FLOOR, -600, true); + }); + + gltfloader.load('models/gltf/Flamingo.glb', function (gltf) { + const mesh = gltf.scene.children[0]; + const clip = gltf.animations[0]; + + addMorph(mesh, clip, 500, 1, 500 - Math.random() * 500, FLOOR + 350, 40); + }); + + gltfloader.load('models/gltf/Stork.glb', function (gltf) { + const mesh = gltf.scene.children[0]; + const clip = gltf.animations[0]; + + addMorph(mesh, clip, 350, 1, 500 - Math.random() * 500, FLOOR + 350, 340); + }); + + gltfloader.load('models/gltf/Parrot.glb', function (gltf) { + const mesh = gltf.scene.children[0]; + const clip = gltf.animations[0]; + + addMorph(mesh, clip, 450, 0.5, 500 - Math.random() * 500, FLOOR + 300, 700); + }); +} + +function animate() { + timer.update(); + + render(); + stats.update(); +} + +function render() { + const delta = timer.getDelta(); + + mixer.update(delta); + + for (let i = 0; i < morphs.length; i++) { + const morph = morphs[i]; + + morph.position.x += morph.speed * delta; + + if (morph.position.x > 2000) { + morph.position.x = -1000 - Math.random() * 500; + } + } + + controls.update(delta); + + renderer.clear(); + renderer.render(scene, camera); + + // Render debug HUD with shadow map + + if (showHUD) { + lightShadowMapViewer.render(renderer); + } +} diff --git a/examples-testing/examples/webgl_shadowmap_csm.ts b/examples-testing/examples/webgl_shadowmap_csm.ts new file mode 100644 index 000000000..c8e959a0b --- /dev/null +++ b/examples-testing/examples/webgl_shadowmap_csm.ts @@ -0,0 +1,253 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import { CSM } from 'three/addons/csm/CSM.js'; +import { CSMHelper } from 'three/addons/csm/CSMHelper.js'; + +let renderer, scene, camera, orthoCamera, controls, csm, csmHelper; + +const params = { + orthographic: false, + fade: false, + shadows: true, + far: 1000, + mode: 'practical', + lightX: -1, + lightY: -1, + lightZ: -1, + margin: 100, + lightFar: 5000, + lightNear: 1, + autoUpdateHelper: true, + updateHelper: function () { + csmHelper.update(); + }, +}; + +init(); + +function updateOrthoCamera() { + const size = controls.target.distanceTo(camera.position); + const aspect = camera.aspect; + + orthoCamera.left = (size * aspect) / -2; + orthoCamera.right = (size * aspect) / 2; + + orthoCamera.top = size / 2; + orthoCamera.bottom = size / -2; + orthoCamera.position.copy(camera.position); + orthoCamera.rotation.copy(camera.rotation); + orthoCamera.updateProjectionMatrix(); +} + +function init() { + scene = new THREE.Scene(); + scene.background = new THREE.Color('#454e61'); + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 5000); + orthoCamera = new THREE.OrthographicCamera(); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + renderer.shadowMap.enabled = params.shadows; + renderer.shadowMap.type = THREE.PCFShadowMap; + + controls = new OrbitControls(camera, renderer.domElement); + controls.maxPolarAngle = Math.PI / 2; + camera.position.set(60, 60, 0); + controls.target = new THREE.Vector3(-100, 10, 0); + controls.update(); + + const ambientLight = new THREE.AmbientLight(0xffffff, 1.5); + scene.add(ambientLight); + + const additionalDirectionalLight = new THREE.DirectionalLight(0x000020, 1.5); + additionalDirectionalLight.position + .set(params.lightX, params.lightY, params.lightZ) + .normalize() + .multiplyScalar(-200); + scene.add(additionalDirectionalLight); + + csm = new CSM({ + maxFar: params.far, + cascades: 4, + mode: params.mode, + parent: scene, + shadowMapSize: 1024, + lightDirection: new THREE.Vector3(params.lightX, params.lightY, params.lightZ).normalize(), + camera: camera, + }); + + csmHelper = new CSMHelper(csm); + csmHelper.visible = false; + scene.add(csmHelper); + + const floorMaterial = new THREE.MeshPhongMaterial({ color: '#252a34' }); + csm.setupMaterial(floorMaterial); + + const floor = new THREE.Mesh(new THREE.PlaneGeometry(10000, 10000, 8, 8), floorMaterial); + floor.rotation.x = -Math.PI / 2; + floor.castShadow = true; + floor.receiveShadow = true; + scene.add(floor); + + const material1 = new THREE.MeshPhongMaterial({ color: '#08d9d6' }); + csm.setupMaterial(material1); + + const material2 = new THREE.MeshPhongMaterial({ color: '#ff2e63' }); + csm.setupMaterial(material2); + + const geometry = new THREE.BoxGeometry(10, 10, 10); + + for (let i = 0; i < 40; i++) { + const cube1 = new THREE.Mesh(geometry, i % 2 === 0 ? material1 : material2); + cube1.castShadow = true; + cube1.receiveShadow = true; + scene.add(cube1); + cube1.position.set(-i * 25, 20, 30); + cube1.scale.y = Math.random() * 2 + 6; + + const cube2 = new THREE.Mesh(geometry, i % 2 === 0 ? material2 : material1); + cube2.castShadow = true; + cube2.receiveShadow = true; + scene.add(cube2); + cube2.position.set(-i * 25, 20, -30); + cube2.scale.y = Math.random() * 2 + 6; + } + + const gui = new GUI(); + + gui.add(params, 'orthographic').onChange(function (value) { + csm.camera = value ? orthoCamera : camera; + csm.updateFrustums(); + }); + + gui.add(params, 'fade').onChange(function (value) { + csm.fade = value; + csm.updateFrustums(); + }); + + gui.add(params, 'shadows').onChange(function (value) { + renderer.shadowMap.enabled = value; + + scene.traverse(function (child) { + if (child.material) { + child.material.needsUpdate = true; + } + }); + }); + + gui.add(params, 'far', 1, 5000) + .step(1) + .name('shadow far') + .onChange(function (value) { + csm.maxFar = value; + csm.updateFrustums(); + }); + + gui.add(params, 'mode', ['uniform', 'logarithmic', 'practical']) + .name('frustum split mode') + .onChange(function (value) { + csm.mode = value; + csm.updateFrustums(); + }); + + gui.add(params, 'lightX', -1, 1) + .name('light direction x') + .onChange(function (value) { + csm.lightDirection.x = value; + }); + + gui.add(params, 'lightY', -1, 1) + .name('light direction y') + .onChange(function (value) { + csm.lightDirection.y = value; + }); + + gui.add(params, 'lightZ', -1, 1) + .name('light direction z') + .onChange(function (value) { + csm.lightDirection.z = value; + }); + + gui.add(params, 'margin', 0, 200) + .name('light margin') + .onChange(function (value) { + csm.lightMargin = value; + }); + + gui.add(params, 'lightNear', 1, 10000) + .name('light near') + .onChange(function (value) { + for (let i = 0; i < csm.lights.length; i++) { + csm.lights[i].shadow.camera.near = value; + csm.lights[i].shadow.camera.updateProjectionMatrix(); + } + }); + + gui.add(params, 'lightFar', 1, 10000) + .name('light far') + .onChange(function (value) { + for (let i = 0; i < csm.lights.length; i++) { + csm.lights[i].shadow.camera.far = value; + csm.lights[i].shadow.camera.updateProjectionMatrix(); + } + }); + + const helperFolder = gui.addFolder('helper'); + + helperFolder.add(csmHelper, 'visible'); + + helperFolder.add(csmHelper, 'displayFrustum').onChange(function () { + csmHelper.updateVisibility(); + }); + + helperFolder.add(csmHelper, 'displayPlanes').onChange(function () { + csmHelper.updateVisibility(); + }); + + helperFolder.add(csmHelper, 'displayShadowBounds').onChange(function () { + csmHelper.updateVisibility(); + }); + + helperFolder.add(params, 'autoUpdateHelper').name('auto update'); + + helperFolder.add(params, 'updateHelper').name('update'); + + helperFolder.open(); + + window.addEventListener('resize', function () { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + updateOrthoCamera(); + csm.updateFrustums(); + + renderer.setSize(window.innerWidth, window.innerHeight); + }); +} + +function animate() { + camera.updateMatrixWorld(); + csm.update(); + controls.update(); + + if (params.orthographic) { + updateOrthoCamera(); + csm.updateFrustums(); + + if (params.autoUpdateHelper) { + csmHelper.update(); + } + + renderer.render(scene, orthoCamera); + } else { + if (params.autoUpdateHelper) { + csmHelper.update(); + } + + renderer.render(scene, camera); + } +} diff --git a/examples-testing/examples/webgl_shadowmap_pcss.ts b/examples-testing/examples/webgl_shadowmap_pcss.ts new file mode 100644 index 000000000..de5426883 --- /dev/null +++ b/examples-testing/examples/webgl_shadowmap_pcss.ts @@ -0,0 +1,165 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let stats; +let camera, scene, renderer; + +let group; + +init(); + +function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + // scene + + scene = new THREE.Scene(); + scene.fog = new THREE.Fog(0xcce0ff, 5, 100); + + // camera + + camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 10000); + + // We use this particular camera position in order to expose a bug that can sometimes happen presumably + // due to lack of precision when interpolating values over really large triangles. + // It reproduced on at least NVIDIA GTX 1080 and GTX 1050 Ti GPUs when the ground plane was not + // subdivided into segments. + camera.position.x = 7; + camera.position.y = 13; + camera.position.z = 7; + + scene.add(camera); + + // lights + + scene.add(new THREE.AmbientLight(0xaaaaaa, 3)); + + const light = new THREE.DirectionalLight(0xf0f6ff, 4.5); + light.position.set(2, 8, 4); + + light.castShadow = true; + light.shadow.mapSize.width = 1024; + light.shadow.mapSize.height = 1024; + light.shadow.camera.far = 20; + + scene.add(light); + + // scene.add( new DirectionalLightHelper( light ) ); + scene.add(new THREE.CameraHelper(light.shadow.camera)); + + // group + + group = new THREE.Group(); + scene.add(group); + + const geometry = new THREE.SphereGeometry(0.3, 20, 20); + + for (let i = 0; i < 20; i++) { + const material = new THREE.MeshPhongMaterial({ color: Math.random() * 0xffffff }); + + const sphere = new THREE.Mesh(geometry, material); + sphere.position.x = Math.random() - 0.5; + sphere.position.z = Math.random() - 0.5; + sphere.position.normalize(); + sphere.position.multiplyScalar(Math.random() * 2 + 1); + sphere.castShadow = true; + sphere.receiveShadow = true; + sphere.userData.phase = Math.random() * Math.PI; + group.add(sphere); + } + + // ground + + const groundMaterial = new THREE.MeshPhongMaterial({ color: 0x898989 }); + + const ground = new THREE.Mesh(new THREE.PlaneGeometry(20000, 20000, 8, 8), groundMaterial); + ground.rotation.x = -Math.PI / 2; + ground.receiveShadow = true; + scene.add(ground); + + // column + + const column = new THREE.Mesh(new THREE.BoxGeometry(1, 4, 1), groundMaterial); + column.position.y = 2; + column.castShadow = true; + column.receiveShadow = true; + scene.add(column); + + // overwrite shadowmap code + + let shader = THREE.ShaderChunk.shadowmap_pars_fragment; + + shader = shader.replace( + '#ifdef USE_SHADOWMAP', + '#ifdef USE_SHADOWMAP' + document.getElementById('PCSS').textContent, + ); + + shader = shader.replace( + '\t\t\tif ( frustumTest ) {\n\t\t\t\tfloat depth = texture2D( shadowMap, shadowCoord.xy ).r;', + '\t\t\tif ( frustumTest ) {\n' + + document.getElementById('PCSSGetShadow').textContent + + '\n' + + '\t\t\t\tfloat depth = texture2D( shadowMap, shadowCoord.xy ).r;', + ); + + THREE.ShaderChunk.shadowmap_pars_fragment = shader; + + // renderer + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.setClearColor(scene.fog.color); + + container.appendChild(renderer.domElement); + + renderer.shadowMap.enabled = true; + renderer.shadowMap.type = THREE.BasicShadowMap; // PCSS requires reading raw depth values + + // controls + const controls = new OrbitControls(camera, renderer.domElement); + controls.maxPolarAngle = Math.PI * 0.5; + controls.minDistance = 10; + controls.maxDistance = 75; + controls.target.set(0, 2.5, 0); + controls.update(); + + // performance monitor + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); +} + +// + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + const time = performance.now() / 1000; + + group.traverse(function (child) { + if ('phase' in child.userData) { + child.position.y = Math.abs(Math.sin(time + child.userData.phase)) * 4 + 0.3; + } + }); + + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/webgl_shadowmap_performance.ts b/examples-testing/examples/webgl_shadowmap_performance.ts new file mode 100644 index 000000000..de54f335f --- /dev/null +++ b/examples-testing/examples/webgl_shadowmap_performance.ts @@ -0,0 +1,282 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { FirstPersonControls } from 'three/addons/controls/FirstPersonControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { FontLoader } from 'three/addons/loaders/FontLoader.js'; +import { TextGeometry } from 'three/addons/geometries/TextGeometry.js'; + +const SHADOW_MAP_WIDTH = 2048, + SHADOW_MAP_HEIGHT = 1024; + +let SCREEN_WIDTH = window.innerWidth; +let SCREEN_HEIGHT = window.innerHeight; +const FLOOR = -250; + +const ANIMATION_GROUPS = 25; + +let camera, controls, scene, renderer; +let stats; + +const NEAR = 5, + FAR = 3000; + +let morph, mixer; + +const morphs = [], + animGroups = []; + +const timer = new THREE.Timer(); +timer.connect(document); + +init(); + +function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + // CAMERA + + camera = new THREE.PerspectiveCamera(23, SCREEN_WIDTH / SCREEN_HEIGHT, NEAR, FAR); + camera.position.set(700, 50, 1900); + + // SCENE + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x59472b); + scene.fog = new THREE.Fog(0x59472b, 1000, FAR); + + // LIGHTS + + const ambient = new THREE.AmbientLight(0xffffff); + scene.add(ambient); + + const light = new THREE.DirectionalLight(0xffffff, 3); + light.position.set(0, 1500, 1000); + light.castShadow = true; + light.shadow.camera.top = 2000; + light.shadow.camera.bottom = -2000; + light.shadow.camera.left = -2000; + light.shadow.camera.right = 2000; + light.shadow.camera.near = 1200; + light.shadow.camera.far = 2500; + light.shadow.bias = 0.0001; + + light.shadow.mapSize.width = SHADOW_MAP_WIDTH; + light.shadow.mapSize.height = SHADOW_MAP_HEIGHT; + + scene.add(light); + + createScene(); + + // RENDERER + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + renderer.autoClear = false; + + // + + renderer.shadowMap.enabled = true; + renderer.shadowMap.type = THREE.PCFShadowMap; + + // CONTROLS + + controls = new FirstPersonControls(camera, renderer.domElement); + + controls.lookSpeed = 0.0125; + controls.movementSpeed = 500; + controls.lookVertical = true; + + controls.lookAt(scene.position); + + // STATS + + stats = new Stats(); + container.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + SCREEN_WIDTH = window.innerWidth; + SCREEN_HEIGHT = window.innerHeight; + + camera.aspect = SCREEN_WIDTH / SCREEN_HEIGHT; + camera.updateProjectionMatrix(); + + renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); +} + +function createScene() { + // GROUND + + const geometry = new THREE.PlaneGeometry(100, 100); + const planeMaterial = new THREE.MeshPhongMaterial({ color: 0xffdd99 }); + + const ground = new THREE.Mesh(geometry, planeMaterial); + + ground.position.set(0, FLOOR, 0); + ground.rotation.x = -Math.PI / 2; + ground.scale.set(100, 100, 100); + + ground.castShadow = false; + ground.receiveShadow = true; + + scene.add(ground); + + // TEXT + + const loader = new FontLoader(); + loader.load('fonts/helvetiker_bold.typeface.json', function (font) { + const textGeo = new TextGeometry('THREE.JS', { + font: font, + + size: 200, + depth: 50, + curveSegments: 12, + + bevelThickness: 2, + bevelSize: 5, + bevelEnabled: true, + }); + + textGeo.computeBoundingBox(); + const centerOffset = -0.5 * (textGeo.boundingBox.max.x - textGeo.boundingBox.min.x); + + const textMaterial = new THREE.MeshPhongMaterial({ color: 0xff0000, specular: 0xffffff }); + + const mesh = new THREE.Mesh(textGeo, textMaterial); + mesh.position.x = centerOffset; + mesh.position.y = FLOOR + 67; + + mesh.castShadow = true; + mesh.receiveShadow = true; + + scene.add(mesh); + }); + + // CUBES + + const cubes1 = new THREE.Mesh(new THREE.BoxGeometry(1500, 220, 150), planeMaterial); + + cubes1.position.y = FLOOR - 50; + cubes1.position.z = 20; + + cubes1.castShadow = true; + cubes1.receiveShadow = true; + + scene.add(cubes1); + + const cubes2 = new THREE.Mesh(new THREE.BoxGeometry(1600, 170, 250), planeMaterial); + + cubes2.position.y = FLOOR - 50; + cubes2.position.z = 20; + + cubes2.castShadow = true; + cubes2.receiveShadow = true; + + scene.add(cubes2); + + mixer = new THREE.AnimationMixer(scene); + + for (let i = 0; i !== ANIMATION_GROUPS; ++i) { + const group = new THREE.AnimationObjectGroup(); + animGroups.push(group); + } + + // MORPHS + + function addMorph(mesh, clip, speed, duration, x, y, z, fudgeColor, massOptimization) { + mesh = mesh.clone(); + mesh.material = mesh.material.clone(); + + if (fudgeColor) { + mesh.material.color.offsetHSL(0, Math.random() * 0.5 - 0.25, Math.random() * 0.5 - 0.25); + } + + mesh.speed = speed; + + if (massOptimization) { + const index = Math.floor(Math.random() * ANIMATION_GROUPS), + animGroup = animGroups[index]; + + animGroup.add(mesh); + + if (!mixer.existingAction(clip, animGroup)) { + const randomness = 0.6 * Math.random() - 0.3; + const phase = (index + randomness) / ANIMATION_GROUPS; + + mixer + .clipAction(clip, animGroup) + .setDuration(duration) + .startAt(-duration * phase) + .play(); + } + } else { + mixer + .clipAction(clip, mesh) + .setDuration(duration) + .startAt(-duration * Math.random()) + .play(); + } + + mesh.position.set(x, y, z); + mesh.rotation.y = Math.PI / 2; + + mesh.castShadow = true; + mesh.receiveShadow = true; + + scene.add(mesh); + + morphs.push(mesh); + } + + const gltfLoader = new GLTFLoader(); + gltfLoader.load('models/gltf/Horse.glb', function (gltf) { + const mesh = gltf.scene.children[0]; + const clip = gltf.animations[0]; + + for (let i = -600; i < 601; i += 2) { + addMorph(mesh, clip, 550, 1, 100 - Math.random() * 3000, FLOOR, i, true, true); + } + }); +} + +// + +function animate() { + timer.update(); + + stats.begin(); + render(); + stats.end(); +} + +function render() { + const delta = timer.getDelta(); + + if (mixer) mixer.update(delta); + + for (let i = 0; i < morphs.length; i++) { + morph = morphs[i]; + + morph.position.x += morph.speed * delta; + + if (morph.position.x > 2000) { + morph.position.x = -1000 - Math.random() * 500; + } + } + + controls.update(delta); + + renderer.clear(); + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_shadowmap_pointlight.ts b/examples-testing/examples/webgl_shadowmap_pointlight.ts new file mode 100644 index 000000000..4323655dc --- /dev/null +++ b/examples-testing/examples/webgl_shadowmap_pointlight.ts @@ -0,0 +1,141 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let camera, scene, renderer, stats; +let pointLight, pointLight2; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(0, 10, 40); + + scene = new THREE.Scene(); + scene.add(new THREE.AmbientLight(0x111122, 3)); + + // lights + + function createLight(color) { + const intensity = 200; + + const light = new THREE.PointLight(color, intensity, 20); + light.castShadow = true; + light.shadow.bias = -0.005; // reduces self-shadowing on double-sided objects + light.shadow.mapSize.width = 128; + light.shadow.radius = 10; + + let geometry = new THREE.SphereGeometry(0.3, 12, 6); + let material = new THREE.MeshBasicMaterial({ color: color }); + material.color.multiplyScalar(intensity); + let sphere = new THREE.Mesh(geometry, material); + light.add(sphere); + + const texture = new THREE.CanvasTexture(generateTexture()); + texture.magFilter = THREE.NearestFilter; + texture.wrapT = THREE.RepeatWrapping; + texture.wrapS = THREE.RepeatWrapping; + texture.repeat.set(1, 4.5); + + geometry = new THREE.SphereGeometry(2, 32, 8); + material = new THREE.MeshPhongMaterial({ + side: THREE.DoubleSide, + alphaMap: texture, + alphaTest: 0.5, + }); + + sphere = new THREE.Mesh(geometry, material); + sphere.castShadow = true; + sphere.receiveShadow = true; + light.add(sphere); + + return light; + } + + pointLight = createLight(0x0088ff); + scene.add(pointLight); + + pointLight2 = createLight(0xff8888); + scene.add(pointLight2); + // + + const geometry = new THREE.BoxGeometry(30, 30, 30); + + const material = new THREE.MeshPhongMaterial({ + color: 0xa0adaf, + shininess: 10, + specular: 0x111111, + side: THREE.BackSide, + }); + + const mesh = new THREE.Mesh(geometry, material); + mesh.position.y = 10; + mesh.receiveShadow = true; + scene.add(mesh); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.shadowMap.enabled = true; + // renderer.shadowMap.type = THREE.BasicShadowMap; + document.body.appendChild(renderer.domElement); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, 10, 0); + controls.update(); + + stats = new Stats(); + document.body.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function generateTexture() { + const canvas = document.createElement('canvas'); + canvas.width = 2; + canvas.height = 2; + + const context = canvas.getContext('2d'); + context.fillStyle = 'white'; + context.fillRect(0, 1, 2, 1); + + return canvas; +} + +function animate() { + let time = performance.now() * 0.001; + + pointLight.position.x = Math.sin(time * 0.6) * 9; + pointLight.position.y = Math.sin(time * 0.7) * 9 + 6; + pointLight.position.z = Math.sin(time * 0.8) * 9; + + pointLight.rotation.x = time; + pointLight.rotation.z = time; + + time += 10000; + + pointLight2.position.x = Math.sin(time * 0.6) * 9; + pointLight2.position.y = Math.sin(time * 0.7) * 9 + 6; + pointLight2.position.z = Math.sin(time * 0.8) * 9; + + pointLight2.rotation.x = time; + pointLight2.rotation.z = time; + + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/webgl_shadowmap_progressive.ts b/examples-testing/examples/webgl_shadowmap_progressive.ts new file mode 100644 index 000000000..29298630f --- /dev/null +++ b/examples-testing/examples/webgl_shadowmap_progressive.ts @@ -0,0 +1,204 @@ +import * as THREE from 'three'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { TransformControls } from 'three/addons/controls/TransformControls.js'; +import { ProgressiveLightMap } from 'three/addons/misc/ProgressiveLightMap.js'; + +// ShadowMap + LightMap Res and Number of Directional Lights +const shadowMapRes = 512, + lightMapRes = 1024, + lightCount = 8; +let camera, + scene, + renderer, + controls, + control, + control2, + object = new THREE.Mesh(), + lightOrigin = null, + progressiveSurfacemap; +const dirLights = [], + lightmapObjects = []; +const params = { + Enable: true, + 'Blur Edges': true, + 'Blend Window': 200, + 'Light Radius': 50, + 'Ambient Weight': 0.5, + 'Debug Lightmap': false, +}; +init(); +createGUI(); + +function init() { + // renderer + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.shadowMap.enabled = true; + document.body.appendChild(renderer.domElement); + + // camera + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(0, 100, 200); + camera.name = 'Camera'; + + // scene + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x949494); + scene.fog = new THREE.Fog(0x949494, 1000, 3000); + + // progressive lightmap + progressiveSurfacemap = new ProgressiveLightMap(renderer, lightMapRes); + + // directional lighting "origin" + lightOrigin = new THREE.Group(); + lightOrigin.position.set(60, 150, 100); + scene.add(lightOrigin); + + // transform gizmo + control = new TransformControls(camera, renderer.domElement); + control.addEventListener('dragging-changed', event => { + controls.enabled = !event.value; + }); + control.attach(lightOrigin); + scene.add(control.getHelper()); + + // create 8 directional lights to speed up the convergence + for (let l = 0; l < lightCount; l++) { + const dirLight = new THREE.DirectionalLight(0xffffff, Math.PI / lightCount); + dirLight.name = 'Dir. Light ' + l; + dirLight.position.set(200, 200, 200); + dirLight.castShadow = true; + dirLight.shadow.camera.near = 100; + dirLight.shadow.camera.far = 5000; + dirLight.shadow.camera.right = 150; + dirLight.shadow.camera.left = -150; + dirLight.shadow.camera.top = 150; + dirLight.shadow.camera.bottom = -150; + dirLight.shadow.mapSize.width = shadowMapRes; + dirLight.shadow.mapSize.height = shadowMapRes; + lightmapObjects.push(dirLight); + dirLights.push(dirLight); + } + + // ground + const groundMesh = new THREE.Mesh( + new THREE.PlaneGeometry(600, 600), + new THREE.MeshPhongMaterial({ color: 0xffffff, depthWrite: true }), + ); + groundMesh.position.y = -0.1; + groundMesh.rotation.x = -Math.PI / 2; + groundMesh.name = 'Ground Mesh'; + lightmapObjects.push(groundMesh); + scene.add(groundMesh); + + // model + function loadModel() { + object.traverse(function (child) { + if (child.isMesh) { + child.name = 'Loaded Mesh'; + child.castShadow = true; + child.receiveShadow = true; + child.material = new THREE.MeshPhongMaterial(); + + // This adds the model to the lightmap + lightmapObjects.push(child); + progressiveSurfacemap.addObjectsToLightMap(lightmapObjects); + } else { + child.layers.disableAll(); // Disable Rendering for this + } + }); + scene.add(object); + object.scale.set(2, 2, 2); + object.position.set(0, -16, 0); + control2 = new TransformControls(camera, renderer.domElement); + control2.addEventListener('dragging-changed', event => { + controls.enabled = !event.value; + }); + control2.attach(object); + scene.add(control2.getHelper()); + const lightTarget = new THREE.Group(); + lightTarget.position.set(0, 20, 0); + for (let l = 0; l < dirLights.length; l++) { + dirLights[l].target = lightTarget; + } + + object.add(lightTarget); + } + + const manager = new THREE.LoadingManager(loadModel); + const loader = new GLTFLoader(manager); + loader.load('models/gltf/ShadowmappableMesh.glb', function (obj) { + object = obj.scene.children[0]; + }); + + // controls + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; // an animation loop is required when either damping or auto-rotation are enabled + controls.dampingFactor = 0.05; + controls.screenSpacePanning = true; + controls.minDistance = 100; + controls.maxDistance = 500; + controls.maxPolarAngle = Math.PI / 1.5; + controls.target.set(0, 100, 0); + + window.addEventListener('resize', onWindowResize); +} + +function createGUI() { + const gui = new GUI({ title: 'Accumulation Settings' }); + gui.add(params, 'Enable'); + gui.add(params, 'Blur Edges'); + gui.add(params, 'Blend Window', 1, 500).step(1); + gui.add(params, 'Light Radius', 0, 200).step(10); + gui.add(params, 'Ambient Weight', 0, 1).step(0.1); + gui.add(params, 'Debug Lightmap'); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + // Update the inertia on the orbit controls + controls.update(); + + // Accumulate Surface Maps + if (params['Enable']) { + progressiveSurfacemap.update(camera, params['Blend Window'], params['Blur Edges']); + + if (!progressiveSurfacemap.firstUpdate) { + progressiveSurfacemap.showDebugLightmap(params['Debug Lightmap']); + } + } + + // Manually Update the Directional Lights + for (let l = 0; l < dirLights.length; l++) { + // Sometimes they will be sampled from the target direction + // Sometimes they will be uniformly sampled from the upper hemisphere + if (Math.random() > params['Ambient Weight']) { + dirLights[l].position.set( + lightOrigin.position.x + Math.random() * params['Light Radius'], + lightOrigin.position.y + Math.random() * params['Light Radius'], + lightOrigin.position.z + Math.random() * params['Light Radius'], + ); + } else { + // Uniform Hemispherical Surface Distribution for Ambient Occlusion + const lambda = Math.acos(2 * Math.random() - 1) - 3.14159 / 2.0; + const phi = 2 * 3.14159 * Math.random(); + dirLights[l].position.set( + Math.cos(lambda) * Math.cos(phi) * 300 + object.position.x, + Math.abs(Math.cos(lambda) * Math.sin(phi) * 300) + object.position.y + 20, + Math.sin(lambda) * 300 + object.position.z, + ); + } + } + + // Render Scene + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_shadowmap_viewer.ts b/examples-testing/examples/webgl_shadowmap_viewer.ts new file mode 100644 index 000000000..2ed16e992 --- /dev/null +++ b/examples-testing/examples/webgl_shadowmap_viewer.ts @@ -0,0 +1,181 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { ShadowMapViewer } from 'three/addons/utils/ShadowMapViewer.js'; + +let camera, scene, renderer, timer, stats; +let dirLight, spotLight; +let torusKnot, cube; +let dirLightShadowMapViewer, spotLightShadowMapViewer; + +init(); + +function init() { + initScene(); + initShadowMapViewers(); + initMisc(); + + document.body.appendChild(renderer.domElement); + window.addEventListener('resize', onWindowResize); +} + +function initScene() { + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(0, 15, 35); + + scene = new THREE.Scene(); + + // Lights + + scene.add(new THREE.AmbientLight(0x404040, 3)); + + spotLight = new THREE.SpotLight(0xffffff, 500); + spotLight.name = 'Spot Light'; + spotLight.angle = Math.PI / 5; + spotLight.penumbra = 0.3; + spotLight.position.set(10, 10, 5); + spotLight.castShadow = true; + spotLight.shadow.camera.near = 8; + spotLight.shadow.camera.far = 30; + spotLight.shadow.mapSize.width = 1024; + spotLight.shadow.mapSize.height = 1024; + scene.add(spotLight); + + scene.add(new THREE.CameraHelper(spotLight.shadow.camera)); + + dirLight = new THREE.DirectionalLight(0xffffff, 3); + dirLight.name = 'Dir. Light'; + dirLight.position.set(0, 10, 0); + dirLight.castShadow = true; + dirLight.shadow.camera.near = 1; + dirLight.shadow.camera.far = 10; + dirLight.shadow.camera.right = 15; + dirLight.shadow.camera.left = -15; + dirLight.shadow.camera.top = 15; + dirLight.shadow.camera.bottom = -15; + dirLight.shadow.mapSize.width = 1024; + dirLight.shadow.mapSize.height = 1024; + scene.add(dirLight); + + scene.add(new THREE.CameraHelper(dirLight.shadow.camera)); + + // Geometry + let geometry = new THREE.TorusKnotGeometry(25, 8, 75, 20); + let material = new THREE.MeshPhongMaterial({ + color: 0xff0000, + shininess: 150, + specular: 0x222222, + }); + + torusKnot = new THREE.Mesh(geometry, material); + torusKnot.scale.multiplyScalar(1 / 18); + torusKnot.position.y = 3; + torusKnot.castShadow = true; + torusKnot.receiveShadow = true; + scene.add(torusKnot); + + geometry = new THREE.BoxGeometry(3, 3, 3); + cube = new THREE.Mesh(geometry, material); + cube.position.set(8, 3, 8); + cube.castShadow = true; + cube.receiveShadow = true; + scene.add(cube); + + geometry = new THREE.BoxGeometry(10, 0.15, 10); + material = new THREE.MeshPhongMaterial({ + color: 0xa0adaf, + shininess: 150, + specular: 0x111111, + }); + + const ground = new THREE.Mesh(geometry, material); + ground.scale.multiplyScalar(3); + ground.castShadow = false; + ground.receiveShadow = true; + scene.add(ground); +} + +function initShadowMapViewers() { + dirLightShadowMapViewer = new ShadowMapViewer(dirLight); + spotLightShadowMapViewer = new ShadowMapViewer(spotLight); + resizeShadowMapViewers(); +} + +function initMisc() { + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.shadowMap.enabled = true; + renderer.shadowMap.type = THREE.BasicShadowMap; + + // Mouse control + const controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, 2, 0); + controls.update(); + + timer = new THREE.Timer(); + timer.connect(document); + + stats = new Stats(); + document.body.appendChild(stats.dom); +} + +function resizeShadowMapViewers() { + const size = window.innerWidth * 0.15; + + dirLightShadowMapViewer.position.x = 10; + dirLightShadowMapViewer.position.y = 10; + dirLightShadowMapViewer.size.width = size; + dirLightShadowMapViewer.size.height = size; + dirLightShadowMapViewer.update(); //Required when setting position or size directly + + spotLightShadowMapViewer.size.set(size, size); + spotLightShadowMapViewer.position.set(size + 20, 10); + // spotLightShadowMapViewer.update(); //NOT required because .set updates automatically +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + resizeShadowMapViewers(); + dirLightShadowMapViewer.updateForWindowResize(); + spotLightShadowMapViewer.updateForWindowResize(); +} + +function animate() { + timer.update(); + + render(); + + stats.update(); +} + +function renderScene() { + renderer.render(scene, camera); +} + +function renderShadowMapViewers() { + dirLightShadowMapViewer.render(renderer); + spotLightShadowMapViewer.render(renderer); +} + +function render() { + const delta = timer.getDelta(); + + renderScene(); + renderShadowMapViewers(); + + torusKnot.rotation.x += 0.25 * delta; + torusKnot.rotation.y += 2 * delta; + torusKnot.rotation.z += 1 * delta; + + cube.rotation.x += 0.25 * delta; + cube.rotation.y += 2 * delta; + cube.rotation.z += 1 * delta; +} diff --git a/examples-testing/examples/webgl_shadowmap_vsm.ts b/examples-testing/examples/webgl_shadowmap_vsm.ts new file mode 100644 index 000000000..d5bf4f7bb --- /dev/null +++ b/examples-testing/examples/webgl_shadowmap_vsm.ts @@ -0,0 +1,203 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let camera, scene, renderer, timer, stats; +let dirLight, spotLight; +let torusKnot, dirGroup; + +init(); + +function init() { + initScene(); + initMisc(); + + // Init gui + const gui = new GUI(); + + const config = { + spotlightRadius: 4, + spotlightSamples: 8, + dirlightRadius: 4, + dirlightSamples: 8, + }; + + const spotlightFolder = gui.addFolder('Spotlight'); + spotlightFolder + .add(config, 'spotlightRadius') + .name('radius') + .min(0) + .max(25) + .onChange(function (value) { + spotLight.shadow.radius = value; + }); + + spotlightFolder + .add(config, 'spotlightSamples', 1, 25, 1) + .name('samples') + .onChange(function (value) { + spotLight.shadow.blurSamples = value; + }); + spotlightFolder.open(); + + const dirlightFolder = gui.addFolder('Directional Light'); + dirlightFolder + .add(config, 'dirlightRadius') + .name('radius') + .min(0) + .max(25) + .onChange(function (value) { + dirLight.shadow.radius = value; + }); + + dirlightFolder + .add(config, 'dirlightSamples', 1, 25, 1) + .name('samples') + .onChange(function (value) { + dirLight.shadow.blurSamples = value; + }); + dirlightFolder.open(); + + document.body.appendChild(renderer.domElement); + window.addEventListener('resize', onWindowResize); +} + +function initScene() { + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(0, 10, 30); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x222244); + scene.fog = new THREE.Fog(0x222244, 50, 100); + + // Lights + + scene.add(new THREE.AmbientLight(0x444444)); + + spotLight = new THREE.SpotLight(0xff8888, 400); + spotLight.angle = Math.PI / 5; + spotLight.penumbra = 0.3; + spotLight.position.set(8, 10, 5); + spotLight.castShadow = true; + spotLight.shadow.camera.near = 8; + spotLight.shadow.camera.far = 200; + spotLight.shadow.mapSize.width = 256; + spotLight.shadow.mapSize.height = 256; + spotLight.shadow.bias = -0.002; + spotLight.shadow.radius = 4; + scene.add(spotLight); + + dirLight = new THREE.DirectionalLight(0x8888ff, 3); + dirLight.position.set(3, 12, 17); + dirLight.castShadow = true; + dirLight.shadow.camera.near = 0.1; + dirLight.shadow.camera.far = 500; + dirLight.shadow.camera.right = 17; + dirLight.shadow.camera.left = -17; + dirLight.shadow.camera.top = 17; + dirLight.shadow.camera.bottom = -17; + dirLight.shadow.mapSize.width = 512; + dirLight.shadow.mapSize.height = 512; + dirLight.shadow.radius = 4; + dirLight.shadow.bias = -0.0005; + + dirGroup = new THREE.Group(); + dirGroup.add(dirLight); + scene.add(dirGroup); + + // Geometry + + const geometry = new THREE.TorusKnotGeometry(25, 8, 75, 20); + const material = new THREE.MeshPhongMaterial({ + color: 0x999999, + shininess: 0, + specular: 0x222222, + }); + + torusKnot = new THREE.Mesh(geometry, material); + torusKnot.scale.multiplyScalar(1 / 18); + torusKnot.position.y = 3; + torusKnot.castShadow = true; + torusKnot.receiveShadow = true; + scene.add(torusKnot); + + const cylinderGeometry = new THREE.CylinderGeometry(0.75, 0.75, 7, 32); + + const pillar1 = new THREE.Mesh(cylinderGeometry, material); + pillar1.position.set(8, 3.5, 8); + pillar1.castShadow = true; + pillar1.receiveShadow = true; + + const pillar2 = pillar1.clone(); + pillar2.position.set(8, 3.5, -8); + const pillar3 = pillar1.clone(); + pillar3.position.set(-8, 3.5, 8); + const pillar4 = pillar1.clone(); + pillar4.position.set(-8, 3.5, -8); + + scene.add(pillar1); + scene.add(pillar2); + scene.add(pillar3); + scene.add(pillar4); + + const planeGeometry = new THREE.PlaneGeometry(200, 200); + const planeMaterial = new THREE.MeshPhongMaterial({ + color: 0x999999, + shininess: 0, + specular: 0x111111, + }); + + const ground = new THREE.Mesh(planeGeometry, planeMaterial); + ground.rotation.x = -Math.PI / 2; + ground.scale.multiplyScalar(3); + ground.castShadow = true; + ground.receiveShadow = true; + scene.add(ground); +} + +function initMisc() { + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.shadowMap.enabled = true; + renderer.shadowMap.type = THREE.VSMShadowMap; + + // Mouse control + const controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, 2, 0); + controls.update(); + + timer = new THREE.Timer(); + timer.connect(document); + + stats = new Stats(); + document.body.appendChild(stats.dom); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate(time) { + timer.update(); + + const delta = timer.getDelta(); + + torusKnot.rotation.x += 0.25 * delta; + torusKnot.rotation.y += 0.5 * delta; + torusKnot.rotation.z += 1 * delta; + + dirGroup.rotation.y += 0.7 * delta; + dirLight.position.z = 17 + Math.sin(time * 0.001) * 5; + + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/webgl_shadowmesh.ts b/examples-testing/examples/webgl_shadowmesh.ts new file mode 100644 index 000000000..e4f7ebb8f --- /dev/null +++ b/examples-testing/examples/webgl_shadowmesh.ts @@ -0,0 +1,253 @@ +import * as THREE from 'three'; + +import { ShadowMesh } from 'three/addons/objects/ShadowMesh.js'; + +let SCREEN_WIDTH = window.innerWidth; +let SCREEN_HEIGHT = window.innerHeight; + +const scene = new THREE.Scene(); +const camera = new THREE.PerspectiveCamera(55, SCREEN_WIDTH / SCREEN_HEIGHT, 1, 3000); +const timer = new THREE.Timer(); +timer.connect(document); +const renderer = new THREE.WebGLRenderer({ stencil: true }); + +const sunLight = new THREE.DirectionalLight('rgb(255,255,255)', 3); +let useDirectionalLight = true; +let arrowHelper1, arrowHelper2, arrowHelper3; +const arrowDirection = new THREE.Vector3(); +const arrowPosition1 = new THREE.Vector3(); +const arrowPosition2 = new THREE.Vector3(); +const arrowPosition3 = new THREE.Vector3(); +let groundMesh; +let lightSphere, lightHolder; +let pyramid, pyramidShadow; +let sphere, sphereShadow; +let cube, cubeShadow; +let cylinder, cylinderShadow; +let torus, torusShadow; +const normalVector = new THREE.Vector3(0, 1, 0); +const planeConstant = 0.01; // this value must be slightly higher than the groundMesh's y position of 0.0 +const groundPlane = new THREE.Plane(normalVector, planeConstant); +const lightPosition4D = new THREE.Vector4(); +let verticalAngle = 0; +let horizontalAngle = 0; +let frameTime = 0; +const TWO_PI = Math.PI * 2; + +init(); + +function init() { + scene.background = new THREE.Color(0x0096ff); + + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); + renderer.setAnimationLoop(animate); + document.getElementById('container').appendChild(renderer.domElement); + + window.addEventListener('resize', onWindowResize); + + camera.position.set(0, 2.5, 10); + scene.add(camera); + onWindowResize(); + + sunLight.position.set(5, 7, -1); + sunLight.lookAt(scene.position); + scene.add(sunLight); + + lightPosition4D.x = sunLight.position.x; + lightPosition4D.y = sunLight.position.y; + lightPosition4D.z = sunLight.position.z; + // amount of light-ray divergence. Ranging from: + // 0.001 = sunlight(min divergence) to 1.0 = pointlight(max divergence) + lightPosition4D.w = 0.001; // must be slightly greater than 0, due to 0 causing matrixInverse errors + + // YELLOW ARROW HELPERS + arrowDirection.subVectors(scene.position, sunLight.position).normalize(); + + arrowPosition1.copy(sunLight.position); + arrowHelper1 = new THREE.ArrowHelper(arrowDirection, arrowPosition1, 0.9, 0xffff00, 0.25, 0.08); + scene.add(arrowHelper1); + + arrowPosition2.copy(sunLight.position).add(new THREE.Vector3(0, 0.2, 0)); + arrowHelper2 = new THREE.ArrowHelper(arrowDirection, arrowPosition2, 0.9, 0xffff00, 0.25, 0.08); + scene.add(arrowHelper2); + + arrowPosition3.copy(sunLight.position).add(new THREE.Vector3(0, -0.2, 0)); + arrowHelper3 = new THREE.ArrowHelper(arrowDirection, arrowPosition3, 0.9, 0xffff00, 0.25, 0.08); + scene.add(arrowHelper3); + + // LIGHTBULB + const lightSphereGeometry = new THREE.SphereGeometry(0.09); + const lightSphereMaterial = new THREE.MeshBasicMaterial({ color: 'rgb(255,255,255)' }); + lightSphere = new THREE.Mesh(lightSphereGeometry, lightSphereMaterial); + scene.add(lightSphere); + lightSphere.visible = false; + + const lightHolderGeometry = new THREE.CylinderGeometry(0.05, 0.05, 0.13); + const lightHolderMaterial = new THREE.MeshBasicMaterial({ color: 'rgb(75,75,75)' }); + lightHolder = new THREE.Mesh(lightHolderGeometry, lightHolderMaterial); + scene.add(lightHolder); + lightHolder.visible = false; + + // GROUND + const groundGeometry = new THREE.BoxGeometry(30, 0.01, 40); + const groundMaterial = new THREE.MeshLambertMaterial({ color: 'rgb(0,130,0)' }); + groundMesh = new THREE.Mesh(groundGeometry, groundMaterial); + groundMesh.position.y = 0.0; //this value must be slightly lower than the planeConstant (0.01) parameter above + scene.add(groundMesh); + + // RED CUBE and CUBE's SHADOW + const cubeGeometry = new THREE.BoxGeometry(1, 1, 1); + const cubeMaterial = new THREE.MeshLambertMaterial({ color: 'rgb(255,0,0)', emissive: 0x200000 }); + cube = new THREE.Mesh(cubeGeometry, cubeMaterial); + cube.position.z = -1; + scene.add(cube); + + cubeShadow = new ShadowMesh(cube); + scene.add(cubeShadow); + + // BLUE CYLINDER and CYLINDER's SHADOW + const cylinderGeometry = new THREE.CylinderGeometry(0.3, 0.3, 2); + const cylinderMaterial = new THREE.MeshPhongMaterial({ color: 'rgb(0,0,255)', emissive: 0x000020 }); + cylinder = new THREE.Mesh(cylinderGeometry, cylinderMaterial); + cylinder.position.z = -2.5; + scene.add(cylinder); + + cylinderShadow = new ShadowMesh(cylinder); + scene.add(cylinderShadow); + + // MAGENTA TORUS and TORUS' SHADOW + const torusGeometry = new THREE.TorusGeometry(1, 0.2, 10, 16, TWO_PI); + const torusMaterial = new THREE.MeshPhongMaterial({ color: 'rgb(255,0,255)', emissive: 0x200020 }); + torus = new THREE.Mesh(torusGeometry, torusMaterial); + torus.position.z = -6; + scene.add(torus); + + torusShadow = new ShadowMesh(torus); + scene.add(torusShadow); + + // WHITE SPHERE and SPHERE'S SHADOW + const sphereGeometry = new THREE.SphereGeometry(0.5, 20, 10); + const sphereMaterial = new THREE.MeshPhongMaterial({ color: 'rgb(255,255,255)', emissive: 0x222222 }); + sphere = new THREE.Mesh(sphereGeometry, sphereMaterial); + sphere.position.set(4, 0.5, 2); + scene.add(sphere); + + sphereShadow = new ShadowMesh(sphere); + scene.add(sphereShadow); + + // YELLOW PYRAMID and PYRAMID'S SHADOW + const pyramidGeometry = new THREE.CylinderGeometry(0, 0.5, 2, 4); + const pyramidMaterial = new THREE.MeshPhongMaterial({ + color: 'rgb(255,255,0)', + emissive: 0x440000, + flatShading: true, + shininess: 0, + }); + pyramid = new THREE.Mesh(pyramidGeometry, pyramidMaterial); + pyramid.position.set(-4, 1, 2); + scene.add(pyramid); + + pyramidShadow = new ShadowMesh(pyramid); + scene.add(pyramidShadow); + + document.getElementById('lightButton').addEventListener('click', lightButtonHandler); +} + +function animate() { + timer.update(); + + frameTime = timer.getDelta(); + + cube.rotation.x += 1.0 * frameTime; + cube.rotation.y += 1.0 * frameTime; + + cylinder.rotation.y += 1.0 * frameTime; + cylinder.rotation.z -= 1.0 * frameTime; + + torus.rotation.x -= 1.0 * frameTime; + torus.rotation.y -= 1.0 * frameTime; + + pyramid.rotation.y += 0.5 * frameTime; + + horizontalAngle += 0.5 * frameTime; + if (horizontalAngle > TWO_PI) horizontalAngle -= TWO_PI; + cube.position.x = Math.sin(horizontalAngle) * 4; + cylinder.position.x = Math.sin(horizontalAngle) * -4; + torus.position.x = Math.cos(horizontalAngle) * 4; + + verticalAngle += 1.5 * frameTime; + if (verticalAngle > TWO_PI) verticalAngle -= TWO_PI; + cube.position.y = Math.sin(verticalAngle) * 2 + 2.9; + cylinder.position.y = Math.sin(verticalAngle) * 2 + 3.1; + torus.position.y = Math.cos(verticalAngle) * 2 + 3.3; + + // update the ShadowMeshes to follow their shadow-casting objects + cubeShadow.update(groundPlane, lightPosition4D); + cylinderShadow.update(groundPlane, lightPosition4D); + torusShadow.update(groundPlane, lightPosition4D); + sphereShadow.update(groundPlane, lightPosition4D); + pyramidShadow.update(groundPlane, lightPosition4D); + + renderer.render(scene, camera); +} + +function onWindowResize() { + SCREEN_WIDTH = window.innerWidth; + SCREEN_HEIGHT = window.innerHeight; + + renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); + + camera.aspect = SCREEN_WIDTH / SCREEN_HEIGHT; + camera.updateProjectionMatrix(); +} + +function lightButtonHandler() { + useDirectionalLight = !useDirectionalLight; + + if (useDirectionalLight) { + scene.background.setHex(0x0096ff); + + groundMesh.material.color.setHex(0x008200); + sunLight.position.set(5, 7, -1); + sunLight.lookAt(scene.position); + + lightPosition4D.x = sunLight.position.x; + lightPosition4D.y = sunLight.position.y; + lightPosition4D.z = sunLight.position.z; + lightPosition4D.w = 0.001; // more of a directional Light value + + arrowHelper1.visible = true; + arrowHelper2.visible = true; + arrowHelper3.visible = true; + + lightSphere.visible = false; + lightHolder.visible = false; + + document.getElementById('lightButton').value = 'Switch to PointLight'; + } else { + scene.background.setHex(0x000000); + + groundMesh.material.color.setHex(0x969696); + + sunLight.position.set(0, 6, -2); + sunLight.lookAt(scene.position); + lightSphere.position.copy(sunLight.position); + lightHolder.position.copy(lightSphere.position); + lightHolder.position.y += 0.12; + + lightPosition4D.x = sunLight.position.x; + lightPosition4D.y = sunLight.position.y; + lightPosition4D.z = sunLight.position.z; + lightPosition4D.w = 0.9; // more of a point Light value + + arrowHelper1.visible = false; + arrowHelper2.visible = false; + arrowHelper3.visible = false; + + lightSphere.visible = true; + lightHolder.visible = true; + + document.getElementById('lightButton').value = 'Switch to THREE.DirectionalLight'; + } +} diff --git a/examples-testing/examples/webgl_simple_gi.ts b/examples-testing/examples/webgl_simple_gi.ts new file mode 100644 index 000000000..4ab6dc895 --- /dev/null +++ b/examples-testing/examples/webgl_simple_gi.ts @@ -0,0 +1,172 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +class GIMesh extends THREE.Mesh { + copy(source) { + super.copy(source); + + this.geometry = source.geometry.clone(); + + return this; + } +} + +// + +const SimpleGI = function (renderer, scene) { + const SIZE = 32, + SIZE2 = SIZE * SIZE; + + const camera = new THREE.PerspectiveCamera(90, 1, 0.01, 100); + + scene.updateMatrixWorld(true); + + let clone = scene.clone(); + clone.matrixWorldAutoUpdate = false; + + const rt = new THREE.WebGLRenderTarget(SIZE, SIZE); + + const normalMatrix = new THREE.Matrix3(); + + const position = new THREE.Vector3(); + const normal = new THREE.Vector3(); + + let bounces = 0; + let currentVertex = 0; + + const color = new Float32Array(3); + const buffer = new Uint8Array(SIZE2 * 4); + + function compute() { + if (bounces === 3) return; + + const object = scene.children[0]; // torusKnot + const geometry = object.geometry; + + const attributes = geometry.attributes; + const positions = attributes.position.array; + const normals = attributes.normal.array; + + if (attributes.color === undefined) { + const colors = new Float32Array(positions.length); + geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3).setUsage(THREE.DynamicDrawUsage)); + } + + const colors = attributes.color.array; + + const startVertex = currentVertex; + const totalVertex = positions.length / 3; + + for (let i = 0; i < 32; i++) { + if (currentVertex >= totalVertex) break; + + position.fromArray(positions, currentVertex * 3); + position.applyMatrix4(object.matrixWorld); + + normal.fromArray(normals, currentVertex * 3); + normal.applyMatrix3(normalMatrix.getNormalMatrix(object.matrixWorld)).normalize(); + + camera.position.copy(position); + camera.lookAt(position.add(normal)); + + renderer.setRenderTarget(rt); + renderer.render(clone, camera); + + renderer.readRenderTargetPixels(rt, 0, 0, SIZE, SIZE, buffer); + + color[0] = 0; + color[1] = 0; + color[2] = 0; + + for (let k = 0, kl = buffer.length; k < kl; k += 4) { + color[0] += buffer[k + 0]; + color[1] += buffer[k + 1]; + color[2] += buffer[k + 2]; + } + + colors[currentVertex * 3 + 0] = color[0] / (SIZE2 * 255); + colors[currentVertex * 3 + 1] = color[1] / (SIZE2 * 255); + colors[currentVertex * 3 + 2] = color[2] / (SIZE2 * 255); + + currentVertex++; + } + + attributes.color.addUpdateRange(startVertex * 3, (currentVertex - startVertex) * 3); + attributes.color.needsUpdate = true; + + if (currentVertex >= totalVertex) { + clone = scene.clone(); + clone.matrixWorldAutoUpdate = false; + + bounces++; + currentVertex = 0; + } + + requestAnimationFrame(compute); + } + + requestAnimationFrame(compute); +}; + +// + +let camera, scene, renderer; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.z = 4; + + scene = new THREE.Scene(); + + // torus knot + + const torusGeometry = new THREE.TorusKnotGeometry(0.75, 0.3, 128, 32, 1); + const material = new THREE.MeshBasicMaterial({ vertexColors: true }); + + const torusKnot = new GIMesh(torusGeometry, material); + scene.add(torusKnot); + + // room + + const materials = []; + + for (let i = 0; i < 8; i++) { + materials.push(new THREE.MeshBasicMaterial({ color: Math.random() * 0xffffff, side: THREE.BackSide })); + } + + const boxGeometry = new THREE.BoxGeometry(3, 3, 3); + + const box = new THREE.Mesh(boxGeometry, materials); + scene.add(box); + + // + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + new SimpleGI(renderer, scene); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 1; + controls.maxDistance = 10; + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + renderer.setRenderTarget(null); + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_sprites.ts b/examples-testing/examples/webgl_sprites.ts new file mode 100644 index 000000000..2e4189347 --- /dev/null +++ b/examples-testing/examples/webgl_sprites.ts @@ -0,0 +1,187 @@ +import * as THREE from 'three'; + +let camera, scene, renderer; +let cameraOrtho, sceneOrtho; + +let spriteTL, spriteTR, spriteBL, spriteBR, spriteC; + +let mapC; + +let group; + +init(); + +function init() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera = new THREE.PerspectiveCamera(60, width / height, 1, 2100); + camera.position.z = 1500; + + cameraOrtho = new THREE.OrthographicCamera(-width / 2, width / 2, height / 2, -height / 2, 1, 10); + cameraOrtho.position.z = 10; + + scene = new THREE.Scene(); + scene.fog = new THREE.Fog(0x000000, 1500, 2100); + + sceneOrtho = new THREE.Scene(); + + // create sprites + + const amount = 200; + const radius = 500; + + const textureLoader = new THREE.TextureLoader(); + + textureLoader.load('textures/sprite0.png', createHUDSprites); + const mapB = textureLoader.load('textures/sprite1.png'); + mapC = textureLoader.load('textures/sprite2.png'); + + mapB.colorSpace = THREE.SRGBColorSpace; + mapC.colorSpace = THREE.SRGBColorSpace; + + group = new THREE.Group(); + + const materialC = new THREE.SpriteMaterial({ map: mapC, color: 0xffffff, fog: true }); + const materialB = new THREE.SpriteMaterial({ map: mapB, color: 0xffffff, fog: true }); + + for (let a = 0; a < amount; a++) { + const x = Math.random() - 0.5; + const y = Math.random() - 0.5; + const z = Math.random() - 0.5; + + let material; + + if (z < 0) { + material = materialB.clone(); + } else { + material = materialC.clone(); + material.color.setHSL(0.5 * Math.random(), 0.75, 0.5); + material.map.offset.set(-0.5, -0.5); + material.map.repeat.set(2, 2); + } + + const sprite = new THREE.Sprite(material); + + sprite.position.set(x, y, z); + sprite.position.normalize(); + sprite.position.multiplyScalar(radius); + + group.add(sprite); + } + + scene.add(group); + + // renderer + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.autoClear = false; // To allow render overlay on top of sprited sphere + + document.body.appendChild(renderer.domElement); + + // + + window.addEventListener('resize', onWindowResize); +} + +function createHUDSprites(texture) { + texture.colorSpace = THREE.SRGBColorSpace; + + const material = new THREE.SpriteMaterial({ map: texture }); + + const width = material.map.image.width; + const height = material.map.image.height; + + spriteTL = new THREE.Sprite(material); + spriteTL.center.set(0.0, 1.0); + spriteTL.scale.set(width, height, 1); + sceneOrtho.add(spriteTL); + + spriteTR = new THREE.Sprite(material); + spriteTR.center.set(1.0, 1.0); + spriteTR.scale.set(width, height, 1); + sceneOrtho.add(spriteTR); + + spriteBL = new THREE.Sprite(material); + spriteBL.center.set(0.0, 0.0); + spriteBL.scale.set(width, height, 1); + sceneOrtho.add(spriteBL); + + spriteBR = new THREE.Sprite(material); + spriteBR.center.set(1.0, 0.0); + spriteBR.scale.set(width, height, 1); + sceneOrtho.add(spriteBR); + + spriteC = new THREE.Sprite(material); + spriteC.center.set(0.5, 0.5); + spriteC.scale.set(width, height, 1); + sceneOrtho.add(spriteC); + + updateHUDSprites(); +} + +function updateHUDSprites() { + const width = window.innerWidth / 2; + const height = window.innerHeight / 2; + + spriteTL.position.set(-width, height, 1); // top left + spriteTR.position.set(width, height, 1); // top right + spriteBL.position.set(-width, -height, 1); // bottom left + spriteBR.position.set(width, -height, 1); // bottom right + spriteC.position.set(0, 0, 1); // center +} + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + cameraOrtho.left = -width / 2; + cameraOrtho.right = width / 2; + cameraOrtho.top = height / 2; + cameraOrtho.bottom = -height / 2; + cameraOrtho.updateProjectionMatrix(); + + updateHUDSprites(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + const time = Date.now() / 1000; + + for (let i = 0, l = group.children.length; i < l; i++) { + const sprite = group.children[i]; + const material = sprite.material; + const scale = Math.sin(time + sprite.position.x * 0.01) * 0.3 + 1.0; + + let imageWidth = 1; + let imageHeight = 1; + + if (material.map && material.map.image && material.map.image.width) { + imageWidth = material.map.image.width; + imageHeight = material.map.image.height; + } + + sprite.material.rotation += 0.1 * (i / l); + sprite.scale.set(scale * imageWidth, scale * imageHeight, 1.0); + + if (material.map !== mapC) { + material.opacity = Math.sin(time + sprite.position.x * 0.01) * 0.4 + 0.6; + } + } + + group.rotation.x = time * 0.5; + group.rotation.y = time * 0.75; + group.rotation.z = time * 1.0; + + renderer.clear(); + renderer.render(scene, camera); + renderer.clearDepth(); + renderer.render(sceneOrtho, cameraOrtho); +} diff --git a/examples-testing/examples/webgl_test_memory.ts b/examples-testing/examples/webgl_test_memory.ts new file mode 100644 index 000000000..f5d0e112d --- /dev/null +++ b/examples-testing/examples/webgl_test_memory.ts @@ -0,0 +1,65 @@ +import * as THREE from 'three'; + +let camera, scene, renderer; + +init(); + +function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 10000); + camera.position.z = 200; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xffffff); + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); +} + +function createImage() { + const canvas = document.createElement('canvas'); + canvas.width = 256; + canvas.height = 256; + + const context = canvas.getContext('2d'); + context.fillStyle = + 'rgb(' + + Math.floor(Math.random() * 256) + + ',' + + Math.floor(Math.random() * 256) + + ',' + + Math.floor(Math.random() * 256) + + ')'; + context.fillRect(0, 0, 256, 256); + + return canvas; +} + +// + +function animate() { + const geometry = new THREE.SphereGeometry(50, Math.random() * 64, Math.random() * 32); + + const texture = new THREE.CanvasTexture(createImage()); + + const material = new THREE.MeshBasicMaterial({ map: texture, wireframe: true }); + + const mesh = new THREE.Mesh(geometry, material); + + scene.add(mesh); + + renderer.render(scene, camera); + + scene.remove(mesh); + + // clean up + + geometry.dispose(); + material.dispose(); + texture.dispose(); +} diff --git a/examples-testing/examples/webgl_test_memory2.ts b/examples-testing/examples/webgl_test_memory2.ts new file mode 100644 index 000000000..366a27914 --- /dev/null +++ b/examples-testing/examples/webgl_test_memory2.ts @@ -0,0 +1,81 @@ +import * as THREE from 'three'; + +const N = 100; + +let container; + +let camera, scene, renderer; + +let geometry; + +const meshes = []; + +let fragmentShader, vertexShader; + +init(); +setInterval(render, 1000 / 60); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + vertexShader = document.getElementById('vertexShader').textContent; + fragmentShader = document.getElementById('fragmentShader').textContent; + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 10000); + camera.position.z = 2000; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xffffff); + + geometry = new THREE.SphereGeometry(15, 64, 32); + + for (let i = 0; i < N; i++) { + const material = new THREE.ShaderMaterial({ + vertexShader: vertexShader, + fragmentShader: generateFragmentShader(), + }); + + const mesh = new THREE.Mesh(geometry, material); + + mesh.position.x = (0.5 - Math.random()) * 1000; + mesh.position.y = (0.5 - Math.random()) * 1000; + mesh.position.z = (0.5 - Math.random()) * 1000; + + scene.add(mesh); + + meshes.push(mesh); + } + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + container.appendChild(renderer.domElement); +} + +// + +function generateFragmentShader() { + return fragmentShader.replace('XXX', Math.random() + ',' + Math.random() + ',' + Math.random()); +} + +function render() { + for (let i = 0; i < N; i++) { + const mesh = meshes[i]; + mesh.material = new THREE.ShaderMaterial({ + vertexShader: vertexShader, + fragmentShader: generateFragmentShader(), + }); + } + + renderer.render(scene, camera); + + console.log('before', renderer.info.programs.length); + + for (let i = 0; i < N; i++) { + const mesh = meshes[i]; + mesh.material.dispose(); + } + + console.log('after', renderer.info.programs.length); +} diff --git a/examples-testing/examples/webgl_test_wide_gamut.ts b/examples-testing/examples/webgl_test_wide_gamut.ts new file mode 100644 index 000000000..5988299e1 --- /dev/null +++ b/examples-testing/examples/webgl_test_wide_gamut.ts @@ -0,0 +1,130 @@ +import * as THREE from 'three'; + +import { + DisplayP3ColorSpace, + DisplayP3ColorSpaceImpl, + LinearDisplayP3ColorSpace, + LinearDisplayP3ColorSpaceImpl, +} from 'three/addons/math/ColorSpaces.js'; + +import WebGL from 'three/addons/capabilities/WebGL.js'; + +let container, camera, renderer, loader; +let sceneL, sceneR, textureL, textureR; + +let sliderPos = window.innerWidth / 2; + +const slider = document.querySelector('.slider'); + +const isP3Context = WebGL.isColorSpaceAvailable(DisplayP3ColorSpace); + +THREE.ColorManagement.define({ + [DisplayP3ColorSpace]: DisplayP3ColorSpaceImpl, + [LinearDisplayP3ColorSpace]: LinearDisplayP3ColorSpaceImpl, +}); + +if (isP3Context) { + THREE.ColorManagement.workingColorSpace = LinearDisplayP3ColorSpace; +} + +init(); + +function init() { + container = document.querySelector('.container'); + + sceneL = new THREE.Scene(); + sceneR = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.z = 6; + + loader = new THREE.TextureLoader(); + + initTextures(); + initSlider(); + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.setScissorTest(true); + container.appendChild(renderer.domElement); + + if (isP3Context && window.matchMedia('( color-gamut: p3 )').matches) { + renderer.outputColorSpace = DisplayP3ColorSpace; + } + + window.addEventListener('resize', onWindowResize); + window.matchMedia('( color-gamut: p3 )').addEventListener('change', onGamutChange); +} + +async function initTextures() { + const path = 'textures/wide_gamut/logo_{colorSpace}.png'; + + textureL = await loader.loadAsync(path.replace('{colorSpace}', 'srgb')); + textureR = await loader.loadAsync(path.replace('{colorSpace}', 'p3')); + + textureL.colorSpace = THREE.SRGBColorSpace; + textureR.colorSpace = DisplayP3ColorSpace; + + sceneL.background = THREE.TextureUtils.contain(textureL, window.innerWidth / window.innerHeight); + sceneR.background = THREE.TextureUtils.contain(textureR, window.innerWidth / window.innerHeight); +} + +function initSlider() { + function onPointerDown() { + if (event.isPrimary === false) return; + + window.addEventListener('pointermove', onPointerMove); + window.addEventListener('pointerup', onPointerUp); + } + + function onPointerUp() { + window.removeEventListener('pointermove', onPointerMove); + window.removeEventListener('pointerup', onPointerUp); + } + + function onPointerMove(e) { + if (event.isPrimary === false) return; + + updateSlider(e.pageX); + } + + updateSlider(sliderPos); + + slider.style.touchAction = 'none'; // disable touch scroll + slider.addEventListener('pointerdown', onPointerDown); +} + +function updateSlider(offset) { + sliderPos = Math.max(10, Math.min(window.innerWidth - 10, offset)); + + slider.style.left = sliderPos - slider.offsetWidth / 2 + 'px'; +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + THREE.TextureUtils.contain(sceneL.background, window.innerWidth / window.innerHeight); + THREE.TextureUtils.contain(sceneR.background, window.innerWidth / window.innerHeight); + + updateSlider(sliderPos); +} + +function onGamutChange({ matches }) { + renderer.outputColorSpace = isP3Context && matches ? DisplayP3ColorSpace : THREE.SRGBColorSpace; + + textureL.needsUpdate = true; + textureR.needsUpdate = true; +} + +function animate() { + renderer.setScissor(0, 0, sliderPos, window.innerHeight); + renderer.render(sceneL, camera); + + renderer.setScissor(sliderPos, 0, window.innerWidth, window.innerHeight); + renderer.render(sceneR, camera); +} diff --git a/examples-testing/examples/webgl_texture2darray_compressed.ts b/examples-testing/examples/webgl_texture2darray_compressed.ts new file mode 100644 index 000000000..e074be576 --- /dev/null +++ b/examples-testing/examples/webgl_texture2darray_compressed.ts @@ -0,0 +1,91 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; +import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js'; + +let camera, scene, mesh, renderer, stats, timer; + +const planeWidth = 50; +const planeHeight = 25; + +let depthStep = 1; + +init(); + +function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 2000); + camera.position.z = 70; + + scene = new THREE.Scene(); + + // + timer = new THREE.Timer(); + timer.connect(document); + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + // + + const ktx2Loader = new KTX2Loader(); + ktx2Loader.setTranscoderPath('jsm/libs/basis/'); + ktx2Loader.detectSupport(renderer); + + ktx2Loader.load('textures/spiritedaway.ktx2', function (texturearray) { + const material = new THREE.ShaderMaterial({ + uniforms: { + diffuse: { value: texturearray }, + depth: { value: 55 }, + size: { value: new THREE.Vector2(planeWidth, planeHeight) }, + }, + vertexShader: document.getElementById('vs').textContent.trim(), + fragmentShader: document.getElementById('fs').textContent.trim(), + glslVersion: THREE.GLSL3, + }); + + const geometry = new THREE.PlaneGeometry(planeWidth, planeHeight); + + mesh = new THREE.Mesh(geometry, material); + + scene.add(mesh); + }); + + stats = new Stats(); + container.appendChild(stats.dom); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + if (mesh) { + const delta = timer.getDelta() * 10; + + depthStep += delta; + + const value = depthStep % 5; + + mesh.material.uniforms['depth'].value = value; + } + + render(); + stats.update(); +} + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_texture2darray_layerupdate.ts b/examples-testing/examples/webgl_texture2darray_layerupdate.ts new file mode 100644 index 000000000..0cc136cb7 --- /dev/null +++ b/examples-testing/examples/webgl_texture2darray_layerupdate.ts @@ -0,0 +1,131 @@ +import * as THREE from 'three'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js'; + +let camera, scene, mesh, renderer; + +const planeWidth = 20; +const planeHeight = 10; + +init(); + +async function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 2000); + camera.position.z = 70; + + scene = new THREE.Scene(); + + // Configure the renderer. + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + container.appendChild(renderer.domElement); + + // Configure the KTX2 loader. + + const ktx2Loader = new KTX2Loader(); + ktx2Loader.setTranscoderPath('jsm/libs/basis/'); + ktx2Loader.detectSupport(renderer); + + // Load several KTX2 textures which will later be used to modify + // specific texture array layers. + + const spiritedaway = await ktx2Loader.loadAsync('textures/spiritedaway.ktx2'); + + // Create a texture array for rendering. + + const layerByteLength = THREE.TextureUtils.getByteLength( + spiritedaway.image.width, + spiritedaway.image.height, + spiritedaway.format, + spiritedaway.type, + ); + + const textureArray = new THREE.CompressedArrayTexture( + [ + { + data: new Uint8Array(layerByteLength * 3), + width: spiritedaway.image.width, + height: spiritedaway.image.height, + }, + ], + spiritedaway.image.width, + spiritedaway.image.height, + 3, + spiritedaway.format, + spiritedaway.type, + ); + + // Setup the GUI + + const formData = { + srcLayer: 0, + destLayer: 0, + transfer() { + const layerElementLength = layerByteLength / spiritedaway.mipmaps[0].data.BYTES_PER_ELEMENT; + textureArray.mipmaps[0].data.set( + spiritedaway.mipmaps[0].data.subarray( + layerElementLength * (formData.srcLayer % spiritedaway.image.depth), + layerElementLength * ((formData.srcLayer % spiritedaway.image.depth) + 1), + ), + layerByteLength * formData.destLayer, + ); + textureArray.addLayerUpdate(formData.destLayer); + textureArray.needsUpdate = true; + + renderer.render(scene, camera); + }, + }; + + const gui = new GUI(); + gui.add(formData, 'srcLayer', 0, spiritedaway.image.depth - 1, 1); + gui.add(formData, 'destLayer', 0, textureArray.image.depth - 1, 1); + gui.add(formData, 'transfer'); + + /// Setup the scene. + + const material = new THREE.ShaderMaterial({ + uniforms: { + diffuse: { value: textureArray }, + size: { value: new THREE.Vector2(planeWidth, planeHeight) }, + }, + vertexShader: document.getElementById('vs').textContent.trim(), + fragmentShader: document.getElementById('fs').textContent.trim(), + glslVersion: THREE.GLSL3, + }); + + const geometry = new THREE.InstancedBufferGeometry(); + geometry.copy(new THREE.PlaneGeometry(planeWidth, planeHeight)); + geometry.instanceCount = 3; + + const instancedIndexAttribute = new THREE.InstancedBufferAttribute(new Uint16Array([0, 1, 2]), 1, false, 1); + instancedIndexAttribute.gpuType = THREE.IntType; + geometry.setAttribute('instancedIndex', instancedIndexAttribute); + + mesh = new THREE.InstancedMesh(geometry, material, 3); + + scene.add(mesh); + + window.addEventListener('resize', onWindowResize); + + // Initialize the texture array by first rendering the spirited away + // frames in order. + + textureArray.mipmaps[0].data.set(spiritedaway.mipmaps[0].data.subarray(0, textureArray.mipmaps[0].data.length)); + textureArray.needsUpdate = true; + renderer.render(scene, camera); +} + +function onWindowResize() { + renderer.setSize(window.innerWidth, window.innerHeight); + + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_texture3d.ts b/examples-testing/examples/webgl_texture3d.ts new file mode 100644 index 000000000..977dbadb7 --- /dev/null +++ b/examples-testing/examples/webgl_texture3d.ts @@ -0,0 +1,128 @@ +import * as THREE from 'three'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { NRRDLoader } from 'three/addons/loaders/NRRDLoader.js'; +import { VolumeRenderShader1 } from 'three/addons/shaders/VolumeShader.js'; + +let renderer, scene, camera, controls, material, volconfig, cmtextures; + +init(); + +function init() { + scene = new THREE.Scene(); + + // Create renderer + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + document.body.appendChild(renderer.domElement); + + // Create camera (The volume renderer does not work very well with perspective yet) + const h = 512; // frustum height + const aspect = window.innerWidth / window.innerHeight; + camera = new THREE.OrthographicCamera((-h * aspect) / 2, (h * aspect) / 2, h / 2, -h / 2, 1, 1000); + camera.position.set(-64, -64, 128); + camera.up.set(0, 0, 1); // In our data, z is up + + // Create controls + controls = new OrbitControls(camera, renderer.domElement); + controls.addEventListener('change', render); + controls.target.set(64, 64, 128); + controls.minZoom = 0.5; + controls.maxZoom = 4; + controls.enablePan = false; + controls.update(); + + // scene.add( new AxesHelper( 128 ) ); + + // Lighting is baked into the shader a.t.m. + // let dirLight = new DirectionalLight( 0xffffff ); + + // The gui for interaction + volconfig = { clim1: 0, clim2: 1, renderstyle: 'iso', isothreshold: 0.15, colormap: 'viridis' }; + const gui = new GUI(); + gui.add(volconfig, 'clim1', 0, 1, 0.01).onChange(updateUniforms); + gui.add(volconfig, 'clim2', 0, 1, 0.01).onChange(updateUniforms); + gui.add(volconfig, 'colormap', { gray: 'gray', viridis: 'viridis' }).onChange(updateUniforms); + gui.add(volconfig, 'renderstyle', { mip: 'mip', iso: 'iso' }).onChange(updateUniforms); + gui.add(volconfig, 'isothreshold', 0, 1, 0.01).onChange(updateUniforms); + + // Load the data ... + new NRRDLoader().load('models/nrrd/stent.nrrd', function (volume) { + // Texture to hold the volume. We have scalars, so we put our data in the red channel. + // THREEJS will select R32F (33326) based on the THREE.RedFormat and THREE.FloatType. + // Also see https://www.khronos.org/registry/webgl/specs/latest/2.0/#TEXTURE_TYPES_FORMATS_FROM_DOM_ELEMENTS_TABLE + // TODO: look the dtype up in the volume metadata + const texture = new THREE.Data3DTexture(volume.data, volume.xLength, volume.yLength, volume.zLength); + texture.format = THREE.RedFormat; + texture.type = THREE.FloatType; + texture.minFilter = texture.magFilter = THREE.LinearFilter; + texture.unpackAlignment = 1; + texture.needsUpdate = true; + + // Colormap textures + cmtextures = { + viridis: new THREE.TextureLoader().load('textures/cm_viridis.png', render), + gray: new THREE.TextureLoader().load('textures/cm_gray.png', render), + }; + + // Material + const shader = VolumeRenderShader1; + + const uniforms = THREE.UniformsUtils.clone(shader.uniforms); + + uniforms['u_data'].value = texture; + uniforms['u_size'].value.set(volume.xLength, volume.yLength, volume.zLength); + uniforms['u_clim'].value.set(volconfig.clim1, volconfig.clim2); + uniforms['u_renderstyle'].value = volconfig.renderstyle == 'mip' ? 0 : 1; // 0: MIP, 1: ISO + uniforms['u_renderthreshold'].value = volconfig.isothreshold; // For ISO renderstyle + uniforms['u_cmdata'].value = cmtextures[volconfig.colormap]; + + material = new THREE.ShaderMaterial({ + uniforms: uniforms, + vertexShader: shader.vertexShader, + fragmentShader: shader.fragmentShader, + side: THREE.BackSide, // The volume shader uses the backface as its "reference point" + }); + + // THREE.Mesh + const geometry = new THREE.BoxGeometry(volume.xLength, volume.yLength, volume.zLength); + geometry.translate(volume.xLength / 2 - 0.5, volume.yLength / 2 - 0.5, volume.zLength / 2 - 0.5); + + const mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + render(); + }); + + window.addEventListener('resize', onWindowResize); +} + +function updateUniforms() { + material.uniforms['u_clim'].value.set(volconfig.clim1, volconfig.clim2); + material.uniforms['u_renderstyle'].value = volconfig.renderstyle == 'mip' ? 0 : 1; // 0: MIP, 1: ISO + material.uniforms['u_renderthreshold'].value = volconfig.isothreshold; // For ISO renderstyle + material.uniforms['u_cmdata'].value = cmtextures[volconfig.colormap]; + + render(); +} + +function onWindowResize() { + renderer.setSize(window.innerWidth, window.innerHeight); + + const aspect = window.innerWidth / window.innerHeight; + + const frustumHeight = camera.top - camera.bottom; + + camera.left = (-frustumHeight * aspect) / 2; + camera.right = (frustumHeight * aspect) / 2; + + camera.updateProjectionMatrix(); + + render(); +} + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_texture3d_partialupdate.ts b/examples-testing/examples/webgl_texture3d_partialupdate.ts new file mode 100644 index 000000000..58615db84 --- /dev/null +++ b/examples-testing/examples/webgl_texture3d_partialupdate.ts @@ -0,0 +1,326 @@ +import * as THREE from 'three'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { ImprovedNoise } from 'three/addons/math/ImprovedNoise.js'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +const INITIAL_CLOUD_SIZE = 128; + +let renderer, scene, camera; +let mesh; +let prevTime = performance.now(); +let cloudTexture = null; + +init(); + +function generateCloudTexture(size, scaleFactor = 1.0) { + const data = new Uint8Array(size * size * size); + const scale = (scaleFactor * 10.0) / size; + + let i = 0; + const perlin = new ImprovedNoise(); + const vector = new THREE.Vector3(); + + for (let z = 0; z < size; z++) { + for (let y = 0; y < size; y++) { + for (let x = 0; x < size; x++) { + const dist = vector + .set(x, y, z) + .subScalar(size / 2) + .divideScalar(size) + .length(); + const fadingFactor = (1.0 - dist) * (1.0 - dist); + data[i] = (128 + 128 * perlin.noise((x * scale) / 1.5, y * scale, (z * scale) / 1.5)) * fadingFactor; + + i++; + } + } + } + + return new THREE.Data3DTexture(data, size, size, size); +} + +function init() { + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(0, 0, 1.5); + + new OrbitControls(camera, renderer.domElement); + + // Sky + + const canvas = document.createElement('canvas'); + canvas.width = 1; + canvas.height = 32; + + const context = canvas.getContext('2d'); + const gradient = context.createLinearGradient(0, 0, 0, 32); + gradient.addColorStop(0.0, '#014a84'); + gradient.addColorStop(0.5, '#0561a0'); + gradient.addColorStop(1.0, '#437ab6'); + context.fillStyle = gradient; + context.fillRect(0, 0, 1, 32); + + const skyMap = new THREE.CanvasTexture(canvas); + skyMap.colorSpace = THREE.SRGBColorSpace; + + const sky = new THREE.Mesh( + new THREE.SphereGeometry(10), + new THREE.MeshBasicMaterial({ map: skyMap, side: THREE.BackSide }), + ); + scene.add(sky); + + // Texture + + const texture = new THREE.Data3DTexture( + new Uint8Array(INITIAL_CLOUD_SIZE * INITIAL_CLOUD_SIZE * INITIAL_CLOUD_SIZE).fill(0), + INITIAL_CLOUD_SIZE, + INITIAL_CLOUD_SIZE, + INITIAL_CLOUD_SIZE, + ); + texture.format = THREE.RedFormat; + texture.minFilter = THREE.LinearFilter; + texture.magFilter = THREE.LinearFilter; + texture.unpackAlignment = 1; + texture.needsUpdate = true; + + cloudTexture = texture; + + // Material + + const vertexShader = /* glsl */ ` + in vec3 position; + + uniform mat4 modelMatrix; + uniform mat4 modelViewMatrix; + uniform mat4 projectionMatrix; + uniform vec3 cameraPos; + + out vec3 vOrigin; + out vec3 vDirection; + + void main() { + vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); + + vOrigin = vec3( inverse( modelMatrix ) * vec4( cameraPos, 1.0 ) ).xyz; + vDirection = position - vOrigin; + + gl_Position = projectionMatrix * mvPosition; + } + `; + + const fragmentShader = /* glsl */ ` + precision highp float; + precision highp sampler3D; + + uniform mat4 modelViewMatrix; + uniform mat4 projectionMatrix; + + in vec3 vOrigin; + in vec3 vDirection; + + out vec4 color; + + uniform vec3 base; + uniform sampler3D map; + + uniform float threshold; + uniform float range; + uniform float opacity; + uniform float steps; + uniform float frame; + + uint wang_hash(uint seed) + { + seed = (seed ^ 61u) ^ (seed >> 16u); + seed *= 9u; + seed = seed ^ (seed >> 4u); + seed *= 0x27d4eb2du; + seed = seed ^ (seed >> 15u); + return seed; + } + + float randomFloat(inout uint seed) + { + return float(wang_hash(seed)) / 4294967296.; + } + + vec2 hitBox( vec3 orig, vec3 dir ) { + const vec3 box_min = vec3( - 0.5 ); + const vec3 box_max = vec3( 0.5 ); + vec3 inv_dir = 1.0 / dir; + vec3 tmin_tmp = ( box_min - orig ) * inv_dir; + vec3 tmax_tmp = ( box_max - orig ) * inv_dir; + vec3 tmin = min( tmin_tmp, tmax_tmp ); + vec3 tmax = max( tmin_tmp, tmax_tmp ); + float t0 = max( tmin.x, max( tmin.y, tmin.z ) ); + float t1 = min( tmax.x, min( tmax.y, tmax.z ) ); + return vec2( t0, t1 ); + } + + float sample1( vec3 p ) { + return texture( map, p ).r; + } + + float shading( vec3 coord ) { + float step = 0.01; + return sample1( coord + vec3( - step ) ) - sample1( coord + vec3( step ) ); + } + + vec4 linearToSRGB( in vec4 value ) { + return vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a ); + } + + void main(){ + vec3 rayDir = normalize( vDirection ); + vec2 bounds = hitBox( vOrigin, rayDir ); + + if ( bounds.x > bounds.y ) discard; + + bounds.x = max( bounds.x, 0.0 ); + + vec3 p = vOrigin + bounds.x * rayDir; + vec3 inc = 1.0 / abs( rayDir ); + float delta = min( inc.x, min( inc.y, inc.z ) ); + delta /= steps; + + // Jitter + + // Nice little seed from + // https://blog.demofox.org/2020/05/25/casual-shadertoy-path-tracing-1-basic-camera-diffuse-emissive/ + uint seed = uint( gl_FragCoord.x ) * uint( 1973 ) + uint( gl_FragCoord.y ) * uint( 9277 ) + uint( frame ) * uint( 26699 ); + vec3 size = vec3( textureSize( map, 0 ) ); + float randNum = randomFloat( seed ) * 2.0 - 1.0; + p += rayDir * randNum * ( 1.0 / size ); + + // + + vec4 ac = vec4( base, 0.0 ); + + for ( float t = bounds.x; t < bounds.y; t += delta ) { + + float d = sample1( p + 0.5 ); + + d = smoothstep( threshold - range, threshold + range, d ) * opacity; + + float col = shading( p + 0.5 ) * 3.0 + ( ( p.x + p.y ) * 0.25 ) + 0.2; + + ac.rgb += ( 1.0 - ac.a ) * d * col; + + ac.a += ( 1.0 - ac.a ) * d; + + if ( ac.a >= 0.95 ) break; + + p += rayDir * delta; + + } + + color = linearToSRGB( ac ); + + if ( color.a == 0.0 ) discard; + + } + `; + + const geometry = new THREE.BoxGeometry(1, 1, 1); + const material = new THREE.RawShaderMaterial({ + glslVersion: THREE.GLSL3, + uniforms: { + base: { value: new THREE.Color(0x798aa0) }, + map: { value: texture }, + cameraPos: { value: new THREE.Vector3() }, + threshold: { value: 0.25 }, + opacity: { value: 0.25 }, + range: { value: 0.1 }, + steps: { value: 100 }, + frame: { value: 0 }, + }, + vertexShader, + fragmentShader, + side: THREE.BackSide, + transparent: true, + }); + + mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + // + + const parameters = { + threshold: 0.25, + opacity: 0.25, + range: 0.1, + steps: 100, + }; + + function update() { + material.uniforms.threshold.value = parameters.threshold; + material.uniforms.opacity.value = parameters.opacity; + material.uniforms.range.value = parameters.range; + material.uniforms.steps.value = parameters.steps; + } + + const gui = new GUI(); + gui.add(parameters, 'threshold', 0, 1, 0.01).onChange(update); + gui.add(parameters, 'opacity', 0, 1, 0.01).onChange(update); + gui.add(parameters, 'range', 0, 1, 0.01).onChange(update); + gui.add(parameters, 'steps', 0, 200, 1).onChange(update); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +let curr = 0; +const countPerRow = 4; +const countPerSlice = countPerRow * countPerRow; +const sliceCount = 4; +const totalCount = sliceCount * countPerSlice; +const margins = 8; + +const perElementPaddedSize = (INITIAL_CLOUD_SIZE - margins) / countPerRow; +const perElementSize = Math.floor((INITIAL_CLOUD_SIZE - 1) / countPerRow); + +function animate() { + const time = performance.now(); + if (time - prevTime > 1500.0 && curr < totalCount) { + const position = new THREE.Vector3( + Math.floor(curr % countPerRow) * perElementSize + margins * 0.5, + Math.floor((curr % countPerSlice) / countPerRow) * perElementSize + margins * 0.5, + Math.floor(curr / countPerSlice) * perElementSize + margins * 0.5, + ).floor(); + + const maxDimension = perElementPaddedSize - 1; + const box = new THREE.Box3( + new THREE.Vector3(0, 0, 0), + new THREE.Vector3(maxDimension, maxDimension, maxDimension), + ); + const scaleFactor = (Math.random() + 0.5) * 0.5; + const source = generateCloudTexture(perElementPaddedSize, scaleFactor); + + renderer.copyTextureToTexture(source, cloudTexture, box, position); + + prevTime = time; + + curr++; + } + + mesh.material.uniforms.cameraPos.value.copy(camera.position); + // mesh.rotation.y = - performance.now() / 7500; + + mesh.material.uniforms.frame.value++; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_tonemapping.ts b/examples-testing/examples/webgl_tonemapping.ts new file mode 100644 index 000000000..2163e1b06 --- /dev/null +++ b/examples-testing/examples/webgl_tonemapping.ts @@ -0,0 +1,172 @@ +import * as THREE from 'three'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; +import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; + +let renderer, scene, camera, controls; +let gui, + guiExposure = null; + +const params = { + exposure: 1.0, + toneMapping: 'Neutral', + blurriness: 0.3, + intensity: 1.0, +}; + +const toneMappingOptions = { + None: THREE.NoToneMapping, + Linear: THREE.LinearToneMapping, + Reinhard: THREE.ReinhardToneMapping, + Cineon: THREE.CineonToneMapping, + ACESFilmic: THREE.ACESFilmicToneMapping, + AgX: THREE.AgXToneMapping, + Neutral: THREE.NeutralToneMapping, + Custom: THREE.CustomToneMapping, +}; + +init().catch(function (err) { + console.error(err); +}); + +async function init() { + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + renderer.toneMapping = toneMappingOptions[params.toneMapping]; + renderer.toneMappingExposure = params.exposure; + + // Set CustomToneMapping to Uncharted2 + // source: http://filmicworlds.com/blog/filmic-tonemapping-operators/ + + THREE.ShaderChunk.tonemapping_pars_fragment = THREE.ShaderChunk.tonemapping_pars_fragment.replace( + 'vec3 CustomToneMapping( vec3 color ) { return color; }', + + `#define Uncharted2Helper( x ) max( ( ( x * ( 0.15 * x + 0.10 * 0.50 ) + 0.20 * 0.02 ) / ( x * ( 0.15 * x + 0.50 ) + 0.20 * 0.30 ) ) - 0.02 / 0.30, vec3( 0.0 ) ) + + float toneMappingWhitePoint = 1.0; + + vec3 CustomToneMapping( vec3 color ) { + color *= toneMappingExposure; + return saturate( Uncharted2Helper( color ) / Uncharted2Helper( vec3( toneMappingWhitePoint ) ) ); + + }`, + ); + + scene = new THREE.Scene(); + scene.backgroundBlurriness = params.blurriness; + + const light = new THREE.DirectionalLight(0xfff3ee, 3); // simulate sun + light.position.set(1, 0.05, 0.7); + scene.add(light); + + // scene.add( new THREE.DirectionalLightHelper( light, 1, 0x000000 ) ); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.01, 10); + camera.position.set(-0.02, 0.03, 0.05); + + controls = new OrbitControls(camera, renderer.domElement); + controls.enablePan = false; + controls.enableDamping = true; + controls.minDistance = 0.03; + controls.maxDistance = 0.2; + controls.target.set(0, 0.03, 0); + controls.update(); + + const hdrLoader = new HDRLoader().setPath('textures/equirectangular/'); + + const dracoLoader = new DRACOLoader(); + dracoLoader.setDecoderPath('jsm/libs/draco/gltf/'); + + const gltfLoader = new GLTFLoader(); + gltfLoader.setDRACOLoader(dracoLoader); + gltfLoader.setPath('models/gltf/'); + + const [texture, gltf] = await Promise.all([ + hdrLoader.loadAsync('venice_sunset_1k.hdr'), + gltfLoader.loadAsync('venice_mask.glb'), + ]); + + // environment + + texture.mapping = THREE.EquirectangularReflectionMapping; + + scene.background = texture; + scene.environment = texture; + + // model + + scene.add(gltf.scene); + + window.addEventListener('resize', onWindowResize); + + // + + gui = new GUI(); + const toneMappingFolder = gui.addFolder('Tone Mapping'); + + toneMappingFolder + .add(params, 'toneMapping', Object.keys(toneMappingOptions)) + + .name('type') + .onChange(function () { + updateGUI(); + + renderer.toneMapping = toneMappingOptions[params.toneMapping]; + }); + + guiExposure = toneMappingFolder + .add(params, 'exposure', 0, 2) + + .onChange(function (value) { + renderer.toneMappingExposure = value; + }); + + const backgroundFolder = gui.addFolder('Background'); + + backgroundFolder + .add(params, 'blurriness', 0, 1) + + .onChange(function (value) { + scene.backgroundBlurriness = value; + }); + + backgroundFolder + .add(params, 'intensity', 0, 1) + + .onChange(function (value) { + scene.backgroundIntensity = value; + }); + + updateGUI(); + + gui.open(); +} + +function updateGUI() { + if (params.toneMapping === 'None') { + guiExposure.hide(); + } else { + guiExposure.show(); + } +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + controls.update(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_tsl_clearcoat.ts b/examples-testing/examples/webgl_tsl_clearcoat.ts new file mode 100644 index 000000000..6a123032e --- /dev/null +++ b/examples-testing/examples/webgl_tsl_clearcoat.ts @@ -0,0 +1,194 @@ +import * as THREE from 'three/webgpu'; +import { WebGLNodesHandler } from 'three/addons/tsl/WebGLNodesHandler.js'; +import { WebGLRenderer } from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { HDRCubeTextureLoader } from 'three/addons/loaders/HDRCubeTextureLoader.js'; + +import { FlakesTexture } from 'three/addons/textures/FlakesTexture.js'; + +let camera, scene, renderer; + +let particleLight; +let group; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 0.25, 50); + camera.position.z = 10; + + scene = new THREE.Scene(); + + group = new THREE.Group(); + scene.add(group); + + new HDRCubeTextureLoader() + .setPath('textures/cube/pisaHDR/') + .load(['px.hdr', 'nx.hdr', 'py.hdr', 'ny.hdr', 'pz.hdr', 'nz.hdr'], function (texture) { + const geometry = new THREE.SphereGeometry(0.8, 64, 32); + + const textureLoader = new THREE.TextureLoader(); + + const diffuse = textureLoader.load('textures/carbon/Carbon.png'); + diffuse.colorSpace = THREE.SRGBColorSpace; + diffuse.wrapS = THREE.RepeatWrapping; + diffuse.wrapT = THREE.RepeatWrapping; + diffuse.repeat.x = 10; + diffuse.repeat.y = 10; + + const normalMap = textureLoader.load('textures/carbon/Carbon_Normal.png'); + normalMap.wrapS = THREE.RepeatWrapping; + normalMap.wrapT = THREE.RepeatWrapping; + normalMap.repeat.x = 10; + normalMap.repeat.y = 10; + + const normalMap2 = textureLoader.load('textures/water/Water_1_M_Normal.jpg'); + + const normalMap3 = new THREE.CanvasTexture(new FlakesTexture()); + normalMap3.wrapS = THREE.RepeatWrapping; + normalMap3.wrapT = THREE.RepeatWrapping; + normalMap3.repeat.x = 10; + normalMap3.repeat.y = 6; + normalMap3.anisotropy = 16; + + const normalMap4 = textureLoader.load('textures/golfball.jpg'); + + const clearcoatNormalMap = textureLoader.load( + 'textures/pbr/Scratched_gold/Scratched_gold_01_1K_Normal.png', + ); + + // car paint + + let material = new THREE.MeshPhysicalNodeMaterial({ + clearcoat: 1.0, + clearcoatRoughness: 0.1, + metalness: 0.9, + roughness: 0.5, + color: 0x0000ff, + normalMap: normalMap3, + normalScale: new THREE.Vector2(0.15, 0.15), + }); + let mesh = new THREE.Mesh(geometry, material); + mesh.position.x = -1; + mesh.position.y = 1; + group.add(mesh); + + // fibers + + material = new THREE.MeshPhysicalNodeMaterial({ + roughness: 0.5, + clearcoat: 1.0, + clearcoatRoughness: 0.1, + map: diffuse, + normalMap: normalMap, + }); + mesh = new THREE.Mesh(geometry, material); + mesh.position.x = 1; + mesh.position.y = 1; + group.add(mesh); + + // golf + + material = new THREE.MeshPhysicalNodeMaterial({ + metalness: 0.0, + roughness: 0.1, + clearcoat: 1.0, + normalMap: normalMap4, + clearcoatNormalMap: clearcoatNormalMap, + + // y scale is negated to compensate for normal map handedness. + clearcoatNormalScale: new THREE.Vector2(2.0, -2.0), + }); + mesh = new THREE.Mesh(geometry, material); + mesh.position.x = -1; + mesh.position.y = -1; + group.add(mesh); + + // clearcoat + normalmap + + material = new THREE.MeshPhysicalNodeMaterial({ + clearcoat: 1.0, + metalness: 1.0, + color: 0xff0000, + normalMap: normalMap2, + normalScale: new THREE.Vector2(0.15, 0.15), + clearcoatNormalMap: clearcoatNormalMap, + + // y scale is negated to compensate for normal map handedness. + clearcoatNormalScale: new THREE.Vector2(2.0, -2.0), + }); + mesh = new THREE.Mesh(geometry, material); + mesh.position.x = 1; + mesh.position.y = -1; + group.add(mesh); + + // + + scene.background = texture; + scene.environment = texture; + }); + + // LIGHTS + + particleLight = new THREE.Mesh( + new THREE.SphereGeometry(0.05, 8, 8), + new THREE.MeshBasicMaterial({ color: 0xffffff }), + ); + scene.add(particleLight); + + particleLight.add(new THREE.PointLight(0xffffff, 30)); + + renderer = new WebGLRenderer({ antialias: true }); + renderer.setNodesHandler(new WebGLNodesHandler()); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // + + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = 1.25; + + // EVENTS + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 3; + controls.maxDistance = 30; + + window.addEventListener('resize', onWindowResize); +} + +// + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); +} + +// + +function animate() { + render(); +} + +function render() { + const timer = Date.now() * 0.00025; + + particleLight.position.x = Math.sin(timer * 7) * 3; + particleLight.position.y = Math.cos(timer * 5) * 4; + particleLight.position.z = Math.cos(timer * 3) * 3; + + for (let i = 0; i < group.children.length; i++) { + const child = group.children[i]; + child.rotation.y += 0.005; + } + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_tsl_instancing.ts b/examples-testing/examples/webgl_tsl_instancing.ts new file mode 100644 index 000000000..c80bebf9b --- /dev/null +++ b/examples-testing/examples/webgl_tsl_instancing.ts @@ -0,0 +1,286 @@ +import * as THREE from 'three'; +import { WebGLNodesHandler } from 'three/addons/tsl/WebGLNodesHandler.js'; + +import Stats from 'three/addons/libs/stats.module.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js'; +import { MeshPhongNodeMaterial } from 'three/webgpu'; +import { sin, time, vec3, positionWorld } from 'three/tsl'; + +let container, stats, gui, guiStatsEl; +let camera, controls, scene, renderer, material; + +// gui + +const Method = { + INSTANCED: 'INSTANCED', + MERGED: 'MERGED', + NAIVE: 'NAIVE', +}; + +const api = { + method: Method.INSTANCED, + count: 1000, +}; + +// + +init(); +initMesh(); + +// + +function clean() { + const meshes = []; + + scene.traverse(function (object) { + if (object.isMesh) meshes.push(object); + }); + + for (let i = 0; i < meshes.length; i++) { + const mesh = meshes[i]; + mesh.material.dispose(); + mesh.geometry.dispose(); + + scene.remove(mesh); + } +} + +const randomizeMatrix = (function () { + const position = new THREE.Vector3(); + const quaternion = new THREE.Quaternion(); + const scale = new THREE.Vector3(); + + return function (matrix) { + position.x = Math.random() * 40 - 20; + position.y = Math.random() * 40 - 20; + position.z = Math.random() * 40 - 20; + + quaternion.random(); + + scale.x = scale.y = scale.z = Math.random() * 1; + + matrix.compose(position, quaternion, scale); + }; +})(); + +function initMesh() { + clean(); + + // make instances + new THREE.BufferGeometryLoader().setPath('models/json/').load('suzanne_buffergeometry.json', function (geometry) { + const posY = positionWorld; + const t = time.mul(4); + material = new MeshPhongNodeMaterial(); + material.colorNode = vec3( + sin(posY.mul(0.1).add(t)).mul(0.5).add(0.5).x, + sin(posY.mul(0.1).add(t.mul(0.5).add(2))) + .mul(0.5) + .add(0.5).y, + sin(posY.mul(0.1).add(t.mul(1.5).add(4))) + .mul(0.5) + .add(0.5).z, + ); + + geometry.computeVertexNormals(); + + console.time(api.method + ' (build)'); + + switch (api.method) { + case Method.INSTANCED: + makeInstanced(geometry); + break; + + case Method.MERGED: + makeMerged(geometry); + break; + + case Method.NAIVE: + makeNaive(geometry); + break; + } + + console.timeEnd(api.method + ' (build)'); + }); +} + +function makeInstanced(geometry) { + const matrix = new THREE.Matrix4(); + const mesh = new THREE.InstancedMesh(geometry, material, api.count); + + for (let i = 0; i < api.count; i++) { + randomizeMatrix(matrix); + mesh.setMatrixAt(i, matrix); + } + + scene.add(mesh); + + // + + const geometryByteLength = getGeometryByteLength(geometry); + + guiStatsEl.innerHTML = [ + 'GPU draw calls: 1', + 'GPU memory: ' + formatBytes(api.count * 16 + geometryByteLength, 2), + ].join('
'); +} + +function makeMerged(geometry) { + const geometries = []; + const matrix = new THREE.Matrix4(); + + for (let i = 0; i < api.count; i++) { + randomizeMatrix(matrix); + + const instanceGeometry = geometry.clone(); + instanceGeometry.applyMatrix4(matrix); + + geometries.push(instanceGeometry); + } + + const mergedGeometry = BufferGeometryUtils.mergeGeometries(geometries); + + scene.add(new THREE.Mesh(mergedGeometry, material)); + + // + + guiStatsEl.innerHTML = [ + 'GPU draw calls: 1', + 'GPU memory: ' + formatBytes(getGeometryByteLength(mergedGeometry), 2), + ].join('
'); +} + +function makeNaive(geometry) { + const matrix = new THREE.Matrix4(); + + for (let i = 0; i < api.count; i++) { + randomizeMatrix(matrix); + + const mesh = new THREE.Mesh(geometry, material); + mesh.applyMatrix4(matrix); + + scene.add(mesh); + } + + // + + const geometryByteLength = getGeometryByteLength(geometry); + + guiStatsEl.innerHTML = [ + 'GPU draw calls: ' + api.count, + 'GPU memory: ' + formatBytes(api.count * 16 + geometryByteLength, 2), + ].join('
'); +} + +function init() { + const width = window.innerWidth; + const height = window.innerHeight; + + // camera + + camera = new THREE.PerspectiveCamera(70, width / height, 1, 100); + camera.position.z = 30; + + // renderer + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setNodesHandler(new WebGLNodesHandler()); + + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(width, height); + renderer.setAnimationLoop(animate); + container = document.getElementById('container'); + container.appendChild(renderer.domElement); + + // scene + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xffffff); + + // controls + + controls = new OrbitControls(camera, renderer.domElement); + controls.autoRotate = true; + controls.autoRotateSpeed = 0.5; + + // light + + const ambientLight = new THREE.AmbientLight(0xffffff, 2); + const directionalLight = new THREE.DirectionalLight(0xffffff, 2); + directionalLight.position.set(1, 1, 1); + scene.add(directionalLight, ambientLight); + + // stats + + stats = new Stats(); + container.appendChild(stats.dom); + + // gui + + gui = new GUI(); + gui.add(api, 'method', Method).onChange(initMesh); + gui.add(api, 'count', 1, 10000).step(1).onChange(initMesh); + + const perfFolder = gui.addFolder('Performance'); + + guiStatsEl = document.createElement('div'); + guiStatsEl.classList.add('gui-stats'); + + perfFolder.$children.appendChild(guiStatsEl); + perfFolder.open(); + + // listeners + + window.addEventListener('resize', onWindowResize); + + Object.assign(window, { scene }); +} + +// + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); +} + +function animate() { + controls.update(); + + renderer.render(scene, camera); + + stats.update(); +} + +// + +function getGeometryByteLength(geometry) { + let total = 0; + + if (geometry.index) total += geometry.index.array.byteLength; + + for (const name in geometry.attributes) { + total += geometry.attributes[name].array.byteLength; + } + + return total; +} + +// Source: https://stackoverflow.com/a/18650828/1314762 +function formatBytes(bytes, decimals) { + if (bytes === 0) return '0 bytes'; + + const k = 1024; + const dm = decimals < 0 ? 0 : decimals; + const sizes = ['bytes', 'KB', 'MB']; + + const i = Math.floor(Math.log(bytes) / Math.log(k)); + + return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; +} diff --git a/examples-testing/examples/webgl_tsl_shadowmap.ts b/examples-testing/examples/webgl_tsl_shadowmap.ts new file mode 100644 index 000000000..f006d7c1a --- /dev/null +++ b/examples-testing/examples/webgl_tsl_shadowmap.ts @@ -0,0 +1,160 @@ +import * as THREE from 'three/webgpu'; +import { WebGLNodesHandler } from 'three/addons/tsl/WebGLNodesHandler.js'; +import { WebGLRenderer } from 'three'; +import { mx_fractal_noise_float, mx_fractal_noise_vec3, positionLocal, positionWorld, Fn, color } from 'three/tsl'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let camera, scene, renderer, timer; +let dirLight, spotLight; +let torusKnot, dirGroup; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(0, 10, 20); + + scene = new THREE.Scene(); + scene.backgroundNode = color(0x222244); + scene.fog = new THREE.Fog(0x222244, 50, 100); + + // lights + + scene.add(new THREE.AmbientLight(0x444444, 2)); + + spotLight = new THREE.SpotLight(0xff8888, 400); + spotLight.angle = Math.PI / 5; + spotLight.penumbra = 0.3; + spotLight.position.set(8, 10, 5); + spotLight.castShadow = true; + spotLight.shadow.camera.near = 8; + spotLight.shadow.camera.far = 200; + spotLight.shadow.mapSize.width = 2048; + spotLight.shadow.mapSize.height = 2048; + spotLight.shadow.radius = 4; + scene.add(spotLight); + + dirLight = new THREE.DirectionalLight(0x8888ff, 3); + dirLight.position.set(3, 12, 17); + dirLight.castShadow = true; + dirLight.shadow.camera.near = 0.1; + dirLight.shadow.camera.far = 500; + dirLight.shadow.camera.right = 17; + dirLight.shadow.camera.left = -17; + dirLight.shadow.camera.top = 17; + dirLight.shadow.camera.bottom = -17; + dirLight.shadow.mapSize.width = 2048; + dirLight.shadow.mapSize.height = 2048; + dirLight.shadow.radius = 4; + + dirGroup = new THREE.Group(); + dirGroup.add(dirLight); + scene.add(dirGroup); + + // geometry + + const geometry = new THREE.TorusKnotGeometry(25, 8, 75, 80); + const material = new THREE.MeshPhongNodeMaterial({ + color: 0x999999, + shininess: 0, + specular: 0x222222, + }); + + const materialCustomShadow = material.clone(); + materialCustomShadow.transparent = true; + + const discardNode = mx_fractal_noise_float(positionLocal.mul(0.1)).x.greaterThan(0.0); + + materialCustomShadow.maskNode = discardNode; + + torusKnot = new THREE.Mesh(geometry, materialCustomShadow); + torusKnot.scale.multiplyScalar(1 / 18); + torusKnot.position.y = 3; + torusKnot.castShadow = true; + torusKnot.receiveShadow = true; + scene.add(torusKnot); + + const cylinderGeometry = new THREE.CylinderGeometry(0.75, 0.75, 7, 32); + + const pillar1 = new THREE.Mesh(cylinderGeometry, material); + pillar1.position.set(8, 3.5, 8); + pillar1.castShadow = true; + + const pillar2 = pillar1.clone(); + pillar2.position.set(8, 3.5, -8); + const pillar3 = pillar1.clone(); + pillar3.position.set(-8, 3.5, 8); + const pillar4 = pillar1.clone(); + pillar4.position.set(-8, 3.5, -8); + + scene.add(pillar1); + scene.add(pillar2); + scene.add(pillar3); + scene.add(pillar4); + + const planeGeometry = new THREE.PlaneGeometry(200, 200); + + const planeMaterial = new THREE.MeshPhongNodeMaterial(); + planeMaterial.color.setHex(0x999999); + planeMaterial.shininess = 0; + planeMaterial.specular.setHex(0x111111); + + planeMaterial.colorNode = Fn(() => { + const pos = positionWorld.toVar(); + pos.xz.addAssign(mx_fractal_noise_vec3(positionWorld.mul(2)).saturate().xz); + return mx_fractal_noise_vec3(positionWorld.mul(2)).saturate().zzz.mul(0.2).add(0.5); + })(); + + const ground = new THREE.Mesh(planeGeometry, planeMaterial); + ground.rotation.x = -Math.PI / 2; + ground.scale.multiplyScalar(3); + ground.castShadow = true; + ground.receiveShadow = true; + scene.add(ground); + + // renderer + + renderer = new WebGLRenderer({ antialias: true }); + renderer.setNodesHandler(new WebGLNodesHandler()); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.shadowMap.enabled = true; + renderer.toneMapping = THREE.ACESFilmicToneMapping; + document.body.appendChild(renderer.domElement); + + // Mouse control + const controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, 2, 0); + controls.minDistance = 7; + controls.maxDistance = 40; + controls.update(); + + timer = new THREE.Timer(); + timer.connect(document); + + window.addEventListener('resize', resize); +} + +function resize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate(time) { + timer.update(); + + const delta = timer.getDelta(); + + torusKnot.rotation.x += 0.25 * delta; + torusKnot.rotation.y += 0.5 * delta; + torusKnot.rotation.z += 1 * delta; + + dirGroup.rotation.y += 0.7 * delta; + dirLight.position.z = 17 + Math.sin(time * 0.001) * 5; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_tsl_skinning.ts b/examples-testing/examples/webgl_tsl_skinning.ts new file mode 100644 index 000000000..6164dbab6 --- /dev/null +++ b/examples-testing/examples/webgl_tsl_skinning.ts @@ -0,0 +1,74 @@ +import * as THREE from 'three/webgpu'; +import { WebGLNodesHandler } from 'three/addons/tsl/WebGLNodesHandler.js'; +import { WebGLRenderer } from 'three'; + +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +let camera, scene, renderer; + +let mixer, timer; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.01, 100); + camera.position.set(1, 2, 3); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x3355aa); + camera.lookAt(0, 1, 0); + + timer = new THREE.Timer(); + timer.connect(document); + + //lights + + const light = new THREE.PointLight(0xffffff, 1, 100); + light.power = 2500; + camera.add(light); + scene.add(camera); + + const ambient = new THREE.AmbientLight(0x4466ff, 1); + scene.add(ambient); + + const loader = new GLTFLoader(); + loader.load('models/gltf/Michelle.glb', function (gltf) { + const object = gltf.scene; + mixer = new THREE.AnimationMixer(object); + + const action = mixer.clipAction(gltf.animations[0]); + action.play(); + + scene.add(object); + }); + + //renderer + + renderer = new WebGLRenderer({ antialias: true }); + renderer.setNodesHandler(new WebGLNodesHandler()); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.toneMapping = THREE.LinearToneMapping; + renderer.toneMappingExposure = 0.4; + document.body.appendChild(renderer.domElement); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + const delta = timer.getDelta(); + + if (mixer) mixer.update(delta); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_ubo.ts b/examples-testing/examples/webgl_ubo.ts new file mode 100644 index 000000000..a34a5b2ff --- /dev/null +++ b/examples-testing/examples/webgl_ubo.ts @@ -0,0 +1,140 @@ +import * as THREE from 'three'; + +let camera, scene, renderer, timer; + +init(); + +function init() { + const container = document.getElementById('container'); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(0, 0, 25); + + scene = new THREE.Scene(); + camera.lookAt(scene.position); + + timer = new THREE.Timer(); + timer.connect(document); + + // geometry + + const geometry1 = new THREE.TetrahedronGeometry(); + const geometry2 = new THREE.BoxGeometry(); + + // texture + + const texture = new THREE.TextureLoader().load('textures/crate.gif'); + texture.colorSpace = THREE.SRGBColorSpace; + + // uniforms groups + + // Camera and lighting related data are perfect examples of using UBOs since you have to store these + // data just once. They can be shared across all shader programs. + + const cameraUniformsGroup = new THREE.UniformsGroup(); + cameraUniformsGroup.setName('ViewData'); + cameraUniformsGroup.add(new THREE.Uniform(camera.projectionMatrix)); // projection matrix + cameraUniformsGroup.add(new THREE.Uniform(camera.matrixWorldInverse)); // view matrix + + const lightingUniformsGroup = new THREE.UniformsGroup(); + lightingUniformsGroup.setName('LightingData'); + lightingUniformsGroup.add(new THREE.Uniform(new THREE.Vector3(0, 0, 10))); // light position + lightingUniformsGroup.add(new THREE.Uniform(new THREE.Color(0x7c7c7c))); // ambient color + lightingUniformsGroup.add(new THREE.Uniform(new THREE.Color(0xd5d5d5))); // diffuse color + lightingUniformsGroup.add(new THREE.Uniform(new THREE.Color(0xe7e7e7))); // specular color + lightingUniformsGroup.add(new THREE.Uniform(64)); // shininess + + // materials + + const material1 = new THREE.RawShaderMaterial({ + uniforms: { + modelMatrix: { value: null }, + normalMatrix: { value: null }, + color: { value: null }, + }, + vertexShader: document.getElementById('vertexShader1').textContent, + fragmentShader: document.getElementById('fragmentShader1').textContent, + glslVersion: THREE.GLSL3, + }); + + const material2 = new THREE.RawShaderMaterial({ + uniforms: { + modelMatrix: { value: null }, + diffuseMap: { value: null }, + }, + vertexShader: document.getElementById('vertexShader2').textContent, + fragmentShader: document.getElementById('fragmentShader2').textContent, + glslVersion: THREE.GLSL3, + }); + + // meshes + + for (let i = 0; i < 200; i++) { + let mesh; + + if (i % 2 === 0) { + mesh = new THREE.Mesh(geometry1, material1.clone()); + + mesh.material.uniformsGroups = [cameraUniformsGroup, lightingUniformsGroup]; + mesh.material.uniforms.modelMatrix.value = mesh.matrixWorld; + mesh.material.uniforms.normalMatrix.value = mesh.normalMatrix; + mesh.material.uniforms.color.value = new THREE.Color(0xffffff * Math.random()); + } else { + mesh = new THREE.Mesh(geometry2, material2.clone()); + + mesh.material.uniformsGroups = [cameraUniformsGroup, lightingUniformsGroup]; + mesh.material.uniforms.modelMatrix.value = mesh.matrixWorld; + mesh.material.uniforms.diffuseMap.value = texture; + } + + scene.add(mesh); + + const s = 1 + Math.random() * 0.5; + + mesh.scale.x = s; + mesh.scale.y = s; + mesh.scale.z = s; + + mesh.rotation.x = Math.random() * Math.PI; + mesh.rotation.y = Math.random() * Math.PI; + mesh.rotation.z = Math.random() * Math.PI; + + mesh.position.x = Math.random() * 40 - 20; + mesh.position.y = Math.random() * 40 - 20; + mesh.position.z = Math.random() * 20 - 10; + } + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + window.addEventListener('resize', onWindowResize, false); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + timer.update(); + + const delta = timer.getDelta(); + + scene.traverse(function (child) { + if (child.isMesh) { + child.rotation.x += delta * 0.5; + child.rotation.y += delta * 0.3; + } + }); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_ubo_arrays.ts b/examples-testing/examples/webgl_ubo_arrays.ts new file mode 100644 index 000000000..1d8f5f763 --- /dev/null +++ b/examples-testing/examples/webgl_ubo_arrays.ts @@ -0,0 +1,174 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let camera, scene, renderer, timer, stats; + +let lightingUniformsGroup, lightCenters; + +const container = document.getElementById('container'); + +const pointLightsMax = 300; + +const api = { + count: 200, +}; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(0, 50, 50); + + scene = new THREE.Scene(); + camera.lookAt(scene.position); + + timer = new THREE.Timer(); + timer.connect(document); + + // geometry + + const geometry = new THREE.SphereGeometry(); + + // uniforms groups + + lightingUniformsGroup = new THREE.UniformsGroup(); + lightingUniformsGroup.setName('LightingData'); + + const data = []; + const dataColors = []; + lightCenters = []; + + for (let i = 0; i < pointLightsMax; i++) { + const col = new THREE.Color(0xffffff * Math.random()).toArray(); + const x = Math.random() * 50 - 25; + const z = Math.random() * 50 - 25; + + data.push(new THREE.Uniform(new THREE.Vector4(x, 1, z, 0))); // light position + dataColors.push(new THREE.Uniform(new THREE.Vector4(col[0], col[1], col[2], 0))); // light color + + // Store the center positions + lightCenters.push({ x, z }); + } + + lightingUniformsGroup.add(data); // light position + lightingUniformsGroup.add(dataColors); // light position + lightingUniformsGroup.add(new THREE.Uniform(pointLightsMax)); // light position + + const cameraUniformsGroup = new THREE.UniformsGroup(); + cameraUniformsGroup.setName('ViewData'); + cameraUniformsGroup.add(new THREE.Uniform(camera.projectionMatrix)); // projection matrix + cameraUniformsGroup.add(new THREE.Uniform(camera.matrixWorldInverse)); // view matrix + + const material = new THREE.RawShaderMaterial({ + uniforms: { + modelMatrix: { value: null }, + normalMatrix: { value: null }, + }, + // uniformsGroups: [ cameraUniformsGroup, lightingUniformsGroup ], + name: 'Box', + defines: { + POINTLIGHTS_MAX: pointLightsMax, + }, + vertexShader: document.getElementById('vertexShader').textContent, + fragmentShader: document.getElementById('fragmentShader').textContent, + glslVersion: THREE.GLSL3, + }); + + const plane = new THREE.Mesh(new THREE.PlaneGeometry(100, 100), material.clone()); + plane.material.uniformsGroups = [cameraUniformsGroup, lightingUniformsGroup]; + plane.material.uniforms.modelMatrix.value = plane.matrixWorld; + plane.material.uniforms.normalMatrix.value = plane.normalMatrix; + plane.rotation.x = -Math.PI / 2; + plane.position.y = -1; + scene.add(plane); + + // meshes + const gridSize = { x: 10, y: 1, z: 10 }; + const spacing = 6; + + for (let i = 0; i < gridSize.x; i++) { + for (let j = 0; j < gridSize.y; j++) { + for (let k = 0; k < gridSize.z; k++) { + const mesh = new THREE.Mesh(geometry, material.clone()); + mesh.name = 'Sphere'; + mesh.material.uniformsGroups = [cameraUniformsGroup, lightingUniformsGroup]; + mesh.material.uniforms.modelMatrix.value = mesh.matrixWorld; + mesh.material.uniforms.normalMatrix.value = mesh.normalMatrix; + scene.add(mesh); + + mesh.position.x = i * spacing - (gridSize.x * spacing) / 2; + mesh.position.y = 0; + mesh.position.z = k * spacing - (gridSize.z * spacing) / 2; + } + } + } + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + window.addEventListener('resize', onWindowResize, false); + + // controls + + const controls = new OrbitControls(camera, renderer.domElement); + controls.enablePan = false; + + // stats + + stats = new Stats(); + document.body.appendChild(stats.dom); + + // gui + const gui = new GUI(); + gui.add(api, 'count', 1, pointLightsMax) + .step(1) + .onChange(function () { + lightingUniformsGroup.uniforms[2].value = api.count; + }); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + timer.update(); + + const elapsedTime = timer.getElapsed(); + + const lights = lightingUniformsGroup.uniforms[0]; + + // Parameters for circular movement + const radius = 5; // Smaller radius for individual circular movements + const speed = 0.5; // Speed of rotation + + // Update each light's position + for (let i = 0; i < lights.length; i++) { + const light = lights[i]; + const center = lightCenters[i]; + + // Calculate circular movement around the light's center + const angle = speed * elapsedTime + i * 0.5; // Phase difference for each light + const x = center.x + Math.sin(angle) * radius; + const z = center.z + Math.cos(angle) * radius; + + // Update the light's position + light.value.set(x, 1, z, 0); + } + + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/webgl_video_kinect.ts b/examples-testing/examples/webgl_video_kinect.ts new file mode 100644 index 000000000..8abc93917 --- /dev/null +++ b/examples-testing/examples/webgl_video_kinect.ts @@ -0,0 +1,114 @@ +import * as THREE from 'three'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let scene, camera, renderer; +let geometry, mesh, material; +let mouse, center; + +init(); + +function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + const info = document.createElement('div'); + info.id = 'info'; + info.innerHTML = 'three.js - kinect'; + document.body.appendChild(info); + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000); + camera.position.set(0, 0, 500); + + scene = new THREE.Scene(); + center = new THREE.Vector3(); + center.z = -1000; + + const video = document.getElementById('video'); + + const texture = new THREE.VideoTexture(video); + texture.minFilter = THREE.NearestFilter; + texture.generateMipmaps = false; + + const width = 640, + height = 480; + const nearClipping = 850, + farClipping = 4000; + + geometry = new THREE.BufferGeometry(); + + const vertices = new Float32Array(width * height * 3); + + for (let i = 0, j = 0, l = vertices.length; i < l; i += 3, j++) { + vertices[i] = j % width; + vertices[i + 1] = Math.floor(j / width); + } + + geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3)); + + material = new THREE.ShaderMaterial({ + uniforms: { + map: { value: texture }, + width: { value: width }, + height: { value: height }, + nearClipping: { value: nearClipping }, + farClipping: { value: farClipping }, + + pointSize: { value: 2 }, + zOffset: { value: 1000 }, + }, + vertexShader: document.getElementById('vs').textContent, + fragmentShader: document.getElementById('fs').textContent, + blending: THREE.AdditiveBlending, + depthTest: false, + depthWrite: false, + transparent: true, + }); + + mesh = new THREE.Points(geometry, material); + scene.add(mesh); + + const gui = new GUI(); + gui.add(material.uniforms.nearClipping, 'value', 1, 10000, 1.0).name('nearClipping'); + gui.add(material.uniforms.farClipping, 'value', 1, 10000, 1.0).name('farClipping'); + gui.add(material.uniforms.pointSize, 'value', 1, 10, 1.0).name('pointSize'); + gui.add(material.uniforms.zOffset, 'value', 0, 4000, 1.0).name('zOffset'); + + video.play(); + + // + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + mouse = new THREE.Vector3(0, 0, 1); + + document.addEventListener('mousemove', onDocumentMouseMove); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function onDocumentMouseMove(event) { + mouse.x = (event.clientX - window.innerWidth / 2) * 8; + mouse.y = (event.clientY - window.innerHeight / 2) * 8; +} + +function animate() { + camera.position.x += (mouse.x - camera.position.x) * 0.05; + camera.position.y += (-mouse.y - camera.position.y) * 0.05; + camera.lookAt(center); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_video_panorama_equirectangular.ts b/examples-testing/examples/webgl_video_panorama_equirectangular.ts new file mode 100644 index 000000000..866eca16a --- /dev/null +++ b/examples-testing/examples/webgl_video_panorama_equirectangular.ts @@ -0,0 +1,95 @@ +import * as THREE from 'three'; + +let camera, scene, renderer; + +let isUserInteracting = false, + lon = 0, + lat = 0, + phi = 0, + theta = 0, + onPointerDownPointerX = 0, + onPointerDownPointerY = 0, + onPointerDownLon = 0, + onPointerDownLat = 0; + +const distance = 0.5; + +init(); + +function init() { + const container = document.getElementById('container'); + + camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.25, 10); + + scene = new THREE.Scene(); + + const geometry = new THREE.SphereGeometry(5, 60, 40); + // invert the geometry on the x-axis so that all of the faces point inward + geometry.scale(-1, 1, 1); + + const video = document.getElementById('video'); + video.play(); + + const texture = new THREE.VideoTexture(video); + texture.colorSpace = THREE.SRGBColorSpace; + const material = new THREE.MeshBasicMaterial({ map: texture }); + + const mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + document.addEventListener('pointerdown', onPointerDown); + document.addEventListener('pointermove', onPointerMove); + document.addEventListener('pointerup', onPointerUp); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function onPointerDown(event) { + isUserInteracting = true; + + onPointerDownPointerX = event.clientX; + onPointerDownPointerY = event.clientY; + + onPointerDownLon = lon; + onPointerDownLat = lat; +} + +function onPointerMove(event) { + if (isUserInteracting === true) { + lon = (onPointerDownPointerX - event.clientX) * 0.1 + onPointerDownLon; + lat = (onPointerDownPointerY - event.clientY) * 0.1 + onPointerDownLat; + } +} + +function onPointerUp() { + isUserInteracting = false; +} + +function animate() { + lat = Math.max(-85, Math.min(85, lat)); + phi = THREE.MathUtils.degToRad(90 - lat); + theta = THREE.MathUtils.degToRad(lon); + + camera.position.x = distance * Math.sin(phi) * Math.cos(theta); + camera.position.y = distance * Math.cos(phi); + camera.position.z = distance * Math.sin(phi) * Math.sin(theta); + + camera.lookAt(0, 0, 0); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_volume_cloud.ts b/examples-testing/examples/webgl_volume_cloud.ts new file mode 100644 index 000000000..9aa07b98f --- /dev/null +++ b/examples-testing/examples/webgl_volume_cloud.ts @@ -0,0 +1,279 @@ +import * as THREE from 'three'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { ImprovedNoise } from 'three/addons/math/ImprovedNoise.js'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let renderer, scene, camera; +let mesh; + +init(); + +function init() { + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(0, 0, 1.5); + + new OrbitControls(camera, renderer.domElement); + + // Sky + + const canvas = document.createElement('canvas'); + canvas.width = 1; + canvas.height = 32; + + const context = canvas.getContext('2d'); + const gradient = context.createLinearGradient(0, 0, 0, 32); + gradient.addColorStop(0.0, '#014a84'); + gradient.addColorStop(0.5, '#0561a0'); + gradient.addColorStop(1.0, '#437ab6'); + context.fillStyle = gradient; + context.fillRect(0, 0, 1, 32); + + const skyMap = new THREE.CanvasTexture(canvas); + skyMap.colorSpace = THREE.SRGBColorSpace; + + const sky = new THREE.Mesh( + new THREE.SphereGeometry(10), + new THREE.MeshBasicMaterial({ map: skyMap, side: THREE.BackSide }), + ); + scene.add(sky); + + // Texture + + const size = 128; + const data = new Uint8Array(size * size * size); + + let i = 0; + const scale = 0.05; + const perlin = new ImprovedNoise(); + const vector = new THREE.Vector3(); + + for (let z = 0; z < size; z++) { + for (let y = 0; y < size; y++) { + for (let x = 0; x < size; x++) { + const d = + 1.0 - + vector + .set(x, y, z) + .subScalar(size / 2) + .divideScalar(size) + .length(); + data[i] = (128 + 128 * perlin.noise((x * scale) / 1.5, y * scale, (z * scale) / 1.5)) * d * d; + i++; + } + } + } + + const texture = new THREE.Data3DTexture(data, size, size, size); + texture.format = THREE.RedFormat; + texture.minFilter = THREE.LinearFilter; + texture.magFilter = THREE.LinearFilter; + texture.unpackAlignment = 1; + texture.needsUpdate = true; + + // Material + + const vertexShader = /* glsl */ ` + in vec3 position; + + uniform mat4 modelMatrix; + uniform mat4 modelViewMatrix; + uniform mat4 projectionMatrix; + uniform vec3 cameraPos; + + out vec3 vOrigin; + out vec3 vDirection; + + void main() { + vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); + + vOrigin = vec3( inverse( modelMatrix ) * vec4( cameraPos, 1.0 ) ).xyz; + vDirection = position - vOrigin; + + gl_Position = projectionMatrix * mvPosition; + } + `; + + const fragmentShader = /* glsl */ ` + precision highp float; + precision highp sampler3D; + + uniform mat4 modelViewMatrix; + uniform mat4 projectionMatrix; + + in vec3 vOrigin; + in vec3 vDirection; + + out vec4 color; + + uniform vec3 base; + uniform sampler3D map; + + uniform float threshold; + uniform float range; + uniform float opacity; + uniform float steps; + uniform float frame; + + uint wang_hash(uint seed) + { + seed = (seed ^ 61u) ^ (seed >> 16u); + seed *= 9u; + seed = seed ^ (seed >> 4u); + seed *= 0x27d4eb2du; + seed = seed ^ (seed >> 15u); + return seed; + } + + float randomFloat(inout uint seed) + { + return float(wang_hash(seed)) / 4294967296.; + } + + vec2 hitBox( vec3 orig, vec3 dir ) { + const vec3 box_min = vec3( - 0.5 ); + const vec3 box_max = vec3( 0.5 ); + vec3 inv_dir = 1.0 / dir; + vec3 tmin_tmp = ( box_min - orig ) * inv_dir; + vec3 tmax_tmp = ( box_max - orig ) * inv_dir; + vec3 tmin = min( tmin_tmp, tmax_tmp ); + vec3 tmax = max( tmin_tmp, tmax_tmp ); + float t0 = max( tmin.x, max( tmin.y, tmin.z ) ); + float t1 = min( tmax.x, min( tmax.y, tmax.z ) ); + return vec2( t0, t1 ); + } + + float sample1( vec3 p ) { + return texture( map, p ).r; + } + + float shading( vec3 coord ) { + float step = 0.01; + return sample1( coord + vec3( - step ) ) - sample1( coord + vec3( step ) ); + } + + vec4 linearToSRGB( in vec4 value ) { + return vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a ); + } + + void main(){ + vec3 rayDir = normalize( vDirection ); + vec2 bounds = hitBox( vOrigin, rayDir ); + + if ( bounds.x > bounds.y ) discard; + + bounds.x = max( bounds.x, 0.0 ); + + float stepSize = ( bounds.y - bounds.x ) / steps; + + // Jitter + + // Nice little seed from + // https://blog.demofox.org/2020/05/25/casual-shadertoy-path-tracing-1-basic-camera-diffuse-emissive/ + uint seed = uint( gl_FragCoord.x ) * uint( 1973 ) + uint( gl_FragCoord.y ) * uint( 9277 ) + uint( frame ) * uint( 26699 ); + vec3 size = vec3( textureSize( map, 0 ) ); + float randNum = randomFloat( seed ) * 2.0 - 1.0; + vec3 p = vOrigin + bounds.x * rayDir; + p += rayDir * randNum * ( 1.0 / size ); + + // + + vec4 ac = vec4( base, 0.0 ); + + for ( float i = 0.0; i < steps; i += 1.0 ) { + + float t = bounds.x + i * stepSize; + + float d = sample1( p + 0.5 ); + + d = smoothstep( threshold - range, threshold + range, d ) * opacity; + + float col = shading( p + 0.5 ) * 3.0 + ( ( p.x + p.y ) * 0.25 ) + 0.2; + + ac.rgb += ( 1.0 - ac.a ) * d * col; + + ac.a += ( 1.0 - ac.a ) * d; + + if ( ac.a >= 0.95 ) break; + + p += rayDir * stepSize; + + } + + color = linearToSRGB( ac ); + + if ( color.a == 0.0 ) discard; + + } + `; + + const geometry = new THREE.BoxGeometry(1, 1, 1); + const material = new THREE.RawShaderMaterial({ + glslVersion: THREE.GLSL3, + uniforms: { + base: { value: new THREE.Color(0x798aa0) }, + map: { value: texture }, + cameraPos: { value: new THREE.Vector3() }, + threshold: { value: 0.25 }, + opacity: { value: 0.25 }, + range: { value: 0.1 }, + steps: { value: 100 }, + frame: { value: 0 }, + }, + vertexShader, + fragmentShader, + side: THREE.BackSide, + transparent: true, + }); + + mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + // + + const parameters = { + threshold: 0.25, + opacity: 0.25, + range: 0.1, + steps: 100, + }; + + function update() { + material.uniforms.threshold.value = parameters.threshold; + material.uniforms.opacity.value = parameters.opacity; + material.uniforms.range.value = parameters.range; + material.uniforms.steps.value = parameters.steps; + } + + const gui = new GUI(); + gui.add(parameters, 'threshold', 0, 1, 0.01).onChange(update); + gui.add(parameters, 'opacity', 0, 1, 0.01).onChange(update); + gui.add(parameters, 'range', 0, 1, 0.01).onChange(update); + gui.add(parameters, 'steps', 0, 200, 1).onChange(update); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + mesh.material.uniforms.cameraPos.value.copy(camera.position); + mesh.rotation.y = -performance.now() / 7500; + + mesh.material.uniforms.frame.value++; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_volume_instancing.ts b/examples-testing/examples/webgl_volume_instancing.ts new file mode 100644 index 000000000..7045732d6 --- /dev/null +++ b/examples-testing/examples/webgl_volume_instancing.ts @@ -0,0 +1,222 @@ +import * as THREE from 'three'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { VOXLoader, buildData3DTexture } from 'three/addons/loaders/VOXLoader.js'; + +let renderer, scene, camera, controls, timer; + +init(); + +function init() { + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000); + camera.position.set(0, 0, 4); + + controls = new OrbitControls(camera, renderer.domElement); + controls.autoRotate = true; + controls.autoRotateSpeed = -1.0; + controls.enableDamping = true; + + timer = new THREE.Timer(); + timer.connect(document); + + // Material + + const vertexShader = /* glsl */ ` + in vec3 position; + in mat4 instanceMatrix; + + uniform mat4 modelMatrix; + uniform mat4 modelViewMatrix; + uniform mat4 projectionMatrix; + uniform vec3 cameraPos; + + out vec4 vScreenPosition; + out mat4 vInstanceToViewMatrix; + + void main() { + vec4 mvPosition = modelViewMatrix * instanceMatrix * vec4( position, 1.0 ); + + gl_Position = projectionMatrix * mvPosition; + vScreenPosition = vec4( gl_Position.xy, 0.0, gl_Position.w ); + vInstanceToViewMatrix = modelViewMatrix * instanceMatrix; + } + `; + + const fragmentShader = /* glsl */ ` + precision highp float; + precision highp sampler3D; + + uniform mat4 viewMatrix; + uniform mat4 modelViewMatrix; + uniform mat4 projectionMatrix; + + in vec4 vScreenPosition; + in mat4 vInstanceToViewMatrix; + + out vec4 color; + + uniform sampler3D map; + + vec2 hitBox( vec3 orig, vec3 dir ) { + const vec3 box_min = vec3( - 0.5 ); + const vec3 box_max = vec3( 0.5 ); + vec3 inv_dir = 1.0 / dir; + vec3 tmin_tmp = ( box_min - orig ) * inv_dir; + vec3 tmax_tmp = ( box_max - orig ) * inv_dir; + vec3 tmin = min( tmin_tmp, tmax_tmp ); + vec3 tmax = max( tmin_tmp, tmax_tmp ); + float t0 = max( tmin.x, max( tmin.y, tmin.z ) ); + float t1 = min( tmax.x, min( tmax.y, tmax.z ) ); + return vec2( t0, t1 ); + } + + float sample1( vec3 p ) { + return texture( map, p ).r; + } + + #define epsilon .0001 + + vec3 normal( vec3 coord ) { + if ( coord.x < epsilon ) return vec3( 1.0, 0.0, 0.0 ); + if ( coord.y < epsilon ) return vec3( 0.0, 1.0, 0.0 ); + if ( coord.z < epsilon ) return vec3( 0.0, 0.0, 1.0 ); + if ( coord.x > 1.0 - epsilon ) return vec3( - 1.0, 0.0, 0.0 ); + if ( coord.y > 1.0 - epsilon ) return vec3( 0.0, - 1.0, 0.0 ); + if ( coord.z > 1.0 - epsilon ) return vec3( 0.0, 0.0, - 1.0 ); + + float step = 0.01; + float x = sample1( coord + vec3( - step, 0.0, 0.0 ) ) - sample1( coord + vec3( step, 0.0, 0.0 ) ); + float y = sample1( coord + vec3( 0.0, - step, 0.0 ) ) - sample1( coord + vec3( 0.0, step, 0.0 ) ); + float z = sample1( coord + vec3( 0.0, 0.0, - step ) ) - sample1( coord + vec3( 0.0, 0.0, step ) ); + + return normalize( vec3( x, y, z ) ); + } + + void main() { + + // perform w divide in the fragment shader to avoid interpolation artifacts + vec2 screenUv = vScreenPosition.xy / vScreenPosition.w; + mat4 invProjectionMatrix = inverse( projectionMatrix ); + mat4 invInstanceToViewMatrix = inverse( vInstanceToViewMatrix ); + + // get camera ray + vec4 temp; + vec3 camRayOrigin, camRayEnd; + temp = invProjectionMatrix * vec4( screenUv, - 1.0, 1.0 ); + camRayOrigin = temp.xyz / temp.w; + + temp = invProjectionMatrix * vec4( screenUv, 1.0, 1.0 ); + camRayEnd = temp.xyz / temp.w; + + // get local ray + vec3 instRayOrigin, instRayDirection, instRayEnd; + instRayOrigin = ( invInstanceToViewMatrix * vec4( camRayOrigin, 1.0 ) ).xyz; + instRayEnd = ( invInstanceToViewMatrix * vec4( camRayEnd, 1.0 ) ).xyz; + instRayDirection = normalize( instRayEnd - instRayOrigin ); + + // calculate the start of the ray at the box edge + vec2 bounds = hitBox( instRayOrigin, instRayDirection ); + + if ( bounds.x > bounds.y ) discard; + + bounds.x = max( bounds.x, 0.0 ); + + float stepSize = ( bounds.y - bounds.x ) / 100.0; + + vec3 p; + + // march through the volume + for ( float i = 0.0; i < 100.0; i += 1.0 ) { + + float t = bounds.x + i * stepSize; + p = instRayOrigin + t * instRayDirection; + float d = sample1( p + 0.5 ); + + if ( d > 0.5 ) { + + color.rgb = p * 2.0; + color.a = 1.; + break; + + } + + } + + if ( color.a == 0.0 ) discard; + + // calculate the final point in the ndc coords + vec4 ndc = projectionMatrix * vInstanceToViewMatrix * vec4( p, 1.0 ); + ndc /= ndc.w; + + // map the ndc coordinate to depth + // https://stackoverflow.com/questions/10264949/glsl-gl-fragcoord-z-calculation-and-setting-gl-fragdepth + float far = gl_DepthRange.far; + float near = gl_DepthRange.near; + gl_FragDepth = ( ( ( far - near ) * ndc.z ) + near + far ) / 2.0; + + } + `; + + const loader = new VOXLoader(); + loader.load('models/vox/menger.vox', function (chunks) { + for (let i = 0; i < chunks.length; i++) { + const chunk = chunks[i]; + + const geometry = new THREE.BoxGeometry(1, 1, 1); + const material = new THREE.RawShaderMaterial({ + glslVersion: THREE.GLSL3, + uniforms: { + map: { value: buildData3DTexture(chunk) }, + cameraPos: { value: new THREE.Vector3() }, + }, + vertexShader, + fragmentShader, + side: THREE.BackSide, + }); + + const mesh = new THREE.InstancedMesh(geometry, material, 50000); + mesh.onBeforeRender = function () { + this.material.uniforms.cameraPos.value.copy(camera.position); + }; + + const transform = new THREE.Object3D(); + + for (let i = 0; i < mesh.count; i++) { + transform.position.random().subScalar(0.5).multiplyScalar(150); + transform.rotation.x = Math.random() * Math.PI; + transform.rotation.y = Math.random() * Math.PI; + transform.rotation.z = Math.random() * Math.PI; + transform.updateMatrix(); + + mesh.setMatrixAt(i, transform.matrix); + } + + scene.add(mesh); + } + }); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + const delta = timer.getDelta(); + controls.update(delta); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_volume_perlin.ts b/examples-testing/examples/webgl_volume_perlin.ts new file mode 100644 index 000000000..0f299f66f --- /dev/null +++ b/examples-testing/examples/webgl_volume_perlin.ts @@ -0,0 +1,205 @@ +import * as THREE from 'three'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { ImprovedNoise } from 'three/addons/math/ImprovedNoise.js'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let renderer, scene, camera; +let mesh; + +init(); + +function init() { + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(0, 0, 2); + + new OrbitControls(camera, renderer.domElement); + + // Texture + + const size = 128; + const data = new Uint8Array(size * size * size); + + let i = 0; + const perlin = new ImprovedNoise(); + const vector = new THREE.Vector3(); + + for (let z = 0; z < size; z++) { + for (let y = 0; y < size; y++) { + for (let x = 0; x < size; x++) { + vector.set(x, y, z).divideScalar(size); + + const d = perlin.noise(vector.x * 6.5, vector.y * 6.5, vector.z * 6.5); + + data[i++] = d * 128 + 128; + } + } + } + + const texture = new THREE.Data3DTexture(data, size, size, size); + texture.format = THREE.RedFormat; + texture.minFilter = THREE.LinearFilter; + texture.magFilter = THREE.LinearFilter; + texture.unpackAlignment = 1; + texture.needsUpdate = true; + + // Material + + const vertexShader = /* glsl */ ` + in vec3 position; + + uniform mat4 modelMatrix; + uniform mat4 modelViewMatrix; + uniform mat4 projectionMatrix; + uniform vec3 cameraPos; + + out vec3 vOrigin; + out vec3 vDirection; + + void main() { + vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); + + vOrigin = vec3( inverse( modelMatrix ) * vec4( cameraPos, 1.0 ) ).xyz; + vDirection = position - vOrigin; + + gl_Position = projectionMatrix * mvPosition; + } + `; + + const fragmentShader = /* glsl */ ` + precision highp float; + precision highp sampler3D; + + uniform mat4 modelViewMatrix; + uniform mat4 projectionMatrix; + + in vec3 vOrigin; + in vec3 vDirection; + + out vec4 color; + + uniform sampler3D map; + + uniform float threshold; + uniform float steps; + + vec2 hitBox( vec3 orig, vec3 dir ) { + const vec3 box_min = vec3( - 0.5 ); + const vec3 box_max = vec3( 0.5 ); + vec3 inv_dir = 1.0 / dir; + vec3 tmin_tmp = ( box_min - orig ) * inv_dir; + vec3 tmax_tmp = ( box_max - orig ) * inv_dir; + vec3 tmin = min( tmin_tmp, tmax_tmp ); + vec3 tmax = max( tmin_tmp, tmax_tmp ); + float t0 = max( tmin.x, max( tmin.y, tmin.z ) ); + float t1 = min( tmax.x, min( tmax.y, tmax.z ) ); + return vec2( t0, t1 ); + } + + float sample1( vec3 p ) { + return texture( map, p ).r; + } + + #define epsilon .0001 + + vec3 normal( vec3 coord ) { + if ( coord.x < epsilon ) return vec3( 1.0, 0.0, 0.0 ); + if ( coord.y < epsilon ) return vec3( 0.0, 1.0, 0.0 ); + if ( coord.z < epsilon ) return vec3( 0.0, 0.0, 1.0 ); + if ( coord.x > 1.0 - epsilon ) return vec3( - 1.0, 0.0, 0.0 ); + if ( coord.y > 1.0 - epsilon ) return vec3( 0.0, - 1.0, 0.0 ); + if ( coord.z > 1.0 - epsilon ) return vec3( 0.0, 0.0, - 1.0 ); + + float step = 0.01; + float x = sample1( coord + vec3( - step, 0.0, 0.0 ) ) - sample1( coord + vec3( step, 0.0, 0.0 ) ); + float y = sample1( coord + vec3( 0.0, - step, 0.0 ) ) - sample1( coord + vec3( 0.0, step, 0.0 ) ); + float z = sample1( coord + vec3( 0.0, 0.0, - step ) ) - sample1( coord + vec3( 0.0, 0.0, step ) ); + + return normalize( vec3( x, y, z ) ); + } + + void main(){ + + vec3 rayDir = normalize( vDirection ); + vec2 bounds = hitBox( vOrigin, rayDir ); + + if ( bounds.x > bounds.y ) discard; + + bounds.x = max( bounds.x, 0.0 ); + + float stepSize = ( bounds.y - bounds.x ) / steps; + + for ( float i = 0.0; i < steps; i += 1.0 ) { + + float t = bounds.x + i * stepSize; + vec3 p = vOrigin + t * rayDir; + float d = sample1( p + 0.5 ); + + if ( d > threshold ) { + + color.rgb = normal( p + 0.5 ) * 0.5 + ( p * 1.5 + 0.25 ); + color.a = 1.; + break; + + } + + } + + if ( color.a == 0.0 ) discard; + + } + `; + + const geometry = new THREE.BoxGeometry(1, 1, 1); + const material = new THREE.RawShaderMaterial({ + glslVersion: THREE.GLSL3, + uniforms: { + map: { value: texture }, + cameraPos: { value: new THREE.Vector3() }, + threshold: { value: 0.6 }, + steps: { value: 200 }, + }, + vertexShader, + fragmentShader, + side: THREE.BackSide, + }); + + mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + // + + const parameters = { threshold: 0.6, steps: 200 }; + + function update() { + material.uniforms.threshold.value = parameters.threshold; + material.uniforms.steps.value = parameters.steps; + } + + const gui = new GUI(); + gui.add(parameters, 'threshold', 0, 1, 0.01).onChange(update); + gui.add(parameters, 'steps', 0, 300, 1).onChange(update); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + mesh.material.uniforms.cameraPos.value.copy(camera.position); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgl_watch.ts b/examples-testing/examples/webgl_watch.ts new file mode 100644 index 000000000..8c7b7ecb6 --- /dev/null +++ b/examples-testing/examples/webgl_watch.ts @@ -0,0 +1,243 @@ +import * as THREE from 'three'; +import * as TWEEN from 'tween'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; +import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js'; +import { TAARenderPass } from 'three/addons/postprocessing/TAARenderPass.js'; + +let camera, scene, renderer; +let gui, dirLight, pointLight, controls, bloomPass, taaPass; +let ready = false; + +const meshes = {}; +const materials = {}; +const torad = Math.PI / 180; + +const setting = { + roughness: 0.1, + metalness: 1.0, + opacity: 0.8, + threshold: 0, + strength: 0.007, + radius: 0.0, + postProcess: false, +}; + +init(); + +function init() { + const container = document.getElementById('container'); + + camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight, 0.1, 20); + camera.position.set(0.8, 0.5, -1.5); + + scene = new THREE.Scene(); + + renderer = new THREE.WebGLRenderer({ antialias: true, outputBufferType: THREE.HalfFloatType }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.toneMapping = THREE.NeutralToneMapping; + renderer.toneMappingExposure = 0.7; + renderer.shadowMap.enabled = true; + container.appendChild(renderer.domElement); + + taaPass = new TAARenderPass(scene, camera); + taaPass.sampleLevel = 2; + + bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85); + bloomPass.threshold = setting.threshold; + bloomPass.strength = setting.strength; + bloomPass.radius = setting.radius; + + new HDRLoader().setPath('textures/equirectangular/').load('lobe.hdr', function (texture) { + texture.mapping = THREE.EquirectangularReflectionMapping; + scene.background = texture; + scene.environment = texture; + scene.backgroundBlurriness = 0.5; + scene.backgroundIntensity = 1.0; + scene.environmentIntensity = 1.5; + + // model + + const loader = new GLTFLoader().setPath('models/gltf/'); + loader.setDRACOLoader(new DRACOLoader().setDecoderPath('jsm/libs/draco/gltf/')); + loader.load('rolex.glb', function (gltf) { + gltf.scene.rotation.x = Math.PI * 0.25; + + gltf.scene.traverse(child => { + if (child.isMesh || child.isGroup) { + if (child.isMesh) { + child.material.vertexColors = false; + if (materials[child.material.name]) child.material = materials[child.material.name]; + else materials[child.material.name] = child.material; + if (child.name !== 'glass' && child.name !== 'floor') { + child.receiveShadow = true; + child.castShadow = true; + } + } + + meshes[child.name] = child; + } + }); + + scene.add(gltf.scene); + + meshes.glass.material = new THREE.MeshPhysicalMaterial({ + color: 0x020205, + transparent: true, + opacity: setting.opacity, + metalness: 0, + roughness: 0, + iridescence: 0.3, + clearcoat: 1.0, + blending: THREE.AdditiveBlending, + }); + + ready = true; + + createGUI(); + }); + }); + + controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 0.3; + controls.maxDistance = 10; + controls.target.set(0, -0.1, 0); + controls.enableDamping = true; + controls.dampingFactor = 0.05; + controls.update(); + + dirLight = new THREE.DirectionalLight(0xffffff, 6); + dirLight.position.set(0.2, 0.6, 0.4); + dirLight.castShadow = true; + scene.add(dirLight); + const shadow = dirLight.shadow; + shadow.mapSize.width = shadow.mapSize.height = 2048; + shadow.radius = 8; + shadow.bias = -0.0005; + const shadowCam = shadow.camera, + s = 0.5; + shadowCam.near = 0.1; + shadowCam.far = 2; + shadowCam.right = shadowCam.top = s; + shadowCam.left = shadowCam.bottom = -s; + // debug shadow + //scene.add( new THREE.CameraHelper(shadowCam) ); + + pointLight = new THREE.PointLight(0x7b8cad, 1, 0, 2); + pointLight.position.set(-0.3, -0.2, -0.2); + scene.add(pointLight); + + window.addEventListener('resize', onWindowResize); + + moveCamera(); +} + +function moveCamera() { + controls.enabled = false; + controls.enableDamping = false; + + const sph = new THREE.Spherical(); + const target = controls.target; + const tmp = { + distance: controls.getDistance(), + phi: controls.getPolarAngle(), + theta: controls.getAzimuthalAngle(), + }; + + new TWEEN.Tween(tmp) + .to({ distance: 1, theta: -Math.PI * 0.2 }, 6000) + .easing(TWEEN.Easing.Quadratic.Out) + .onUpdate(function (n) { + sph.set(n.distance, n.phi, n.theta); + camera.position.setFromSpherical(sph).add(target); + camera.lookAt(target); + }) + .onComplete(function () { + controls.enabled = true; + controls.enableDamping = true; + }) + .start(); +} + +function postProcess(b) { + if (b) { + renderer.setEffects([taaPass, bloomPass]); + } else { + renderer.setEffects(null); + } +} + +function createGUI() { + gui = new GUI(); + gui.add(setting, 'roughness', 0, 1, 0.01).onChange(upMaterial); + gui.add(setting, 'metalness', 0, 1, 0.01).onChange(upMaterial); + gui.add(setting, 'opacity', 0, 1, 0.01).onChange(upMaterial); + + // + + gui.add(setting, 'postProcess').onChange(postProcess); + gui.add(setting, 'threshold', 0, 1, 0.01).onChange(upBloom); + gui.add(setting, 'strength', 0, 0.1, 0.001).onChange(upBloom); + gui.add(setting, 'radius', 0, 1, 0.01).onChange(upBloom); +} + +function upMaterial() { + materials.Gold.metalness = materials.Silver.metalness = setting.metalness; + materials.Gold.roughness = materials.Silver.roughness = setting.roughness; + meshes.glass.material.opacity = setting.opacity; +} + +function upBloom() { + if (!bloomPass) return; + bloomPass.threshold = setting.threshold; + bloomPass.strength = setting.strength; + bloomPass.radius = setting.radius; +} + +function getTime() { + const currentDate = new Date(); + let hour = currentDate.getHours(); + const minute = currentDate.getMinutes(); + const second = currentDate.getSeconds(); + let day = currentDate.getDay(); + const month = currentDate.getMonth(); + const milli = currentDate.getMilliseconds(); + if (hour >= 12) hour -= 12; + if (day > 30) day = 30; + + meshes.hour.rotation.y = -hour * 30 * torad; + meshes.minute.rotation.y = -minute * 6 * torad; + meshes.second.rotation.y = -second * 6 * torad; + meshes.mini_03.rotation.y = -day * 12 * torad; + meshes.mini_02.rotation.y = -month * 30 * torad; + meshes.mini_01.rotation.y = -milli * 0.36 * torad; +} + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + renderer.setSize(width, height); +} + +// + +function animate() { + controls.update(); + + TWEEN.update(); + + renderer.render(scene, camera); + + if (ready) getTime(); +} diff --git a/examples-testing/examples/webgpu_animation_retargeting.ts b/examples-testing/examples/webgpu_animation_retargeting.ts new file mode 100644 index 000000000..23ebb2af4 --- /dev/null +++ b/examples-testing/examples/webgpu_animation_retargeting.ts @@ -0,0 +1,295 @@ +import * as THREE from 'three/webgpu'; +import { + color, + screenUV, + hue, + reflector, + time, + Fn, + vec2, + length, + atan, + float, + sin, + cos, + vec3, + sub, + mul, + pow, + blendDodge, + normalWorldGeometry, +} from 'three/tsl'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import * as SkeletonUtils from 'three/addons/utils/SkeletonUtils.js'; + +const [sourceModel, targetModel] = await Promise.all([ + new Promise((resolve, reject) => { + new GLTFLoader().load('./models/gltf/Michelle.glb', resolve, undefined, reject); + }), + + new Promise((resolve, reject) => { + new GLTFLoader().load('./models/gltf/Soldier.glb', resolve, undefined, reject); + }), +]); + +// + +const timer = new THREE.Timer(); +timer.connect(document); + +export const lightSpeed = /*#__PURE__*/ Fn(([suv_immutable]) => { + // forked from https://www.shadertoy.com/view/7ly3D1 + + const suv = vec2(suv_immutable); + const uv = vec2(length(suv), atan(suv.y, suv.x)); + const offset = float( + float(0.1) + .mul(sin(uv.y.mul(10).sub(time.mul(0.6)))) + .mul(cos(uv.y.mul(48).add(time.mul(0.3)))) + .mul(cos(uv.y.mul(3.7).add(time))), + ); + const rays = vec3( + vec3(sin(uv.y.mul(150).add(time)).mul(0.5).add(0.5)) + .mul( + vec3( + sin(uv.y.mul(80).sub(time.mul(0.6))) + .mul(0.5) + .add(0.5), + ), + ) + .mul( + vec3( + sin(uv.y.mul(45).add(time.mul(0.8))) + .mul(0.5) + .add(0.5), + ), + ) + .mul(vec3(sub(1, cos(uv.y.add(mul(22, time).sub(pow(uv.x.add(offset), 0.3).mul(60))))))) + .mul(vec3(uv.x.mul(2))), + ); + + return rays; +}).setLayout({ + name: 'lightSpeed', + type: 'vec3', + inputs: [{ name: 'suv', type: 'vec2' }], +}); + +// scene + +const scene = new THREE.Scene(); + +// background + +const coloredVignette = screenUV + .distance(0.5) + .mix(hue(color(0x0175ad), time.mul(0.1)), hue(color(0x02274f), time.mul(0.5))); +const lightSpeedEffect = lightSpeed(normalWorldGeometry).clamp(); +const lightSpeedSky = normalWorldGeometry.y.remapClamp(-0.1, 1).mix(0, lightSpeedEffect); +const composedBackground = blendDodge(coloredVignette, lightSpeedSky); + +scene.backgroundNode = composedBackground; + +// + +const helpers = new THREE.Group(); +helpers.visible = false; +scene.add(helpers); + +const light = new THREE.HemisphereLight(0xe9c0a5, 0x0175ad, 5); +scene.add(light); + +const dirLight = new THREE.DirectionalLight(0xfff9ea, 4); +dirLight.position.set(2, 5, 2); +scene.add(dirLight); + +const camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.25, 50); +camera.position.set(0, 1, 4); + +// add models to scene +scene.add(sourceModel.scene); +scene.add(targetModel.scene); + +// reposition models +sourceModel.scene.position.x -= 0.8; +targetModel.scene.position.x += 0.7; + +targetModel.scene.position.z -= 0.1; + +// reajust model +targetModel.scene.scale.setScalar(0.01); + +// flip model +sourceModel.scene.rotation.y = Math.PI / 2; +targetModel.scene.rotation.y = -Math.PI / 2; + +// retarget +const source = getSource(sourceModel); +const mixer = retargetModel(source, targetModel); + +// floor +const reflection = reflector(); +reflection.target.rotateX(-Math.PI / 2); +scene.add(reflection.target); + +const floorMaterial = new THREE.NodeMaterial(); +floorMaterial.colorNode = reflection; +floorMaterial.opacity = 0.2; +floorMaterial.transparent = true; + +const floor = new THREE.Mesh(new THREE.BoxGeometry(50, 0.001, 50), floorMaterial); +floor.receiveShadow = true; + +floor.position.set(0, 0, 0); +scene.add(floor); + +// renderer +const renderer = new THREE.WebGPURenderer({ antialias: true }); +renderer.setPixelRatio(window.devicePixelRatio); +renderer.setSize(window.innerWidth, window.innerHeight); +renderer.setAnimationLoop(animate); +renderer.toneMapping = THREE.NeutralToneMapping; +renderer.inspector = new Inspector(); +document.body.appendChild(renderer.domElement); + +const controls = new OrbitControls(camera, renderer.domElement); +controls.minDistance = 3; +controls.maxDistance = 12; +controls.target.set(0, 1, 0); +controls.maxPolarAngle = Math.PI / 2; + +const gui = renderer.inspector.createParameters('Scene settings'); +gui.add(helpers, 'visible').name('show helpers'); + +// + +function getSource(sourceModel) { + const clip = sourceModel.animations[0]; + + const helper = new THREE.SkeletonHelper(sourceModel.scene); + helpers.add(helper); + + const skeleton = new THREE.Skeleton(helper.bones); + + const mixer = new THREE.AnimationMixer(sourceModel.scene); + mixer.clipAction(sourceModel.animations[0]).play(); + + return { clip, skeleton, mixer }; +} + +function retargetModel(sourceModel, targetModel) { + const targetSkin = targetModel.scene.children[0].children[0]; + + const targetSkelHelper = new THREE.SkeletonHelper(targetModel.scene); + helpers.add(targetSkelHelper); + + const rotateCW45 = new THREE.Matrix4().makeRotationY(THREE.MathUtils.degToRad(45)); + const rotateCCW180 = new THREE.Matrix4().makeRotationY(THREE.MathUtils.degToRad(-180)); + const rotateCW180 = new THREE.Matrix4().makeRotationY(THREE.MathUtils.degToRad(180)); + const rotateFoot = new THREE.Matrix4().makeRotationFromEuler( + new THREE.Euler(THREE.MathUtils.degToRad(45), THREE.MathUtils.degToRad(180), THREE.MathUtils.degToRad(0)), + ); + + const retargetOptions = { + // specify the name of the source's hip bone. + hip: 'mixamorigHips', + + // specify the influence of the source's hip bone. + // use ( 0, 1, 0 ) to ignore xz hip movement. + //hipInfluence: new THREE.Vector3( 0, 1, 0 ), + + // specify an animation range in seconds. + //trim: [ 3.0, 4.0 ], + + // preserve the scale of the target model + scale: 1 / targetModel.scene.scale.y, + + // offset target bones -> { targetBoneName: offsetMatrix } + localOffsets: { + mixamorigLeftShoulder: rotateCW45, + mixamorigRightShoulder: rotateCCW180, + mixamorigLeftArm: rotateCW45, + mixamorigRightArm: rotateCCW180, + mixamorigLeftForeArm: rotateCW45, + mixamorigRightForeArm: rotateCCW180, + mixamorigLeftHand: rotateCW45, + mixamorigRightHand: rotateCCW180, + + mixamorigLeftUpLeg: rotateCW180, + mixamorigRightUpLeg: rotateCW180, + mixamorigLeftLeg: rotateCW180, + mixamorigRightLeg: rotateCW180, + mixamorigLeftFoot: rotateFoot, + mixamorigRightFoot: rotateFoot, + mixamorigLeftToeBase: rotateCW180, + mixamorigRightToeBase: rotateCW180, + }, + + // Map of target's bone names to source's bone names -> { targetBoneName: sourceBoneName } + names: { + mixamorigHips: 'mixamorigHips', + + mixamorigSpine: 'mixamorigSpine', + mixamorigSpine2: 'mixamorigSpine2', + mixamorigHead: 'mixamorigHead', + + mixamorigLeftShoulder: 'mixamorigLeftShoulder', + mixamorigRightShoulder: 'mixamorigRightShoulder', + mixamorigLeftArm: 'mixamorigLeftArm', + mixamorigRightArm: 'mixamorigRightArm', + mixamorigLeftForeArm: 'mixamorigLeftForeArm', + mixamorigRightForeArm: 'mixamorigRightForeArm', + mixamorigLeftHand: 'mixamorigLeftHand', + mixamorigRightHand: 'mixamorigRightHand', + + mixamorigLeftUpLeg: 'mixamorigLeftUpLeg', + mixamorigRightUpLeg: 'mixamorigRightUpLeg', + mixamorigLeftLeg: 'mixamorigLeftLeg', + mixamorigRightLeg: 'mixamorigRightLeg', + mixamorigLeftFoot: 'mixamorigLeftFoot', + mixamorigRightFoot: 'mixamorigRightFoot', + mixamorigLeftToeBase: 'mixamorigLeftToeBase', + mixamorigRightToeBase: 'mixamorigRightToeBase', + }, + }; + + const retargetedClip = SkeletonUtils.retargetClip( + targetSkin, + sourceModel.skeleton, + sourceModel.clip, + retargetOptions, + ); + + // Apply the mixer directly to the SkinnedMesh, not any + // ancestor node, because that's what + // SkeletonUtils.retargetClip outputs the clip to be + // compatible with. + const mixer = new THREE.AnimationMixer(targetSkin); + mixer.clipAction(retargetedClip).play(); + + return mixer; +} + +window.onresize = function () { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +}; + +function animate() { + timer.update(); + + const delta = timer.getDelta(); + + source.mixer.update(delta); + mixer.update(delta); + + controls.update(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_animation_retargeting_readyplayer.ts b/examples-testing/examples/webgpu_animation_retargeting_readyplayer.ts new file mode 100644 index 000000000..de90d890f --- /dev/null +++ b/examples-testing/examples/webgpu_animation_retargeting_readyplayer.ts @@ -0,0 +1,165 @@ +import * as THREE from 'three/webgpu'; +import { screenUV, color, vec2, vec4, reflector, positionWorld } from 'three/tsl'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { FBXLoader } from 'three/addons/loaders/FBXLoader.js'; +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import * as SkeletonUtils from 'three/addons/utils/SkeletonUtils.js'; + +const [sourceModel, targetModel] = await Promise.all([ + new Promise((resolve, reject) => { + new FBXLoader().load('./models/fbx/mixamo.fbx', resolve, undefined, reject); + }), + + new Promise((resolve, reject) => { + new GLTFLoader().load('./models/gltf/readyplayer.me.glb', resolve, undefined, reject); + }), +]); + +// + +const timer = new THREE.Timer(); +timer.connect(document); + +// scene + +const scene = new THREE.Scene(); + +// background + +const horizontalEffect = screenUV.x.mix(color(0x13172b), color(0x311649)); +const lightEffect = screenUV.distance(vec2(0.5, 1.0)).oneMinus().mul(color(0x0c5d68)); + +scene.backgroundNode = horizontalEffect.add(lightEffect); + +// + +const light = new THREE.HemisphereLight(0x311649, 0x0c5d68, 10); +scene.add(light); + +const backLight = new THREE.DirectionalLight(0xffffff, 10); +backLight.position.set(0, 5, -5); +scene.add(backLight); + +const keyLight = new THREE.DirectionalLight(0xfff9ea, 4); +keyLight.position.set(3, 5, 3); +scene.add(keyLight); + +const camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.25, 50); +camera.position.set(0, 3, 5); + +// add models to scene +scene.add(sourceModel); +scene.add(targetModel.scene); + +// reposition models +sourceModel.position.x -= 0.9; +targetModel.scene.position.x += 0.9; + +// reajust model - mixamo use centimeters, readyplayer.me use meters (three.js scale is meters) +sourceModel.scale.setScalar(0.01); + +// retarget +const source = getSource(sourceModel); +const mixer = retargetModel(source, targetModel); + +// renderer +const renderer = new THREE.WebGPURenderer({ antialias: true }); +renderer.setPixelRatio(window.devicePixelRatio); +renderer.setSize(window.innerWidth, window.innerHeight); +renderer.setAnimationLoop(animate); +renderer.inspector = new Inspector(); +renderer.toneMapping = THREE.NeutralToneMapping; +document.body.appendChild(renderer.domElement); + +const controls = new OrbitControls(camera, renderer.domElement); +controls.minDistance = 3; +controls.maxDistance = 12; +controls.target.set(0, 1, 0); +controls.maxPolarAngle = Math.PI / 2; + +// floor +const reflection = reflector(); +reflection.target.rotateX(-Math.PI / 2); +scene.add(reflection.target); + +const reflectionMask = positionWorld.xz.distance(0).mul(0.1).clamp().oneMinus(); + +const floorMaterial = new THREE.NodeMaterial(); +floorMaterial.colorNode = vec4(reflection.rgb, reflectionMask); +floorMaterial.opacity = 0.2; +floorMaterial.transparent = true; + +const floor = new THREE.Mesh(new THREE.BoxGeometry(50, 0.001, 50), floorMaterial); +floor.receiveShadow = true; + +floor.position.set(0, 0, 0); +scene.add(floor); + +// + +function getSource(sourceModel) { + const clip = sourceModel.animations[0]; + + const helper = new THREE.SkeletonHelper(sourceModel); + const skeleton = new THREE.Skeleton(helper.bones); + + const mixer = new THREE.AnimationMixer(sourceModel); + mixer.clipAction(sourceModel.animations[0]).play(); + + return { clip, skeleton, mixer }; +} + +function retargetModel(sourceModel, targetModel) { + const targetSkin = targetModel.scene.children[0].children[1]; + + const retargetOptions = { + // specify the name of the source's hip bone. + hip: 'mixamorigHips', + + // preserve the scale of the target model + scale: 0.01, + + // use ( 0, 1, 0 ) to ignore xz hip movement. + //hipInfluence: new THREE.Vector3( 0, 1, 0 ), + + // Map of target's bone names to source's bone names -> { targetBoneName: sourceBoneName } + getBoneName: function (bone) { + return 'mixamorig' + bone.name; + }, + }; + + const retargetedClip = SkeletonUtils.retargetClip( + targetSkin, + sourceModel.skeleton, + sourceModel.clip, + retargetOptions, + ); + + const mixer = new THREE.AnimationMixer(targetSkin); + mixer.clipAction(retargetedClip).play(); + + return mixer; +} + +window.onresize = function () { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +}; + +function animate() { + timer.update(); + + const delta = timer.getDelta(); + + source.mixer.update(delta); + mixer.update(delta); + + controls.update(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_backdrop.ts b/examples-testing/examples/webgpu_backdrop.ts new file mode 100644 index 000000000..af6dcc399 --- /dev/null +++ b/examples-testing/examples/webgpu_backdrop.ts @@ -0,0 +1,134 @@ +import * as THREE from 'three/webgpu'; +import { + float, + vec3, + color, + viewportSharedTexture, + hue, + blendOverlay, + posterize, + grayscale, + saturation, + viewportSafeUV, + screenUV, + checker, + uv, + time, + oscSine, + output, +} from 'three/tsl'; + +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let camera, scene, renderer; +let portals, + rotate = true; +let mixer, timer; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.01, 100); + camera.position.set(1, 2, 3); + + scene = new THREE.Scene(); + scene.backgroundNode = screenUV.y.mix(color(0x66bbff), color(0x4466ff)); + camera.lookAt(0, 1, 0); + + timer = new THREE.Timer(); + timer.connect(document); + + // lights + + const light = new THREE.SpotLight(0xffffff, 1); + light.power = 2000; + camera.add(light); + scene.add(camera); + + const loader = new GLTFLoader(); + loader.load('models/gltf/Michelle.glb', function (gltf) { + const object = gltf.scene; + mixer = new THREE.AnimationMixer(object); + + const material = object.children[0].children[0].material; + material.outputNode = oscSine(time.mul(0.1)).mix(output, posterize(output.add(0.1), 4).mul(2)); + + const action = mixer.clipAction(gltf.animations[0]); + action.play(); + + scene.add(object); + }); + + // portals + + const geometry = new THREE.SphereGeometry(0.3, 32, 16); + + portals = new THREE.Group(); + scene.add(portals); + + function addBackdropSphere(backdropNode, backdropAlphaNode = null) { + const distance = 1; + const id = portals.children.length; + const rotation = THREE.MathUtils.degToRad(id * 45); + + const material = new THREE.MeshStandardNodeMaterial({ color: 0x0066ff }); + material.roughnessNode = float(0.2); + material.metalnessNode = float(0); + material.backdropNode = backdropNode; + material.backdropAlphaNode = backdropAlphaNode; + material.transparent = true; + + const mesh = new THREE.Mesh(geometry, material); + mesh.position.set(Math.cos(rotation) * distance, 1, Math.sin(rotation) * distance); + + portals.add(mesh); + } + + addBackdropSphere(hue(viewportSharedTexture().bgr, oscSine().mul(Math.PI))); + addBackdropSphere(viewportSharedTexture().rgb.oneMinus()); + addBackdropSphere(grayscale(viewportSharedTexture().rgb)); + addBackdropSphere(saturation(viewportSharedTexture().rgb, 10), oscSine()); + addBackdropSphere(blendOverlay(viewportSharedTexture().rgb, checker(uv().mul(10)))); + addBackdropSphere(viewportSharedTexture(viewportSafeUV(screenUV.mul(40).floor().div(40)))); + addBackdropSphere(viewportSharedTexture(viewportSafeUV(screenUV.mul(80).floor().div(80))).add(color(0x0033ff))); + addBackdropSphere(vec3(0, 0, viewportSharedTexture().b)); + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: false }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.toneMapping = THREE.NeutralToneMapping; + renderer.toneMappingExposure = 0.3; + document.body.appendChild(renderer.domElement); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, 1, 0); + controls.addEventListener('start', () => (rotate = false)); + controls.addEventListener('end', () => (rotate = true)); + controls.update(); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + const delta = timer.getDelta(); + + if (mixer) mixer.update(delta); + + if (rotate) portals.rotation.y += delta * 0.5; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_backdrop_area.ts b/examples-testing/examples/webgpu_backdrop_area.ts new file mode 100644 index 000000000..384311dd4 --- /dev/null +++ b/examples-testing/examples/webgpu_backdrop_area.ts @@ -0,0 +1,157 @@ +import * as THREE from 'three/webgpu'; +import { + color, + positionWorld, + linearDepth, + viewportLinearDepth, + viewportSharedTexture, + screenUV, + hue, + time, + checker, + uv, + modelScale, +} from 'three/tsl'; +import { hashBlur } from 'three/addons/tsl/display/hashBlur.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let camera, scene, renderer; +let mixer, timer; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.25, 25); + camera.position.set(3, 2, 3); + + scene = new THREE.Scene(); + scene.backgroundNode = hue(screenUV.y.mix(color(0x66bbff), color(0x4466ff)), time.mul(0.1)); + camera.lookAt(0, 1, 0); + + timer = new THREE.Timer(); + timer.connect(document); + + const ambient = new THREE.AmbientLight(0xffffff, 2.5); + scene.add(ambient); + + // model + + const loader = new GLTFLoader(); + loader.load('models/gltf/Michelle.glb', function (gltf) { + const object = gltf.scene; + mixer = new THREE.AnimationMixer(object); + + const action = mixer.clipAction(gltf.animations[0]); + action.play(); + + scene.add(object); + }); + + // volume + + // compare depth from viewportLinearDepth with linearDepth() to create a distance field + // viewportLinearDepth return the linear depth of the scene + // linearDepth() returns the linear depth of the mesh + const depthDistance = viewportLinearDepth.distance(linearDepth()); + + const depthAlphaNode = depthDistance.oneMinus().smoothstep(0.9, 2).mul(10).saturate(); + const depthBlurred = hashBlur(viewportSharedTexture(), depthDistance.smoothstep(0, 0.6).mul(40).clamp().mul(0.1)); + + const blurredBlur = new THREE.MeshBasicNodeMaterial(); + blurredBlur.backdropNode = depthBlurred.add(depthAlphaNode.mix(color(0x003399).mul(0.3), 0)); + blurredBlur.transparent = true; + blurredBlur.side = THREE.DoubleSide; + + const depthMaterial = new THREE.MeshBasicNodeMaterial(); + depthMaterial.backdropNode = depthAlphaNode; + depthMaterial.transparent = true; + depthMaterial.side = THREE.DoubleSide; + + const checkerMaterial = new THREE.MeshBasicNodeMaterial(); + checkerMaterial.backdropNode = hashBlur(viewportSharedTexture(), 0.05); + checkerMaterial.backdropAlphaNode = checker(uv().mul(3).mul(modelScale.xy)); + checkerMaterial.opacityNode = checkerMaterial.backdropAlphaNode; + checkerMaterial.transparent = true; + checkerMaterial.side = THREE.DoubleSide; + + const pixelMaterial = new THREE.MeshBasicNodeMaterial(); + pixelMaterial.backdropNode = viewportSharedTexture(screenUV.mul(100).floor().div(100)); + pixelMaterial.transparent = true; + + // box / floor + + const box = new THREE.Mesh(new THREE.BoxGeometry(2, 2, 2), blurredBlur); + box.position.set(0, 1, 0); + box.renderOrder = 1; + scene.add(box); + + const floor = new THREE.Mesh( + new THREE.BoxGeometry(5, 0.01, 5), + new THREE.MeshBasicNodeMaterial({ + color: 0xff6600, + opacityNode: positionWorld.xz.distance(0).oneMinus().clamp(), + transparent: true, + depthWrite: false, + }), + ); + floor.position.set(0, 0, 0); + scene.add(floor); + + // renderer + + renderer = new THREE.WebGPURenderer(/*{ antialias: true }*/); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.toneMapping = THREE.NeutralToneMapping; + renderer.toneMappingExposure = 0.9; + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, 1, 0); + controls.update(); + + window.addEventListener('resize', onWindowResize); + + // gui + + const materials = { + blurred: blurredBlur, + depth: depthMaterial, + checker: checkerMaterial, + pixel: pixelMaterial, + }; + + const options = { material: 'blurred' }; + box.material = materials[options.material]; + + const gui = renderer.inspector.createParameters('Scene settings'); + gui.add(box.scale, 'x', 0.1, 2, 0.01).name('box scale x'); + gui.add(box.scale, 'y', 0.1, 2, 0.01).name('box scale y'); + gui.add(options, 'material', Object.keys(materials)).onChange(name => { + box.material = materials[name]; + }); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + const delta = timer.getDelta(); + + if (mixer) mixer.update(delta); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_backdrop_water.ts b/examples-testing/examples/webgpu_backdrop_water.ts new file mode 100644 index 000000000..a8538cf94 --- /dev/null +++ b/examples-testing/examples/webgpu_backdrop_water.ts @@ -0,0 +1,247 @@ +import * as THREE from 'three/webgpu'; +import { + color, + vec2, + pass, + linearDepth, + normalWorld, + triplanarTexture, + texture, + objectPosition, + screenUV, + viewportLinearDepth, + viewportDepthTexture, + viewportSharedTexture, + mx_worley_noise_float, + positionWorld, + time, +} from 'three/tsl'; +import { gaussianBlur } from 'three/addons/tsl/display/GaussianBlurNode.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let camera, scene, renderer; +let mixer, objects, timer; +let model, floor, floorPosition; +let renderPipeline; +let controls; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.25, 30); + camera.position.set(3, 2, 4); + + scene = new THREE.Scene(); + scene.fog = new THREE.Fog(0x0487e2, 7, 25); + scene.backgroundNode = normalWorld.y.mix(color(0x0487e2), color(0x0066ff)); + camera.lookAt(0, 1, 0); + + const sunLight = new THREE.DirectionalLight(0xffe499, 5); + sunLight.position.set(0.5, 3, 0.5); + + const waterAmbientLight = new THREE.HemisphereLight(0x333366, 0x74ccf4, 5); + const skyAmbientLight = new THREE.HemisphereLight(0x74ccf4, 0, 1); + + scene.add(sunLight); + scene.add(skyAmbientLight); + scene.add(waterAmbientLight); + + timer = new THREE.Timer(); + timer.connect(document); + + // animated model + + const loader = new GLTFLoader(); + loader.load('models/gltf/Michelle.glb', function (gltf) { + model = gltf.scene; + + mixer = new THREE.AnimationMixer(model); + + const action = mixer.clipAction(gltf.animations[0]); + action.play(); + + scene.add(model); + }); + + // objects + + const textureLoader = new THREE.TextureLoader(); + const iceDiffuse = textureLoader.load('./textures/water.jpg'); + iceDiffuse.wrapS = THREE.RepeatWrapping; + iceDiffuse.wrapT = THREE.RepeatWrapping; + iceDiffuse.colorSpace = THREE.NoColorSpace; + + const iceColorNode = triplanarTexture(texture(iceDiffuse)).add(color(0x0066ff)).mul(0.8); + + const geometry = new THREE.IcosahedronGeometry(1, 3); + const material = new THREE.MeshStandardNodeMaterial({ colorNode: iceColorNode }); + + const count = 100; + const scale = 3.5; + const column = 10; + + objects = new THREE.Group(); + + for (let i = 0; i < count; i++) { + const x = i % column; + const y = i / column; + + const mesh = new THREE.Mesh(geometry, material); + mesh.position.set(x * scale, 0, y * scale); + mesh.rotation.set(Math.random(), Math.random(), Math.random()); + objects.add(mesh); + } + + objects.position.set((column - 1) * scale * -0.5, -1, (count / column) * scale * -0.5); + + scene.add(objects); + + // water + + const t = time.mul(0.8); + const floorUV = positionWorld.xzy; + + const waterLayer0 = mx_worley_noise_float(floorUV.mul(4).add(t)); + const waterLayer1 = mx_worley_noise_float(floorUV.mul(2).add(t)); + + const waterIntensity = waterLayer0.mul(waterLayer1); + const waterColor = waterIntensity.mul(1.4).mix(color(0x0487e2), color(0x74ccf4)); + + // linearDepth() returns the linear depth of the mesh + const depth = linearDepth(); + const depthWater = viewportLinearDepth.sub(depth).toInspector('Water / Depth', node => node.oneMinus()); + const depthEffect = depthWater.remapClamp(-0.002, 0.04); + + const refractionUV = screenUV.add(vec2(0, waterIntensity.mul(0.1))).toInspector('Water / Refraction UV'); + + // linearDepth( viewportDepthTexture( uv ) ) return the linear depth of the scene + const depthTestForRefraction = linearDepth(viewportDepthTexture(refractionUV)).sub(depth); + + const depthRefraction = depthTestForRefraction.remapClamp(0, 0.1); + + const finalUV = depthTestForRefraction.lessThan(0).select(screenUV, refractionUV); + + const viewportTexture = viewportSharedTexture(finalUV).toInspector('Water / Viewport Texture + Refraction UV'); + + const waterMaterial = new THREE.MeshBasicNodeMaterial(); + waterMaterial.colorNode = waterColor.toInspector('Water / Color'); + waterMaterial.backdropNode = depthEffect.mix( + viewportSharedTexture(), + viewportTexture.mul(depthRefraction.mix(1, waterColor)), + ); + waterMaterial.backdropAlphaNode = depthRefraction.oneMinus(); + waterMaterial.transparent = true; + + const water = new THREE.Mesh(new THREE.BoxGeometry(50, 0.001, 50), waterMaterial); + water.position.set(0, 0, 0); + scene.add(water); + + // floor + + floor = new THREE.Mesh( + new THREE.CylinderGeometry(1.1, 1.1, 10), + new THREE.MeshStandardNodeMaterial({ colorNode: iceColorNode }), + ); + floor.position.set(0, -5, 0); + scene.add(floor); + + // caustics + + const waterPosY = positionWorld.y.sub(water.position.y); + + let transition = waterPosY.add(0.1).saturate().oneMinus(); + transition = waterPosY.lessThan(0).select(transition, normalWorld.y.mix(transition, 0)).toVar(); + + const colorNode = transition.mix(material.colorNode, material.colorNode.add(waterLayer0)); + + //material.colorNode = colorNode; + floor.material.colorNode = colorNode; + + // renderer + + renderer = new THREE.WebGPURenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 1; + controls.maxDistance = 10; + controls.maxPolarAngle = Math.PI * 0.9; + controls.autoRotate = true; + controls.autoRotateSpeed = 1; + controls.target.set(0, 0.2, 0); + controls.update(); + + // gui + + const gui = renderer.inspector.createParameters('Settings'); + + floorPosition = new THREE.Vector3(0, 0.2, 0); + + gui.add(floorPosition, 'y', -1, 1, 0.001).name('floor position'); + + // post processing + + const scenePass = pass(scene, camera); + const scenePassColor = scenePass.getTextureNode(); + const scenePassDepth = scenePass.getLinearDepthNode().remapClamp(0.3, 0.5); + + const waterMask = objectPosition(camera) + .y.greaterThan(screenUV.y.sub(0.5).mul(camera.near)) + .toInspector('Post-Processing / Water Mask'); + + const scenePassColorBlurred = gaussianBlur(scenePassColor); + scenePassColorBlurred.directionNode = waterMask + .select(scenePassDepth, scenePass.getLinearDepthNode().mul(5)) + .toInspector('Post-Processing / Blur Strength [ Depth ]', node => node.toFloat()); + + const vignette = screenUV.distance(0.5).mul(1.35).clamp().oneMinus().toInspector('Post-Processing / Vignette'); + + renderPipeline = new THREE.RenderPipeline(renderer); + renderPipeline.outputNode = waterMask.select( + scenePassColorBlurred, + scenePassColorBlurred.mul(color(0x74ccf4)).mul(vignette), + ); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + controls.update(); + + const delta = timer.getDelta(); + + floor.position.y = floorPosition.y - 5; + + if (model) { + mixer.update(delta); + + model.position.y = floorPosition.y; + } + + for (const object of objects.children) { + object.position.y = Math.sin(timer.getElapsed() + object.id) * 0.3; + object.rotation.y += delta * 0.3; + } + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_camera.ts b/examples-testing/examples/webgpu_camera.ts new file mode 100644 index 000000000..bf4724ac8 --- /dev/null +++ b/examples-testing/examples/webgpu_camera.ts @@ -0,0 +1,211 @@ +import * as THREE from 'three/webgpu'; + +let SCREEN_WIDTH = window.innerWidth; +let SCREEN_HEIGHT = window.innerHeight; +let aspect = SCREEN_WIDTH / SCREEN_HEIGHT; + +let container; +let camera, scene, renderer, mesh; +let cameraRig, activeCamera, activeHelper; +let cameraPerspective, cameraOrtho; +let cameraPerspectiveHelper, cameraOrthoHelper; +const frustumSize = 600; + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + scene = new THREE.Scene(); + + // + + camera = new THREE.PerspectiveCamera(50, 0.5 * aspect, 1, 10000); + camera.position.z = 2500; + + cameraPerspective = new THREE.PerspectiveCamera(50, 0.5 * aspect, 150, 1000); + + cameraPerspectiveHelper = new THREE.CameraHelper(cameraPerspective); + scene.add(cameraPerspectiveHelper); + + // + cameraOrtho = new THREE.OrthographicCamera( + (0.5 * frustumSize * aspect) / -2, + (0.5 * frustumSize * aspect) / 2, + frustumSize / 2, + frustumSize / -2, + 150, + 1000, + ); + + cameraOrthoHelper = new THREE.CameraHelper(cameraOrtho); + scene.add(cameraOrthoHelper); + + // + + activeCamera = cameraPerspective; + activeHelper = cameraPerspectiveHelper; + + // counteract different front orientation of cameras vs rig + + cameraOrtho.rotation.y = Math.PI; + cameraPerspective.rotation.y = Math.PI; + + cameraRig = new THREE.Group(); + + cameraRig.add(cameraPerspective); + cameraRig.add(cameraOrtho); + + scene.add(cameraRig); + + // + + mesh = new THREE.Mesh( + new THREE.SphereGeometry(100, 16, 8), + new THREE.MeshBasicMaterial({ color: 0xffffff, wireframe: true }), + ); + scene.add(mesh); + + const mesh2 = new THREE.Mesh( + new THREE.SphereGeometry(50, 16, 8), + new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true }), + ); + mesh2.position.y = 150; + mesh.add(mesh2); + + const mesh3 = new THREE.Mesh( + new THREE.SphereGeometry(5, 16, 8), + new THREE.MeshBasicMaterial({ color: 0x0000ff, wireframe: true }), + ); + mesh3.position.z = 150; + cameraRig.add(mesh3); + + // + + const geometry = new THREE.BufferGeometry(); + const vertices = []; + + for (let i = 0; i < 10000; i++) { + vertices.push(THREE.MathUtils.randFloatSpread(2000)); // x + vertices.push(THREE.MathUtils.randFloatSpread(2000)); // y + vertices.push(THREE.MathUtils.randFloatSpread(2000)); // z + } + + geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); + + const particles = new THREE.Points(geometry, new THREE.PointsMaterial({ color: 0xffffff })); + scene.add(particles); + + // + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + renderer.setScissorTest(true); + renderer.setClearColor(0x000000, 1); + + // + + window.addEventListener('resize', onWindowResize); + document.addEventListener('keydown', onKeyDown); +} + +// + +function onKeyDown(event) { + switch (event.keyCode) { + case 79 /*O*/: + activeCamera = cameraOrtho; + activeHelper = cameraOrthoHelper; + + break; + + case 80 /*P*/: + activeCamera = cameraPerspective; + activeHelper = cameraPerspectiveHelper; + + break; + } +} + +// + +function onWindowResize() { + SCREEN_WIDTH = window.innerWidth; + SCREEN_HEIGHT = window.innerHeight; + aspect = SCREEN_WIDTH / SCREEN_HEIGHT; + + renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); + + camera.aspect = 0.5 * aspect; + camera.updateProjectionMatrix(); + + cameraPerspective.aspect = 0.5 * aspect; + cameraPerspective.updateProjectionMatrix(); + + cameraOrtho.left = (-0.5 * frustumSize * aspect) / 2; + cameraOrtho.right = (0.5 * frustumSize * aspect) / 2; + cameraOrtho.top = frustumSize / 2; + cameraOrtho.bottom = -frustumSize / 2; + cameraOrtho.updateProjectionMatrix(); +} + +// + +function animate() { + render(); +} + +function render() { + const r = Date.now() * 0.0005; + + mesh.position.x = 700 * Math.cos(r); + mesh.position.z = 700 * Math.sin(r); + mesh.position.y = 700 * Math.sin(r); + + mesh.children[0].position.x = 70 * Math.cos(2 * r); + mesh.children[0].position.z = 70 * Math.sin(r); + + if (activeCamera === cameraPerspective) { + cameraPerspective.fov = 35 + 30 * Math.sin(0.5 * r); + cameraPerspective.far = mesh.position.length(); + cameraPerspective.updateProjectionMatrix(); + + cameraPerspectiveHelper.update(); + cameraPerspectiveHelper.visible = true; + + cameraOrthoHelper.visible = false; + } else { + cameraOrtho.far = mesh.position.length(); + cameraOrtho.updateProjectionMatrix(); + + cameraOrthoHelper.update(); + cameraOrthoHelper.visible = true; + + cameraPerspectiveHelper.visible = false; + } + + cameraRig.lookAt(mesh.position); + + // + + activeHelper.visible = false; + + renderer.setClearColor(0x000000, 1); + renderer.setScissor(0, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT); + renderer.setViewport(0, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT); + renderer.render(scene, activeCamera); + + // + + activeHelper.visible = true; + + renderer.setClearColor(0x111111, 1); + renderer.setScissor(SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT); + renderer.setViewport(SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT); + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_camera_array.ts b/examples-testing/examples/webgpu_camera_array.ts new file mode 100644 index 000000000..a4d82a709 --- /dev/null +++ b/examples-testing/examples/webgpu_camera_array.ts @@ -0,0 +1,101 @@ +import * as THREE from 'three/webgpu'; + +let camera, scene, renderer; +let mesh; + +const AMOUNT = 6; + +init(); + +function init() { + const subCameras = []; + + for (let i = 0; i < AMOUNT * AMOUNT; i++) { + const subCamera = new THREE.PerspectiveCamera(40, 1, 0.1, 10); + subCamera.viewport = new THREE.Vector4(); + + subCameras.push(subCamera); + } + + camera = new THREE.ArrayCamera(subCameras); + camera.position.z = 3; + + updateCameras(); + + scene = new THREE.Scene(); + + scene.add(new THREE.AmbientLight(0x999999)); + + const light = new THREE.DirectionalLight(0xffffff, 3); + light.position.set(0.5, 0.5, 1); + light.castShadow = true; + light.shadow.camera.zoom = 4; // tighter shadow map + scene.add(light); + + const geometryBackground = new THREE.PlaneGeometry(100, 100); + const materialBackground = new THREE.MeshPhongMaterial({ color: 0x000066 }); + + const background = new THREE.Mesh(geometryBackground, materialBackground); + background.receiveShadow = true; + background.position.set(0, 0, -1); + scene.add(background); + + const geometryCylinder = new THREE.CylinderGeometry(0.5, 0.5, 1, 32); + const materialCylinder = new THREE.MeshPhongMaterial({ color: 0xff0000 }); + + mesh = new THREE.Mesh(geometryCylinder, materialCylinder); + mesh.castShadow = true; + mesh.receiveShadow = true; + scene.add(mesh); + + renderer = new THREE.WebGPURenderer(/*{ forceWebGL: true }*/); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.shadowMap.enabled = true; + document.body.appendChild(renderer.domElement); + + // + + window.addEventListener('resize', onWindowResize); +} + +function updateCameras() { + const ASPECT_RATIO = window.innerWidth / window.innerHeight; + const WIDTH = window.innerWidth / AMOUNT; + const HEIGHT = window.innerHeight / AMOUNT; + + camera.aspect = ASPECT_RATIO; + camera.updateProjectionMatrix(); + + for (let y = 0; y < AMOUNT; y++) { + for (let x = 0; x < AMOUNT; x++) { + const subcamera = camera.cameras[AMOUNT * y + x]; + subcamera.copy(camera); // copy fov, aspect ratio, near, far from the root camera + + subcamera.viewport.set(Math.floor(x * WIDTH), Math.floor(y * HEIGHT), Math.ceil(WIDTH), Math.ceil(HEIGHT)); + subcamera.updateProjectionMatrix(); + + subcamera.position.x = x / AMOUNT - 0.5; + subcamera.position.y = 0.5 - y / AMOUNT; + subcamera.position.z = 1.5 + (x + y) * 0.5; + subcamera.position.multiplyScalar(2); + + subcamera.lookAt(0, 0, 0); + subcamera.updateMatrixWorld(); + } + } +} + +function onWindowResize() { + updateCameras(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + mesh.rotation.x += 0.005; + mesh.rotation.z += 0.01; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_camera_logarithmicdepthbuffer.ts b/examples-testing/examples/webgpu_camera_logarithmicdepthbuffer.ts new file mode 100644 index 000000000..a0e748e1b --- /dev/null +++ b/examples-testing/examples/webgpu_camera_logarithmicdepthbuffer.ts @@ -0,0 +1,239 @@ +import * as THREE from 'three/webgpu'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { FontLoader } from 'three/addons/loaders/FontLoader.js'; +import { TextGeometry } from 'three/addons/geometries/TextGeometry.js'; + +// 1 micrometer to 100 billion light years in one scene, with 1 unit = 1 meter? preposterous! and yet... +const NEAR = 1e-6, + FAR = 1e27; +let SCREEN_WIDTH = window.innerWidth; +let SCREEN_HEIGHT = window.innerHeight; +let screensplit = 0.25, + screensplit_right = 0; +const mouse = [0.5, 0.5]; +let zoompos = -100, + minzoomspeed = 0.015; +let zoomspeed = minzoomspeed; + +let border; +const objects = {}; + +// Generate a number of text labels, from 1µm in size up to 100,000,000 light years +// Try to use some descriptive real-world examples of objects at each scale + +const labeldata = [ + { size: 0.01, scale: 0.0001, label: 'microscopic (1µm)' }, // FIXME - triangulating text fails at this size, so we scale instead + { size: 0.01, scale: 0.1, label: 'minuscule (1mm)' }, + { size: 0.01, scale: 1.0, label: 'tiny (1cm)' }, + { size: 1, scale: 1.0, label: 'child-sized (1m)' }, + { size: 10, scale: 1.0, label: 'tree-sized (10m)' }, + { size: 100, scale: 1.0, label: 'building-sized (100m)' }, + { size: 1000, scale: 1.0, label: 'medium (1km)' }, + { size: 10000, scale: 1.0, label: 'city-sized (10km)' }, + { size: 3400000, scale: 1.0, label: 'moon-sized (3,400 Km)' }, + { size: 12000000, scale: 1.0, label: 'planet-sized (12,000 km)' }, + { size: 1400000000, scale: 1.0, label: 'sun-sized (1,400,000 km)' }, + { size: 7.47e12, scale: 1.0, label: 'solar system-sized (50Au)' }, + { size: 9.4605284e15, scale: 1.0, label: 'gargantuan (1 light year)' }, + { size: 3.08567758e16, scale: 1.0, label: 'ludicrous (1 parsec)' }, + { size: 1e19, scale: 1.0, label: 'mind boggling (1000 light years)' }, +]; + +init().then(animate); + +async function init() { + const loader = new FontLoader(); + const font = await loader.loadAsync('fonts/helvetiker_regular.typeface.json'); + + const scene = initScene(font); + + // Initialize two copies of the same scene, one with normal z-buffer and one with logarithmic z-buffer + objects.normal = await initView(scene, 'normal', false); + objects.logzbuf = await initView(scene, 'logzbuf', true); + + // Resize border allows the user to easily compare effects of logarithmic depth buffer over the whole scene + border = document.getElementById('renderer_border'); + border.addEventListener('pointerdown', onBorderPointerDown); + + window.addEventListener('mousemove', onMouseMove); + window.addEventListener('resize', onWindowResize); + window.addEventListener('wheel', onMouseWheel); +} + +async function initView(scene, name, logDepthBuf) { + const framecontainer = document.getElementById('container_' + name); + + const camera = new THREE.PerspectiveCamera(50, (screensplit * SCREEN_WIDTH) / SCREEN_HEIGHT, NEAR, FAR); + scene.add(camera); + + const renderer = new THREE.WebGPURenderer({ antialias: true, logarithmicDepthBuffer: logDepthBuf }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(SCREEN_WIDTH / 2, SCREEN_HEIGHT); + renderer.domElement.style.position = 'relative'; + renderer.domElement.id = 'renderer_' + name; + renderer.inspector = new Inspector(); + framecontainer.appendChild(renderer.domElement); + + await renderer.init(); + + return { container: framecontainer, renderer: renderer, scene: scene, camera: camera }; +} + +function initScene(font) { + const scene = new THREE.Scene(); + + scene.add(new THREE.AmbientLight(0x777777)); + + const light = new THREE.DirectionalLight(0xffffff, 3); + light.position.set(100, 100, 100); + scene.add(light); + + const materialargs = { + color: 0xffffff, + specular: 0x050505, + shininess: 50, + emissive: 0x000000, + }; + + const geometry = new THREE.SphereGeometry(0.5, 24, 12); + + for (let i = 0; i < labeldata.length; i++) { + const scale = labeldata[i].scale || 1; + + const labelgeo = new TextGeometry(labeldata[i].label, { + font: font, + size: labeldata[i].size, + depth: labeldata[i].size / 2, + }); + + labelgeo.computeBoundingSphere(); + + // center text + labelgeo.translate(-labelgeo.boundingSphere.radius, 0, 0); + + materialargs.color = new THREE.Color().setHSL(Math.random(), 0.5, 0.5); + + const material = new THREE.MeshPhongMaterial(materialargs); + + const group = new THREE.Group(); + group.position.z = -labeldata[i].size * scale; + scene.add(group); + + const textmesh = new THREE.Mesh(labelgeo, material); + textmesh.scale.set(scale, scale, scale); + textmesh.position.z = -labeldata[i].size * scale; + textmesh.position.y = (labeldata[i].size / 4) * scale; + group.add(textmesh); + + const dotmesh = new THREE.Mesh(geometry, material); + dotmesh.position.y = (-labeldata[i].size / 4) * scale; + dotmesh.scale.multiplyScalar(labeldata[i].size * scale); + group.add(dotmesh); + } + + return scene; +} + +function updateRendererSizes() { + // Recalculate size for both renderers when screen size or split location changes + + SCREEN_WIDTH = window.innerWidth; + SCREEN_HEIGHT = window.innerHeight; + + screensplit_right = 1 - screensplit; + + objects.normal.renderer.setSize(screensplit * SCREEN_WIDTH, SCREEN_HEIGHT); + objects.normal.camera.aspect = (screensplit * SCREEN_WIDTH) / SCREEN_HEIGHT; + objects.normal.camera.updateProjectionMatrix(); + objects.normal.camera.setViewOffset(SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, SCREEN_WIDTH * screensplit, SCREEN_HEIGHT); + objects.normal.container.style.width = screensplit * 100 + '%'; + + objects.logzbuf.renderer.setSize(screensplit_right * SCREEN_WIDTH, SCREEN_HEIGHT); + objects.logzbuf.camera.aspect = (screensplit_right * SCREEN_WIDTH) / SCREEN_HEIGHT; + objects.logzbuf.camera.updateProjectionMatrix(); + objects.logzbuf.camera.setViewOffset( + SCREEN_WIDTH, + SCREEN_HEIGHT, + SCREEN_WIDTH * screensplit, + 0, + SCREEN_WIDTH * screensplit_right, + SCREEN_HEIGHT, + ); + objects.logzbuf.container.style.width = screensplit_right * 100 + '%'; + + border.style.left = screensplit * 100 + '%'; +} + +function animate() { + requestAnimationFrame(animate); + + // Put some limits on zooming + const minzoom = labeldata[0].size * labeldata[0].scale * 1; + const maxzoom = labeldata[labeldata.length - 1].size * labeldata[labeldata.length - 1].scale * 100; + let damping = Math.abs(zoomspeed) > minzoomspeed ? 0.95 : 1.0; + + // Zoom out faster the further out you go + const zoom = THREE.MathUtils.clamp(Math.pow(Math.E, zoompos), minzoom, maxzoom); + zoompos = Math.log(zoom); + + // Slow down quickly at the zoom limits + if ((zoom == minzoom && zoomspeed < 0) || (zoom == maxzoom && zoomspeed > 0)) { + damping = 0.85; + } + + zoompos += zoomspeed; + zoomspeed *= damping; + + objects.normal.camera.position.x = Math.sin(0.5 * Math.PI * (mouse[0] - 0.5)) * zoom; + objects.normal.camera.position.y = Math.sin(0.25 * Math.PI * (mouse[1] - 0.5)) * zoom; + objects.normal.camera.position.z = Math.cos(0.5 * Math.PI * (mouse[0] - 0.5)) * zoom; + objects.normal.camera.lookAt(objects.normal.scene.position); + + // Clone camera settings across both scenes + objects.logzbuf.camera.position.copy(objects.normal.camera.position); + objects.logzbuf.camera.quaternion.copy(objects.normal.camera.quaternion); + + // Update renderer sizes if the split has changed + if (screensplit_right != 1 - screensplit) { + updateRendererSizes(); + } + + objects.normal.renderer.render(objects.normal.scene, objects.normal.camera); + objects.logzbuf.renderer.render(objects.logzbuf.scene, objects.logzbuf.camera); +} + +function onWindowResize() { + updateRendererSizes(); +} + +function onBorderPointerDown() { + // activate draggable window resizing bar + window.addEventListener('pointermove', onBorderPointerMove); + window.addEventListener('pointerup', onBorderPointerUp); +} + +function onBorderPointerMove(ev) { + screensplit = Math.max(0, Math.min(1, ev.clientX / window.innerWidth)); +} + +function onBorderPointerUp() { + window.removeEventListener('pointermove', onBorderPointerMove); + window.removeEventListener('pointerup', onBorderPointerUp); +} + +function onMouseMove(ev) { + mouse[0] = ev.clientX / window.innerWidth; + mouse[1] = ev.clientY / window.innerHeight; +} + +function onMouseWheel(ev) { + const amount = ev.deltaY; + if (amount === 0) return; + const dir = amount / Math.abs(amount); + zoomspeed = dir / 10; + + // Slow down default zoom speed after user starts zooming, to give them more control + minzoomspeed = 0.001; +} diff --git a/examples-testing/examples/webgpu_caustics.ts b/examples-testing/examples/webgpu_caustics.ts new file mode 100644 index 000000000..0829fb558 --- /dev/null +++ b/examples-testing/examples/webgpu_caustics.ts @@ -0,0 +1,202 @@ +import * as THREE from 'three/webgpu'; + +import { + uniform, + refract, + div, + positionViewDirection, + positionLocal, + normalView, + texture, + Fn, + vec2, + vec3, + vec4, +} from 'three/tsl'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer, controls; +let gltf; + +init(); + +async function init() { + camera = new THREE.PerspectiveCamera(25, window.innerWidth / window.innerHeight, 0.025, 5); + camera.position.set(-0.5, 0.35, 0.2); + + scene = new THREE.Scene(); + + // light + + const spotLight = new THREE.SpotLight(0xffffff, 1); + spotLight.position.set(0.2, 0.3, 0.2); + spotLight.castShadow = true; + spotLight.angle = Math.PI / 6; + spotLight.penumbra = 1; + spotLight.decay = 2; + spotLight.distance = 0; + spotLight.shadow.mapType = THREE.HalfFloatType; // For HDR Caustics + spotLight.shadow.mapSize.width = 1024; + spotLight.shadow.mapSize.height = 1024; + spotLight.shadow.camera.near = 0.1; + spotLight.shadow.camera.far = 1; + spotLight.shadow.intensity = 0.95; + scene.add(spotLight); + + // model / textures + + const dracoLoader = new DRACOLoader(); + dracoLoader.setDecoderPath('jsm/libs/draco/'); + dracoLoader.setDecoderConfig({ type: 'js' }); + + gltf = (await new GLTFLoader().setDRACOLoader(dracoLoader).loadAsync('./models/gltf/duck.glb')).scene; + gltf.scale.setScalar(0.5); + scene.add(gltf); + + const causticMap = new THREE.TextureLoader().load('./textures/opengameart/Caustic_Free.jpg'); + causticMap.wrapS = causticMap.wrapT = THREE.RepeatWrapping; + causticMap.colorSpace = THREE.SRGBColorSpace; + + // objects / material + + const duck = gltf.children[0]; + duck.material = new THREE.MeshPhysicalNodeMaterial(); + duck.material.side = THREE.DoubleSide; + duck.material.transparent = true; + duck.material.color = new THREE.Color(0xffd700); + duck.material.transmission = 1; + duck.material.thickness = 0.25; + duck.material.ior = 1.5; + duck.material.metalness = 0; + duck.material.roughness = 0.1; + duck.castShadow = true; + + // tsl shader + + const causticOcclusion = uniform(20); + + duck.material.castShadowPositionNode = Fn(() => { + // optional: add some distortion to the geometry shadow position if needed + + return positionLocal; + })(); + + duck.material.castShadowNode = Fn(() => { + const refractionVector = refract( + positionViewDirection.negate(), + normalView, + div(1.0, duck.material.ior), + ).normalize(); + const viewZ = normalView.z.pow(causticOcclusion); + + const textureUV = refractionVector.xy.mul(0.6); + + const causticColor = uniform(duck.material.color); + const chromaticAberrationOffset = normalView.z.pow(-0.9).mul(0.004); + + const causticProjection = vec3( + texture(causticMap, textureUV.add(vec2(chromaticAberrationOffset.negate(), 0))).r, + texture(causticMap, textureUV.add(vec2(0, chromaticAberrationOffset.negate()))).g, + texture(causticMap, textureUV.add(vec2(chromaticAberrationOffset, chromaticAberrationOffset))).b, + ); + + return causticProjection.mul(viewZ.mul(25)).add(viewZ).mul(causticColor); + })(); + + // + + const textureLoader = new THREE.TextureLoader(); + + // glass + + const colorMap = textureLoader.load('textures/colors.png'); + colorMap.wrapS = colorMap.wrapT = THREE.RepeatWrapping; + colorMap.colorSpace = THREE.SRGBColorSpace; + + const glassMaterial = new THREE.MeshPhysicalNodeMaterial(); + glassMaterial.map = colorMap; + glassMaterial.side = THREE.DoubleSide; + glassMaterial.transparent = true; + glassMaterial.color = new THREE.Color(0xffffff); + glassMaterial.transmission = 1; + glassMaterial.ior = 1.5; + glassMaterial.metalness = 0; + glassMaterial.roughness = 0.1; + glassMaterial.castShadowNode = vec4(texture(colorMap).rgb, 0.8); + + const glass = new THREE.Mesh(new THREE.PlaneGeometry(0.2, 0.2), glassMaterial); + glass.position.y = 0.1; + glass.castShadow = true; + glass.visible = false; + scene.add(glass); + + // ground + + const map = textureLoader.load('textures/hardwood2_diffuse.jpg'); + map.wrapS = map.wrapT = THREE.RepeatWrapping; + map.repeat.set(10, 10); + + const geometry = new THREE.PlaneGeometry(2, 2); + const material = new THREE.MeshStandardMaterial({ color: 0x999999, map }); + + const ground = new THREE.Mesh(geometry, material); + ground.rotation.x = -Math.PI / 2; + ground.receiveShadow = true; + scene.add(ground); + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.shadowMap.enabled = true; + renderer.shadowMap.transmitted = true; + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // gui + + const gui = renderer.inspector.createParameters('Settings'); + gui.add(causticOcclusion, 'value', 0, 20).name('caustic occlusion'); + gui.addColor(duck.material, 'color').name('material color'); + gui.add({ model: 'duck' }, 'model', ['duck', 'glass']).onChange(model => { + duck.visible = glass.visible = false; + + if (model === 'duck') { + duck.visible = true; + } else if (model === 'glass') { + glass.visible = true; + } + }); + + // controls + + controls = new OrbitControls(camera, renderer.domElement); + controls.maxDistance = 3; + controls.maxPolarAngle = Math.PI / 2; + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + for (const mesh of gltf.children) { + mesh.rotation.y -= 0.01; + } + + controls.update(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_centroid_sampling.ts b/examples-testing/examples/webgpu_centroid_sampling.ts new file mode 100644 index 000000000..ca159d04c --- /dev/null +++ b/examples-testing/examples/webgpu_centroid_sampling.ts @@ -0,0 +1,199 @@ +import * as THREE from 'three/webgpu'; +import { varying, uv, texture, Fn } from 'three/tsl'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let rendererAntialiasingEnabled; +let rendererAntialiasingDisabled; +let camera; +let scene; +let gui; + +const effectController = { + sampling: 'normal', +}; + +const atlasCanvas = document.createElement('canvas'); +atlasCanvas.width = 16; +atlasCanvas.height = 16; + +const ctx = atlasCanvas.getContext('2d'); +ctx.fillStyle = 'red'; +ctx.fillRect(0, 0, 8, 8); + +const redUVs = [0, 1, 0.5, 1, 0.5, 0.5, 0, 0.5]; +ctx.fillStyle = 'green'; +ctx.fillRect(8, 0, 8, 8); + +const greenUVs = [1, 1, 0.5, 1, 0.5, 0.5, 1, 0.5]; + +ctx.fillStyle = 'blue'; +ctx.fillRect(0, 8, 8, 8); + +const blueUVs = [0, 0, 0.5, 0, 0.5, 0.5, 0, 0.5]; + +ctx.fillStyle = 'yellow'; +ctx.fillRect(8, 8, 8, 8); + +const yellowUVs = [1, 0, 0.5, 0, 0.5, 0.5, 1, 0.5]; + +const faces = [redUVs, greenUVs, blueUVs, yellowUVs]; + +const canvasTexture = new THREE.CanvasTexture(atlasCanvas); +canvasTexture.colorSpace = THREE.SRGBColorSpace; +canvasTexture.mapping = THREE.UVMapping; +canvasTexture.wrapS = THREE.RepeatWrapping; +canvasTexture.wrapT = THREE.RepeatWrapping; +canvasTexture.magFilter = THREE.NearestFilter; +canvasTexture.minFilter = THREE.NearestFilter; +canvasTexture.format = THREE.RGBAFormat; +canvasTexture.type = THREE.UnsignedByteType; + +const forceWebGL = false; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(); + camera.fov = 60; + camera.near = 1; + camera.far = 2100; + camera.position.z = 50; + + scene = new THREE.Scene(); + + const makeFaceGeometry = uvs => { + const geometry = new THREE.BufferGeometry(); + const positions = [-1, -1, 0, 1, -1, 0, 1, 1, 0, -1, 1, 0]; + geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(positions), 3)); + + const indices = [0, 1, 2, 2, 3, 0]; + geometry.setIndex(indices); + + geometry.setAttribute('uv', new THREE.BufferAttribute(new Float32Array(uvs), 2)); + + return geometry; + }; + + const material = new THREE.MeshBasicNodeMaterial(); + const testUV = varying(uv(), 'testUV'); + + const createShader = (type, sampling) => { + return Fn(() => { + testUV.setInterpolation(type, sampling); + + return texture(canvasTexture, testUV).rgb; + }); + }; + + const withFlatFirstShader = createShader( + THREE.InterpolationSamplingType.FLAT, + THREE.InterpolationSamplingMode.FIRST, + ); + const withFlatEitherShader = createShader( + THREE.InterpolationSamplingType.FLAT, + THREE.InterpolationSamplingMode.EITHER, + ); + + const withSampleShader = Fn(() => { + testUV.setInterpolation(THREE.InterpolationSamplingType.PERSPECTIVE, THREE.InterpolationSamplingMode.SAMPLE); + + return texture(canvasTexture, testUV).rgb; + }); + + const withInterpolationShader = Fn(() => { + testUV.setInterpolation(THREE.InterpolationSamplingType.PERSPECTIVE, THREE.InterpolationSamplingMode.CENTROID); + + return texture(canvasTexture, testUV).rgb; + }); + + const withoutInterpolationShader = Fn(() => { + return texture(canvasTexture, uv()).rgb; + }); + + material.colorNode = withoutInterpolationShader(); + + const faceMeshes = []; + + for (let x = -5; x < 5; x++) { + for (let y = -5; y < 5; y++) { + const face = faces[Math.floor(Math.random() * faces.length)]; + const geometry = makeFaceGeometry(face); + const mesh = new THREE.Mesh(geometry, material); + mesh.position.set(x * 2, y * 2, 0); + faceMeshes.push(mesh); + scene.add(mesh); + } + } + + // Create Standard Renderer + rendererAntialiasingDisabled = new THREE.WebGPURenderer({ + antialias: false, + forceWebGL: forceWebGL, + }); + + rendererAntialiasingDisabled.setPixelRatio(window.devicePixelRatio); + rendererAntialiasingDisabled.setSize(window.innerWidth / 2, window.innerHeight); + rendererAntialiasingDisabled.setAnimationLoop(animateStandard); + + // Create antialiased renderer + rendererAntialiasingEnabled = new THREE.WebGPURenderer({ + antialias: true, + forceWebGL: forceWebGL, + }); + + document.body.querySelector('#antialiasing-enabled').appendChild(rendererAntialiasingEnabled.domElement); + rendererAntialiasingEnabled.setPixelRatio(window.devicePixelRatio); + rendererAntialiasingEnabled.setSize(window.innerWidth / 2, window.innerHeight); + rendererAntialiasingEnabled.setAnimationLoop(animateAliased); + rendererAntialiasingEnabled.inspector = new Inspector(); + + document.body.querySelector('#antialiasing-disabled').appendChild(rendererAntialiasingDisabled.domElement); + document.body.querySelector('#antialiasing-disabled').appendChild(rendererAntialiasingDisabled.domElement); + + onWindowResize(); + + window.addEventListener('resize', onWindowResize); + + gui = rendererAntialiasingEnabled.inspector.createParameters('Settings'); + gui.add(effectController, 'sampling', [ + THREE.InterpolationSamplingMode.NORMAL, + THREE.InterpolationSamplingMode.CENTROID, + THREE.InterpolationSamplingMode.SAMPLE, + 'flat first', + 'flat either', + ]).onChange(() => { + const interpolationShaderLib = { + [THREE.InterpolationSamplingMode.NORMAL]: withoutInterpolationShader, + [THREE.InterpolationSamplingMode.CENTROID]: withInterpolationShader, + [THREE.InterpolationSamplingMode.SAMPLE]: withSampleShader, + ['flat first']: withFlatFirstShader, + ['flat either']: withFlatEitherShader, + }; + + const shader = interpolationShaderLib[effectController.sampling]; + + for (let i = 0; i < faceMeshes.length; i++) { + faceMeshes[i].material.colorNode = shader(); + faceMeshes[i].material.needsUpdate = true; + } + }); +} + +function onWindowResize() { + const halfWidth = window.innerWidth / 2; + rendererAntialiasingDisabled.setSize(halfWidth, window.innerHeight); + rendererAntialiasingEnabled.setSize(halfWidth, window.innerHeight); + const aspect = halfWidth / window.innerHeight; + + camera.aspect = aspect; + camera.updateProjectionMatrix(); +} + +function animateStandard() { + rendererAntialiasingDisabled.render(scene, camera); +} + +function animateAliased() { + rendererAntialiasingEnabled.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_clearcoat.ts b/examples-testing/examples/webgpu_clearcoat.ts new file mode 100644 index 000000000..02fbacb9d --- /dev/null +++ b/examples-testing/examples/webgpu_clearcoat.ts @@ -0,0 +1,194 @@ +import * as THREE from 'three/webgpu'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { HDRCubeTextureLoader } from 'three/addons/loaders/HDRCubeTextureLoader.js'; + +import { FlakesTexture } from 'three/addons/textures/FlakesTexture.js'; + +let camera, scene, renderer; + +let particleLight; +let group; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 0.25, 50); + camera.position.z = 10; + + scene = new THREE.Scene(); + + group = new THREE.Group(); + scene.add(group); + + new HDRCubeTextureLoader() + .setPath('textures/cube/pisaHDR/') + .load(['px.hdr', 'nx.hdr', 'py.hdr', 'ny.hdr', 'pz.hdr', 'nz.hdr'], function (texture) { + const geometry = new THREE.SphereGeometry(0.8, 64, 32); + + const textureLoader = new THREE.TextureLoader(); + + const diffuse = textureLoader.load('textures/carbon/Carbon.png'); + diffuse.colorSpace = THREE.SRGBColorSpace; + diffuse.wrapS = THREE.RepeatWrapping; + diffuse.wrapT = THREE.RepeatWrapping; + diffuse.repeat.x = 10; + diffuse.repeat.y = 10; + + const normalMap = textureLoader.load('textures/carbon/Carbon_Normal.png'); + normalMap.wrapS = THREE.RepeatWrapping; + normalMap.wrapT = THREE.RepeatWrapping; + normalMap.repeat.x = 10; + normalMap.repeat.y = 10; + + const normalMap2 = textureLoader.load('textures/water/Water_1_M_Normal.jpg'); + + const normalMap3 = new THREE.CanvasTexture(new FlakesTexture()); + normalMap3.wrapS = THREE.RepeatWrapping; + normalMap3.wrapT = THREE.RepeatWrapping; + normalMap3.repeat.x = 10; + normalMap3.repeat.y = 6; + normalMap3.anisotropy = 16; + + const normalMap4 = textureLoader.load('textures/golfball.jpg'); + + const clearcoatNormalMap = textureLoader.load( + 'textures/pbr/Scratched_gold/Scratched_gold_01_1K_Normal.png', + ); + + // car paint + + let material = new THREE.MeshPhysicalMaterial({ + clearcoat: 1.0, + clearcoatRoughness: 0.1, + metalness: 0.9, + roughness: 0.5, + color: 0x0000ff, + normalMap: normalMap3, + normalScale: new THREE.Vector2(0.15, 0.15), + }); + let mesh = new THREE.Mesh(geometry, material); + mesh.position.x = -1; + mesh.position.y = 1; + group.add(mesh); + + // fibers + + material = new THREE.MeshPhysicalMaterial({ + roughness: 0.5, + clearcoat: 1.0, + clearcoatRoughness: 0.1, + map: diffuse, + normalMap: normalMap, + }); + mesh = new THREE.Mesh(geometry, material); + mesh.position.x = 1; + mesh.position.y = 1; + group.add(mesh); + + // golf + + material = new THREE.MeshPhysicalMaterial({ + metalness: 0.0, + roughness: 0.1, + clearcoat: 1.0, + normalMap: normalMap4, + clearcoatNormalMap: clearcoatNormalMap, + + // y scale is negated to compensate for normal map handedness. + clearcoatNormalScale: new THREE.Vector2(2.0, -2.0), + }); + mesh = new THREE.Mesh(geometry, material); + mesh.position.x = -1; + mesh.position.y = -1; + group.add(mesh); + + // clearcoat + normalmap + + material = new THREE.MeshPhysicalMaterial({ + clearcoat: 1.0, + metalness: 1.0, + color: 0xff0000, + normalMap: normalMap2, + normalScale: new THREE.Vector2(0.15, 0.15), + clearcoatNormalMap: clearcoatNormalMap, + + // y scale is negated to compensate for normal map handedness. + clearcoatNormalScale: new THREE.Vector2(2.0, -2.0), + }); + mesh = new THREE.Mesh(geometry, material); + mesh.position.x = 1; + mesh.position.y = -1; + group.add(mesh); + + // + + scene.background = texture; + scene.environment = texture; + }); + + // LIGHTS + + particleLight = new THREE.Mesh( + new THREE.SphereGeometry(0.05, 8, 8), + new THREE.MeshBasicMaterial({ color: 0xffffff }), + ); + scene.add(particleLight); + + particleLight.add(new THREE.PointLight(0xffffff, 30)); + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // + + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = 1.25; + renderer.inspector = new Inspector(); + + // EVENTS + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 3; + controls.maxDistance = 30; + + window.addEventListener('resize', onWindowResize); +} + +// + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); +} + +// + +function animate() { + render(); +} + +function render() { + const timer = Date.now() * 0.00025; + + particleLight.position.x = Math.sin(timer * 7) * 3; + particleLight.position.y = Math.cos(timer * 5) * 4; + particleLight.position.z = Math.cos(timer * 3) * 3; + + for (let i = 0; i < group.children.length; i++) { + const child = group.children[i]; + child.rotation.y += 0.005; + } + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_clipping.ts b/examples-testing/examples/webgpu_clipping.ts new file mode 100644 index 000000000..b7e94da98 --- /dev/null +++ b/examples-testing/examples/webgpu_clipping.ts @@ -0,0 +1,203 @@ +import * as THREE from 'three/webgpu'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let camera, scene, renderer, startTime, object; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(36, window.innerWidth / window.innerHeight, 0.25, 16); + + camera.position.set(0, 1.3, 3); + + scene = new THREE.Scene(); + + // Lights + + scene.add(new THREE.AmbientLight(0xcccccc)); + + const spotLight = new THREE.SpotLight(0xffffff, 60); + spotLight.angle = Math.PI / 5; + spotLight.penumbra = 0.2; + spotLight.position.set(2, 3, 3); + spotLight.castShadow = true; + spotLight.shadow.camera.near = 3; + spotLight.shadow.camera.far = 10; + spotLight.shadow.mapSize.width = 2048; + spotLight.shadow.mapSize.height = 2048; + spotLight.shadow.radius = 4; + scene.add(spotLight); + + const dirLight = new THREE.DirectionalLight(0x55505a, 3); + dirLight.position.set(0, 3, 0); + dirLight.castShadow = true; + dirLight.shadow.camera.near = 1; + dirLight.shadow.camera.far = 10; + + dirLight.shadow.camera.right = 1; + dirLight.shadow.camera.left = -1; + dirLight.shadow.camera.top = 1; + dirLight.shadow.camera.bottom = -1; + + dirLight.shadow.mapSize.width = 1024; + dirLight.shadow.mapSize.height = 1024; + scene.add(dirLight); + + // Clipping planes + + const globalPlane = new THREE.Plane(new THREE.Vector3(-1, 0, 0), 0.1); + const localPlane1 = new THREE.Plane(new THREE.Vector3(0, -1, 0), 0.8); + const localPlane2 = new THREE.Plane(new THREE.Vector3(0, 0, -1), 0.1); + + // Clipping Groups + + const globalClippingGroup = new THREE.ClippingGroup(); + globalClippingGroup.clippingPlanes = [globalPlane]; + + const knotClippingGroup = new THREE.ClippingGroup(); + knotClippingGroup.clippingPlanes = [localPlane1, localPlane2]; + knotClippingGroup.clipIntersection = true; + + scene.add(globalClippingGroup); + globalClippingGroup.add(knotClippingGroup); + + // Geometry + + const material = new THREE.MeshPhongNodeMaterial({ + color: 0x80ee10, + shininess: 0, + side: THREE.DoubleSide, + + // ***** Clipping setup (material): ***** + alphaToCoverage: true, + }); + + const geometry = new THREE.TorusKnotGeometry(0.4, 0.08, 95, 20); + + object = new THREE.Mesh(geometry, material); + object.castShadow = true; + knotClippingGroup.add(object); + + const ground = new THREE.Mesh( + new THREE.PlaneGeometry(9, 9, 1, 1), + new THREE.MeshPhongNodeMaterial({ color: 0xa0adaf, shininess: 150, alphaToCoverage: true }), + ); + + ground.rotation.x = -Math.PI / 2; // rotates X/Y to X/Z + ground.receiveShadow = true; + globalClippingGroup.add(ground); + + // Renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.shadowMap.enabled = true; + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + window.addEventListener('resize', onWindowResize); + document.body.appendChild(renderer.domElement); + + // Controls + + const controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, 1, 0); + controls.update(); + + // GUI + + const gui = renderer.inspector.createParameters('Clipping settings'); + const props = { + alphaToCoverage: true, + }; + + const folderKnot = gui.addFolder('Knot Clipping Group'); + const propsKnot = { + get Enabled() { + return knotClippingGroup.enabled; + }, + set Enabled(v) { + knotClippingGroup.enabled = v; + }, + + get Shadows() { + return knotClippingGroup.clipShadows; + }, + set Shadows(v) { + knotClippingGroup.clipShadows = v; + }, + + get Intersection() { + return knotClippingGroup.clipIntersection; + }, + + set Intersection(v) { + knotClippingGroup.clipIntersection = v; + }, + + get Plane() { + return localPlane1.constant; + }, + set Plane(v) { + localPlane1.constant = v; + }, + }; + + const folderGlobal = gui.addFolder('Global Clipping Group'); + const propsGlobal = { + get Enabled() { + return globalClippingGroup.enabled; + }, + set Enabled(v) { + globalClippingGroup.enabled = v; + }, + + get Plane() { + return globalPlane.constant; + }, + set Plane(v) { + globalPlane.constant = v; + }, + }; + + gui.add(props, 'alphaToCoverage').onChange(function (value) { + ground.material.alphaToCoverage = value; + ground.material.needsUpdate = true; + + material.alphaToCoverage = value; + material.needsUpdate = true; + }); + + folderKnot.add(propsKnot, 'Enabled'); + folderKnot.add(propsKnot, 'Shadows'); + folderKnot.add(propsKnot, 'Intersection'); + folderKnot.add(propsKnot, 'Plane', 0.3, 1.25); + + folderGlobal.add(propsGlobal, 'Enabled'); + folderGlobal.add(propsGlobal, 'Plane', -0.4, 3); + + // Start + + startTime = Date.now(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate(currentTime) { + const time = (currentTime - startTime) / 1000; + + object.position.y = 0.8; + object.rotation.x = time * 0.5; + object.rotation.y = time * 0.2; + object.scale.setScalar(Math.cos(time) * 0.125 + 0.875); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_compile_async.ts b/examples-testing/examples/webgpu_compile_async.ts new file mode 100644 index 000000000..fbcf0a7b0 --- /dev/null +++ b/examples-testing/examples/webgpu_compile_async.ts @@ -0,0 +1,265 @@ +import * as THREE from 'three/webgpu'; +import { + uv, + float, + vec3, + hash, + mx_noise_vec3, + mx_worley_noise_vec3, + mx_cell_noise_float, + mx_fractal_noise_vec3, +} from 'three/tsl'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let MESH_COUNT = 256; +let GRID_SIZE = 16; +const ADD_DELAY = 1000; + +let camera, scene, renderer; +let sphere; +let gui; + +// Frame timing +let lastFrameTime = 0; +let longestFrameTime = 0; +let isTracking = false; +let shouldStartTracking = false; +let sphereStartTime = 0; +let currentMode = ''; +let framesAfterComplete = 0; +let testDone = false; +let meshGroup = null; + +const longestFrameEl = document.getElementById('longestFrame'); +const meshCountEl = document.getElementById('meshCount'); +const compileModeEl = document.getElementById('compileMode'); + +const params = { + withoutCompile: function () { + window.location.href = window.location.pathname + '?mode=no-compile'; + }, + withCompileAsync: function () { + window.location.href = window.location.pathname + '?mode=compile-async'; + }, +}; + +init(); + +async function init() { + // GUI + gui = new GUI(); + gui.add(params, 'withoutCompile').name('Build on render'); + gui.add(params, 'withCompileAsync').name('Pre-build (compileAsync)'); + + window.addEventListener('resize', onWindowResize); + + // Orthographic camera + const aspect = window.innerWidth / window.innerHeight; + const frustumSize = 20; + camera = new THREE.OrthographicCamera( + (frustumSize * aspect) / -2, + (frustumSize * aspect) / 2, + frustumSize / 2, + frustumSize / -2, + 0.1, + 100, + ); + camera.position.set(0, 0, 20); + camera.lookAt(0, 0, 0); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x111111); + + // Create animated sphere + const sphereGeometry = new THREE.SphereGeometry(0.5, 32, 32); + const sphereMaterial = new THREE.MeshNormalMaterial(); + sphere = new THREE.Mesh(sphereGeometry, sphereMaterial); + scene.add(sphere); + + // Renderer + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + await renderer.init(); + + // Reduce mesh count for WebGL (slower compilation) + if (renderer.backend.isWebGLBackend) { + MESH_COUNT = 64; + GRID_SIZE = 8; + document.getElementById('materialCount').textContent = '64'; + meshCountEl.textContent = '0 / 64'; + } + + // Get mode from URL + const urlParams = new URLSearchParams(window.location.search); + const mode = urlParams.get('mode') || 'compile-async'; + + const modeLabel = mode === 'compile-async' ? 'Pre-build (compileAsync)' : 'Build on render'; + compileModeEl.textContent = modeLabel; + + // Start sphere animation + sphereStartTime = performance.now(); + + // Schedule mesh addition after delay + setTimeout(() => addMeshes(mode), ADD_DELAY); +} + +function createUniqueMaterial(index) { + const material = new THREE.MeshBasicNodeMaterial(); + + const seed = float(index * 0.1 + 1.0); + const scale = float((index % 5) + 2.0); + const uvNode = uv() + .mul(scale) + .add(hash(float(index))); + + const noiseType = index % 4; + let colorNode; + + switch (noiseType) { + case 0: + colorNode = mx_noise_vec3(uvNode.mul(seed)).mul(0.5).add(0.5); + break; + + case 1: + colorNode = mx_worley_noise_vec3(uvNode.mul(seed.mul(0.5))); + break; + + case 2: + const cellNoise = mx_cell_noise_float(uvNode.mul(seed)); + colorNode = vec3(cellNoise, cellNoise.mul(0.7), cellNoise.mul(0.4)); + break; + + case 3: + colorNode = mx_fractal_noise_vec3(uvNode.mul(seed.mul(0.3)), float(3), float(2.0), float(0.5)) + .mul(0.5) + .add(0.5); + break; + } + + const tintR = hash(float(index * 3)); + const tintG = hash(float(index * 3 + 1)); + const tintB = hash(float(index * 3 + 2)); + const tint = vec3(tintR, tintG, tintB).mul(0.3).add(0.7); + + material.colorNode = colorNode.mul(tint); + + return material; +} + +async function addMeshes(mode) { + const geometry = new THREE.PlaneGeometry(0.9, 0.9); + const startX = -(GRID_SIZE - 1) / 2; + const startY = -(GRID_SIZE - 1) / 2; + + meshGroup = new THREE.Group(); + + for (let i = 0; i < MESH_COUNT; i++) { + const material = createUniqueMaterial(i); + const mesh = new THREE.Mesh(geometry, material); + + const col = i % GRID_SIZE; + const row = Math.floor(i / GRID_SIZE); + + mesh.position.x = startX + col; + mesh.position.y = startY + row; + + meshGroup.add(mesh); + } + + currentMode = mode; + + if (mode === 'compile-async') { + // Pre-compile all meshes before adding to scene + // Start tracking BEFORE compile to measure longest frame during compile + shouldStartTracking = true; + + await renderer.compileAsync(meshGroup, camera, scene); + + // Add all meshes at once (already compiled - should render instantly) + scene.add(meshGroup); + testDone = true; + } else { + // Add all meshes at once - renderer compiles on-demand + // Meshes appear progressively as they compile + scene.add(meshGroup); + + // Start tracking on next animate() frame + shouldStartTracking = true; + testDone = true; + } +} + +function finishTest() { + isTracking = false; + + longestFrameEl.textContent = longestFrameTime.toFixed(1) + ' ms'; + longestFrameEl.className = longestFrameTime > 100 ? 'value bad' : 'value'; +} + +function onWindowResize() { + if (!renderer || !camera) return; + + const aspect = window.innerWidth / window.innerHeight; + const frustumSize = 12; + + camera.left = (frustumSize * aspect) / -2; + camera.right = (frustumSize * aspect) / 2; + camera.top = frustumSize / 2; + camera.bottom = frustumSize / -2; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + const now = performance.now(); + + // Initialize tracking on first frame after meshes added + if (shouldStartTracking) { + shouldStartTracking = false; + isTracking = true; + lastFrameTime = now; + longestFrameTime = 0; + framesAfterComplete = 0; + } + + // Track longest frame during test + if (isTracking && lastFrameTime > 0 && lastFrameTime !== now) { + const frameTime = now - lastFrameTime; + if (frameTime > longestFrameTime) { + longestFrameTime = frameTime; + } + } + + lastFrameTime = now; + + // Animate sphere left to right + if (sphere && sphereStartTime > 0) { + const elapsed = (now - sphereStartTime) / 1000; + sphere.position.x = Math.sin(elapsed * 2) * 8; + } + + renderer.render(scene, camera); + + // Update mesh count display + if (meshGroup) { + meshCountEl.textContent = meshGroup.children.length + ' / ' + MESH_COUNT; + } + + // Finish test - wait a few frames after testDone to capture frame times + if (isTracking && testDone) { + framesAfterComplete++; + + // Wait longer for deferred mode (meshes compile progressively) + const framesToWait = currentMode === 'compile-async' ? 2 : 60; + + if (framesAfterComplete >= framesToWait) { + finishTest(); + } + } +} diff --git a/examples-testing/examples/webgpu_compute_audio.ts b/examples-testing/examples/webgpu_compute_audio.ts new file mode 100644 index 000000000..229033d79 --- /dev/null +++ b/examples-testing/examples/webgpu_compute_audio.ts @@ -0,0 +1,178 @@ +import * as THREE from 'three/webgpu'; +import { Fn, uniform, instanceIndex, instancedArray, float, texture, screenUV, color } from 'three/tsl'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer; +let computeNode; +let waveBuffer, sampleRate; +let waveArray; +let currentAudio, currentAnalyser; +const analyserBuffer = new Uint8Array(1024); +let analyserTexture; + +const startButton = document.getElementById('startButton'); +startButton.addEventListener('click', init); + +async function playAudioBuffer() { + if (currentAudio) currentAudio.stop(); + + // compute audio + + renderer.compute(computeNode); + + const wave = new Float32Array(await renderer.getArrayBufferAsync(waveArray.value)); + + // play result + + const audioOutputContext = new AudioContext({ sampleRate }); + const audioOutputBuffer = audioOutputContext.createBuffer(1, wave.length, sampleRate); + + audioOutputBuffer.copyToChannel(wave, 0); + + const source = audioOutputContext.createBufferSource(); + source.connect(audioOutputContext.destination); + source.buffer = audioOutputBuffer; + source.start(); + + currentAudio = source; + + // visual feedback + + currentAnalyser = audioOutputContext.createAnalyser(); + currentAnalyser.fftSize = 2048; + + source.connect(currentAnalyser); +} + +async function init() { + const overlay = document.getElementById('overlay'); + overlay.remove(); + + // audio buffer + + const soundBuffer = await fetch('sounds/webgpu-audio-processing.mp3').then(res => res.arrayBuffer()); + const audioContext = new AudioContext(); + + const audioBuffer = await audioContext.decodeAudioData(soundBuffer); + + waveBuffer = audioBuffer.getChannelData(0); + + // adding extra silence to delay and pitch + waveBuffer = new Float32Array([...waveBuffer, ...new Float32Array(200000)]); + + sampleRate = audioBuffer.sampleRate / audioBuffer.numberOfChannels; + + // create webgpu buffers + + waveArray = instancedArray(waveBuffer); + + // read-only buffer + + const originalWave = instancedArray(waveBuffer).toReadOnly(); + + // The Pixel Buffer Object (PBO) is required to get the GPU computed data to the CPU in the WebGL2 fallback. + // As used in `renderer.getArrayBufferAsync( waveArray.value )`. + + originalWave.setPBO(true); + waveArray.setPBO(true); + + // params + + const pitch = uniform(1.5); + const delayVolume = uniform(0.2); + const delayOffset = uniform(0.55); + + // compute (shader-node) + + const computeShaderFn = Fn(() => { + const index = float(instanceIndex); + + // pitch + + const time = index.mul(pitch); + + let wave = originalWave.element(time); + + // delay + + for (let i = 1; i < 7; i++) { + const waveOffset = originalWave.element(index.sub(delayOffset.mul(sampleRate).mul(i)).mul(pitch)); + const waveOffsetVolume = waveOffset.mul(delayVolume.div(i * i)); + + wave = wave.add(waveOffsetVolume); + } + + // store + + const waveStorageElementNode = waveArray.element(instanceIndex); + + waveStorageElementNode.assign(wave); + }); + + // compute + + computeNode = computeShaderFn().compute(waveBuffer.length); + + // renderer + + const container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.01, 30); + + // nodes + + analyserTexture = new THREE.DataTexture(analyserBuffer, analyserBuffer.length, 1, THREE.RedFormat); + + const spectrum = texture(analyserTexture, screenUV.x).x.mul(screenUV.y); + const backgroundNode = color(0x0000ff).mul(spectrum); + + // scene + + scene = new THREE.Scene(); + scene.backgroundNode = backgroundNode; + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(render); + renderer.inspector = new Inspector(); + container.appendChild(renderer.domElement); + + await renderer.init(); + + window.addEventListener('resize', onWindowResize); + document.addEventListener('click', playAudioBuffer); + + // gui + + const gui = renderer.inspector.createParameters('Audio'); + + gui.add(pitch, 'value', 0.5, 2, 0.01).name('pitch'); + gui.add(delayVolume, 'value', 0, 1, 0.01).name('delayVolume'); + gui.add(delayOffset, 'value', 0.1, 1, 0.01).name('delayOffset'); + + // + + playAudioBuffer(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function render() { + if (currentAnalyser) { + currentAnalyser.getByteFrequencyData(analyserBuffer); + + analyserTexture.needsUpdate = true; + } + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_compute_birds.ts b/examples-testing/examples/webgpu_compute_birds.ts new file mode 100644 index 000000000..665a54528 --- /dev/null +++ b/examples-testing/examples/webgpu_compute_birds.ts @@ -0,0 +1,424 @@ +import * as THREE from 'three/webgpu'; +import { + uniform, + varying, + vec4, + add, + sub, + max, + dot, + sin, + mat3, + uint, + negate, + instancedArray, + cameraProjectionMatrix, + cameraViewMatrix, + positionLocal, + modelWorldMatrix, + sqrt, + float, + Fn, + If, + cos, + Loop, + Continue, + normalize, + instanceIndex, + length, + vertexIndex, +} from 'three/tsl'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import WebGPU from 'three/addons/capabilities/WebGPU.js'; + +let container; +let camera, scene, renderer; + +let last = performance.now(); + +let pointer, raycaster; +let computeVelocity, computePosition, effectController; + +const BIRDS = 8192; +const SPEED_LIMIT = 9.0; +const BOUNDS = 800, + BOUNDS_HALF = BOUNDS / 2; + +// Custom Geometry - using 3 triangles each. No normals currently. +class BirdGeometry extends THREE.BufferGeometry { + constructor() { + super(); + + const points = 3 * 3; + + const vertices = new THREE.BufferAttribute(new Float32Array(points * 3), 3); + + this.setAttribute('position', vertices); + + let v = 0; + + function verts_push() { + for (let i = 0; i < arguments.length; i++) { + vertices.array[v++] = arguments[i]; + } + } + + const wingsSpan = 20; + + // Body + verts_push(0, 0, -20, 0, -8, 10, 0, 0, 30); + + // Left Wing + verts_push(0, 0, -15, -wingsSpan, 0, 5, 0, 0, 15); + + // Right Wing + verts_push(0, 0, 15, wingsSpan, 0, 5, 0, 0, -15); + + this.scale(0.2, 0.2, 0.2); + } +} + +// TODO: Fix example with WebGL backend + +if (WebGPU.isAvailable() === false) { + document.body.appendChild(WebGPU.getErrorMessage()); + + throw new Error('No WebGPU support'); +} + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 5000); + camera.position.z = 1000; + + scene = new THREE.Scene(); + scene.fog = new THREE.Fog(0xffffff, 700, 3000); + + // Pointer + + pointer = new THREE.Vector2(); + raycaster = new THREE.Raycaster(); + + // Sky + + const geometry = new THREE.IcosahedronGeometry(1, 6); + const material = new THREE.MeshBasicNodeMaterial({ + // Use vertex positions to create atmosphere colors + colorNode: varying( + vec4(sub(0.25, positionLocal.y), sub(-0.25, positionLocal.y), add(1.5, positionLocal.y), 1.0), + ), + side: THREE.BackSide, + }); + + const mesh = new THREE.Mesh(geometry, material); + mesh.rotation.z = 0.75; + mesh.scale.setScalar(1200); + scene.add(mesh); + + // + + renderer = new THREE.WebGPURenderer({ + antialias: true, + forceWebGL: false, + requiredLimits: { maxStorageBuffersInVertexStage: 3 }, + }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(render); + renderer.toneMapping = THREE.NeutralToneMapping; + renderer.inspector = new Inspector(); + container.appendChild(renderer.domElement); + + const controls = new OrbitControls(camera); + controls.connect(renderer.domElement); + + // Initialize position, velocity, and phase values + + const positionArray = new Float32Array(BIRDS * 3); + const velocityArray = new Float32Array(BIRDS * 3); + const phaseArray = new Float32Array(BIRDS); + + for (let i = 0; i < BIRDS; i++) { + const posX = Math.random() * BOUNDS - BOUNDS_HALF; + const posY = Math.random() * BOUNDS - BOUNDS_HALF; + const posZ = Math.random() * BOUNDS - BOUNDS_HALF; + + positionArray[i * 3 + 0] = posX; + positionArray[i * 3 + 1] = posY; + positionArray[i * 3 + 2] = posZ; + + const velX = Math.random() - 0.5; + const velY = Math.random() - 0.5; + const velZ = Math.random() - 0.5; + + velocityArray[i * 3 + 0] = velX * 10; + velocityArray[i * 3 + 1] = velY * 10; + velocityArray[i * 3 + 2] = velZ * 10; + + phaseArray[i] = 1; + } + + // Labels applied to storage nodes and uniform nodes are reflected within the shader output, + // and are useful for debugging purposes. + + const positionStorage = instancedArray(positionArray, 'vec3').setName('positionStorage'); + const velocityStorage = instancedArray(velocityArray, 'vec3').setName('velocityStorage'); + const phaseStorage = instancedArray(phaseArray, 'float').setName('phaseStorage'); + + // The Pixel Buffer Object (PBO) is required to get the GPU computed data in the WebGL2 fallback. + + positionStorage.setPBO(true); + velocityStorage.setPBO(true); + phaseStorage.setPBO(true); + + // Define Uniforms. Uniforms only need to be defined once rather than per shader. + + effectController = { + separation: uniform(15.0).setName('separation'), + alignment: uniform(20.0).setName('alignment'), + cohesion: uniform(20.0).setName('cohesion'), + freedom: uniform(0.75).setName('freedom'), + now: uniform(0.0), + deltaTime: uniform(0.0).setName('deltaTime'), + rayOrigin: uniform(new THREE.Vector3()).setName('rayOrigin'), + rayDirection: uniform(new THREE.Vector3()).setName('rayDirection'), + }; + + // Create geometry + + const birdGeometry = new BirdGeometry(); + const birdMaterial = new THREE.NodeMaterial(); + + // Animate bird mesh within vertex shader, then apply position offset to vertices. + + const birdVertexTSL = Fn(() => { + const position = positionLocal.toVar(); + const newPhase = phaseStorage.element(instanceIndex).toVar(); + const newVelocity = normalize(velocityStorage.element(instanceIndex)).toVar(); + + If(vertexIndex.equal(4).or(vertexIndex.equal(7)), () => { + // flap wings + position.y = sin(newPhase).mul(5.0); + }); + + const newPosition = modelWorldMatrix.mul(position); + + newVelocity.z.mulAssign(-1.0); + const xz = length(newVelocity.xz); + const xyz = float(1.0); + const x = sqrt(newVelocity.y.mul(newVelocity.y).oneMinus()); + + const cosry = newVelocity.x.div(xz).toVar(); + const sinry = newVelocity.z.div(xz).toVar(); + + const cosrz = x.div(xyz); + const sinrz = newVelocity.y.div(xyz).toVar(); + + // Nodes must be negated with negate(). Using '-', their values will resolve to NaN. + const maty = mat3(cosry, 0, negate(sinry), 0, 1, 0, sinry, 0, cosry); + + const matz = mat3(cosrz, sinrz, 0, negate(sinrz), cosrz, 0, 0, 0, 1); + + const finalVert = maty.mul(matz).mul(newPosition); + finalVert.addAssign(positionStorage.element(instanceIndex)); + + return cameraProjectionMatrix.mul(cameraViewMatrix).mul(finalVert); + }); + + birdMaterial.vertexNode = birdVertexTSL(); + birdMaterial.side = THREE.DoubleSide; + + const birdMesh = new THREE.InstancedMesh(birdGeometry, birdMaterial, BIRDS); + birdMesh.rotation.y = Math.PI / 2; + birdMesh.matrixAutoUpdate = false; + birdMesh.frustumCulled = false; + birdMesh.updateMatrix(); + + // Define GPU Compute shaders. + // Shaders are computationally identical to their GLSL counterparts outside of texture destructuring. + + computeVelocity = Fn(() => { + // Define consts + const PI = float(3.141592653589793); + const PI_2 = PI.mul(2.0); + const limit = float(SPEED_LIMIT).toVar('limit'); + + // Destructure uniforms + const { alignment, separation, cohesion, deltaTime, rayOrigin, rayDirection } = effectController; + + const zoneRadius = separation.add(alignment).add(cohesion).toConst(); + const separationThresh = separation.div(zoneRadius).toConst(); + const alignmentThresh = separation.add(alignment).div(zoneRadius).toConst(); + const zoneRadiusSq = zoneRadius.mul(zoneRadius).toConst(); + + // Cache current bird's position and velocity outside the loop + const birdIndex = instanceIndex.toConst('birdIndex'); + const position = positionStorage.element(birdIndex).toVar(); + const velocity = velocityStorage.element(birdIndex).toVar(); + + // Add influence of pointer position to velocity using cached position + const directionToRay = rayOrigin.sub(position).toConst(); + const projectionLength = dot(directionToRay, rayDirection).toConst(); + const closestPoint = rayOrigin.sub(rayDirection.mul(projectionLength)).toConst(); + const directionToClosestPoint = closestPoint.sub(position).toConst(); + const distanceToClosestPoint = length(directionToClosestPoint).toConst(); + const distanceToClosestPointSq = distanceToClosestPoint.mul(distanceToClosestPoint).toConst(); + + const rayRadius = float(150.0).toConst(); + const rayRadiusSq = rayRadius.mul(rayRadius).toConst(); + + If(distanceToClosestPointSq.lessThan(rayRadiusSq), () => { + const velocityAdjust = distanceToClosestPointSq.div(rayRadiusSq).sub(1.0).mul(deltaTime).mul(100.0); + velocity.addAssign(normalize(directionToClosestPoint).mul(velocityAdjust)); + limit.addAssign(5.0); + }); + + // Attract flocks to center + const dirToCenter = position.toVar(); + dirToCenter.y.mulAssign(2.5); + velocity.subAssign(normalize(dirToCenter).mul(deltaTime).mul(5.0)); + + Loop({ start: uint(0), end: uint(BIRDS), type: 'uint', condition: '<' }, ({ i }) => { + If(i.equal(birdIndex), () => { + Continue(); + }); + + // Cache bird's position and velocity + + const birdPosition = positionStorage.element(i); + const dirToBird = birdPosition.sub(position); + const distToBird = length(dirToBird); + + If(distToBird.lessThan(0.0001), () => { + Continue(); + }); + + const distToBirdSq = distToBird.mul(distToBird); + + // Don't apply any changes to velocity if changes if the bird is outsize the zone's radius. + If(distToBirdSq.greaterThan(zoneRadiusSq), () => { + Continue(); + }); + + // Determine which threshold the bird is flying within and adjust its velocity accordingly + + const percent = distToBirdSq.div(zoneRadiusSq); + + If(percent.lessThan(separationThresh), () => { + // Separation - Move apart for comfort + const velocityAdjust = separationThresh.div(percent).sub(1.0).mul(deltaTime); + velocity.subAssign(normalize(dirToBird).mul(velocityAdjust)); + }) + .ElseIf(percent.lessThan(alignmentThresh), () => { + // Alignment - fly the same direction + const threshDelta = alignmentThresh.sub(separationThresh); + const adjustedPercent = percent.sub(separationThresh).div(threshDelta); + const birdVelocity = velocityStorage.element(i); + + const cosRange = cos(adjustedPercent.mul(PI_2)); + const cosRangeAdjust = float(0.5).sub(cosRange.mul(0.5)).add(0.5); + const velocityAdjust = cosRangeAdjust.mul(deltaTime); + velocity.addAssign(normalize(birdVelocity).mul(velocityAdjust)); + }) + .Else(() => { + // Attraction / Cohesion - move closer + const threshDelta = alignmentThresh.oneMinus(); + const adjustedPercent = threshDelta + .equal(0.0) + .select(1.0, percent.sub(alignmentThresh).div(threshDelta)); + + const cosRange = cos(adjustedPercent.mul(PI_2)); + const adj1 = cosRange.mul(-0.5); + const adj2 = adj1.add(0.5); + const adj3 = float(0.5).sub(adj2); + + const velocityAdjust = adj3.mul(deltaTime); + velocity.addAssign(normalize(dirToBird).mul(velocityAdjust)); + }); + }); + + If(length(velocity).greaterThan(limit), () => { + velocity.assign(normalize(velocity).mul(limit)); + }); + + // Write back the final velocity to storage + velocityStorage.element(birdIndex).assign(velocity); + })() + .compute(BIRDS) + .setName('Birds Velocity'); + + computePosition = Fn(() => { + const { deltaTime } = effectController; + positionStorage + .element(instanceIndex) + .addAssign(velocityStorage.element(instanceIndex).mul(deltaTime).mul(15.0)); + + const velocity = velocityStorage.element(instanceIndex); + const phase = phaseStorage.element(instanceIndex); + + const modValue = phase + .add(deltaTime) + .add(length(velocity.xz).mul(deltaTime).mul(3.0)) + .add(max(velocity.y, 0.0).mul(deltaTime).mul(6.0)); + phaseStorage.element(instanceIndex).assign(modValue.mod(62.83)); + })() + .compute(BIRDS) + .setName('Birds Position'); + + scene.add(birdMesh); + + container.style.touchAction = 'none'; + container.addEventListener('pointermove', onPointerMove); + + window.addEventListener('resize', onWindowResize); + + const gui = renderer.inspector.createParameters('Birds settings'); + gui.add(effectController.separation, 'value', 0.0, 100.0, 1.0).name('Separation'); + gui.add(effectController.alignment, 'value', 0.0, 100, 0.001).name('Alignment '); + gui.add(effectController.cohesion, 'value', 0.0, 100, 0.025).name('Cohesion'); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function onPointerMove(event) { + if (event.isPrimary === false) return; + + pointer.x = (event.clientX / window.innerWidth) * 2.0 - 1.0; + pointer.y = 1.0 - (event.clientY / window.innerHeight) * 2.0; +} + +function render() { + const now = performance.now(); + let deltaTime = (now - last) / 1000; + + if (deltaTime > 1) deltaTime = 1; // safety cap on large deltas + last = now; + + raycaster.setFromCamera(pointer, camera); + + effectController.now.value = now; + effectController.deltaTime.value = deltaTime; + effectController.rayOrigin.value.copy(raycaster.ray.origin); + effectController.rayDirection.value.copy(raycaster.ray.direction); + + renderer.compute(computeVelocity); + renderer.compute(computePosition); + + renderer.render(scene, camera); + + // Move pointer away so we only affect birds when moving the mouse + pointer.y = 10; +} + +init(); diff --git a/examples-testing/examples/webgpu_compute_cloth.ts b/examples-testing/examples/webgpu_compute_cloth.ts new file mode 100644 index 000000000..c18af53f2 --- /dev/null +++ b/examples-testing/examples/webgpu_compute_cloth.ts @@ -0,0 +1,487 @@ +import * as THREE from 'three/webgpu'; + +import { + Fn, + If, + Return, + instancedArray, + instanceIndex, + uniform, + select, + attribute, + Loop, + float, + transformNormalToView, + cross, + triNoise3D, + time, +} from 'three/tsl'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; +import WebGPU from 'three/addons/capabilities/WebGPU.js'; + +let renderer, scene, camera, controls; + +const clothWidth = 1; +const clothHeight = 1; +const clothNumSegmentsX = 30; +const clothNumSegmentsY = 30; +const sphereRadius = 0.15; + +let vertexPositionBuffer, vertexForceBuffer, vertexParamsBuffer; +let springVertexIdBuffer, springRestLengthBuffer, springForceBuffer; +let springListBuffer; +let computeSpringForces, computeVertexForces; +let dampeningUniform, spherePositionUniform, stiffnessUniform, sphereUniform, windUniform; +let vertexWireframeObject, springWireframeObject; +let clothMesh, clothMaterial, sphere; +let timeSinceLastStep = 0; +let timestamp = 0; +const verletVertices = []; +const verletSprings = []; +const verletVertexColumns = []; + +const timer = new THREE.Timer(); +timer.connect(document); + +const params = { + wireframe: false, + sphere: true, + wind: 1.0, +}; + +const API = { + color: 0x204080, // sRGB + sheenColor: 0xffffff, // sRGB +}; + +// TODO: Fix example with WebGL backend + +if (WebGPU.isAvailable() === false) { + document.body.appendChild(WebGPU.getErrorMessage()); + + throw new Error('No WebGPU support'); +} + +init(); + +async function init() { + renderer = new THREE.WebGPURenderer({ antialias: true, requiredLimits: { maxStorageBuffersInVertexStage: 1 } }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.toneMapping = THREE.NeutralToneMapping; + renderer.toneMappingExposure = 1; + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.01, 10); + camera.position.set(-1.6, -0.1, -1.6); + + controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 1; + controls.maxDistance = 3; + controls.target.set(0, -0.1, 0); + controls.update(); + + const hdrLoader = new UltraHDRLoader().setPath('textures/equirectangular/'); + + const hdrTexture = await hdrLoader.loadAsync('royal_esplanade_2k.hdr.jpg'); + hdrTexture.mapping = THREE.EquirectangularReflectionMapping; + scene.background = hdrTexture; + scene.backgroundBlurriness = 0.5; + scene.environment = hdrTexture; + + setupCloth(); + + const gui = renderer.inspector.createParameters('Settings'); + gui.add(stiffnessUniform, 'value', 0.1, 0.5, 0.01).name('stiffness'); + gui.add(params, 'wireframe'); + gui.add(params, 'sphere'); + gui.add(params, 'wind', 0, 5, 0.1); + + const materialFolder = gui.addFolder('material'); + materialFolder.addColor(API, 'color').onChange(function (color) { + clothMaterial.color.setHex(color); + }); + materialFolder.add(clothMaterial, 'roughness', 0.0, 1, 0.01); + materialFolder.add(clothMaterial, 'sheen', 0.0, 1, 0.01); + materialFolder.add(clothMaterial, 'sheenRoughness', 0.0, 1, 0.01); + materialFolder.addColor(API, 'sheenColor').onChange(function (color) { + clothMaterial.sheenColor.setHex(color); + }); + + window.addEventListener('resize', onWindowResize); + + renderer.setAnimationLoop(render); +} + +function setupVerletGeometry() { + // this function sets up the geometry of the verlet system, a grid of vertices connected by springs + + const addVerletVertex = (x, y, z, isFixed) => { + const id = verletVertices.length; + const vertex = { + id, + position: new THREE.Vector3(x, y, z), + isFixed, + springIds: [], + }; + verletVertices.push(vertex); + return vertex; + }; + + const addVerletSpring = (vertex0, vertex1) => { + const id = verletSprings.length; + const spring = { + id, + vertex0, + vertex1, + }; + vertex0.springIds.push(id); + vertex1.springIds.push(id); + verletSprings.push(spring); + return spring; + }; + + // create the cloth's verlet vertices + for (let x = 0; x <= clothNumSegmentsX; x++) { + const column = []; + for (let y = 0; y <= clothNumSegmentsY; y++) { + const posX = x * (clothWidth / clothNumSegmentsX) - clothWidth * 0.5; + const posZ = y * (clothHeight / clothNumSegmentsY); + const isFixed = y === 0 && x % 5 === 0; // make some of the top vertices' positions fixed + const vertex = addVerletVertex(posX, clothHeight * 0.5, posZ, isFixed); + column.push(vertex); + } + + verletVertexColumns.push(column); + } + + // create the cloth's verlet springs + for (let x = 0; x <= clothNumSegmentsX; x++) { + for (let y = 0; y <= clothNumSegmentsY; y++) { + const vertex0 = verletVertexColumns[x][y]; + if (x > 0) addVerletSpring(vertex0, verletVertexColumns[x - 1][y]); + if (y > 0) addVerletSpring(vertex0, verletVertexColumns[x][y - 1]); + if (x > 0 && y > 0) addVerletSpring(vertex0, verletVertexColumns[x - 1][y - 1]); + if (x > 0 && y < clothNumSegmentsY) addVerletSpring(vertex0, verletVertexColumns[x - 1][y + 1]); + + // You can make the cloth more rigid by adding more springs between further apart vertices + //if (x > 1) addVerletSpring(vertex0, verletVertexColumns[x - 2][y]); + //if (y > 1) addVerletSpring(vertex0, verletVertexColumns[x][y - 2]); + } + } +} + +function setupVerletVertexBuffers() { + // setup the buffers holding the vertex data for the compute shaders + + const vertexCount = verletVertices.length; + + const springListArray = []; + // this springListArray will hold a list of spring ids, ordered by the id of the vertex affected by that spring. + // this is so the compute shader that accumulates the spring forces for each vertex can efficiently iterate over all springs affecting that vertex + + const vertexPositionArray = new Float32Array(vertexCount * 3); + const vertexParamsArray = new Uint32Array(vertexCount * 3); + // the params Array holds three values for each verlet vertex: + // x: isFixed, y: springCount, z: springPointer + // isFixed is 1 if the verlet is marked as immovable, 0 if not + // springCount is the number of springs connected to that vertex + // springPointer is the index of the first spring in the springListArray that is connected to that vertex + + for (let i = 0; i < vertexCount; i++) { + const vertex = verletVertices[i]; + vertexPositionArray[i * 3] = vertex.position.x; + vertexPositionArray[i * 3 + 1] = vertex.position.y; + vertexPositionArray[i * 3 + 2] = vertex.position.z; + vertexParamsArray[i * 3] = vertex.isFixed ? 1 : 0; + if (!vertex.isFixed) { + vertexParamsArray[i * 3 + 1] = vertex.springIds.length; + vertexParamsArray[i * 3 + 2] = springListArray.length; + springListArray.push(...vertex.springIds); + } + } + + vertexPositionBuffer = instancedArray(vertexPositionArray, 'vec3').setPBO(true); // setPBO(true) is only important for the WebGL Fallback + vertexForceBuffer = instancedArray(vertexCount, 'vec3'); + vertexParamsBuffer = instancedArray(vertexParamsArray, 'uvec3'); + + springListBuffer = instancedArray(new Uint32Array(springListArray), 'uint').setPBO(true); +} + +function setupVerletSpringBuffers() { + // setup the buffers holding the spring data for the compute shaders + + const springCount = verletSprings.length; + + const springVertexIdArray = new Uint32Array(springCount * 2); + const springRestLengthArray = new Float32Array(springCount); + + for (let i = 0; i < springCount; i++) { + const spring = verletSprings[i]; + springVertexIdArray[i * 2] = spring.vertex0.id; + springVertexIdArray[i * 2 + 1] = spring.vertex1.id; + springRestLengthArray[i] = spring.vertex0.position.distanceTo(spring.vertex1.position); + } + + springVertexIdBuffer = instancedArray(springVertexIdArray, 'uvec2').setPBO(true); + springRestLengthBuffer = instancedArray(springRestLengthArray, 'float'); + springForceBuffer = instancedArray(springCount * 3, 'vec3').setPBO(true); +} + +function setupUniforms() { + dampeningUniform = uniform(0.99); + spherePositionUniform = uniform(new THREE.Vector3(0, 0, 0)); + sphereUniform = uniform(1.0); + windUniform = uniform(1.0); + stiffnessUniform = uniform(0.2); +} + +function setupComputeShaders() { + // This function sets up the compute shaders for the verlet simulation + // There are two shaders that are executed for each simulation step + + const vertexCount = verletVertices.length; + const springCount = verletSprings.length; + + // 1. computeSpringForces: + // This shader computes a force for each spring, depending on the distance between the two vertices connected by that spring and the targeted rest length + computeSpringForces = Fn(() => { + const vertexIds = springVertexIdBuffer.element(instanceIndex); + const restLength = springRestLengthBuffer.element(instanceIndex); + + const vertex0Position = vertexPositionBuffer.element(vertexIds.x); + const vertex1Position = vertexPositionBuffer.element(vertexIds.y); + + const delta = vertex1Position.sub(vertex0Position).toVar(); + const dist = delta.length().max(0.000001).toVar(); + const force = dist.sub(restLength).mul(stiffnessUniform).mul(delta).mul(0.5).div(dist); + springForceBuffer.element(instanceIndex).assign(force); + })() + .compute(springCount) + .setName('Spring Forces'); + + // 2. computeVertexForces: + // This shader accumulates the force for each vertex. + // First it iterates over all springs connected to this vertex and accumulates their forces. + // Then it adds a gravital force, wind force, and the collision with the sphere. + // In the end it adds the force to the vertex' position. + computeVertexForces = Fn(() => { + const params = vertexParamsBuffer.element(instanceIndex).toVar(); + const isFixed = params.x; + const springCount = params.y; + const springPointer = params.z; + + If(isFixed, () => { + // don't need to calculate vertex forces if the vertex is set as immovable + Return(); + }); + + const position = vertexPositionBuffer.element(instanceIndex).toVar('vertexPosition'); + const force = vertexForceBuffer.element(instanceIndex).toVar('vertexForce'); + + force.mulAssign(dampeningUniform); + + const ptrStart = springPointer.toVar('ptrStart'); + const ptrEnd = ptrStart.add(springCount).toVar('ptrEnd'); + + Loop({ start: ptrStart, end: ptrEnd, type: 'uint', condition: '<' }, ({ i }) => { + const springId = springListBuffer.element(i).toVar('springId'); + const springForce = springForceBuffer.element(springId); + const springVertexIds = springVertexIdBuffer.element(springId); + const factor = select(springVertexIds.x.equal(instanceIndex), 1.0, -1.0); + force.addAssign(springForce.mul(factor)); + }); + + // gravity + force.y.subAssign(0.00005); + + // wind + const noise = triNoise3D(position, 1, time).sub(0.2).mul(0.0001); + const windForce = noise.mul(windUniform); + force.z.subAssign(windForce); + + // collision with sphere + const deltaSphere = position.add(force).sub(spherePositionUniform); + const dist = deltaSphere.length(); + const sphereForce = float(sphereRadius).sub(dist).max(0).mul(deltaSphere).div(dist).mul(sphereUniform); + force.addAssign(sphereForce); + + vertexForceBuffer.element(instanceIndex).assign(force); + vertexPositionBuffer.element(instanceIndex).addAssign(force); + })() + .compute(vertexCount) + .setName('Vertex Forces'); +} + +function setupWireframe() { + // adds helpers to visualize the verlet system + + // verlet vertex visualizer + const vertexWireframeMaterial = new THREE.SpriteNodeMaterial(); + vertexWireframeMaterial.positionNode = vertexPositionBuffer.element(instanceIndex); + vertexWireframeObject = new THREE.Mesh(new THREE.PlaneGeometry(0.01, 0.01), vertexWireframeMaterial); + vertexWireframeObject.frustumCulled = false; + vertexWireframeObject.count = verletVertices.length; + scene.add(vertexWireframeObject); + + // verlet spring visualizer + const springWireframePositionBuffer = new THREE.BufferAttribute(new Float32Array(6), 3, false); + const springWireframeIndexBuffer = new THREE.BufferAttribute(new Uint32Array([0, 1]), 1, false); + const springWireframeMaterial = new THREE.LineBasicNodeMaterial(); + springWireframeMaterial.positionNode = Fn(() => { + const vertexIds = springVertexIdBuffer.element(instanceIndex); + const vertexId = select(attribute('vertexIndex').equal(0), vertexIds.x, vertexIds.y); + return vertexPositionBuffer.element(vertexId); + })(); + + const springWireframeGeometry = new THREE.InstancedBufferGeometry(); + springWireframeGeometry.setAttribute('position', springWireframePositionBuffer); + springWireframeGeometry.setAttribute('vertexIndex', springWireframeIndexBuffer); + springWireframeGeometry.instanceCount = verletSprings.length; + + springWireframeObject = new THREE.Line(springWireframeGeometry, springWireframeMaterial); + springWireframeObject.frustumCulled = false; + springWireframeObject.count = verletSprings.length; + scene.add(springWireframeObject); +} + +function setupSphere() { + const geometry = new THREE.IcosahedronGeometry(sphereRadius * 0.95, 4); + const material = new THREE.MeshStandardNodeMaterial(); + sphere = new THREE.Mesh(geometry, material); + scene.add(sphere); +} + +function setupClothMesh() { + // This function generates a three Geometry and Mesh to render the cloth based on the verlet systems position data. + // Therefore it creates a plane mesh, in which each vertex will be centered in the center of 4 verlet vertices. + + const vertexCount = clothNumSegmentsX * clothNumSegmentsY; + const geometry = new THREE.BufferGeometry(); + + // verletVertexIdArray will hold the 4 verlet vertex ids that contribute to each geometry vertex's position + const verletVertexIdArray = new Uint32Array(vertexCount * 4); + const indices = []; + + const getIndex = (x, y) => { + return y * clothNumSegmentsX + x; + }; + + for (let x = 0; x < clothNumSegmentsX; x++) { + for (let y = 0; y < clothNumSegmentsX; y++) { + const index = getIndex(x, y); + verletVertexIdArray[index * 4] = verletVertexColumns[x][y].id; + verletVertexIdArray[index * 4 + 1] = verletVertexColumns[x + 1][y].id; + verletVertexIdArray[index * 4 + 2] = verletVertexColumns[x][y + 1].id; + verletVertexIdArray[index * 4 + 3] = verletVertexColumns[x + 1][y + 1].id; + + if (x > 0 && y > 0) { + indices.push(getIndex(x, y), getIndex(x - 1, y), getIndex(x - 1, y - 1)); + indices.push(getIndex(x, y), getIndex(x - 1, y - 1), getIndex(x, y - 1)); + } + } + } + + const verletVertexIdBuffer = new THREE.BufferAttribute(verletVertexIdArray, 4, false); + const positionBuffer = new THREE.BufferAttribute(new Float32Array(vertexCount * 3), 3, false); + geometry.setAttribute('position', positionBuffer); + geometry.setAttribute('vertexIds', verletVertexIdBuffer); + geometry.setIndex(indices); + + clothMaterial = new THREE.MeshPhysicalNodeMaterial({ + color: new THREE.Color().setHex(API.color), + side: THREE.DoubleSide, + transparent: true, + opacity: 0.85, + sheen: 1.0, + sheenRoughness: 0.5, + sheenColor: new THREE.Color().setHex(API.sheenColor), + }); + + clothMaterial.positionNode = Fn(({ material }) => { + // gather the position of the 4 verlet vertices and calculate the center position and normal from that + const vertexIds = attribute('vertexIds'); + const v0 = vertexPositionBuffer.element(vertexIds.x).toVar(); + const v1 = vertexPositionBuffer.element(vertexIds.y).toVar(); + const v2 = vertexPositionBuffer.element(vertexIds.z).toVar(); + const v3 = vertexPositionBuffer.element(vertexIds.w).toVar(); + + const top = v0.add(v1); + const right = v1.add(v3); + const bottom = v2.add(v3); + const left = v0.add(v2); + + const tangent = right.sub(left).normalize(); + const bitangent = bottom.sub(top).normalize(); + + const normal = cross(tangent, bitangent); + + // send the normalView from the vertex shader to the fragment shader + material.normalNode = transformNormalToView(normal).toVarying(); + + return v0.add(v1).add(v2).add(v3).mul(0.25); + })(); + + clothMesh = new THREE.Mesh(geometry, clothMaterial); + clothMesh.frustumCulled = false; + scene.add(clothMesh); +} + +function setupCloth() { + setupVerletGeometry(); + setupVerletVertexBuffers(); + setupVerletSpringBuffers(); + setupUniforms(); + setupComputeShaders(); + setupWireframe(); + setupSphere(); + setupClothMesh(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function updateSphere() { + sphere.position.set(Math.sin(timestamp * 2.1) * 0.1, 0, Math.sin(timestamp * 0.8)); + spherePositionUniform.value.copy(sphere.position); +} + +async function render() { + timer.update(); + + sphere.visible = params.sphere; + sphereUniform.value = params.sphere ? 1 : 0; + windUniform.value = params.wind; + clothMesh.visible = !params.wireframe; + vertexWireframeObject.visible = params.wireframe; + springWireframeObject.visible = params.wireframe; + + const deltaTime = Math.min(timer.getDelta(), 1 / 60); // don't advance the time too far, for example when the window is out of focus + const stepsPerSecond = 360; // ensure the same amount of simulation steps per second on all systems, independent of refresh rate + const timePerStep = 1 / stepsPerSecond; + + timeSinceLastStep += deltaTime; + + while (timeSinceLastStep >= timePerStep) { + // run a verlet system simulation step + timestamp += timePerStep; + timeSinceLastStep -= timePerStep; + updateSphere(); + renderer.compute(computeSpringForces); + renderer.compute(computeVertexForces); + } + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_compute_geometry.ts b/examples-testing/examples/webgpu_compute_geometry.ts new file mode 100644 index 000000000..9299e215b --- /dev/null +++ b/examples-testing/examples/webgpu_compute_geometry.ts @@ -0,0 +1,190 @@ +import * as THREE from 'three/webgpu'; +import { + vec4, + storage, + Fn, + If, + uniform, + instanceIndex, + objectWorldMatrix, + color, + screenUV, + attribute, +} from 'three/tsl'; + +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer; +let raycaster, pointer; + +let mesh; + +const pointerPosition = uniform(vec4(0)); +const elasticity = uniform(0.4); // elasticity ( how "strong" the spring is ) +const damping = uniform(0.94); // damping factor ( energy loss ) +const brushSize = uniform(0.25); +const brushStrength = uniform(0.22); + +init(); + +const jelly = Fn(({ renderer, geometry, object }) => { + const count = geometry.attributes.position.count; + + // Create storage buffer attribute for modified position. + + const positionBaseAttribute = geometry.attributes.position; + const positionStorageBufferAttribute = new THREE.StorageBufferAttribute(count, 3); + const speedBufferAttribute = new THREE.StorageBufferAttribute(count, 3); + + geometry.setAttribute('storagePosition', positionStorageBufferAttribute); + + // Attributes + + const positionAttribute = storage(positionBaseAttribute, 'vec3', count); + const positionStorageAttribute = storage(positionStorageBufferAttribute, 'vec3', count); + + const speedAttribute = storage(speedBufferAttribute, 'vec3', count); + + // Vectors + + // Base vec3 position of the mesh vertices. + const basePosition = positionAttribute.element(instanceIndex); + // Mesh vertices after compute modification. + const currentPosition = positionStorageAttribute.element(instanceIndex); + // Speed of each mesh vertex. + const currentSpeed = speedAttribute.element(instanceIndex); + + // + + const computeInit = Fn(() => { + // Modified storage position starts out the same as the base position. + + currentPosition.assign(basePosition); + })().compute(count); + + // + + const computeUpdate = Fn(() => { + // pinch + + If(pointerPosition.w.equal(1), () => { + const worldPosition = objectWorldMatrix(object).mul(currentPosition); + + const dist = worldPosition.distance(pointerPosition.xyz); + const direction = pointerPosition.xyz.sub(worldPosition).normalize(); + + const power = brushSize.sub(dist).max(0).mul(brushStrength); + + currentPosition.addAssign(direction.mul(power)); + }); + + // compute ( jelly ) + + const distance = basePosition.distance(currentPosition); + const force = elasticity.mul(distance).mul(basePosition.sub(currentPosition)); + + currentSpeed.addAssign(force); + currentSpeed.mulAssign(damping); + + currentPosition.addAssign(currentSpeed); + })() + .compute(count) + .setName('Update Jelly'); + + // initialize the storage buffer with the base position + + computeUpdate.onInit(() => renderer.compute(computeInit)); + + // + + return computeUpdate; +}); + +function init() { + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 10); + camera.position.set(0, 0, 1); + + scene = new THREE.Scene(); + + raycaster = new THREE.Raycaster(); + pointer = new THREE.Vector2(); + + // background + + const bgColor = screenUV.y.mix(color(0x9f87f7), color(0xf2cdcd)); + const bgVignette = screenUV.distance(0.5).remapClamp(0.3, 0.8).oneMinus(); + const bgIntensity = 4; + + scene.backgroundNode = bgColor.mul(bgVignette.mul(color(0xa78ff6).mul(bgIntensity))); + + // model + + new GLTFLoader().load('models/gltf/LeePerrySmith/LeePerrySmith.glb', function (gltf) { + // create jelly effect material + + const material = new THREE.MeshNormalNodeMaterial(); + material.geometryNode = jelly(); + material.positionNode = attribute('storagePosition'); + + // apply the material to the mesh + + mesh = gltf.scene.children[0]; + mesh.scale.setScalar(0.1); + mesh.material = material; + scene.add(mesh); + }); + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 0.7; + controls.maxDistance = 2; + + const gui = renderer.inspector.createParameters('Settings'); + gui.add(elasticity, 'value', 0, 0.5).name('elasticity'); + gui.add(damping, 'value', 0.9, 0.98).name('damping'); + gui.add(brushSize, 'value', 0.1, 0.5).name('brush size'); + gui.add(brushStrength, 'value', 0.1, 0.3).name('brush strength'); + + window.addEventListener('resize', onWindowResize); + window.addEventListener('pointermove', onPointerMove); +} + +function onPointerMove(event) { + pointer.set((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1); + + raycaster.setFromCamera(pointer, camera); + + const intersects = raycaster.intersectObject(scene); + + if (intersects.length > 0) { + const intersect = intersects[0]; + + pointerPosition.value.copy(intersect.point); + pointerPosition.value.w = 1; // enable + } else { + pointerPosition.value.w = 0; // disable + } +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +async function animate() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_compute_particles.ts b/examples-testing/examples/webgpu_compute_particles.ts new file mode 100644 index 000000000..71fbd6622 --- /dev/null +++ b/examples-testing/examples/webgpu_compute_particles.ts @@ -0,0 +1,225 @@ +import * as THREE from 'three/webgpu'; +import { Fn, If, uniform, float, uv, vec3, hash, shapeCircle, instancedArray, instanceIndex } from 'three/tsl'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +const particleCount = 200000; + +const gravity = uniform(-0.00098); +const bounce = uniform(0.8); +const friction = uniform(0.99); +const size = uniform(0.12); + +const clickPosition = uniform(new THREE.Vector3()); + +let camera, scene, renderer; +let controls; +let computeParticles; + +let isOrbitControlsActive; + +init(); + +async function init() { + const { innerWidth, innerHeight } = window; + + camera = new THREE.PerspectiveCamera(50, innerWidth / innerHeight, 0.1, 1000); + camera.position.set(0, 5, 20); + + scene = new THREE.Scene(); + + // + + const positions = instancedArray(particleCount, 'vec3'); + const velocities = instancedArray(particleCount, 'vec3'); + const colors = instancedArray(particleCount, 'vec3'); + + // compute + + const separation = 0.2; + const amount = Math.sqrt(particleCount); + const offset = float(amount / 2); + + const computeInit = Fn(() => { + const position = positions.element(instanceIndex); + const color = colors.element(instanceIndex); + + const x = instanceIndex.mod(amount); + const z = instanceIndex.div(amount); + + position.x = offset.sub(x).mul(separation); + position.z = offset.sub(z).mul(separation); + + color.x = hash(instanceIndex); + color.y = hash(instanceIndex.add(2)); + })() + .compute(particleCount) + .setName('Init Particles'); + + // + + const computeUpdate = Fn(() => { + const position = positions.element(instanceIndex); + const velocity = velocities.element(instanceIndex); + + velocity.addAssign(vec3(0.0, gravity, 0.0)); + position.addAssign(velocity); + + velocity.mulAssign(friction); + + // floor + + If(position.y.lessThan(0), () => { + position.y = 0; + velocity.y = velocity.y.negate().mul(bounce); + + // floor friction + + velocity.x = velocity.x.mul(0.9); + velocity.z = velocity.z.mul(0.9); + }); + }); + + computeParticles = computeUpdate().compute(particleCount).setName('Update Particles'); + + // create particles + + const material = new THREE.SpriteNodeMaterial(); + material.colorNode = uv().mul(colors.element(instanceIndex)); + material.positionNode = positions.toAttribute(); + material.scaleNode = size; + material.opacityNode = shapeCircle(); + material.alphaToCoverage = true; + material.transparent = true; + + const particles = new THREE.Sprite(material); + particles.count = particleCount; + particles.frustumCulled = false; + scene.add(particles); + + // + + const helper = new THREE.GridHelper(90, 45, 0x303030, 0x303030); + scene.add(helper); + + const geometry = new THREE.PlaneGeometry(200, 200); + geometry.rotateX(-Math.PI / 2); + + const plane = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ visible: false })); + scene.add(plane); + + const raycaster = new THREE.Raycaster(); + const pointer = new THREE.Vector2(); + + // + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + await renderer.init(); + + // + + renderer.compute(computeInit); + + // Hit + + const computeHit = Fn(() => { + const position = positions.element(instanceIndex); + const velocity = velocities.element(instanceIndex); + + const dist = position.distance(clickPosition); + const direction = position.sub(clickPosition).normalize(); + const distArea = float(3).sub(dist).max(0); + + const power = distArea.mul(0.01); + const relativePower = power.mul(hash(instanceIndex).mul(1.5).add(0.5)); + + velocity.assign(velocity.add(direction.mul(relativePower))); + })() + .compute(particleCount) + .setName('Hit Particles'); + + // + + function onMove(event) { + if (isOrbitControlsActive) return; + + pointer.set((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1); + + raycaster.setFromCamera(pointer, camera); + + const intersects = raycaster.intersectObject(plane, false); + + if (intersects.length > 0) { + const { point } = intersects[0]; + + // move to uniform + + clickPosition.value.copy(point); + clickPosition.value.y = -1; + + // compute + + renderer.compute(computeHit); + } + } + + renderer.domElement.addEventListener('pointermove', onMove); + + // controls + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + controls.minDistance = 5; + controls.maxDistance = 200; + controls.target.set(0, -8, 0); + controls.update(); + + controls.addEventListener('start', () => { + isOrbitControlsActive = true; + }); + controls.addEventListener('end', () => { + isOrbitControlsActive = false; + }); + + controls.touches = { + ONE: null, + TWO: THREE.TOUCH.DOLLY_PAN, + }; + + // + + window.addEventListener('resize', onWindowResize); + + // gui + + const gui = renderer.inspector.createParameters('Settings'); + + gui.add(gravity, 'value', -0.0098, 0, 0.0001).name('gravity'); + gui.add(bounce, 'value', 0.1, 1, 0.01).name('bounce'); + gui.add(friction, 'value', 0.96, 0.99, 0.01).name('friction'); + gui.add(size, 'value', 0.12, 0.5, 0.01).name('size'); +} + +function onWindowResize() { + const { innerWidth, innerHeight } = window; + + camera.aspect = innerWidth / innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(innerWidth, innerHeight); +} + +function animate() { + controls.update(); + + renderer.compute(computeParticles); + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_compute_particles_rain.ts b/examples-testing/examples/webgpu_compute_particles_rain.ts new file mode 100644 index 000000000..eaa4e957e --- /dev/null +++ b/examples-testing/examples/webgpu_compute_particles_rain.ts @@ -0,0 +1,329 @@ +import * as THREE from 'three/webgpu'; +import { + Fn, + texture, + uv, + uint, + instancedArray, + positionWorld, + billboarding, + time, + hash, + deltaTime, + vec2, + instanceIndex, + positionGeometry, + If, +} from 'three/tsl'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js'; + +const maxParticleCount = 50000; +const instanceCount = maxParticleCount / 2; + +let camera, scene, renderer; +let controls; +let computeParticles; +let monkey; +let timer; + +let collisionBox, collisionCamera, collisionPosRT, collisionPosMaterial; +let collisionBoxPos, collisionBoxPosUI; + +init(); + +async function init() { + const { innerWidth, innerHeight } = window; + + camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 0.1, 110); + camera.position.set(40, 8, 0); + camera.lookAt(0, 0, 0); + + scene = new THREE.Scene(); + + const dirLight = new THREE.DirectionalLight(0xffffff, 0.5); + dirLight.position.set(3, 17, 17); + scene.add(dirLight); + + scene.add(new THREE.AmbientLight(0x111111)); + + // + + collisionCamera = new THREE.OrthographicCamera(-50, 50, 50, -50, 0.1, 50); + collisionCamera.position.y = 50; + collisionCamera.lookAt(0, 0, 0); + collisionCamera.layers.disableAll(); + collisionCamera.layers.enable(1); + + collisionPosRT = new THREE.RenderTarget(1024, 1024); + collisionPosRT.texture.type = THREE.HalfFloatType; + collisionPosRT.texture.magFilter = THREE.NearestFilter; + collisionPosRT.texture.minFilter = THREE.NearestFilter; + collisionPosRT.texture.generateMipmaps = false; + + collisionPosMaterial = new THREE.MeshBasicNodeMaterial(); + collisionPosMaterial.colorNode = positionWorld; + + // + + const positionBuffer = instancedArray(maxParticleCount, 'vec3'); + const velocityBuffer = instancedArray(maxParticleCount, 'vec3'); + const ripplePositionBuffer = instancedArray(maxParticleCount, 'vec3'); + const rippleTimeBuffer = instancedArray(maxParticleCount, 'vec3'); + + // compute + + const randUint = () => uint(Math.random() * 0xffffff); + + const computeInit = Fn(() => { + const position = positionBuffer.element(instanceIndex); + const velocity = velocityBuffer.element(instanceIndex); + const rippleTime = rippleTimeBuffer.element(instanceIndex); + + const randX = hash(instanceIndex); + const randY = hash(instanceIndex.add(randUint())); + const randZ = hash(instanceIndex.add(randUint())); + + position.x = randX.mul(100).add(-50); + position.y = randY.mul(25); + position.z = randZ.mul(100).add(-50); + + velocity.y = randX.mul(-0.04).add(-0.2); + + rippleTime.x = 1000; + })().compute(maxParticleCount); + + // + + const computeUpdate = Fn(() => { + const getCoord = pos => pos.add(50).div(100); + + const position = positionBuffer.element(instanceIndex); + const velocity = velocityBuffer.element(instanceIndex); + const ripplePosition = ripplePositionBuffer.element(instanceIndex); + const rippleTime = rippleTimeBuffer.element(instanceIndex); + + position.addAssign(velocity); + + rippleTime.x = rippleTime.x.add(deltaTime.mul(4)); + + // + + const collisionArea = texture(collisionPosRT.texture, getCoord(position.xz)); + + const surfaceOffset = 0.05; + + const floorPosition = collisionArea.y.add(surfaceOffset); + + // floor + + const ripplePivotOffsetY = -0.9; + + If(position.y.add(ripplePivotOffsetY).lessThan(floorPosition), () => { + position.y = 25; + + ripplePosition.xz = position.xz; + ripplePosition.y = floorPosition; + + // reset hit time: x = time + + rippleTime.x = 1; + + // next drops will not fall in the same place + + position.x = hash(instanceIndex.add(time)).mul(100).add(-50); + position.z = hash(instanceIndex.add(time.add(randUint()))) + .mul(100) + .add(-50); + }); + + const rippleOnSurface = texture(collisionPosRT.texture, getCoord(ripplePosition.xz)); + + const rippleFloorArea = rippleOnSurface.y.add(surfaceOffset); + + If(ripplePosition.y.greaterThan(rippleFloorArea), () => { + rippleTime.x = 1000; + }); + }); + + computeParticles = computeUpdate().compute(maxParticleCount).setName('Particles'); + + // rain + + const rainMaterial = new THREE.MeshBasicNodeMaterial(); + rainMaterial.colorNode = uv().distance(vec2(0.5, 0)).oneMinus().mul(3).exp().mul(0.1); + rainMaterial.vertexNode = billboarding({ position: positionBuffer.toAttribute() }); + rainMaterial.opacity = 0.2; + rainMaterial.side = THREE.DoubleSide; + rainMaterial.forceSinglePass = true; + rainMaterial.depthWrite = false; + rainMaterial.depthTest = true; + rainMaterial.transparent = true; + + const rainParticles = new THREE.Mesh(new THREE.PlaneGeometry(0.1, 2), rainMaterial); + rainParticles.count = instanceCount; + scene.add(rainParticles); + + // ripple + + const rippleTime = rippleTimeBuffer.element(instanceIndex).x; + + const rippleEffect = Fn(() => { + const center = uv().add(vec2(-0.5)).length().mul(7); + const distance = rippleTime.sub(center); + + return distance.min(1).sub(distance.max(1).sub(1)); + }); + + const rippleMaterial = new THREE.MeshBasicNodeMaterial(); + rippleMaterial.colorNode = rippleEffect(); + rippleMaterial.positionNode = positionGeometry.add(ripplePositionBuffer.toAttribute()); + rippleMaterial.opacityNode = rippleTime.mul(0.3).oneMinus().max(0).mul(0.5); + rippleMaterial.side = THREE.DoubleSide; + rippleMaterial.forceSinglePass = true; + rippleMaterial.depthWrite = false; + rippleMaterial.depthTest = true; + rippleMaterial.transparent = true; + + // ripple geometry + + const surfaceRippleGeometry = new THREE.PlaneGeometry(2.5, 2.5); + surfaceRippleGeometry.rotateX(-Math.PI / 2); + + const xRippleGeometry = new THREE.PlaneGeometry(1, 2); + xRippleGeometry.rotateY(-Math.PI / 2); + + const zRippleGeometry = new THREE.PlaneGeometry(1, 2); + + const rippleGeometry = BufferGeometryUtils.mergeGeometries([ + surfaceRippleGeometry, + xRippleGeometry, + zRippleGeometry, + ]); + + const rippleParticles = new THREE.Mesh(rippleGeometry, rippleMaterial); + rippleParticles.count = instanceCount; + scene.add(rippleParticles); + + // floor geometry + + const floorGeometry = new THREE.PlaneGeometry(1000, 1000); + floorGeometry.rotateX(-Math.PI / 2); + + const plane = new THREE.Mesh(floorGeometry, new THREE.MeshBasicMaterial({ color: 0x050505 })); + scene.add(plane); + + // + + collisionBox = new THREE.Mesh(new THREE.BoxGeometry(30, 1, 15), new THREE.MeshStandardMaterial()); + collisionBox.material.color.set(0x333333); + collisionBox.position.y = 12; + collisionBox.scale.x = 3.5; + collisionBox.layers.enable(1); + scene.add(collisionBox); + + // + + const loader = new THREE.BufferGeometryLoader(); + loader.load('models/json/suzanne_buffergeometry.json', function (geometry) { + geometry.computeVertexNormals(); + + monkey = new THREE.Mesh(geometry, new THREE.MeshStandardMaterial({ roughness: 1, metalness: 0 })); + monkey.scale.setScalar(5); + monkey.rotation.y = Math.PI / 2; + monkey.position.y = 4.5; + monkey.layers.enable(1); // add to collision layer + + scene.add(monkey); + }); + + // + + timer = new THREE.Timer(); + timer.connect(document); + + // + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + await renderer.init(); + + // + + renderer.compute(computeInit); + + // + + controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 5; + controls.maxDistance = 50; + controls.update(); + + // + + window.addEventListener('resize', onWindowResize); + + // gui + + const gui = renderer.inspector.createParameters('Settings'); + + // use lerp to smooth the movement + collisionBoxPosUI = new THREE.Vector3().copy(collisionBox.position); + collisionBoxPos = new THREE.Vector3(); + + gui.add(collisionBoxPosUI, 'z', -50, 50, 0.001).name('position'); + gui.add(collisionBox.scale, 'x', 0.1, 3.5, 0.01).name('scale'); + gui.add(rainParticles, 'count', 200, maxParticleCount, 1) + .name('drop count') + .onChange(v => (rippleParticles.count = v)); +} + +function onWindowResize() { + const { innerWidth, innerHeight } = window; + + camera.aspect = innerWidth / innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(innerWidth, innerHeight); +} + +function animate() { + timer.update(); + + const delta = timer.getDelta(); + + if (monkey) { + monkey.rotation.y += delta; + } + + collisionBoxPos.set(collisionBoxPosUI.x, collisionBoxPosUI.y, -collisionBoxPosUI.z); + + collisionBox.position.lerp(collisionBoxPos, 10 * delta); + + // position + + scene.overrideMaterial = collisionPosMaterial; + scene.name = 'Collision Scene'; + renderer.setRenderTarget(collisionPosRT); + renderer.render(scene, collisionCamera); + + // compute + + renderer.compute(computeParticles); + + // result + + scene.overrideMaterial = null; + scene.name = 'Scene'; + renderer.setRenderTarget(null); + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_compute_particles_snow.ts b/examples-testing/examples/webgpu_compute_particles_snow.ts new file mode 100644 index 000000000..e00fe068f --- /dev/null +++ b/examples-testing/examples/webgpu_compute_particles_snow.ts @@ -0,0 +1,328 @@ +import * as THREE from 'three/webgpu'; +import { + Fn, + texture, + vec3, + pass, + color, + uint, + screenUV, + instancedArray, + positionWorld, + positionLocal, + time, + vec2, + hash, + instanceIndex, + If, +} from 'three/tsl'; +import { gaussianBlur } from 'three/addons/tsl/display/GaussianBlurNode.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { TeapotGeometry } from 'three/addons/geometries/TeapotGeometry.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +const maxParticleCount = 100000; + +let camera, scene, renderer; +let controls; +let computeParticles; +let renderPipeline; + +let collisionCamera, collisionPosRT, collisionPosMaterial; + +init(); + +async function init() { + const { innerWidth, innerHeight } = window; + + camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 0.1, 100); + camera.position.set(20, 2, 20); + camera.layers.enable(2); + camera.lookAt(0, 40, 0); + + scene = new THREE.Scene(); + scene.fog = new THREE.Fog(0x0f3c37, 5, 40); + + const dirLight = new THREE.DirectionalLight(0xf9ff9b, 9); + dirLight.position.set(10, 10, 0); + scene.add(dirLight); + + scene.add(new THREE.HemisphereLight(0x0f3c37, 0x080d10, 100)); + + // + + collisionCamera = new THREE.OrthographicCamera(-50, 50, 50, -50, 0.1, 50); + collisionCamera.position.y = 50; + collisionCamera.lookAt(0, 0, 0); + collisionCamera.layers.enable(1); + + collisionPosRT = new THREE.RenderTarget(1024, 1024); + collisionPosRT.texture.type = THREE.HalfFloatType; + collisionPosRT.texture.magFilter = THREE.NearestFilter; + collisionPosRT.texture.minFilter = THREE.NearestFilter; + collisionPosRT.texture.generateMipmaps = false; + + collisionPosMaterial = new THREE.MeshBasicNodeMaterial(); + collisionPosMaterial.fog = false; + collisionPosMaterial.toneMapped = false; + collisionPosMaterial.colorNode = positionWorld.y; + + // + + const positionBuffer = instancedArray(maxParticleCount, 'vec3'); + const scaleBuffer = instancedArray(maxParticleCount, 'vec3'); + const staticPositionBuffer = instancedArray(maxParticleCount, 'vec3'); + const dataBuffer = instancedArray(maxParticleCount, 'vec4'); + + // compute + + const randUint = () => uint(Math.random() * 0xffffff); + + const computeInit = Fn(() => { + const position = positionBuffer.element(instanceIndex); + const scale = scaleBuffer.element(instanceIndex); + const particleData = dataBuffer.element(instanceIndex); + + const randX = hash(instanceIndex); + const randY = hash(instanceIndex.add(randUint())); + const randZ = hash(instanceIndex.add(randUint())); + + position.x = randX.mul(100).add(-50); + position.y = randY.mul(500).add(3); + position.z = randZ.mul(100).add(-50); + + scale.xyz = hash(instanceIndex.add(Math.random())).mul(0.8).add(0.2); + + staticPositionBuffer.element(instanceIndex).assign(vec3(1000, 10000, 1000)); + + particleData.y = randY.mul(-0.1).add(-0.02); + + particleData.x = position.x; + particleData.z = position.z; + particleData.w = randX; + })() + .compute(maxParticleCount) + .setName('Init Particles'); + + // + + const surfaceOffset = 0.2; + const speed = 0.4; + + const computeUpdate = Fn(() => { + const getCoord = pos => pos.add(50).div(100); + + const position = positionBuffer.element(instanceIndex); + const scale = scaleBuffer.element(instanceIndex); + const particleData = dataBuffer.element(instanceIndex); + + const velocity = particleData.y; + const random = particleData.w; + + const rippleOnSurface = texture(collisionPosRT.texture, getCoord(position.xz)).toInspector( + 'Collision Test', + () => { + return texture(collisionPosRT.texture).y; // .div( collisionCamera.position.y ); + }, + ); + + const rippleFloorArea = rippleOnSurface.y.add(scale.x.mul(surfaceOffset)); + + If(position.y.greaterThan(rippleFloorArea), () => { + position.x = particleData.x.add(time.mul(random.mul(random)).mul(speed).sin().mul(3)); + position.z = particleData.z.add(time.mul(random).mul(speed).cos().mul(random.mul(10))); + + position.y = position.y.add(velocity); + }).Else(() => { + staticPositionBuffer.element(instanceIndex).assign(position); + }); + }); + + computeParticles = computeUpdate().compute(maxParticleCount); + computeParticles.name = 'Update Particles'; + + // rain + + const geometry = new THREE.SphereGeometry(surfaceOffset, 5, 5); + + function particle(staticParticles) { + const posBuffer = staticParticles ? staticPositionBuffer : positionBuffer; + const layer = staticParticles ? 1 : 2; + + const staticMaterial = new THREE.MeshStandardNodeMaterial({ + color: 0xeeeeee, + roughness: 0.9, + metalness: 0, + }); + + staticMaterial.positionNode = positionLocal.mul(scaleBuffer.toAttribute()).add(posBuffer.toAttribute()); + + const rainParticles = new THREE.Mesh(geometry, staticMaterial); + rainParticles.count = maxParticleCount; + rainParticles.layers.disableAll(); + rainParticles.layers.enable(layer); + + return rainParticles; + } + + const dynamicParticles = particle(); + const staticParticles = particle(true); + + scene.add(dynamicParticles); + scene.add(staticParticles); + + // floor geometry + + const floorGeometry = new THREE.PlaneGeometry(100, 100); + floorGeometry.rotateX(-Math.PI / 2); + + const plane = new THREE.Mesh( + floorGeometry, + new THREE.MeshStandardMaterial({ + color: 0x0c1e1e, + roughness: 0.5, + metalness: 0, + transparent: true, + }), + ); + + plane.material.opacityNode = positionLocal.xz.mul(0.05).distance(0).saturate().oneMinus(); + + scene.add(plane); + + // tree + + function tree(count = 8) { + const coneMaterial = new THREE.MeshStandardNodeMaterial({ + color: 0x0d492c, + roughness: 0.6, + metalness: 0, + }); + + const object = new THREE.Group(); + + for (let i = 0; i < count; i++) { + const radius = 1 + i; + + const coneGeometry = new THREE.ConeGeometry(radius * 0.95, radius * 1.25, 32); + + const cone = new THREE.Mesh(coneGeometry, coneMaterial); + cone.position.y = (count - i) * 1.5 + count * 0.6; + object.add(cone); + } + + const geometry = new THREE.CylinderGeometry(1, 1, count, 32); + const cone = new THREE.Mesh(geometry, coneMaterial); + cone.position.y = count / 2; + object.add(cone); + + return object; + } + + const teapotTree = new THREE.Mesh( + new TeapotGeometry(0.5, 18), + new THREE.MeshBasicNodeMaterial({ + color: 0xfcfb9e, + }), + ); + + teapotTree.name = 'Teapot Pass'; + teapotTree.position.y = 18; + + scene.add(tree()); + scene.add(teapotTree); + + // + + scene.backgroundNode = screenUV.distance(0.5).mul(2).mix(color(0x0f4140), color(0x060a0d)); + + // + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + await renderer.init(); + + // + + controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, 10, 0); + controls.minDistance = 25; + controls.maxDistance = 35; + controls.maxPolarAngle = Math.PI / 1.7; + controls.autoRotate = true; + controls.autoRotateSpeed = -0.7; + controls.update(); + + // post processing + + const scenePass = pass(scene, camera); + const scenePassColor = scenePass.getTextureNode(); + const vignette = screenUV.distance(0.5).mul(1.35).clamp().oneMinus(); + + const teapotTreePass = pass(teapotTree, camera).getTextureNode(); + const teapotTreePassBlurred = gaussianBlur(teapotTreePass, vec2(1), 6); + teapotTreePassBlurred.resolutionScale = 0.2; + + const scenePassColorBlurred = gaussianBlur(scenePassColor); + scenePassColorBlurred.resolutionScale = 0.5; + scenePassColorBlurred.directionNode = vec2(1); + + // compose + + let totalPass = scenePass.toInspector('Scene'); + totalPass = totalPass.add(scenePassColorBlurred.mul(0.1)); + totalPass = totalPass.mul(vignette); + totalPass = totalPass.add(teapotTreePass.mul(10).add(teapotTreePassBlurred).toInspector('Teapot Blur')); + + renderPipeline = new THREE.RenderPipeline(renderer); + renderPipeline.outputNode = totalPass; + + // + + renderer.compute(computeInit); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + const { innerWidth, innerHeight } = window; + + camera.aspect = innerWidth / innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(innerWidth, innerHeight); +} + +function animate() { + controls.update(); + + // position + + scene.name = 'Collider Position'; + scene.overrideMaterial = collisionPosMaterial; + renderer.setRenderTarget(collisionPosRT); + renderer.render(scene, collisionCamera); + + // compute + + renderer.compute(computeParticles); + + // result + + scene.name = 'Scene'; + scene.overrideMaterial = null; + renderer.setRenderTarget(null); + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_compute_points.ts b/examples-testing/examples/webgpu_compute_points.ts new file mode 100644 index 000000000..eab1d9f61 --- /dev/null +++ b/examples-testing/examples/webgpu_compute_points.ts @@ -0,0 +1,124 @@ +import * as THREE from 'three/webgpu'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { Fn, uniform, instancedArray, float, vec2, color, instanceIndex } from 'three/tsl'; + +let camera, scene, renderer; +let computeNode; + +const pointerVector = new THREE.Vector2(-10.0, -10.0); // Out of bounds first +const scaleVector = new THREE.Vector2(1, 1); + +init(); + +async function init() { + camera = new THREE.OrthographicCamera(-1.0, 1.0, 1.0, -1.0, 0, 1); + camera.position.z = 1; + + scene = new THREE.Scene(); + + // initialize particles + + const particlesCount = 300000; + + const particleArray = instancedArray(particlesCount, 'vec2'); + const velocityArray = instancedArray(particlesCount, 'vec2'); + + // create function + + const computeShaderFn = Fn(() => { + const particle = particleArray.element(instanceIndex); + const velocity = velocityArray.element(instanceIndex); + + const pointer = uniform(pointerVector); + const limit = uniform(scaleVector); + + const position = particle.add(velocity).toVar(); + + velocity.x = position.x.abs().greaterThanEqual(limit.x).select(velocity.x.negate(), velocity.x); + velocity.y = position.y.abs().greaterThanEqual(limit.y).select(velocity.y.negate(), velocity.y); + + position.assign(position.min(limit).max(limit.negate())); + + const pointerSize = 0.1; + const distanceFromPointer = pointer.sub(position).length(); + + particle.assign(distanceFromPointer.lessThanEqual(pointerSize).select(vec2(), position)); + }); + + // compute + + computeNode = computeShaderFn().compute(particlesCount).setName('Update Particles'); + computeNode.onInit(({ renderer }) => { + const precomputeShaderNode = Fn(() => { + const particleIndex = float(instanceIndex); + + const randomAngle = particleIndex.mul(0.005).mul(Math.PI * 2); + const randomSpeed = particleIndex.mul(0.00000001).add(0.0000001); + + const velX = randomAngle.sin().mul(randomSpeed); + const velY = randomAngle.cos().mul(randomSpeed); + + const velocity = velocityArray.element(instanceIndex); + + velocity.xy = vec2(velX, velY); + }); + + renderer.compute(precomputeShaderNode().compute(particlesCount)); + }); + + // use a compute shader to animate the point cloud's vertex data. + + const pointsGeometry = new THREE.BufferGeometry(); + pointsGeometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(3), 3)); // single vertex ( not triangle ) + pointsGeometry.drawRange.count = 1; // force render points as instances ( not triangle ) + + const pointsMaterial = new THREE.PointsNodeMaterial(); + pointsMaterial.colorNode = particleArray.element(instanceIndex).add(color(0xffffff)); + pointsMaterial.positionNode = particleArray.element(instanceIndex); + + const mesh = new THREE.Points(pointsGeometry, pointsMaterial); + mesh.count = particlesCount; + scene.add(mesh); + + renderer = new THREE.WebGPURenderer({ antialias: true, requiredLimits: { maxStorageBuffersInVertexStage: 1 } }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + await renderer.init(); + + window.addEventListener('resize', onWindowResize); + window.addEventListener('mousemove', onMouseMove); + + // gui + + const gui = renderer.inspector.createParameters('Settings'); + + gui.add(scaleVector, 'x', 0, 1, 0.01); + gui.add(scaleVector, 'y', 0, 1, 0.01); +} + +function onWindowResize() { + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function onMouseMove(event) { + const x = event.clientX; + const y = event.clientY; + + const width = window.innerWidth; + const height = window.innerHeight; + + pointerVector.set((x / width - 0.5) * 2.0, (-y / height + 0.5) * 2.0); +} + +function animate() { + renderer.compute(computeNode); + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_compute_sort_bitonic.ts b/examples-testing/examples/webgpu_compute_sort_bitonic.ts new file mode 100644 index 000000000..9a37a9ece --- /dev/null +++ b/examples-testing/examples/webgpu_compute_sort_bitonic.ts @@ -0,0 +1,457 @@ +import * as THREE from 'three/webgpu'; +import { storage, If, vec3, not, uniform, uv, uint, Fn, vec2, abs, int, uvec2, floor, instanceIndex } from 'three/tsl'; + +import { BitonicSort, getBitonicDisperseIndices, getBitonicFlipIndices } from 'three/addons/gpgpu/BitonicSort.js'; + +import WebGPU from 'three/addons/capabilities/WebGPU.js'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +const StepType = { + NONE: 0, + // Swap values within workgroup local values + SWAP_LOCAL: 1, + DISPERSE_LOCAL: 2, + // Swap values within global data buffer. + FLIP_GLOBAL: 3, + DISPERSE_GLOBAL: 4, +}; + +const timestamps = { + local_swap: document.getElementById('local_swap'), + global_swap: document.getElementById('global_swap'), +}; + +const localColors = ['rgb(203, 64, 203)', 'rgb(0, 215, 215)']; +const globalColors = ['rgb(1, 150, 1)', 'red']; + +// Total number of elements and the dimensions of the display grid. +const size = 16384; +const gridDim = Math.sqrt(size); + +const getNumSteps = () => { + const n = Math.log2(size); + return (n * (n + 1)) / 2; +}; + +// Total number of steps in a bitonic sort with 'size' elements. +const MAX_STEPS = getNumSteps(); + +const effectController = { + // Sqr root of 16834 + gridWidth: uniform(gridDim), + gridHeight: uniform(gridDim), + highlight: uniform(1), + stepBitonic: true, + 'Display Mode': 'Swap Zone Highlight', +}; + +const gui = new GUI(); +gui.add(effectController, 'Display Mode', ['Elements', 'Swap Zone Highlight']).onChange(() => { + if (effectController['Display Mode'] === 'Elements') { + effectController.highlight.value = 0; + } else { + effectController.highlight.value = 1; + } +}); + +if (WebGPU.isAvailable() === false) { + document.body.appendChild(WebGPU.getErrorMessage()); + + throw new Error('No WebGPU support'); +} + +// Display utilities + +const getElementIndex = Fn( + ([uvNode, gridWidth, gridHeight]) => { + const newUV = uvNode.mul(vec2(gridWidth, gridHeight)); + const pixel = uvec2(uint(floor(newUV.x)), uint(floor(newUV.y))); + const elementIndex = uint(gridWidth).mul(pixel.y).add(pixel.x); + + return elementIndex; + }, + { + uvNode: 'vec2', + gridWidth: 'uint', + gridHeight: 'uint', + return: 'uint', + }, +); + +const getColor = Fn( + ([colorChanger, gridWidth, gridHeight]) => { + const subtracter = colorChanger.div(gridWidth.mul(gridHeight)); + return vec3(subtracter.oneMinus()).toVar(); + }, + { + colorChanger: 'float', + gridWidth: 'float', + gridHeight: 'float', + return: 'vec3', + }, +); + +const randomizeDataArray = array => { + let currentIndex = array.length; + while (currentIndex !== 0) { + const randomIndex = Math.floor(Math.random() * currentIndex); + currentIndex -= 1; + [array[currentIndex], array[randomIndex]] = [array[randomIndex], array[currentIndex]]; + } +}; + +const windowResizeCallback = (renderer, scene, camera) => { + renderer.setSize(window.innerWidth / 2, window.innerHeight); + const aspect = window.innerWidth / 2 / window.innerHeight; + const frustumHeight = camera.top - camera.bottom; + camera.left = (-frustumHeight * aspect) / 2; + camera.right = (frustumHeight * aspect) / 2; + camera.updateProjectionMatrix(); + renderer.render(scene, camera); +}; + +const constructInnerHTML = (isGlobal, colorsArr) => { + return ` + + Compute ${isGlobal ? 'Global' : 'Local'}: +
+ ${isGlobal ? 'Global Swaps' : 'Local Swaps'} Compare Region  +
+  to Region  +
+
`; +}; + +const createDisplayMesh = (elementsStorage, algoStorage = null, blockHeightStorage = null) => { + const material = new THREE.MeshBasicNodeMaterial({ color: 0x00ff00 }); + + const display = Fn(() => { + const { gridWidth, gridHeight, highlight } = effectController; + + const elementIndex = getElementIndex(uv(), gridWidth, gridHeight); + const color = getColor(elementsStorage.element(elementIndex), gridWidth, gridHeight).toVar(); + + if (algoStorage !== null && blockHeightStorage !== null) { + If(highlight.equal(1).and(not(algoStorage.element(0).equal(StepType.NONE))), () => { + const boolCheck = int( + elementIndex.mod(blockHeightStorage.element(0)).lessThan(blockHeightStorage.element(0).div(2)), + ); + color.z.assign(algoStorage.element(0).lessThanEqual(StepType.DISPERSE_LOCAL)); + color.x.mulAssign(boolCheck); + color.y.mulAssign(abs(boolCheck.sub(1))); + }); + } + + return color; + }); + + material.colorNode = display(); + const plane = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), material); + return plane; +}; + +const createDisplayMesh2 = (elementsStorage, infoStorage) => { + const material = new THREE.MeshBasicNodeMaterial({ color: 0x00ff00 }); + + const display = Fn(() => { + const { gridWidth, gridHeight, highlight } = effectController; + + const elementIndex = getElementIndex(uv(), gridWidth, gridHeight); + const color = getColor(elementsStorage.element(elementIndex), gridWidth, gridHeight).toVar(); + + If(highlight.equal(1).and(not(infoStorage.element(0).equal(StepType.SWAP_LOCAL))), () => { + const boolCheck = int(elementIndex.mod(infoStorage.element(1)).lessThan(infoStorage.element(1).div(2))); + color.z.assign(infoStorage.element(0).lessThanEqual(StepType.DISPERSE_LOCAL)); + color.x.mulAssign(boolCheck); + color.y.mulAssign(abs(boolCheck.sub(1))); + }); + + return color; + }); + + material.colorNode = display(); + const plane = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), material); + return plane; +}; + +const setupDomElement = renderer => { + document.body.appendChild(renderer.domElement); + renderer.domElement.style.position = 'absolute'; + renderer.domElement.style.top = '0'; + renderer.domElement.style.left = '0'; + renderer.domElement.style.width = '50%'; + renderer.domElement.style.height = '100%'; +}; + +async function initBitonicSort() { + let currentStep = 0; + + const aspect = window.innerWidth / 2 / window.innerHeight; + const camera = new THREE.OrthographicCamera(-aspect, aspect, 1, -1, 0, 2); + camera.position.z = 1; + + const scene = new THREE.Scene(); + + const array = new Uint32Array( + Array.from({ length: size }, (_, i) => { + return i; + }), + ); + + randomizeDataArray(array); + + const currentElementsBuffer = new THREE.StorageInstancedBufferAttribute(array, 1); + const currentElementsStorage = storage(currentElementsBuffer, 'uint', size).setPBO(true).setName('Elements'); + const randomizedElementsBuffer = new THREE.StorageInstancedBufferAttribute(size, 1); + const randomizedElementsStorage = storage(randomizedElementsBuffer, 'uint', size) + .setPBO(true) + .setName('RandomizedElements'); + + const computeInitFn = Fn(() => { + randomizedElementsStorage.element(instanceIndex).assign(currentElementsStorage.element(instanceIndex)); + }); + + const computeResetBuffersFn = Fn(() => { + currentElementsStorage.element(instanceIndex).assign(randomizedElementsStorage.element(instanceIndex)); + }); + + const renderer = new THREE.WebGPURenderer({ antialias: false }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth / 2, window.innerHeight); + + await renderer.init(); + + const animate = () => { + renderer.render(scene, camera); + }; + + renderer.setAnimationLoop(animate); + setupDomElement(renderer); + scene.background = new THREE.Color(0x313131); + + const bitonicSortModule = new BitonicSort(renderer, currentElementsStorage, { + workgroupSize: 64, + }); + + scene.add(createDisplayMesh2(currentElementsStorage, bitonicSortModule.infoStorage)); + + // Initialize each value in the elements buffer. + const computeInit = computeInitFn().compute(size); + const computeReset = computeResetBuffersFn().compute(size); + + renderer.compute(computeInit); + + const stepAnimation = async function () { + renderer.info.reset(); + + if (currentStep < bitonicSortModule.stepCount) { + bitonicSortModule.computeStep(renderer); + + currentStep++; + } else { + renderer.compute(computeReset); + + currentStep = 0; + } + + timestamps['local_swap'].innerHTML = constructInnerHTML(false, localColors); + + if (currentStep === bitonicSortModule.stepCount) { + setTimeout(stepAnimation, 1000); + } else { + setTimeout(stepAnimation, 100); + } + }; + + stepAnimation(); + + window.addEventListener('resize', onWindowResize); + + function onWindowResize() { + windowResizeCallback(renderer, scene, camera); + } +} + +initBitonicSort(); + +// Global Swaps Only +initGlobalSwapOnly(); + +// When forceGlobalSwap is true, force all valid local swaps to be global swaps. +async function initGlobalSwapOnly() { + let currentStep = 0; + + const aspect = window.innerWidth / 2 / window.innerHeight; + const camera = new THREE.OrthographicCamera(-aspect, aspect, 1, -1, 0, 2); + camera.position.z = 1; + + const scene = new THREE.Scene(); + + const infoArray = new Uint32Array([3, 2, 2]); + const infoBuffer = new THREE.StorageInstancedBufferAttribute(infoArray, 1); + const infoStorage = storage(infoBuffer, 'uint', infoBuffer.count).setPBO(true).setName('TheInfo'); + + const nextBlockHeightBuffer = new THREE.StorageInstancedBufferAttribute(new Uint32Array(1).fill(2), 1); + const nextBlockHeightStorage = storage(nextBlockHeightBuffer, 'uint', nextBlockHeightBuffer.count) + .setPBO(true) + .setName('NextBlockHeight'); + const nextBlockHeightRead = storage(nextBlockHeightBuffer, 'uint', nextBlockHeightBuffer.count) + .setPBO(true) + .setName('NextBlockHeight') + .toReadOnly(); + + const array = new Uint32Array( + Array.from({ length: size }, (_, i) => { + return i; + }), + ); + + randomizeDataArray(array); + + const currentElementsBuffer = new THREE.StorageInstancedBufferAttribute(array, 1); + const currentElementsStorage = storage(currentElementsBuffer, 'uint', size).setPBO(true).setName('Elements'); + const tempBuffer = new THREE.StorageInstancedBufferAttribute(array, 1); + const tempStorage = storage(tempBuffer, 'uint', size).setPBO(true).setName('Temp'); + const randomizedElementsBuffer = new THREE.StorageInstancedBufferAttribute(size, 1); + const randomizedElementsStorage = storage(randomizedElementsBuffer, 'uint', size) + .setPBO(true) + .setName('RandomizedElements'); + + // Swap the elements in local storage + const globalCompareAndSwap = (idxBefore, idxAfter) => { + // If the later element is less than the current element + If(currentElementsStorage.element(idxAfter).lessThan(currentElementsStorage.element(idxBefore)), () => { + // Apply the swapped values to temporary storage. + tempStorage.element(idxBefore).assign(currentElementsStorage.element(idxAfter)); + tempStorage.element(idxAfter).assign(currentElementsStorage.element(idxBefore)); + }).Else(() => { + // Otherwise apply the existing values to temporary storage. + tempStorage.element(idxBefore).assign(currentElementsStorage.element(idxBefore)); + tempStorage.element(idxAfter).assign(currentElementsStorage.element(idxAfter)); + }); + }; + + const computeInitFn = Fn(() => { + randomizedElementsStorage.element(instanceIndex).assign(currentElementsStorage.element(instanceIndex)); + }); + + const computeBitonicStepFn = Fn(() => { + const nextBlockHeight = nextBlockHeightStorage.element(0).toVar(); + const nextAlgo = infoStorage.element(0).toVar(); + + // TODO: Convert to switch block. + If(nextAlgo.equal(uint(StepType.FLIP_GLOBAL)), () => { + const idx = getBitonicFlipIndices(instanceIndex, nextBlockHeight); + globalCompareAndSwap(idx.x, idx.y); + }).ElseIf(nextAlgo.equal(uint(StepType.DISPERSE_GLOBAL)), () => { + const idx = getBitonicDisperseIndices(instanceIndex, nextBlockHeight); + globalCompareAndSwap(idx.x, idx.y); + }); + + // Since this algorithm is global only, we execute an additional compute step to sync the current buffer with the output buffer. + }); + + const computeSetAlgoFn = Fn(() => { + const nextBlockHeight = nextBlockHeightStorage.element(0).toVar(); + const nextAlgo = infoStorage.element(0); + const highestBlockHeight = infoStorage.element(2).toVar(); + + nextBlockHeight.divAssign(2); + + If(nextBlockHeight.equal(1), () => { + highestBlockHeight.mulAssign(2); + + If(highestBlockHeight.equal(size * 2), () => { + nextAlgo.assign(StepType.NONE); + nextBlockHeight.assign(0); + }).Else(() => { + nextAlgo.assign(StepType.FLIP_GLOBAL); + nextBlockHeight.assign(highestBlockHeight); + }); + }).Else(() => { + nextAlgo.assign(StepType.DISPERSE_GLOBAL); + }); + + nextBlockHeightStorage.element(0).assign(nextBlockHeight); + infoStorage.element(2).assign(highestBlockHeight); + }); + + const computeAlignCurrentFn = Fn(() => { + currentElementsStorage.element(instanceIndex).assign(tempStorage.element(instanceIndex)); + }); + + const computeResetBuffersFn = Fn(() => { + currentElementsStorage.element(instanceIndex).assign(randomizedElementsStorage.element(instanceIndex)); + }); + + const computeResetAlgoFn = Fn(() => { + infoStorage.element(0).assign(StepType.FLIP_GLOBAL); + nextBlockHeightStorage.element(0).assign(2); + infoStorage.element(2).assign(2); + }); + + // Initialize each value in the elements buffer. + const computeInit = computeInitFn().compute(size); + // Swap a pair of elements in the elements buffer. + const computeBitonicStep = computeBitonicStepFn().compute(size / 2); + // Set the conditions for the next swap. + const computeSetAlgo = computeSetAlgoFn().compute(1); + // Align the current buffer with the temp buffer if the previous sort was executed in a global scope. + const computeAlignCurrent = computeAlignCurrentFn().compute(size); + // Reset the buffers and algorithm information after a full bitonic sort has been completed. + const computeResetBuffers = computeResetBuffersFn().compute(size); + const computeResetAlgo = computeResetAlgoFn().compute(1); + + scene.add(createDisplayMesh(currentElementsStorage, infoStorage, nextBlockHeightRead)); + + const renderer = new THREE.WebGPURenderer({ antialias: false }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth / 2, window.innerHeight); + + await renderer.init(); + + const animate = () => { + renderer.render(scene, camera); + }; + + renderer.setAnimationLoop(animate); + setupDomElement(renderer); + renderer.domElement.style.left = '50%'; + scene.background = new THREE.Color(0x212121); + + renderer.compute(computeInit); + + const stepAnimation = async function () { + if (currentStep !== MAX_STEPS) { + renderer.compute(computeBitonicStep); + + renderer.compute(computeAlignCurrent); + + renderer.compute(computeSetAlgo); + + currentStep++; + } else { + renderer.compute(computeResetBuffers); + renderer.compute(computeResetAlgo); + + currentStep = 0; + } + + timestamps['global_swap'].innerHTML = constructInnerHTML(true, globalColors); + + if (currentStep === MAX_STEPS) { + setTimeout(stepAnimation, 1000); + } else { + setTimeout(stepAnimation, 100); + } + }; + + stepAnimation(); + + window.addEventListener('resize', onWindowResize); + + function onWindowResize() { + windowResizeCallback(renderer, scene, camera); + } +} diff --git a/examples-testing/examples/webgpu_compute_texture.ts b/examples-testing/examples/webgpu_compute_texture.ts new file mode 100644 index 000000000..43d162ab5 --- /dev/null +++ b/examples-testing/examples/webgpu_compute_texture.ts @@ -0,0 +1,96 @@ +import * as THREE from 'three/webgpu'; +import { texture, textureStore, Fn, instanceIndex, float, uvec2, vec4 } from 'three/tsl'; + +import WebGPU from 'three/addons/capabilities/WebGPU.js'; + +let camera, scene, renderer; + +init().then(render); + +async function init() { + if (WebGPU.isAvailable() === false) { + document.body.appendChild(WebGPU.getErrorMessage()); + + throw new Error('No WebGPU support'); + } + + const aspect = window.innerWidth / window.innerHeight; + camera = new THREE.OrthographicCamera(-aspect, aspect, 1, -1, 0, 2); + camera.position.z = 1; + + scene = new THREE.Scene(); + + // texture + + const width = 512, + height = 512; + + const storageTexture = new THREE.StorageTexture(width, height); + //storageTexture.minFilter = THREE.LinearMipMapLinearFilter; + + // create function + + const computeTexture = Fn(({ storageTexture }) => { + const posX = instanceIndex.mod(width); + const posY = instanceIndex.div(width); + const indexUV = uvec2(posX, posY); + + // https://www.shadertoy.com/view/Xst3zN + + const x = float(posX).div(50.0); + const y = float(posY).div(50.0); + + const v1 = x.sin(); + const v2 = y.sin(); + const v3 = x.add(y).sin(); + const v4 = x.mul(x).add(y.mul(y)).sqrt().add(5.0).sin(); + const v = v1.add(v2, v3, v4); + + const r = v.sin(); + const g = v.add(Math.PI).sin(); + const b = v.add(Math.PI).sub(0.5).sin(); + + textureStore(storageTexture, indexUV, vec4(r, g, b, 1)).toWriteOnly(); + }); + + // compute + + const computeNode = computeTexture({ storageTexture }).compute(width * height); + + const material = new THREE.MeshBasicNodeMaterial({ color: 0x00ff00 }); + material.colorNode = texture(storageTexture); + + const plane = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), material); + scene.add(plane); + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + document.body.appendChild(renderer.domElement); + + await renderer.init(); + + // compute texture + renderer.compute(computeNode); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + renderer.setSize(window.innerWidth, window.innerHeight); + + const aspect = window.innerWidth / window.innerHeight; + + const frustumHeight = camera.top - camera.bottom; + + camera.left = (-frustumHeight * aspect) / 2; + camera.right = (frustumHeight * aspect) / 2; + + camera.updateProjectionMatrix(); + + render(); +} + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_compute_texture_3d.ts b/examples-testing/examples/webgpu_compute_texture_3d.ts new file mode 100644 index 000000000..ccdfba0be --- /dev/null +++ b/examples-testing/examples/webgpu_compute_texture_3d.ts @@ -0,0 +1,193 @@ +import * as THREE from 'three/webgpu'; +import { + time, + mx_noise_vec3, + instanceIndex, + textureStore, + float, + vec3, + vec4, + If, + Break, + Fn, + smoothstep, + texture3D, + uniform, +} from 'three/tsl'; + +import { RaymarchingBox } from 'three/addons/tsl/utils/Raymarching.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import WebGPU from 'three/addons/capabilities/WebGPU.js'; + +let renderer, scene, camera; +let mesh; +let computeNode; + +if (WebGPU.isAvailable() === false) { + document.body.appendChild(WebGPU.getErrorMessage()); + + throw new Error('No WebGPU support'); +} + +init(); + +async function init() { + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + await renderer.init(); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(0, 1, 1.5); + + new OrbitControls(camera, renderer.domElement); + + // Sky + + const canvas = document.createElement('canvas'); + canvas.width = 1; + canvas.height = 32; + + const context = canvas.getContext('2d'); + const gradient = context.createLinearGradient(0, 0, 0, 32); + gradient.addColorStop(0.0, '#014a84'); + gradient.addColorStop(0.5, '#0561a0'); + gradient.addColorStop(1.0, '#437ab6'); + context.fillStyle = gradient; + context.fillRect(0, 0, 1, 32); + + const skyMap = new THREE.CanvasTexture(canvas); + skyMap.colorSpace = THREE.SRGBColorSpace; + + const sky = new THREE.Mesh( + new THREE.SphereGeometry(10), + new THREE.MeshBasicNodeMaterial({ map: skyMap, side: THREE.BackSide }), + ); + scene.add(sky); + + // Texture + + const size = 200; + + const computeCloud = Fn(({ storageTexture }) => { + const scale = float(0.05); + const id = instanceIndex; + + const x = id.mod(size); + const y = id.div(size).mod(size); + const z = id.div(size * size); + + const coord3d = vec3(x, y, z); + const centered = coord3d.sub(size / 2).div(size); + const d = float(1.0).sub(centered.length()); + + const noiseCoord = coord3d.mul(scale.div(1.5)).add(time); + + const noise = mx_noise_vec3(noiseCoord).toConst('noise'); + + const data = noise.mul(d).mul(d).toConst('data'); + + textureStore(storageTexture, vec3(x, y, z), vec4(vec3(data.x), 1.0)); + }); + + const storageTexture = new THREE.Storage3DTexture(size, size, size); + storageTexture.generateMipmaps = false; + storageTexture.name = 'cloud'; + + computeNode = computeCloud({ storageTexture }) + .compute(size * size * size) + .setName('computeCloud'); + + // Shader + + const transparentRaymarchingTexture = Fn( + ({ texture, range = float(0.14), threshold = float(0.08), opacity = float(0.18), steps = float(100) }) => { + const finalColor = vec4(0).toVar(); + + RaymarchingBox(steps, ({ positionRay }) => { + const mapValue = float(texture.sample(positionRay.add(0.5)).r).toVar(); + + mapValue.assign(smoothstep(threshold.sub(range), threshold.add(range), mapValue).mul(opacity)); + + const shading = texture + .sample(positionRay.add(vec3(-0.01))) + .r.sub(texture.sample(positionRay.add(vec3(0.01))).r); + + const col = shading.mul(4.0).add(positionRay.x.add(positionRay.y).mul(0.5)).add(0.3); + + finalColor.rgb.addAssign(finalColor.a.oneMinus().mul(mapValue).mul(col)); + + finalColor.a.addAssign(finalColor.a.oneMinus().mul(mapValue)); + + If(finalColor.a.greaterThanEqual(0.95), () => { + Break(); + }); + }); + + return finalColor; + }, + ); + + // Material + + const baseColor = uniform(new THREE.Color(0x798aa0)); + const range = uniform(0.1); + const threshold = uniform(0.08); + const opacity = uniform(0.08); + const steps = uniform(100); + + const cloud3d = transparentRaymarchingTexture({ + texture: texture3D(storageTexture, null, 0), + range, + threshold, + opacity, + steps, + }); + + const finalCloud = cloud3d.setRGB(cloud3d.rgb.add(baseColor)); + + const material = new THREE.NodeMaterial(); + material.colorNode = finalCloud; + material.side = THREE.BackSide; + material.transparent = true; + material.name = 'transparentRaymarchingMaterial'; + + mesh = new THREE.Mesh(new THREE.BoxGeometry(10, 10, 10), material); + scene.add(mesh); + + mesh.rotation.y = Math.PI / 2; + + // + + renderer.compute(computeNode); + + const gui = renderer.inspector.createParameters('Settings'); + gui.add(threshold, 'value', 0, 1, 0.01).name('threshold'); + gui.add(opacity, 'value', 0, 1, 0.01).name('opacity'); + gui.add(range, 'value', 0, 1, 0.01).name('range'); + gui.add(steps, 'value', 0, 200, 1).name('steps'); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + renderer.compute(computeNode); + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_compute_texture_pingpong.ts b/examples-testing/examples/webgpu_compute_texture_pingpong.ts new file mode 100644 index 000000000..5c411fa69 --- /dev/null +++ b/examples-testing/examples/webgpu_compute_texture_pingpong.ts @@ -0,0 +1,170 @@ +import * as THREE from 'three/webgpu'; +import { + storageTexture, + textureStore, + Fn, + instanceIndex, + uniform, + float, + vec2, + vec4, + uvec2, + ivec2, + int, + NodeAccess, +} from 'three/tsl'; + +import WebGPU from 'three/addons/capabilities/WebGPU.js'; + +let camera, scene, renderer; +let computeInitNode, computeToPing, computeToPong; +let pingTexture, pongTexture; +let material; +let phase = true; +let lastUpdate = -1; + +const width = 512, + height = 512; + +const seed = uniform(new THREE.Vector2()); + +init(); + +async function init() { + if (WebGPU.isAvailable() === false) { + document.body.appendChild(WebGPU.getErrorMessage()); + + throw new Error('No WebGPU support'); + } + + const aspect = window.innerWidth / window.innerHeight; + camera = new THREE.OrthographicCamera(-aspect, aspect, 1, -1, 0, 2); + camera.position.z = 1; + + scene = new THREE.Scene(); + + // texture + + const hdr = true; + + pingTexture = new THREE.StorageTexture(width, height); + pongTexture = new THREE.StorageTexture(width, height); + + if (hdr) { + pingTexture.type = THREE.HalfFloatType; + pongTexture.type = THREE.HalfFloatType; + } + + const rand2 = Fn(([n]) => { + return n.dot(vec2(12.9898, 4.1414)).sin().mul(43758.5453).fract(); + }); + + // Create storage texture nodes with proper access + const writePing = storageTexture(pingTexture).setAccess(NodeAccess.WRITE_ONLY); + const readPing = storageTexture(pingTexture).setAccess(NodeAccess.READ_ONLY); + const writePong = storageTexture(pongTexture).setAccess(NodeAccess.WRITE_ONLY); + const readPong = storageTexture(pongTexture).setAccess(NodeAccess.READ_ONLY); + + const computeInit = Fn(() => { + const posX = instanceIndex.mod(width); + const posY = instanceIndex.div(width); + const indexUV = uvec2(posX, posY); + const uv = vec2(float(posX).div(width), float(posY).div(height)); + + const r = rand2(uv.add(seed.mul(100))).sub(rand2(uv.add(seed.mul(300)))); + const g = rand2(uv.add(seed.mul(200))).sub(rand2(uv.add(seed.mul(300)))); + const b = rand2(uv.add(seed.mul(200))).sub(rand2(uv.add(seed.mul(100)))); + + textureStore(writePing, indexUV, vec4(r, g, b, 1)); + }); + + computeInitNode = computeInit().compute(width * height); + + // compute ping-pong: blur function using .load() for textureLoad + const blur = Fn(([readTex, uv]) => { + const c0 = readTex.load(uv.add(ivec2(-1, 1))); + const c1 = readTex.load(uv.add(ivec2(-1, -1))); + const c2 = readTex.load(uv.add(ivec2(0, 0))); + const c3 = readTex.load(uv.add(ivec2(1, -1))); + const c4 = readTex.load(uv.add(ivec2(1, 1))); + + return c0.add(c1).add(c2).add(c3).add(c4).div(5.0); + }); + + // compute loop: read from one texture, blur, write to another + const computePingPong = Fn(([readTex, writeTex]) => { + const posX = instanceIndex.mod(width); + const posY = instanceIndex.div(width); + const indexUV = ivec2(int(posX), int(posY)); + + const color = blur(readTex, indexUV); + + textureStore(writeTex, indexUV, vec4(color.rgb.mul(1.05), 1)); + }); + + computeToPong = computePingPong(readPing, writePong).compute(width * height); + computeToPing = computePingPong(readPong, writePing).compute(width * height); + + // + + material = new THREE.MeshBasicMaterial({ color: 0xffffff, map: pongTexture }); + + const plane = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), material); + scene.add(plane); + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(render); + document.body.appendChild(renderer.domElement); + + await renderer.init(); + + window.addEventListener('resize', onWindowResize); + + // compute init + + renderer.compute(computeInitNode); +} + +function onWindowResize() { + renderer.setSize(window.innerWidth, window.innerHeight); + + const aspect = window.innerWidth / window.innerHeight; + + const frustumHeight = camera.top - camera.bottom; + + camera.left = (-frustumHeight * aspect) / 2; + camera.right = (frustumHeight * aspect) / 2; + + camera.updateProjectionMatrix(); +} + +function render() { + const time = performance.now(); + const seconds = Math.floor(time / 1000); + + // reset every second + + if (phase && seconds !== lastUpdate) { + seed.value.set(Math.random(), Math.random()); + + renderer.compute(computeInitNode); + + lastUpdate = seconds; + } + + // compute step + + renderer.compute(phase ? computeToPong : computeToPing); + + material.map = phase ? pongTexture : pingTexture; + + phase = !phase; + + // render step + + // update material texture node + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_cubemap_adjustments.ts b/examples-testing/examples/webgpu_cubemap_adjustments.ts new file mode 100644 index 000000000..2e886d824 --- /dev/null +++ b/examples-testing/examples/webgpu_cubemap_adjustments.ts @@ -0,0 +1,165 @@ +import * as THREE from 'three/webgpu'; +import { + uniform, + mix, + pmremTexture, + reference, + positionLocal, + hue, + saturation, + positionWorld, + normalWorld, + positionWorldDirection, + reflectVector, +} from 'three/tsl'; + +import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer; + +init(); + +async function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + const initialDistance = 2; + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); + camera.position.set(-1.8 * initialDistance, 0.6 * initialDistance, 2.7 * initialDistance); + + scene = new THREE.Scene(); + + // HDR textures + + const hdr1Texture = await new HDRLoader().loadAsync('./textures/equirectangular/pedestrian_overpass_1k.hdr'); + + hdr1Texture.mapping = THREE.EquirectangularReflectionMapping; + hdr1Texture.generateMipmaps = true; + hdr1Texture.minFilter = THREE.LinearMipmapLinearFilter; + + const hdr2Texture = await new HDRLoader().loadAsync('./textures/equirectangular/752-hdri-skies-com_1k.hdr'); + + hdr2Texture.mapping = THREE.EquirectangularReflectionMapping; + hdr2Texture.generateMipmaps = true; + hdr2Texture.minFilter = THREE.LinearMipmapLinearFilter; + + // nodes and environment + + const adjustments = { + mix: 0, + procedural: 0, + intensity: 1, + hue: 0, + saturation: 1, + }; + + const mixNode = reference('mix', 'float', adjustments); + const proceduralNode = reference('procedural', 'float', adjustments); + const intensityNode = reference('intensity', 'float', adjustments); + const hueNode = reference('hue', 'float', adjustments); + const saturationNode = reference('saturation', 'float', adjustments); + + const rotateY1Matrix = new THREE.Matrix4(); + const rotateY2Matrix = new THREE.Matrix4(); + + const getEnvironmentNode = (reflectNode, positionNode) => { + const custom1UV = reflectNode.xyz.mul(uniform(rotateY1Matrix)); + const custom2UV = reflectNode.xyz.mul(uniform(rotateY2Matrix)); + const mixCubeMaps = mix( + pmremTexture(hdr1Texture, custom1UV), + pmremTexture(hdr2Texture, custom2UV), + positionNode.y.add(mixNode).clamp(), + ); + + const proceduralEnv = mix(mixCubeMaps, normalWorld, proceduralNode); + + const intensityFilter = proceduralEnv.mul(intensityNode); + const hueFilter = hue(intensityFilter, hueNode); + return saturation(hueFilter, saturationNode); + }; + + const blurNode = uniform(0); + + scene.environmentNode = getEnvironmentNode(reflectVector, positionWorld); + + scene.backgroundNode = getEnvironmentNode(positionWorldDirection, positionLocal).context({ + getTextureLevel: () => blurNode, + }); + + // scene objects + + const loader = new GLTFLoader().setPath('models/gltf/DamagedHelmet/glTF/'); + const gltf = await loader.loadAsync('DamagedHelmet.gltf'); + + scene.add(gltf.scene); + + const sphereGeometry = new THREE.SphereGeometry(0.5, 64, 32); + + const sphereRightView = new THREE.Mesh( + sphereGeometry, + new THREE.MeshStandardMaterial({ roughness: 0, metalness: 1 }), + ); + sphereRightView.position.x += 2; + + const sphereLeftView = new THREE.Mesh( + sphereGeometry, + new THREE.MeshStandardMaterial({ roughness: 1, metalness: 1 }), + ); + sphereLeftView.position.x -= 2; + + scene.add(sphereLeftView); + scene.add(sphereRightView); + + // renderer and controls + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.toneMapping = THREE.LinearToneMapping; + renderer.inspector = new Inspector(); + renderer.setAnimationLoop(render); + container.appendChild(renderer.domElement); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 2; + controls.maxDistance = 10; + + window.addEventListener('resize', onWindowResize); + + // gui + + const gui = renderer.inspector.createParameters('Settings'); + gui.add(adjustments, 'mix', -1, 2, 0.01); + gui.add({ blurBackground: blurNode.value }, 'blurBackground', 0, 1, 0.01).onChange(value => { + blurNode.value = value; + }); + gui.add({ offsetHDR1: 0 }, 'offsetHDR1', 0, Math.PI * 2, 0.01).onChange(value => { + rotateY1Matrix.makeRotationY(value); + }); + gui.add({ offsetHDR2: 0 }, 'offsetHDR2', 0, Math.PI * 2, 0.01).onChange(value => { + rotateY2Matrix.makeRotationY(value); + }); + gui.add(adjustments, 'procedural', 0, 1, 0.01); + gui.add(adjustments, 'intensity', 0, 5, 0.01); + gui.add(adjustments, 'hue', 0, Math.PI * 2, 0.01); + gui.add(adjustments, 'saturation', 0, 2, 0.01); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_cubemap_dynamic.ts b/examples-testing/examples/webgpu_cubemap_dynamic.ts new file mode 100644 index 000000000..244b85935 --- /dev/null +++ b/examples-testing/examples/webgpu_cubemap_dynamic.ts @@ -0,0 +1,130 @@ +import * as THREE from 'three/webgpu'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { HDRCubeTextureLoader } from 'three/addons/loaders/HDRCubeTextureLoader.js'; +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer; +let cube, sphere, torus, material; + +let cubeCamera, cubeRenderTarget; + +let controls; + +init(); + +async function init() { + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.z = 75; + + scene = new THREE.Scene(); + + const uvTexture = new THREE.TextureLoader().load('./textures/uv_grid_opengl.jpg'); + + const hdrUrls = ['px.hdr', 'nx.hdr', 'py.hdr', 'ny.hdr', 'pz.hdr', 'nz.hdr']; + const texture = await new HDRCubeTextureLoader().setPath('./textures/cube/pisaHDR/').loadAsync(hdrUrls); + + texture.name = 'pisaHDR'; + texture.minFilter = THREE.LinearMipmapLinearFilter; + texture.magFilter = THREE.LinearFilter; + + scene.background = texture; + scene.environment = texture; + + // + + cubeRenderTarget = new THREE.CubeRenderTarget(256); + cubeRenderTarget.texture.type = THREE.HalfFloatType; + cubeRenderTarget.texture.minFilter = THREE.LinearMipmapLinearFilter; + cubeRenderTarget.texture.magFilter = THREE.LinearFilter; + cubeRenderTarget.texture.generateMipmaps = true; + + cubeCamera = new THREE.CubeCamera(1, 1000, cubeRenderTarget); + + // + + material = new THREE.MeshStandardNodeMaterial({ + envMap: cubeRenderTarget.texture, + roughness: 0.05, + metalness: 1, + }); + + sphere = new THREE.Mesh(new THREE.IcosahedronGeometry(15, 8), material); + scene.add(sphere); + + const material1 = new THREE.MeshStandardNodeMaterial({ + map: uvTexture, + roughness: 0.1, + metalness: 0, + }); + + const material2 = new THREE.MeshStandardNodeMaterial({ + map: uvTexture, + roughness: 0.1, + metalness: 0, + envMap: texture, + }); + + cube = new THREE.Mesh(new THREE.BoxGeometry(15, 15, 15), material1); + scene.add(cube); + + torus = new THREE.Mesh(new THREE.TorusKnotGeometry(8, 3, 128, 16), material2); + scene.add(torus); + + // + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animation); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + window.addEventListener('resize', onWindowResized); + + controls = new OrbitControls(camera, renderer.domElement); + controls.autoRotate = true; + + const gui = renderer.inspector.createParameters('Settings'); + gui.add(material, 'roughness', 0, 1); + gui.add(material, 'metalness', 0, 1); + gui.add(renderer, 'toneMappingExposure', 0, 2).name('exposure'); + gui.add(scene, 'environmentIntensity', 0, 1); + gui.add(material2, 'envMapIntensity', 0, 1); +} + +function onWindowResized() { + renderer.setSize(window.innerWidth, window.innerHeight); + + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); +} + +function animation(msTime) { + const time = msTime / 1000; + + cube.position.x = Math.cos(time) * 30; + cube.position.y = Math.sin(time) * 30; + cube.position.z = Math.sin(time) * 30; + + cube.rotation.x += 0.02; + cube.rotation.y += 0.03; + + torus.position.x = Math.cos(time + 10) * 30; + torus.position.y = Math.sin(time + 10) * 30; + torus.position.z = Math.sin(time + 10) * 30; + + torus.rotation.x += 0.02; + torus.rotation.y += 0.03; + + material.visible = false; + + cubeCamera.update(renderer, scene); + + material.visible = true; + + controls.update(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_cubemap_mix.ts b/examples-testing/examples/webgpu_cubemap_mix.ts new file mode 100644 index 000000000..6fbdb55ae --- /dev/null +++ b/examples-testing/examples/webgpu_cubemap_mix.ts @@ -0,0 +1,78 @@ +import * as THREE from 'three/webgpu'; +import { mix, oscSine, time, pmremTexture, float } from 'three/tsl'; + +import { HDRCubeTextureLoader } from 'three/addons/loaders/HDRCubeTextureLoader.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer; + +init(); + +async function init() { + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); + camera.position.set(-1.8, 0.6, 2.7); + + scene = new THREE.Scene(); + + const hdrUrls = ['px.hdr', 'nx.hdr', 'py.hdr', 'ny.hdr', 'pz.hdr', 'nz.hdr']; + const cube1Texture = new HDRCubeTextureLoader().setPath('./textures/cube/pisaHDR/').load(hdrUrls); + + cube1Texture.generateMipmaps = true; + cube1Texture.minFilter = THREE.LinearMipmapLinearFilter; + + const cube2Urls = [ + 'dark-s_px.jpg', + 'dark-s_nx.jpg', + 'dark-s_py.jpg', + 'dark-s_ny.jpg', + 'dark-s_pz.jpg', + 'dark-s_nz.jpg', + ]; + const cube2Texture = await new THREE.CubeTextureLoader().setPath('./textures/cube/MilkyWay/').loadAsync(cube2Urls); + + cube2Texture.generateMipmaps = true; + cube2Texture.minFilter = THREE.LinearMipmapLinearFilter; + + scene.environmentNode = mix(pmremTexture(cube2Texture), pmremTexture(cube1Texture), oscSine(time.mul(0.1))); + + scene.backgroundNode = scene.environmentNode.context({ + getTextureLevel: () => float(0.5), + }); + + const loader = new GLTFLoader().setPath('models/gltf/DamagedHelmet/glTF/'); + const gltf = await loader.loadAsync('DamagedHelmet.gltf'); + + scene.add(gltf.scene); + + renderer = new THREE.WebGPURenderer({ antialias: true }); + + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.toneMapping = THREE.LinearToneMapping; + renderer.inspector = new Inspector(); + renderer.setAnimationLoop(render); + document.body.appendChild(renderer.domElement); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 2; + controls.maxDistance = 10; + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_custom_fog.ts b/examples-testing/examples/webgpu_custom_fog.ts new file mode 100644 index 000000000..bcfcb0fd3 --- /dev/null +++ b/examples-testing/examples/webgpu_custom_fog.ts @@ -0,0 +1,135 @@ +import * as THREE from 'three/webgpu'; +import { color, fog, float, positionWorld, triNoise3D, positionView, normalWorld, uniform } from 'three/tsl'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer; +let controls; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 600); + camera.position.set(30, 15, 30); + + scene = new THREE.Scene(); + + // custom fog + + const skyColorValue = 0xf0f5f5; + const groundColorValue = 0xd0dee7; + + const skyColor = color(skyColorValue); + const groundColor = color(groundColorValue); + + const fogNoiseDistance = positionView.z.negate().smoothstep(0, camera.far - 300); + + const distance = fogNoiseDistance.mul(20).max(4); + const alpha = 0.98; + const groundFogArea = float(distance).sub(positionWorld.y).div(distance).pow(3).saturate().mul(alpha); + + // a alternative way to create a TimerNode + const timer = uniform(0).onFrameUpdate(frame => frame.time); + + const fogNoiseA = triNoise3D(positionWorld.mul(0.005), 0.2, timer); + const fogNoiseB = triNoise3D(positionWorld.mul(0.01), 0.2, timer.mul(1.2)); + + const fogNoise = fogNoiseA.add(fogNoiseB).mul(groundColor); + + // apply custom fog + + scene.fogNode = fog(fogNoiseDistance.oneMinus().mix(groundColor, fogNoise), groundFogArea); + scene.backgroundNode = normalWorld.y.max(0).mix(groundColor, skyColor); + + // builds + + const buildWindows = positionWorld.y + .mul(10) + .floor() + .mod(4) + .sign() + .mix(color(0x000066).add(fogNoiseDistance), color(0xffffff)); + + const buildGeometry = new THREE.BoxGeometry(1, 1, 1); + const buildMaterial = new THREE.MeshPhongNodeMaterial({ + colorNode: buildWindows, + }); + + const buildMesh = new THREE.InstancedMesh(buildGeometry, buildMaterial, 4000); + scene.add(buildMesh); + + const dummy = new THREE.Object3D(); + const center = new THREE.Vector3(); + + for (let i = 0; i < buildMesh.count; i++) { + const scaleY = Math.random() * 7 + 0.5; + + dummy.position.x = Math.random() * 600 - 300; + dummy.position.z = Math.random() * 600 - 300; + + const distance = Math.max(dummy.position.distanceTo(center) * 0.012, 1); + + dummy.position.y = 0.5 * scaleY * distance; + + dummy.scale.x = dummy.scale.z = Math.random() * 3 + 0.5; + dummy.scale.y = scaleY * distance; + + dummy.updateMatrix(); + + buildMesh.setMatrixAt(i, dummy.matrix); + } + + // lights + + scene.add(new THREE.HemisphereLight(skyColorValue, groundColorValue, 0.5)); + + // geometry + + const planeGeometry = new THREE.PlaneGeometry(200, 200); + const planeMaterial = new THREE.MeshPhongMaterial({ + color: 0x999999, + }); + + const ground = new THREE.Mesh(planeGeometry, planeMaterial); + ground.rotation.x = -Math.PI / 2; + ground.scale.multiplyScalar(3); + ground.castShadow = true; + ground.receiveShadow = true; + scene.add(ground); + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // controls + + controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, 2, 0); + controls.minDistance = 7; + controls.maxDistance = 100; + controls.maxPolarAngle = Math.PI / 2; + controls.autoRotate = true; + controls.autoRotateSpeed = 0.1; + controls.update(); + + window.addEventListener('resize', resize); +} + +function resize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + controls.update(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_custom_fog_background.ts b/examples-testing/examples/webgpu_custom_fog_background.ts new file mode 100644 index 000000000..a9b619345 --- /dev/null +++ b/examples-testing/examples/webgpu_custom_fog_background.ts @@ -0,0 +1,86 @@ +import * as THREE from 'three/webgpu'; +import { pass, color, rangeFogFactor } from 'three/tsl'; + +import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +let camera, scene, renderer; +let renderPipeline; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); + camera.position.set(-1.8, 0.6, 2.7); + + scene = new THREE.Scene(); + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.toneMapping = THREE.NoToneMapping; // apply tone mapping in post processing, instead + document.body.appendChild(renderer.domElement); + + // post processing + + // render scene pass + const scenePass = pass(scene, camera); + const scenePassViewZ = scenePass.getViewZNode(); + + // fog color + const fogColor = color(0x4080cc); // in sRGB color space + + // get fog factor from the scene pass context + // equivalent to: scene.fog = new THREE.Fog( 0x4080cc, 2.7, 4 ); + const fogFactor = rangeFogFactor(2.7, 4).context({ getViewZ: () => scenePassViewZ }); + + // tone-mapped scene pass + const scenePassTM = scenePass.toneMapping(THREE.ACESFilmicToneMapping, 1); + + // mix fog using fog factor and fog color + const compose = fogFactor.mix(scenePassTM, fogColor); + + renderPipeline = new THREE.RenderPipeline(renderer); + renderPipeline.outputColorTransform = true; // no tone mapping will be applied, only the default color space transform + renderPipeline.outputNode = compose; + + // + + new UltraHDRLoader().setPath('textures/equirectangular/').load('royal_esplanade_2k.hdr.jpg', function (texture) { + texture.mapping = THREE.EquirectangularReflectionMapping; + scene.environment = texture; + + // model + + const loader = new GLTFLoader().setPath('models/gltf/DamagedHelmet/glTF/'); + loader.load('DamagedHelmet.gltf', function (gltf) { + scene.add(gltf.scene); + }); + }); + + // + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 2; + controls.maxDistance = 5; + controls.target.set(0, -0.1, -0.2); + controls.update(); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_custom_fog_scattering.ts b/examples-testing/examples/webgpu_custom_fog_scattering.ts new file mode 100644 index 000000000..7f14133fd --- /dev/null +++ b/examples-testing/examples/webgpu_custom_fog_scattering.ts @@ -0,0 +1,161 @@ +import * as THREE from 'three/webgpu'; +import { positionWorld, densityFogFactor, pass, vec2, uniform, mix, color } from 'three/tsl'; +import { gaussianBlur } from 'three/addons/tsl/display/GaussianBlurNode.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer, renderPipeline, controls; + +const params = { + scatteringEnabled: true, +}; + +init(); + +async function init() { + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 600); + camera.position.set(30, 10, 30); + + scene = new THREE.Scene(); + + // fog + + scene.fog = new THREE.FogExp2(0xcccccc, 0.014); + scene.background = new THREE.Color(0xcccccc); + + // builds + + const buildWindows = positionWorld.y.mul(10).floor().mod(4).sign().mix(color(0x000066), color(0xffffff)); + + const buildGeometry = new THREE.BoxGeometry(1, 1, 1); + const buildMaterial = new THREE.MeshPhongNodeMaterial({ + colorNode: buildWindows, + }); + + const buildMesh = new THREE.InstancedMesh(buildGeometry, buildMaterial, 4000); + scene.add(buildMesh); + + const dummy = new THREE.Object3D(); + const center = new THREE.Vector3(); + + for (let i = 0; i < buildMesh.count; i++) { + const scaleY = Math.random() * 7 + 0.5; + + dummy.position.x = Math.random() * 600 - 300; + dummy.position.z = Math.random() * 600 - 300; + + const distance = Math.max(dummy.position.distanceTo(center) * 0.012, 1); + + dummy.position.y = 0.5 * scaleY * distance; + + dummy.scale.x = dummy.scale.z = Math.random() * 3 + 0.5; + dummy.scale.y = scaleY * distance; + + dummy.updateMatrix(); + + buildMesh.setMatrixAt(i, dummy.matrix); + } + + // lights + + scene.add(new THREE.HemisphereLight(0xf0f5f5, 0xd0dee7, 0.5)); + + // geometry + + const planeGeometry = new THREE.PlaneGeometry(200, 200); + const planeMaterial = new THREE.MeshPhongMaterial({ + color: 0x999999, + }); + + const ground = new THREE.Mesh(planeGeometry, planeMaterial); + ground.rotation.x = -Math.PI / 2; + ground.scale.multiplyScalar(3); + ground.castShadow = true; + ground.receiveShadow = true; + scene.add(ground); + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // controls + + controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, 2, 0); + controls.minDistance = 7; + controls.maxDistance = 100; + controls.maxPolarAngle = Math.PI / 2; + controls.enableDamping = true; + controls.update(); + + // + + renderPipeline = new THREE.RenderPipeline(renderer); + + // uniforms + + const density = uniform(0.014); + const scattering = uniform(2); + + // scene pass + + const scenePass = pass(scene, camera); + + const scenePassColor = scenePass.getTextureNode('output'); + const scenePassViewZ = scenePass.getViewZNode(); + + // blur pass (always downsampled to improve performance) + + const sceneColorBlurred = gaussianBlur(scenePassColor, vec2(scattering), 4, { resolutionScale: 0.5 }); + + // composite + + const fogFactor = densityFogFactor(density).context({ getViewZ: () => scenePassViewZ }); + + const compositeNode = mix(scenePassColor, sceneColorBlurred, fogFactor); + + renderPipeline.outputNode = compositeNode; + + // gui + + const gui = renderer.inspector.createParameters('Settings'); + gui.add(density, 'value', 0.005, 0.03) + .step(0.0001) + .name('fog density') + .onChange(value => { + scene.fog.density = value; + }); + gui.add(scattering, 'value', 0, 5).name('scattering factor'); + gui.add(params, 'scatteringEnabled') + .name('enable scattering') + .onChange(value => { + if (value === true) { + renderPipeline.outputNode = compositeNode; + } else { + renderPipeline.outputNode = scenePassColor; + } + + renderPipeline.needsUpdate = true; + }); + + window.addEventListener('resize', resize); +} + +function resize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + controls.update(); + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_display_stereo.ts b/examples-testing/examples/webgpu_display_stereo.ts new file mode 100644 index 000000000..7f1ade726 --- /dev/null +++ b/examples-testing/examples/webgpu_display_stereo.ts @@ -0,0 +1,191 @@ +import * as THREE from 'three/webgpu'; + +import { stereoPass } from 'three/addons/tsl/display/StereoPassNode.js'; +import { anaglyphPass, AnaglyphAlgorithm, AnaglyphColorMode } from 'three/addons/tsl/display/AnaglyphPassNode.js'; +import { parallaxBarrierPass } from 'three/addons/tsl/display/ParallaxBarrierPassNode.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer, renderPipeline; + +let stereo, anaglyph, parallaxBarrier; + +let mesh, dummy, timer; + +let anaglyphFolder; + +const position = new THREE.Vector3(); + +const params = { + effect: 'stereo', + eyeSep: 0.064, + planeDistance: 3, + anaglyphAlgorithm: 'dubois', + anaglyphColorMode: 'redCyan', +}; + +const effects = { Stereo: 'stereo', Anaglyph: 'anaglyph', ParallaxBarrier: 'parallaxBarrier' }; + +const anaglyphAlgorithms = { + True: AnaglyphAlgorithm.TRUE, + Grey: AnaglyphAlgorithm.GREY, + Colour: AnaglyphAlgorithm.COLOUR, + 'Half-Colour': AnaglyphAlgorithm.HALF_COLOUR, + Dubois: AnaglyphAlgorithm.DUBOIS, + Optimised: AnaglyphAlgorithm.OPTIMISED, + Compromise: AnaglyphAlgorithm.COMPROMISE, +}; + +const anaglyphColorModes = { + 'Red / Cyan': AnaglyphColorMode.RED_CYAN, + 'Magenta / Cyan': AnaglyphColorMode.MAGENTA_CYAN, + 'Magenta / Green': AnaglyphColorMode.MAGENTA_GREEN, +}; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.z = 3; + + scene = new THREE.Scene(); + scene.background = new THREE.CubeTextureLoader() + .setPath('textures/cube/Park3Med/') + .load(['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg']); + + timer = new THREE.Timer(); + timer.connect(document); + + const geometry = new THREE.SphereGeometry(0.1, 32, 16); + + const textureCube = new THREE.CubeTextureLoader() + .setPath('textures/cube/Park3Med/') + .load(['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg']); + + const material = new THREE.MeshBasicMaterial({ color: 0xffffff, envMap: textureCube }); + + mesh = new THREE.InstancedMesh(geometry, material, 500); + mesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage); + + dummy = new THREE.Mesh(); + + for (let i = 0; i < 500; i++) { + dummy.position.x = Math.random() * 10 - 5; + dummy.position.y = Math.random() * 10 - 5; + dummy.position.z = Math.random() * 10 - 5; + dummy.scale.x = dummy.scale.y = dummy.scale.z = Math.random() * 3 + 1; + + dummy.updateMatrix(); + + mesh.setMatrixAt(i, dummy.matrix); + } + + scene.add(mesh); + + // + + renderer = new THREE.WebGPURenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + renderPipeline = new THREE.RenderPipeline(renderer); + stereo = stereoPass(scene, camera); + anaglyph = anaglyphPass(scene, camera); + parallaxBarrier = parallaxBarrierPass(scene, camera); + + // Configure anaglyph for physically-correct stereo with zero parallax at scene center + anaglyph.eyeSep = params.eyeSep; + anaglyph.planeDistance = params.planeDistance; + + renderPipeline.outputNode = stereo; + + const gui = renderer.inspector.createParameters('Stereo Settings'); + gui.add(params, 'effect', effects).onChange(update); + gui.add(params, 'eyeSep', 0.001, 0.15, 0.001).onChange(function (value) { + stereo.stereo.eyeSep = value; + anaglyph.eyeSep = value; // Anaglyph has direct eyeSep property + parallaxBarrier.stereo.eyeSep = value; + }); + + // Anaglyph-specific settings folder + anaglyphFolder = gui.addFolder('Anaglyph Options'); + anaglyphFolder + .add(params, 'anaglyphAlgorithm', anaglyphAlgorithms) + .name('Algorithm') + .onChange(function (value) { + anaglyph.algorithm = value; + }); + anaglyphFolder + .add(params, 'anaglyphColorMode', anaglyphColorModes) + .name('Color Mode') + .onChange(function (value) { + anaglyph.colorMode = value; + }); + anaglyphFolder + .add(params, 'planeDistance', 0.5, 10, 0.1) + .name('Plane Distance') + .onChange(function (value) { + anaglyph.planeDistance = value; + }); + anaglyphFolder.paramList.domElement.style.display = 'none'; + + window.addEventListener('resize', onWindowResize); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 1; + controls.maxDistance = 25; +} + +function update(value) { + if (value === 'stereo') { + renderPipeline.outputNode = stereo; + anaglyphFolder.paramList.domElement.style.display = 'none'; + } else if (value === 'anaglyph') { + renderPipeline.outputNode = anaglyph; + anaglyphFolder.paramList.domElement.style.display = ''; + } else if (value === 'parallaxBarrier') { + renderPipeline.outputNode = parallaxBarrier; + anaglyphFolder.paramList.domElement.style.display = 'none'; + } + + renderPipeline.needsUpdate = true; +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function extractPosition(matrix, position) { + position.x = matrix.elements[12]; + position.y = matrix.elements[13]; + position.z = matrix.elements[14]; +} + +function animate() { + timer.update(); + + const elapsedTime = timer.getElapsed() * 0.1; + + for (let i = 0; i < mesh.count; i++) { + mesh.getMatrixAt(i, dummy.matrix); + + extractPosition(dummy.matrix, position); + + position.x = 5 * Math.cos(elapsedTime + i); + position.y = 5 * Math.sin(elapsedTime + i * 1.1); + + dummy.matrix.setPosition(position); + + mesh.setMatrixAt(i, dummy.matrix); + + mesh.instanceMatrix.needsUpdate = true; + } + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_equirectangular.ts b/examples-testing/examples/webgpu_equirectangular.ts new file mode 100644 index 000000000..9e159e465 --- /dev/null +++ b/examples-testing/examples/webgpu_equirectangular.ts @@ -0,0 +1,57 @@ +import * as THREE from 'three/webgpu'; +import { texture, equirectUV } from 'three/tsl'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer; +let controls; + +init(); + +function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); + camera.position.set(1, 0, 0); + + const equirectTexture = new THREE.TextureLoader().load('textures/2294472375_24a3b8ef46_o.jpg'); + equirectTexture.colorSpace = THREE.SRGBColorSpace; + + scene = new THREE.Scene(); + scene.backgroundNode = texture(equirectTexture, equirectUV(), 0); + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + container.appendChild(renderer.domElement); + + controls = new OrbitControls(camera, renderer.domElement); + controls.autoRotate = true; + controls.rotateSpeed = -0.125; // negative, to track mouse pointer + controls.autoRotateSpeed = 1.0; + + // GUI + + const gui = renderer.inspector.createParameters('Settings'); + + gui.add(scene, 'backgroundIntensity', 0, 1).name('background intensity'); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + controls.update(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_fog_height.ts b/examples-testing/examples/webgpu_fog_height.ts new file mode 100644 index 000000000..65bda9cd8 --- /dev/null +++ b/examples-testing/examples/webgpu_fog_height.ts @@ -0,0 +1,99 @@ +import * as THREE from 'three/webgpu'; +import { exponentialHeightFogFactor, uniform, fog, color } from 'three/tsl'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer; +let controls; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 600); + camera.position.set(20, 10, 25); + + scene = new THREE.Scene(); + + // height fog + + const density = uniform(0.04); + const height = uniform(2); + + const fogFactor = exponentialHeightFogFactor(density, height); + + scene.fogNode = fog(color(0xffdfc1), fogFactor); + scene.backgroundNode = color(0xffdfc1); + + // meshes + + const geometry = new THREE.BoxGeometry(1, 25, 1); + const material = new THREE.MeshPhongNodeMaterial({ color: 0xcd959a }); + + const mesh = new THREE.InstancedMesh(geometry, material, 100); + mesh.position.y = -10; + scene.add(mesh); + + const dummy = new THREE.Object3D(); + + let index = 0; + + for (let i = 0; i < 10; i++) { + for (let j = 0; j < 10; j++) { + dummy.position.x = -18 + i * 4; + dummy.position.z = -18 + j * 4; + + dummy.updateMatrix(); + + mesh.setMatrixAt(index++, dummy.matrix); + } + } + + // lights + + const directionalLight = new THREE.DirectionalLight(0xffc0cb, 2); + directionalLight.position.set(-10, 10, 10); + scene.add(directionalLight); + + const ambientLight = new THREE.AmbientLight(0xcccccc); + scene.add(ambientLight); + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // gui + + const gui = renderer.inspector.createParameters('Fog Settings'); + + gui.add(density, 'value', 0.001, 0.1).step(0.0001).name('Density'); + gui.add(height, 'value', -5, 5).name('Height'); + + // controls + + controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 7; + controls.maxDistance = 100; + controls.maxPolarAngle = Math.PI / 2; + controls.enableDamping = true; + + window.addEventListener('resize', resize); +} + +function resize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + controls.update(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_hdr.ts b/examples-testing/examples/webgpu_hdr.ts new file mode 100644 index 000000000..b96ba5f42 --- /dev/null +++ b/examples-testing/examples/webgpu_hdr.ts @@ -0,0 +1,124 @@ +import * as THREE from 'three/webgpu'; +import { pass, uv, uniform } from 'three/tsl'; +import WebGPU from 'three/addons/capabilities/WebGPU.js'; +import { afterImage } from 'three/addons/tsl/display/AfterImageNode.js'; +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { ExtendedSRGBColorSpace, ExtendedSRGBColorSpaceImpl } from 'three/addons/math/ColorSpaces.js'; + +const params = { + intensity: uniform(4.0, 'float').setName('intensity'), + hardness: uniform(0.4, 'float').setName('hardness'), + radius: uniform(0.5, 'float').setName('radius'), + afterImageDecay: uniform(0.985, 'float').setName('afterImageDecay'), +}; + +const hdrMediaQuery = window.matchMedia('(dynamic-range: high)'); + +function updateHDRWarning() { + const displayIsHDR = hdrMediaQuery.matches; + document.querySelector('#no-hdr').style.display = displayIsHDR ? 'none' : ''; +} + +hdrMediaQuery.addEventListener('change', updateHDRWarning); +updateHDRWarning(); + +if (WebGPU.isAvailable() === false) { + document.body.appendChild(WebGPU.getErrorMessage()); + throw new Error('No WebGPU support'); +} + +// Enable Extended sRGB output color space for HDR presentation +THREE.ColorManagement.define({ [ExtendedSRGBColorSpace]: ExtendedSRGBColorSpaceImpl }); + +// Renderer (HalfFloat output + Extended sRGB) +const renderer = new THREE.WebGPURenderer({ + antialias: true, + outputType: THREE.HalfFloatType, +}); + +renderer.outputColorSpace = ExtendedSRGBColorSpace; +renderer.setPixelRatio(window.devicePixelRatio); +renderer.setSize(window.innerWidth, window.innerHeight); +renderer.inspector = new Inspector(); +document.body.appendChild(renderer.domElement); + +const camera = new THREE.OrthographicCamera(0, window.innerWidth, window.innerHeight, 0, 1, 2); +camera.position.z = 1; + +// Brush scene (rendered into drawTarget) +const brushScene = new THREE.Scene(); + +brushScene.background = new THREE.Color(0xffffff); +const brushMat = new THREE.MeshBasicNodeMaterial(); +brushMat.transparent = true; +brushMat.depthTest = false; +brushMat.depthWrite = false; +brushMat.blending = THREE.AdditiveBlending; // additive to build HDR energy + +const renderPipeline = new THREE.RenderPipeline(renderer); +const brushPass = pass(brushScene, camera, { type: THREE.HalfFloatType }); +brushPass.renderTarget.texture.colorSpace = ExtendedSRGBColorSpace; + +renderPipeline.outputNode = afterImage(brushPass, params.afterImageDecay); + +// HDR brush uniforms +const uColor = params.intensity; +const uHard = params.hardness; +const uRadius = params.radius; + +// Radial falloff in TSL +const d = uv().sub(0.5).length(); +const t = d.div(uRadius); +const a = t.clamp().oneMinus().pow(uHard.mul(8.0).add(1.0)); + +brushMat.colorNode = uColor.mul(a); +brushMat.opacityNode = a; // premultiplied style with additive blending + +const brushMesh = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), brushMat); +brushMesh.scale.set(300, 300, 1); // ~300px default brush size +brushScene.add(brushMesh); + +function onPointerMove(e) { + const rect = renderer.domElement.getBoundingClientRect(); + const x = e.clientX - rect.left; + const y = e.clientY - rect.top; + + // camera has origin at bottom-left (0,0) + brushMesh.position.set(x, window.innerHeight - y, 0); +} + +window.addEventListener('pointermove', onPointerMove, { passive: false }); + +// Prevent mobile scroll on touch +renderer.domElement.addEventListener('touchstart', e => e.preventDefault(), { passive: false }); +renderer.domElement.addEventListener('touchmove', e => e.preventDefault(), { passive: false }); +renderer.domElement.addEventListener('touchend', e => e.preventDefault(), { passive: false }); + +// GUI setup +const gui = renderer.inspector.createParameters('Settings'); + +const colorFolder = gui.addFolder('HDR'); +colorFolder.add(params.intensity, 'value', 0, 10, 0.1).name('Intensity'); + +const brushFolder = gui.addFolder('Brush Settings'); +brushFolder.add(params.hardness, 'value', 0, 0.99, 0.01).name('Hardness'); +brushFolder.add(params.radius, 'value', 0.1, 2.0, 0.01).name('Radius'); + +const effectFolder = gui.addFolder('Effects'); +effectFolder.add(params.afterImageDecay, 'value', 0.9, 0.999, 0.001).name('After Image Decay'); + +// Resize handling +function onResize() { + renderer.setSize(window.innerWidth, window.innerHeight); + camera.right = window.innerWidth; + camera.top = window.innerHeight; + camera.updateProjectionMatrix(); +} + +window.addEventListener('resize', onResize); + +// Main loop +renderer.setAnimationLoop(async () => { + renderPipeline.render(); +}); diff --git a/examples-testing/examples/webgpu_instance_mesh.ts b/examples-testing/examples/webgpu_instance_mesh.ts new file mode 100644 index 000000000..0abd2f26c --- /dev/null +++ b/examples-testing/examples/webgpu_instance_mesh.ts @@ -0,0 +1,98 @@ +import * as THREE from 'three/webgpu'; +import { mix, range, normalWorld, oscSine, time } from 'three/tsl'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer; + +let mesh; +const amount = parseInt(window.location.search.slice(1)) || 10; +const count = Math.pow(amount, 3); +const dummy = new THREE.Object3D(); + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(amount * 0.9, amount * 0.9, amount * 0.9); + camera.lookAt(0, 0, 0); + + scene = new THREE.Scene(); + + const material = new THREE.MeshBasicMaterial(); + + // random colors between instances from 0x000000 to 0xFFFFFF + const randomColors = range(new THREE.Color(0x000000), new THREE.Color(0xffffff)); + + material.colorNode = mix(normalWorld, randomColors, oscSine(time.mul(0.1))); + + const loader = new THREE.BufferGeometryLoader(); + loader.load('models/json/suzanne_buffergeometry.json', function (geometry) { + geometry.computeVertexNormals(); + geometry.scale(0.5, 0.5, 0.5); + + mesh = new THREE.InstancedMesh(geometry, material, count); + mesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage); + + scene.add(mesh); + + // + + const gui = renderer.inspector.createParameters('Settings'); + gui.add(mesh, 'count', 1, count, 1).name('instance count'); + }); + + // + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + render(); +} + +async function render() { + if (mesh) { + const time = Date.now() * 0.001; + + mesh.rotation.x = Math.sin(time / 4); + mesh.rotation.y = Math.sin(time / 2); + + let i = 0; + const offset = (amount - 1) / 2; + + for (let x = 0; x < amount; x++) { + for (let y = 0; y < amount; y++) { + for (let z = 0; z < amount; z++) { + dummy.position.set(offset - x, offset - y, offset - z); + dummy.rotation.y = Math.sin(x / 4 + time) + Math.sin(y / 4 + time) + Math.sin(z / 4 + time); + dummy.rotation.z = dummy.rotation.y * 2; + + dummy.updateMatrix(); + + mesh.setMatrixAt(i++, dummy.matrix); + } + } + } + } + + await renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_instance_path.ts b/examples-testing/examples/webgpu_instance_path.ts new file mode 100644 index 000000000..a5bce3225 --- /dev/null +++ b/examples-testing/examples/webgpu_instance_path.ts @@ -0,0 +1,147 @@ +import * as THREE from 'three/webgpu'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { + abs, + add, + instancedBufferAttribute, + positionLocal, + mod, + time, + sin, + vec3, + select, + float, + screenUV, + color, +} from 'three/tsl'; + +let camera, scene, renderer, controls; + +const count = 1000; + +init(); + +async function init() { + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.01, 100); + camera.position.z = 15; + + scene = new THREE.Scene(); + scene.backgroundNode = screenUV.distance(0.5).remap(0, 0.65).mix(color(0x94254c), color(0x000000)); + + // generate a path representing a heart shape + + const x = 0, + y = 0; + + const path = new THREE.Path() + .moveTo(x - 2.5, y - 2.5) + .bezierCurveTo(x - 2.5, y - 2.5, x - 2, y, x, y) + .bezierCurveTo(x + 3, y, x + 3, y - 3.5, x + 3, y - 3.5) + .bezierCurveTo(x + 3, y - 5.5, x + 1, y - 7.7, x - 2.5, y - 9.5) + .bezierCurveTo(x - 6, y - 7.7, x - 8, y - 5.5, x - 8, y - 3.5) + .bezierCurveTo(x - 8, y - 3.5, x - 8, y, x - 5, y) + .bezierCurveTo(x - 3.5, y, x - 2.5, y - 2.5, x - 2.5, y - 2.5); + + // generate instanced ico-spheres along the path + + const geometry = new THREE.IcosahedronGeometry(0.1); + const material = new THREE.MeshStandardNodeMaterial(); + + const mesh = new THREE.Mesh(geometry, material); + mesh.position.set(2.5, 5, 0); + mesh.count = count; + mesh.frustumCulled = false; + + scene.add(mesh); + + // instance data + + const v = new THREE.Vector3(); + const c = new THREE.Color(); + + const positions = []; + const times = []; + const seeds = []; + const colors = []; + + for (let i = 0; i < count; i++) { + const t = i / count; + path.getPointAt(t, v); + + v.x += 0.5 - Math.random(); + v.y += 0.5 - Math.random(); + v.z = 0.5 - Math.random(); + + positions.push(v.x, v.y, v.z); + times.push(t); + seeds.push(Math.random()); + + c.setHSL(0.75 + Math.random() * 0.25, 1, 0.4); + + colors.push(c.r, c.g, c.b); + } + + const positionAttribute = new THREE.InstancedBufferAttribute(new Float32Array(positions), 3); + const colorAttribute = new THREE.InstancedBufferAttribute(new Float32Array(colors), 3); + const timeAttribute = new THREE.InstancedBufferAttribute(new Float32Array(times), 1); + const seedAttribute = new THREE.InstancedBufferAttribute(new Float32Array(seeds), 1); + + // TSL + + const instancePosition = instancedBufferAttribute(positionAttribute); + const instanceColor = instancedBufferAttribute(colorAttribute); + const instanceSeed = instancedBufferAttribute(seedAttribute); + const instanceTime = instancedBufferAttribute(timeAttribute); + + const localTime = instanceTime.add(time); + const modTime = mod(time.mul(0.4), 1); + + const s0 = sin(localTime.add(instanceSeed)).mul(0.25); + + const dist = abs(instanceTime.sub(modTime)).toConst(); // modTime and instanceTime are in the range [0,1] + const wrapDist = select(dist.greaterThan(0.5), dist.oneMinus(), dist).toConst(); // the normalized distance should wrap around 0/1 + const s1 = select(wrapDist.greaterThan(0.1), float(1), wrapDist.remap(0, 0.1, 3, 1)); // compute a scale in a range around the current interpolated value + + const offset = vec3(instancePosition.x, instancePosition.y.add(s0), instancePosition.z).toConst('offset'); + material.positionNode = add(positionLocal.mul(s1), offset); + material.colorNode = instanceColor; + + // + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.toneMapping = THREE.NeutralToneMapping; + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + await renderer.init(); + + const pmremGenerator = new THREE.PMREMGenerator(renderer); + scene.environment = pmremGenerator.fromScene(new RoomEnvironment(), 0.04).texture; + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + controls.update(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_instance_points.ts b/examples-testing/examples/webgpu_instance_points.ts new file mode 100644 index 000000000..409129373 --- /dev/null +++ b/examples-testing/examples/webgpu_instance_points.ts @@ -0,0 +1,203 @@ +import * as THREE from 'three/webgpu'; +import { + color, + storage, + Fn, + instancedBufferAttribute, + instanceIndex, + sin, + time, + float, + uniform, + shapeCircle, + mix, + vec3, +} from 'three/tsl'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import * as GeometryUtils from 'three/addons/utils/GeometryUtils.js'; + +let renderer, scene, camera, camera2, controls, backgroundNode; +let material; +let effectController; + +// viewport +let insetWidth; +let insetHeight; + +// compute +let computeSize; + +init(); + +async function init() { + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(-40, 0, 60); + + camera2 = new THREE.PerspectiveCamera(40, 1, 1, 1000); + camera2.position.copy(camera.position); + + backgroundNode = color(0x222222); + + effectController = { + pulseSpeed: uniform(6), + minWidth: uniform(6), + maxWidth: uniform(20), + }; + + // Position and THREE.Color Data + + const points = GeometryUtils.hilbert3D(new THREE.Vector3(0, 0, 0), 20.0, 1, 0, 1, 2, 3, 4, 5, 6, 7); + + const spline = new THREE.CatmullRomCurve3(points); + const divisions = Math.round(4 * points.length); + const point = new THREE.Vector3(); + const pointColor = new THREE.Color(); + + const positions = []; + const colors = []; + const sizes = new Float32Array(divisions); + + for (let i = 0, l = divisions; i < l; i++) { + const t = i / l; + + spline.getPoint(t, point); + positions.push(point.x, point.y, point.z); + + pointColor.setHSL(t, 1.0, 0.5, THREE.SRGBColorSpace); + colors.push(pointColor.r, pointColor.g, pointColor.b); + + sizes[i] = 10.0; + } + + // Instanced Points + + const positionAttribute = new THREE.InstancedBufferAttribute(new Float32Array(positions), 3); + const colorsAttribute = new THREE.InstancedBufferAttribute(new Float32Array(colors), 3); + + const instanceSizeBufferAttribute = new THREE.StorageInstancedBufferAttribute(sizes, 1); + const instanceSizeStorage = storage(instanceSizeBufferAttribute, 'float', instanceSizeBufferAttribute.count); + + computeSize = Fn(() => { + const { pulseSpeed, minWidth, maxWidth } = effectController; + + const relativeTime = time.add(float(instanceIndex)); + + const sizeFactor = sin(relativeTime.mul(pulseSpeed)).add(1).div(2); + + instanceSizeStorage.element(instanceIndex).assign(sizeFactor.mul(maxWidth.sub(minWidth)).add(minWidth)); + })().compute(divisions); + + // Material / Sprites + + const attributeRange = instancedBufferAttribute(instanceSizeBufferAttribute); + const pointColors = mix( + vec3(0.0), + instancedBufferAttribute(colorsAttribute), + attributeRange.div(float(effectController.maxWidth)), + ); + + material = new THREE.PointsNodeMaterial({ + colorNode: pointColors, + opacityNode: shapeCircle(), + positionNode: instancedBufferAttribute(positionAttribute), + // rotationNode: time, + sizeNode: instancedBufferAttribute(instanceSizeBufferAttribute), + // size: 40, // in pixels units + vertexColors: true, + sizeAttenuation: false, + alphaToCoverage: true, + }); + + const instancedPoints = new THREE.Sprite(material); + instancedPoints.count = divisions; + scene.add(instancedPoints); + + // Renderer / Controls + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + controls.minDistance = 10; + controls.maxDistance = 500; + + window.addEventListener('resize', onWindowResize); + onWindowResize(); + + // GUI + + const gui = renderer.inspector.createParameters('Settings'); + + gui.add(material, 'alphaToCoverage'); + + gui.add(effectController.minWidth, 'value', 1, 30, 1).name('minWidth'); + gui.add(effectController.maxWidth, 'value', 2, 30, 1).name('maxWidth'); + gui.add(effectController.pulseSpeed, 'value', 1, 20, 0.1).name('pulseSpeed'); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + insetWidth = window.innerHeight / 4; // square + insetHeight = window.innerHeight / 4; + + camera2.aspect = insetWidth / insetHeight; + camera2.updateProjectionMatrix(); +} + +function animate() { + // compute + + renderer.compute(computeSize); + + // main scene + + renderer.setViewport(0, 0, window.innerWidth, window.innerHeight); + + controls.update(); + + renderer.autoClear = true; + + scene.backgroundNode = null; + + renderer.render(scene, camera); + + // inset scene + + const posY = window.innerHeight - insetHeight - 20; + + renderer.clearDepth(); // important! + + renderer.setScissorTest(true); + + renderer.setScissor(20, posY, insetWidth, insetHeight); + + renderer.setViewport(20, posY, insetWidth, insetHeight); + + camera2.position.copy(camera.position); + + camera2.quaternion.copy(camera.quaternion); + + renderer.autoClear = false; + + scene.backgroundNode = backgroundNode; + + renderer.render(scene, camera2); + + renderer.setScissorTest(false); +} + +// diff --git a/examples-testing/examples/webgpu_instancing_morph.ts b/examples-testing/examples/webgpu_instancing_morph.ts new file mode 100644 index 000000000..f8f5c5fe5 --- /dev/null +++ b/examples-testing/examples/webgpu_instancing_morph.ts @@ -0,0 +1,145 @@ +import * as THREE from 'three/webgpu'; + +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer, mesh, mixer, dummy; + +const offset = 5000; + +const timeOffsets = new Float32Array(1024); + +for (let i = 0; i < 1024; i++) { + timeOffsets[i] = Math.random() * 3; +} + +const timer = new THREE.Timer(); +timer.connect(document); + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 100, 10000); + + scene = new THREE.Scene(); + + scene.background = new THREE.Color(0x99ddff); + + scene.fog = new THREE.Fog(0x99ddff, 5000, 10000); + + // + + const light = new THREE.DirectionalLight(0xffffff, 1); + + light.position.set(200, 1000, 50); + + light.shadow.mapSize.width = 2048; + light.shadow.mapSize.height = 2048; + light.castShadow = true; + + light.shadow.camera.left = -5000; + light.shadow.camera.right = 5000; + light.shadow.camera.top = 5000; + light.shadow.camera.bottom = -5000; + light.shadow.camera.far = 2000; + + light.shadow.camera.updateProjectionMatrix(); + + scene.add(light); + + const hemi = new THREE.HemisphereLight(0x99ddff, 0x669933, 1 / 3); + + scene.add(hemi); + + const ground = new THREE.Mesh( + new THREE.PlaneGeometry(1000000, 1000000), + new THREE.MeshStandardMaterial({ color: 0x669933 }), + ); + + ground.rotation.x = -Math.PI / 2; + + ground.receiveShadow = true; + + scene.add(ground); + + const loader = new GLTFLoader(); + + loader.load('models/gltf/Horse.glb', function (glb) { + dummy = glb.scene.children[0]; + + mesh = new THREE.InstancedMesh( + dummy.geometry, + new THREE.MeshStandardNodeMaterial({ + flatShading: true, + }), + 1024, + ); + + mesh.castShadow = true; + + for (let x = 0, i = 0; x < 32; x++) { + for (let y = 0; y < 32; y++) { + dummy.position.set(offset - 300 * x + 200 * Math.random(), 0, offset - 300 * y); + + dummy.updateMatrix(); + + mesh.setMatrixAt(i, dummy.matrix); + + mesh.setColorAt(i, new THREE.Color(`hsl(${Math.random() * 360}, 50%, 66%)`)); + + i++; + } + } + + scene.add(mesh); + + mixer = new THREE.AnimationMixer(glb.scene); + + const action = mixer.clipAction(glb.animations[0]); + + action.play(); + }); + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setAnimationLoop(animate); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.shadowMap.enabled = true; + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + timer.update(); + + const time = timer.getElapsed(); + + const r = 3000; + camera.position.set(Math.sin(time / 10) * r, 1500 + 1000 * Math.cos(time / 5), Math.cos(time / 10) * r); + camera.lookAt(0, 0, 0); + + if (mesh) { + for (let i = 0; i < 1024; i++) { + mixer.setTime(time + timeOffsets[i]); + + mesh.setMorphAt(i, dummy); + } + + mesh.morphTexture.needsUpdate = true; + } + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_layers.ts b/examples-testing/examples/webgpu_layers.ts new file mode 100644 index 000000000..c1db7d60c --- /dev/null +++ b/examples-testing/examples/webgpu_layers.ts @@ -0,0 +1,144 @@ +import * as THREE from 'three/webgpu'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { positionLocal, time, mod, instancedBufferAttribute, rotate, screenUV, color, vec2 } from 'three/tsl'; + +let camera, scene, renderer; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); + camera.layers.enable(0); // enabled by default + camera.layers.enable(1); + camera.layers.enable(2); + + camera.position.z = 10; + + scene = new THREE.Scene(); + + const horizontalEffect = screenUV.x.mix(color(0xf996ae), color(0xf6f0a3)); + const lightEffect = screenUV.distance(vec2(0.5, 1.0)).oneMinus().mul(color(0xd9b6fd)); + + scene.backgroundNode = horizontalEffect.add(lightEffect); + + const sprite = new THREE.TextureLoader().load('textures/sprites/blossom.png'); + sprite.colorSpace = THREE.SRGBColorSpace; + + const count = 2500; + + const geometry = new THREE.PlaneGeometry(0.25, 0.25); + + const colors = [0xd70654, 0xffd95f, 0xb8d576]; + + for (let i = 0; i < 3; i++) { + const particles = new THREE.Mesh(geometry, getMaterial(count, colors[i], sprite)); + particles.layers.set(i); + particles.count = count; + scene.add(particles); + } + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // GUI + + const layers = { + Red: true, + Yellow: true, + Green: true, + }; + + const gui = renderer.inspector.createParameters('Layers'); + + gui.add(layers, 'Red').onChange(() => { + camera.layers.toggle(0); + }); + + gui.add(layers, 'Yellow').onChange(() => { + camera.layers.toggle(1); + }); + + gui.add(layers, 'Green').onChange(() => { + camera.layers.toggle(2); + }); + + // + + window.addEventListener('resize', onWindowResize); +} + +function getMaterial(count, color, sprite) { + // instance data + + const positions = []; + const rotations = []; + const directions = []; + const timeOffsets = []; + + const v = new THREE.Vector3(); + + for (let i = 0; i < count; i++) { + positions.push( + THREE.MathUtils.randFloat(-25, -20), + THREE.MathUtils.randFloat(-10, 50), + THREE.MathUtils.randFloat(-5, 5), + ); + + v.set(THREE.MathUtils.randFloat(0.7, 0.9), THREE.MathUtils.randFloat(-0.3, -0.15), 0).normalize(); + + rotations.push(Math.random(), Math.random(), Math.random()); + + directions.push(v.x, v.y, v.z); + + timeOffsets.push(i / count); + } + + const positionAttribute = new THREE.InstancedBufferAttribute(new Float32Array(positions), 3); + const rotationAttribute = new THREE.InstancedBufferAttribute(new Float32Array(rotations), 3); + const directionAttribute = new THREE.InstancedBufferAttribute(new Float32Array(directions), 3); + const timeAttribute = new THREE.InstancedBufferAttribute(new Float32Array(timeOffsets), 1); + + // material + + const material = new THREE.MeshBasicNodeMaterial({ + color: color, + map: sprite, + alphaMap: sprite, + alphaTest: 0.1, + side: THREE.DoubleSide, + forceSinglePass: true, + }); + + // TSL + + const instancePosition = instancedBufferAttribute(positionAttribute); + const instanceDirection = instancedBufferAttribute(directionAttribute); + const instanceRotation = instancedBufferAttribute(rotationAttribute); + + const localTime = instancedBufferAttribute(timeAttribute).add(time.mul(0.02)); + const modTime = mod(localTime, 1.0); + + const rotatedPosition = rotate(positionLocal, instanceRotation.mul(modTime.mul(20))); + material.positionNode = rotatedPosition.add(instancePosition).add(instanceDirection.mul(modTime.mul(50))); + + return material; +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_lensflares.ts b/examples-testing/examples/webgpu_lensflares.ts new file mode 100644 index 000000000..ab4e49677 --- /dev/null +++ b/examples-testing/examples/webgpu_lensflares.ts @@ -0,0 +1,137 @@ +import * as THREE from 'three/webgpu'; + +import { FlyControls } from 'three/addons/controls/FlyControls.js'; +import { LensflareMesh, LensflareElement } from 'three/addons/objects/LensflareMesh.js'; +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let container; + +let camera, scene, renderer; +let controls; + +const timer = new THREE.Timer(); +timer.connect(document); + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + // camera + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 15000); + camera.position.z = 250; + + // scene + + scene = new THREE.Scene(); + scene.background = new THREE.Color().setHSL(0.51, 0.4, 0.01, THREE.SRGBColorSpace); + scene.fog = new THREE.Fog(scene.background, 3500, 15000); + + // world + + const s = 250; + + const geometry = new THREE.BoxGeometry(s, s, s); + const material = new THREE.MeshPhongNodeMaterial({ color: 0xffffff, specular: 0xffffff, shininess: 50 }); + + for (let i = 0; i < 3000; i++) { + const mesh = new THREE.Mesh(geometry, material); + + mesh.position.x = 8000 * (2.0 * Math.random() - 1.0); + mesh.position.y = 8000 * (2.0 * Math.random() - 1.0); + mesh.position.z = 8000 * (2.0 * Math.random() - 1.0); + + mesh.rotation.x = Math.random() * Math.PI; + mesh.rotation.y = Math.random() * Math.PI; + mesh.rotation.z = Math.random() * Math.PI; + + mesh.matrixAutoUpdate = false; + mesh.updateMatrix(); + + scene.add(mesh); + } + + // lights + + const dirLight = new THREE.DirectionalLight(0xffffff, 0.15); + dirLight.position.set(0, -1, 0).normalize(); + dirLight.color.setHSL(0.1, 0.7, 0.5); + scene.add(dirLight); + + // lensflares + const textureLoader = new THREE.TextureLoader(); + + const textureFlare0 = textureLoader.load('textures/lensflare/lensflare0.png'); + const textureFlare3 = textureLoader.load('textures/lensflare/lensflare3.png'); + + textureFlare0.colorSpace = THREE.SRGBColorSpace; + textureFlare3.colorSpace = THREE.SRGBColorSpace; + + addLight(0.55, 0.95, 0.6, 5000, 0, -1000); + addLight(0.1, 0.85, 0.65, 0, 0, -1000); + addLight(0.995, 0.5, 0.95, 5000, 5000, -1000); + + function addLight(h, s, l, x, y, z) { + const light = new THREE.PointLight(0xffffff, 1.5, 2000, 0); + light.color.setHSL(h, s, l); + light.position.set(x, y, z); + scene.add(light); + + const lensflare = new LensflareMesh(); + lensflare.addElement(new LensflareElement(textureFlare0, 700, 0, light.color)); + lensflare.addElement(new LensflareElement(textureFlare3, 60, 0.6)); + lensflare.addElement(new LensflareElement(textureFlare3, 70, 0.7)); + lensflare.addElement(new LensflareElement(textureFlare3, 120, 0.9)); + lensflare.addElement(new LensflareElement(textureFlare3, 70, 1)); + light.add(lensflare); + } + + // renderer + + renderer = new THREE.WebGPURenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + container.appendChild(renderer.domElement); + + // + + controls = new FlyControls(camera, renderer.domElement); + + controls.movementSpeed = 2500; + controls.domElement = container; + controls.rollSpeed = Math.PI / 6; + controls.autoForward = false; + controls.dragToLook = false; + + // events + + window.addEventListener('resize', onWindowResize); +} + +// + +function onWindowResize() { + renderer.setSize(window.innerWidth, window.innerHeight); + + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); +} + +// + +function animate() { + timer.update(); + + render(); +} + +function render() { + const delta = timer.getDelta(); + + controls.update(delta); + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_lightprobe.ts b/examples-testing/examples/webgpu_lightprobe.ts new file mode 100644 index 000000000..3e90b6f66 --- /dev/null +++ b/examples-testing/examples/webgpu_lightprobe.ts @@ -0,0 +1,136 @@ +import * as THREE from 'three/webgpu'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { LightProbeGenerator } from 'three/addons/lights/LightProbeGenerator.js'; + +import { LightProbeHelper } from 'three/addons/helpers/LightProbeHelperGPU.js'; + +let mesh, renderer, scene, camera; + +let gui; + +let lightProbe; +let directionalLight; + +// linear color space +const API = { + lightProbeIntensity: 1.0, + directionalLightIntensity: 0.6, + envMapIntensity: 1, +}; + +init(); + +function init() { + // renderer + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // tone mapping + renderer.toneMapping = THREE.NoToneMapping; + + // scene + scene = new THREE.Scene(); + + // camera + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(0, 0, 30); + + // controls + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 10; + controls.maxDistance = 50; + controls.enablePan = false; + + // probe + lightProbe = new THREE.LightProbe(); + scene.add(lightProbe); + + // light + directionalLight = new THREE.DirectionalLight(0xffffff, API.directionalLightIntensity); + directionalLight.position.set(10, 10, 10); + scene.add(directionalLight); + + // envmap + const genCubeUrls = function (prefix, postfix) { + return [ + prefix + 'px' + postfix, + prefix + 'nx' + postfix, + prefix + 'py' + postfix, + prefix + 'ny' + postfix, + prefix + 'pz' + postfix, + prefix + 'nz' + postfix, + ]; + }; + + const urls = genCubeUrls('textures/cube/pisa/', '.png'); + + new THREE.CubeTextureLoader().load(urls, function (cubeTexture) { + scene.background = cubeTexture; + + lightProbe.copy(LightProbeGenerator.fromCubeTexture(cubeTexture)); + lightProbe.intensity = API.lightProbeIntensity; + lightProbe.position.set(-10, 0, 0); // position not used in scene lighting calculations (helper honors the position, however) + + const geometry = new THREE.SphereGeometry(5, 64, 32); + //const geometry = new THREE.TorusKnotGeometry( 4, 1.5, 256, 32, 2, 3 ); + + const material = new THREE.MeshStandardMaterial({ + color: 0xffffff, + metalness: 0, + roughness: 0, + envMap: cubeTexture, + envMapIntensity: API.envMapIntensity, + }); + + // mesh + mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + // helper + const helper = new LightProbeHelper(lightProbe, 1); + scene.add(helper); + }); + + // gui + gui = renderer.inspector.createParameters('Intensity'); + + gui.add(API, 'lightProbeIntensity', 0, 1, 0.02) + .name('light probe') + .onChange(function () { + lightProbe.intensity = API.lightProbeIntensity; + }); + + gui.add(API, 'directionalLightIntensity', 0, 1, 0.02) + .name('directional light') + .onChange(function () { + directionalLight.intensity = API.directionalLightIntensity; + }); + + gui.add(API, 'envMapIntensity', 0, 1, 0.02) + .name('envMap') + .onChange(function () { + mesh.material.envMapIntensity = API.envMapIntensity; + }); + + // listener + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + renderer.setSize(window.innerWidth, window.innerHeight); + + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); +} + +function animate() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_lightprobe_cubecamera.ts b/examples-testing/examples/webgpu_lightprobe_cubecamera.ts new file mode 100644 index 000000000..60fe6ccc9 --- /dev/null +++ b/examples-testing/examples/webgpu_lightprobe_cubecamera.ts @@ -0,0 +1,90 @@ +import * as THREE from 'three/webgpu'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { LightProbeHelper } from 'three/addons/helpers/LightProbeHelperGPU.js'; +import { LightProbeGenerator } from 'three/addons/lights/LightProbeGenerator.js'; + +let renderer, scene, camera, cubeCamera; + +let lightProbe; + +init(); + +function init() { + // renderer + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // scene + scene = new THREE.Scene(); + + // camera + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(0, 0, 30); + + const cubeRenderTarget = new THREE.CubeRenderTarget(256); + + cubeCamera = new THREE.CubeCamera(1, 1000, cubeRenderTarget); + + // controls + const controls = new OrbitControls(camera, renderer.domElement); + controls.addEventListener('change', render); + controls.minDistance = 10; + controls.maxDistance = 50; + controls.enablePan = false; + + // probe + lightProbe = new THREE.LightProbe(); + scene.add(lightProbe); + + // envmap + const genCubeUrls = function (prefix, postfix) { + return [ + prefix + 'px' + postfix, + prefix + 'nx' + postfix, + prefix + 'py' + postfix, + prefix + 'ny' + postfix, + prefix + 'pz' + postfix, + prefix + 'nz' + postfix, + ]; + }; + + const urls = genCubeUrls('textures/cube/pisa/', '.png'); + + new THREE.CubeTextureLoader().load(urls, async function (cubeTexture) { + scene.background = cubeTexture; + + await renderer.init(); + + cubeCamera.update(renderer, scene); + + const probe = await LightProbeGenerator.fromCubeRenderTarget(renderer, cubeRenderTarget); + + lightProbe.copy(probe); + + scene.add(new LightProbeHelper(lightProbe, 5)); + + render(); + }); + + // listener + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + renderer.setSize(window.innerWidth, window.innerHeight); + + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + render(); +} + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_lights_dynamic.ts b/examples-testing/examples/webgpu_lights_dynamic.ts new file mode 100644 index 000000000..b08c5902f --- /dev/null +++ b/examples-testing/examples/webgpu_lights_dynamic.ts @@ -0,0 +1,242 @@ +import * as THREE from 'three/webgpu'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { DynamicLighting } from 'three/addons/lighting/DynamicLighting.js'; +import { Inspector } from 'three/addons/inspector/Inspector.js'; +import Stats from 'three/addons/libs/stats.module.js'; + +let camera, scene, renderer, controls, timer, stats; + +const pointLights = []; +let autoAddInterval = null; + +const params = { + dynamic: true, + autoAdd: false, + lightCount: 2, + addLight() { + addLight(); + }, + removeLight() { + removeLight(); + }, + removeAll() { + removeAllLights(); + }, +}; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 200); + camera.position.set(0, 15, 30); + + scene = new THREE.Scene(); + + timer = new THREE.Timer(); + timer.connect(document); + + // Stats + + stats = new Stats(); + document.body.appendChild(stats.dom); + + // Floor + + const floorGeometry = new THREE.PlaneGeometry(120, 120); + const floorMaterial = new THREE.MeshStandardMaterial({ color: 0x444444, roughness: 0.8 }); + const floor = new THREE.Mesh(floorGeometry, floorMaterial); + floor.rotation.x = -Math.PI / 2; + scene.add(floor); + + // Shared geometries + + const sphereGeometry = new THREE.SphereGeometry(0.8, 128, 128); + const boxGeometry = new THREE.BoxGeometry(1.2, 1.2, 1.2, 64, 64, 64); + const torusGeometry = new THREE.TorusGeometry(0.6, 0.25, 128, 128); + const cylinderGeometry = new THREE.CylinderGeometry(0.5, 0.5, 1.4, 128, 64); + const coneGeometry = new THREE.ConeGeometry(0.6, 1.4, 128, 64); + + const geometries = [sphereGeometry, boxGeometry, torusGeometry, cylinderGeometry, coneGeometry]; + + // 100 meshes — first 50 with unique PBR materials, remaining 50 reuse them + + const uniqueMaterials = []; + + for (let i = 0; i < 50; i++) { + const material = new THREE.MeshStandardMaterial({ + color: new THREE.Color().setHSL(i / 50, 0.6 + Math.random() * 0.4, 0.35 + Math.random() * 0.3), + roughness: Math.random(), + metalness: Math.random(), + }); + material.name = 'Standard_' + i; + + uniqueMaterials.push(material); + } + + const meshesPerRing = 10; + + for (let i = 0; i < 100; i++) { + const material = i < 50 ? uniqueMaterials[i] : uniqueMaterials[i - 50]; + const geometry = geometries[i % geometries.length]; + const mesh = new THREE.Mesh(geometry, material); + + const ring = Math.floor(i / meshesPerRing); + const indexInRing = i % meshesPerRing; + const angle = (indexInRing / meshesPerRing) * Math.PI * 2 + ring * 0.3; + const radius = 6 + ring * 4; + + mesh.position.set(Math.cos(angle) * radius, 0.7 + Math.random() * 2, Math.sin(angle) * radius); + + mesh.rotation.set(Math.random() * Math.PI, Math.random() * Math.PI, 0); + + scene.add(mesh); + } + + // Center sphere + + const centerMaterial = new THREE.MeshStandardMaterial({ color: 0xffffff, roughness: 0.1, metalness: 0.9 }); + const centerSphere = new THREE.Mesh(new THREE.SphereGeometry(2, 128, 128), centerMaterial); + centerSphere.position.y = 2; + scene.add(centerSphere); + + // Ambient light + + scene.add(new THREE.AmbientLight(0x404040, 0.5)); + + // Start with a couple point lights + + addLight(); + addLight(); + + // Renderer + + createRenderer(); + + // Inspector GUI + + window.addEventListener('resize', onWindowResize); +} + +function createRenderer() { + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + if (params.dynamic) renderer.lighting = new DynamicLighting(); + + // Inspector GUI + + const gui = renderer.inspector.createParameters('Dynamic Lights'); + + gui.add(params, 'dynamic') + .name('dynamic mode') + .onChange(() => { + renderer.dispose(); + document.body.removeChild(renderer.domElement); + clearInterval(autoAddInterval); + autoAddInterval = null; + params.autoAdd = false; + createRenderer(); + }); + + gui.add(params, 'autoAdd') + .name('auto-add lights') + .onChange(value => { + if (value) { + autoAddInterval = setInterval(() => { + addLight(); + }, 500); + } else { + clearInterval(autoAddInterval); + autoAddInterval = null; + } + }); + + gui.add(params, 'addLight').name('add light'); + gui.add(params, 'removeLight').name('remove light'); + gui.add(params, 'removeAll').name('remove all lights'); + gui.add(params, 'lightCount').name('point lights').listen(); + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + controls.target.set(0, 2, 0); + controls.update(); +} + +function addLight() { + const color = new THREE.Color().setHSL(Math.random(), 0.8, 0.5); + const light = new THREE.PointLight(color, 1000); + + const angle = Math.random() * Math.PI * 2; + const radius = 5 + Math.random() * 20; + light.position.set(Math.cos(angle) * radius, 1 + Math.random() * 6, Math.sin(angle) * radius); + + light.userData.angle = angle; + light.userData.radius = radius; + light.userData.speed = 0.2 + Math.random() * 0.8; + light.userData.baseY = light.position.y; + + // Visual indicator + const sphere = new THREE.Mesh(new THREE.SphereGeometry(0.15, 8, 8), new THREE.MeshBasicMaterial({ color: color })); + light.add(sphere); + + scene.add(light); + pointLights.push(light); + + params.lightCount = pointLights.length; +} + +function removeLight() { + if (pointLights.length === 0) return; + + const light = pointLights.pop(); + scene.remove(light); + light.dispose(); + + params.lightCount = pointLights.length; +} + +function removeAllLights() { + while (pointLights.length > 0) { + const light = pointLights.pop(); + scene.remove(light); + light.dispose(); + } + + params.lightCount = 0; +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + const time = timer.getElapsed(); + + controls.update(); + + // Animate lights + + for (let i = 0; i < pointLights.length; i++) { + const light = pointLights[i]; + const d = light.userData; + const t = time * d.speed + d.angle; + + light.position.x = Math.cos(t) * d.radius; + light.position.z = Math.sin(t) * d.radius; + light.position.y = d.baseY + Math.sin(t * 2) * 0.5; + } + + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/webgpu_lights_ies_spotlight.ts b/examples-testing/examples/webgpu_lights_ies_spotlight.ts new file mode 100644 index 000000000..edfc51efa --- /dev/null +++ b/examples-testing/examples/webgpu_lights_ies_spotlight.ts @@ -0,0 +1,168 @@ +import * as THREE from 'three/webgpu'; + +import { OrbitControls } from './jsm/controls/OrbitControls.js'; + +import { IESLoader } from 'three/addons/loaders/IESLoader.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let renderer, scene, camera; +let lights; + +async function init() { + const iesLoader = new IESLoader().setPath('./ies/'); + //iesLoader.type = THREE.UnsignedByteType; // LDR + + const [iesTexture1, iesTexture2, iesTexture3, iesTexture4] = await Promise.all([ + iesLoader.loadAsync('007cfb11e343e2f42e3b476be4ab684e.ies'), + iesLoader.loadAsync('06b4cfdc8805709e767b5e2e904be8ad.ies'), + iesLoader.loadAsync('02a7562c650498ebb301153dbbf59207.ies'), + iesLoader.loadAsync('1a936937a49c63374e6d4fbed9252b29.ies'), + ]); + + // + + scene = new THREE.Scene(); + + // + + const spotLight = new THREE.IESSpotLight(0xff0000, 500); + spotLight.position.set(6.5, 3, 6.5); + spotLight.angle = Math.PI / 8; + spotLight.penumbra = 0.7; + spotLight.distance = 20; + spotLight.castShadow = true; + spotLight.iesMap = iesTexture1; + spotLight.userData.helper = new THREE.SpotLightHelper(spotLight); + scene.add(spotLight); + scene.add(spotLight.target); + scene.add(spotLight.userData.helper); + + // + + const spotLight2 = new THREE.IESSpotLight(0x00ff00, 500); + spotLight2.position.set(-6.5, 3, 6.5); + spotLight2.angle = Math.PI / 8; + spotLight2.penumbra = 0.7; + spotLight2.distance = 20; + spotLight2.castShadow = true; + spotLight2.iesMap = iesTexture2; + spotLight2.userData.helper = new THREE.SpotLightHelper(spotLight2); + scene.add(spotLight2); + scene.add(spotLight2.target); + scene.add(spotLight2.userData.helper); + + // + + const spotLight3 = new THREE.IESSpotLight(0x0000ff, 500); + spotLight3.position.set(-6.5, 3, -6.5); + spotLight3.angle = Math.PI / 8; + spotLight3.penumbra = 0.7; + spotLight3.distance = 20; + spotLight3.castShadow = true; + spotLight3.iesMap = iesTexture3; + spotLight3.userData.helper = new THREE.SpotLightHelper(spotLight3); + scene.add(spotLight3); + scene.add(spotLight3.target); + scene.add(spotLight3.userData.helper); + + // + + const spotLight4 = new THREE.IESSpotLight(0xffffff, 500); + spotLight4.position.set(6.5, 3, -6.5); + spotLight4.angle = Math.PI / 8; + spotLight4.penumbra = 0.7; + spotLight4.distance = 20; + spotLight4.castShadow = true; + spotLight4.iesMap = iesTexture4; + spotLight4.userData.helper = new THREE.SpotLightHelper(spotLight4); + scene.add(spotLight4); + scene.add(spotLight4.target); + scene.add(spotLight4.userData.helper); + + // + + lights = [spotLight, spotLight2, spotLight3, spotLight4]; + + // + + const material = new THREE.MeshPhongMaterial({ color: 0x999999 /*, dithering: true*/ }); + + const geometry = new THREE.PlaneGeometry(200, 200); + + const mesh = new THREE.Mesh(geometry, material); + mesh.rotation.x = -Math.PI * 0.5; + mesh.receiveShadow = true; + scene.add(mesh); + + const geometry2 = new THREE.BoxGeometry(2, 2, 2); + //const geometry2 = new THREE.IcosahedronGeometry( 1, 5 ); + + const mesh2 = new THREE.Mesh(geometry2, material); + mesh2.position.y = 1; + mesh2.castShadow = true; + scene.add(mesh2); + + // + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(render); + renderer.shadowMap.enabled = true; + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(16, 4, 1); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 2; + controls.maxDistance = 50; + controls.enablePan = false; + + // + + function setHelperVisible(value) { + for (let i = 0; i < lights.length; i++) { + lights[i].userData.helper.visible = value; + } + } + + setHelperVisible(false); + + // + + const gui = renderer.inspector.createParameters('Settings'); + gui.add({ helper: false }, 'helper').onChange(v => setHelperVisible(v)); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function render(time) { + time = time / 1000; + + for (let i = 0; i < lights.length; i++) { + const t = (Math.sin((time + i) * (Math.PI / 2)) + 1) / 2; + + const x = THREE.MathUtils.lerp(lights[i].position.x, 0, t); + const z = THREE.MathUtils.lerp(lights[i].position.z, 0, t); + + lights[i].target.position.x = x; + lights[i].target.position.z = z; + if (lights[i].userData.helper) lights[i].userData.helper.update(); + } + + renderer.render(scene, camera); +} + +init(); diff --git a/examples-testing/examples/webgpu_lights_phong.ts b/examples-testing/examples/webgpu_lights_phong.ts new file mode 100644 index 000000000..c2cdf4393 --- /dev/null +++ b/examples-testing/examples/webgpu_lights_phong.ts @@ -0,0 +1,136 @@ +import * as THREE from 'three/webgpu'; +import { color, fog, rangeFogFactor, checker, uv, mix, texture, lights, normalMap } from 'three/tsl'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { TeapotGeometry } from 'three/addons/geometries/TeapotGeometry.js'; + +let camera, scene, renderer, light1, light2, light3, light4, controls; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.01, 100); + camera.position.z = 7; + + scene = new THREE.Scene(); + scene.fogNode = fog(color(0xff00ff), rangeFogFactor(12, 30)); + + const sphereGeometry = new THREE.SphereGeometry(0.1, 16, 8); + + // textures + + const textureLoader = new THREE.TextureLoader(); + + const normalMapTexture = textureLoader.load('./textures/water/Water_1_M_Normal.jpg'); + normalMapTexture.wrapS = THREE.RepeatWrapping; + normalMapTexture.wrapT = THREE.RepeatWrapping; + + const alphaTexture = textureLoader.load('./textures/roughness_map.jpg'); + alphaTexture.wrapS = THREE.RepeatWrapping; + alphaTexture.wrapT = THREE.RepeatWrapping; + + // lights + + const addLight = (hexColor, power = 1700, distance = 100) => { + const material = new THREE.MeshPhongNodeMaterial(); + material.colorNode = color(hexColor); + material.lights = false; + + const mesh = new THREE.Mesh(sphereGeometry, material); + + const light = new THREE.PointLight(hexColor, 1, distance); + light.power = power; + light.add(mesh); + + scene.add(light); + + return light; + }; + + light1 = addLight(0x0040ff); + light2 = addLight(0xffffff); + light3 = addLight(0x80ff80); + light4 = addLight(0xffaa00); + + // light nodes ( selective lights ) + + const blueLightsNode = lights([light1]); + const whiteLightsNode = lights([light2]); + + // models + + const geometryTeapot = new TeapotGeometry(0.8, 18); + + const leftObject = new THREE.Mesh(geometryTeapot, new THREE.MeshPhongNodeMaterial({ color: 0x555555 })); + leftObject.material.lightsNode = blueLightsNode; + leftObject.material.specularNode = texture(alphaTexture); + leftObject.position.x = -3; + scene.add(leftObject); + + const centerObject = new THREE.Mesh(geometryTeapot, new THREE.MeshPhongNodeMaterial({ color: 0x555555 })); + centerObject.material.normalNode = normalMap(texture(normalMapTexture)); + centerObject.material.shininess = 80; + scene.add(centerObject); + + const rightObject = new THREE.Mesh(geometryTeapot, new THREE.MeshPhongNodeMaterial({ color: 0x555555 })); + rightObject.material.lightsNode = whiteLightsNode; + //rightObject.material.specular.setHex( 0xFF00FF ); + rightObject.material.specularNode = mix(color(0x0000ff), color(0xff0000), checker(uv().mul(5))); + rightObject.material.shininess = 90; + rightObject.position.x = 3; + scene.add(rightObject); + + leftObject.rotation.y = centerObject.rotation.y = rightObject.rotation.y = Math.PI * -0.5; + leftObject.position.y = centerObject.position.y = rightObject.position.y = -1; + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // controls + + controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 3; + controls.maxDistance = 25; + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + const time = performance.now() / 1000; + const lightTime = time * 0.5; + + light1.position.x = Math.sin(lightTime * 0.7) * 3; + light1.position.y = Math.cos(lightTime * 0.5) * 4; + light1.position.z = Math.cos(lightTime * 0.3) * 3; + + light2.position.x = Math.cos(lightTime * 0.3) * 3; + light2.position.y = Math.sin(lightTime * 0.5) * 4; + light2.position.z = Math.sin(lightTime * 0.7) * 3; + + light3.position.x = Math.sin(lightTime * 0.7) * 3; + light3.position.y = Math.cos(lightTime * 0.3) * 4; + light3.position.z = Math.sin(lightTime * 0.5) * 3; + + light4.position.x = Math.sin(lightTime * 0.3) * 3; + light4.position.y = Math.cos(lightTime * 0.7) * 4; + light4.position.z = Math.sin(lightTime * 0.5) * 3; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_lights_physical.ts b/examples-testing/examples/webgpu_lights_physical.ts new file mode 100644 index 000000000..43d7ce723 --- /dev/null +++ b/examples-testing/examples/webgpu_lights_physical.ts @@ -0,0 +1,236 @@ +import * as THREE from 'three/webgpu'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let camera, scene, renderer, bulbLight, bulbMat, hemiLight; +let ballMat, cubeMat, floorMat; + +let previousShadowMap = false; + +// ref for lumens: http://www.power-sure.com/lumens.htm +const bulbLuminousPowers = { + '110000 lm (1000W)': 110000, + '3500 lm (300W)': 3500, + '1700 lm (100W)': 1700, + '800 lm (60W)': 800, + '400 lm (40W)': 400, + '180 lm (25W)': 180, + '20 lm (4W)': 20, + Off: 0, +}; + +// ref for solar irradiances: https://en.wikipedia.org/wiki/Lux +const hemiLuminousIrradiances = { + '0.0001 lx (Moonless Night)': 0.0001, + '0.002 lx (Night Airglow)': 0.002, + '0.5 lx (Full Moon)': 0.5, + '3.4 lx (City Twilight)': 3.4, + '50 lx (Living Room)': 50, + '100 lx (Very Overcast)': 100, + '350 lx (Office Room)': 350, + '400 lx (Sunrise/Sunset)': 400, + '1000 lx (Overcast)': 1000, + '18000 lx (Daylight)': 18000, + '50000 lx (Direct Sun)': 50000, +}; + +const params = { + shadows: true, + exposure: 0.68, + bulbPower: Object.keys(bulbLuminousPowers)[4], + hemiIrradiance: Object.keys(hemiLuminousIrradiances)[0], +}; + +init(); + +function init() { + const container = document.getElementById('container'); + + // + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.x = -4; + camera.position.z = 4; + camera.position.y = 2; + + scene = new THREE.Scene(); + + const bulbGeometry = new THREE.SphereGeometry(0.02, 16, 8); + bulbLight = new THREE.PointLight(0xffee88, 1, 100, 2); + + bulbMat = new THREE.MeshStandardMaterial({ + emissive: 0xffffee, + emissiveIntensity: 1, + color: 0x000000, + }); + bulbLight.add(new THREE.Mesh(bulbGeometry, bulbMat)); + bulbLight.position.set(0, 2, 0); + bulbLight.castShadow = true; + scene.add(bulbLight); + + hemiLight = new THREE.HemisphereLight(0xddeeff, 0x0f0e0d, 0.02); + scene.add(hemiLight); + + floorMat = new THREE.MeshStandardMaterial({ + roughness: 0.8, + color: 0xffffff, + metalness: 0.2, + bumpScale: 1, + }); + const textureLoader = new THREE.TextureLoader(); + textureLoader.load('textures/hardwood2_diffuse.jpg', function (map) { + map.wrapS = THREE.RepeatWrapping; + map.wrapT = THREE.RepeatWrapping; + map.anisotropy = 4; + map.repeat.set(10, 24); + map.colorSpace = THREE.SRGBColorSpace; + floorMat.map = map; + floorMat.needsUpdate = true; + }); + textureLoader.load('textures/hardwood2_bump.jpg', function (map) { + map.wrapS = THREE.RepeatWrapping; + map.wrapT = THREE.RepeatWrapping; + map.anisotropy = 4; + map.repeat.set(10, 24); + floorMat.bumpMap = map; + floorMat.needsUpdate = true; + }); + textureLoader.load('textures/hardwood2_roughness.jpg', function (map) { + map.wrapS = THREE.RepeatWrapping; + map.wrapT = THREE.RepeatWrapping; + map.anisotropy = 4; + map.repeat.set(10, 24); + floorMat.roughnessMap = map; + floorMat.needsUpdate = true; + }); + + cubeMat = new THREE.MeshStandardMaterial({ + roughness: 0.7, + color: 0xffffff, + bumpScale: 1, + metalness: 0.2, + }); + textureLoader.load('textures/brick_diffuse.jpg', function (map) { + map.wrapS = THREE.RepeatWrapping; + map.wrapT = THREE.RepeatWrapping; + map.anisotropy = 4; + map.repeat.set(1, 1); + map.colorSpace = THREE.SRGBColorSpace; + cubeMat.map = map; + cubeMat.needsUpdate = true; + }); + textureLoader.load('textures/brick_bump.jpg', function (map) { + map.wrapS = THREE.RepeatWrapping; + map.wrapT = THREE.RepeatWrapping; + map.anisotropy = 4; + map.repeat.set(1, 1); + cubeMat.bumpMap = map; + cubeMat.needsUpdate = true; + }); + + ballMat = new THREE.MeshStandardMaterial({ + color: 0xffffff, + roughness: 0.5, + metalness: 1.0, + }); + textureLoader.load('textures/planets/earth_atmos_2048.jpg', function (map) { + map.anisotropy = 4; + map.colorSpace = THREE.SRGBColorSpace; + ballMat.map = map; + ballMat.needsUpdate = true; + }); + textureLoader.load('textures/planets/earth_specular_2048.jpg', function (map) { + map.anisotropy = 4; + map.colorSpace = THREE.SRGBColorSpace; + ballMat.metalnessMap = map; + ballMat.needsUpdate = true; + }); + + const floorGeometry = new THREE.PlaneGeometry(20, 20); + const floorMesh = new THREE.Mesh(floorGeometry, floorMat); + floorMesh.receiveShadow = true; + floorMesh.rotation.x = -Math.PI / 2.0; + scene.add(floorMesh); + + const ballGeometry = new THREE.SphereGeometry(0.25, 32, 32); + const ballMesh = new THREE.Mesh(ballGeometry, ballMat); + ballMesh.position.set(1, 0.25, 1); + ballMesh.rotation.y = Math.PI; + ballMesh.castShadow = true; + scene.add(ballMesh); + + const boxGeometry = new THREE.BoxGeometry(0.5, 0.5, 0.5); + const boxMesh = new THREE.Mesh(boxGeometry, cubeMat); + boxMesh.position.set(-0.5, 0.25, -1); + boxMesh.castShadow = true; + scene.add(boxMesh); + + const boxMesh2 = new THREE.Mesh(boxGeometry, cubeMat); + boxMesh2.position.set(0, 0.25, -5); + boxMesh2.castShadow = true; + scene.add(boxMesh2); + + const boxMesh3 = new THREE.Mesh(boxGeometry, cubeMat); + boxMesh3.position.set(7, 0.25, 0); + boxMesh3.castShadow = true; + scene.add(boxMesh3); + + // + + renderer = new THREE.WebGPURenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.shadowMap.enabled = true; + renderer.toneMapping = THREE.ReinhardToneMapping; + renderer.inspector = new Inspector(); + container.appendChild(renderer.domElement); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 1; + controls.maxDistance = 20; + + window.addEventListener('resize', onWindowResize); + + // + + const gui = renderer.inspector.createParameters('Settings'); + gui.add(params, 'hemiIrradiance', Object.keys(hemiLuminousIrradiances)); + gui.add(params, 'bulbPower', Object.keys(bulbLuminousPowers)); + gui.add(params, 'exposure', 0, 1); + gui.add(params, 'shadows'); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + renderer.toneMappingExposure = Math.pow(params.exposure, 5.0); // to allow for very bright scenes. + renderer.shadowMap.enabled = params.shadows; + bulbLight.castShadow = params.shadows; + + if (params.shadows !== previousShadowMap) { + ballMat.needsUpdate = true; + cubeMat.needsUpdate = true; + floorMat.needsUpdate = true; + previousShadowMap = params.shadows; + } + + bulbLight.power = bulbLuminousPowers[params.bulbPower]; + bulbMat.emissiveIntensity = bulbLight.intensity / Math.pow(0.02, 2.0); // convert from intensity to irradiance at bulb surface + + hemiLight.intensity = hemiLuminousIrradiances[params.hemiIrradiance]; + const time = Date.now() * 0.0005; + + bulbLight.position.y = Math.cos(time) * 0.75 + 1.25; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_lights_pointlights.ts b/examples-testing/examples/webgpu_lights_pointlights.ts new file mode 100644 index 000000000..e00d75b21 --- /dev/null +++ b/examples-testing/examples/webgpu_lights_pointlights.ts @@ -0,0 +1,189 @@ +import * as THREE from 'three/webgpu'; + +import { OBJLoader } from 'three/addons/loaders/OBJLoader.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { + abs, + attribute, + distance, + float, + max, + modelWorldMatrixInverse, + positionLocal, + sin, + time, + uniform, +} from 'three/tsl'; + +let camera, scene, timer, renderer, controls; + +let light1, light2; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.z = 100; + + scene = new THREE.Scene(); + + timer = new THREE.Timer(); + timer.connect(document); + + // model + + const loader = new OBJLoader(); + loader.load('models/obj/walt/WaltHead.obj', function (obj) { + const mesh = obj.children[0]; + mesh.geometry = createGeometry(mesh.geometry); + mesh.material = createMaterial(); + + mesh.scale.multiplyScalar(0.8); + mesh.position.y = -30; + scene.add(mesh); + }); + + const sphere = new THREE.SphereGeometry(0.5, 16, 8); + + // lights + + light1 = new THREE.PointLight(0xff0040, 2000); + light1.add(new THREE.Mesh(sphere, new THREE.MeshBasicMaterial({ color: 0xff0040 }))); + scene.add(light1); + + light2 = new THREE.PointLight(0x0040ff, 2000); + light2.add(new THREE.Mesh(sphere, new THREE.MeshBasicMaterial({ color: 0x0040ff }))); + scene.add(light2); + + scene.add(new THREE.AmbientLight(0xaaaaaa, 0.1)); + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + const time = timer.getElapsed() * 0.5; + + controls.update(); + + light1.position.x = Math.sin(time) * 20; + light1.position.y = Math.cos(time * 0.75) * -30; + light1.position.z = Math.cos(time * 0.5) * 20; + + light2.position.x = Math.cos(time * 0.5) * 20; + light2.position.y = Math.sin(time * 0.75) * -30; + light2.position.z = Math.sin(time) * 20; + + renderer.render(scene, camera); +} + +// helpers + +function createMaterial() { + const material = new THREE.MeshPhongNodeMaterial(); + + const seedAttribute = attribute('seed'); + const displaceNormalAttribute = attribute('displaceNormal'); + + const localTime = attribute('time').add(time); + + const effector1 = uniform(light1.position).toVar(); + const effector2 = uniform(light2.position).toVar(); + + const distance1 = distance(positionLocal, modelWorldMatrixInverse.mul(effector1)); + const distance2 = distance(positionLocal, modelWorldMatrixInverse.mul(effector2)); + + const invDistance1 = max(0.0, float(20.0).sub(distance1)).div(2.0); + const invDistance2 = max(0.0, float(20.0).sub(distance2)).div(2.0); + + const s = abs(sin(localTime.mul(2).add(seedAttribute)).mul(0.5)) + .add(invDistance1) + .add(invDistance2); + + material.positionNode = positionLocal.add(displaceNormalAttribute.mul(s)); + + return material; +} + +function createGeometry(geometry) { + const positionAttribute = geometry.getAttribute('position'); + + const v0 = new THREE.Vector3(); + const v1 = new THREE.Vector3(); + const v2 = new THREE.Vector3(); + const v3 = new THREE.Vector3(); + const n = new THREE.Vector3(); + + const plane = new THREE.Plane(); + + const vertices = []; + const times = []; + const seeds = []; + const displaceNormal = []; + + for (let i = 0; i < positionAttribute.count; i += 3) { + v0.fromBufferAttribute(positionAttribute, i); + v1.fromBufferAttribute(positionAttribute, i + 1); + v2.fromBufferAttribute(positionAttribute, i + 2); + + plane.setFromCoplanarPoints(v0, v1, v2); + + v3.copy(v0).add(v1).add(v2).divideScalar(3); // compute center + v3.add(n.copy(plane.normal).multiplyScalar(-1)); // displace center inwards + + // generate tetrahedron for each triangle + + vertices.push(v0.x, v0.y, v0.z, v1.x, v1.y, v1.z, v2.x, v2.y, v2.z); + vertices.push(v3.x, v3.y, v3.z, v1.x, v1.y, v1.z, v0.x, v0.y, v0.z); + vertices.push(v3.x, v3.y, v3.z, v2.x, v2.y, v2.z, v1.x, v1.y, v1.z); + vertices.push(v3.x, v3.y, v3.z, v0.x, v0.y, v0.z, v2.x, v2.y, v2.z); + + const t = Math.random(); + const s = Math.random(); + n.copy(plane.normal); + + times.push(t, t, t); + times.push(t, t, t); + times.push(t, t, t); + times.push(t, t, t); + seeds.push(s, s, s); + seeds.push(s, s, s); + seeds.push(s, s, s); + seeds.push(s, s, s); + + displaceNormal.push(n.x, n.y, n.z, n.x, n.y, n.z, n.x, n.y, n.z); + displaceNormal.push(n.x, n.y, n.z, n.x, n.y, n.z, n.x, n.y, n.z); + displaceNormal.push(n.x, n.y, n.z, n.x, n.y, n.z, n.x, n.y, n.z); + displaceNormal.push(n.x, n.y, n.z, n.x, n.y, n.z, n.x, n.y, n.z); + } + + const newGeometry = new THREE.BufferGeometry(); + newGeometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); + newGeometry.setAttribute('time', new THREE.Float32BufferAttribute(times, 1)); + newGeometry.setAttribute('seed', new THREE.Float32BufferAttribute(seeds, 1)); + newGeometry.setAttribute('displaceNormal', new THREE.Float32BufferAttribute(displaceNormal, 3)); + + newGeometry.computeVertexNormals(); + + return newGeometry; +} diff --git a/examples-testing/examples/webgpu_lights_rectarealight.ts b/examples-testing/examples/webgpu_lights_rectarealight.ts new file mode 100644 index 000000000..75e2cc442 --- /dev/null +++ b/examples-testing/examples/webgpu_lights_rectarealight.ts @@ -0,0 +1,87 @@ +import * as THREE from 'three/webgpu'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { RectAreaLightHelper } from 'three/addons/helpers/RectAreaLightHelper.js'; +import { RectAreaLightTexturesLib } from 'three/addons/lights/RectAreaLightTexturesLib.js'; + +import { checker, uv } from 'three/tsl'; + +let renderer, scene, camera; +let rectLight1, rectLight2, rectLight3; +let timer; + +init(); + +function init() { + THREE.RectAreaLightNode.setLTC(RectAreaLightTexturesLib.init()); + + timer = new THREE.Timer(); + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animation); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(0, 5, -15); + + scene = new THREE.Scene(); + + rectLight1 = new THREE.RectAreaLight(0xff0000, 5, 4, 10); + rectLight1.position.set(-5, 6, 5); + scene.add(rectLight1); + + rectLight2 = new THREE.RectAreaLight(0x00ff00, 5, 4, 10); + rectLight2.position.set(0, 6, 5); + scene.add(rectLight2); + + rectLight3 = new THREE.RectAreaLight(0x0000ff, 5, 4, 10); + rectLight3.position.set(5, 6, 5); + scene.add(rectLight3); + + scene.add(new RectAreaLightHelper(rectLight1)); + scene.add(new RectAreaLightHelper(rectLight2)); + scene.add(new RectAreaLightHelper(rectLight3)); + + const geoFloor = new THREE.BoxGeometry(2000, 0.1, 2000); + const matStdFloor = new THREE.MeshStandardMaterial({ color: 0x444444 }); + matStdFloor.roughnessNode = checker(uv().mul(400)); + const mshStdFloor = new THREE.Mesh(geoFloor, matStdFloor); + scene.add(mshStdFloor); + + const geoKnot = new THREE.TorusKnotGeometry(1.5, 0.5, 200, 16); + const matKnot = new THREE.MeshStandardMaterial({ color: 0xffffff, roughness: 0, metalness: 0 }); + const meshKnot = new THREE.Mesh(geoKnot, matKnot); + meshKnot.position.set(0, 5.5, 0); + scene.add(meshKnot); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.target.copy(meshKnot.position); + controls.update(); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + renderer.setSize(window.innerWidth, window.innerHeight); + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); +} + +function animation() { + timer.update(); + + const delta = timer.getDelta(); + + rectLight1.rotation.y += -delta; + rectLight2.rotation.y += delta * 0.5; + rectLight3.rotation.y += delta; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_lights_selective.ts b/examples-testing/examples/webgpu_lights_selective.ts new file mode 100644 index 000000000..d00048347 --- /dev/null +++ b/examples-testing/examples/webgpu_lights_selective.ts @@ -0,0 +1,150 @@ +import * as THREE from 'three/webgpu'; +import { fog, rangeFogFactor, color, lights, texture, normalMap } from 'three/tsl'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { TeapotGeometry } from 'three/addons/geometries/TeapotGeometry.js'; + +let camera, scene, renderer, light1, light2, light3, light4, controls; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.01, 100); + camera.position.z = 7; + + scene = new THREE.Scene(); + scene.fogNode = fog(color(0xff00ff), rangeFogFactor(12, 30)); + + const sphereGeometry = new THREE.SphereGeometry(0.1, 16, 8); + + // textures + + const textureLoader = new THREE.TextureLoader(); + + const normalMapTexture = textureLoader.load('./textures/water/Water_1_M_Normal.jpg'); + normalMapTexture.wrapS = THREE.RepeatWrapping; + normalMapTexture.wrapT = THREE.RepeatWrapping; + + const alphaTexture = textureLoader.load('./textures/roughness_map.jpg'); + alphaTexture.wrapS = THREE.RepeatWrapping; + alphaTexture.wrapT = THREE.RepeatWrapping; + + // lights + + const addLight = (hexColor, power = 1700, distance = 100) => { + const material = new THREE.MeshStandardNodeMaterial(); + material.colorNode = color(hexColor); + material.lights = false; + + const mesh = new THREE.Mesh(sphereGeometry, material); + + const light = new THREE.PointLight(hexColor, 1, distance); + light.power = power; + light.add(mesh); + + scene.add(light); + + return light; + }; + + light1 = addLight(0xff0040); + light2 = addLight(0x0040ff); + light3 = addLight(0x80ff80); + light4 = addLight(0xffaa00); + + // light nodes ( selective lights ) + + const redLightsNode = lights([light1]); + const blueLightsNode = lights([light2]); + + // models + + const geometryTeapot = new TeapotGeometry(0.8, 18); + + const leftObject = new THREE.Mesh(geometryTeapot, new THREE.MeshStandardNodeMaterial({ color: 0x555555 })); + leftObject.material.lightsNode = redLightsNode; + leftObject.material.roughnessNode = texture(alphaTexture); + leftObject.material.metalness = 0; + leftObject.position.x = -3; + scene.add(leftObject); + + const centerObject = new THREE.Mesh(geometryTeapot, new THREE.MeshStandardNodeMaterial({ color: 0x555555 })); + centerObject.material.normalNode = normalMap(texture(normalMapTexture)); + centerObject.material.metalness = 0.5; + centerObject.material.roughness = 0.5; + scene.add(centerObject); + + const rightObject = new THREE.Mesh(geometryTeapot, new THREE.MeshStandardNodeMaterial({ color: 0x555555 })); + rightObject.material.lightsNode = blueLightsNode; + rightObject.material.metalnessNode = texture(alphaTexture); + rightObject.position.x = 3; + scene.add(rightObject); + + leftObject.rotation.y = centerObject.rotation.y = rightObject.rotation.y = Math.PI * -0.5; + leftObject.position.y = centerObject.position.y = rightObject.position.y = -1; + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // controls + + controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 3; + controls.maxDistance = 25; + + // events + + window.addEventListener('resize', onWindowResize); + + // gui + + const gui = renderer.inspector.createParameters('Material'); + + gui.add(centerObject.material, 'roughness', 0, 1, 0.01); + gui.add(centerObject.material, 'metalness', 0, 1, 0.01); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + const time = performance.now() / 1000; + const lightTime = time * 0.5; + + light1.position.x = Math.sin(lightTime * 0.7) * 3; + light1.position.y = Math.cos(lightTime * 0.5) * 4; + light1.position.z = Math.cos(lightTime * 0.3) * 3; + + light2.position.x = Math.cos(lightTime * 0.3) * 3; + light2.position.y = Math.sin(lightTime * 0.5) * 4; + light2.position.z = Math.sin(lightTime * 0.7) * 3; + + light3.position.x = Math.sin(lightTime * 0.7) * 3; + light3.position.y = Math.cos(lightTime * 0.3) * 4; + light3.position.z = Math.sin(lightTime * 0.5) * 3; + + light4.position.x = Math.sin(lightTime * 0.3) * 3; + light4.position.y = Math.cos(lightTime * 0.7) * 4; + light4.position.z = Math.sin(lightTime * 0.5) * 3; + /* + @TODO: Used to test scene light change ( currently unavailable ) + + if ( time > 2.0 && light1.parent === null ) scene.add( light1 ); + if ( time > 2.5 && light2.parent === null ) scene.add( light2 ); + if ( time > 3.0 && light3.parent === null ) scene.add( light3 ); + if ( time > 3.5 && light4.parent === null ) scene.add( light4 ); + */ + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_lights_spotlight.ts b/examples-testing/examples/webgpu_lights_spotlight.ts new file mode 100644 index 000000000..85e320800 --- /dev/null +++ b/examples-testing/examples/webgpu_lights_spotlight.ts @@ -0,0 +1,201 @@ +import * as THREE from 'three/webgpu'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { PLYLoader } from 'three/addons/loaders/PLYLoader.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let renderer, scene, camera; + +let spotLight; + +init(); + +function init() { + // Renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + renderer.inspector = new Inspector(); + + renderer.toneMapping = THREE.NeutralToneMapping; + renderer.toneMappingExposure = 1; + + renderer.shadowMap.enabled = true; + renderer.shadowMap.type = THREE.PCFSoftShadowMap; + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(7, 4, 1); + + // Controls + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 2; + controls.maxDistance = 10; + controls.maxPolarAngle = Math.PI / 2; + controls.target.set(0, 1, 0); + controls.update(); + + // Textures + + const loader = new THREE.TextureLoader().setPath('textures/'); + const filenames = ['disturb.jpg', 'colors.png', 'uv_grid_opengl.jpg']; + + const textures = { none: null }; + + for (let i = 0; i < filenames.length; i++) { + const filename = filenames[i]; + + const texture = loader.load(filename); + texture.minFilter = THREE.LinearFilter; + texture.magFilter = THREE.LinearFilter; + texture.generateMipmaps = false; + texture.colorSpace = THREE.SRGBColorSpace; + + textures[filename] = texture; + } + + // Lights + + const ambient = new THREE.HemisphereLight(0xffffff, 0x8d8d8d, 0.25); + scene.add(ambient); + + spotLight = new THREE.SpotLight(0xffffff, 100); + spotLight.name = 'spotLight'; + spotLight.map = textures['disturb.jpg']; + spotLight.position.set(2.5, 5, 2.5); + spotLight.angle = Math.PI / 6; + spotLight.penumbra = 1; + spotLight.decay = 2; + spotLight.distance = 0; + + spotLight.castShadow = true; + spotLight.shadow.mapSize.width = 1024; + spotLight.shadow.mapSize.height = 1024; + spotLight.shadow.camera.near = 2; + spotLight.shadow.camera.far = 10; + spotLight.shadow.focus = 1; + spotLight.shadow.intensity = 1; + scene.add(spotLight); + + spotLight.lightHelper = new THREE.SpotLightHelper(spotLight); + spotLight.lightHelper.visible = false; + scene.add(spotLight.lightHelper); + + spotLight.shadowCameraHelper = new THREE.CameraHelper(spotLight.shadow.camera); // colored lines + spotLight.shadowCameraHelper.visible = false; + scene.add(spotLight.shadowCameraHelper); + + // + + const geometry = new THREE.PlaneGeometry(10, 10); + const material = new THREE.MeshLambertMaterial({ color: 0xbcbcbc }); + + const mesh = new THREE.Mesh(geometry, material); + mesh.position.set(0, -1, 0); + mesh.rotation.x = -Math.PI / 2; + mesh.receiveShadow = true; + scene.add(mesh); + + // Model + + new PLYLoader().load('models/ply/binary/Lucy100k.ply', function (geometry) { + geometry.scale(0.0024, 0.0024, 0.0024); + geometry.computeVertexNormals(); + + const material = new THREE.MeshLambertMaterial(); + + const mesh = new THREE.Mesh(geometry, material); + mesh.rotation.y = -Math.PI / 2; + mesh.position.y = 0.8; + mesh.castShadow = true; + mesh.receiveShadow = true; + scene.add(mesh); + }); + + // + + window.addEventListener('resize', onWindowResize); + + // GUI + + const gui = renderer.inspector.createParameters('Settings'); + + const params = { + map: textures['disturb.jpg'], + color: spotLight.color.getHex(), + intensity: spotLight.intensity, + distance: spotLight.distance, + angle: spotLight.angle, + penumbra: spotLight.penumbra, + decay: spotLight.decay, + focus: spotLight.shadow.focus, + shadowIntensity: spotLight.shadow.intensity, + helpers: false, + }; + + gui.add(params, 'map', textures).onChange(function (val) { + spotLight.map = val; + }); + + gui.addColor(params, 'color').onChange(function (val) { + spotLight.color.setHex(val); + }); + + gui.add(params, 'intensity', 0, 500).onChange(function (val) { + spotLight.intensity = val; + }); + + gui.add(params, 'distance', 0, 20).onChange(function (val) { + spotLight.distance = val; + }); + + gui.add(params, 'angle', 0, Math.PI / 3).onChange(function (val) { + spotLight.angle = val; + }); + + gui.add(params, 'penumbra', 0, 1).onChange(function (val) { + spotLight.penumbra = val; + }); + + gui.add(params, 'decay', 1, 2).onChange(function (val) { + spotLight.decay = val; + }); + + gui.add(params, 'focus', 0, 1).onChange(function (val) { + spotLight.shadow.focus = val; + }); + + gui.add(params, 'shadowIntensity', 0, 1).onChange(function (val) { + spotLight.shadow.intensity = val; + }); + + gui.add(params, 'helpers').onChange(function (val) { + spotLight.lightHelper.visible = val; + spotLight.shadowCameraHelper.visible = val; + }); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + const time = performance.now() / 3000; + + spotLight.position.x = Math.cos(time) * 2.5; + spotLight.position.z = Math.sin(time) * 2.5; + + spotLight.lightHelper.update(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_lights_tiled.ts b/examples-testing/examples/webgpu_lights_tiled.ts new file mode 100644 index 000000000..e575ee344 --- /dev/null +++ b/examples-testing/examples/webgpu_lights_tiled.ts @@ -0,0 +1,180 @@ +import * as THREE from 'three/webgpu'; +import { texture, uv, pass, normalMap, uniform } from 'three/tsl'; +import { bloom } from 'three/addons/tsl/display/BloomNode.js'; + +import { TiledLighting } from 'three/addons/lighting/TiledLighting.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import WebGPU from 'three/addons/capabilities/WebGPU.js'; + +let camera, scene, renderer, lights, lightDummy, controls, compose, tileInfluence, lighting, count, renderPipeline; + +init(); + +function init() { + if (WebGPU.isAvailable() === false) { + document.body.appendChild(WebGPU.getErrorMessage()); + + throw new Error('No WebGPU support'); + } + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 600); + camera.position.z = 200; + camera.position.y = 30; + + scene = new THREE.Scene(); + scene.fog = new THREE.Fog(0x111111, 300, 500); + scene.background = new THREE.Color(0x111111); + + count = 1000; + + const material = new THREE.MeshBasicMaterial(); + + lightDummy = new THREE.InstancedMesh(new THREE.SphereGeometry(0.1, 16, 8), material, count); + lightDummy.instanceMatrix.setUsage(THREE.DynamicDrawUsage); + scene.add(lightDummy); + + // lights + + lights = new THREE.Group(); + scene.add(lights); + + const addLight = (hexColor, power = 10, distance = 3) => { + const light = new THREE.PointLight(hexColor, 1, distance); + light.position.set(Math.random() * 300 - 150, 1, Math.random() * 300 - 150); + light.power = power; + light.userData.fixedPosition = light.position.clone(); + lights.add(light); + + return light; + }; + + const color = new THREE.Color(); + + for (let i = 0; i < count; i++) { + const hex = Math.random() * 0xffffff + 0x666666; + + lightDummy.setColorAt(i, color.setHex(hex)); + + addLight(hex); + } + + // + + const lightAmbient = new THREE.AmbientLight(0xffffff, 0.1); + scene.add(lightAmbient); + + // textures + + const textureLoader = new THREE.TextureLoader(); + + const floorColor = textureLoader.load('textures/floors/FloorsCheckerboard_S_Diffuse.jpg'); + floorColor.wrapS = THREE.RepeatWrapping; + floorColor.wrapT = THREE.RepeatWrapping; + floorColor.colorSpace = THREE.SRGBColorSpace; + + const floorNormal = textureLoader.load('textures/floors/FloorsCheckerboard_S_Normal.jpg'); + floorNormal.wrapS = THREE.RepeatWrapping; + floorNormal.wrapT = THREE.RepeatWrapping; + + const uvTile = uv().mul(50); + + const planeGeometry = new THREE.PlaneGeometry(1000, 1000); + const planeMaterial = new THREE.MeshPhongNodeMaterial({ + colorNode: texture(floorColor, uvTile), + normalNode: normalMap(texture(floorNormal, uvTile)), + }); + + const ground = new THREE.Mesh(planeGeometry, planeMaterial); + ground.rotation.x = -Math.PI / 2; + ground.position.y = 0; + ground.castShadow = true; + ground.receiveShadow = true; + scene.add(ground); + + // renderer + + lighting = new TiledLighting(); // ( maxLights = 1024, tileSize = 32 ) + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.lighting = lighting; // set lighting system + renderer.toneMapping = THREE.NeutralToneMapping; + renderer.toneMappingExposure = 5; + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // controls + + controls = new OrbitControls(camera, renderer.domElement); + controls.maxDistance = 400; + + // events + + window.addEventListener('resize', onWindowResize); + + // post processing + + const scenePass = pass(scene, camera); + const bloomPass = bloom(scenePass, 3, 0.9, 0.2); + + // compose + + compose = scenePass.add(bloomPass); + tileInfluence = uniform(0); + + renderPipeline = new THREE.RenderPipeline(renderer); + + updatePostProcessing(); + + // gui + + const gui = renderer.inspector.createParameters('Settings'); + gui.add(tileInfluence, 'value', 0, 1).name('tile indexes debug'); +} + +function updatePostProcessing() { + // tile indexes debug, needs to be updated every time the renderer size changes + + const debugBlockIndexes = lighting + .getNode(scene) + .setSize(window.innerWidth * window.devicePixelRatio, window.innerHeight * window.devicePixelRatio) + .getBlock() + .toColor() + .div(count * 2); + + renderPipeline.outputNode = compose.add(debugBlockIndexes.mul(tileInfluence)); + renderPipeline.needsUpdate = true; +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + updatePostProcessing(); +} + +function animate() { + const time = performance.now() / 1000; + + for (let i = 0; i < lights.children.length; i++) { + const light = lights.children[i]; + const lightTime = time * 0.5 + light.id; + + light.position.copy(light.userData.fixedPosition); + light.position.x += Math.sin(lightTime * 0.7) * 3; + light.position.y += Math.cos(lightTime * 0.5) * 0.5; + light.position.z += Math.cos(lightTime * 0.3) * 3; + + lightDummy.setMatrixAt(i, light.matrixWorld); + } + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_lines_fat.ts b/examples-testing/examples/webgpu_lines_fat.ts new file mode 100644 index 000000000..96417888c --- /dev/null +++ b/examples-testing/examples/webgpu_lines_fat.ts @@ -0,0 +1,255 @@ +import * as THREE from 'three/webgpu'; +import { color } from 'three/tsl'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { Line2 } from 'three/addons/lines/webgpu/Line2.js'; +import { LineGeometry } from 'three/addons/lines/LineGeometry.js'; +import * as GeometryUtils from 'three/addons/utils/GeometryUtils.js'; + +let line, renderer, scene, camera, camera2, controls, backgroundNode; +let line1; +let matLine, matLineBasic, matLineDashed; +let stats; +let gui; + +// viewport +let insetWidth; +let insetHeight; + +init(); + +function init() { + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setClearColor(0x000000, 0.0); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(-40, 0, 60); + + camera2 = new THREE.PerspectiveCamera(40, 1, 1, 1000); + camera2.position.copy(camera.position); + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + controls.minDistance = 10; + controls.maxDistance = 500; + + backgroundNode = color(0x222222); + + // Position and THREE.Color Data + + const positions = []; + const colors = []; + + const points = GeometryUtils.hilbert3D(new THREE.Vector3(0, 0, 0), 20.0, 1, 0, 1, 2, 3, 4, 5, 6, 7); + + const spline = new THREE.CatmullRomCurve3(points); + const divisions = Math.round(12 * points.length); + const point = new THREE.Vector3(); + const lineColor = new THREE.Color(); + + for (let i = 0, l = divisions; i < l; i++) { + const t = i / l; + + spline.getPoint(t, point); + positions.push(point.x, point.y, point.z); + + lineColor.setHSL(t, 1.0, 0.5, THREE.SRGBColorSpace); + colors.push(lineColor.r, lineColor.g, lineColor.b); + } + + // Line2 ( LineGeometry, LineMaterial ) + + const geometry = new LineGeometry(); + geometry.setPositions(positions); + geometry.setColors(colors); + + matLine = new THREE.Line2NodeMaterial({ + color: 0xffffff, + linewidth: 5, // in world units with size attenuation, pixels otherwise + vertexColors: true, + dashed: false, + alphaToCoverage: true, + }); + + line = new Line2(geometry, matLine); + line.computeLineDistances(); + line.scale.set(1, 1, 1); + scene.add(line); + + const geo = new THREE.BufferGeometry(); + geo.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)); + geo.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); + + matLineBasic = new THREE.LineBasicNodeMaterial({ vertexColors: true }); + matLineDashed = new THREE.LineDashedNodeMaterial({ vertexColors: true, scale: 2, dashSize: 1, gapSize: 1 }); + + line1 = new THREE.Line(geo, matLineBasic); + line1.computeLineDistances(); + line1.visible = false; + scene.add(line1); + + // + + window.addEventListener('resize', onWindowResize); + onWindowResize(); + + stats = new Stats(); + document.body.appendChild(stats.dom); + + initGui(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + insetWidth = window.innerHeight / 4; // square + insetHeight = window.innerHeight / 4; + + camera2.aspect = insetWidth / insetHeight; + camera2.updateProjectionMatrix(); +} + +function animate() { + stats.update(); + + // main scene + + renderer.setClearColor(0x000000, 0); + + renderer.setViewport(0, 0, window.innerWidth, window.innerHeight); + + controls.update(); + + renderer.autoClear = true; + + scene.backgroundNode = null; + renderer.render(scene, camera); + + // inset scene + + const posY = window.innerHeight - insetHeight - 20; + + renderer.clearDepth(); // important! + + renderer.setScissorTest(true); + + renderer.setScissor(20, posY, insetWidth, insetHeight); + + renderer.setViewport(20, posY, insetWidth, insetHeight); + + camera2.position.copy(camera.position); + camera2.quaternion.copy(camera.quaternion); + + renderer.autoClear = false; + + scene.backgroundNode = backgroundNode; + renderer.render(scene, camera2); + + renderer.setScissorTest(false); +} + +// + +function initGui() { + gui = new GUI(); + + const param = { + 'line type': 0, + 'world units': false, + width: 5, + alphaToCoverage: true, + dashed: false, + 'dash offset': 0, + 'dash scale': 1, + 'dash / gap': 1, + }; + + gui.add(param, 'line type', { LineGeometry: 0, '"line-strip"': 1 }).onChange(function (val) { + switch (val) { + case 0: + line.visible = true; + + line1.visible = false; + + break; + + case 1: + line.visible = false; + + line1.visible = true; + + break; + } + }); + + gui.add(param, 'world units').onChange(function (val) { + matLine.worldUnits = val; + matLine.needsUpdate = true; + }); + + gui.add(param, 'width', 1, 10).onChange(function (val) { + matLine.linewidth = val; + }); + + gui.add(param, 'alphaToCoverage').onChange(function (val) { + matLine.alphaToCoverage = val; + }); + + gui.add(param, 'dashed').onChange(function (val) { + matLine.dashed = val; + line1.material = val ? matLineDashed : matLineBasic; + }); + + gui.add(param, 'dash scale', 0.5, 2, 0.1).onChange(function (val) { + matLine.scale = val; + matLineDashed.scale = val; + }); + + gui.add(param, 'dash offset', 0, 5, 0.1).onChange(function (val) { + matLine.dashOffset = val; + matLineDashed.dashOffset = val; + }); + + gui.add(param, 'dash / gap', { '2 : 1': 0, '1 : 1': 1, '1 : 2': 2 }).onChange(function (val) { + switch (val) { + case 0: + matLine.dashSize = 2; + matLine.gapSize = 1; + + matLineDashed.dashSize = 2; + matLineDashed.gapSize = 1; + + break; + + case 1: + matLine.dashSize = 1; + matLine.gapSize = 1; + + matLineDashed.dashSize = 1; + matLineDashed.gapSize = 1; + + break; + + case 2: + matLine.dashSize = 1; + matLine.gapSize = 2; + + matLineDashed.dashSize = 1; + matLineDashed.gapSize = 2; + + break; + } + }); +} diff --git a/examples-testing/examples/webgpu_lines_fat_raycasting.ts b/examples-testing/examples/webgpu_lines_fat_raycasting.ts new file mode 100644 index 000000000..4280b580c --- /dev/null +++ b/examples-testing/examples/webgpu_lines_fat_raycasting.ts @@ -0,0 +1,286 @@ +import * as THREE from 'three/webgpu'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { LineSegments2 } from 'three/addons/lines/webgpu/LineSegments2.js'; +import { LineSegmentsGeometry } from 'three/addons/lines/LineSegmentsGeometry.js'; +import { Line2 } from 'three/addons/lines/webgpu/Line2.js'; +import { LineGeometry } from 'three/addons/lines/LineGeometry.js'; + +// + +let line, thresholdLine, segments, thresholdSegments; +let renderer, scene, camera, controls; +let sphereInter, sphereOnLine; +let gui; +let timer; + +const color = new THREE.Color(); + +const pointer = new THREE.Vector2(Infinity, Infinity); + +const raycaster = new THREE.Raycaster(); + +raycaster.params.Line2 = {}; +raycaster.params.Line2.threshold = 0; + +const matLine = new THREE.Line2NodeMaterial({ + color: 0xffffff, + linewidth: 1, // in world units with size attenuation, pixels otherwise + worldUnits: true, + vertexColors: true, + + alphaToCoverage: true, +}); + +const matThresholdLine = new THREE.Line2NodeMaterial({ + color: 0xffffff, + linewidth: matLine.linewidth, // in world units with size attenuation, pixels otherwise + worldUnits: true, + // vertexColors: true, + transparent: true, + opacity: 0.2, + depthTest: false, + visible: false, +}); + +const params = { + 'line type': 1, + 'world units': matLine.worldUnits, + 'visualize threshold': matThresholdLine.visible, + width: matLine.linewidth, + alphaToCoverage: matLine.alphaToCoverage, + threshold: raycaster.params.Line2.threshold, + translation: 0, + animate: true, +}; + +init(); + +function init() { + timer = new THREE.Timer(); + timer.connect(document); + + renderer = new THREE.WebGPURenderer({ antialias: true, alpha: true, trackTimestamp: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setClearColor(0x000000, 0.0); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(-40, 0, 60); + + controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 10; + controls.maxDistance = 500; + + const sphereGeometry = new THREE.SphereGeometry(0.25, 8, 4); + const sphereInterMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000, depthTest: false }); + const sphereOnLineMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00, depthTest: false }); + + sphereInter = new THREE.Mesh(sphereGeometry, sphereInterMaterial); + sphereOnLine = new THREE.Mesh(sphereGeometry, sphereOnLineMaterial); + sphereInter.visible = false; + sphereOnLine.visible = false; + sphereInter.renderOrder = 10; + sphereOnLine.renderOrder = 10; + scene.add(sphereInter); + scene.add(sphereOnLine); + + // Position and THREE.Color Data + + const positions = []; + const colors = []; + const points = []; + for (let i = -50; i < 50; i++) { + const t = i / 3; + points.push(new THREE.Vector3(t * Math.sin(2 * t), t, t * Math.cos(2 * t))); + } + + const spline = new THREE.CatmullRomCurve3(points); + const divisions = Math.round(3 * points.length); + const point = new THREE.Vector3(); + const color = new THREE.Color(); + + for (let i = 0, l = divisions; i < l; i++) { + const t = i / l; + + spline.getPoint(t, point); + positions.push(point.x, point.y, point.z); + + color.setHSL(t, 1.0, 0.5, THREE.SRGBColorSpace); + colors.push(color.r, color.g, color.b); + } + + const lineGeometry = new LineGeometry(); + lineGeometry.setPositions(positions); + lineGeometry.setColors(colors); + + const segmentsGeometry = new LineSegmentsGeometry(); + segmentsGeometry.setPositions(positions); + segmentsGeometry.setColors(colors); + + segments = new LineSegments2(segmentsGeometry, matLine); + segments.computeLineDistances(); + segments.scale.set(1, 1, 1); + scene.add(segments); + + thresholdSegments = new LineSegments2(segmentsGeometry, matThresholdLine); + thresholdSegments.computeLineDistances(); + thresholdSegments.scale.set(1, 1, 1); + scene.add(thresholdSegments); + + line = new Line2(lineGeometry, matLine); + line.computeLineDistances(); + line.scale.set(1, 1, 1); + scene.add(line); + + thresholdLine = new Line2(lineGeometry, matThresholdLine); + thresholdLine.computeLineDistances(); + thresholdLine.scale.set(1, 1, 1); + scene.add(thresholdLine); + + const geo = new THREE.BufferGeometry(); + geo.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)); + geo.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); + + // + + switchLine(params['line type']); + + // + + document.addEventListener('pointermove', onPointerMove); + window.addEventListener('resize', onWindowResize); + onWindowResize(); + + initGui(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function onPointerMove(event) { + pointer.x = (event.clientX / window.innerWidth) * 2 - 1; + pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; +} + +async function animate() { + timer.update(); + + const delta = timer.getDelta(); + + const obj = line.visible ? line : segments; + thresholdLine.position.copy(line.position); + thresholdLine.quaternion.copy(line.quaternion); + thresholdSegments.position.copy(segments.position); + thresholdSegments.quaternion.copy(segments.quaternion); + + if (params.animate) { + line.rotation.y += delta * 0.1; + + segments.rotation.y = line.rotation.y; + } + + raycaster.setFromCamera(pointer, camera); + + const intersects = raycaster.intersectObject(obj); + + if (intersects.length > 0) { + sphereInter.visible = true; + sphereOnLine.visible = true; + + sphereInter.position.copy(intersects[0].point); + sphereOnLine.position.copy(intersects[0].pointOnLine); + + const index = intersects[0].faceIndex; + const colors = obj.geometry.getAttribute('instanceColorStart'); + + color.fromBufferAttribute(colors, index); + + sphereInter.material.color.copy(color).offsetHSL(0.3, 0, 0); + sphereOnLine.material.color.copy(color).offsetHSL(0.7, 0, 0); + + renderer.domElement.style.cursor = 'crosshair'; + } else { + sphereInter.visible = false; + sphereOnLine.visible = false; + renderer.domElement.style.cursor = ''; + } + + renderer.render(scene, camera); +} + +// + +function switchLine(val) { + switch (val) { + case 0: + line.visible = true; + thresholdLine.visible = true; + + segments.visible = false; + thresholdSegments.visible = false; + + break; + + case 1: + line.visible = false; + thresholdLine.visible = false; + + segments.visible = true; + thresholdSegments.visible = true; + + break; + } +} + +function initGui() { + gui = renderer.inspector.createParameters('Settings'); + + gui.add(params, 'line type', { LineGeometry: 0, LineSegmentsGeometry: 1 }).onChange(function (val) { + switchLine(val); + }); + + gui.add(params, 'world units').onChange(function (val) { + matLine.worldUnits = val; + matLine.needsUpdate = true; + + matThresholdLine.worldUnits = val; + matThresholdLine.needsUpdate = true; + }); + + gui.add(params, 'visualize threshold').onChange(function (val) { + matThresholdLine.visible = val; + }); + + gui.add(params, 'width', 1, 10).onChange(function (val) { + matLine.linewidth = val; + matThresholdLine.linewidth = matLine.linewidth + raycaster.params.Line2.threshold; + }); + + gui.add(params, 'alphaToCoverage').onChange(function (val) { + matLine.alphaToCoverage = val; + }); + + gui.add(params, 'threshold', 0, 10).onChange(function (val) { + raycaster.params.Line2.threshold = val; + matThresholdLine.linewidth = matLine.linewidth + raycaster.params.Line2.threshold; + }); + + gui.add(params, 'translation', 0, 10).onChange(function (val) { + line.position.x = val; + segments.position.x = val; + }); + + gui.add(params, 'animate'); +} diff --git a/examples-testing/examples/webgpu_lines_fat_wireframe.ts b/examples-testing/examples/webgpu_lines_fat_wireframe.ts new file mode 100644 index 000000000..9ab5c44c0 --- /dev/null +++ b/examples-testing/examples/webgpu_lines_fat_wireframe.ts @@ -0,0 +1,210 @@ +import * as THREE from 'three/webgpu'; +import { color } from 'three/tsl'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { Wireframe } from 'three/addons/lines/webgpu/Wireframe.js'; +import { WireframeGeometry2 } from 'three/addons/lines/WireframeGeometry2.js'; + +let wireframe, renderer, scene, camera, camera2, controls, backgroundNode; +let wireframe1; +let matLine, matLineBasic, matLineDashed; +let gui; + +// viewport +let insetWidth; +let insetHeight; + +init(); + +function init() { + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setClearColor(0x000000, 0.0); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(-50, 0, 50); + + camera2 = new THREE.PerspectiveCamera(40, 1, 1, 1000); + camera2.position.copy(camera.position); + + controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 10; + controls.maxDistance = 500; + + backgroundNode = color(0x222222); + + // Wireframe ( WireframeGeometry2, Line2NodeMaterial ) + + let geo = new THREE.IcosahedronGeometry(20, 1); + + const geometry = new WireframeGeometry2(geo); + + matLine = new THREE.Line2NodeMaterial({ + color: 0x4080ff, + linewidth: 5, // in world units with size attenuation, pixels otherwise + dashed: false, + }); + + wireframe = new Wireframe(geometry, matLine); + wireframe.computeLineDistances(); + wireframe.scale.set(1, 1, 1); + scene.add(wireframe); + + // Line ( THREE.WireframeGeometry, THREE.LineBasicMaterial ) - rendered with gl.LINE + + geo = new THREE.WireframeGeometry(geo); + + matLineBasic = new THREE.LineBasicMaterial({ color: 0x4080ff }); + matLineDashed = new THREE.LineDashedMaterial({ scale: 2, dashSize: 1, gapSize: 1 }); + + wireframe1 = new THREE.LineSegments(geo, matLineBasic); + wireframe1.computeLineDistances(); + wireframe1.visible = false; + scene.add(wireframe1); + + // + + window.addEventListener('resize', onWindowResize); + onWindowResize(); + + initGui(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + insetWidth = window.innerHeight / 4; // square + insetHeight = window.innerHeight / 4; + + camera2.aspect = insetWidth / insetHeight; + camera2.updateProjectionMatrix(); +} + +function animate() { + // main scene + + renderer.setClearColor(0x000000, 0); + + renderer.setViewport(0, 0, window.innerWidth, window.innerHeight); + + renderer.autoClear = true; + + scene.backgroundNode = null; + + scene.name = 'Scene'; + + renderer.render(scene, camera); + + // inset scene + + const posY = window.innerHeight - insetHeight - 20; + + renderer.clearDepth(); // important! + + renderer.setScissorTest(true); + + renderer.setScissor(20, posY, insetWidth, insetHeight); + + renderer.setViewport(20, posY, insetWidth, insetHeight); + + camera2.position.copy(camera.position); + camera2.quaternion.copy(camera.quaternion); + + renderer.autoClear = false; + + scene.backgroundNode = backgroundNode; + + scene.name = 'Scene [ Scissor ]'; + + renderer.render(scene, camera2); + + renderer.setScissorTest(false); +} + +// + +function initGui() { + gui = renderer.inspector.createParameters('Settings'); + + const param = { + 'line type': 0, + 'width (px)': 5, + dashed: false, + 'dash scale': 1, + 'dash / gap': 1, + }; + + gui.add(param, 'line type', { LineGeometry: 0, 'gl.LINE': 1 }).onChange(function (val) { + switch (val) { + case 0: + wireframe.visible = true; + + wireframe1.visible = false; + + break; + + case 1: + wireframe.visible = false; + + wireframe1.visible = true; + + break; + } + }); + + gui.add(param, 'width (px)', 1, 10).onChange(function (val) { + matLine.linewidth = val; + }); + + gui.add(param, 'dashed').onChange(function (val) { + matLine.dashed = val; + wireframe1.material = val ? matLineDashed : matLineBasic; + }); + + gui.add(param, 'dash scale', 0.5, 1, 0.1).onChange(function (val) { + matLine.scale = val; + matLineDashed.scale = val; + }); + + gui.add(param, 'dash / gap', { '2 : 1': 0, '1 : 1': 1, '1 : 2': 2 }).onChange(function (val) { + switch (val) { + case 0: + matLine.dashSize = 2; + matLine.gapSize = 1; + + matLineDashed.dashSize = 2; + matLineDashed.gapSize = 1; + + break; + + case 1: + matLine.dashSize = 1; + matLine.gapSize = 1; + + matLineDashed.dashSize = 1; + matLineDashed.gapSize = 1; + + break; + + case 2: + matLine.dashSize = 1; + matLine.gapSize = 2; + + matLineDashed.dashSize = 1; + matLineDashed.gapSize = 2; + + break; + } + }); +} diff --git a/examples-testing/examples/webgpu_loader_gltf.ts b/examples-testing/examples/webgpu_loader_gltf.ts new file mode 100644 index 000000000..6380bd903 --- /dev/null +++ b/examples-testing/examples/webgpu_loader_gltf.ts @@ -0,0 +1,183 @@ +import * as THREE from 'three/webgpu'; + +import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer, controls; +let currentModel, mixer; +let currentLoadId = 0; + +const timer = new THREE.Timer(); + +init().then(render); + +async function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); + camera.position.set(-1.8, 0.6, 2.7); + + scene = new THREE.Scene(); + + new UltraHDRLoader().setPath('textures/equirectangular/').load('royal_esplanade_2k.hdr.jpg', function (texture) { + texture.mapping = THREE.EquirectangularReflectionMapping; + //texture.minFilter = THREE.LinearMipmapLinearFilter; + //texture.generateMipmaps = true; + + scene.background = texture; + scene.environment = texture; + + // model + + fetch('https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Assets/main/Models/model-index.json') + .then(response => response.json()) + .then(models => { + const gui = renderer.inspector.createParameters('Model'); + const modelNames = models.map(m => m.name); + const params = { model: 'DamagedHelmet' }; + + if (!modelNames.includes(params.model) && modelNames.length > 0) { + params.model = modelNames[0]; + } + + gui.add(params, 'model', modelNames).onChange(name => { + const modelInfo = models.find(m => m.name === name); + loadModel(modelInfo); + }); + + gui.add(scene, 'backgroundBlurriness', 0, 1); + + const initialModel = models.find(m => m.name === params.model); + if (initialModel) loadModel(initialModel); + }); + }); + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(render); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.inspector = new Inspector(); + container.appendChild(renderer.domElement); + + await renderer.init(); + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + controls.minDistance = 2; + controls.maxDistance = 10; + controls.target.set(0, 0, -0.2); + controls.update(); + + window.addEventListener('resize', onWindowResize); +} + +function loadModel(modelInfo) { + const variants = modelInfo.variants; + const variant = variants['glTF-Binary'] || variants['glTF']; + const url = `https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Assets/main/Models/${modelInfo.name}/${variant.endsWith('.glb') ? 'glTF-Binary' : 'glTF'}/${variant}`; + + if (currentModel) { + currentModel.traverse(model => { + if (model.isMesh) { + model.geometry.dispose(); + model.material.dispose(); + + for (const value of Object.values(model.material)) { + if (value && value.isTexture) { + value.dispose(); + } + } + } + }); + + scene.remove(currentModel); + currentModel = null; + } + + if (mixer) { + mixer.stopAllAction(); + mixer = null; + } + + const loadId = ++currentLoadId; + + const loader = new GLTFLoader(); + loader.load(url, async function (gltf) { + if (loadId !== currentLoadId) return; + + currentModel = gltf.scene; + + // wait until the model can be added to the scene without blocking due to shader compilation + + await renderer.compileAsync(currentModel, camera, scene); + + if (loadId !== currentLoadId) return; + + scene.add(currentModel); + + fitCameraToSelection(camera, controls, currentModel); + + // animations + + if (gltf.animations.length > 0) { + mixer = new THREE.AnimationMixer(currentModel); + + for (const animation of gltf.animations) { + mixer.clipAction(animation).play(); + } + } + }); +} + +function fitCameraToSelection(camera, controls, selection, fitOffset = 1.3) { + const box = new THREE.Box3(); + box.setFromObject(selection); + + const size = box.getSize(new THREE.Vector3()); + const center = box.getCenter(new THREE.Vector3()); + + const maxSize = Math.max(size.x, size.y, size.z); + const fitHeightDistance = maxSize / (2 * Math.atan((Math.PI * camera.fov) / 360)); + // const fitWidthDistance = fitHeightDistance / camera.aspect; + // const distance = fitOffset * Math.max( fitHeightDistance, fitWidthDistance ); + const distance = fitOffset * fitHeightDistance; + + const direction = controls.target.clone().sub(camera.position).normalize().multiplyScalar(distance); + + controls.maxDistance = distance * 10; + controls.minDistance = distance / 10; + controls.target.copy(center); + + camera.near = distance / 100; + camera.far = distance * 100; + camera.updateProjectionMatrix(); + + camera.position.copy(controls.target).sub(direction); + + controls.update(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function render() { + timer.update(); + + controls.update(); + + if (mixer) mixer.update(timer.getDelta()); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_loader_gltf_anisotropy.ts b/examples-testing/examples/webgpu_loader_gltf_anisotropy.ts new file mode 100644 index 000000000..a3aa95e15 --- /dev/null +++ b/examples-testing/examples/webgpu_loader_gltf_anisotropy.ts @@ -0,0 +1,64 @@ +import * as THREE from 'three/webgpu'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; + +let renderer, scene, camera, controls; + +init(); + +async function init() { + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = 1.35; + document.body.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.01, 10); + camera.position.set(-0.35, -0.2, 0.35); + + controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, -0.08, 0.11); + controls.minDistance = 0.1; + controls.maxDistance = 2; + controls.update(); + + const hdrLoader = new UltraHDRLoader().setPath('textures/equirectangular/'); + const gltfLoader = new GLTFLoader().setPath('models/gltf/'); + + const [texture, gltf] = await Promise.all([ + hdrLoader.loadAsync('royal_esplanade_2k.hdr.jpg'), + gltfLoader.loadAsync('AnisotropyBarnLamp.glb'), + ]); + + // environment + + texture.mapping = THREE.EquirectangularReflectionMapping; + + scene.background = texture; + scene.backgroundBlurriness = 0.5; + scene.environment = texture; + + // model + + scene.add(gltf.scene); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_loader_gltf_compressed.ts b/examples-testing/examples/webgpu_loader_gltf_compressed.ts new file mode 100644 index 000000000..31d5d0d1a --- /dev/null +++ b/examples-testing/examples/webgpu_loader_gltf_compressed.ts @@ -0,0 +1,69 @@ +import * as THREE from 'three/webgpu'; + +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js'; +import { MeshoptDecoder } from 'three/addons/libs/meshopt_decoder.module.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let camera, scene, renderer; + +init(); + +async function init() { + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 20); + camera.position.set(2, 2, 2); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xeeeeee); + + //lights + + const light = new THREE.PointLight(0xffffff); + light.power = 1300; + camera.add(light); + scene.add(camera); + + //renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.toneMapping = THREE.ReinhardToneMapping; + renderer.toneMappingExposure = 1; + document.body.appendChild(renderer.domElement); + + await renderer.init(); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 3; + controls.maxDistance = 6; + controls.update(); + + const ktx2Loader = await new KTX2Loader().setTranscoderPath('jsm/libs/basis/').detectSupport(renderer); + + const loader = new GLTFLoader(); + loader.setKTX2Loader(ktx2Loader); + loader.setMeshoptDecoder(MeshoptDecoder); + loader.load('models/gltf/coffeemat.glb', function (gltf) { + const gltfScene = gltf.scene; + gltfScene.position.y = -0.8; + gltfScene.scale.setScalar(0.01); + + scene.add(gltfScene); + }); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_loader_gltf_dispersion.ts b/examples-testing/examples/webgpu_loader_gltf_dispersion.ts new file mode 100644 index 000000000..a2e815ddb --- /dev/null +++ b/examples-testing/examples/webgpu_loader_gltf_dispersion.ts @@ -0,0 +1,63 @@ +import * as THREE from 'three/webgpu'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; + +let camera, scene, renderer; + +init(); + +async function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.01, 5); + camera.position.set(0.1, 0.05, 0.15); + + scene = new THREE.Scene(); + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(render); + renderer.toneMapping = THREE.ReinhardToneMapping; // TODO: Add THREE.NeutralToneMapping; + renderer.toneMappingExposure = 1; + container.appendChild(renderer.domElement); + + const hdrTexture = await new HDRLoader() + .setPath('textures/equirectangular/') + .loadAsync('pedestrian_overpass_1k.hdr'); + hdrTexture.mapping = THREE.EquirectangularReflectionMapping; + + scene = new THREE.Scene(); + scene.backgroundBlurriness = 0.5; + scene.environment = hdrTexture; + scene.background = hdrTexture; + + const loader = new GLTFLoader(); + const gltf = await loader.loadAsync('models/gltf/DispersionTest.glb'); + + scene.add(gltf.scene); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 0.1; + controls.maxDistance = 10; + controls.target.set(0, 0, 0); + controls.update(); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_loader_gltf_iridescence.ts b/examples-testing/examples/webgpu_loader_gltf_iridescence.ts new file mode 100644 index 000000000..8007d5e65 --- /dev/null +++ b/examples-testing/examples/webgpu_loader_gltf_iridescence.ts @@ -0,0 +1,70 @@ +import * as THREE from 'three/webgpu'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; + +let renderer, scene, camera, controls; + +init().catch(function (err) { + console.error(err); +}); + +async function init() { + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setAnimationLoop(render); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + document.body.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.05, 20); + camera.position.set(0.35, 0.05, 0.35); + + controls = new OrbitControls(camera, renderer.domElement); + controls.autoRotate = true; + controls.autoRotateSpeed = -0.5; + controls.target.set(0, 0.2, 0); + controls.update(); + + const hdrLoader = new HDRLoader().setPath('textures/equirectangular/'); + + const gltfLoader = new GLTFLoader().setPath('models/gltf/'); + + const [texture, gltf] = await Promise.all([ + hdrLoader.loadAsync('venice_sunset_1k.hdr'), + gltfLoader.loadAsync('IridescenceLamp.glb'), + ]); + + // environment + + texture.mapping = THREE.EquirectangularReflectionMapping; + + scene.background = texture; + scene.environment = texture; + + // model + + scene.add(gltf.scene); + + render(); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + render(); +} + +function render() { + controls.update(); + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_loader_gltf_sheen.ts b/examples-testing/examples/webgpu_loader_gltf_sheen.ts new file mode 100644 index 000000000..442edc7fb --- /dev/null +++ b/examples-testing/examples/webgpu_loader_gltf_sheen.ts @@ -0,0 +1,81 @@ +import * as THREE from 'three/webgpu'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer, controls; + +init(); + +function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 20); + camera.position.set(-0.75, 0.7, 1.25); + + scene = new THREE.Scene(); + //scene.add( new THREE.DirectionalLight( 0xffffff, 2 ) ); + + // model + + new GLTFLoader().setPath('models/gltf/').load('SheenChair.glb', function (gltf) { + scene.add(gltf.scene); + + const object = gltf.scene.getObjectByName('SheenChair_fabric'); + + const gui = renderer.inspector.createParameters('SheenChair_fabric'); + + gui.add(object.material, 'sheen', 0, 1); + }); + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = 1; + renderer.inspector = new Inspector(); + container.appendChild(renderer.domElement); + + scene.background = new THREE.Color(0xaaaaaa); + + new UltraHDRLoader().setPath('textures/equirectangular/').load('royal_esplanade_2k.hdr.jpg', function (texture) { + texture.mapping = THREE.EquirectangularReflectionMapping; + + scene.background = texture; + //scene.backgroundBlurriness = 1; // @TODO: Needs PMREM + scene.environment = texture; + }); + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + controls.minDistance = 1; + controls.maxDistance = 10; + controls.target.set(0, 0.35, 0); + controls.update(); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + controls.update(); // required if damping enabled + + render(); +} + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_loader_gltf_transmission.ts b/examples-testing/examples/webgpu_loader_gltf_transmission.ts new file mode 100644 index 000000000..45b86e772 --- /dev/null +++ b/examples-testing/examples/webgpu_loader_gltf_transmission.ts @@ -0,0 +1,83 @@ +import * as THREE from 'three/webgpu'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; + +import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; + +let camera, scene, renderer, controls, timer, mixer; + +init(); + +function init() { + timer = new THREE.Timer(); + timer.connect(document); + + const container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); + camera.position.set(0, 0.4, 0.7); + + scene = new THREE.Scene(); + + new UltraHDRLoader().setPath('textures/equirectangular/').load('royal_esplanade_2k.hdr.jpg', function (texture) { + texture.mapping = THREE.EquirectangularReflectionMapping; + + scene.background = texture; + scene.backgroundBlurriness = 0.35; + + scene.environment = texture; + + // model + + new GLTFLoader() + .setPath('models/gltf/') + .setDRACOLoader(new DRACOLoader().setDecoderPath('jsm/libs/draco/gltf/')) + .load('IridescentDishWithOlives.glb', function (gltf) { + mixer = new THREE.AnimationMixer(gltf.scene); + mixer.clipAction(gltf.animations[0]).play(); + + scene.add(gltf.scene); + }); + }); + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setAnimationLoop(render); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = 1; + container.appendChild(renderer.domElement); + + controls = new OrbitControls(camera, renderer.domElement); + controls.autoRotate = true; + controls.autoRotateSpeed = -0.75; + controls.enableDamping = true; + controls.minDistance = 0.5; + controls.maxDistance = 1; + controls.target.set(0, 0.1, 0); + controls.update(); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function render() { + timer.update(); + + if (mixer) mixer.update(timer.getDelta()); + + controls.update(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_loader_materialx.ts b/examples-testing/examples/webgpu_loader_materialx.ts new file mode 100644 index 000000000..bb84eb693 --- /dev/null +++ b/examples-testing/examples/webgpu_loader_materialx.ts @@ -0,0 +1,247 @@ +import * as THREE from 'three/webgpu'; + +import { Fn, abs, fract, fwidth, length, max, saturate, smoothstep, vec4, positionWorld, float } from 'three/tsl'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +import { MaterialXLoader } from 'three/addons/loaders/MaterialXLoader.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +const SAMPLE_PATH = + 'https://raw.githubusercontent.com/materialx/MaterialX/main/resources/Materials/Examples/StandardSurface/'; +const LOCAL_SAMPLE_PATH = 'materialx/'; +const samples = [ + 'standard_surface_brass_tiled.mtlx', + 'standard_surface_brick_procedural.mtlx', + 'standard_surface_carpaint.mtlx', + //'standard_surface_chess_set.mtlx', + 'standard_surface_chrome.mtlx', + 'standard_surface_copper.mtlx', + //'standard_surface_default.mtlx', + //'standard_surface_glass.mtlx', + //'standard_surface_glass_tinted.mtlx', + 'standard_surface_gold.mtlx', + //'standard_surface_greysphere.mtlx', + //'standard_surface_greysphere_calibration.mtlx', + 'standard_surface_jade.mtlx', + //'standard_surface_look_brass_tiled.mtlx', + //'standard_surface_look_wood_tiled.mtlx', + 'standard_surface_marble_solid.mtlx', + 'standard_surface_metal_brushed.mtlx', + 'standard_surface_plastic.mtlx', + //'standard_surface_thin_film.mtlx', + 'standard_surface_velvet.mtlx', + 'standard_surface_wood_tiled.mtlx', +]; + +const localSamples = [ + 'heightnormal.mtlx', + 'conditional_if_float.mtlx', + 'image_transform.mtlx', + 'color3_vec3_cm_test.mtlx', + 'rotate2d_test.mtlx', + 'rotate3d_test.mtlx', + 'heighttonormal_normal_input.mtlx', + 'roughness_test.mtlx', + 'opacity_test.mtlx', + 'opacity_only_test.mtlx', + 'specular_test.mtlx', + 'ior_test.mtlx', + 'combined_test.mtlx', + 'texture_opacity_test.mtlx', + 'transmission_test.mtlx', + 'transmission_only_test.mtlx', + 'transmission_rough.mtlx', + 'thin_film_rainbow_test.mtlx', + 'thin_film_ior_clamp_test.mtlx', + 'sheen_test.mtlx', +]; + +let camera, scene, renderer; +let controls, prefab; +const models = []; + +init(); + +function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.toneMapping = THREE.LinearToneMapping; + renderer.toneMappingExposure = 0.5; + renderer.inspector = new Inspector(); + renderer.setAnimationLoop(render); + container.appendChild(renderer.domElement); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 200); + camera.position.set(10, 10, 20); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xffffff); + + // Ground plane + + const material = new THREE.MeshBasicNodeMaterial(); + + const grid = Fn(([coord, lineWidth = float(0.01), dotSize = float(0.03)]) => { + // https://bgolus.medium.com/the-best-darn-grid-shader-yet-727f9278b9d8 + + const g = fract(coord); + const fw = fwidth(coord); + const gx = abs(g.x.sub(0.5)); + const gy = abs(g.y.sub(0.5)); + + const lineX = saturate(lineWidth.sub(gx).div(fw.x).add(0.5)); + const lineY = saturate(lineWidth.sub(gy).div(fw.y).add(0.5)); + const lines = max(lineX, lineY); + + const squareDist = max(gx, gy); + const aa = max(fw.x, fw.y); + const dots = smoothstep(dotSize.add(aa), dotSize.sub(aa), squareDist); + + return max(dots, lines); + }); + + const fade = Fn(([radius = float(10.0), falloff = float(1.0)]) => { + return smoothstep(radius, radius.sub(falloff), length(positionWorld)); + }); + + const gridColor = vec4(0.5, 0.5, 0.5, 1.0); + const baseColor = vec4(1.0, 1.0, 1.0, 0.0); + + material.colorNode = grid(positionWorld.xz, 0.007, 0.03).mix(baseColor, gridColor).mul(fade(30.0, 20.0)); + material.transparent = true; + + const plane = new THREE.Mesh(new THREE.CircleGeometry(40), material); + plane.rotation.x = -Math.PI / 2; + plane.renderOrder = -1; + scene.add(plane); + + // + + controls = new OrbitControls(camera); + controls.connect(renderer.domElement); + controls.enableDamping = true; + controls.minDistance = 2; + controls.maxDistance = 40; + + // + + new HDRLoader().setPath('textures/equirectangular/').load('san_giuseppe_bridge_2k.hdr', async texture => { + texture.mapping = THREE.EquirectangularReflectionMapping; + + scene.environment = texture; + + prefab = (await new GLTFLoader().loadAsync('./models/gltf/ShaderBall.glb')).scene; + + for (const sample of samples) { + await addSample(sample, SAMPLE_PATH); + } + + for (const sample of localSamples) { + await addSample(sample, LOCAL_SAMPLE_PATH); + } + + addGUI(); + }); + + window.addEventListener('resize', onWindowResize); +} + +function updateModelsAlign() { + const COLUMN_COUNT = 6; + const DIST_X = 3; + const DIST_Z = 3; + + const lineCount = Math.floor(models.length / COLUMN_COUNT) - 1.5; + + const offsetX = DIST_X * (COLUMN_COUNT - 1) * -0.5; + const offsetZ = DIST_Z * lineCount * 0.5; + + for (let i = 0; i < models.length; i++) { + const model = models[i]; + + model.position.x = (i % COLUMN_COUNT) * DIST_X + offsetX; + model.position.z = Math.floor(i / COLUMN_COUNT) * -DIST_Z + offsetZ; + } +} + +async function addSample(sample, path) { + const model = prefab.clone(); + + models.push(model); + + scene.add(model); + + updateModelsAlign(); + + // + + const material = await new MaterialXLoader() + .setPath(path) + .loadAsync(sample) + .then(({ materials }) => Object.values(materials).pop()); + + const calibrationMesh = model.getObjectByName('Calibration_Mesh'); + calibrationMesh.material = material; + + const previewMesh = model.getObjectByName('Preview_Mesh'); + previewMesh.material = material; + + if (material.transparent) { + calibrationMesh.renderOrder = 1; + previewMesh.renderOrder = 2; + } + + await renderer.compileAsync(model, camera, scene); +} + +function addGUI() { + const gui = renderer.inspector.createParameters('MaterialX Loader'); + + const API = { + showCalibrationMesh: true, + showPreviewMesh: true, + }; + + gui.add(API, 'showCalibrationMesh') + .name('Calibration Mesh') + .onChange(function (value) { + setVisibility('Calibration_Mesh', value); + }); + + gui.add(API, 'showPreviewMesh') + .name('Preview Mesh') + .onChange(function (value) { + setVisibility('Preview_Mesh', value); + }); +} + +function setVisibility(name, visible) { + scene.traverse(function (node) { + if (node.isMesh) { + if (node.name == name) node.visible = visible; + } + }); +} + +// + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function render() { + controls.update(); + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_loader_texture_ktx2.ts b/examples-testing/examples/webgpu_loader_texture_ktx2.ts new file mode 100644 index 000000000..1daf0f032 --- /dev/null +++ b/examples-testing/examples/webgpu_loader_texture_ktx2.ts @@ -0,0 +1,189 @@ +import * as THREE from 'three'; + +import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js'; + +let canvas, renderer; + +const scenes = []; + +const sections = [ + { + title: 'Uncompressed', + description: + 'Uncompressed formats (rgba8, rgba16, rgba32) load as THREE.DataTexture objects.' + + ' Lossless, easy to read/write, uncompressed on GPU, optionally compressed over the network.', + textures: [ + { path: '2d_rgba8.ktx2' }, + { path: '2d_rgba8_linear.ktx2' }, + { path: '2d_rgba16_linear.ktx2' }, + { path: '2d_rgba32_linear.ktx2' }, + { path: '2d_rgb9e5_linear.ktx2' }, + { path: '2d_r11g11b10_linear.ktx2' }, + ], + }, + { + title: 'Compressed', + description: + 'Compressed formats (ASTC, BCn, ...) load as THREE.CompressedTexture objects,' + + ' reducing memory cost. Requires native support on the device GPU: no single compressed' + + ' format is supported on every device.', + textures: [ + { path: '2d_astc4x4.ktx2' }, + { path: '2d_etc1.ktx2' }, + { path: '2d_etc2.ktx2' }, + { path: '2d_bc1.ktx2' }, + { path: '2d_bc3.ktx2' }, + { path: '2d_bc4.ktx2' }, + { path: '2d_bc5.ktx2' }, + { path: '2d_bc7.ktx2' }, + ], + }, + + { + title: 'Universal', + description: + 'Basis Universal textures are specialized intermediate formats supporting fast' + + ' runtime transcoding into other GPU texture compression formats. After transcoding,' + + ' universal textures can be used on any device at reduced memory cost.', + textures: [{ path: '2d_etc1s.ktx2' }, { path: '2d_uastc.ktx2' }], + }, +]; + +init(); + +async function init() { + canvas = document.getElementById('c'); + + renderer = new THREE.WebGPURenderer({ canvas, antialias: true, forceWebGL: false }); + renderer.setClearColor(0xffffff, 1); + renderer.setPixelRatio(window.devicePixelRatio); + + await renderer.init(); + + const loader = new KTX2Loader() + .setTranscoderPath('jsm/libs/basis/') + .setPath('textures/ktx2/') + .detectSupport(renderer); + + const geometry = flipY(new THREE.PlaneGeometry(1, 1)); + + const content = document.getElementById('content'); + + for (const section of sections) { + const sectionElement = document.createElement('section'); + + const sectionHeader = document.createElement('h2'); + sectionHeader.textContent = section.title; + sectionElement.appendChild(sectionHeader); + + const sectionDescription = document.createElement('p'); + sectionDescription.className = 'description'; + sectionDescription.textContent = section.description; + sectionElement.appendChild(sectionDescription); + + for (const { path, supported } of section.textures) { + const scene = new THREE.Scene(); + + // make a list item + const element = document.createElement('div'); + element.className = 'list-item'; + + const sceneElement = document.createElement('div'); + element.appendChild(sceneElement); + + const labelElement = document.createElement('div'); + labelElement.innerText = 'file: ' + path; + element.appendChild(labelElement); + + // the element that represents the area we want to render the scene + scene.userData.element = sceneElement; + sectionElement.appendChild(element); + + const camera = new THREE.PerspectiveCamera(50, 1, 1, 10); + camera.position.z = 2; + scene.userData.camera = camera; + + try { + const texture = await loader.loadAsync(supported === false ? 'fail_load.ktx2' : path); + const mesh = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ map: texture })); + + labelElement.innerText += '\ncolorSpace: ' + texture.colorSpace; + + scene.add(mesh); + scenes.push(scene); + } catch (e) { + console.error(`Failed to load ${path}`, e); + } + } + + content.appendChild(sectionElement); + } + + renderer.setAnimationLoop(animate); +} + +function updateSize() { + const width = canvas.clientWidth; + const height = canvas.clientHeight; + + if (canvas.width !== width || canvas.height !== height) { + renderer.setSize(width, height, false); + } +} + +// Rewrite UVs for `flipY=false` textures. + +function flipY(geometry) { + const uv = geometry.attributes.uv; + + for (let i = 0; i < uv.count; i++) { + uv.setY(i, 1 - uv.getY(i)); + } + + return geometry; +} + +function animate() { + updateSize(); + + canvas.style.transform = `translateY(${window.scrollY}px)`; + + renderer.setClearColor(0xffffff); + renderer.setScissorTest(false); + renderer.clear(); + + renderer.setClearColor(0xe0e0e0); + renderer.setScissorTest(true); + + scenes.forEach(function (scene) { + // get the element that is a place holder for where we want to + // draw the scene + const element = scene.userData.element; + + // get its position relative to the page's viewport + const rect = element.getBoundingClientRect(); + + // check if it's offscreen. If so skip it + if ( + rect.top < 0 || + rect.bottom > renderer.domElement.clientHeight || + rect.left < 0 || + rect.right > renderer.domElement.clientWidth + ) { + return; // it's off screen + } + + // set the viewport + const width = rect.right - rect.left; + const height = rect.bottom - rect.top; + const left = rect.left; + const top = rect.top; + + renderer.setViewport(left, top, width, height); + renderer.setScissor(left, top, width, height); + + const camera = scene.userData.camera; + + renderer.render(scene, camera); + }); +} diff --git a/examples-testing/examples/webgpu_materials_alphahash.ts b/examples-testing/examples/webgpu_materials_alphahash.ts new file mode 100644 index 000000000..047bbe582 --- /dev/null +++ b/examples-testing/examples/webgpu_materials_alphahash.ts @@ -0,0 +1,129 @@ +import * as THREE from 'three/webgpu'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; + +import { ssaaPass } from 'three/addons/tsl/display/SSAAPassNode.js'; + +let camera, scene, renderer, controls, mesh, material, renderPipeline; + +const amount = parseInt(window.location.search.slice(1)) || 3; +const count = Math.pow(amount, 3); + +const color = new THREE.Color(); + +const params = { + alpha: 0.5, + alphaHash: true, +}; + +init(); + +async function init() { + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(amount, amount, amount); + camera.lookAt(0, 0, 0); + + scene = new THREE.Scene(); + + const geometry = new THREE.IcosahedronGeometry(0.5, 3); + + material = new THREE.MeshStandardMaterial({ + color: 0xffffff, + alphaHash: params.alphaHash, + opacity: params.alpha, + }); + + mesh = new THREE.InstancedMesh(geometry, material, count); + + let i = 0; + const offset = (amount - 1) / 2; + + const matrix = new THREE.Matrix4(); + + for (let x = 0; x < amount; x++) { + for (let y = 0; y < amount; y++) { + for (let z = 0; z < amount; z++) { + matrix.setPosition(offset - x, offset - y, offset - z); + + mesh.setMatrixAt(i, matrix); + mesh.setColorAt(i, color.setHex(Math.random() * 0xffffff)); + + i++; + } + } + } + + scene.add(mesh); + + // + + renderer = new THREE.WebGPURenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + await renderer.init(); + + // + + const environment = new RoomEnvironment(); + const pmremGenerator = new THREE.PMREMGenerator(renderer); + + scene.environment = pmremGenerator.fromScene(environment, 0.04).texture; + environment.dispose(); + + // + + // postprocessing + + renderPipeline = new THREE.RenderPipeline(renderer); + const scenePass = ssaaPass(scene, camera); + scenePass.sampleLevel = 3; + + renderPipeline.outputNode = scenePass; + + // + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableZoom = false; + controls.enablePan = false; + + // + + const gui = renderer.inspector.createParameters('Parameters'); + + gui.add(params, 'alpha', 0, 1).onChange(onMaterialUpdate); + gui.add(params, 'alphaHash').onChange(onMaterialUpdate); + + const ssaaFolder = gui.addFolder('SSAA'); + ssaaFolder.add(scenePass, 'sampleLevel', 0, 4, 1); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function onMaterialUpdate() { + material.opacity = params.alpha; + material.alphaHash = params.alphaHash; + material.transparent = !params.alphaHash; + material.depthWrite = params.alphaHash; + + material.needsUpdate = true; +} + +function animate() { + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_materials_arrays.ts b/examples-testing/examples/webgpu_materials_arrays.ts new file mode 100644 index 000000000..e6d15853e --- /dev/null +++ b/examples-testing/examples/webgpu_materials_arrays.ts @@ -0,0 +1,135 @@ +import * as THREE from 'three/webgpu'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let renderer, scene, camera, controls; +let planeMesh, boxMesh, boxMeshWireframe, planeMeshWireframe; +let materials; + +const api = { + webgpu: true, +}; + +init(!api.webgpu); + +function init(forceWebGL = false) { + if (renderer) { + renderer.dispose(); + controls.dispose(); + document.body.removeChild(renderer.domElement); + } + + // renderer + renderer = new THREE.WebGPURenderer({ + forceWebGL, + antialias: true, + }); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // scene + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x000000); + + // camera + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 100); + camera.position.set(0, 0, 10); + + // controls + controls = new OrbitControls(camera, renderer.domElement); + + // materials + materials = [ + new THREE.MeshBasicMaterial({ color: 0xff1493, side: THREE.DoubleSide }), + new THREE.MeshBasicMaterial({ color: 0x0000ff, side: THREE.DoubleSide }), + new THREE.MeshBasicMaterial({ color: 0x00ff00, side: THREE.DoubleSide }), + ]; + + // plane geometry + const planeGeometry = new THREE.PlaneGeometry(1, 1, 4, 4); + + planeGeometry.clearGroups(); + const numFacesPerRow = 4; // Number of faces in a row (since each face is made of 2 triangles) + + planeGeometry.addGroup(0, 6 * numFacesPerRow, 0); + planeGeometry.addGroup(6 * numFacesPerRow, 6 * numFacesPerRow, 1); + planeGeometry.addGroup(12 * numFacesPerRow, 6 * numFacesPerRow, 2); + + // box geometry + const boxGeometry = new THREE.BoxGeometry(0.75, 0.75, 0.75); + + boxGeometry.clearGroups(); + boxGeometry.addGroup(0, 6, 0); // front face + boxGeometry.addGroup(6, 6, 0); // back face + boxGeometry.addGroup(12, 6, 2); // top face + boxGeometry.addGroup(18, 6, 2); // bottom face + boxGeometry.addGroup(24, 6, 1); // left face + boxGeometry.addGroup(30, 6, 1); // right face + + scene.background = forceWebGL ? new THREE.Color(0x000000) : new THREE.Color(0x222222); + + // meshes + planeMesh = new THREE.Mesh(planeGeometry, materials); + + const materialsWireframe = []; + + for (let index = 0; index < materials.length; index++) { + const material = new THREE.MeshBasicMaterial({ + color: materials[index].color, + side: THREE.DoubleSide, + wireframe: true, + }); + materialsWireframe.push(material); + } + + planeMeshWireframe = new THREE.Mesh(planeGeometry, materialsWireframe); + boxMeshWireframe = new THREE.Mesh(boxGeometry, materialsWireframe); + + boxMesh = new THREE.Mesh(boxGeometry, materials); + + planeMesh.position.set(-1.5, -1, 0); + boxMesh.position.set(1.5, -0.75, 0); + boxMesh.rotation.set(-Math.PI / 8, Math.PI / 4, Math.PI / 4); + + planeMeshWireframe.position.set(-1.5, 1, 0); + boxMeshWireframe.position.set(1.5, 1.25, 0); + boxMeshWireframe.rotation.set(-Math.PI / 8, Math.PI / 4, Math.PI / 4); + + scene.add(planeMesh, planeMeshWireframe); + scene.add(boxMesh, boxMeshWireframe); +} + +function animate() { + boxMesh.rotation.y += 0.005; + boxMesh.rotation.x += 0.005; + boxMeshWireframe.rotation.y += 0.005; + boxMeshWireframe.rotation.x += 0.005; + renderer.render(scene, camera); +} + +// gui + +const gui = renderer.inspector.createParameters('Parameters'); + +gui.add(api, 'webgpu').onChange(() => { + init(!api.webgpu); +}); + +// listeners + +window.addEventListener('resize', onWindowResize); + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); +} diff --git a/examples-testing/examples/webgpu_materials_basic.ts b/examples-testing/examples/webgpu_materials_basic.ts new file mode 100644 index 000000000..6aca76c97 --- /dev/null +++ b/examples-testing/examples/webgpu_materials_basic.ts @@ -0,0 +1,129 @@ +import * as THREE from 'three/webgpu'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer; + +const spheres = []; + +let mouseX = 0; +let mouseY = 0; + +let windowHalfX = window.innerWidth / 2; +let windowHalfY = window.innerHeight / 2; + +const params = { + color: '#ffffff', + mapping: THREE.CubeReflectionMapping, + refractionRatio: 0.98, + transparent: false, + opacity: 1, +}; + +const mappings = { ReflectionMapping: THREE.CubeReflectionMapping, RefractionMapping: THREE.CubeRefractionMapping }; + +document.addEventListener('mousemove', onDocumentMouseMove); + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.01, 100); + camera.position.z = 3; + + const path = './textures/cube/pisa/'; + const format = '.png'; + const urls = [ + path + 'px' + format, + path + 'nx' + format, + path + 'py' + format, + path + 'ny' + format, + path + 'pz' + format, + path + 'nz' + format, + ]; + + const textureCube = new THREE.CubeTextureLoader().load(urls); + + scene = new THREE.Scene(); + scene.background = textureCube; + + const geometry = new THREE.SphereGeometry(0.1, 32, 16); + const material = new THREE.MeshBasicMaterial({ color: 0xffffff, envMap: textureCube }); + + for (let i = 0; i < 500; i++) { + const mesh = new THREE.Mesh(geometry, material); + + mesh.position.x = Math.random() * 10 - 5; + mesh.position.y = Math.random() * 10 - 5; + mesh.position.z = Math.random() * 10 - 5; + + mesh.scale.x = mesh.scale.y = mesh.scale.z = Math.random() * 3 + 1; + + scene.add(mesh); + + spheres.push(mesh); + } + + // + + renderer = new THREE.WebGPURenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // + + const gui = renderer.inspector.createParameters('Parameters'); + + gui.addColor(params, 'color').onChange(value => material.color.set(value)); + gui.add(params, 'mapping', mappings).onChange(value => { + textureCube.mapping = value; + material.needsUpdate = true; + }); + gui.add(params, 'refractionRatio', 0, 1, 0.01).onChange(value => (material.refractionRatio = value)); + gui.add(params, 'transparent').onChange(value => { + material.transparent = value; + material.needsUpdate = true; + }); + gui.add(params, 'opacity', 0, 1, 0.01).onChange(value => (material.opacity = value)); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + windowHalfX = window.innerWidth / 2; + windowHalfY = window.innerHeight / 2; + + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function onDocumentMouseMove(event) { + mouseX = (event.clientX - windowHalfX) / 100; + mouseY = (event.clientY - windowHalfY) / 100; +} + +// + +function animate() { + const timer = 0.0001 * Date.now(); + + camera.position.x += (mouseX - camera.position.x) * 0.05; + camera.position.y += (-mouseY - camera.position.y) * 0.05; + + camera.lookAt(scene.position); + + for (let i = 0, il = spheres.length; i < il; i++) { + const sphere = spheres[i]; + + sphere.position.x = 5 * Math.cos(timer + i); + sphere.position.y = 5 * Math.sin(timer + i * 1.1); + } + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_materials_cubemap_mipmaps.ts b/examples-testing/examples/webgpu_materials_cubemap_mipmaps.ts new file mode 100644 index 000000000..6c66f9d35 --- /dev/null +++ b/examples-testing/examples/webgpu_materials_cubemap_mipmaps.ts @@ -0,0 +1,119 @@ +import * as THREE from 'three/webgpu'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let container; + +let camera, scene, renderer; + +init(); + +// load customized cube texture +async function loadCubeTextureWithMipmaps() { + const path = 'textures/cube/angus/'; + const format = '.jpg'; + const mipmaps = []; + const maxLevel = 8; + + async function loadCubeTexture(urls) { + return new Promise(function (resolve) { + new THREE.CubeTextureLoader().load(urls, function (cubeTexture) { + resolve(cubeTexture); + }); + }); + } + + // load mipmaps + const pendings = []; + + for (let level = 0; level <= maxLevel; ++level) { + const urls = []; + + for (let face = 0; face < 6; ++face) { + urls.push(path + 'cube_m0' + level + '_c0' + face + format); + } + + const mipmapLevel = level; + + pendings.push( + loadCubeTexture(urls).then(function (cubeTexture) { + mipmaps[mipmapLevel] = cubeTexture; + }), + ); + } + + await Promise.all(pendings); + + const customizedCubeTexture = mipmaps.shift(); + customizedCubeTexture.mipmaps = mipmaps; + customizedCubeTexture.colorSpace = THREE.SRGBColorSpace; + customizedCubeTexture.minFilter = THREE.LinearMipMapLinearFilter; + customizedCubeTexture.magFilter = THREE.LinearFilter; + customizedCubeTexture.generateMipmaps = false; + customizedCubeTexture.needsUpdate = true; + + return customizedCubeTexture; +} + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000); + camera.position.z = 500; + + scene = new THREE.Scene(); + + loadCubeTextureWithMipmaps().then(function (cubeTexture) { + // model + const sphere = new THREE.SphereGeometry(100, 128, 128); + + // manual mipmaps + let material = new THREE.MeshBasicMaterial({ color: 0xffffff, envMap: cubeTexture }); + material.name = 'manual mipmaps'; + + let mesh = new THREE.Mesh(sphere, material); + mesh.position.set(100, 0, 0); + scene.add(mesh); + + // auto mipmaps + material = material.clone(); + material.name = 'auto mipmaps'; + + const autoCubeTexture = cubeTexture.clone(); + autoCubeTexture.mipmaps = []; + autoCubeTexture.generateMipmaps = true; + autoCubeTexture.needsUpdate = true; + + material.envMap = autoCubeTexture; + + mesh = new THREE.Mesh(sphere, material); + mesh.position.set(-100, 0, 0); + scene.add(mesh); + }); + + //renderer + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + //controls + const controls = new OrbitControls(camera, renderer.domElement); + controls.minPolarAngle = Math.PI / 4; + controls.maxPolarAngle = Math.PI / 1.5; + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_materials_displacementmap.ts b/examples-testing/examples/webgpu_materials_displacementmap.ts new file mode 100644 index 000000000..17a01f324 --- /dev/null +++ b/examples-testing/examples/webgpu_materials_displacementmap.ts @@ -0,0 +1,195 @@ +import * as THREE from 'three/webgpu'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { OBJLoader } from 'three/addons/loaders/OBJLoader.js'; + +let camera, scene, renderer, controls; + +const settings = { + metalness: 1.0, + roughness: 0.4, + ambientIntensity: 0.2, + aoMapIntensity: 1.0, + envMapIntensity: 1.0, + displacementScale: 2.436143, // from original model + normalScale: 1.0, +}; + +let mesh, material; + +let pointLight, ambientLight; + +const height = 500; // of camera frustum + +let r = 0.0; + +init(); +initGui(); + +// Init gui +function initGui() { + const gui = renderer.inspector.createParameters('settings'); + + gui.add(settings, 'metalness', 0, 1).onChange(function (value) { + material.metalness = value; + }); + + gui.add(settings, 'roughness', 0, 1).onChange(function (value) { + material.roughness = value; + }); + + gui.add(settings, 'aoMapIntensity', 0, 1).onChange(function (value) { + material.aoMapIntensity = value; + }); + + gui.add(settings, 'ambientIntensity', 0, 1).onChange(function (value) { + ambientLight.intensity = value; + }); + + gui.add(settings, 'envMapIntensity', 0, 3).onChange(function (value) { + material.envMapIntensity = value; + }); + + gui.add(settings, 'displacementScale', 0, 3.0).onChange(function (value) { + material.displacementScale = value; + }); + + gui.add(settings, 'normalScale', -1, 1).onChange(function (value) { + material.normalScale.set(1, -1).multiplyScalar(value); + }); +} + +function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + renderer = new THREE.WebGPURenderer(); + renderer.setAnimationLoop(animate); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.inspector = new Inspector(); + container.appendChild(renderer.domElement); + + // + + scene = new THREE.Scene(); + + const aspect = window.innerWidth / window.innerHeight; + camera = new THREE.OrthographicCamera(-height * aspect, height * aspect, height, -height, 1, 10000); + camera.position.z = 1500; + scene.add(camera); + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableZoom = false; + controls.enableDamping = true; + + // lights + + ambientLight = new THREE.AmbientLight(0xffffff, settings.ambientIntensity); + scene.add(ambientLight); + + pointLight = new THREE.PointLight(0xff0000, 1.5, 0, 0); + pointLight.position.z = 2500; + scene.add(pointLight); + + const pointLight2 = new THREE.PointLight(0xff6666, 3, 0, 0); + camera.add(pointLight2); + + const pointLight3 = new THREE.PointLight(0x0000ff, 1.5, 0, 0); + pointLight3.position.x = -1000; + pointLight3.position.z = 1000; + scene.add(pointLight3); + + // env map + + const path = 'textures/cube/SwedishRoyalCastle/'; + const format = '.jpg'; + const urls = [ + path + 'px' + format, + path + 'nx' + format, + path + 'py' + format, + path + 'ny' + format, + path + 'pz' + format, + path + 'nz' + format, + ]; + + const reflectionCube = new THREE.CubeTextureLoader().load(urls); + + // textures + + const textureLoader = new THREE.TextureLoader(); + const normalMap = textureLoader.load('models/obj/ninja/normal.png'); + const aoMap = textureLoader.load('models/obj/ninja/ao.jpg'); + const displacementMap = textureLoader.load('models/obj/ninja/displacement.jpg'); + + // material + + material = new THREE.MeshStandardNodeMaterial({ + color: 0xc1c1c1, + roughness: settings.roughness, + metalness: settings.metalness, + + normalMap: normalMap, + normalScale: new THREE.Vector2(1, -1), // why does the normal map require negation in this case? + + aoMap: aoMap, + aoMapIntensity: 1, + + displacementMap: displacementMap, + displacementScale: settings.displacementScale, + displacementBias: -0.428408, // from original model + + envMap: reflectionCube, + envMapIntensity: settings.envMapIntensity, + + side: THREE.DoubleSide, + }); + + // + + const loader = new OBJLoader(); + loader.load('models/obj/ninja/ninjaHead_Low.obj', function (group) { + const geometry = group.children[0].geometry; + geometry.center(); + + mesh = new THREE.Mesh(geometry, material); + mesh.scale.multiplyScalar(25); + scene.add(mesh); + }); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + const aspect = window.innerWidth / window.innerHeight; + + camera.left = -height * aspect; + camera.right = height * aspect; + camera.top = height; + camera.bottom = -height; + + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + controls.update(); + + render(); +} + +function render() { + pointLight.position.x = 2500 * Math.cos(r); + pointLight.position.z = 2500 * Math.sin(r); + + r += 0.01; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_materials_envmaps.ts b/examples-testing/examples/webgpu_materials_envmaps.ts new file mode 100644 index 000000000..6862caeed --- /dev/null +++ b/examples-testing/examples/webgpu_materials_envmaps.ts @@ -0,0 +1,132 @@ +import * as THREE from 'three/webgpu'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let controls, camera, scene, renderer; +let textureEquirec, textureCube; +let sphereMesh, sphereMaterial, params; + +init(); + +function init() { + // CAMERAS + + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(0, 0, 2.5); + + // SCENE + + scene = new THREE.Scene(); + + // Textures + + const loader = new THREE.CubeTextureLoader(); + loader.setPath('textures/cube/Bridge2/'); + + textureCube = loader.load(['posx.jpg', 'negx.jpg', 'posy.jpg', 'negy.jpg', 'posz.jpg', 'negz.jpg']); + + const textureLoader = new THREE.TextureLoader(); + + textureEquirec = textureLoader.load('textures/2294472375_24a3b8ef46_o.jpg'); + textureEquirec.mapping = THREE.EquirectangularReflectionMapping; + textureEquirec.colorSpace = THREE.SRGBColorSpace; + + scene.background = textureCube; + + // + + const geometry = new THREE.IcosahedronGeometry(1, 15); + sphereMaterial = new THREE.MeshBasicMaterial({ envMap: textureCube }); + sphereMesh = new THREE.Mesh(geometry, sphereMaterial); + scene.add(sphereMesh); + + // + + renderer = new THREE.WebGPURenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // + + controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 1.5; + controls.maxDistance = 6; + + // + + params = { + Type: 'Cube', + Refraction: false, + backgroundRotationX: false, + backgroundRotationY: false, + backgroundRotationZ: false, + syncMaterial: false, + }; + + const gui = renderer.inspector.createParameters('Parameters'); + gui.add(params, 'Type', ['Cube', 'Equirectangular']).onChange(function (value) { + if (value === 'Cube') { + scene.background = textureCube; + + sphereMaterial.envMap = textureCube; + sphereMaterial.needsUpdate = true; + } else if (value === 'Equirectangular') { + scene.background = textureEquirec; + + sphereMaterial.envMap = textureEquirec; + sphereMaterial.needsUpdate = true; + } + }); + gui.add(params, 'Refraction').onChange(function (value) { + if (value) { + textureEquirec.mapping = THREE.EquirectangularRefractionMapping; + textureCube.mapping = THREE.CubeRefractionMapping; + } else { + textureEquirec.mapping = THREE.EquirectangularReflectionMapping; + textureCube.mapping = THREE.CubeReflectionMapping; + } + + sphereMaterial.needsUpdate = true; + }); + gui.add(params, 'backgroundRotationX'); + gui.add(params, 'backgroundRotationY'); + gui.add(params, 'backgroundRotationZ'); + gui.add(params, 'syncMaterial'); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + if (params.backgroundRotationX) { + scene.backgroundRotation.x += 0.001; + } + + if (params.backgroundRotationY) { + scene.backgroundRotation.y += 0.001; + } + + if (params.backgroundRotationZ) { + scene.backgroundRotation.z += 0.001; + } + + if (params.syncMaterial) { + sphereMesh.material.envMapRotation.copy(scene.backgroundRotation); + } + + camera.lookAt(scene.position); + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_materials_envmaps_bpcem.ts b/examples-testing/examples/webgpu_materials_envmaps_bpcem.ts new file mode 100644 index 000000000..adc28c989 --- /dev/null +++ b/examples-testing/examples/webgpu_materials_envmaps_bpcem.ts @@ -0,0 +1,202 @@ +import * as THREE from 'three/webgpu'; +import { + bumpMap, + float, + getParallaxCorrectNormal, + pmremTexture, + reflectVector, + texture, + uniform, + vec3, +} from 'three/tsl'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { RectAreaLightHelper } from 'three/addons/helpers/RectAreaLightHelper.js'; +import { RectAreaLightTexturesLib } from 'three/addons/lights/RectAreaLightTexturesLib.js'; + +let camera, scene, renderer; + +let controls, cubeCamera; + +let groundPlane, wallMat; + +init(); + +async function init() { + THREE.RectAreaLightNode.setLTC(RectAreaLightTexturesLib.init()); + + // scene + + scene = new THREE.Scene(); + + // camera + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000); + camera.position.set(0, 200, -200); + + // cube camera for environment map + + const renderTarget = new THREE.CubeRenderTarget(512); + renderTarget.texture.type = THREE.HalfFloatType; + renderTarget.texture.minFilter = THREE.LinearMipmapLinearFilter; + renderTarget.texture.magFilter = THREE.LinearFilter; + renderTarget.texture.generateMipmaps = true; + renderTarget.texture.mapping = THREE.CubeReflectionMapping; + + cubeCamera = new THREE.CubeCamera(1, 1000, renderTarget); + cubeCamera.position.set(0, -100, 0); + + // ground floor ( with box projected environment mapping ) + + const loader = new THREE.TextureLoader(); + const rMap = loader.load('textures/lava/lavatile.jpg'); + rMap.wrapS = THREE.RepeatWrapping; + rMap.wrapT = THREE.RepeatWrapping; + rMap.repeat.set(2, 1); + + const roughnessUniform = uniform(0.25); + + const defaultMat = new THREE.MeshStandardNodeMaterial(); + defaultMat.envNode = pmremTexture(renderTarget.texture); + defaultMat.roughnessNode = texture(rMap).mul(roughnessUniform); + defaultMat.metalnessNode = float(1); + + const boxProjectedMat = new THREE.MeshStandardNodeMaterial(); + boxProjectedMat.envNode = pmremTexture( + renderTarget.texture, + getParallaxCorrectNormal(reflectVector, vec3(200, 100, 100), vec3(0, -50, 0)), + ); + boxProjectedMat.roughnessNode = texture(rMap).mul(roughnessUniform); + boxProjectedMat.metalnessNode = float(1); + + groundPlane = new THREE.Mesh(new THREE.PlaneGeometry(200, 100, 100), boxProjectedMat); + groundPlane.rotateX(-Math.PI / 2); + groundPlane.position.set(0, -49, 0); + scene.add(groundPlane); + + // walls + + const [diffuseTex, bumpTex] = await Promise.all([ + loader.loadAsync('textures/brick_diffuse.jpg'), + loader.loadAsync('textures/brick_bump.jpg'), + ]); + diffuseTex.colorSpace = THREE.SRGBColorSpace; + + wallMat = new THREE.MeshStandardNodeMaterial(); + + wallMat.colorNode = texture(diffuseTex); + wallMat.normalNode = bumpMap(texture(bumpTex), float(5)); + + const planeGeo = new THREE.PlaneGeometry(100, 100); + + const planeBack1 = new THREE.Mesh(planeGeo, wallMat); + planeBack1.position.z = -50; + planeBack1.position.x = -50; + scene.add(planeBack1); + + const planeBack2 = new THREE.Mesh(planeGeo, wallMat); + planeBack2.position.z = -50; + planeBack2.position.x = 50; + scene.add(planeBack2); + + const planeFront1 = new THREE.Mesh(planeGeo, wallMat); + planeFront1.position.z = 50; + planeFront1.position.x = -50; + planeFront1.rotateY(Math.PI); + scene.add(planeFront1); + + const planeFront2 = new THREE.Mesh(planeGeo, wallMat); + planeFront2.position.z = 50; + planeFront2.position.x = 50; + planeFront2.rotateY(Math.PI); + scene.add(planeFront2); + + const planeRight = new THREE.Mesh(planeGeo, wallMat); + planeRight.position.x = 100; + planeRight.rotateY(-Math.PI / 2); + scene.add(planeRight); + + const planeLeft = new THREE.Mesh(planeGeo, wallMat); + planeLeft.position.x = -100; + planeLeft.rotateY(Math.PI / 2); + scene.add(planeLeft); + + // area lights + + const width = 50; + const height = 50; + const intensity = 5; + + const blueRectLight = new THREE.RectAreaLight(0x9aaeff, intensity, width, height); + blueRectLight.position.set(-99, 5, 0); + blueRectLight.lookAt(0, 5, 0); + scene.add(blueRectLight); + + const blueRectLightHelper = new RectAreaLightHelper(blueRectLight, 0xffffff); + blueRectLight.add(blueRectLightHelper); + + const redRectLight = new THREE.RectAreaLight(0xf3aaaa, intensity, width, height); + redRectLight.position.set(99, 5, 0); + redRectLight.lookAt(0, 5, 0); + scene.add(redRectLight); + + const redRectLightHelper = new RectAreaLightHelper(redRectLight, 0xffffff); + redRectLight.add(redRectLightHelper); + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + await renderer.init(); + + updateCubeMap(); + + window.addEventListener('resize', onWindowResize); + + // controls + + controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, -10, 0); + controls.maxDistance = 400; + controls.minDistance = 10; + controls.update(); + + // gui + + const gui = renderer.inspector.createParameters('Parameters'); + const params = { + 'box projected': true, + }; + gui.add(params, 'box projected').onChange(value => { + groundPlane.material = value ? boxProjectedMat : defaultMat; + }); + gui.add(roughnessUniform, 'value', 0, 1).name('roughness'); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function updateCubeMap() { + groundPlane.visible = false; + + cubeCamera.position.copy(groundPlane.position); + + cubeCamera.update(renderer, scene); + + groundPlane.visible = true; +} + +function animate() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_materials_lightmap.ts b/examples-testing/examples/webgpu_materials_lightmap.ts new file mode 100644 index 000000000..081de95ac --- /dev/null +++ b/examples-testing/examples/webgpu_materials_lightmap.ts @@ -0,0 +1,105 @@ +import * as THREE from 'three/webgpu'; +import { vec4, color, positionLocal, mix } from 'three/tsl'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let container; +let camera, scene, renderer; + +const params = { + intensity: 1, +}; + +init(); + +async function init() { + const { innerWidth, innerHeight } = window; + + container = document.createElement('div'); + document.body.appendChild(container); + + // CAMERA + + camera = new THREE.PerspectiveCamera(40, innerWidth / innerHeight, 1, 10000); + camera.position.set(700, 200, -500); + + // SCENE + + scene = new THREE.Scene(); + + // LIGHTS + + const light = new THREE.DirectionalLight(0xd5deff); + light.position.x = 300; + light.position.y = 250; + light.position.z = -500; + scene.add(light); + + // SKYDOME + + const topColor = new THREE.Color().copy(light.color); + const bottomColor = new THREE.Color(0xffffff); + const offset = 400; + const exponent = 0.6; + + const h = positionLocal.add(offset).normalize().y; + + const skyMat = new THREE.MeshBasicNodeMaterial(); + skyMat.colorNode = vec4(mix(color(bottomColor), color(topColor), h.max(0.0).pow(exponent)), 1.0); + skyMat.side = THREE.BackSide; + + const sky = new THREE.Mesh(new THREE.SphereGeometry(4000, 32, 15), skyMat); + scene.add(sky); + + // MODEL + + const loader = new THREE.ObjectLoader(); + const object = await loader.loadAsync('models/json/lightmap/lightmap.json'); + scene.add(object); + + // RENDERER + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setAnimationLoop(animate); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(innerWidth, innerHeight); + renderer.inspector = new Inspector(); + container.appendChild(renderer.domElement); + + // CONTROLS + + const controls = new OrbitControls(camera, renderer.domElement); + controls.maxPolarAngle = (0.9 * Math.PI) / 2; + controls.enableZoom = false; + + // GUI + + const gui = renderer.inspector.createParameters('Parameters'); + + gui.add(params, 'intensity', 0, 1) + .name('Light Map Intensity') + .onChange(value => { + for (const material of object.material) { + material.lightMapIntensity = value; + } + }); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_materials_matcap.ts b/examples-testing/examples/webgpu_materials_matcap.ts new file mode 100644 index 000000000..0a841ae27 --- /dev/null +++ b/examples-testing/examples/webgpu_materials_matcap.ts @@ -0,0 +1,194 @@ +import * as THREE from 'three/webgpu'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { EXRLoader } from 'three/addons/loaders/EXRLoader.js'; + +let mesh, renderer, scene, camera; + +const API = { + color: 0xffffff, // sRGB + exposure: 1.0, +}; + +init(); + +function init() { + // renderer + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // tone mapping + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = API.exposure; + + // scene + scene = new THREE.Scene(); + + // camera + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 100); + camera.position.set(0, 0, 13); + + // controls + const controls = new OrbitControls(camera, renderer.domElement); + controls.enableZoom = false; + controls.enablePan = false; + + // matcap + const loaderEXR = new EXRLoader(); + const matcap = loaderEXR.load('textures/matcaps/040full.exr'); + + // normalmap + const loader = new THREE.TextureLoader(); + + const normalmap = loader.load('models/gltf/LeePerrySmith/Infinite-Level_02_Tangent_SmoothUV.jpg'); + + // model + new GLTFLoader().load('models/gltf/LeePerrySmith/LeePerrySmith.glb', function (gltf) { + mesh = gltf.scene.children[0]; + mesh.position.y = -0.25; + + mesh.material = new THREE.MeshMatcapNodeMaterial({ + color: new THREE.Color().setHex(API.color), + matcap: matcap, + normalMap: normalmap, + }); + + scene.add(mesh); + }); + + // gui + const gui = renderer.inspector.createParameters('Parameters'); + + gui.addColor(API, 'color') + .listen() + .onChange(function () { + mesh.material.color.set(API.color); + }); + + gui.add(API, 'exposure', 0, 2) + .listen() + .onChange(function () { + renderer.toneMappingExposure = API.exposure; + }); + + // drag 'n drop + initDragAndDrop(); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + renderer.setSize(window.innerWidth, window.innerHeight); + + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); +} + +function animate() { + renderer.render(scene, camera); +} + +// +// drag and drop anywhere in document +// + +function updateMatcap(texture) { + if (mesh.material.matcap) { + mesh.material.matcap.dispose(); + } + + mesh.material.matcap = texture; + + texture.needsUpdate = true; + + mesh.material.needsUpdate = true; // because the color space can change +} + +function handleJPG(event) { + // PNG, WebP, AVIF, too + + function imgCallback(event) { + const texture = new THREE.Texture(event.target); + + texture.colorSpace = THREE.SRGBColorSpace; + + updateMatcap(texture); + } + + const img = new Image(); + + img.onload = imgCallback; + + img.src = event.target.result; +} + +function handleEXR(event) { + const contents = event.target.result; + + const loader = new EXRLoader(); + + loader.setDataType(THREE.HalfFloatType); + + const texData = loader.parse(contents); + + const texture = new THREE.DataTexture(); + + texture.image.width = texData.width; + texture.image.height = texData.height; + texture.image.data = texData.data; + + texture.format = texData.format; + texture.type = texData.type; + texture.colorSpace = THREE.LinearSRGBColorSpace; + texture.minFilter = THREE.LinearFilter; + texture.magFilter = THREE.LinearFilter; + texture.generateMipmaps = false; + texture.flipY = false; + + updateMatcap(texture); +} + +function loadFile(file) { + const filename = file.name; + const extension = filename.split('.').pop().toLowerCase(); + + if (extension === 'exr') { + const reader = new FileReader(); + + reader.addEventListener('load', function (event) { + handleEXR(event); + }); + + reader.readAsArrayBuffer(file); + } else { + // 'jpg', 'png' + + const reader = new FileReader(); + + reader.addEventListener('load', function (event) { + handleJPG(event); + }); + + reader.readAsDataURL(file); + } +} + +function initDragAndDrop() { + document.addEventListener('dragover', function (event) { + event.preventDefault(); + event.dataTransfer.dropEffect = 'copy'; + }); + + document.addEventListener('drop', function (event) { + event.preventDefault(); + + loadFile(event.dataTransfer.files[0]); + }); +} diff --git a/examples-testing/examples/webgpu_materials_sss.ts b/examples-testing/examples/webgpu_materials_sss.ts new file mode 100644 index 000000000..c76df99dc --- /dev/null +++ b/examples-testing/examples/webgpu_materials_sss.ts @@ -0,0 +1,153 @@ +import * as THREE from 'three/webgpu'; +import { texture, uniform, vec3 } from 'three/tsl'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { FBXLoader } from 'three/addons/loaders/FBXLoader.js'; + +let container; +let camera, scene, renderer; +let model; + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 5000); + camera.position.set(0.0, 300, 400 * 4); + + scene = new THREE.Scene(); + + // Lights + + scene.add(new THREE.AmbientLight(0xc1c1c1)); + + const directionalLight = new THREE.DirectionalLight(0xffffff, 0.03); + directionalLight.position.set(0.0, 0.5, 0.5).normalize(); + scene.add(directionalLight); + + const pointLight1 = new THREE.Mesh( + new THREE.SphereGeometry(4, 8, 8), + new THREE.MeshBasicMaterial({ color: 0xc1c1c1 }), + ); + pointLight1.add(new THREE.PointLight(0xc1c1c1, 4.0, 300, 0)); + scene.add(pointLight1); + pointLight1.position.x = 0; + pointLight1.position.y = -50; + pointLight1.position.z = 350; + + const pointLight2 = new THREE.Mesh( + new THREE.SphereGeometry(4, 8, 8), + new THREE.MeshBasicMaterial({ color: 0xc1c100 }), + ); + pointLight2.add(new THREE.PointLight(0xc1c100, 0.75, 500, 0)); + scene.add(pointLight2); + pointLight2.position.x = -100; + pointLight2.position.y = 20; + pointLight2.position.z = -260; + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(render); + container.appendChild(renderer.domElement); + + // + + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.inspector.domElement); + + // + + const controls = new OrbitControls(camera, container); + controls.minDistance = 500; + controls.maxDistance = 3000; + + window.addEventListener('resize', onWindowResize); + + initMaterial(); +} + +function initMaterial() { + const loader = new THREE.TextureLoader(); + const imgTexture = loader.load('models/fbx/white.jpg'); + imgTexture.colorSpace = THREE.SRGBColorSpace; + + const thicknessTexture = loader.load('models/fbx/bunny_thickness.jpg'); + imgTexture.wrapS = imgTexture.wrapT = THREE.RepeatWrapping; + + const material = new THREE.MeshSSSNodeMaterial(); + material.color = new THREE.Color(1.0, 0.2, 0.2); + material.roughness = 0.3; + material.thicknessColorNode = texture(thicknessTexture).mul(vec3(0.5, 0.3, 0.0)); + material.thicknessDistortionNode = uniform(0.1); + material.thicknessAmbientNode = uniform(0.4); + material.thicknessAttenuationNode = uniform(0.8); + material.thicknessPowerNode = uniform(2.0); + material.thicknessScaleNode = uniform(16.0); + + // LOADER + + const loaderFBX = new FBXLoader(); + loaderFBX.load('models/fbx/stanford-bunny.fbx', function (object) { + model = object.children[0]; + model.position.set(0, 0, 10); + model.scale.setScalar(1); + model.material = material; + scene.add(model); + }); + + initGUI(material); +} + +function initGUI(material) { + const gui = renderer.inspector.createParameters('Parameters'); + + const ThicknessControls = function () { + this.distortion = material.thicknessDistortionNode.value; + this.ambient = material.thicknessAmbientNode.value; + this.attenuation = material.thicknessAttenuationNode.value; + this.power = material.thicknessPowerNode.value; + this.scale = material.thicknessScaleNode.value; + }; + + const thicknessControls = new ThicknessControls(); + + gui.add(thicknessControls, 'distortion', 0.01, 1, 0.01).onChange(function () { + material.thicknessDistortionNode.value = thicknessControls.distortion; + }); + + gui.add(thicknessControls, 'ambient', 0.01, 5.0, 0.05).onChange(function () { + material.thicknessAmbientNode.value = thicknessControls.ambient; + }); + + gui.add(thicknessControls, 'attenuation', 0.01, 5.0, 0.05).onChange(function () { + material.thicknessAttenuationNode.value = thicknessControls.attenuation; + }); + + gui.add(thicknessControls, 'power', 0.01, 16.0, 0.1).onChange(function () { + material.thicknessPowerNode.value = thicknessControls.power; + }); + + gui.add(thicknessControls, 'scale', 0.01, 50.0, 0.1).onChange(function () { + material.thicknessScaleNode.value = thicknessControls.scale; + }); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function render() { + if (model) model.rotation.y = performance.now() / 5000; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_materials_texture_manualmipmap.ts b/examples-testing/examples/webgpu_materials_texture_manualmipmap.ts new file mode 100644 index 000000000..090372729 --- /dev/null +++ b/examples-testing/examples/webgpu_materials_texture_manualmipmap.ts @@ -0,0 +1,170 @@ +import * as THREE from 'three/webgpu'; + +const SCREEN_WIDTH = window.innerWidth; +const SCREEN_HEIGHT = window.innerHeight; + +let container; + +let camera, scene1, scene2, renderer; + +let mouseX = 0, + mouseY = 0; + +const windowHalfX = window.innerWidth / 2; +const windowHalfY = window.innerHeight / 2; + +init(); + +async function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(35, SCREEN_WIDTH / SCREEN_HEIGHT, 1, 5000); + camera.position.z = 1500; + + scene1 = new THREE.Scene(); + scene1.background = new THREE.Color(0x000000); + scene1.fog = new THREE.Fog(0x000000, 1500, 4000); + + scene2 = new THREE.Scene(); + scene2.background = new THREE.Color(0x000000); + scene2.fog = new THREE.Fog(0x000000, 1500, 4000); + + // GROUND + + const canvas = mipmap(128, '#f00'); + const textureCanvas1 = new THREE.CanvasTexture(canvas); + textureCanvas1.mipmaps[0] = canvas; + textureCanvas1.mipmaps[1] = mipmap(64, '#0f0'); + textureCanvas1.mipmaps[2] = mipmap(32, '#00f'); + textureCanvas1.mipmaps[3] = mipmap(16, '#400'); + textureCanvas1.mipmaps[4] = mipmap(8, '#040'); + textureCanvas1.mipmaps[5] = mipmap(4, '#004'); + textureCanvas1.mipmaps[6] = mipmap(2, '#044'); + textureCanvas1.mipmaps[7] = mipmap(1, '#404'); + textureCanvas1.colorSpace = THREE.SRGBColorSpace; + textureCanvas1.repeat.set(1000, 1000); + textureCanvas1.wrapS = THREE.RepeatWrapping; + textureCanvas1.wrapT = THREE.RepeatWrapping; + + const textureCanvas2 = textureCanvas1.clone(); + textureCanvas2.magFilter = THREE.NearestFilter; + textureCanvas2.minFilter = THREE.NearestMipmapNearestFilter; + + const materialCanvas1 = new THREE.MeshBasicMaterial({ map: textureCanvas1 }); + const materialCanvas2 = new THREE.MeshBasicMaterial({ color: 0xffccaa, map: textureCanvas2 }); + + const geometry = new THREE.PlaneGeometry(100, 100); + + const meshCanvas1 = new THREE.Mesh(geometry, materialCanvas1); + meshCanvas1.rotation.x = -Math.PI / 2; + meshCanvas1.scale.set(1000, 1000, 1000); + + const meshCanvas2 = new THREE.Mesh(geometry, materialCanvas2); + meshCanvas2.rotation.x = -Math.PI / 2; + meshCanvas2.scale.set(1000, 1000, 1000); + + scene1.add(meshCanvas1); + scene2.add(meshCanvas2); + + // PAINTING + + const texturePainting1 = await new THREE.TextureLoader().loadAsync( + 'textures/758px-Canestra_di_frutta_(Caravaggio).jpg', + ); + const texturePainting2 = texturePainting1.clone(); + + texturePainting1.colorSpace = THREE.SRGBColorSpace; + texturePainting2.colorSpace = THREE.SRGBColorSpace; + + texturePainting1.minFilter = texturePainting1.magFilter = THREE.LinearFilter; + texturePainting2.minFilter = texturePainting2.magFilter = THREE.NearestFilter; + + const materialPainting1 = new THREE.MeshBasicMaterial({ color: 0xffffff, map: texturePainting1 }); + const materialPainting2 = new THREE.MeshBasicMaterial({ color: 0xffccaa, map: texturePainting2 }); + + const geometryPainting = new THREE.PlaneGeometry(100, 100); + const mesh1 = new THREE.Mesh(geometryPainting, materialPainting1); + const mesh2 = new THREE.Mesh(geometryPainting, materialPainting2); + + addPainting(scene1, mesh1); + addPainting(scene2, mesh2); + + function addPainting(zscene, zmesh) { + const image = texturePainting1.image; + + zmesh.scale.x = image.width / 100; + zmesh.scale.y = image.height / 100; + + zscene.add(zmesh); + + const meshFrame = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ color: 0x000000 })); + meshFrame.position.z = -10.0; + meshFrame.scale.x = (1.1 * image.width) / 100; + meshFrame.scale.y = (1.1 * image.height) / 100; + zscene.add(meshFrame); + + const meshShadow = new THREE.Mesh( + geometry, + new THREE.MeshBasicMaterial({ color: 0x000000, opacity: 0.75, transparent: true }), + ); + meshShadow.position.y = (-1.1 * image.height) / 2; + meshShadow.position.z = (-1.1 * image.height) / 2; + meshShadow.rotation.x = -Math.PI / 2; + meshShadow.scale.x = (1.1 * image.width) / 100; + meshShadow.scale.y = (1.1 * image.height) / 100; + zscene.add(meshShadow); + + const floorHeight = (-1.117 * image.height) / 2; + meshCanvas1.position.y = meshCanvas2.position.y = floorHeight; + } + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); + renderer.setAnimationLoop(animate); + renderer.autoClear = false; + + renderer.domElement.style.position = 'relative'; + container.appendChild(renderer.domElement); + + document.addEventListener('mousemove', onDocumentMouseMove); +} + +function mipmap(size, color) { + const imageCanvas = document.createElement('canvas'); + const context = imageCanvas.getContext('2d'); + + imageCanvas.width = imageCanvas.height = size; + + context.fillStyle = '#444'; + context.fillRect(0, 0, size, size); + + context.fillStyle = color; + context.fillRect(0, 0, size / 2, size / 2); + context.fillRect(size / 2, size / 2, size / 2, size / 2); + return imageCanvas; +} + +function onDocumentMouseMove(event) { + mouseX = event.clientX - windowHalfX; + mouseY = event.clientY - windowHalfY; +} + +function animate() { + camera.position.x += (mouseX - camera.position.x) * 0.05; + camera.position.y += (-(mouseY - 200) - camera.position.y) * 0.05; + + camera.lookAt(scene1.position); + + renderer.clear(); + renderer.setScissorTest(true); + + renderer.setScissor(0, 0, SCREEN_WIDTH / 2 - 2, SCREEN_HEIGHT); + renderer.render(scene1, camera); + + renderer.setScissor(SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 2 - 2, SCREEN_HEIGHT); + renderer.render(scene2, camera); + + renderer.setScissorTest(false); +} diff --git a/examples-testing/examples/webgpu_materials_toon.ts b/examples-testing/examples/webgpu_materials_toon.ts new file mode 100644 index 000000000..78cb62ed6 --- /dev/null +++ b/examples-testing/examples/webgpu_materials_toon.ts @@ -0,0 +1,149 @@ +import * as THREE from 'three/webgpu'; +import { toonOutlinePass } from 'three/tsl'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { FontLoader } from 'three/addons/loaders/FontLoader.js'; +import { TextGeometry } from 'three/addons/geometries/TextGeometry.js'; + +let container; + +let camera, scene, renderer, renderPipeline; +let particleLight; + +const loader = new FontLoader(); +loader.load('fonts/gentilis_regular.typeface.json', function (font) { + init(font); +}); + +function init(font) { + container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 2500); + camera.position.set(0.0, 400, 400 * 3.5); + + // + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x444488); + + // + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(render); + renderer.inspector = new Inspector(); + container.appendChild(renderer.domElement); + + // + + renderPipeline = new THREE.RenderPipeline(renderer); + + renderPipeline.outputNode = toonOutlinePass(scene, camera); + + // Materials + + const cubeWidth = 400; + const numberOfSpheresPerSide = 5; + const sphereRadius = (cubeWidth / numberOfSpheresPerSide) * 0.8 * 0.5; + const stepSize = 1.0 / numberOfSpheresPerSide; + + const geometry = new THREE.SphereGeometry(sphereRadius, 32, 16); + + for (let alpha = 0, alphaIndex = 0; alpha <= 1.0; alpha += stepSize, alphaIndex++) { + const colors = new Uint8Array(alphaIndex + 2); + + for (let c = 0; c <= colors.length; c++) { + colors[c] = (c / colors.length) * 256; + } + + const gradientMap = new THREE.DataTexture(colors, colors.length, 1, THREE.RedFormat); + gradientMap.needsUpdate = true; + + for (let beta = 0; beta <= 1.0; beta += stepSize) { + for (let gamma = 0; gamma <= 1.0; gamma += stepSize) { + // basic monochromatic energy preservation + const diffuseColor = new THREE.Color() + .setHSL(alpha, 0.5, gamma * 0.5 + 0.1) + .multiplyScalar(1 - beta * 0.2); + + const material = new THREE.MeshToonNodeMaterial({ + color: diffuseColor, + gradientMap: gradientMap, + }); + + const mesh = new THREE.Mesh(geometry, material); + + mesh.position.x = alpha * 400 - 200; + mesh.position.y = beta * 400 - 200; + mesh.position.z = gamma * 400 - 200; + + scene.add(mesh); + } + } + } + + function addLabel(name, location) { + const textGeo = new TextGeometry(name, { + font: font, + + size: 20, + depth: 1, + curveSegments: 1, + }); + + const textMaterial = new THREE.MeshBasicNodeMaterial(); + const textMesh = new THREE.Mesh(textGeo, textMaterial); + textMesh.position.copy(location); + scene.add(textMesh); + } + + addLabel('-gradientMap', new THREE.Vector3(-350, 0, 0)); + addLabel('+gradientMap', new THREE.Vector3(350, 0, 0)); + + addLabel('-diffuse', new THREE.Vector3(0, 0, -300)); + addLabel('+diffuse', new THREE.Vector3(0, 0, 300)); + + particleLight = new THREE.Mesh( + new THREE.SphereGeometry(4, 8, 8), + new THREE.MeshBasicNodeMaterial({ color: 0xffffff }), + ); + scene.add(particleLight); + + // Lights + + scene.add(new THREE.AmbientLight(0xc1c1c1, 3)); + + const pointLight = new THREE.PointLight(0xffffff, 2, 800, 0); + particleLight.add(pointLight); + + // + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 200; + controls.maxDistance = 2000; + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function render() { + const timer = Date.now() * 0.00025; + + particleLight.position.x = Math.sin(timer * 7) * 300; + particleLight.position.y = Math.cos(timer * 5) * 400; + particleLight.position.z = Math.cos(timer * 3) * 300; + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_materials_transmission.ts b/examples-testing/examples/webgpu_materials_transmission.ts new file mode 100644 index 000000000..310d979d5 --- /dev/null +++ b/examples-testing/examples/webgpu_materials_transmission.ts @@ -0,0 +1,173 @@ +import * as THREE from 'three/webgpu'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; + +const params = { + color: 0xffffff, + transmission: 1, + opacity: 1, + metalness: 0, + roughness: 0, + ior: 1.5, + thickness: 0.01, + specularIntensity: 1, + specularColor: 0xffffff, + envMapIntensity: 1, + lightIntensity: 1, + exposure: 1, +}; + +let camera, scene, renderer; + +let mesh; + +const hdrEquirect = new UltraHDRLoader() + .setPath('textures/equirectangular/') + .load('royal_esplanade_2k.hdr.jpg', function () { + hdrEquirect.mapping = THREE.EquirectangularReflectionMapping; + + init(); + }); + +function init() { + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = params.exposure; + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 2000); + camera.position.set(0, 0, 120); + + // + + scene.background = hdrEquirect; + + // + + const geometry = new THREE.SphereGeometry(20, 64, 32); + + const texture = new THREE.CanvasTexture(generateTexture()); + texture.magFilter = THREE.NearestFilter; + texture.wrapT = THREE.RepeatWrapping; + texture.wrapS = THREE.RepeatWrapping; + texture.repeat.set(1, 3.5); + + const material = new THREE.MeshPhysicalMaterial({ + color: params.color, + metalness: params.metalness, + roughness: params.roughness, + ior: params.ior, + alphaMap: texture, + envMap: hdrEquirect, + envMapIntensity: params.envMapIntensity, + transmission: params.transmission, // use material.transmission for glass materials + specularIntensity: params.specularIntensity, + specularColor: params.specularColor, + opacity: params.opacity, + side: THREE.DoubleSide, + transparent: true, + }); + + mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + // + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 10; + controls.maxDistance = 150; + + window.addEventListener('resize', onWindowResize); + + // + + const gui = renderer.inspector.createParameters('Settings'); + + gui.addColor(params, 'color').onChange(function () { + material.color.set(params.color); + }); + + gui.add(params, 'transmission', 0, 1, 0.01).onChange(function () { + material.transmission = params.transmission; + }); + + gui.add(params, 'opacity', 0, 1, 0.01).onChange(function () { + material.opacity = params.opacity; + }); + + gui.add(params, 'metalness', 0, 1, 0.01).onChange(function () { + material.metalness = params.metalness; + }); + + gui.add(params, 'roughness', 0, 1, 0.01).onChange(function () { + material.roughness = params.roughness; + }); + + gui.add(params, 'ior', 1, 2, 0.01).onChange(function () { + material.ior = params.ior; + }); + + gui.add(params, 'thickness', 0, 5, 0.01).onChange(function () { + material.thickness = params.thickness; + }); + + gui.add(params, 'specularIntensity', 0, 1, 0.01) + .name('specular intensity') + .onChange(function () { + material.specularIntensity = params.specularIntensity; + }); + + gui.addColor(params, 'specularColor') + .name('specular color') + .onChange(function () { + material.specularColor.set(params.specularColor); + }); + + gui.add(params, 'envMapIntensity', 0, 1, 0.01) + .name('envMap intensity') + .onChange(function () { + material.envMapIntensity = params.envMapIntensity; + }); + + gui.add(params, 'exposure', 0, 1, 0.01).onChange(function () { + renderer.toneMappingExposure = params.exposure; + }); +} + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); +} + +// + +function generateTexture() { + const canvas = document.createElement('canvas'); + canvas.width = 2; + canvas.height = 2; + + const context = canvas.getContext('2d'); + context.fillStyle = 'white'; + context.fillRect(0, 1, 2, 1); + + return canvas; +} + +function animate() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_materials_video.ts b/examples-testing/examples/webgpu_materials_video.ts new file mode 100644 index 000000000..bc837653f --- /dev/null +++ b/examples-testing/examples/webgpu_materials_video.ts @@ -0,0 +1,185 @@ +import * as THREE from 'three/webgpu'; + +let container; + +let camera, scene, renderer; + +let video, texture, material, mesh; + +let mouseX = 0; +let mouseY = 0; + +let windowHalfX = window.innerWidth / 2; +let windowHalfY = window.innerHeight / 2; + +let cube_count; + +const meshes = [], + materials = [], + xgrid = 20, + ygrid = 10; + +const startButton = document.getElementById('startButton'); +startButton.addEventListener('click', function () { + init(); +}); + +function init() { + const overlay = document.getElementById('overlay'); + overlay.remove(); + + container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 10000); + camera.position.z = 500; + + scene = new THREE.Scene(); + + const light = new THREE.DirectionalLight(0xffffff, 7); + light.position.set(0.5, 1, 1).normalize(); + scene.add(light); + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(render); + container.appendChild(renderer.domElement); + + video = document.getElementById('video'); + video.play(); + video.addEventListener('play', function () { + this.currentTime = 3; + }); + + texture = new THREE.VideoTexture(video); + texture.colorSpace = THREE.SRGBColorSpace; + + // + + let i, j, ox, oy, geometry; + + const ux = 1 / xgrid; + const uy = 1 / ygrid; + + const xsize = 480 / xgrid; + const ysize = 204 / ygrid; + + const parameters = { color: 0xffffff, map: texture }; + + cube_count = 0; + + for (i = 0; i < xgrid; i++) { + for (j = 0; j < ygrid; j++) { + ox = i; + oy = j; + + geometry = new THREE.BoxGeometry(xsize, ysize, xsize); + + change_uvs(geometry, ux, uy, ox, oy); + + materials[cube_count] = new THREE.MeshPhongMaterial(parameters); + + material = materials[cube_count]; + + material.hue = i / xgrid; + material.saturation = 1 - j / ygrid; + + material.color.setHSL(material.hue, material.saturation, 0.5); + + mesh = new THREE.Mesh(geometry, material); + + mesh.position.x = (i - xgrid / 2) * xsize; + mesh.position.y = (j - ygrid / 2) * ysize; + mesh.position.z = 0; + + mesh.scale.x = mesh.scale.y = mesh.scale.z = 1; + + scene.add(mesh); + + mesh.dx = 0.001 * (0.5 - Math.random()); + mesh.dy = 0.001 * (0.5 - Math.random()); + + meshes[cube_count] = mesh; + + cube_count += 1; + } + } + + document.addEventListener('mousemove', onDocumentMouseMove); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + windowHalfX = window.innerWidth / 2; + windowHalfY = window.innerHeight / 2; + + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function change_uvs(geometry, unitx, unity, offsetx, offsety) { + const uvs = geometry.attributes.uv.array; + + for (let i = 0; i < uvs.length; i += 2) { + uvs[i] = (uvs[i] + offsetx) * unitx; + uvs[i + 1] = (uvs[i + 1] + offsety) * unity; + } +} + +function onDocumentMouseMove(event) { + mouseX = event.clientX - windowHalfX; + mouseY = (event.clientY - windowHalfY) * 0.3; +} + +// + +let h, + counter = 1; + +function render() { + const time = Date.now() * 0.00005; + + camera.position.x += (mouseX - camera.position.x) * 0.05; + camera.position.y += (-mouseY - camera.position.y) * 0.05; + + camera.lookAt(scene.position); + + for (let i = 0; i < cube_count; i++) { + material = materials[i]; + + h = ((360 * (material.hue + time)) % 360) / 360; + material.color.setHSL(h, material.saturation, 0.5); + } + + if (counter % 1000 > 200) { + for (let i = 0; i < cube_count; i++) { + mesh = meshes[i]; + + mesh.rotation.x += 10 * mesh.dx; + mesh.rotation.y += 10 * mesh.dy; + + mesh.position.x -= 150 * mesh.dx; + mesh.position.y += 150 * mesh.dy; + mesh.position.z += 300 * mesh.dx; + } + } + + if (counter % 1000 === 0) { + for (let i = 0; i < cube_count; i++) { + mesh = meshes[i]; + + mesh.dx *= -1; + mesh.dy *= -1; + } + } + + counter++; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_materialx_noise.ts b/examples-testing/examples/webgpu_materialx_noise.ts new file mode 100644 index 000000000..229c26a8e --- /dev/null +++ b/examples-testing/examples/webgpu_materialx_noise.ts @@ -0,0 +1,146 @@ +import * as THREE from 'three/webgpu'; +import { + normalWorld, + time, + mx_noise_vec3, + mx_worley_noise_vec3, + mx_cell_noise_float, + mx_fractal_noise_vec3, +} from 'three/tsl'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { HDRCubeTextureLoader } from 'three/addons/loaders/HDRCubeTextureLoader.js'; + +let container; + +let camera, scene, renderer; + +let particleLight; +let group; + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.z = 100; + + scene = new THREE.Scene(); + + group = new THREE.Group(); + scene.add(group); + + new HDRCubeTextureLoader() + .setPath('textures/cube/pisaHDR/') + .load(['px.hdr', 'nx.hdr', 'py.hdr', 'ny.hdr', 'pz.hdr', 'nz.hdr'], function (hdrTexture) { + const geometry = new THREE.SphereGeometry(8, 64, 32); + + const customUV = normalWorld.mul(10).add(time); + + // left top + + let material = new THREE.MeshPhysicalNodeMaterial(); + material.colorNode = mx_noise_vec3(customUV); + + let mesh = new THREE.Mesh(geometry, material); + mesh.position.x = -10; + mesh.position.y = 10; + group.add(mesh); + + // right top + + material = new THREE.MeshPhysicalNodeMaterial(); + material.colorNode = mx_cell_noise_float(customUV); + + mesh = new THREE.Mesh(geometry, material); + mesh.position.x = 10; + mesh.position.y = 10; + group.add(mesh); + + // left bottom + + material = new THREE.MeshPhysicalNodeMaterial(); + material.colorNode = mx_worley_noise_vec3(customUV); + + mesh = new THREE.Mesh(geometry, material); + mesh.position.x = -10; + mesh.position.y = -10; + group.add(mesh); + + // right bottom + + material = new THREE.MeshPhysicalNodeMaterial(); + material.colorNode = mx_fractal_noise_vec3(customUV.mul(0.2)); + + mesh = new THREE.Mesh(geometry, material); + mesh.position.x = 10; + mesh.position.y = -10; + group.add(mesh); + + // + + scene.background = hdrTexture; + scene.environment = hdrTexture; + }); + + // LIGHTS + + particleLight = new THREE.Mesh( + new THREE.SphereGeometry(0.4, 8, 8), + new THREE.MeshBasicMaterial({ color: 0xffffff }), + ); + scene.add(particleLight); + + particleLight.add(new THREE.PointLight(0xffffff, 1000)); + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setAnimationLoop(render); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.inspector = new Inspector(); + container.appendChild(renderer.domElement); + + // + + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = 1.25; + + // EVENTS + + new OrbitControls(camera, renderer.domElement); + + window.addEventListener('resize', onWindowResize); +} + +// + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); +} + +// + +function render() { + const timer = Date.now() * 0.00025; + + particleLight.position.x = Math.sin(timer * 7) * 30; + particleLight.position.y = Math.cos(timer * 5) * 40; + particleLight.position.z = Math.cos(timer * 3) * 30; + + for (let i = 0; i < group.children.length; i++) { + const child = group.children[i]; + child.rotation.y += 0.005; + } + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_mesh_batch.ts b/examples-testing/examples/webgpu_mesh_batch.ts new file mode 100644 index 000000000..c20dc4b57 --- /dev/null +++ b/examples-testing/examples/webgpu_mesh_batch.ts @@ -0,0 +1,265 @@ +import * as THREE from 'three/webgpu'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { radixSort } from 'three/addons/utils/SortUtils.js'; + +import { normalView, directionToColor, diffuseColor } from 'three/tsl'; + +let camera, scene, renderer; +let controls; +let gui; +let geometries, mesh, material; +const ids = []; + +const matrix = new THREE.Matrix4(); + +// + +const position = new THREE.Vector3(); +const rotation = new THREE.Euler(); +const quaternion = new THREE.Quaternion(); +const scale = new THREE.Vector3(); + +// + +const MAX_GEOMETRY_COUNT = 20000; + +const api = { + webgpu: true, + count: 512, + dynamic: 16, + + sortObjects: true, + perObjectFrustumCulled: true, + opacity: 1, + useCustomSort: true, + randomizeGeometry: () => { + for (let i = 0; i < api.count; i++) { + mesh.setGeometryIdAt(i, Math.floor(Math.random() * geometries.length)); + } + }, +}; + +init(); + +// + +function randomizeMatrix(matrix) { + position.x = Math.random() * 40 - 20; + position.y = Math.random() * 40 - 20; + position.z = Math.random() * 40 - 20; + + rotation.x = Math.random() * 2 * Math.PI; + rotation.y = Math.random() * 2 * Math.PI; + rotation.z = Math.random() * 2 * Math.PI; + + quaternion.setFromEuler(rotation); + + scale.x = scale.y = scale.z = 0.5 + Math.random() * 0.5; + + return matrix.compose(position, quaternion, scale); +} + +function randomizeRotationSpeed(rotation) { + rotation.x = Math.random() * 0.01; + rotation.y = Math.random() * 0.01; + rotation.z = Math.random() * 0.01; + return rotation; +} + +function initGeometries() { + geometries = [ + new THREE.ConeGeometry(1.0, 2.0), + new THREE.BoxGeometry(2.0, 2.0, 2.0), + new THREE.SphereGeometry(1.0, 16, 8), + ]; +} + +function createMaterial() { + if (!material) { + material = new THREE.MeshBasicNodeMaterial(); + material.outputNode = diffuseColor.mul(directionToColor(normalView).y.add(0.5)); + } + + return material; +} + +function cleanup() { + if (mesh) { + mesh.parent.remove(mesh); + + if (mesh.dispose) { + mesh.dispose(); + } + } +} + +function initMesh() { + cleanup(); + initBatchedMesh(); +} + +function initBatchedMesh() { + const geometryCount = api.count; + const vertexCount = geometries.length * 512; + const indexCount = geometries.length * 1024; + + const euler = new THREE.Euler(); + const matrix = new THREE.Matrix4(); + mesh = new THREE.BatchedMesh(geometryCount, vertexCount, indexCount, createMaterial()); + mesh.userData.rotationSpeeds = []; + + // disable full-object frustum culling since all of the objects can be dynamic. + mesh.frustumCulled = false; + + ids.length = 0; + + const geometryIds = [ + mesh.addGeometry(geometries[0]), + mesh.addGeometry(geometries[1]), + mesh.addGeometry(geometries[2]), + ]; + for (let i = 0; i < api.count; i++) { + const id = mesh.addInstance(geometryIds[i % geometryIds.length]); + mesh.setMatrixAt(id, randomizeMatrix(matrix)); + mesh.setColorAt(id, new THREE.Color(Math.random() * 0xffffff)); + + const rotationMatrix = new THREE.Matrix4(); + rotationMatrix.makeRotationFromEuler(randomizeRotationSpeed(euler)); + mesh.userData.rotationSpeeds.push(rotationMatrix); + + ids.push(id); + } + + scene.add(mesh); +} + +function init(forceWebGL = false) { + if (renderer) { + renderer.dispose(); + controls.dispose(); + document.body.removeChild(renderer.domElement); + } + + // camera + + const aspect = window.innerWidth / window.innerHeight; + + camera = new THREE.PerspectiveCamera(70, aspect, 1, 100); + camera.position.z = 30; + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: true, forceWebGL }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.inspector = new Inspector(); + renderer.setAnimationLoop(animate); + + // scene + + scene = new THREE.Scene(); + scene.background = forceWebGL ? new THREE.Color(0xffc1c1) : new THREE.Color(0xc1c1ff); + + document.body.appendChild(renderer.domElement); + + initGeometries(); + initMesh(); + + // controls + + controls = new OrbitControls(camera, renderer.domElement); + controls.autoRotate = true; + controls.autoRotateSpeed = 1.0; + + // gui + + gui = renderer.inspector.createParameters('Settings'); + gui.add(api, 'webgpu').onChange(() => { + init(!api.webgpu); + }); + gui.add(api, 'count', 1, MAX_GEOMETRY_COUNT, 1).onChange(initMesh); + gui.add(api, 'dynamic', 0, MAX_GEOMETRY_COUNT, 1); + + gui.add(api, 'opacity', 0, 1).onChange(v => { + if (v < 1) { + material.transparent = true; + material.depthWrite = false; + } else { + material.transparent = false; + material.depthWrite = true; + } + + material.opacity = v; + material.needsUpdate = true; + }); + gui.add(api, 'sortObjects'); + gui.add(api, 'perObjectFrustumCulled'); + gui.add(api, 'useCustomSort'); + gui.add(api, 'randomizeGeometry').name('randomize geometry'); + + // listeners + + window.addEventListener('resize', onWindowResize); + + function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); + } + + async function animate() { + animateMeshes(); + + controls.update(); + + if (mesh.isBatchedMesh) { + mesh.sortObjects = api.sortObjects; + mesh.perObjectFrustumCulled = api.perObjectFrustumCulled; + mesh.setCustomSort(api.useCustomSort ? sortFunction : null); + } + + renderer.render(scene, camera); + } + + function animateMeshes() { + const loopNum = Math.min(api.count, api.dynamic); + + for (let i = 0; i < loopNum; i++) { + const rotationMatrix = mesh.userData.rotationSpeeds[i]; + const id = ids[i]; + + mesh.getMatrixAt(id, matrix); + matrix.multiply(rotationMatrix); + mesh.setMatrixAt(id, matrix); + } + } +} + +// + +function sortFunction(list, camera) { + // initialize options + this._options = this._options || { + get: el => el.z, + aux: new Array(this.maxInstanceCount), + }; + + const options = this._options; + options.reversed = this.material.transparent; + + // convert depth to unsigned 32 bit range + const factor = (2 ** 32 - 1) / camera.far; // UINT32_MAX / max_depth + for (let i = 0, l = list.length; i < l; i++) { + list[i].z *= factor; + } + + // perform a fast-sort using the hybrid radix sort function + radixSort(list, options); +} diff --git a/examples-testing/examples/webgpu_mirror.ts b/examples-testing/examples/webgpu_mirror.ts new file mode 100644 index 000000000..56b226193 --- /dev/null +++ b/examples-testing/examples/webgpu_mirror.ts @@ -0,0 +1,194 @@ +import * as THREE from 'three/webgpu'; +import { reflector, uv, texture, color } from 'three/tsl'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer; + +let cameraControls; + +let sphereGroup, smallSphere; + +init(); + +function init() { + // scene + scene = new THREE.Scene(); + + // camera + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 500); + camera.position.set(0, 75, 160); + + // + + let geometry, material; + + // + + sphereGroup = new THREE.Object3D(); + scene.add(sphereGroup); + + geometry = new THREE.CylinderGeometry(0.1, 15 * Math.cos((Math.PI / 180) * 30), 0.1, 24, 1); + material = new THREE.MeshPhongMaterial({ color: 0xffffff, emissive: 0x8d8d8d }); + const sphereCap = new THREE.Mesh(geometry, material); + sphereCap.position.y = -15 * Math.sin((Math.PI / 180) * 30) - 0.05; + sphereCap.rotateX(-Math.PI); + + geometry = new THREE.SphereGeometry(15, 24, 24, Math.PI / 2, Math.PI * 2, 0, (Math.PI / 180) * 120); + const halfSphere = new THREE.Mesh(geometry, material); + halfSphere.add(sphereCap); + halfSphere.rotateX((-Math.PI / 180) * 135); + halfSphere.rotateZ((-Math.PI / 180) * 20); + halfSphere.position.y = 7.5 + 15 * Math.sin((Math.PI / 180) * 30); + + sphereGroup.add(halfSphere); + + geometry = new THREE.IcosahedronGeometry(5, 0); + material = new THREE.MeshPhongMaterial({ color: 0xffffff, emissive: 0x7b7b7b, flatShading: true }); + smallSphere = new THREE.Mesh(geometry, material); + scene.add(smallSphere); + + // textures + + const textureLoader = new THREE.TextureLoader(); + + const floorNormal = textureLoader.load('textures/floors/FloorsCheckerboard_S_Normal.jpg'); + floorNormal.wrapS = THREE.RepeatWrapping; + floorNormal.wrapT = THREE.RepeatWrapping; + + const decalDiffuse = textureLoader.load('textures/decal/decal-diffuse.png'); + decalDiffuse.colorSpace = THREE.SRGBColorSpace; + + const decalNormal = textureLoader.load('textures/decal/decal-normal.jpg'); + + // reflectors / mirrors + + const groundReflector = reflector().toInspector('Ground Reflector'); + const verticalReflector = reflector().toInspector('Vertical Reflector'); + + const groundNormalScale = -0.08; + const verticalNormalScale = 0.1; + + const groundUVOffset = texture(decalNormal).xy.mul(2).sub(1).mul(groundNormalScale); + const verticalUVOffset = texture(floorNormal, uv().mul(5)).xy.mul(2).sub(1).mul(verticalNormalScale); + + groundReflector.uvNode = groundReflector.uvNode.add(groundUVOffset); + verticalReflector.uvNode = verticalReflector.uvNode.add(verticalUVOffset); + + const groundNode = texture(decalDiffuse).a.mix(color(0xffffff), groundReflector); + const verticalNode = color(0x0000ff).mul(0.1).add(verticalReflector); + + // walls + + const planeGeo = new THREE.PlaneGeometry(100.1, 100.1); + + // + + const planeBottom = new THREE.Mesh( + planeGeo, + new THREE.MeshPhongNodeMaterial({ + colorNode: groundNode, + }), + ); + planeBottom.rotateX(-Math.PI / 2); + planeBottom.add(groundReflector.target); + scene.add(planeBottom); + + const planeBack = new THREE.Mesh( + planeGeo, + new THREE.MeshPhongNodeMaterial({ + colorNode: verticalNode, + }), + ); + planeBack.position.z = -50; + planeBack.position.y = 50; + planeBack.add(verticalReflector.target); + scene.add(planeBack); + + // + + const planeTop = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xffffff })); + planeTop.position.y = 100; + planeTop.rotateX(Math.PI / 2); + scene.add(planeTop); + + const planeFront = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0x7f7fff })); + planeFront.position.z = 50; + planeFront.position.y = 50; + planeFront.rotateY(Math.PI); + scene.add(planeFront); + + const planeRight = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0x00ff00 })); + planeRight.position.x = 50; + planeRight.position.y = 50; + planeRight.rotateY(-Math.PI / 2); + scene.add(planeRight); + + const planeLeft = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xff0000 })); + planeLeft.position.x = -50; + planeLeft.position.y = 50; + planeLeft.rotateY(Math.PI / 2); + scene.add(planeLeft); + + // lights + + const mainLight = new THREE.PointLight(0xe7e7e7, 2.5, 250, 0); + mainLight.position.y = 60; + scene.add(mainLight); + + const greenLight = new THREE.PointLight(0x00ff00, 0.5, 1000, 0); + greenLight.position.set(550, 50, 0); + scene.add(greenLight); + + const redLight = new THREE.PointLight(0xff0000, 0.5, 1000, 0); + redLight.position.set(-550, 50, 0); + scene.add(redLight); + + const blueLight = new THREE.PointLight(0xbbbbfe, 0.5, 1000, 0); + blueLight.position.set(0, 50, 550); + scene.add(blueLight); + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // controls + + cameraControls = new OrbitControls(camera, renderer.domElement); + cameraControls.target.set(0, 40, 0); + cameraControls.maxDistance = 400; + cameraControls.minDistance = 10; + cameraControls.update(); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + const timer = Date.now() * 0.01; + + sphereGroup.rotation.y -= 0.002; + + smallSphere.position.set( + Math.cos(timer * 0.1) * 30, + Math.abs(Math.cos(timer * 0.2)) * 20 + 5, + Math.sin(timer * 0.1) * 30, + ); + smallSphere.rotation.y = Math.PI / 2 - timer * 0.1; + smallSphere.rotation.z = timer * 0.8; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_modifier_curve.ts b/examples-testing/examples/webgpu_modifier_curve.ts new file mode 100644 index 000000000..c465ddc57 --- /dev/null +++ b/examples-testing/examples/webgpu_modifier_curve.ts @@ -0,0 +1,154 @@ +import * as THREE from 'three/webgpu'; +import { TransformControls } from 'three/addons/controls/TransformControls.js'; + +import { Flow } from 'three/addons/modifiers/CurveModifierGPU.js'; +import { FontLoader } from 'three/addons/loaders/FontLoader.js'; +import { TextGeometry } from 'three/addons/geometries/TextGeometry.js'; + +const ACTION_SELECT = 1, + ACTION_NONE = 0; +const curveHandles = []; +const mouse = new THREE.Vector2(); + +let scene, + camera, + renderer, + rayCaster, + control, + flow, + action = ACTION_NONE; + +init(); + +function init() { + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(2, 2, 4); + camera.lookAt(scene.position); + + const initialPoints = [ + { x: 1, y: 0, z: -1 }, + { x: 1, y: 0, z: 1 }, + { x: -1, y: 0, z: 1 }, + { x: -1, y: 0, z: -1 }, + ]; + + const boxGeometry = new THREE.BoxGeometry(0.1, 0.1, 0.1); + const boxMaterial = new THREE.MeshBasicNodeMaterial(); + + for (const handlePos of initialPoints) { + const handle = new THREE.Mesh(boxGeometry, boxMaterial); + handle.position.copy(handlePos); + curveHandles.push(handle); + scene.add(handle); + } + + const curve = new THREE.CatmullRomCurve3(curveHandles.map(handle => handle.position)); + curve.curveType = 'centripetal'; + curve.closed = true; + + const points = curve.getPoints(50); + const line = new THREE.Line( + new THREE.BufferGeometry().setFromPoints(points), + new THREE.LineBasicMaterial({ color: 0x00ff00 }), + ); + + scene.add(line); + + // + + const light = new THREE.DirectionalLight(0xffaa33, 3); + light.position.set(-10, 10, 10); + scene.add(light); + + const light2 = new THREE.AmbientLight(0x003973, 3); + scene.add(light2); + + // + + const loader = new FontLoader(); + loader.load('fonts/helvetiker_regular.typeface.json', function (font) { + const geometry = new TextGeometry('Hello three.js!', { + font: font, + size: 0.2, + depth: 0.05, + curveSegments: 12, + bevelEnabled: true, + bevelThickness: 0.02, + bevelSize: 0.01, + bevelOffset: 0, + bevelSegments: 5, + }); + + geometry.rotateX(Math.PI); + + const material = new THREE.MeshStandardNodeMaterial({ + color: 0x99ffff, + }); + + const objectToCurve = new THREE.Mesh(geometry, material); + + flow = new Flow(objectToCurve); + flow.updateCurve(0, curve); + scene.add(flow.object3D); + }); + + // + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + renderer.domElement.addEventListener('pointerdown', onPointerDown); + + rayCaster = new THREE.Raycaster(); + control = new TransformControls(camera, renderer.domElement); + control.addEventListener('dragging-changed', function (event) { + if (!event.value) { + const points = curve.getPoints(50); + line.geometry.setFromPoints(points); + flow.updateCurve(0, curve); + } + }); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function onPointerDown(event) { + action = ACTION_SELECT; + mouse.x = (event.clientX / window.innerWidth) * 2 - 1; + mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; +} + +function animate() { + if (action === ACTION_SELECT) { + rayCaster.setFromCamera(mouse, camera); + action = ACTION_NONE; + const intersects = rayCaster.intersectObjects(curveHandles, false); + if (intersects.length) { + const target = intersects[0].object; + control.attach(target); + scene.add(control.getHelper()); + } + } + + if (flow) { + flow.moveAlongCurve(0.001); + } + + render(); +} + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_morphtargets.ts b/examples-testing/examples/webgpu_morphtargets.ts new file mode 100644 index 000000000..cde3ae83b --- /dev/null +++ b/examples-testing/examples/webgpu_morphtargets.ts @@ -0,0 +1,119 @@ +import * as THREE from 'three/webgpu'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let container, camera, scene, renderer, mesh; + +init(); + +function init() { + container = document.getElementById('container'); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x8fbcd4); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 20); + camera.position.z = 10; + scene.add(camera); + + scene.add(new THREE.AmbientLight(0x8fbcd4, 1.5)); + + const pointLight = new THREE.PointLight(0xffffff, 200); + camera.add(pointLight); + + const geometry = createGeometry(); + + const material = new THREE.MeshPhongMaterial({ + color: 0xff0000, + flatShading: true, + }); + + mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.inspector = new Inspector(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(function () { + renderer.render(scene, camera); + }); + container.appendChild(renderer.domElement); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.enableZoom = false; + + window.addEventListener('resize', onWindowResize); + + initGUI(); +} + +function createGeometry() { + const geometry = new THREE.BoxGeometry(2, 2, 2, 32, 32, 32); + + // create an empty array to hold targets for the attribute we want to morph + // morphing positions and normals is supported + geometry.morphAttributes.position = []; + + // the original positions of the cube's vertices + const positionAttribute = geometry.attributes.position; + + // for the first morph target we'll move the cube's vertices onto the surface of a sphere + const spherePositions = []; + + // for the second morph target, we'll twist the cubes vertices + const twistPositions = []; + const direction = new THREE.Vector3(1, 0, 0); + const vertex = new THREE.Vector3(); + + for (let i = 0; i < positionAttribute.count; i++) { + const x = positionAttribute.getX(i); + const y = positionAttribute.getY(i); + const z = positionAttribute.getZ(i); + + spherePositions.push( + x * Math.sqrt(1 - (y * y) / 2 - (z * z) / 2 + (y * y * z * z) / 3), + y * Math.sqrt(1 - (z * z) / 2 - (x * x) / 2 + (z * z * x * x) / 3), + z * Math.sqrt(1 - (x * x) / 2 - (y * y) / 2 + (x * x * y * y) / 3), + ); + + // stretch along the x-axis so we can see the twist better + vertex.set(x * 2, y, z); + + vertex.applyAxisAngle(direction, (Math.PI * x) / 2).toArray(twistPositions, twistPositions.length); + } + + // add the spherical positions as the first morph target + geometry.morphAttributes.position[0] = new THREE.Float32BufferAttribute(spherePositions, 3); + + // add the twisted positions as the second morph target + geometry.morphAttributes.position[1] = new THREE.Float32BufferAttribute(twistPositions, 3); + + return geometry; +} + +function initGUI() { + // Set up dat.GUI to control targets + const params = { + Spherify: 0, + Twist: 0, + }; + + const gui = renderer.inspector.createParameters('Morph Targets'); + + gui.add(params, 'Spherify', 0, 1, 0.01).onChange(function (value) { + mesh.morphTargetInfluences[0] = value; + }); + gui.add(params, 'Twist', 0, 1, 0.01).onChange(function (value) { + mesh.morphTargetInfluences[1] = value; + }); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} diff --git a/examples-testing/examples/webgpu_morphtargets_face.ts b/examples-testing/examples/webgpu_morphtargets_face.ts new file mode 100644 index 000000000..91120dfb5 --- /dev/null +++ b/examples-testing/examples/webgpu_morphtargets_face.ts @@ -0,0 +1,98 @@ +import * as THREE from 'three/webgpu'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js'; +import { MeshoptDecoder } from 'three/addons/libs/meshopt_decoder.module.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +init(); + +async function init() { + let mixer; + + const timer = new THREE.Timer(); + timer.connect(document); + + const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 20); + camera.position.set(-1.8, 0.8, 3); + + const scene = new THREE.Scene(); + + const renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + await renderer.init(); + + const environment = new RoomEnvironment(); + const pmremGenerator = new THREE.PMREMGenerator(renderer); + + scene.background = new THREE.Color(0x666666); + scene.environment = pmremGenerator.fromScene(environment, 0.04).texture; + + const ktx2Loader = await new KTX2Loader().setTranscoderPath('jsm/libs/basis/').detectSupport(renderer); + + new GLTFLoader() + .setKTX2Loader(ktx2Loader) + .setMeshoptDecoder(MeshoptDecoder) + .load('models/gltf/facecap.glb', gltf => { + const mesh = gltf.scene.children[0]; + + scene.add(mesh); + + mixer = new THREE.AnimationMixer(mesh); + + mixer.clipAction(gltf.animations[0]).play(); + + // GUI + + const head = mesh.getObjectByName('mesh_2'); + const influences = head.morphTargetInfluences; + + const gui = renderer.inspector.createParameters('Morph Targets'); + + for (const [key, value] of Object.entries(head.morphTargetDictionary)) { + gui.add(influences, value, 0, 1, 0.01).name(key.replace('blendShape1.', '')).listen(); + } + }); + + scene.background = new THREE.Color(0x666666); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + controls.minDistance = 2.5; + controls.maxDistance = 5; + controls.minAzimuthAngle = -Math.PI / 2; + controls.maxAzimuthAngle = Math.PI / 2; + controls.maxPolarAngle = Math.PI / 1.8; + controls.target.set(0, 0.15, -0.2); + + function animate() { + timer.update(); + + const delta = timer.getDelta(); + + if (mixer) { + mixer.update(delta); + } + + renderer.render(scene, camera); + + controls.update(); + } + + window.addEventListener('resize', () => { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + }); +} diff --git a/examples-testing/examples/webgpu_mrt.ts b/examples-testing/examples/webgpu_mrt.ts new file mode 100644 index 000000000..2b41adc9c --- /dev/null +++ b/examples-testing/examples/webgpu_mrt.ts @@ -0,0 +1,123 @@ +import * as THREE from 'three/webgpu'; +import { + output, + normalView, + pass, + step, + diffuseColor, + emissive, + directionToColor, + screenUV, + mix, + mrt, + Fn, +} from 'three/tsl'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +let camera, scene, renderer; +let renderPipeline; + +init(); + +function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + // scene + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); + camera.position.set(-1.8, 0.6, 2.7); + + scene = new THREE.Scene(); + + new UltraHDRLoader().setPath('textures/equirectangular/').load('royal_esplanade_2k.hdr.jpg', function (texture) { + texture.mapping = THREE.EquirectangularReflectionMapping; + + scene.background = texture; + scene.environment = texture; + + // model + + const loader = new GLTFLoader().setPath('models/gltf/DamagedHelmet/glTF/'); + loader.load('DamagedHelmet.gltf', function (gltf) { + scene.add(gltf.scene); + }); + }); + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: true, requiredLimits: { maxColorAttachments: 5 } }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(render); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.inspector = new Inspector(); + container.appendChild(renderer.domElement); + + // post processing + + const scenePass = pass(scene, camera, { minFilter: THREE.NearestFilter, magFilter: THREE.NearestFilter }); + scenePass.setMRT( + mrt({ + output: output, + normal: directionToColor(normalView), + diffuse: diffuseColor, + emissive: emissive, + }), + ); + + // optimize textures + + const normalTexture = scenePass.getTexture('normal'); + const diffuseTexture = scenePass.getTexture('diffuse'); + const emissiveTexture = scenePass.getTexture('emissive'); + + normalTexture.type = diffuseTexture.type = emissiveTexture.type = THREE.UnsignedByteType; + + // post processing - mrt + + renderPipeline = new THREE.RenderPipeline(renderer); + renderPipeline.outputColorTransform = false; + renderPipeline.outputNode = Fn(() => { + const output = scenePass.getTextureNode('output'); // output name is optional here + const normal = scenePass.getTextureNode('normal'); + const diffuse = scenePass.getTextureNode('diffuse'); + const emissive = scenePass.getTextureNode('emissive'); + + const out = mix(output.renderOutput(), output, step(0.2, screenUV.x)); + const nor = mix(out, normal, step(0.4, screenUV.x)); + const emi = mix(nor, emissive, step(0.6, screenUV.x)); + const dif = mix(emi, diffuse, step(0.8, screenUV.x)); + + return dif; + })(); + + // controls + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 2; + controls.maxDistance = 10; + controls.target.set(0, 0, -0.2); + controls.update(); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function render() { + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_mrt_mask.ts b/examples-testing/examples/webgpu_mrt_mask.ts new file mode 100644 index 000000000..2bb80b84e --- /dev/null +++ b/examples-testing/examples/webgpu_mrt_mask.ts @@ -0,0 +1,132 @@ +import * as THREE from 'three/webgpu'; +import { color, screenUV, mrt, output, pass, vec4 } from 'three/tsl'; +import { gaussianBlur } from 'three/addons/tsl/display/GaussianBlurNode.js'; + +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let camera, scene, renderer; +let renderPipeline; +let spheres, + rotate = true; +let mixer, timer; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.01, 100); + camera.position.set(1, 2, 3); + + scene = new THREE.Scene(); + scene.backgroundNode = screenUV.y.mix(color(0x66bbff), color(0x4466ff)).mul(0.05); + camera.lookAt(0, 1, 0); + + timer = new THREE.Timer(); + timer.connect(document); + + // lights + + const light = new THREE.SpotLight(0xffffff, 1); + light.power = 2000; + camera.add(light); + scene.add(camera); + + const loader = new GLTFLoader(); + loader.load('models/gltf/Michelle.glb', function (gltf) { + const object = gltf.scene; + mixer = new THREE.AnimationMixer(object); + + const material = object.children[0].children[0].material; + + // add glow effect + material.mrtNode = mrt({ mask: output.add(1) }); + + const action = mixer.clipAction(gltf.animations[0]); + action.play(); + + scene.add(object); + }); + + // spheres + + const geometry = new THREE.SphereGeometry(0.3, 32, 16); + + spheres = new THREE.Group(); + scene.add(spheres); + + function addSphere(color, mrtNode = null) { + const distance = 1; + const id = spheres.children.length; + const rotation = THREE.MathUtils.degToRad(id * 90); + + const material = new THREE.MeshStandardNodeMaterial({ color }); + material.mrtNode = mrtNode; + + const mesh = new THREE.Mesh(geometry, material); + mesh.position.set(Math.cos(rotation) * distance, 1, Math.sin(rotation) * distance); + + spheres.add(mesh); + } + + addSphere(0x0000ff, mrt({ mask: output })); + addSphere(0x00ff00); + addSphere(0xff0000); + addSphere(0x00ffff); + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.toneMapping = THREE.NeutralToneMapping; + renderer.toneMappingExposure = 0.4; + document.body.appendChild(renderer.domElement); + + // post processing + + const scenePass = pass(scene, camera); + scenePass.setMRT( + mrt({ + output: output.renderOutput(), + mask: vec4(0), // empty as default, custom materials can set this + }), + ); + + const colorPass = scenePass.getTextureNode(); + const maskPass = scenePass.getTextureNode('mask'); + + renderPipeline = new THREE.RenderPipeline(renderer); + renderPipeline.outputColorTransform = false; + renderPipeline.outputNode = colorPass.add(gaussianBlur(maskPass, 1, 20).mul(0.3)).renderOutput(); + + // controls + + const controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, 1, 0); + controls.addEventListener('start', () => (rotate = false)); + controls.addEventListener('end', () => (rotate = true)); + controls.update(); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + const delta = timer.getDelta(); + + if (mixer) mixer.update(delta); + + if (rotate) spheres.rotation.y += delta * 0.5; + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_multiple_canvas.ts b/examples-testing/examples/webgpu_multiple_canvas.ts new file mode 100644 index 000000000..db55fe165 --- /dev/null +++ b/examples-testing/examples/webgpu_multiple_canvas.ts @@ -0,0 +1,111 @@ +import * as THREE from 'three'; +import { color } from 'three/tsl'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import WebGPU from 'three/addons/capabilities/WebGPU.js'; + +// + +if (WebGPU.isAvailable() === false) { + document.body.appendChild(WebGPU.getErrorMessage()); + + throw new Error('No WebGPU support'); +} + +// + +let renderer; + +const scenes = []; + +init(); + +function init() { + const geometries = [ + new THREE.BoxGeometry(1, 1, 1), + new THREE.SphereGeometry(0.5, 12, 8), + new THREE.DodecahedronGeometry(0.5), + new THREE.CylinderGeometry(0.5, 0.5, 1, 12), + ]; + + const content = document.getElementById('content'); + + for (let i = 0; i < 40; i++) { + const scene = new THREE.Scene(); + scene.backgroundNode = color(0xeeeeee); + + // make a list item + const element = document.createElement('div'); + element.className = 'list-item'; + + const sceneCanvas = document.createElement('canvas'); + element.appendChild(sceneCanvas); + + const descriptionElement = document.createElement('div'); + descriptionElement.innerText = 'Scene ' + (i + 1); + element.appendChild(descriptionElement); + + const canvasTarget = new THREE.CanvasTarget(sceneCanvas); + canvasTarget.setPixelRatio(window.devicePixelRatio); + canvasTarget.setSize(200, 200); + + // the element that represents the area we want to render the scene + scene.userData.canvasTarget = canvasTarget; + content.appendChild(element); + + const camera = new THREE.PerspectiveCamera(50, 1, 1, 10); + camera.position.z = 2; + scene.userData.camera = camera; + + const controls = new OrbitControls(scene.userData.camera, scene.userData.canvasTarget.domElement); + controls.minDistance = 2; + controls.maxDistance = 5; + controls.enablePan = false; + controls.enableZoom = false; + scene.userData.controls = controls; + + // add one random mesh to each scene + const geometry = geometries[(geometries.length * Math.random()) | 0]; + + const material = new THREE.MeshStandardMaterial({ + color: new THREE.Color().setHSL(Math.random(), 1, 0.75, THREE.SRGBColorSpace), + roughness: 0.5, + metalness: 0, + flatShading: true, + }); + + scene.add(new THREE.Mesh(geometry, material)); + + scene.add(new THREE.HemisphereLight(0xaaaaaa, 0x444444, 3)); + + const light = new THREE.DirectionalLight(0xffffff, 1.5); + light.position.set(1, 1, 1); + scene.add(light); + + scenes.push(scene); + } + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setClearColor(0xffffff, 1); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setAnimationLoop(animate); +} + +function animate() { + scenes.forEach(function (scene) { + // so something moves + //scene.children[ 0 ].rotation.y = Date.now() * 0.001; + + // get the canvas and camera for this scene + const { canvasTarget, camera } = scene.userData; + + //camera.aspect = width / height; // not changing in this example + //camera.updateProjectionMatrix(); + + //scene.userData.controls.update(); + + renderer.setCanvasTarget(canvasTarget); + renderer.render(scene, camera); + }); +} diff --git a/examples-testing/examples/webgpu_multiple_elements.ts b/examples-testing/examples/webgpu_multiple_elements.ts new file mode 100644 index 000000000..65bd3e75d --- /dev/null +++ b/examples-testing/examples/webgpu_multiple_elements.ts @@ -0,0 +1,142 @@ +import * as THREE from 'three'; +import { color } from 'three/tsl'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let canvas, renderer; + +const scenes = []; + +init(); + +function init() { + canvas = document.getElementById('c'); + + const geometries = [ + new THREE.BoxGeometry(1, 1, 1), + new THREE.SphereGeometry(0.5, 12, 8), + new THREE.DodecahedronGeometry(0.5), + new THREE.CylinderGeometry(0.5, 0.5, 1, 12), + ]; + + const content = document.getElementById('content'); + + for (let i = 0; i < 40; i++) { + const scene = new THREE.Scene(); + scene.backgroundNode = color(0xeeeeee); + + // make a list item + const element = document.createElement('div'); + element.className = 'list-item'; + + const sceneElement = document.createElement('div'); + element.appendChild(sceneElement); + + const descriptionElement = document.createElement('div'); + descriptionElement.innerText = 'Scene ' + (i + 1); + element.appendChild(descriptionElement); + + // the element that represents the area we want to render the scene + scene.userData.element = sceneElement; + content.appendChild(element); + + const camera = new THREE.PerspectiveCamera(50, 1, 1, 10); + camera.position.z = 2; + scene.userData.camera = camera; + + const controls = new OrbitControls(scene.userData.camera, scene.userData.element); + controls.minDistance = 2; + controls.maxDistance = 5; + controls.enablePan = false; + controls.enableZoom = false; + scene.userData.controls = controls; + + // add one random mesh to each scene + const geometry = geometries[(geometries.length * Math.random()) | 0]; + + const material = new THREE.MeshStandardMaterial({ + color: new THREE.Color().setHSL(Math.random(), 1, 0.75, THREE.SRGBColorSpace), + roughness: 0.5, + metalness: 0, + flatShading: true, + }); + + scene.add(new THREE.Mesh(geometry, material)); + + scene.add(new THREE.HemisphereLight(0xaaaaaa, 0x444444, 3)); + + const light = new THREE.DirectionalLight(0xffffff, 1.5); + light.position.set(1, 1, 1); + scene.add(light); + + scenes.push(scene); + } + + renderer = new THREE.WebGPURenderer({ canvas: canvas, antialias: true }); + renderer.setClearColor(0xffffff, 1); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setAnimationLoop(animate); +} + +function updateSize() { + const width = canvas.clientWidth; + const height = canvas.clientHeight; + + if (canvas.width !== width || canvas.height !== height) { + renderer.setSize(width, height, false); + } +} + +function animate() { + updateSize(); + + canvas.style.transform = `translateY(${window.scrollY}px)`; + + renderer.setClearColor(0xffffff); + renderer.setScissorTest(false); + renderer.setViewport(0, 0, canvas.width, canvas.height); + renderer.clear(); + + //renderer.setClearColor( 0xe0e0e0 ); + renderer.setScissorTest(true); + + scenes.forEach(function (scene) { + // so something moves + scene.children[0].rotation.y = Date.now() * 0.001; + + // get the element that is a place holder for where we want to + // draw the scene + const element = scene.userData.element; + + // get its position relative to the page's viewport + const rect = element.getBoundingClientRect(); + + // check if it's offscreen. If so skip it + if ( + rect.bottom < 0 || + rect.top > renderer.domElement.clientHeight || + rect.right < 0 || + rect.left > renderer.domElement.clientWidth + ) { + return; // it's off screen + } + + // set the viewport + const width = rect.right - rect.left; + const height = rect.bottom - rect.top; + const left = rect.left; + const top = rect.top; + + renderer.setViewport(left, top, width, height); + renderer.setScissor(left, top, width, height); + + const camera = scene.userData.camera; + + //camera.aspect = width / height; // not changing in this example + //camera.updateProjectionMatrix(); + + //scene.userData.controls.update(); + + renderer.render(scene, camera); + }); +} diff --git a/examples-testing/examples/webgpu_multiple_rendertargets.ts b/examples-testing/examples/webgpu_multiple_rendertargets.ts new file mode 100644 index 000000000..2085d3c63 --- /dev/null +++ b/examples-testing/examples/webgpu_multiple_rendertargets.ts @@ -0,0 +1,97 @@ +import * as THREE from 'three/webgpu'; +import { mix, vec2, step, texture, uv, screenUV, normalWorld, output, mrt } from 'three/tsl'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let camera, scene, renderer, torus; +let renderPipeline, renderTarget; + +init(); + +function init() { + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(render); + document.body.appendChild(renderer.domElement); + + // Create a multi render target with Float buffers + + renderTarget = new THREE.RenderTarget( + window.innerWidth * window.devicePixelRatio, + window.innerHeight * window.devicePixelRatio, + { count: 2, minFilter: THREE.NearestFilter, magFilter: THREE.NearestFilter }, + ); + + // Name our G-Buffer attachments for debugging + + renderTarget.textures[0].name = 'output'; + renderTarget.textures[1].name = 'normal'; + + // Scene + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x222222); + + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 50); + camera.position.z = 4; + + const loader = new THREE.TextureLoader(); + + const diffuse = loader.load('textures/hardwood2_diffuse.jpg'); + diffuse.colorSpace = THREE.SRGBColorSpace; + diffuse.wrapS = THREE.RepeatWrapping; + diffuse.wrapT = THREE.RepeatWrapping; + + const torusMaterial = new THREE.NodeMaterial(); + torusMaterial.colorNode = texture(diffuse, uv().mul(vec2(10, 4))); + + torus = new THREE.Mesh(new THREE.TorusKnotGeometry(1, 0.3, 128, 32), torusMaterial); + scene.add(torus); + + // MRT + + renderer.setMRT( + mrt({ + output: output, + normal: normalWorld, + }), + ); + + // Post Processing + + renderPipeline = new THREE.RenderPipeline(renderer); + renderPipeline.outputNode = mix( + texture(renderTarget.textures[0]), + texture(renderTarget.textures[1]), + step(0.5, screenUV.x), + ); + + // Controls + + new OrbitControls(camera, renderer.domElement); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + const dpr = renderer.getPixelRatio(); + renderTarget.setSize(window.innerWidth * dpr, window.innerHeight * dpr); +} + +function render(time) { + torus.rotation.y = (time / 1000) * 0.4; + + // render scene into target + renderer.setRenderTarget(renderTarget); + renderer.render(scene, camera); + + // render post FX + renderer.setRenderTarget(null); + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_multiple_rendertargets_readback.ts b/examples-testing/examples/webgpu_multiple_rendertargets_readback.ts new file mode 100644 index 000000000..2232ab182 --- /dev/null +++ b/examples-testing/examples/webgpu_multiple_rendertargets_readback.ts @@ -0,0 +1,161 @@ +import * as THREE from 'three/webgpu'; +import { mix, step, texture, screenUV, mrt, output, normalWorld, uv, vec2 } from 'three/tsl'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer, torus; +let quadMesh, sceneMRT, renderTarget, readbackTarget, material, readbackMaterial, pixelBuffer, pixelBufferTexture; + +const options = { + selection: 'mrt', +}; + +init(); + +function init() { + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(render); + document.body.appendChild(renderer.domElement); + + // + + renderer.inspector = new Inspector(); + + // + + const gui = renderer.inspector.createParameters('Settings'); + gui.add(options, 'selection', ['mrt', 'diffuse', 'normal']); + + // Create a multi render target with Float buffers + + renderTarget = new THREE.RenderTarget( + window.innerWidth * window.devicePixelRatio, + window.innerHeight * window.devicePixelRatio, + { count: 2, minFilter: THREE.NearestFilter, magFilter: THREE.NearestFilter }, + ); + + // Name our G-Buffer attachments for debugging + + renderTarget.textures[0].name = 'output'; + renderTarget.textures[1].name = 'normal'; + + // Init readback render target, readback data texture, readback material + // Be careful with the size! 512 is already big. Reading data back from the GPU is computationally intensive + + const size = 512; + + readbackTarget = new THREE.RenderTarget(size, size, { count: 2 }); + + pixelBuffer = new Uint8Array(size ** 2 * 4).fill(0); + pixelBufferTexture = new THREE.DataTexture(pixelBuffer, size, size); + pixelBufferTexture.type = THREE.UnsignedByteType; + pixelBufferTexture.format = THREE.RGBAFormat; + + readbackMaterial = new THREE.MeshBasicNodeMaterial(); + readbackMaterial.colorNode = texture(pixelBufferTexture); + + // MRT + + sceneMRT = mrt({ + output: output, + normal: normalWorld, + }); + + // Scene + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x222222); + + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 50); + camera.position.z = 4; + + const loader = new THREE.TextureLoader(); + + const diffuse = loader.load('textures/hardwood2_diffuse.jpg'); + diffuse.colorSpace = THREE.SRGBColorSpace; + diffuse.wrapS = THREE.RepeatWrapping; + diffuse.wrapT = THREE.RepeatWrapping; + + const torusMaterial = new THREE.NodeMaterial(); + torusMaterial.colorNode = texture(diffuse, uv().mul(vec2(10, 4))); + + torus = new THREE.Mesh(new THREE.TorusKnotGeometry(1, 0.3, 128, 32), torusMaterial); + scene.add(torus); + + // Output + + material = new THREE.NodeMaterial(); + material.colorNode = mix( + texture(renderTarget.textures[0]), + texture(renderTarget.textures[1]), + step(0.5, screenUV.x), + ); + + quadMesh = new THREE.QuadMesh(material); + + // Controls + + new OrbitControls(camera, renderer.domElement); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + + const dpr = renderer.getPixelRatio(); + renderTarget.setSize(window.innerWidth * dpr, window.innerHeight * dpr); +} + +async function render(time) { + const selection = options.selection; + + torus.rotation.y = (time / 1000) * 0.4; + + const isReadback = selection !== 'mrt'; + + // render scene into target + renderer.setMRT(sceneMRT); + renderer.setRenderTarget(isReadback ? readbackTarget : renderTarget); + renderer.render(scene, camera); + + // render post FX + renderer.setMRT(null); + renderer.setRenderTarget(null); + + if (isReadback) { + quadMesh.material = readbackMaterial; + + await readback(); + } else { + quadMesh.material = material; + } + + quadMesh.render(renderer); +} + +async function readback() { + const width = readbackTarget.width; + const height = readbackTarget.height; + + const selection = options.selection; + + if (selection === 'diffuse') { + pixelBuffer = await renderer.readRenderTargetPixelsAsync(readbackTarget, 0, 0, width, height, 0); // zero is optional + + pixelBufferTexture.image.data = pixelBuffer; + pixelBufferTexture.needsUpdate = true; + } else if (selection === 'normal') { + pixelBuffer = await renderer.readRenderTargetPixelsAsync(readbackTarget, 0, 0, width, height, 1); + + pixelBufferTexture.image.data = pixelBuffer; + pixelBufferTexture.needsUpdate = true; + } +} diff --git a/examples-testing/examples/webgpu_multisampled_renderbuffers.ts b/examples-testing/examples/webgpu_multisampled_renderbuffers.ts new file mode 100644 index 000000000..05098c8d4 --- /dev/null +++ b/examples-testing/examples/webgpu_multisampled_renderbuffers.ts @@ -0,0 +1,129 @@ +import * as THREE from 'three/webgpu'; +import { texture } from 'three/tsl'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer; +const mouse = new THREE.Vector2(); + +let quadMesh, renderTarget; + +let box, box2; + +const dpr = 1; + +const params = { + animated: true, + samples: 4, +}; + +const mat4 = new THREE.Matrix4(); + +const count = 50; +const fullRadius = 20; // Radius of the sphere +const halfRadius = 10; // Radius of the sphere +const positions = new Array(count).fill().map((_, i) => { + const radius = i % 2 === 0 ? fullRadius : halfRadius; + + const phi = Math.acos(2 * Math.random() - 1) - Math.PI / 2; // phi: latitude, range -π/2 to π/2 + const theta = 2 * Math.PI * Math.random(); // theta: longitude, range 0 to 2π + + return new THREE.Vector3( + radius * Math.cos(phi) * Math.cos(theta), // x + radius * Math.sin(phi), // y + radius * Math.cos(phi) * Math.sin(theta), // z + ); +}); + +init(); +initGUI(); + +function initGUI() { + const gui = renderer.inspector.createParameters('Settings'); + gui.add(params, 'samples', 0, 4, 1); + gui.add(params, 'animated'); +} + +function init() { + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 50); + camera.position.z = 3; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x111111); + + // textured mesh + + const geometryBox = new THREE.BoxGeometry(7, 7, 7, 12, 12, 12); + const materialBox = new THREE.MeshBasicNodeMaterial(); + const materialBoxInner = new THREE.MeshBasicNodeMaterial({ color: 0xff0000 }); + + materialBox.wireframe = true; + + // + + box = new THREE.InstancedMesh(geometryBox, materialBox, count); + box2 = new THREE.InstancedMesh(geometryBox, materialBoxInner, count); + + for (let i = 0; i < count; i++) { + box.setMatrixAt(i, mat4.identity().setPosition(positions[i])); + box2.setMatrixAt(i, mat4.multiplyScalar(0.996).setPosition(positions[i])); + } + + scene.add(box, box2); + + // + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.inspector = new Inspector(); + renderer.setPixelRatio(dpr); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + renderTarget = new THREE.RenderTarget(window.innerWidth * dpr, window.innerHeight * dpr, { + samples: params.samples, + depthBuffer: true, + }); + + window.addEventListener('mousemove', onWindowMouseMove); + window.addEventListener('resize', onWindowResize); + + // FX + + // modulate the final color based on the mouse position + + const materialFX = new THREE.MeshBasicNodeMaterial(); + materialFX.colorNode = texture(renderTarget.texture).rgb; + + quadMesh = new THREE.QuadMesh(materialFX); +} + +function onWindowMouseMove(e) { + mouse.x = e.offsetX / window.innerWidth; + mouse.y = e.offsetY / window.innerHeight; +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + renderTarget.setSize(window.innerWidth * dpr, window.innerHeight * dpr); +} + +function animate() { + if (params.animated) { + box.rotation.x += 0.001; + box.rotation.y += 0.002; + box2.rotation.x += 0.001; + box2.rotation.y += 0.002; + } + + renderTarget.samples = params.samples; + + renderer.setRenderTarget(renderTarget); + renderer.render(scene, camera); + + renderer.setRenderTarget(null); + quadMesh.render(renderer); +} diff --git a/examples-testing/examples/webgpu_occlusion.ts b/examples-testing/examples/webgpu_occlusion.ts new file mode 100644 index 000000000..376dc987d --- /dev/null +++ b/examples-testing/examples/webgpu_occlusion.ts @@ -0,0 +1,97 @@ +import * as THREE from 'three/webgpu'; +import { uniform } from 'three/tsl'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let camera, scene, renderer, controls; + +class OcclusionNode extends THREE.Node { + constructor(testObject, normalColor, occludedColor) { + super('vec3'); + + this.updateType = THREE.NodeUpdateType.OBJECT; + + this.uniformNode = uniform(new THREE.Color()); + + this.testObject = testObject; + this.normalColor = normalColor; + this.occludedColor = occludedColor; + } + + async update(frame) { + const isOccluded = frame.renderer.isOccluded(this.testObject); + + this.uniformNode.value.copy(isOccluded ? this.occludedColor : this.normalColor); + } + + setup(/* builder */) { + return this.uniformNode; + } +} + +init(); + +async function init() { + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.01, 100); + camera.position.z = 7; + + scene = new THREE.Scene(); + + // lights + + const ambientLight = new THREE.AmbientLight(0xb0b0b0); + + const light = new THREE.DirectionalLight(0xffffff, 1.0); + light.position.set(0.32, 0.39, 0.7); + + scene.add(ambientLight); + scene.add(light); + + // models + + const planeGeometry = new THREE.PlaneGeometry(2, 2); + const sphereGeometry = new THREE.SphereGeometry(0.5); + + const plane = new THREE.Mesh( + planeGeometry, + new THREE.MeshPhongNodeMaterial({ color: 0x00ff00, side: THREE.DoubleSide }), + ); + const sphere = new THREE.Mesh(sphereGeometry, new THREE.MeshPhongNodeMaterial({ color: 0xffff00 })); + + const instanceUniform = new OcclusionNode(sphere, new THREE.Color(0x0000ff), new THREE.Color(0x00ff00)); + + plane.material.colorNode = instanceUniform; + + sphere.position.z = -1; + sphere.occlusionTest = true; + + scene.add(plane); + scene.add(sphere); + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(render); + document.body.appendChild(renderer.domElement); + + // controls + + controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 3; + controls.maxDistance = 25; + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_ocean.ts b/examples-testing/examples/webgpu_ocean.ts new file mode 100644 index 000000000..a7939461e --- /dev/null +++ b/examples-testing/examples/webgpu_ocean.ts @@ -0,0 +1,181 @@ +import * as THREE from 'three/webgpu'; +import { pass } from 'three/tsl'; +import { bloom } from 'three/addons/tsl/display/BloomNode.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { WaterMesh } from 'three/addons/objects/WaterMesh.js'; +import { SkyMesh } from 'three/addons/objects/SkyMesh.js'; + +let container; +let camera, scene, renderer, renderPipeline; +let controls, water, sun, sky, mesh, bloomPass; + +init(); + +function init() { + container = document.getElementById('container'); + + // + + renderer = new THREE.WebGPURenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(render); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = 0.1; + renderer.inspector = new Inspector(); + container.appendChild(renderer.domElement); + + // + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight, 1, 20000); + camera.position.set(30, 30, 100); + + // Post-processing + + renderPipeline = new THREE.RenderPipeline(renderer); + + const scenePass = pass(scene, camera); + const scenePassColor = scenePass.getTextureNode('output'); + + bloomPass = bloom(scenePassColor); + bloomPass.threshold.value = 0; + bloomPass.strength.value = 0.1; + bloomPass.radius.value = 0; + + renderPipeline.outputNode = scenePassColor.add(bloomPass); + + // + + sun = new THREE.Vector3(); + + // Water + + const waterGeometry = new THREE.PlaneGeometry(10000, 10000); + const loader = new THREE.TextureLoader(); + const waterNormals = loader.load('textures/waternormals.jpg'); + waterNormals.wrapS = waterNormals.wrapT = THREE.RepeatWrapping; + + water = new WaterMesh(waterGeometry, { + waterNormals: waterNormals, + sunDirection: new THREE.Vector3(), + sunColor: 0xffffff, + waterColor: 0x001e0f, + distortionScale: 3.7, + }); + + water.rotation.x = -Math.PI / 2; + + scene.add(water); + + // Skybox + + sky = new SkyMesh(); + sky.scale.setScalar(10000); + scene.add(sky); + + sky.turbidity.value = 10; + sky.rayleigh.value = 2; + sky.mieCoefficient.value = 0.005; + sky.mieDirectionalG.value = 0.8; + sky.cloudCoverage.value = 0.4; + sky.cloudDensity.value = 0.5; + sky.cloudElevation.value = 0.5; + + const parameters = { + elevation: 2, + azimuth: 180, + exposure: 0.1, + }; + + const pmremGenerator = new THREE.PMREMGenerator(renderer); + const sceneEnv = new THREE.Scene(); + + let renderTarget; + + function updateSun() { + const phi = THREE.MathUtils.degToRad(90 - parameters.elevation); + const theta = THREE.MathUtils.degToRad(parameters.azimuth); + + sun.setFromSphericalCoords(1, phi, theta); + + sky.sunPosition.value.copy(sun); + water.sunDirection.value.copy(sun).normalize(); + + if (renderTarget !== undefined) renderTarget.dispose(); + + sceneEnv.add(sky); + renderTarget = pmremGenerator.fromScene(sceneEnv); + scene.add(sky); + + scene.environment = renderTarget.texture; + } + + renderer.init().then(updateSun); + + // + + const geometry = new THREE.BoxGeometry(30, 30, 30); + const material = new THREE.MeshStandardMaterial({ roughness: 0 }); + + mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + // + + controls = new OrbitControls(camera, renderer.domElement); + controls.maxPolarAngle = Math.PI * 0.495; + controls.target.set(0, 10, 0); + controls.minDistance = 40.0; + controls.maxDistance = 200.0; + controls.update(); + + // GUI + + const gui = renderer.inspector.createParameters('Settings'); + + const folderSky = gui.addFolder('Sky'); + folderSky.add(parameters, 'elevation', 0, 90, 0.1).onChange(updateSun); + folderSky.add(parameters, 'azimuth', -180, 180, 0.1).onChange(updateSun); + folderSky.add(parameters, 'exposure', 0, 1, 0.0001).onChange(function (value) { + renderer.toneMappingExposure = value; + }); + + const folderWater = gui.addFolder('Water'); + folderWater.add(water.distortionScale, 'value', 0, 8, 0.1).name('distortionScale'); + folderWater.add(water.size, 'value', 0.1, 10, 0.1).name('size'); + + const folderBloom = gui.addFolder('Bloom'); + folderBloom.add(bloomPass.strength, 'value', 0, 3, 0.01).name('strength'); + folderBloom.add(bloomPass.radius, 'value', 0, 1, 0.01).name('radius'); + + const folderClouds = gui.addFolder('Clouds'); + folderClouds.add(sky.cloudCoverage, 'value', 0, 1, 0.01).name('coverage'); + folderClouds.add(sky.cloudDensity, 'value', 0, 1, 0.01).name('density'); + folderClouds.add(sky.cloudElevation, 'value', 0, 1, 0.01).name('elevation'); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function render() { + const time = performance.now() * 0.001; + + mesh.position.y = Math.sin(time) * 20 + 5; + mesh.rotation.x = time * 0.5; + mesh.rotation.z = time * 0.51; + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_parallax_uv.ts b/examples-testing/examples/webgpu_parallax_uv.ts new file mode 100644 index 000000000..b6282d89f --- /dev/null +++ b/examples-testing/examples/webgpu_parallax_uv.ts @@ -0,0 +1,135 @@ +import * as THREE from 'three/webgpu'; +import { texture, parallaxUV, blendOverlay, uv, normalMap, uniform } from 'three/tsl'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer; + +let controls; + +init(); + +async function init() { + // scene + + scene = new THREE.Scene(); + + // camera + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(15, 7, 15); + + // environment + + const environmentTexture = await new HDRLoader().loadAsync('./textures/equirectangular/752-hdri-skies-com_1k.hdr'); + + environmentTexture.mapping = THREE.EquirectangularReflectionMapping; + + scene.environment = environmentTexture; + scene.background = environmentTexture; + scene.backgroundBlurriness = 0.4; + + // textures + + const loader = new THREE.TextureLoader(); + + const topTexture = await loader.loadAsync('textures/ambientcg/Ice002_1K-JPG_Color.jpg'); + topTexture.colorSpace = THREE.SRGBColorSpace; + topTexture.wrapS = THREE.RepeatWrapping; + topTexture.wrapT = THREE.RepeatWrapping; + + const roughnessTexture = await loader.loadAsync('textures/ambientcg/Ice002_1K-JPG_Roughness.jpg'); + roughnessTexture.colorSpace = THREE.NoColorSpace; + roughnessTexture.wrapS = THREE.RepeatWrapping; + roughnessTexture.wrapT = THREE.RepeatWrapping; + + const normalTexture = await loader.loadAsync('textures/ambientcg/Ice002_1K-JPG_NormalGL.jpg'); + normalTexture.colorSpace = THREE.NoColorSpace; + normalTexture.wrapS = THREE.RepeatWrapping; + normalTexture.wrapT = THREE.RepeatWrapping; + + const displaceTexture = await loader.loadAsync('textures/ambientcg/Ice002_1K-JPG_Displacement.jpg'); + displaceTexture.colorSpace = THREE.NoColorSpace; + displaceTexture.wrapS = THREE.RepeatWrapping; + displaceTexture.wrapT = THREE.RepeatWrapping; + + // + + const bottomTexture = await loader.loadAsync('textures/ambientcg/Ice003_1K-JPG_Color.jpg'); + bottomTexture.colorSpace = THREE.SRGBColorSpace; + bottomTexture.wrapS = THREE.RepeatWrapping; + bottomTexture.wrapT = THREE.RepeatWrapping; + + // parallax effect + + const scaleUV = uniform(3); + const scaledUV = uv().mul(scaleUV); + + const parallaxScale = uniform(0.5); // parallax scale + const offsetUV = texture(displaceTexture, scaledUV).mul(parallaxScale); + + const parallaxUVOffset = parallaxUV(scaledUV, offsetUV); + const parallaxResult = texture(bottomTexture, parallaxUVOffset); + + const iceNode = blendOverlay(texture(topTexture, scaledUV), parallaxResult); + + // material + + const material = new THREE.MeshStandardNodeMaterial(); + material.colorNode = iceNode.mul(5); // increase the color intensity to 5 ( contrast ) + material.roughnessNode = texture(roughnessTexture, scaledUV); + material.normalNode = normalMap(texture(normalTexture, scaledUV)); + material.metalness = 0; + + const geometry = new THREE.CircleGeometry(25, 64); + + const ground = new THREE.Mesh(geometry, material); + ground.rotateX(-Math.PI / 2); + scene.add(ground); + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.toneMapping = THREE.ReinhardToneMapping; + renderer.toneMappingExposure = 6; + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // gui + + const gui = renderer.inspector.createParameters('Scene'); + gui.add(scene, 'backgroundBlurriness', 0, 1).name('Background Blurriness'); + gui.add(parallaxScale, 'value', 0.2, 0.5).name('Parallax Scale'); + gui.add(scaleUV, 'value', 1, 5).name('UV Scale'); + + // controls + + controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, 0, 0); + controls.maxDistance = 40; + controls.minDistance = 10; + controls.autoRotate = true; + controls.autoRotateSpeed = -1; + controls.update(); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + controls.update(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_particles.ts b/examples-testing/examples/webgpu_particles.ts new file mode 100644 index 000000000..9e81f0761 --- /dev/null +++ b/examples-testing/examples/webgpu_particles.ts @@ -0,0 +1,151 @@ +import * as THREE from 'three/webgpu'; +import { range, texture, mix, uv, color, rotateUV, positionLocal, time, uniform } from 'three/tsl'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import WebGPU from 'three/addons/capabilities/WebGPU.js'; + +let camera, scene, renderer; +let controls; + +init(); + +function init() { + if (WebGPU.isAvailable() === false) { + document.body.appendChild(WebGPU.getErrorMessage()); + + throw new Error('No WebGPU support'); + } + + const { innerWidth, innerHeight } = window; + + camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 5000); + camera.position.set(1300, 500, 0); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x333333); + + // textures + + const textureLoader = new THREE.TextureLoader(); + const map = textureLoader.load('textures/opengameart/smoke1.png'); + + // create nodes + + const lifeRange = range(0.1, 1); + const offsetRange = range(new THREE.Vector3(-2, 3, -2), new THREE.Vector3(2, 5, 2)); + + const speed = uniform(0.2); + const scaledTime = time.add(5).mul(speed); + + const lifeTime = scaledTime.mul(lifeRange).mod(1); + const scaleRange = range(0.3, 2); + const rotateRange = range(0.1, 4); + + const life = lifeTime.div(lifeRange); + + const fakeLightEffect = positionLocal.y.oneMinus().max(0.2); + + const textureNode = texture(map, rotateUV(uv(), scaledTime.mul(rotateRange))); + + const opacityNode = textureNode.a.mul(life.oneMinus()); + + const smokeColor = mix(color(0x2c1501), color(0x222222), positionLocal.y.mul(3).clamp()); + + // create particles + + const smokeNodeMaterial = new THREE.SpriteNodeMaterial(); + smokeNodeMaterial.colorNode = mix(color(0xf27d0c), smokeColor, life.mul(2.5).min(1)).mul(fakeLightEffect); + smokeNodeMaterial.opacityNode = opacityNode; + smokeNodeMaterial.positionNode = offsetRange.mul(lifeTime); + smokeNodeMaterial.scaleNode = scaleRange.mul(lifeTime.max(0.3)); + smokeNodeMaterial.depthWrite = false; + + const smokeInstancedSprite = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), smokeNodeMaterial); + smokeInstancedSprite.scale.setScalar(400); + smokeInstancedSprite.count = 2000; + scene.add(smokeInstancedSprite); + + // + + const fireGeometry = new THREE.PlaneGeometry(1, 1); + const fireCount = 1000; + + const fireNodeMaterial = new THREE.SpriteNodeMaterial(); + fireNodeMaterial.colorNode = mix(color(0xb72f17), color(0xb72f17), life); + fireNodeMaterial.positionNode = range(new THREE.Vector3(-1, 1, -1), new THREE.Vector3(1, 2, 1)).mul(lifeTime); + fireNodeMaterial.scaleNode = smokeNodeMaterial.scaleNode; + fireNodeMaterial.opacityNode = opacityNode.mul(0.5); + fireNodeMaterial.blending = THREE.AdditiveBlending; + fireNodeMaterial.transparent = true; + fireNodeMaterial.depthWrite = false; + + const fireInstancedSprite = new THREE.Mesh(fireGeometry, fireNodeMaterial); + fireInstancedSprite.scale.setScalar(400); + fireInstancedSprite.count = fireCount; + fireInstancedSprite.position.y = -100; + fireInstancedSprite.renderOrder = 1; + scene.add(fireInstancedSprite); + + // indirect draw ( optional ) + // each indirect draw call is 5 uint32 values for indexes ( different structure for non-indexed draw calls using 4 uint32 values ) + + const indexCount = fireGeometry.index.array.length; + + const uint32 = new Uint32Array(5); + uint32[0] = indexCount; // indexCount + uint32[1] = fireCount; // instanceCount + uint32[2] = 0; // firstIndex + uint32[3] = 0; // baseVertex + uint32[4] = 0; // firstInstance + + const indirectAttribute = new THREE.IndirectStorageBufferAttribute(uint32, 5); + fireGeometry.setIndirect(indirectAttribute); + + // + + const helper = new THREE.GridHelper(3000, 40, 0x444444, 0x444444); + helper.position.y = -75; + scene.add(helper); + + // + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(render); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // + + controls = new OrbitControls(camera, renderer.domElement); + controls.maxDistance = 2700; + controls.target.set(0, 500, 0); + controls.update(); + + // + + window.addEventListener('resize', onWindowResize); + + // gui + + const gui = renderer.inspector.createParameters('Settings'); + + gui.add(speed, 'value', 0, 1, 0.01).name('speed'); +} + +function onWindowResize() { + const { innerWidth, innerHeight } = window; + + camera.aspect = innerWidth / innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(innerWidth, innerHeight); +} + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_performance.ts b/examples-testing/examples/webgpu_performance.ts new file mode 100644 index 000000000..fb4ed1556 --- /dev/null +++ b/examples-testing/examples/webgpu_performance.ts @@ -0,0 +1,101 @@ +import * as THREE from 'three/webgpu'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; + +import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; + +let camera, scene, renderer; +let model; + +const options = { static: true }; + +init(); + +function setStatic(object, value) { + object.traverse(child => { + if (child.isMesh) { + child.static = value; + } + }); +} + +function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(60, 60, 60); + + scene = new THREE.Scene(); + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = 1; + renderer.inspector = new Inspector(); + renderer.setAnimationLoop(render); + container.appendChild(renderer.domElement); + + // + + new UltraHDRLoader().setPath('textures/equirectangular/').load('royal_esplanade_2k.hdr.jpg', function (texture) { + texture.mapping = THREE.EquirectangularReflectionMapping; + + scene.environment = texture; + + // model + + const dracoLoader = new DRACOLoader(); + dracoLoader.setDecoderPath('jsm/libs/draco/gltf/'); + + const loader = new GLTFLoader().setPath('models/gltf/'); + loader.setDRACOLoader(dracoLoader); + + loader.load('dungeon_warkarma.glb', async function (gltf) { + model = gltf.scene; + + // wait until the model can be added to the scene without blocking due to shader compilation + + await renderer.compileAsync(model, camera, scene); + + scene.add(model); + + // + + setStatic(model, options.static); + }); + }); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 2; + controls.maxDistance = 60; + controls.target.set(0, 0, -0.2); + controls.update(); + + // gui + + const gui = renderer.inspector.createParameters('Settings'); + gui.add(options, 'static').onChange(() => { + setStatic(model, options.static); + }); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_performance_renderbundle.ts b/examples-testing/examples/webgpu_performance_renderbundle.ts new file mode 100644 index 000000000..3b20847b8 --- /dev/null +++ b/examples-testing/examples/webgpu_performance_renderbundle.ts @@ -0,0 +1,197 @@ +import * as THREE from 'three/webgpu'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let camera, scene, renderer; +let controls; +let gui; +let geometries, group; + +// + +const position = new THREE.Vector3(); +const rotation = new THREE.Euler(); +const quaternion = new THREE.Quaternion(); +const scale = new THREE.Vector3(); + +// + +const api = { + webgpu: true, + renderBundle: true, + count: 4000, + opacity: 1, + dynamic: false, +}; + +init(); + +// + +function randomizeMatrix(matrix) { + position.x = Math.random() * 80 - 40; + position.y = Math.random() * 80 - 40; + position.z = Math.random() * 80 - 40; + + rotation.x = Math.random() * 2 * Math.PI; + rotation.y = Math.random() * 2 * Math.PI; + rotation.z = Math.random() * 2 * Math.PI; + + quaternion.setFromEuler(rotation); + + scale.x = scale.y = scale.z = 0.35 + Math.random() * 0.5; + + return matrix.compose(position, quaternion, scale); +} + +function randomizeRotationSpeed(rotation) { + rotation.x = Math.random() * 0.05; + rotation.y = Math.random() * 0.05; + rotation.z = Math.random() * 0.05; + return rotation; +} + +function initGeometries() { + geometries = [ + new THREE.ConeGeometry(1.0, 2.0, 3, 1), + new THREE.BoxGeometry(2.0, 2.0, 2.0), + new THREE.PlaneGeometry(2.0, 2, 1, 1), + new THREE.CapsuleGeometry(), + new THREE.CircleGeometry(1.0, 3), + new THREE.CylinderGeometry(1.0, 1.0, 2.0, 3, 1), + new THREE.DodecahedronGeometry(1.0, 0), + new THREE.IcosahedronGeometry(1.0, 0), + new THREE.OctahedronGeometry(1.0, 0), + new THREE.PolyhedronGeometry([0, 0, 0], [0, 0, 0], 1, 0), + new THREE.RingGeometry(1.0, 1.5, 3), + new THREE.SphereGeometry(1.0, 3, 2), + new THREE.TetrahedronGeometry(1.0, 0), + new THREE.TorusGeometry(1.0, 0.5, 3, 3), + new THREE.TorusKnotGeometry(1.0, 0.5, 20, 3, 1, 1), + ]; +} + +function initMesh(count) { + initRegularMesh(count); +} + +function initRegularMesh(count) { + group = api.renderBundle ? new THREE.BundleGroup() : new THREE.Group(); + + for (let i = 0; i < count; i++) { + const material = new THREE.MeshToonNodeMaterial({ + color: new THREE.Color(Math.random() * 0xffffff), + side: THREE.DoubleSide, + }); + + const child = new THREE.Mesh(geometries[i % geometries.length], material); + randomizeMatrix(child.matrix); + child.matrix.decompose(child.position, child.quaternion, child.scale); + child.userData.rotationSpeed = randomizeRotationSpeed(new THREE.Euler()); + child.frustumCulled = false; + group.add(child); + } + + scene.add(group); +} + +function init() { + const searchParams = new URLSearchParams(window.location.search); + api.webgpu = searchParams.get('backend') !== 'webgl'; + api.renderBundle = searchParams.get('renderBundle') !== 'false'; + api.count = parseFloat(searchParams.get('count') || 4000); + + const count = api.count; + + // camera + + const aspect = window.innerWidth / window.innerHeight; + + camera = new THREE.PerspectiveCamera(70, aspect, 1, 100); + camera.position.z = 50; + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: true, forceWebGL: !api.webgpu }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.inspector = new Inspector(); + renderer.setAnimationLoop(animate); + + // scene + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xc1c1c1); + + const light = new THREE.DirectionalLight(0xffffff, 3.4); + scene.add(light); + + document.body.appendChild(renderer.domElement); + + initGeometries(); + initMesh(count); + + controls = new OrbitControls(camera, renderer.domElement); + controls.autoRotate = true; + controls.autoRotateSpeed = 1.0; + + // gui + + gui = renderer.inspector.createParameters('Settings'); + gui.add(api, 'renderBundle').name('render bundle').onChange(reload); + + gui.add(api, 'webgpu').onChange(reload); + + gui.add(api, 'dynamic').onChange(() => { + group.static = !group.static; + }); + + // listeners + + window.addEventListener('resize', onWindowResize); + + function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); + group.needsUpdate = true; + } + + function reload() { + const backendParam = 'backend=' + (api.webgpu ? 'webgpu' : 'webgl'); + const renderBundleParam = '&renderBundle=' + (api.renderBundle ? 'true' : 'false'); + const countParam = '&count=' + api.count; + + location.href = location.pathname + '?' + backendParam + renderBundleParam + countParam; // relative redirect with parameters + } + + function animate() { + animateMeshes(); + + controls.update(); + + renderer.render(scene, camera); + } + + function animateMeshes() { + const count = api.count; + const loopNum = api.dynamic ? count : 0; + + for (let i = 0; i < loopNum; i++) { + const child = group.children[i]; + const rotationSpeed = child.userData.rotationSpeed; + + child.rotation.set( + child.rotation.x + rotationSpeed.x, + child.rotation.y + rotationSpeed.y, + child.rotation.z + rotationSpeed.z, + ); + } + } +} diff --git a/examples-testing/examples/webgpu_pmrem_cubemap.ts b/examples-testing/examples/webgpu_pmrem_cubemap.ts new file mode 100644 index 000000000..bd751ba33 --- /dev/null +++ b/examples-testing/examples/webgpu_pmrem_cubemap.ts @@ -0,0 +1,75 @@ +import * as THREE from 'three/webgpu'; +import { normalWorldGeometry, uniform, pmremTexture } from 'three/tsl'; + +import { HDRCubeTextureLoader } from 'three/addons/loaders/HDRCubeTextureLoader.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let camera, scene, renderer; + +init(); + +async function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); + camera.position.set(0, 0, 8); + + scene = new THREE.Scene(); + + const forceWebGL = false; + + renderer = new THREE.WebGPURenderer({ antialias: true, forceWebGL }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(render); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + + await renderer.init(); + + container.appendChild(renderer.domElement); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 2; + controls.maxDistance = 10; + controls.update(); + + new HDRCubeTextureLoader() + .setPath('./textures/cube/pisaHDR/') + .load(['px.hdr', 'nx.hdr', 'py.hdr', 'ny.hdr', 'pz.hdr', 'nz.hdr'], function (map) { + scene.backgroundNode = pmremTexture(map, normalWorldGeometry, uniform(0.5)); + + const geometry = new THREE.SphereGeometry(0.4, 64, 64); + + for (let i = 0; i < 6; i++) { + for (let j = 0; j < 5; j++) { + const material = new THREE.MeshPhysicalNodeMaterial({ + roughness: i / 5, + metalness: j / 4, + envMap: map, + }); + + const mesh = new THREE.Mesh(geometry, material); + mesh.position.x = i - 2.5; + mesh.position.y = j - 2; + scene.add(mesh); + } + } + }); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_pmrem_equirectangular.ts b/examples-testing/examples/webgpu_pmrem_equirectangular.ts new file mode 100644 index 000000000..76f8326d3 --- /dev/null +++ b/examples-testing/examples/webgpu_pmrem_equirectangular.ts @@ -0,0 +1,75 @@ +import * as THREE from 'three/webgpu'; +import { normalWorldGeometry, uniform, pmremTexture } from 'three/tsl'; + +import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let camera, scene, renderer; + +init(); + +async function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); + camera.position.set(0, 0, 8); + + scene = new THREE.Scene(); + + const forceWebGL = false; + + renderer = new THREE.WebGPURenderer({ antialias: true, forceWebGL }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(render); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + + await renderer.init(); + + container.appendChild(renderer.domElement); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 2; + controls.maxDistance = 10; + controls.update(); + + new UltraHDRLoader().setPath('textures/equirectangular/').load('royal_esplanade_2k.hdr.jpg', function (map) { + map.mapping = THREE.EquirectangularReflectionMapping; + + scene.backgroundNode = pmremTexture(map, normalWorldGeometry, uniform(0.5)); + + const geometry = new THREE.SphereGeometry(0.4, 64, 64); + + for (let i = 0; i < 6; i++) { + for (let j = 0; j < 5; j++) { + const material = new THREE.MeshPhysicalNodeMaterial({ + roughness: i / 5, + metalness: j / 4, + envMap: map, + }); + + const mesh = new THREE.Mesh(geometry, material); + mesh.position.x = i - 2.5; + mesh.position.y = j - 2; + scene.add(mesh); + } + } + }); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_pmrem_scene.ts b/examples-testing/examples/webgpu_pmrem_scene.ts new file mode 100644 index 000000000..deab005fc --- /dev/null +++ b/examples-testing/examples/webgpu_pmrem_scene.ts @@ -0,0 +1,106 @@ +import * as THREE from 'three/webgpu'; +import { normalWorld, uniform, pmremTexture } from 'three/tsl'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer; + +init(); + +async function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); + camera.position.set(-1.8, 0.6, 2.7); + + scene = new THREE.Scene(); + + const forceWebGL = false; + + renderer = new THREE.WebGPURenderer({ antialias: true, forceWebGL }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(render); + renderer.inspector = new Inspector(); + container.appendChild(renderer.domElement); + + await renderer.init(); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.addEventListener('change', render); // use if there is no animation loop + controls.minDistance = 2; + controls.maxDistance = 10; + controls.update(); + + // + const loader = new THREE.CubeTextureLoader().setPath('./textures/cube/Park3Med/'); + + scene.background = await loader.loadAsync(['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg']); + + let model; + + model = new THREE.Mesh(new THREE.SphereGeometry(0.2, 64, 64), new THREE.MeshBasicMaterial({ color: 0x0000ff })); + model.position.z -= 1; + scene.add(model); + + model = new THREE.Mesh(new THREE.SphereGeometry(0.2, 64, 64), new THREE.MeshBasicMaterial({ color: 0xff0000 })); + model.position.z += 1; + scene.add(model); + + model = new THREE.Mesh(new THREE.SphereGeometry(0.2, 64, 64), new THREE.MeshBasicMaterial({ color: 0xff00ff })); + model.position.x += 1; + scene.add(model); + + model = new THREE.Mesh(new THREE.SphereGeometry(0.2, 64, 64), new THREE.MeshBasicMaterial({ color: 0x00ffff })); + model.position.x -= 1; + scene.add(model); + + model = new THREE.Mesh(new THREE.SphereGeometry(0.2, 64, 64), new THREE.MeshBasicMaterial({ color: 0xffff00 })); + model.position.y -= 1; + scene.add(model); + + model = new THREE.Mesh(new THREE.SphereGeometry(0.2, 64, 64), new THREE.MeshBasicMaterial({ color: 0x00ff00 })); + model.position.y += 1; + scene.add(model); + + //while ( scene.children.length > 0 ) scene.remove( scene.children[ 0 ] ); + + const sceneRT = new THREE.PMREMGenerator(renderer).fromScene(scene); + + // + + const pmremRoughness = uniform(0.5); + const pmremNode = pmremTexture(sceneRT.texture, normalWorld, pmremRoughness); + + scene.add( + new THREE.Mesh( + new THREE.SphereGeometry(0.5, 64, 64), + new THREE.MeshBasicNodeMaterial({ colorNode: pmremNode }), + ), + ); + + // gui + + const gui = renderer.inspector.createParameters('Settings'); + gui.add(pmremRoughness, 'value', 0, 1, 0.001) + .name('roughness') + .onChange(() => render()); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_pmrem_test.ts b/examples-testing/examples/webgpu_pmrem_test.ts new file mode 100644 index 000000000..3e1ea3ef9 --- /dev/null +++ b/examples-testing/examples/webgpu_pmrem_test.ts @@ -0,0 +1,138 @@ +import * as THREE from 'three'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let scene, camera, controls, renderer; + +async function init() { + const width = window.innerWidth; + const height = window.innerHeight; + const aspect = width / height; + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(width, height); + renderer.setAnimationLoop(render); + renderer.inspector = new Inspector(); + + // tonemapping + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = 1; + + document.body.appendChild(renderer.domElement); + + window.addEventListener('resize', onWindowResize); + + await renderer.init(); + + // scene + + scene = new THREE.Scene(); + + // camera + + camera = new THREE.PerspectiveCamera(40, aspect, 1, 30); + updateCamera(); + camera.position.set(0, 0, 16); + + // controls + + controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 4; + controls.maxDistance = 20; + + // light + + const directionalLight = new THREE.DirectionalLight(0xffffff, 0); // set intensity to 0 to start + const x = 597; + const y = 213; + const theta = ((x + 0.5) * Math.PI) / 512; + const phi = ((y + 0.5) * Math.PI) / 512; + + directionalLight.position.setFromSphericalCoords(100, -phi, Math.PI / 2 - theta); + + scene.add(directionalLight); + // scene.add( new THREE.DirectionalLightHelper( directionalLight ) ); + + // The spot1Lux HDR environment map is expressed in nits (lux / sr). The directional light has units of lux, + // so to match a 1 lux light, we set a single pixel with a value equal to 1 divided by the solid + // angle of the pixel in steradians. This image is 1024 x 512, + // so the value is 1 / ( sin( phi ) * ( pi / 512 ) ^ 2 ) = 27,490 nits. + + const gui = renderer.inspector.createParameters('Settings'); + gui.add({ enabled: true }, 'enabled') + .name('PMREM') + .onChange(value => { + directionalLight.intensity = value ? 0 : 1; + + scene.traverse(function (child) { + if (child.isMesh) { + child.material.envMapIntensity = 1 - directionalLight.intensity; + } + }); + }); +} + +function createObjects() { + let radianceMap = null; + new HDRLoader() + // .setDataType( THREE.FloatType ) + .setPath('textures/equirectangular/') + .load('spot1Lux.hdr', function (texture) { + radianceMap = pmremGenerator.fromEquirectangular(texture).texture; + pmremGenerator.dispose(); + + scene.background = radianceMap; + + const geometry = new THREE.SphereGeometry(0.4, 32, 32); + + for (let x = 0; x <= 10; x++) { + for (let y = 0; y <= 2; y++) { + const material = new THREE.MeshPhysicalMaterial({ + roughness: x / 10, + metalness: y < 1 ? 1 : 0, + color: y < 2 ? 0xffffff : 0x000000, + envMap: radianceMap, + envMapIntensity: 1, + }); + + const mesh = new THREE.Mesh(geometry, material); + mesh.position.x = x - 5; + mesh.position.y = 1 - y; + scene.add(mesh); + } + } + }); + + const pmremGenerator = new THREE.PMREMGenerator(renderer); + pmremGenerator.compileEquirectangularShader(); +} + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + updateCamera(); + + renderer.setSize(width, height); +} + +function updateCamera() { + const horizontalFoV = 40; + const verticalFoV = + (2 * Math.atan(Math.tan(((horizontalFoV / 2) * Math.PI) / 180) / camera.aspect) * 180) / Math.PI; + camera.fov = verticalFoV; + camera.updateProjectionMatrix(); +} + +function render() { + renderer.render(scene, camera); +} + +Promise.resolve().then(init).then(createObjects); diff --git a/examples-testing/examples/webgpu_portal.ts b/examples-testing/examples/webgpu_portal.ts new file mode 100644 index 000000000..df2e400f3 --- /dev/null +++ b/examples-testing/examples/webgpu_portal.ts @@ -0,0 +1,156 @@ +import * as THREE from 'three/webgpu'; +import { + pass, + color, + mx_worley_noise_float, + time, + screenUV, + vec2, + uv, + normalWorld, + mx_fractal_noise_vec3, +} from 'three/tsl'; + +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, sceneMain, scenePortal, renderer; +let timer; + +const mixers = []; + +init(); + +function init() { + // + + sceneMain = new THREE.Scene(); + sceneMain.background = new THREE.Color(0x222222); + sceneMain.backgroundNode = normalWorld.y.mix(color(0x0066ff), color(0xff0066)); + + scenePortal = new THREE.Scene(); + scenePortal.backgroundNode = mx_worley_noise_float(normalWorld.mul(20).add(vec2(0, time.oneMinus()))).mul( + color(0x0066ff), + ); + scenePortal.name = 'Portal Scene'; + + // + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.01, 30); + camera.position.set(2.5, 1, 3); + camera.position.multiplyScalar(0.8); + camera.lookAt(0, 1, 0); + + timer = new THREE.Timer(); + timer.connect(document); + + // lights + + const light = new THREE.PointLight(0xffffff, 1); + light.position.set(0, 1, 5); + light.power = 17000; + + sceneMain.add(new THREE.HemisphereLight(0xff0066, 0x0066ff, 7)); + sceneMain.add(light); + scenePortal.add(light.clone()); + + // models + + const loader = new GLTFLoader(); + loader.load('models/gltf/Xbot.glb', function (gltf) { + const createModel = (colorNode = null) => { + let object; + + if (mixers.length === 0) { + object = gltf.scene; + } else { + object = gltf.scene.clone(); + + const children = object.children[0].children; + + const applyFX = index => { + children[index].material = children[index].material.clone(); + children[index].material.colorNode = colorNode; + children[index].material.wireframe = true; + }; + + applyFX(0); + applyFX(1); + } + + const mixer = new THREE.AnimationMixer(object); + + const action = mixer.clipAction(gltf.animations[6]); + action.play(); + + mixers.push(mixer); + + return object; + }; + + const colorNode = mx_fractal_noise_vec3(uv().mul(20).add(time)); + + const modelMain = createModel(); + const modelPortal = createModel(colorNode); + + // model portal + + sceneMain.add(modelMain); + scenePortal.add(modelPortal); + }); + + // portal + + const geometry = new THREE.PlaneGeometry(1.7, 2); + + const material = new THREE.MeshBasicNodeMaterial(); + material.colorNode = pass(scenePortal, camera).context({ getUV: () => screenUV }); + material.opacityNode = uv().distance(0.5).remapClamp(0.3, 0.5).oneMinus(); + material.side = THREE.DoubleSide; + material.transparent = true; + + const plane = new THREE.Mesh(geometry, material); + plane.position.set(0, 1, 0.8); + sceneMain.add(plane); + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.toneMapping = THREE.LinearToneMapping; + renderer.toneMappingExposure = 0.15; + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // + + const controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, 1, 0); + controls.update(); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + const delta = timer.getDelta(); + + for (const mixer of mixers) { + mixer.update(delta); + } + + renderer.render(sceneMain, camera); +} diff --git a/examples-testing/examples/webgpu_postprocessing.ts b/examples-testing/examples/webgpu_postprocessing.ts new file mode 100644 index 000000000..d554a8835 --- /dev/null +++ b/examples-testing/examples/webgpu_postprocessing.ts @@ -0,0 +1,81 @@ +import * as THREE from 'three/webgpu'; +import { pass } from 'three/tsl'; +import { dotScreen } from 'three/addons/tsl/display/DotScreenNode.js'; +import { rgbShift } from 'three/addons/tsl/display/RGBShiftNode.js'; +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, renderer, renderPipeline; +let object; + +init(); + +function init() { + renderer = new THREE.WebGPURenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // + + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.z = 400; + + const scene = new THREE.Scene(); + scene.fog = new THREE.Fog(0x000000, 1, 1000); + + object = new THREE.Object3D(); + scene.add(object); + + const geometry = new THREE.SphereGeometry(1, 4, 4); + const material = new THREE.MeshPhongMaterial({ color: 0xffffff, flatShading: true }); + + for (let i = 0; i < 100; i++) { + const mesh = new THREE.Mesh(geometry, material); + mesh.position.set(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5).normalize(); + mesh.position.multiplyScalar(Math.random() * 400); + mesh.rotation.set(Math.random() * 2, Math.random() * 2, Math.random() * 2); + mesh.scale.x = mesh.scale.y = mesh.scale.z = Math.random() * 50; + object.add(mesh); + } + + scene.add(new THREE.AmbientLight(0xcccccc)); + + const light = new THREE.DirectionalLight(0xffffff, 3); + light.position.set(1, 1, 1); + scene.add(light); + + // postprocessing + + renderPipeline = new THREE.RenderPipeline(renderer); + + const scenePass = pass(scene, camera); + const scenePassColor = scenePass.getTextureNode().toInspector('Scene Color'); + + const dotScreenPass = dotScreen(scenePassColor); + dotScreenPass.scale.value = 0.3; + + const rgbShiftPass = rgbShift(dotScreenPass); + rgbShiftPass.amount.value = 0.001; + + renderPipeline.outputNode = rgbShiftPass; + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + object.rotation.x += 0.005; + object.rotation.y += 0.01; + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_postprocessing_3dlut.ts b/examples-testing/examples/webgpu_postprocessing_3dlut.ts new file mode 100644 index 000000000..867cc62f7 --- /dev/null +++ b/examples-testing/examples/webgpu_postprocessing_3dlut.ts @@ -0,0 +1,228 @@ +import * as THREE from 'three/webgpu'; +import { + mix, + mul, + oneMinus, + positionLocal, + smoothstep, + texture, + time, + rotateUV, + Fn, + uv, + vec2, + vec3, + vec4, + pass, + texture3D, + uniform, + renderOutput, +} from 'three/tsl'; +import { lut3D } from 'three/addons/tsl/display/Lut3DNode.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { LUTCubeLoader } from 'three/addons/loaders/LUTCubeLoader.js'; +import { LUT3dlLoader } from 'three/addons/loaders/LUT3dlLoader.js'; +import { LUTImageLoader } from 'three/addons/loaders/LUTImageLoader.js'; +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +const params = { + lut: 'Bourbon 64.CUBE', + intensity: 1, +}; + +const lutMap = { + 'Bourbon 64.CUBE': null, + 'Chemical 168.CUBE': null, + 'Clayton 33.CUBE': null, + 'Cubicle 99.CUBE': null, + 'Remy 24.CUBE': null, + 'Presetpro-Cinematic.3dl': null, + NeutralLUT: null, + 'B&WLUT': null, + NightLUT: null, +}; + +let camera, scene, renderer, renderPipeline, controls, lutPass; + +init(); + +async function init() { + camera = new THREE.PerspectiveCamera(25, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(8, 10, 12); + + scene = new THREE.Scene(); + + // Loaders + + const gltfLoader = new GLTFLoader(); + const textureLoader = new THREE.TextureLoader(); + + // LUTs + + const lutCubeLoader = new LUTCubeLoader(); + const lutImageLoader = new LUTImageLoader(); + const lut3dlLoader = new LUT3dlLoader(); + + for (const name in lutMap) { + if (/\.CUBE$/i.test(name)) { + lutMap[name] = lutCubeLoader.loadAsync('luts/' + name); + } else if (/\LUT$/i.test(name)) { + lutMap[name] = lutImageLoader.loadAsync(`luts/${name}.png`); + } else { + lutMap[name] = lut3dlLoader.loadAsync('luts/' + name); + } + } + + const pendings = Object.values(lutMap); + await Promise.all(pendings); + + for (const name in lutMap) { + lutMap[name] = await lutMap[name]; + } + + // baked model + + gltfLoader.load('./models/gltf/coffeeMug.glb', gltf => { + gltf.scene.getObjectByName('baked').material.map.anisotropy = 8; + scene.add(gltf.scene); + }); + + // geometry + + const smokeGeometry = new THREE.PlaneGeometry(1, 1, 16, 64); + smokeGeometry.translate(0, 0.5, 0); + smokeGeometry.scale(1.5, 6, 1.5); + + // texture + + const noiseTexture = textureLoader.load('./textures/noises/perlin/128x128.png'); + noiseTexture.wrapS = THREE.RepeatWrapping; + noiseTexture.wrapT = THREE.RepeatWrapping; + + // material + + const smokeMaterial = new THREE.MeshBasicNodeMaterial({ + transparent: true, + side: THREE.DoubleSide, + depthWrite: false, + }); + + // position + + smokeMaterial.positionNode = Fn(() => { + // twist + + const twistNoiseUv = vec2(0.5, uv().y.mul(0.2).sub(time.mul(0.005)).mod(1)); + const twist = texture(noiseTexture, twistNoiseUv).r.mul(10); + positionLocal.xz.assign(rotateUV(positionLocal.xz, twist, vec2(0))); + + // wind + + const windOffset = vec2( + texture(noiseTexture, vec2(0.25, time.mul(0.01)).mod(1)).r.sub(0.5), + texture(noiseTexture, vec2(0.75, time.mul(0.01)).mod(1)).r.sub(0.5), + ).mul(uv().y.pow(2).mul(10)); + positionLocal.addAssign(windOffset); + + return positionLocal; + })(); + + // color + + smokeMaterial.colorNode = Fn(() => { + // alpha + + const alphaNoiseUv = uv() + .mul(vec2(0.5, 0.3)) + .add(vec2(0, time.mul(0.03).negate())); + const alpha = mul( + // pattern + texture(noiseTexture, alphaNoiseUv).r.smoothstep(0.4, 1), + + // edges fade + smoothstep(0, 0.1, uv().x), + smoothstep(0, 0.1, oneMinus(uv().x)), + smoothstep(0, 0.1, uv().y), + smoothstep(0, 0.1, oneMinus(uv().y)), + ); + + // color + + const finalColor = mix(vec3(0.6, 0.3, 0.2), vec3(1, 1, 1), alpha.pow(3)); + + return vec4(finalColor, alpha); + })(); + + // mesh + + const smoke = new THREE.Mesh(smokeGeometry, smokeMaterial); + smoke.position.y = 1.83; + scene.add(smoke); + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // post processing + + renderPipeline = new THREE.RenderPipeline(renderer); + + // ignore default output color transform ( toneMapping and outputColorSpace ) + // use renderOutput() for control the sequence + + renderPipeline.outputColorTransform = false; + + // scene pass + + const scenePass = pass(scene, camera); + const outputPass = renderOutput(scenePass); + + const lut = lutMap[params.lut]; + lutPass = lut3D(outputPass, texture3D(lut.texture3D), lut.texture3D.image.width, uniform(1)); + + renderPipeline.outputNode = lutPass; + + // controls + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + controls.minDistance = 0.1; + controls.maxDistance = 50; + controls.target.y = 3; + + // gui + + const gui = renderer.inspector.createParameters('Settings'); + gui.add(params, 'lut', Object.keys(lutMap)); + gui.add(params, 'intensity', 0, 1); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +async function animate() { + controls.update(); + + lutPass.intensityNode.value = params.intensity; + + if (lutMap[params.lut]) { + const lut = lutMap[params.lut]; + lutPass.lutNode.value = lut.texture3D; + lutPass.size.value = lut.texture3D.image.width; + } + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_postprocessing_afterimage.ts b/examples-testing/examples/webgpu_postprocessing_afterimage.ts new file mode 100644 index 000000000..2df8212f5 --- /dev/null +++ b/examples-testing/examples/webgpu_postprocessing_afterimage.ts @@ -0,0 +1,151 @@ +import * as THREE from 'three/webgpu'; +import { + instancedBufferAttribute, + uniform, + mod, + pass, + texture, + float, + time, + vec2, + vec3, + vec4, + sin, + cos, +} from 'three/tsl'; +import { afterImage } from 'three/addons/tsl/display/AfterImageNode.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer, particles; +let renderPipeline, afterImagePass, scenePass; + +const params = { + damp: uniform(0.8, 'float').setName('damp'), + enabled: true, +}; + +init(); + +function init() { + renderer = new THREE.WebGPURenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 10000); + camera.position.z = 1000; + + scene = new THREE.Scene(); + + const sprite = new THREE.TextureLoader().load('textures/sprites/circle.png'); + + // geometry + + const radius = 600; + const count = 50000; + + const vertex = new THREE.Vector3(); + const color = new THREE.Color(); + + const colors = []; + const vertices = []; + const timeOffsets = []; + + for (var i = 0; i < count; i++) { + getRandomPointOnSphere(radius, vertex); + vertices.push(vertex.x, vertex.y, vertex.z); + + color.setHSL(i / count, 0.7, 0.7, THREE.SRGBColorSpace); + colors.push(color.r, color.g, color.b); + + timeOffsets.push(i / count); + } + + const positionAttribute = new THREE.InstancedBufferAttribute(new Float32Array(vertices), 3); + const colorAttribute = new THREE.InstancedBufferAttribute(new Float32Array(colors), 3); + const timeAttribute = new THREE.InstancedBufferAttribute(new Float32Array(timeOffsets), 1); + + // material and TSL + + const material = new THREE.SpriteNodeMaterial({ blending: THREE.AdditiveBlending, depthWrite: false }); + + const localTime = instancedBufferAttribute(timeAttribute).add(time.mul(0.1)); + const modTime = mod(localTime, 1.0); + const accTime = modTime.mul(modTime); + + const angle = accTime.mul(40.0); + const pulse = vec2(sin(angle).mul(20.0), cos(angle).mul(20.0)); + const pos = instancedBufferAttribute(positionAttribute); + + const animated = vec3( + pos.x.mul(accTime).add(pulse.x), + pos.y.mul(accTime).add(pulse.y), + pos.z.mul(accTime).mul(1.75), + ); + const fAlpha = modTime.oneMinus().mul(2.0); + + material.colorNode = texture(sprite).mul(vec4(instancedBufferAttribute(colorAttribute), fAlpha)); + material.positionNode = animated; + material.scaleNode = float(2); + + particles = new THREE.Sprite(material); + particles.count = count; + scene.add(particles); + + // postprocessing + + renderPipeline = new THREE.RenderPipeline(renderer); + + scenePass = pass(scene, camera); + + afterImagePass = afterImage(scenePass, params.damp); + + renderPipeline.outputNode = afterImagePass; + + // + + const gui = renderer.inspector.createParameters('Settings'); + gui.add(afterImagePass.damp, 'value', 0.25, 1); + gui.add(params, 'enabled').onChange(updatePassChain); + + window.addEventListener('resize', onWindowResize); +} + +function updatePassChain() { + if (params.enabled === true) { + renderPipeline.outputNode = afterImagePass; + } else { + renderPipeline.outputNode = scenePass; + } + + renderPipeline.needsUpdate = true; +} + +function getRandomPointOnSphere(r, v) { + const angle = Math.random() * Math.PI * 2; + const u = Math.random() * 2 - 1; + + v.set( + Math.cos(angle) * Math.sqrt(1 - Math.pow(u, 2)) * r, + Math.sin(angle) * Math.sqrt(1 - Math.pow(u, 2)) * r, + u * r, + ); + + return v; +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate(time) { + particles.rotation.z = time * 0.001; + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_postprocessing_anamorphic.ts b/examples-testing/examples/webgpu_postprocessing_anamorphic.ts new file mode 100644 index 000000000..1cdb6893c --- /dev/null +++ b/examples-testing/examples/webgpu_postprocessing_anamorphic.ts @@ -0,0 +1,104 @@ +import * as THREE from 'three/webgpu'; +import { pass, cubeTexture, screenUV, grayscale, uniform } from 'three/tsl'; +import { anamorphic } from 'three/addons/tsl/display/AnamorphicNode.js'; + +import { HDRCubeTextureLoader } from 'three/addons/loaders/HDRCubeTextureLoader.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer; +let renderPipeline; + +const params = { + resolutionScale: 0.2, +}; + +init(); + +async function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); + camera.position.set(-1.8, -0.6, 2.7); + + scene = new THREE.Scene(); + + const hdrUrls = ['px.hdr', 'nx.hdr', 'py.hdr', 'ny.hdr', 'pz.hdr', 'nz.hdr']; + const cube1Texture = await new HDRCubeTextureLoader().setPath('./textures/cube/pisaHDR/').loadAsync(hdrUrls); + + scene.environment = cube1Texture; + scene.backgroundNode = grayscale( + cubeTexture(cube1Texture).mul(screenUV.distance(0.5).oneMinus().remapClamp(0.1, 4)), + ); + + const loader = new GLTFLoader().setPath('models/gltf/DamagedHelmet/glTF/'); + loader.load('DamagedHelmet.gltf', function (gltf) { + scene.add(gltf.scene); + }); + + renderer = new THREE.WebGPURenderer({ antialias: true }); + + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.toneMapping = THREE.LinearToneMapping; + renderer.toneMappingExposure = 1; + renderer.inspector = new Inspector(); + renderer.setAnimationLoop(render); + container.appendChild(renderer.domElement); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 2; + controls.maxDistance = 10; + + // post-processing + + const scenePass = pass(scene, camera); + + const threshold = uniform(1.4); + const scaleNode = uniform(5); + const intensity = uniform(1); + const samples = 64; + + const anamorphicPass = anamorphic( + scenePass.getTextureNode().toInspector('Color'), + threshold, + scaleNode, + samples, + ).toInspector('Anamorphic'); + anamorphicPass.resolutionScale = params.resolutionScale; // 1 = full resolution + + renderPipeline = new THREE.RenderPipeline(renderer); + renderPipeline.outputNode = scenePass.add(anamorphicPass.mul(intensity)); + //renderPipeline.outputNode = scenePass.add( anamorphicPass.getTextureNode().gaussianBlur() ); + + // gui + + const gui = renderer.inspector.createParameters('Settings'); + gui.add(intensity, 'value', 0, 4, 0.1).name('intensity'); + gui.add(threshold, 'value', 0.8, 3, 0.001).name('threshold'); + gui.add(scaleNode, 'value', 1, 10, 0.1).name('scale'); + gui.add(params, 'resolutionScale', 0.1, 1, 0.1) + .name('resolution scale') + .onChange(value => (anamorphicPass.resolutionScale = value)); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function render() { + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_postprocessing_ao.ts b/examples-testing/examples/webgpu_postprocessing_ao.ts new file mode 100644 index 000000000..76046bee8 --- /dev/null +++ b/examples-testing/examples/webgpu_postprocessing_ao.ts @@ -0,0 +1,210 @@ +import * as THREE from 'three/webgpu'; +import { + sample, + pass, + mrt, + screenUV, + normalView, + velocity, + vec3, + vec4, + directionToColor, + colorToDirection, + colorSpaceToWorking, + builtinAOContext, +} from 'three/tsl'; +import { ao } from 'three/addons/tsl/display/GTAONode.js'; +import { traa } from 'three/addons/tsl/display/TRAANode.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; +import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer, renderPipeline, controls; + +let aoPass, traaPass, transparentMesh; + +const params = { + samples: 16, + distanceExponent: 1, + distanceFallOff: 1, + radius: 0.25, + scale: 1, + thickness: 1, + aoOnly: false, + transparentOpacity: 0.3, +}; + +init(); + +async function init() { + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 50); + camera.position.set(1, 1.3, 5); + + scene = new THREE.Scene(); + + renderer = new THREE.WebGPURenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + await renderer.init(); + + // controls + + controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, 0.5, -1); + controls.update(); + controls.enablePan = false; + controls.enableDamping = true; + controls.minDistance = 2; + controls.maxDistance = 8; + + // environment + + const environment = new RoomEnvironment(); + const pmremGenerator = new THREE.PMREMGenerator(renderer); + + scene.background = new THREE.Color(0x666666); + scene.environment = pmremGenerator.fromScene(environment, 0.04).texture; + environment.dispose(); + pmremGenerator.dispose(); + + // post-processing + + renderPipeline = new THREE.RenderPipeline(renderer); + + // pre-pass + + const prePass = pass(scene, camera).toInspector('Normal', inspectNode => + colorSpaceToWorking(inspectNode, THREE.SRGBColorSpace), + ); + prePass.name = 'Pre-Pass'; + prePass.transparent = false; + + prePass.setMRT( + mrt({ + output: directionToColor(normalView), + velocity: velocity, + }), + ); + + const prePassNormal = sample(uv => { + return colorToDirection(prePass.getTextureNode().sample(uv)); + }); + + const prePassDepth = prePass.getTextureNode('depth').toInspector('Depth', () => prePass.getLinearDepthNode()); + const prePassVelocity = prePass.getTextureNode('velocity').toInspector('Velocity'); + + // pre-pass - bandwidth optimization + + const normalTexture = prePass.getTexture('output'); + normalTexture.type = THREE.UnsignedByteType; + + // scene pass + + const scenePass = pass(scene, camera).toInspector('Color'); + + // ao + + aoPass = ao(prePassDepth, prePassNormal, camera).toInspector('GTAO', inspectNode => inspectNode.r); + aoPass.resolutionScale = 0.5; // running AO in half resolution is often sufficient + aoPass.useTemporalFiltering = true; + + const aoPassOutput = aoPass.getTextureNode(); + + // scene context + + scenePass.contextNode = builtinAOContext(aoPassOutput.sample(screenUV).r); + + // final output + traa + + traaPass = traa(scenePass, prePassDepth, prePassVelocity, camera); + traaPass.useSubpixelCorrection = false; + + renderPipeline.outputNode = traaPass; + + // models + + const dracoLoader = new DRACOLoader(); + dracoLoader.setDecoderPath('jsm/libs/draco/'); + dracoLoader.setDecoderConfig({ type: 'js' }); + const loader = new GLTFLoader(); + loader.setDRACOLoader(dracoLoader); + loader.setPath('models/gltf/'); + + const gltf = await loader.loadAsync('minimalistic_modern_bedroom.glb'); + + const model = gltf.scene; + model.position.set(0, 1, 0); + scene.add(model); + + // + + transparentMesh = new THREE.Mesh( + new THREE.PlaneGeometry(1.8, 2), + new THREE.MeshStandardNodeMaterial({ transparent: true, opacity: params.transparentOpacity }), + ); + transparentMesh.position.z = 0; + transparentMesh.position.y = 0.5; + transparentMesh.visible = false; + scene.add(transparentMesh); + + // events + + window.addEventListener('resize', onWindowResize); + + // + + const gui = renderer.inspector.createParameters('Settings'); + gui.add(params, 'samples', 4, 32, 1).onChange(updateParameters); + gui.add(params, 'distanceExponent', 1, 2).onChange(updateParameters); + gui.add(params, 'distanceFallOff', 0.01, 1).onChange(updateParameters); + gui.add(params, 'radius', 0.1, 1).onChange(updateParameters); + gui.add(params, 'scale', 0.01, 2).onChange(updateParameters); + gui.add(params, 'thickness', 0.01, 2).onChange(updateParameters); + gui.add(aoPass, 'useTemporalFiltering').name('temporal filtering'); + gui.add(transparentMesh, 'visible').name('show transparent mesh'); + gui.add(params, 'transparentOpacity', 0, 1, 0.01).name('transparent opacity').onChange(updateParameters); + gui.add(params, 'aoOnly').onChange(value => { + if (value === true) { + renderPipeline.outputNode = vec4(vec3(aoPass.r), 1); + } else { + renderPipeline.outputNode = traaPass; + } + + renderPipeline.needsUpdate = true; + }); +} + +function updateParameters() { + aoPass.samples.value = params.samples; + aoPass.distanceExponent.value = params.distanceExponent; + aoPass.distanceFallOff.value = params.distanceFallOff; + aoPass.radius.value = params.radius; + aoPass.scale.value = params.scale; + aoPass.thickness.value = params.thickness; + + transparentMesh.material.opacity = params.transparentOpacity; +} + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); +} + +function animate() { + controls.update(); + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_postprocessing_bloom.ts b/examples-testing/examples/webgpu_postprocessing_bloom.ts new file mode 100644 index 000000000..3197d680d --- /dev/null +++ b/examples-testing/examples/webgpu_postprocessing_bloom.ts @@ -0,0 +1,120 @@ +import * as THREE from 'three/webgpu'; +import { pass } from 'three/tsl'; +import { bloom } from 'three/addons/tsl/display/BloomNode.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +let camera; +let renderPipeline, renderer, mixer, timer; + +const params = { + threshold: 0, + strength: 1, + radius: 0, + exposure: 1, +}; + +init(); + +async function init() { + timer = new THREE.Timer(); + timer.connect(document); + + const scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 100); + camera.position.set(-5, 2.5, -3.5); + scene.add(camera); + + scene.add(new THREE.AmbientLight(0xcccccc)); + + const pointLight = new THREE.PointLight(0xffffff, 100); + camera.add(pointLight); + + const loader = new GLTFLoader(); + const gltf = await loader.loadAsync('models/gltf/PrimaryIonDrive.glb'); + + const model = gltf.scene; + scene.add(model); + + mixer = new THREE.AnimationMixer(model); + const clip = gltf.animations[0]; + mixer.clipAction(clip.optimize()).play(); + + // + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.toneMapping = THREE.ReinhardToneMapping; + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // + + renderPipeline = new THREE.RenderPipeline(renderer); + + const scenePass = pass(scene, camera); + const scenePassColor = scenePass.getTextureNode('output').toInspector('Color'); + + const bloomPass = bloom(scenePassColor).toInspector('Bloom'); + + renderPipeline.outputNode = scenePassColor.add(bloomPass); + + // + + const controls = new OrbitControls(camera, renderer.domElement); + controls.maxPolarAngle = Math.PI * 0.5; + controls.minDistance = 3; + controls.maxDistance = 8; + + // + + const gui = renderer.inspector.createParameters('Settings'); + + const bloomFolder = gui.addFolder('bloom'); + + bloomFolder.add(params, 'threshold', 0.0, 1.0).onChange(function (value) { + bloomPass.threshold.value = value; + }); + + bloomFolder.add(params, 'strength', 0.0, 3.0).onChange(function (value) { + bloomPass.strength.value = value; + }); + + gui.add(params, 'radius', 0.0, 1.0, 0.01).onChange(function (value) { + bloomPass.radius.value = value; + }); + + const toneMappingFolder = gui.addFolder('tone mapping'); + + toneMappingFolder.add(params, 'exposure', 0.1, 2).onChange(function (value) { + renderer.toneMappingExposure = Math.pow(value, 4.0); + }); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); +} + +function animate() { + timer.update(); + + const delta = timer.getDelta(); + + mixer.update(delta); + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_postprocessing_bloom_emissive.ts b/examples-testing/examples/webgpu_postprocessing_bloom_emissive.ts new file mode 100644 index 000000000..faa5b67fb --- /dev/null +++ b/examples-testing/examples/webgpu_postprocessing_bloom_emissive.ts @@ -0,0 +1,115 @@ +import * as THREE from 'three/webgpu'; +import { pass, mrt, output, emissive, vec4 } from 'three/tsl'; +import { bloom } from 'three/addons/tsl/display/BloomNode.js'; + +import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer; +let renderPipeline; + +init(); + +function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + // + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); + camera.position.set(-1.8, 0.6, 2.7); + + scene = new THREE.Scene(); + + new HDRLoader().setPath('textures/equirectangular/').load('moonless_golf_1k.hdr', function (texture) { + texture.mapping = THREE.EquirectangularReflectionMapping; + + scene.background = texture; + scene.environment = texture; + + // model + + const loader = new GLTFLoader().setPath('models/gltf/DamagedHelmet/glTF/'); + loader.load('DamagedHelmet.gltf', function (gltf) { + scene.add(gltf.scene); + }); + }); + + // + + renderer = new THREE.WebGPURenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(render); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.inspector = new Inspector(); + container.appendChild(renderer.domElement); + + // + + const scenePass = pass(scene, camera); + + // set up MRT with emissive + + const mrtNode = mrt({ + output: output, + emissive: vec4(emissive, output.a), + }); + + mrtNode.setBlendMode('emissive', new THREE.BlendMode(THREE.NormalBlending)); + + scenePass.setMRT(mrtNode); + + // optimize the bandwidth + + const emissiveTexture = scenePass.getTexture('emissive'); + emissiveTexture.type = THREE.UnsignedByteType; + + // + + const outputPass = scenePass.getTextureNode().toInspector('Color'); + const emissivePass = scenePass.getTextureNode('emissive').toInspector('Emissive'); + + const bloomPass = bloom(emissivePass, 2.5, 0.5); + + renderPipeline = new THREE.RenderPipeline(renderer); + renderPipeline.outputNode = outputPass.add(bloomPass); + + // + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 2; + controls.maxDistance = 10; + controls.target.set(0, 0, -0.2); + controls.update(); + + window.addEventListener('resize', onWindowResize); + + // + + const gui = renderer.inspector.createParameters('Settings'); + + const bloomFolder = gui.addFolder('Bloom'); + bloomFolder.add(bloomPass.strength, 'value', 0.0, 5.0).name('strength'); + bloomFolder.add(bloomPass.radius, 'value', 0.0, 1.0).name('radius'); + + const toneMappingFolder = gui.addFolder('Tone Mapping'); + toneMappingFolder.add(renderer, 'toneMappingExposure', 0.1, 2).name('exposure'); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function render() { + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_postprocessing_bloom_selective.ts b/examples-testing/examples/webgpu_postprocessing_bloom_selective.ts new file mode 100644 index 000000000..b8f2427d4 --- /dev/null +++ b/examples-testing/examples/webgpu_postprocessing_bloom_selective.ts @@ -0,0 +1,124 @@ +import * as THREE from 'three/webgpu'; +import { pass, mrt, output, float, uniform } from 'three/tsl'; +import { bloom } from 'three/addons/tsl/display/BloomNode.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +// scene + +const scene = new THREE.Scene(); + +const camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 200); +camera.position.set(0, 0, 20); +camera.lookAt(0, 0, 0); + +const geometry = new THREE.IcosahedronGeometry(1, 15); + +for (let i = 0; i < 50; i++) { + const color = new THREE.Color(); + color.setHSL(Math.random(), 0.7, Math.random() * 0.2 + 0.05); + + const bloomIntensity = Math.random() > 0.5 ? 1 : 0; + + const material = new THREE.MeshBasicNodeMaterial({ color: color }); + material.mrtNode = mrt({ + bloomIntensity: uniform(bloomIntensity), + }); + + const sphere = new THREE.Mesh(geometry, material); + sphere.position.x = Math.random() * 10 - 5; + sphere.position.y = Math.random() * 10 - 5; + sphere.position.z = Math.random() * 10 - 5; + sphere.position.normalize().multiplyScalar(Math.random() * 4.0 + 2.0); + sphere.scale.setScalar(Math.random() * Math.random() + 0.5); + scene.add(sphere); +} + +// renderer + +const renderer = new THREE.WebGPURenderer(); +renderer.setPixelRatio(window.devicePixelRatio); +renderer.setSize(window.innerWidth, window.innerHeight); +renderer.setAnimationLoop(animate); +renderer.inspector = new Inspector(); +renderer.toneMapping = THREE.NeutralToneMapping; +document.body.appendChild(renderer.domElement); + +// post processing + +const scenePass = pass(scene, camera); +scenePass.setMRT( + mrt({ + output, + bloomIntensity: float(0), // default bloom intensity + }), +); + +const outputPass = scenePass.getTextureNode().toInspector('Color'); +const bloomIntensityPass = scenePass.getTextureNode('bloomIntensity').toInspector('Bloom Intensity'); + +const bloomPass = bloom(outputPass.mul(bloomIntensityPass)); + +const renderPipeline = new THREE.RenderPipeline(renderer); +renderPipeline.outputColorTransform = false; +renderPipeline.outputNode = outputPass.add(bloomPass).renderOutput(); + +// controls + +const controls = new OrbitControls(camera, renderer.domElement); +controls.maxPolarAngle = Math.PI * 0.5; +controls.minDistance = 1; +controls.maxDistance = 100; + +// raycaster + +const raycaster = new THREE.Raycaster(); +const mouse = new THREE.Vector2(); + +window.addEventListener('pointerdown', event => { + mouse.x = (event.clientX / window.innerWidth) * 2 - 1; + mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; + + raycaster.setFromCamera(mouse, camera); + + const intersects = raycaster.intersectObjects(scene.children, false); + + if (intersects.length > 0) { + const material = intersects[0].object.material; + + const bloomIntensity = material.mrtNode.get('bloomIntensity'); + bloomIntensity.value = bloomIntensity.value === 0 ? 1 : 0; + } +}); + +// gui + +const gui = renderer.inspector.createParameters('Settings'); + +const bloomFolder = gui.addFolder('Bloom'); +bloomFolder.add(bloomPass.threshold, 'value', 0.0, 1.0).name('threshold'); +bloomFolder.add(bloomPass.strength, 'value', 0.0, 3).name('strength'); +bloomFolder.add(bloomPass.radius, 'value', 0.0, 1.0).name('radius'); + +const toneMappingFolder = gui.addFolder('Tone Mapping'); +toneMappingFolder.add(renderer, 'toneMappingExposure', 0.1, 3).name('exposure'); + +// events + +window.onresize = function () { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); +}; + +// animate + +function animate() { + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_postprocessing_ca.ts b/examples-testing/examples/webgpu_postprocessing_ca.ts new file mode 100644 index 000000000..48b549050 --- /dev/null +++ b/examples-testing/examples/webgpu_postprocessing_ca.ts @@ -0,0 +1,266 @@ +import * as THREE from 'three/webgpu'; +import { pass, renderOutput, uniform } from 'three/tsl'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; +import { chromaticAberration } from 'three/addons/tsl/display/ChromaticAberrationNode.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +const params = { + enabled: true, + animated: true, + strength: 1.5, + center: new THREE.Vector2(0.5, 0.5), + scale: 1.2, + autoRotate: true, + cameraDistance: 40, +}; + +let camera, scene, renderer, timer, mainGroup; +let controls, renderPipeline; + +init(); + +async function init() { + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + await renderer.init(); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 200); + camera.position.set(0, 15, params.cameraDistance); + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + controls.dampingFactor = 0.1; + controls.autoRotate = true; + controls.autoRotateSpeed = -0.1; + controls.target.set(0, 0.5, 0); + controls.update(); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x0a0a0a); + + const pmremGenerator = new THREE.PMREMGenerator(renderer); + scene.environment = pmremGenerator.fromScene(new RoomEnvironment(), 0.04).texture; + + timer = new THREE.Timer(); + timer.connect(document); + + // Create main group + mainGroup = new THREE.Group(); + scene.add(mainGroup); + + // Create shapes + createShapes(); + + // Add a grid for reference + const gridHelper = new THREE.GridHelper(40, 20, 0x444444, 0x222222); + gridHelper.position.y = -10; + scene.add(gridHelper); + + // post processing + renderPipeline = new THREE.RenderPipeline(renderer); + renderPipeline.outputColorTransform = false; + + // scene pass + const scenePass = pass(scene, camera); + const outputPass = renderOutput(scenePass); + + // Create uniform nodes for the static version that can be updated + const staticStrength = uniform(params.strength); + const staticCenter = uniform(new THREE.Vector2(params.center.x, params.center.y)); + const staticScale = uniform(params.scale); + + // With static values (using uniform nodes) + const caPass = chromaticAberration(outputPass, staticStrength, staticCenter, staticScale); + + // Set initial output based on params + renderPipeline.outputNode = params.enabled ? caPass : outputPass; + + window.addEventListener('resize', onWindowResize); + + // GUI + + const gui = renderer.inspector.createParameters('Settings'); + + gui.add(params, 'enabled').onChange(value => { + renderPipeline.outputNode = value ? caPass : outputPass; + renderPipeline.needsUpdate = true; + }); + + const staticFolder = gui.addFolder('Static Parameters'); + + staticFolder.add(staticStrength, 'value', 0, 3).name('Strength'); + staticFolder.add(staticCenter.value, 'x', -1, 1).name('Center X'); + staticFolder.add(staticCenter.value, 'y', -1, 1).name('Center Y'); + staticFolder.add(staticScale, 'value', 0.5, 2).name('Scale'); + + const animationFolder = gui.addFolder('Animation'); + animationFolder.add(params, 'animated'); + animationFolder.add(params, 'autoRotate').onChange(value => { + controls.autoRotate = value; + }); +} + +function createShapes() { + const shapes = []; + const materials = []; + + // Define colors for different materials + const colors = [ + 0xff0000, // Red + 0x00ff00, // Green + 0x0000ff, // Blue + 0xffff00, // Yellow + 0xff00ff, // Magenta + 0x00ffff, // Cyan + 0xffffff, // White + 0xff8800, // Orange + ]; + + // Create materials + colors.forEach(color => { + materials.push( + new THREE.MeshStandardMaterial({ + color: color, + roughness: 0.2, + metalness: 0.8, + }), + ); + }); + + // Create geometries + const geometries = [ + new THREE.BoxGeometry(3, 3, 3), + new THREE.SphereGeometry(2, 32, 16), + new THREE.ConeGeometry(2, 4, 8), + new THREE.CylinderGeometry(1.5, 1.5, 4, 8), + new THREE.TorusGeometry(2, 0.8, 8, 16), + new THREE.OctahedronGeometry(2.5), + new THREE.IcosahedronGeometry(2.5), + new THREE.TorusKnotGeometry(1.5, 0.5, 64, 8), + ]; + + // Create central showcase + const centralGroup = new THREE.Group(); + + // Large central torus + const centralTorus = new THREE.Mesh( + new THREE.TorusGeometry(5, 1.5, 16, 32), + new THREE.MeshStandardMaterial({ + color: 0xffffff, + roughness: 0.1, + metalness: 1, + emissive: 0x222222, + }), + ); + centralGroup.add(centralTorus); + + // Inner rotating shapes + for (let i = 0; i < 6; i++) { + const angle = (i / 6) * Math.PI * 2; + const radius = 3; + + const mesh = new THREE.Mesh(geometries[i % geometries.length], materials[i % materials.length]); + + mesh.position.set(Math.cos(angle) * radius, 0, Math.sin(angle) * radius); + + mesh.scale.setScalar(0.5); + centralGroup.add(mesh); + shapes.push(mesh); + } + + mainGroup.add(centralGroup); + shapes.push(centralGroup); + + // Create outer ring of shapes + const numShapes = 12; + const outerRadius = 15; + + for (let i = 0; i < numShapes; i++) { + const angle = (i / numShapes) * Math.PI * 2; + const shapesGroup = new THREE.Group(); + + const geometry = geometries[i % geometries.length]; + const material = materials[i % materials.length]; + + const mesh = new THREE.Mesh(geometry, material); + mesh.castShadow = true; + mesh.receiveShadow = true; + + shapesGroup.add(mesh); + shapesGroup.position.set(Math.cos(angle) * outerRadius, Math.sin(i * 0.5) * 2, Math.sin(angle) * outerRadius); + + mainGroup.add(shapesGroup); + shapes.push(shapesGroup); + } + + // Add floating particles + const particlesGeometry = new THREE.BufferGeometry(); + const particlesCount = 200; + const positions = new Float32Array(particlesCount * 3); + + for (let i = 0; i < particlesCount * 3; i += 3) { + const radius = 25 + Math.random() * 10; + const theta = Math.random() * Math.PI * 2; + const phi = Math.random() * Math.PI; + + positions[i] = radius * Math.sin(phi) * Math.cos(theta); + positions[i + 1] = radius * Math.cos(phi); + positions[i + 2] = radius * Math.sin(phi) * Math.sin(theta); + } + + particlesGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); + + const particlesMaterial = new THREE.PointsMaterial({ + color: 0xffffff, + size: 0.5, + sizeAttenuation: true, + }); + + const particles = new THREE.Points(particlesGeometry, particlesMaterial); + mainGroup.add(particles); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + const time = timer.getElapsed(); + + controls.update(); + + if (params.animated) { + // Animate individual shapes + mainGroup.children.forEach((child, index) => { + if (child.children.length > 0) { + // Central group + child.rotation.y = time * 0.5; + child.children.forEach((subChild, subIndex) => { + if (subChild.geometry) { + subChild.rotation.x = time * (1 + subIndex * 0.1); + subChild.rotation.z = time * (1 - subIndex * 0.1); + } + }); + } else if (child.type === 'Group') { + // Outer shapes + child.rotation.x = time * 0.5 + index; + child.rotation.y = time * 0.3 + index; + child.position.y = Math.sin(time + index) * 2; + } + }); + } + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_postprocessing_difference.ts b/examples-testing/examples/webgpu_postprocessing_difference.ts new file mode 100644 index 000000000..91cc4cbf5 --- /dev/null +++ b/examples-testing/examples/webgpu_postprocessing_difference.ts @@ -0,0 +1,93 @@ +import * as THREE from 'three/webgpu'; +import { pass, luminance, saturation } from 'three/tsl'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +const params = { + speed: 0, +}; + +let camera, renderer, renderPipeline; +let timer, mesh, controls; + +init(); + +function init() { + renderer = new THREE.WebGPURenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + renderer.toneMapping = THREE.NeutralToneMapping; + document.body.appendChild(renderer.domElement); + + // + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 100); + camera.position.set(1, 2, 3); + + const scene = new THREE.Scene(); + scene.fog = new THREE.Fog(0x0487e2, 7, 25); + scene.background = new THREE.Color(0x0487e2); + + timer = new THREE.Timer(); + timer.connect(document); + + const texture = new THREE.TextureLoader().load('textures/crate.gif'); + texture.colorSpace = THREE.SRGBColorSpace; + + const geometry = new THREE.BoxGeometry(); + const material = new THREE.MeshBasicMaterial({ map: texture }); + + mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + // post processing + + renderPipeline = new THREE.RenderPipeline(renderer); + + const scenePass = pass(scene, camera); + + const currentTexture = scenePass.getTextureNode(); + const previousTexture = scenePass.getPreviousTextureNode(); + + const frameDiff = previousTexture.sub(currentTexture).abs(); + + const saturationAmount = luminance(frameDiff).mul(1000).clamp(0, 3); + + renderPipeline.outputNode = saturation(currentTexture, saturationAmount); + + // + + controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 2; + controls.maxDistance = 10; + controls.enableDamping = true; + controls.dampingFactor = 0.01; + + window.addEventListener('resize', onWindowResize); + + // + + const gui = renderer.inspector.createParameters('Settings'); + gui.add(params, 'speed', 0, 2); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + controls.update(); + + mesh.rotation.y += timer.getDelta() * 5 * params.speed; + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_postprocessing_dof.ts b/examples-testing/examples/webgpu_postprocessing_dof.ts new file mode 100644 index 000000000..862c2802b --- /dev/null +++ b/examples-testing/examples/webgpu_postprocessing_dof.ts @@ -0,0 +1,132 @@ +import * as THREE from 'three/webgpu'; +import { cubeTexture, positionWorld, oscSine, time, pass, uniform } from 'three/tsl'; +import { dof } from 'three/addons/tsl/display/DepthOfFieldNode.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +// + +let camera, scene, renderer, mesh, controls; + +let width = window.innerWidth; +let height = window.innerHeight; + +let renderPipeline; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(70, width / height, 1, 3500); + camera.position.z = 200; + + scene = new THREE.Scene(); + + const path = 'textures/cube/SwedishRoyalCastle/'; + const format = '.jpg'; + const urls = [ + path + 'px' + format, + path + 'nx' + format, + path + 'py' + format, + path + 'ny' + format, + path + 'pz' + format, + path + 'nz' + format, + ]; + + const xgrid = 14, + ygrid = 9, + zgrid = 14; + const count = xgrid * ygrid * zgrid; + + const textureCube = new THREE.CubeTextureLoader().load(urls); + const cubeTextureNode = cubeTexture(textureCube); + const oscPos = oscSine(positionWorld.div(1000 /* scene distance */).add(time.mul(0.2))); + + const geometry = new THREE.SphereGeometry(60, 20, 10); + const material = new THREE.MeshBasicNodeMaterial(); + material.colorNode = cubeTextureNode.mul(oscPos); + + mesh = new THREE.InstancedMesh(geometry, material, count); + scene.add(mesh); + + const matrix = new THREE.Matrix4(); + + let index = 0; + + for (let i = 0; i < xgrid; i++) { + for (let j = 0; j < ygrid; j++) { + for (let k = 0; k < zgrid; k++) { + const x = 200 * (i - xgrid / 2); + const y = 200 * (j - ygrid / 2); + const z = 200 * (k - zgrid / 2); + + mesh.setMatrixAt(index, matrix.identity().setPosition(x, y, z)); + index++; + } + } + } + + // renderer + + renderer = new THREE.WebGPURenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + const effectController = { + focusDistance: uniform(500), + focalLength: uniform(200), + bokehScale: uniform(10), + }; + + // post processing + + renderPipeline = new THREE.RenderPipeline(renderer); + + const scenePass = pass(scene, camera); + + const scenePassColor = scenePass.getTextureNode().toInspector('Color'); + const scenePassViewZ = scenePass.getViewZNode(); + + const dofPass = dof( + scenePassColor, + scenePassViewZ, + effectController.focusDistance, + effectController.focalLength, + effectController.bokehScale, + ); + + renderPipeline.outputNode = dofPass; + + // controls + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + + window.addEventListener('resize', onWindowResize); + + // gui + + const gui = renderer.inspector.createParameters('Settings'); + gui.add(effectController.focusDistance, 'value', 10.0, 3000.0).name('focus distance'); + gui.add(effectController.focalLength, 'value', 50, 750).name('focal length'); + gui.add(effectController.bokehScale, 'value', 1, 20).name('bokeh scale'); +} + +function onWindowResize() { + width = window.innerWidth; + height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); +} + +function animate() { + controls.update(); + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_postprocessing_dof_basic.ts b/examples-testing/examples/webgpu_postprocessing_dof_basic.ts new file mode 100644 index 000000000..3ad295247 --- /dev/null +++ b/examples-testing/examples/webgpu_postprocessing_dof_basic.ts @@ -0,0 +1,154 @@ +import * as THREE from 'three/webgpu'; +import { mix, pass, renderOutput, smoothstep, uniform, vec3 } from 'three/tsl'; +import { boxBlur } from 'three/addons/tsl/display/boxBlur.js'; +import { fxaa } from 'three/addons/tsl/display/FXAANode.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; +import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; +import TWEEN from 'three/addons/libs/tween.module.js'; + +let camera, controls, scene, timer, renderer, model, mixer, raycaster, renderPipeline; + +const pointerCoords = new THREE.Vector2(); +const focusPoint = new THREE.Vector3(1, 1.75, -0.4); +const focusPointView = uniform(vec3()); + +init(); + +async function init() { + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(-6, 5, 6); + + controls = new OrbitControls(camera); + controls.target.set(0, 2, 0); + controls.enableDamping = true; + controls.update(); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x90d5ff); + + raycaster = new THREE.Raycaster(); + + timer = new THREE.Timer(); + + const dracoLoader = new DRACOLoader(); + dracoLoader.setDecoderPath('jsm/libs/draco/gltf/'); + + const loader = new GLTFLoader(); + loader.setDRACOLoader(dracoLoader); + const gltf = await loader.loadAsync('models/gltf/bath_day.glb'); + + model = gltf.scene; + scene.add(model); + + mixer = new THREE.AnimationMixer(model); + const action = mixer.clipAction(gltf.animations[0]); + action.play(); + + // + + const hdrLoader = new UltraHDRLoader(); + const envMap = await hdrLoader.loadAsync('textures/equirectangular/spruit_sunrise_2k.hdr.jpg'); + envMap.mapping = THREE.EquirectangularReflectionMapping; + scene.environmentRotation.y = Math.PI * -0.5; + scene.environment = envMap; + + // renderer + + renderer = new THREE.WebGPURenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + renderer.toneMapping = THREE.NeutralToneMapping; + document.body.appendChild(renderer.domElement); + + // post processing + + renderPipeline = new THREE.RenderPipeline(renderer); + renderPipeline.outputColorTransform = false; + + // DOF uniforms + + const blurSize = uniform(2); // determines the kernel size of the blur + const blurSpread = uniform(4); // determines how far the blur is spread + const minDistance = uniform(1); // all positions at or below minDistance will be completely in focus. + const maxDistance = uniform(3); // all positions at or beyond maxDistance will be completely out of focus. + + // beauty and blur/out-of-focus pass + + const scenePass = pass(scene, camera); + + const scenePassColor = scenePass.getTextureNode().toInspector('Color'); + const scenePassViewZ = scenePass.getViewZNode(); + const scenePassBlurred = boxBlur(scenePassColor, { size: blurSize, separation: blurSpread }); + + // simple DOF from https://lettier.github.io/3d-game-shaders-for-beginners/depth-of-field.html + + const blur = smoothstep(minDistance, maxDistance, scenePassViewZ.sub(focusPointView.z).abs()); + const dofPass = mix(scenePassColor, scenePassBlurred, blur); + + const outputPass = renderOutput(dofPass); + const fxaaPass = fxaa(outputPass); + + renderPipeline.outputNode = fxaaPass; + + // GUI + + const gui = renderer.inspector.createParameters('Settings'); + gui.add(minDistance, 'value', 0, 3).name('min distance'); + gui.add(maxDistance, 'value', 0, 5).name('max distance'); + gui.add(blurSize, 'value', 1, 3, 1).name('blur size'); + gui.add(blurSpread, 'value', 1, 7, 1).name('blur spread'); + + // + + controls.connect(renderer.domElement); + + renderer.domElement.addEventListener('pointerdown', onPointerDown); + + window.addEventListener('resize', onWindowResize); +} + +function onPointerDown(event) { + pointerCoords.set((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1); + + raycaster.setFromCamera(pointerCoords, camera); + + const intersects = raycaster.intersectObject(model); + + if (intersects.length > 0) { + TWEEN.removeAll(); + + new TWEEN.Tween(focusPoint).to(intersects[0].point, 500).easing(TWEEN.Easing.Cubic.InOut).start(); + } +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + TWEEN.update(); + + controls.update(); + + timer.update(); + + mixer.update(timer.getDelta()); + + // since the focus point is expressed in view space, it must be updated on every + // camera change. for simplicity, do this every frame. + + camera.updateMatrixWorld(); + focusPointView.value.copy(focusPoint).applyMatrix4(camera.matrixWorldInverse); + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_postprocessing_fxaa.ts b/examples-testing/examples/webgpu_postprocessing_fxaa.ts new file mode 100644 index 000000000..24c01a950 --- /dev/null +++ b/examples-testing/examples/webgpu_postprocessing_fxaa.ts @@ -0,0 +1,129 @@ +import * as THREE from 'three/webgpu'; +import { pass, renderOutput } from 'three/tsl'; +import { fxaa } from 'three/addons/tsl/display/FXAANode.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +const params = { + enabled: true, + animated: false, +}; + +let camera, scene, renderer, timer, group; +let renderPipeline; + +init(); + +async function init() { + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 200); + camera.position.z = 50; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xffffff); + + timer = new THREE.Timer(); + timer.connect(document); + + // + + const hemiLight = new THREE.HemisphereLight(0xffffff, 0x8d8d8d); + hemiLight.position.set(0, 1000, 0); + scene.add(hemiLight); + + const dirLight = new THREE.DirectionalLight(0xffffff, 3); + dirLight.position.set(-3000, 1000, -1000); + scene.add(dirLight); + + // + + group = new THREE.Group(); + + const geometry = new THREE.TetrahedronGeometry(); + const material = new THREE.MeshStandardMaterial({ color: 0xf73232, flatShading: true }); + + const mesh = new THREE.InstancedMesh(geometry, material, 100); + const dummy = new THREE.Object3D(); + + for (let i = 0; i < 100; i++) { + dummy.position.x = Math.random() * 50 - 25; + dummy.position.y = Math.random() * 50 - 25; + dummy.position.z = Math.random() * 50 - 25; + + dummy.scale.setScalar(Math.random() * 2 + 1); + + dummy.rotation.x = Math.random() * Math.PI; + dummy.rotation.y = Math.random() * Math.PI; + dummy.rotation.z = Math.random() * Math.PI; + + dummy.updateMatrix(); + mesh.setMatrixAt(i, dummy.matrix); + } + + group.add(mesh); + scene.add(group); + + renderer = new THREE.WebGPURenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // post processing + + renderPipeline = new THREE.RenderPipeline(renderer); + + // ignore default output color transform ( toneMapping and outputColorSpace ) + // use renderOutput() for control the sequence + + renderPipeline.outputColorTransform = false; + + // scene pass + + const scenePass = pass(scene, camera).toInspector('Color'); + const outputPass = renderOutput(scenePass); + + // FXAA must be computed in sRGB color space (so after tone mapping and color space conversion) + + const fxaaPass = fxaa(outputPass); + renderPipeline.outputNode = fxaaPass; + + // + + window.addEventListener('resize', onWindowResize); + + // + + const gui = renderer.inspector.createParameters('Settings'); + gui.add(params, 'enabled').onChange(value => { + if (value === true) { + renderPipeline.outputNode = fxaaPass; + } else { + renderPipeline.outputNode = outputPass; + } + + renderPipeline.needsUpdate = true; + }); + gui.add(params, 'animated'); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + timer.update(); + + const delta = timer.getDelta(); + + if (params.animated === true) { + group.rotation.y += delta * 0.1; + } + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_postprocessing_godrays.ts b/examples-testing/examples/webgpu_postprocessing_godrays.ts new file mode 100644 index 000000000..32e619a47 --- /dev/null +++ b/examples-testing/examples/webgpu_postprocessing_godrays.ts @@ -0,0 +1,209 @@ +import * as THREE from 'three/webgpu'; +import { pass, uniform, float, int, color } from 'three/tsl'; +import { godrays } from 'three/addons/tsl/display/GodraysNode.js'; +import { bilateralBlur } from 'three/addons/tsl/display/BilateralBlurNode.js'; +import { depthAwareBlend } from 'three/addons/tsl/display/depthAwareBlend.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, controls, scene, renderer, renderPipeline; + +const params = { + enabledBlur: true, +}; + +init(); + +async function init() { + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000); + camera.position.set(-175, 50, 0); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x000000); + + // asset + + const loader = new GLTFLoader(); + const gltf = await loader.loadAsync('models/gltf/godrays_demo.glb'); + scene.add(gltf.scene); + + const pillars = gltf.scene.getObjectByName('concrete'); + pillars.material = new THREE.MeshStandardMaterial({ + color: 0x333333, + }); + + const base = gltf.scene.getObjectByName('base'); + base.material = new THREE.MeshStandardMaterial({ + color: 0x333333, + side: THREE.DoubleSide, + }); + + // lights + + const lightPos = new THREE.Vector3(0, 50, 0); + const lightSphereMaterial = new THREE.MeshBasicMaterial({ + color: 0xffffff, + }); + const lightSphere = new THREE.Mesh(new THREE.SphereGeometry(0.5, 16, 16), lightSphereMaterial); + lightSphere.position.copy(lightPos); + scene.add(lightSphere); + + scene.add(new THREE.AmbientLight(0xcccccc, 0.4)); + + const pointLight = new THREE.PointLight(0xf6287d, 10000); + pointLight.castShadow = true; + pointLight.shadow.bias = -0.00001; + pointLight.shadow.mapSize.width = 2048; + pointLight.shadow.mapSize.height = 2048; + pointLight.position.copy(lightPos); + scene.add(pointLight); + + setupBackdrop(); + + // shadow setup + + scene.traverse(obj => { + if (obj.isMesh === true) { + obj.castShadow = true; + obj.receiveShadow = true; + } + }); + + lightSphere.castShadow = false; + lightSphere.receiveShadow = false; + + // renderer + + renderer = new THREE.WebGPURenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + renderer.shadowMap.enabled = true; + document.body.appendChild(renderer.domElement); + + // post processing + + renderPipeline = new THREE.RenderPipeline(renderer); + + // beauty + + const scenePass = pass(scene, camera); + const scenePassColor = scenePass.getTextureNode('output'); + const scenePassDepth = scenePass.getTextureNode('depth'); + + // godrays + + const godraysPass = godrays(scenePassDepth, camera, pointLight); + const godraysPassColor = godraysPass.getTextureNode(); + + // blur + + const blurPass = bilateralBlur(godraysPassColor); + const blurPassColor = blurPass.getTextureNode(); + + // composite + + const blendColor = uniform(color(0xf6287d)); + const edgeRadius = uniform(int(2)); + const edgeStrength = uniform(float(2)); + + const outputBlurred = depthAwareBlend(scenePassColor, blurPassColor, scenePassDepth, camera, { + blendColor, + edgeRadius, + edgeStrength, + }); + const outputRaw = depthAwareBlend(scenePassColor, godraysPassColor, scenePassDepth, camera, { + blendColor, + edgeRadius, + edgeStrength, + }); + + renderPipeline.outputNode = outputBlurred; + + // GUI + + const gui = renderer.inspector.createParameters('Settings'); + const godraysFolder = gui.addFolder('Godrays'); + godraysFolder.add(godraysPass.raymarchSteps, 'value', 24, 120).step(1).name('raymarch steps'); + godraysFolder.add(godraysPass.density, 'value', 0, 1).name('density'); + godraysFolder.add(godraysPass.maxDensity, 'value', 0, 1).name('max density'); + godraysFolder.add(godraysPass.distanceAttenuation, 'value', 0, 5).name('distance attenuation'); + + const compositeFolder = gui.addFolder('composite'); + compositeFolder.add(edgeRadius, 'value', 0, 5, 1).name('edge radius'); + compositeFolder.add(edgeStrength, 'value', 0, 5).name('edge strength'); + + const blurFolder = gui.addFolder('blur'); + blurFolder + .add(params, 'enabledBlur') + .name('enabled') + .onChange(value => { + if (value === true) { + renderPipeline.outputNode = outputBlurred; + } else { + renderPipeline.outputNode = outputRaw; + } + + renderPipeline.needsUpdate = true; + }); + + // + + controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, 0.5, 0); + controls.enableDamping = true; + controls.maxDistance = 200; + controls.update(); + + window.addEventListener('resize', onWindowResize); +} + +function setupBackdrop() { + const backdropDistance = 200; + // Add backdrop walls `backdropDistance` units away from the origin + const backdropGeometry = new THREE.PlaneGeometry(400, 200); + const backdropMaterial = new THREE.MeshBasicMaterial({ + color: 0x000000, + side: THREE.DoubleSide, + }); + const backdropLeft = new THREE.Mesh(backdropGeometry, backdropMaterial); + backdropLeft.position.set(-backdropDistance, 100, 0); + backdropLeft.rotateY(Math.PI / 2); + scene.add(backdropLeft); + + const backdropRight = new THREE.Mesh(backdropGeometry, backdropMaterial); + backdropRight.position.set(backdropDistance, 100, 0); + backdropRight.rotateY(Math.PI / 2); + scene.add(backdropRight); + + const backdropFront = new THREE.Mesh(backdropGeometry, backdropMaterial); + backdropFront.position.set(0, 100, -backdropDistance); + scene.add(backdropFront); + + const backdropBack = new THREE.Mesh(backdropGeometry, backdropMaterial); + backdropBack.position.set(0, 100, backdropDistance); + scene.add(backdropBack); + + const backdropTop = new THREE.Mesh(backdropGeometry, backdropMaterial); + backdropTop.position.set(0, 200, 0); + backdropTop.rotateX(Math.PI / 2); + backdropTop.scale.set(3, 6, 1); + scene.add(backdropTop); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + controls.update(); + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_postprocessing_lensflare.ts b/examples-testing/examples/webgpu_postprocessing_lensflare.ts new file mode 100644 index 000000000..39b1e5f6e --- /dev/null +++ b/examples-testing/examples/webgpu_postprocessing_lensflare.ts @@ -0,0 +1,135 @@ +import * as THREE from 'three/webgpu'; +import { pass, mrt, output, emissive, uniform } from 'three/tsl'; +import { bloom } from 'three/addons/tsl/display/BloomNode.js'; +import { lensflare } from 'three/addons/tsl/display/LensflareNode.js'; +import { gaussianBlur } from 'three/addons/tsl/display/GaussianBlurNode.js'; + +import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer, controls; +let renderPipeline; + +init(); + +async function init() { + // + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(0, 0.5, -0.5); + + scene = new THREE.Scene(); + + const texture = await new UltraHDRLoader().loadAsync('textures/equirectangular/ice_planet_close.jpg'); + + texture.mapping = THREE.EquirectangularReflectionMapping; + + scene.background = texture; + scene.environment = texture; + + scene.backgroundIntensity = 2; + scene.environmentIntensity = 15; + + // model + + const loader = new GLTFLoader(); + const gltf = await loader.loadAsync('models/gltf/space_ship_hallway.glb'); + + const object = gltf.scene; + + const aabb = new THREE.Box3().setFromObject(object); + const center = aabb.getCenter(new THREE.Vector3()); + + object.position.x += object.position.x - center.x; + object.position.y += object.position.y - center.y; + object.position.z += object.position.z - center.z; + + scene.add(object); + + // + + renderer = new THREE.WebGPURenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(render); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // + + const scenePass = pass(scene, camera); + scenePass.setMRT( + mrt({ + output, + emissive, + }), + ); + + const outputPass = scenePass.getTextureNode().toInspector('Color'); + const emissivePass = scenePass.getTextureNode('emissive').toInspector('Emissive'); + + const bloomPass = bloom(emissivePass, 1, 1).toInspector('Bloom'); + + const threshold = uniform(0.5); + const ghostAttenuationFactor = uniform(25); + const ghostSpacing = uniform(0.25); + + const flarePass = lensflare(bloomPass, { + threshold, + ghostAttenuationFactor, + ghostSpacing, + }); + + const blurPass = gaussianBlur(flarePass, 8); // optional (blurring produces better flare quality but also adds some overhead) + + renderPipeline = new THREE.RenderPipeline(renderer); + renderPipeline.outputNode = outputPass.add(bloomPass).add(blurPass); + + // + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + controls.enablePan = false; + controls.enableZoom = false; + controls.target.copy(camera.position); + controls.target.z -= 0.01; + controls.update(); + + window.addEventListener('resize', onWindowResize); + + // + + const gui = renderer.inspector.createParameters('Settings'); + + const bloomFolder = gui.addFolder('bloom'); + bloomFolder.add(bloomPass.strength, 'value', 0.0, 2.0).name('strength'); + bloomFolder.add(bloomPass.radius, 'value', 0.0, 1.0).name('radius'); + + const lensflareFolder = gui.addFolder('lensflare'); + lensflareFolder.add(threshold, 'value', 0.0, 1.0).name('threshold'); + lensflareFolder.add(ghostAttenuationFactor, 'value', 10.0, 50.0).name('attenuation'); + lensflareFolder.add(ghostSpacing, 'value', 0.0, 0.3).name('spacing'); + + const toneMappingFolder = gui.addFolder('tone mapping'); + toneMappingFolder.add(renderer, 'toneMappingExposure', 0.1, 2).name('exposure'); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function render() { + controls.update(); + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_postprocessing_masking.ts b/examples-testing/examples/webgpu_postprocessing_masking.ts new file mode 100644 index 000000000..b234abc66 --- /dev/null +++ b/examples-testing/examples/webgpu_postprocessing_masking.ts @@ -0,0 +1,88 @@ +import * as THREE from 'three/webgpu'; +import { pass, texture } from 'three/tsl'; +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, renderPipeline, renderer; +let box, torus; + +init(); + +function init() { + // scene + + const baseScene = new THREE.Scene(); + baseScene.background = new THREE.Color(0xe0e0e0); + + const maskScene1 = new THREE.Scene(); + box = new THREE.Mesh(new THREE.BoxGeometry(4, 4, 4)); + maskScene1.add(box); + + const maskScene2 = new THREE.Scene(); + torus = new THREE.Mesh(new THREE.TorusGeometry(3, 1, 16, 32)); + maskScene2.add(torus); + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.z = 10; + + // textures + + const texture1 = new THREE.TextureLoader().load('textures/758px-Canestra_di_frutta_(Caravaggio).jpg'); + texture1.colorSpace = THREE.SRGBColorSpace; + texture1.minFilter = THREE.LinearFilter; + texture1.generateMipmaps = false; + texture1.flipY = false; + + const texture2 = new THREE.TextureLoader().load('textures/2294472375_24a3b8ef46_o.jpg'); + texture2.colorSpace = THREE.SRGBColorSpace; + texture2.flipY = false; + + // renderer + + renderer = new THREE.WebGPURenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + window.addEventListener('resize', onWindowResize); + + // post processing + + const base = pass(baseScene, camera); + const sceneMask1 = pass(maskScene1, camera).a; + const sceneMask2 = pass(maskScene2, camera).a; + + let compose = base; + compose = sceneMask1.mix(compose, texture(texture1)); + compose = sceneMask2.mix(compose, texture(texture2)); + + renderPipeline = new THREE.RenderPipeline(renderer); + renderPipeline.outputNode = compose; +} + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); +} + +function animate() { + const time = performance.now() * 0.001 + 6000; + + box.position.x = Math.cos(time / 1.5) * 2; + box.position.y = Math.sin(time) * 2; + box.rotation.x = time; + box.rotation.y = time / 2; + + torus.position.x = Math.cos(time) * 2; + torus.position.y = Math.sin(time / 1.5) * 2; + torus.rotation.x = time; + torus.rotation.y = time / 2; + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_postprocessing_motion_blur.ts b/examples-testing/examples/webgpu_postprocessing_motion_blur.ts new file mode 100644 index 000000000..5ae3d9661 --- /dev/null +++ b/examples-testing/examples/webgpu_postprocessing_motion_blur.ts @@ -0,0 +1,199 @@ +import * as THREE from 'three/webgpu'; +import { pass, texture, uniform, output, mrt, velocity, uv, screenUV } from 'three/tsl'; +import { motionBlur } from 'three/addons/tsl/display/MotionBlur.js'; + +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer; +let boxLeft, boxRight, model, mixer, timer; +let renderPipeline; +let controls; + +const params = { + speed: 1.0, +}; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.25, 30); + camera.position.set(0, 1.5, 4.5); + + scene = new THREE.Scene(); + scene.fog = new THREE.Fog(0x0487e2, 7, 25); + + const sunLight = new THREE.DirectionalLight(0xffe499, 5); + sunLight.castShadow = true; + sunLight.shadow.camera.near = 0.1; + sunLight.shadow.camera.far = 10; + sunLight.shadow.camera.right = 2; + sunLight.shadow.camera.left = -2; + sunLight.shadow.camera.top = 2; + sunLight.shadow.camera.bottom = -2; + sunLight.shadow.mapSize.width = 1024; + sunLight.shadow.mapSize.height = 1024; + sunLight.position.set(4, 4, 2); + + const waterAmbientLight = new THREE.HemisphereLight(0x333366, 0x74ccf4, 5); + const skyAmbientLight = new THREE.HemisphereLight(0x74ccf4, 0, 1); + + scene.add(sunLight); + scene.add(skyAmbientLight); + scene.add(waterAmbientLight); + + timer = new THREE.Timer(); + timer.connect(document); + + // animated model + + const loader = new GLTFLoader(); + loader.load('models/gltf/Xbot.glb', function (gltf) { + model = gltf.scene; + + model.rotation.y = Math.PI / 2; + + model.traverse(function (child) { + if (child.isMesh) { + child.castShadow = true; + child.receiveShadow = true; + } + }); + + mixer = new THREE.AnimationMixer(model); + + const action = mixer.clipAction(gltf.animations[3]); + action.play(); + + scene.add(model); + }); + + // textures + + const textureLoader = new THREE.TextureLoader(); + + const floorColor = textureLoader.load('textures/floors/FloorsCheckerboard_S_Diffuse.jpg'); + floorColor.wrapS = THREE.RepeatWrapping; + floorColor.wrapT = THREE.RepeatWrapping; + floorColor.colorSpace = THREE.SRGBColorSpace; + + const floorNormal = textureLoader.load('textures/floors/FloorsCheckerboard_S_Normal.jpg'); + floorNormal.wrapS = THREE.RepeatWrapping; + floorNormal.wrapT = THREE.RepeatWrapping; + + // floor + + const floorUV = uv().mul(5); + + const floorMaterial = new THREE.MeshPhongNodeMaterial(); + floorMaterial.colorNode = texture(floorColor, floorUV); + + const floor = new THREE.Mesh(new THREE.BoxGeometry(15, 0.001, 15), floorMaterial); + floor.receiveShadow = true; + + floor.position.set(0, 0, 0); + scene.add(floor); + + const walls = new THREE.Mesh( + new THREE.BoxGeometry(15, 15, 15), + new THREE.MeshPhongNodeMaterial({ colorNode: floorMaterial.colorNode, side: THREE.BackSide }), + ); + scene.add(walls); + + const map = new THREE.TextureLoader().load('textures/uv_grid_opengl.jpg'); + map.colorSpace = THREE.SRGBColorSpace; + + const geometry = new THREE.TorusGeometry(0.8); + const material = new THREE.MeshBasicMaterial({ map }); + + boxRight = new THREE.Mesh(geometry, material); + boxRight.position.set(3.5, 1.5, -4); + scene.add(boxRight); + + boxLeft = new THREE.Mesh(geometry, material); + boxLeft.position.set(-3.5, 1.5, -4); + scene.add(boxLeft); + + // renderer + + renderer = new THREE.WebGPURenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.shadowMap.enabled = true; + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 1; + controls.maxDistance = 10; + controls.maxPolarAngle = Math.PI / 2; + controls.autoRotate = true; + controls.autoRotateSpeed = 1; + controls.target.set(0, 1, 0); + controls.enableDamping = true; + controls.dampingFactor = 0.05; + controls.update(); + + // post-processing + + const blurAmount = uniform(1); + + const scenePass = pass(scene, camera); + + scenePass.setMRT( + mrt({ + output, + velocity, + }), + ); + + const beauty = scenePass.getTextureNode().toInspector('Color'); + const vel = scenePass.getTextureNode('velocity').toInspector('Velocity').mul(blurAmount); + + const mBlur = motionBlur(beauty, vel); + + const vignette = screenUV.distance(0.5).remap(0.6, 1).mul(2).clamp().oneMinus(); + + renderPipeline = new THREE.RenderPipeline(renderer); + renderPipeline.outputNode = mBlur.mul(vignette); + + // + + const gui = renderer.inspector.createParameters('Motion Blur Settings'); + gui.add(controls, 'autoRotate'); + gui.add(blurAmount, 'value', 0, 3).name('blur amount'); + gui.add(params, 'speed', 0, 2); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + controls.update(); + + const delta = timer.getDelta(); + const speed = params.speed; + + boxRight.rotation.y += delta * 4 * speed; + boxLeft.scale.setScalar(1 + Math.sin(timer.getElapsed() * 10 * speed) * 0.2); + + if (model) { + mixer.update(delta * speed); + } + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_postprocessing_outline.ts b/examples-testing/examples/webgpu_postprocessing_outline.ts new file mode 100644 index 000000000..f87415b74 --- /dev/null +++ b/examples-testing/examples/webgpu_postprocessing_outline.ts @@ -0,0 +1,232 @@ +import * as THREE from 'three/webgpu'; +import { pass, uniform, time, oscSine } from 'three/tsl'; +import { outline } from 'three/addons/tsl/display/OutlineNode.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { OBJLoader } from 'three/addons/loaders/OBJLoader.js'; + +let camera, scene, renderer, controls; +let renderPipeline, outlinePass; + +let selectedObjects = []; + +const raycaster = new THREE.Raycaster(); +const mouse = new THREE.Vector2(); + +const obj3d = new THREE.Object3D(); +const group = new THREE.Group(); + +init(); + +function init() { + const width = window.innerWidth; + const height = window.innerHeight; + + renderer = new THREE.WebGPURenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(width, height); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + renderer.shadowMap.enabled = true; + document.body.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 100); + camera.position.set(0, 0, 8); + + controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 5; + controls.maxDistance = 20; + controls.enablePan = false; + controls.enableDamping = true; + controls.dampingFactor = 0.05; + + // + + scene.add(new THREE.AmbientLight(0xaaaaaa, 0.6)); + + const light = new THREE.DirectionalLight(0xddffdd, 2); + light.position.set(5, 5, 5); + light.castShadow = true; + light.shadow.mapSize.width = 2048; + light.shadow.mapSize.height = 2048; + + const d = 10; + + light.shadow.camera.left = -d; + light.shadow.camera.right = d; + light.shadow.camera.top = d; + light.shadow.camera.bottom = -d; + light.shadow.camera.far = 25; + + scene.add(light); + + // model + + const loader = new OBJLoader(); + loader.load('models/obj/tree.obj', function (object) { + let scale = 1.0; + + object.traverse(function (child) { + if (child instanceof THREE.Mesh) { + child.geometry.center(); + child.geometry.computeBoundingSphere(); + scale = 0.2 * child.geometry.boundingSphere.radius; + + const phongMaterial = new THREE.MeshPhongMaterial({ + color: 0xffffff, + specular: 0x111111, + shininess: 5, + }); + child.material = phongMaterial; + child.receiveShadow = true; + child.castShadow = true; + } + }); + + object.position.y = 1; + object.scale.divideScalar(scale); + obj3d.add(object); + }); + + scene.add(group); + + group.add(obj3d); + + // + + const geometry = new THREE.SphereGeometry(3, 48, 24); + + for (let i = 0; i < 20; i++) { + const material = new THREE.MeshLambertMaterial(); + material.color.setHSL(Math.random(), 1.0, 0.3); + + const mesh = new THREE.Mesh(geometry, material); + mesh.position.x = Math.random() * 4 - 2; + mesh.position.y = Math.random() * 4 - 2; + mesh.position.z = Math.random() * 4 - 2; + mesh.receiveShadow = true; + mesh.castShadow = true; + mesh.scale.multiplyScalar(Math.random() * 0.3 + 0.1); + group.add(mesh); + } + + const floorMaterial = new THREE.MeshLambertMaterial({ side: THREE.DoubleSide }); + + const floorGeometry = new THREE.PlaneGeometry(12, 12); + const floorMesh = new THREE.Mesh(floorGeometry, floorMaterial); + floorMesh.rotation.x -= Math.PI * 0.5; + floorMesh.position.y -= 1.5; + group.add(floorMesh); + floorMesh.receiveShadow = true; + + const torusGeometry = new THREE.TorusGeometry(1, 0.3, 16, 100); + const torusMaterial = new THREE.MeshPhongMaterial({ color: 0xffaaff }); + const torus = new THREE.Mesh(torusGeometry, torusMaterial); + torus.position.z = -4; + group.add(torus); + torus.receiveShadow = true; + torus.castShadow = true; + + // outline pass + + const edgeStrength = uniform(3.0); + const edgeGlow = uniform(0.0); + const edgeThickness = uniform(1.0); + const pulsePeriod = uniform(0); + const visibleEdgeColor = uniform(new THREE.Color(0xffffff)); + const hiddenEdgeColor = uniform(new THREE.Color(0x4e3636)); + + outlinePass = outline(scene, camera, { + selectedObjects, + edgeGlow, + edgeThickness, + }); + + const { visibleEdge, hiddenEdge } = outlinePass; + + const period = time.div(pulsePeriod).mul(2); + const osc = oscSine(period).mul(0.5).add(0.5); // osc [ 0.5, 1.0 ] + + const outlineColor = visibleEdge.mul(visibleEdgeColor).add(hiddenEdge.mul(hiddenEdgeColor)).mul(edgeStrength); + const outlinePulse = pulsePeriod.greaterThan(0).select(outlineColor.mul(osc), outlineColor); + + // postprocessing + + const scenePass = pass(scene, camera).toInspector('Color'); + + renderPipeline = new THREE.RenderPipeline(renderer); + renderPipeline.outputNode = outlinePulse.add(scenePass); + + // gui + + const gui = renderer.inspector.createParameters('Settings'); + gui.add(edgeStrength, 'value', 0.01, 10).name('edgeStrength'); + gui.add(edgeGlow, 'value', 0.0, 1).name('edgeGlow'); + gui.add(edgeThickness, 'value', 1, 4).name('edgeThickness'); + gui.add(pulsePeriod, 'value', 0.0, 5).name('pulsePeriod'); + gui.addColor({ color: visibleEdgeColor.value.getHex(THREE.SRGBColorSpace) }, 'color') + .onChange(value => { + visibleEdgeColor.value.set(value); + }) + .name('visibleEdgeColor'); + gui.addColor({ color: hiddenEdgeColor.value.getHex(THREE.SRGBColorSpace) }, 'color') + .onChange(value => { + hiddenEdgeColor.value.set(value); + }) + .name('hiddenEdgeColor'); + + // + + window.addEventListener('resize', onWindowResize); + + renderer.domElement.style.touchAction = 'none'; + renderer.domElement.addEventListener('pointermove', onPointerMove); + + function onPointerMove(event) { + if (event.isPrimary === false) return; + + mouse.x = (event.clientX / window.innerWidth) * 2 - 1; + mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; + + checkIntersection(); + } + + function addSelectedObject(object) { + selectedObjects = []; + selectedObjects.push(object); + } + + function checkIntersection() { + raycaster.setFromCamera(mouse, camera); + + const intersects = raycaster.intersectObject(scene, true); + + if (intersects.length > 0) { + const selectedObject = intersects[0].object; + addSelectedObject(selectedObject); + outlinePass.selectedObjects = selectedObjects; + } else { + // outlinePass.selectedObjects = []; + } + } +} + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); +} + +function animate() { + controls.update(); + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_postprocessing_pixel.ts b/examples-testing/examples/webgpu_postprocessing_pixel.ts new file mode 100644 index 000000000..a3d58f203 --- /dev/null +++ b/examples-testing/examples/webgpu_postprocessing_pixel.ts @@ -0,0 +1,237 @@ +import * as THREE from 'three/webgpu'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { uniform } from 'three/tsl'; +import { pixelationPass } from 'three/addons/tsl/display/PixelationPassNode.js'; + +let camera, scene, renderer, renderPipeline, crystalMesh, timer; +let effectController; + +init(); + +function init() { + const aspectRatio = window.innerWidth / window.innerHeight; + + camera = new THREE.OrthographicCamera(-aspectRatio, aspectRatio, 1, -1, 0.1, 10); + camera.position.y = 2 * Math.tan(Math.PI / 6); + camera.position.z = 2; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x151729); + + timer = new THREE.Timer(); + timer.connect(document); + + // textures + + const loader = new THREE.TextureLoader(); + const texChecker = pixelTexture(loader.load('textures/checker.png')); + const texChecker2 = pixelTexture(loader.load('textures/checker.png')); + texChecker.repeat.set(3, 3); + texChecker2.repeat.set(1.5, 1.5); + + // meshes + + const boxMaterial = new THREE.MeshPhongMaterial({ map: texChecker2 }); + + function addBox(boxSideLength, x, z, rotation) { + const mesh = new THREE.Mesh(new THREE.BoxGeometry(boxSideLength, boxSideLength, boxSideLength), boxMaterial); + mesh.castShadow = true; + mesh.receiveShadow = true; + mesh.rotation.y = rotation; + mesh.position.y = boxSideLength / 2; + mesh.position.set(x, boxSideLength / 2 + 0.0001, z); + scene.add(mesh); + return mesh; + } + + addBox(0.4, 0, 0, Math.PI / 4); + addBox(0.5, -0.5, -0.5, Math.PI / 4); + + const planeSideLength = 2; + const planeMesh = new THREE.Mesh( + new THREE.PlaneGeometry(planeSideLength, planeSideLength), + new THREE.MeshPhongMaterial({ map: texChecker }), + ); + planeMesh.receiveShadow = true; + planeMesh.rotation.x = -Math.PI / 2; + scene.add(planeMesh); + + const radius = 0.2; + const geometry = new THREE.IcosahedronGeometry(radius); + crystalMesh = new THREE.Mesh( + geometry, + new THREE.MeshPhongMaterial({ + color: 0x68b7e9, + emissive: 0x4f7e8b, + shininess: 10, + specular: 0xffffff, + }), + ); + crystalMesh.receiveShadow = true; + crystalMesh.castShadow = true; + scene.add(crystalMesh); + + // lights + + scene.add(new THREE.AmbientLight(0x757f8e, 3)); + + const directionalLight = new THREE.DirectionalLight(0xfffecd, 1.5); + directionalLight.position.set(100, 100, 100); + directionalLight.castShadow = true; + directionalLight.shadow.mapSize.set(2048, 2048); + scene.add(directionalLight); + + const spotLight = new THREE.SpotLight(0xffc100, 10, 10, Math.PI / 16, 0.02, 2); + spotLight.position.set(2, 2, 0); + const target = spotLight.target; + scene.add(target); + target.position.set(0, 0, 0); + spotLight.castShadow = true; + scene.add(spotLight); + + renderer = new THREE.WebGPURenderer(); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + renderer.shadowMap.enabled = true; + renderer.shadowMap.type = THREE.BasicShadowMap; + document.body.appendChild(renderer.domElement); + + effectController = { + pixelSize: uniform(6), + normalEdgeStrength: uniform(0.3), + depthEdgeStrength: uniform(0.4), + pixelAlignedPanning: true, + }; + + renderPipeline = new THREE.RenderPipeline(renderer); + const scenePass = pixelationPass( + scene, + camera, + effectController.pixelSize, + effectController.normalEdgeStrength, + effectController.depthEdgeStrength, + ); + renderPipeline.outputNode = scenePass; + + window.addEventListener('resize', onWindowResize); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.maxZoom = 2; + + // gui + + const gui = renderer.inspector.createParameters('Settings'); + gui.add(effectController.pixelSize, 'value', 1, 16, 1).name('Pixel Size'); + gui.add(effectController.normalEdgeStrength, 'value', 0, 2, 0.05).name('Normal Edge Strength'); + gui.add(effectController.depthEdgeStrength, 'value', 0, 1, 0.05).name('Depth Edge Strength'); + gui.add(effectController, 'pixelAlignedPanning'); +} + +function onWindowResize() { + const aspectRatio = window.innerWidth / window.innerHeight; + camera.left = -aspectRatio; + camera.right = aspectRatio; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + const t = timer.getElapsed(); + + crystalMesh.material.emissiveIntensity = Math.sin(t * 3) * 0.5 + 0.5; + crystalMesh.position.y = 0.7 + Math.sin(t * 2) * 0.05; + crystalMesh.rotation.y = stopGoEased(t, 2, 4) * 2 * Math.PI; + + const rendererSize = renderer.getSize(new THREE.Vector2()); + const aspectRatio = rendererSize.x / rendererSize.y; + + if (effectController.pixelAlignedPanning) { + const pixelSize = effectController.pixelSize.value; + + pixelAlignFrustum( + camera, + aspectRatio, + Math.floor(rendererSize.x / pixelSize), + Math.floor(rendererSize.y / pixelSize), + ); + } else if (camera.left != -aspectRatio || camera.top != 1.0) { + // Reset the Camera Frustum if it has been modified + camera.left = -aspectRatio; + camera.right = aspectRatio; + camera.top = 1.0; + camera.bottom = -1.0; + camera.updateProjectionMatrix(); + } + + renderPipeline.render(); +} + +// Helper functions + +function pixelTexture(texture) { + texture.minFilter = THREE.NearestFilter; + texture.magFilter = THREE.NearestFilter; + texture.generateMipmaps = false; + texture.wrapS = THREE.RepeatWrapping; + texture.wrapT = THREE.RepeatWrapping; + texture.colorSpace = THREE.SRGBColorSpace; + return texture; +} + +function easeInOutCubic(x) { + return x ** 2 * 3 - x ** 3 * 2; +} + +function linearStep(x, edge0, edge1) { + const w = edge1 - edge0; + const m = 1 / w; + const y0 = -m * edge0; + return THREE.MathUtils.clamp(y0 + m * x, 0, 1); +} + +function stopGoEased(x, downtime, period) { + const cycle = (x / period) | 0; + const tween = x - cycle * period; + const linStep = easeInOutCubic(linearStep(tween, downtime, period)); + return cycle + linStep; +} + +function pixelAlignFrustum(camera, aspectRatio, pixelsPerScreenWidth, pixelsPerScreenHeight) { + // 0. Get Pixel Grid Units + const worldScreenWidth = (camera.right - camera.left) / camera.zoom; + const worldScreenHeight = (camera.top - camera.bottom) / camera.zoom; + const pixelWidth = worldScreenWidth / pixelsPerScreenWidth; + const pixelHeight = worldScreenHeight / pixelsPerScreenHeight; + + // 1. Project the current camera position along its local rotation bases + const camPos = new THREE.Vector3(); + camera.getWorldPosition(camPos); + const camRot = new THREE.Quaternion(); + camera.getWorldQuaternion(camRot); + const camRight = new THREE.Vector3(1.0, 0.0, 0.0).applyQuaternion(camRot); + const camUp = new THREE.Vector3(0.0, 1.0, 0.0).applyQuaternion(camRot); + const camPosRight = camPos.dot(camRight); + const camPosUp = camPos.dot(camUp); + + // 2. Find how far along its position is along these bases in pixel units + const camPosRightPx = camPosRight / pixelWidth; + const camPosUpPx = camPosUp / pixelHeight; + + // 3. Find the fractional pixel units and convert to world units + const fractX = camPosRightPx - Math.round(camPosRightPx); + const fractY = camPosUpPx - Math.round(camPosUpPx); + + // 4. Add fractional world units to the left/right top/bottom to align with the pixel grid + camera.left = -aspectRatio - fractX * pixelWidth; + camera.right = aspectRatio - fractX * pixelWidth; + camera.top = 1.0 - fractY * pixelHeight; + camera.bottom = -1.0 - fractY * pixelHeight; + camera.updateProjectionMatrix(); +} diff --git a/examples-testing/examples/webgpu_postprocessing_radial_blur.ts b/examples-testing/examples/webgpu_postprocessing_radial_blur.ts new file mode 100644 index 000000000..f358421e3 --- /dev/null +++ b/examples-testing/examples/webgpu_postprocessing_radial_blur.ts @@ -0,0 +1,149 @@ +import * as THREE from 'three/webgpu'; +import { float, int, pass, uniform } from 'three/tsl'; +import { radialBlur } from 'three/addons/tsl/display/radialBlur.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +const params = { + enabled: true, + animated: true, +}; + +let camera, scene, renderer, timer, group; +let renderPipeline; + +init(); + +async function init() { + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 200); + camera.position.z = 50; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x000000); + + timer = new THREE.Timer(); + + // + + const hemiLight = new THREE.HemisphereLight(0xffffff, 0x8d8d8d); + hemiLight.position.set(0, 1000, 0); + scene.add(hemiLight); + + const pointLight = new THREE.PointLight(0xffffff, 1000); + pointLight.position.set(0, 0, 0); + scene.add(pointLight); + + // + + group = new THREE.Group(); + + const geometry = new THREE.TetrahedronGeometry(); + const material = new THREE.MeshStandardMaterial({ flatShading: true }); + + const mesh = new THREE.InstancedMesh(geometry, material, 100); + const dummy = new THREE.Object3D(); + const col = new THREE.Color(); + const center = new THREE.Vector2(); + + for (let i = 0; i < mesh.count; i++) { + dummy.position.x = Math.random() * 50 - 25; + dummy.position.y = Math.random() * 50 - 25; + dummy.position.z = Math.random() * 50 - 25; + + center.set(dummy.position.x, dummy.position.y); + + // make sure tetrahedrons are not positioned at the origin + + if (center.length() < 6) { + center.normalize().multiplyScalar(6); + dummy.position.x = center.x; + dummy.position.y = center.y; + } + + dummy.scale.setScalar(Math.random() * 2 + 1); + + dummy.rotation.x = Math.random() * Math.PI; + dummy.rotation.y = Math.random() * Math.PI; + dummy.rotation.z = Math.random() * Math.PI; + + dummy.updateMatrix(); + mesh.setMatrixAt(i, dummy.matrix); + + col.setHSL(0.55 + (i / mesh.count) * 0.15, 1, 0.2); + + mesh.setColorAt(i, col); + } + + group.add(mesh); + scene.add(group); + + renderer = new THREE.WebGPURenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + renderer.toneMapping = THREE.NeutralToneMapping; + document.body.appendChild(renderer.domElement); + + // post processing + + renderPipeline = new THREE.RenderPipeline(renderer); + + const scenePass = pass(scene, camera); + + const weightUniform = uniform(float(0.9)); + const decayUniform = uniform(float(0.95)); + const exposureUniform = uniform(int(5)); + const countUniform = uniform(int(32)); + + const blurPass = radialBlur(scenePass, { + weight: weightUniform, + decay: decayUniform, + count: countUniform, + exposure: exposureUniform, + }); + + renderPipeline.outputNode = blurPass; + + // + + window.addEventListener('resize', onWindowResize); + + // + + const gui = renderer.inspector.createParameters('Settings'); + gui.add(weightUniform, 'value', 0, 1).name('weight'); + gui.add(decayUniform, 'value', 0, 1).name('decay'); + gui.add(countUniform, 'value', 16, 64, 1).name('sample count'); + gui.add(exposureUniform, 'value', 1, 10).name('exposure'); + gui.add(params, 'enabled').onChange(value => { + if (value === true) { + renderPipeline.outputNode = blurPass; + } else { + renderPipeline.outputNode = scenePass; + } + + renderPipeline.needsUpdate = true; + }); + gui.add(params, 'animated'); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + timer.update(); + const delta = timer.getDelta(); + + if (params.animated === true) { + group.rotation.y += delta * 0.1; + } + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_postprocessing_retro.ts b/examples-testing/examples/webgpu_postprocessing_retro.ts new file mode 100644 index 000000000..b9958352f --- /dev/null +++ b/examples-testing/examples/webgpu_postprocessing_retro.ts @@ -0,0 +1,370 @@ +import * as THREE from 'three/webgpu'; +import { + pass, + mix, + mul, + oneMinus, + positionLocal, + smoothstep, + texture, + time, + rotateUV, + Fn, + uv, + vec2, + vec3, + vec4, + uniform, + posterize, + floor, + float, + sin, + fract, + dot, + step, + color, + normalWorld, + length, + atan, + replaceDefaultUV, + screenSize, +} from 'three/tsl'; +import { retroPass } from 'three/addons/tsl/display/RetroPassNode.js'; +import { bayerDither } from 'three/addons/tsl/math/Bayer.js'; +import { scanlines, vignette, colorBleeding, barrelUV } from 'three/addons/tsl/display/CRT.js'; +import { circle } from 'three/addons/tsl/display/Shape.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer, renderPipeline, controls; +let environment; +let currentModel; + +init(); + +async function init() { + camera = new THREE.PerspectiveCamera(25, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(8, 5, 20); + + scene = new THREE.Scene(); + + // PS1-style background: gradient sky with simple stars + + const ps1Background = Fn(() => { + // Flip Y coordinate for correct orientation + const flippedY = normalWorld.y.negate(); + const skyUV = flippedY.mul(0.5).add(0.5); + + // Simple gradient sky (dark blue at top to purple/orange at horizon) + const topColor = color(0x000033); // dark blue night sky + const midColor = color(0x330066); // purple + const horizonColor = color(0x663322); // warm orange/brown horizon + + // Two-step gradient (inverted - top is dark, horizon is warm) + const skyGradient = mix( + horizonColor, + mix(midColor, topColor, skyUV.smoothstep(0.4, 0.9)), + skyUV.smoothstep(0.0, 0.4), + ); + + // PS1-style "stars" using spherical coordinates + const longitude = atan(normalWorld.x, normalWorld.z); + const latitude = flippedY.asin(); // Use flipped Y for latitude too + + // More stars with smaller scale + const starScale = float(50.0); + const starUV = vec2(longitude.mul(starScale), latitude.mul(starScale)); + const starCell = floor(starUV); + + // Hash for randomness + const cellHash = fract(sin(dot(starCell, vec2(12.9898, 78.233))).mul(43758.5453)); + + // Position within cell (0-1) + const cellUV = fract(starUV); + const toCenter = cellUV.sub(0.5); + + // Gemini-style star: bright center with soft glow + cross flare + const distToCenter = length(toCenter); + + // Core (small bright center) + const core = smoothstep(float(0.08), float(0.0), distToCenter); + + // Soft glow around + const glow = smoothstep(float(0.25), float(0.0), distToCenter).mul(0.4); + + // Cross/diamond flare effect + const crossX = smoothstep(float(0.15), float(0.0), toCenter.x.abs()).mul( + smoothstep(float(0.4), float(0.0), toCenter.y.abs()), + ); + const crossY = smoothstep(float(0.15), float(0.0), toCenter.y.abs()).mul( + smoothstep(float(0.4), float(0.0), toCenter.x.abs()), + ); + const cross = crossX.add(crossY).mul(0.3); + + // Combine star shape + const starShape = core.add(glow).add(cross); + + // More stars (lower threshold = more stars) + const isStar = step(0.85, cellHash); + + // Show stars from horizon up + const aboveHorizon = smoothstep(float(-0.2), float(0.1), flippedY); + + // Star brightness varies + twinkle color + const starIntensity = isStar.mul(aboveHorizon).mul(starShape).mul(cellHash.mul(0.6).add(0.4)); + + // Slight color variation (white to light blue) + const starColor = mix(vec3(1.0, 1.0, 0.95), vec3(0.8, 0.9, 1.0), cellHash); + + // Combine sky and stars + const finalColor = mix(skyGradient, starColor, starIntensity.clamp(0.0, 1.0)); + + return finalColor; + })(); + + scene.backgroundNode = ps1Background; + + // Loaders + + const gltfLoader = new GLTFLoader(); + const textureLoader = new THREE.TextureLoader(); + + // Model + + const models = { + 'Coffee Mug': 'models/gltf/coffeeMug.glb', + 'Damaged Helmet': 'models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf', + }; + + function loadModel(name) { + function loadEnvironment() { + if (environment) return; + + environment = new HDRLoader().setPath('textures/equirectangular/').load('venice_sunset_1k.hdr', texture => { + texture.mapping = THREE.EquirectangularReflectionMapping; + scene.environment = texture; + + // re-invalidate retro pass textures + + retro.dispose(); + }); + } + + if (currentModel) { + scene.remove(currentModel); + currentModel.traverse(child => { + if (child.isMesh) { + child.geometry.dispose(); + child.material.dispose(); + } + }); + } + + gltfLoader.load(models[name], gltf => { + currentModel = gltf.scene; + currentModel.position.set(0, 0, 0); + + smoke.visible = false; + + if (name === 'Damaged Helmet') { + loadEnvironment(); + + currentModel.scale.setScalar(3); + currentModel.position.y = 1; + } else if (name === 'Coffee Mug') { + smoke.visible = true; + } + + scene.add(currentModel); + }); + } + + loadModel('Coffee Mug'); + + // lighting + + const ambientLight = new THREE.AmbientLight(0x404040, 2); + scene.add(ambientLight); + + const directionalLight = new THREE.DirectionalLight(0xffffff, 3); + directionalLight.position.set(5, 10, 5); + scene.add(directionalLight); + + const pointLight = new THREE.PointLight(0xff6600, 5, 20); + pointLight.position.set(-3, 3, 2); + scene.add(pointLight); + + // geometry + + const smokeGeometry = new THREE.PlaneGeometry(1, 1, 16, 64); + smokeGeometry.translate(0, 0.5, 0); + smokeGeometry.scale(1.5, 6, 1.5); + + // texture + + const noiseTexture = textureLoader.load('./textures/noises/perlin/128x128.png'); + noiseTexture.wrapS = THREE.RepeatWrapping; + noiseTexture.wrapT = THREE.RepeatWrapping; + + // material + + const smokeMaterial = new THREE.MeshBasicNodeMaterial({ + transparent: true, + side: THREE.DoubleSide, + depthWrite: false, + }); + + // position + + smokeMaterial.positionNode = Fn(() => { + // twist + + const twistNoiseUv = vec2(0.5, uv().y.mul(0.2).sub(time.mul(0.005)).mod(1)); + const twist = texture(noiseTexture, twistNoiseUv).r.mul(10); + positionLocal.xz.assign(rotateUV(positionLocal.xz, twist, vec2(0))); + + // wind + + const windOffset = vec2( + texture(noiseTexture, vec2(0.25, time.mul(0.01)).mod(1)).r.sub(0.5), + texture(noiseTexture, vec2(0.75, time.mul(0.01)).mod(1)).r.sub(0.5), + ).mul(uv().y.pow(2).mul(10)); + positionLocal.addAssign(windOffset); + + return positionLocal; + })(); + + // color + + smokeMaterial.colorNode = Fn(() => { + // alpha + + const alphaNoiseUv = uv() + .mul(vec2(0.5, 0.3)) + .add(vec2(0, time.mul(0.03).negate())); + const alpha = mul( + // pattern + texture(noiseTexture, alphaNoiseUv).r.smoothstep(0.4, 1), + + // edges fade + smoothstep(0, 0.1, uv().x), + smoothstep(0, 0.1, oneMinus(uv().x)), + smoothstep(0, 0.1, uv().y), + smoothstep(0, 0.1, oneMinus(uv().y)), + ); + + // color + + const finalColor = mix(vec3(0.6, 0.3, 0.2), vec3(1, 1, 1), alpha.pow(3)); + + return vec4(finalColor, alpha); + })(); + + // mesh + + const smoke = new THREE.Mesh(smokeGeometry, smokeMaterial); + smoke.position.y = 1.83; + scene.add(smoke); + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // uniforms + + // PS1-style: 15-bit color (32 levels per channel) + const colorDepthSteps = uniform(32); + + // CRT effect parameters (subtle for PS1 look) + const scanlineIntensity = uniform(0.3); // subtle scanlines + const scanlineDensity = uniform(1); // 0.1-1: normalized scanline density (1 = full screen resolution) + const scanlineSpeed = uniform(0.0); // no scanline movement + const vignetteIntensity = uniform(0.3); // subtle vignette + const bleeding = uniform(0.001); // minimal bleeding + const curvature = uniform(0.02); // subtle curve + const affineDistortion = uniform(0); // no affine distortion + + // render pipeline + + renderPipeline = new THREE.RenderPipeline(renderer); + + // retro pipeline + + const distortedUV = barrelUV(curvature); + const distortedDelta = circle(curvature.add(0.1).mul(10), 1).mul(curvature).mul(0.05); + + const retro = retroPass(scene, camera, { affineDistortion }); + + let retroPipeline = retro; + retroPipeline = replaceDefaultUV(distortedUV, retroPipeline); + retroPipeline = colorBleeding(retroPipeline, bleeding.add(distortedDelta)); + retroPipeline = bayerDither(retroPipeline, colorDepthSteps); + retroPipeline = posterize(retroPipeline, colorDepthSteps); + retroPipeline = vignette(retroPipeline, vignetteIntensity, 0.6); + retroPipeline = scanlines(retroPipeline, scanlineIntensity, screenSize.y.mul(scanlineDensity), scanlineSpeed); + + renderPipeline.outputNode = retroPipeline; + + // default pass (no post-processing) + + const defaultPass = pass(scene, camera); + + // controls + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + controls.minDistance = 0.1; + controls.maxDistance = 50; + controls.target.y = 1; + + // gui + + const gui = renderer.inspector.createParameters('Settings'); + + gui.add({ model: 'Coffee Mug' }, 'model', Object.keys(models)).name('Model').onChange(loadModel); + + gui.add({ enabled: true }, 'enabled') + .onChange(v => { + renderPipeline.outputNode = v ? retroPipeline : defaultPass; + renderPipeline.needsUpdate = true; + }) + .name('Retro Pipeline'); + + gui.add(curvature, 'value', 0, 0.2, 0.01).name('Curvature'); + gui.add(colorDepthSteps, 'value', 4, 32, 1).name('Color Depth'); + gui.add(scanlineIntensity, 'value', 0, 1, 0.01).name('Scanlines'); + gui.add(scanlineDensity, 'value', 0.02, 1, 0.01).name('Scanline Density'); + gui.add(scanlineSpeed, 'value', 0, 0.1, 0.01).name('Scanline Speed'); + gui.add(vignetteIntensity, 'value', 0, 1, 0.01).name('Vignette'); + gui.add(bleeding, 'value', 0, 0.005, 0.001).name('Color Bleeding'); + gui.add(affineDistortion, 'value', 0, 1).name('Affine Distortion'); + gui.add(retro, 'filterTextures') + .name('Filter Textures') + .onChange(() => { + retro.dispose(); + }); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +async function animate() { + controls.update(); + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_postprocessing_smaa.ts b/examples-testing/examples/webgpu_postprocessing_smaa.ts new file mode 100644 index 000000000..f06374258 --- /dev/null +++ b/examples-testing/examples/webgpu_postprocessing_smaa.ts @@ -0,0 +1,98 @@ +import * as THREE from 'three/webgpu'; +import { pass } from 'three/tsl'; +import { smaa } from 'three/addons/tsl/display/SMAANode.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer, renderPipeline; + +const params = { + enabled: true, + autoRotate: true, +}; + +init(); + +function init() { + renderer = new THREE.WebGPURenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // + + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.z = 300; + + scene = new THREE.Scene(); + + const geometry = new THREE.BoxGeometry(120, 120, 120); + const material1 = new THREE.MeshBasicMaterial({ color: 0xffffff, wireframe: true }); + + const mesh1 = new THREE.Mesh(geometry, material1); + mesh1.position.x = -100; + scene.add(mesh1); + + const texture = new THREE.TextureLoader().load('textures/brick_diffuse.jpg'); + texture.colorSpace = THREE.SRGBColorSpace; + + const material2 = new THREE.MeshBasicMaterial({ map: texture }); + + const mesh2 = new THREE.Mesh(geometry, material2); + mesh2.position.x = 100; + scene.add(mesh2); + + // post processing + + renderPipeline = new THREE.RenderPipeline(renderer); + + const scenePass = pass(scene, camera).toInspector('Color'); + const smaaPass = smaa(scenePass); + + renderPipeline.outputNode = smaaPass; + + // + + window.addEventListener('resize', onWindowResize); + + const gui = renderer.inspector.createParameters('Settings'); + + const smaaFolder = gui.addFolder('SMAA'); + smaaFolder.add(params, 'enabled').onChange(value => { + if (value === true) { + renderPipeline.outputNode = smaaPass; + } else { + renderPipeline.outputNode = scenePass; + } + + renderPipeline.needsUpdate = true; + }); + + const sceneFolder = gui.addFolder('Scene'); + sceneFolder.add(params, 'autoRotate'); +} + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); +} + +function animate() { + if (params.autoRotate === true) { + for (let i = 0; i < scene.children.length; i++) { + const child = scene.children[i]; + + child.rotation.x += 0.005; + child.rotation.y += 0.01; + } + } + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_postprocessing_sobel.ts b/examples-testing/examples/webgpu_postprocessing_sobel.ts new file mode 100644 index 000000000..bdc37d2fd --- /dev/null +++ b/examples-testing/examples/webgpu_postprocessing_sobel.ts @@ -0,0 +1,94 @@ +import * as THREE from 'three/webgpu'; +import { pass, renderOutput } from 'three/tsl'; +import { sobel } from 'three/addons/tsl/display/SobelOperatorNode.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer, controls; +let renderPipeline; + +const params = { + enabled: true, +}; + +init(); + +async function init() { + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(0, 1, 3); + + // + + const loader = new GLTFLoader(); + const gltf = await loader.loadAsync('models/gltf/DragonAttenuation.glb'); + const model = gltf.scene.children[1]; + model.material = new THREE.MeshStandardNodeMaterial(); + + scene.add(model); + + // + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + renderer.toneMapping = THREE.LinearToneMapping; + document.body.appendChild(renderer.domElement); + + await renderer.init(); + + const environment = new RoomEnvironment(); + const pmremGenerator = new THREE.PMREMGenerator(renderer); + + scene.environment = pmremGenerator.fromScene(environment, 0.04).texture; + pmremGenerator.dispose(); + + // + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + controls.enableZoom = false; + controls.target.set(0, 0.5, 0); + controls.update(); + + // postprocessing + + renderPipeline = new THREE.RenderPipeline(renderer); + renderPipeline.outputColorTransform = false; + + const scenePass = pass(scene, camera); + + renderPipeline.outputNode = sobel(renderOutput(scenePass)); + + // + + const gui = renderer.inspector.createParameters('Settings'); + gui.add(params, 'enabled'); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + controls.update(); + + if (params.enabled === true) { + renderPipeline.render(); + } else { + renderer.render(scene, camera); + } +} diff --git a/examples-testing/examples/webgpu_postprocessing_ssaa.ts b/examples-testing/examples/webgpu_postprocessing_ssaa.ts new file mode 100644 index 000000000..c38832c7a --- /dev/null +++ b/examples-testing/examples/webgpu_postprocessing_ssaa.ts @@ -0,0 +1,170 @@ +import * as THREE from 'three/webgpu'; +import { ssaaPass } from 'three/addons/tsl/display/SSAAPassNode.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let scene, mesh, renderer, renderPipeline; +let camera, ssaaRenderPass; +let timer; + +const params = { + sampleLevel: 3, + camera: 'perspective', + clearColor: 'black', + clearAlpha: 1.0, + viewOffsetX: 0, + autoRotate: true, +}; + +init(); + +function init() { + const width = window.innerWidth; + const height = window.innerHeight; + + renderer = new THREE.WebGPURenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + timer = new THREE.Timer(); + timer.connect(document); + + camera = new THREE.PerspectiveCamera(65, width / height, 3, 10); + camera.position.z = 7; + camera.setViewOffset(width, height, params.viewOffsetX, 0, width, height); + + scene = new THREE.Scene(); + + const group = new THREE.Group(); + scene.add(group); + + const light = new THREE.PointLight(0xefffef, 500); + light.position.z = 10; + light.position.y = -10; + light.position.x = -10; + scene.add(light); + + const light2 = new THREE.PointLight(0xffefef, 500); + light2.position.z = 10; + light2.position.x = -10; + light2.position.y = 10; + scene.add(light2); + + const light3 = new THREE.PointLight(0xefefff, 500); + light3.position.z = 10; + light3.position.x = 10; + light3.position.y = -10; + scene.add(light3); + + const light4 = new THREE.AmbientLight(0xffffff, 0.2); + scene.add(light4); + + const geometry = new THREE.SphereGeometry(3, 48, 24); + const material = new THREE.MeshStandardMaterial(); + + mesh = new THREE.InstancedMesh(geometry, material, 120); + + const dummy = new THREE.Mesh(); + const color = new THREE.Color(); + + for (let i = 0; i < mesh.count; i++) { + dummy.position.x = Math.random() * 4 - 2; + dummy.position.y = Math.random() * 4 - 2; + dummy.position.z = Math.random() * 4 - 2; + dummy.rotation.x = Math.random(); + dummy.rotation.y = Math.random(); + dummy.rotation.z = Math.random(); + dummy.scale.setScalar(Math.random() * 0.2 + 0.05); + + dummy.updateMatrix(); + + color.setHSL(Math.random(), 1.0, 0.3); + + mesh.setMatrixAt(i, dummy.matrix); + mesh.setColorAt(i, color); + } + + scene.add(mesh); + + // postprocessing + + renderPipeline = new THREE.RenderPipeline(renderer); + + ssaaRenderPass = ssaaPass(scene, camera); + const scenePassColor = ssaaRenderPass.getTextureNode(); + + renderPipeline.outputNode = scenePassColor; + + window.addEventListener('resize', onWindowResize); + + // GUI + + const gui = renderer.inspector.createParameters('Settings'); + + gui.add(params, 'sampleLevel', { + 'Level 0: 1 Sample': 0, + 'Level 1: 2 Samples': 1, + 'Level 2: 4 Samples': 2, + 'Level 3: 8 Samples': 3, + 'Level 4: 16 Samples': 4, + 'Level 5: 32 Samples': 5, + }); + gui.add(params, 'clearColor', ['black', 'white', 'blue', 'green', 'red']); + gui.add(params, 'clearAlpha', 0, 1); + gui.add(params, 'viewOffsetX', -100, 100); + gui.add(params, 'autoRotate'); +} + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.setViewOffset(width, height, params.viewOffsetX, 0, width, height); + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); +} + +function animate() { + timer.update(); + + if (params.autoRotate) { + const delta = timer.getDelta(); + + mesh.rotation.x += delta * 0.25; + mesh.rotation.y += delta * 0.5; + } + + let newColor = ssaaRenderPass.clearColor; + + switch (params.clearColor) { + case 'blue': + newColor = 0x0000ff; + break; + case 'red': + newColor = 0xff0000; + break; + case 'green': + newColor = 0x00ff00; + break; + case 'white': + newColor = 0xffffff; + break; + case 'black': + newColor = 0x000000; + break; + } + + ssaaRenderPass.clearColor.set(newColor); + ssaaRenderPass.clearAlpha = params.clearAlpha; + + ssaaRenderPass.sampleLevel = params.sampleLevel; + + camera.view.offsetX = params.viewOffsetX; + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_postprocessing_ssgi.ts b/examples-testing/examples/webgpu_postprocessing_ssgi.ts new file mode 100644 index 000000000..8c8e4a91c --- /dev/null +++ b/examples-testing/examples/webgpu_postprocessing_ssgi.ts @@ -0,0 +1,244 @@ +import * as THREE from 'three/webgpu'; +import { + pass, + mrt, + output, + normalView, + diffuseColor, + velocity, + add, + vec3, + vec4, + directionToColor, + colorToDirection, + sample, +} from 'three/tsl'; +import { ssgi } from 'three/addons/tsl/display/SSGINode.js'; +import { traa } from 'three/addons/tsl/display/TRAANode.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer, renderPipeline, controls; + +init(); + +async function init() { + camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(0, 10, 30); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xaaaaaa); + + renderer = new THREE.WebGPURenderer(); + //renderer.setPixelRatio( window.devicePixelRatio ); // probably too costly for most hardware + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.shadowMap.enabled = true; + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // + + controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, 7, 0); + controls.enablePan = true; + controls.minDistance = 1; + controls.maxDistance = 100; + controls.update(); + + // + + renderPipeline = new THREE.RenderPipeline(renderer); + + const scenePass = pass(scene, camera); + scenePass.setMRT( + mrt({ + output: output, + diffuseColor: diffuseColor, + normal: directionToColor(normalView), + velocity: velocity, + }), + ); + + const scenePassColor = scenePass.getTextureNode('output'); + const scenePassDiffuse = scenePass.getTextureNode('diffuseColor').toInspector('Diffuse Color'); + const scenePassDepth = scenePass.getTextureNode('depth').toInspector('Depth', () => { + return scenePass.getLinearDepthNode(); + }); + + const scenePassNormal = scenePass.getTextureNode('normal').toInspector('Normal'); + const scenePassVelocity = scenePass.getTextureNode('velocity').toInspector('Velocity'); + + // bandwidth optimization + + const diffuseTexture = scenePass.getTexture('diffuseColor'); + diffuseTexture.type = THREE.UnsignedByteType; + + const normalTexture = scenePass.getTexture('normal'); + normalTexture.type = THREE.UnsignedByteType; + + const sceneNormal = sample(uv => { + return colorToDirection(scenePassNormal.sample(uv)); + }); + + // gi + + const giPass = ssgi(scenePassColor, scenePassDepth, sceneNormal, camera); + giPass.sliceCount.value = 2; + giPass.stepCount.value = 8; + + // composite + + const gi = giPass.rgb.toInspector('SSGI'); + const ao = giPass.a.toInspector('AO'); + + const compositePass = vec4(add(scenePassColor.rgb.mul(ao), scenePassDiffuse.rgb.mul(gi)), scenePassColor.a); + compositePass.name = 'Composite'; + + // traa + + const traaPass = traa(compositePass, scenePassDepth, scenePassVelocity, camera); + renderPipeline.outputNode = traaPass; + + // Cornell Box inspired scene + + // Walls + const wallGeometry = new THREE.PlaneGeometry(1, 1); + + // Left wall - red + const redWallMaterial = new THREE.MeshPhysicalMaterial({ color: '#ff0000' }); + const leftWall = new THREE.Mesh(wallGeometry, redWallMaterial); + leftWall.scale.set(20, 15, 1); + leftWall.rotation.y = Math.PI * 0.5; + leftWall.position.set(-10, 7.5, 0); + leftWall.receiveShadow = true; + scene.add(leftWall); + + // Right wall - green + const greenWallMaterial = new THREE.MeshPhysicalMaterial({ color: '#00ff00' }); + const rightWall = new THREE.Mesh(wallGeometry, greenWallMaterial); + rightWall.scale.set(20, 15, 1); + rightWall.rotation.y = Math.PI * -0.5; + rightWall.position.set(10, 7.5, 0); + rightWall.receiveShadow = true; + scene.add(rightWall); + + // White walls and boxes + const whiteMaterial = new THREE.MeshPhysicalMaterial({ color: '#fff' }); + + // Floor + const floor = new THREE.Mesh(wallGeometry, whiteMaterial); + floor.scale.set(20, 20, 1); + floor.rotation.x = Math.PI * -0.5; + floor.receiveShadow = true; + scene.add(floor); + + // Back wall + const backWall = new THREE.Mesh(wallGeometry, whiteMaterial); + backWall.scale.set(15, 20, 1); + backWall.rotation.z = Math.PI * -0.5; + backWall.position.set(0, 7.5, -10); + backWall.receiveShadow = true; + scene.add(backWall); + + // Ceiling + const ceiling = new THREE.Mesh(wallGeometry, whiteMaterial); + ceiling.scale.set(20, 20, 1); + ceiling.rotation.x = Math.PI * 0.5; + ceiling.position.set(0, 15, 0); + ceiling.receiveShadow = true; + scene.add(ceiling); + + // Boxes + const tallBoxGeometry = new THREE.BoxGeometry(5, 7, 5); + const tallBox = new THREE.Mesh(tallBoxGeometry, whiteMaterial); + tallBox.rotation.y = Math.PI * 0.25; + tallBox.position.set(-3, 3.5, -2); + tallBox.castShadow = true; + tallBox.receiveShadow = true; + scene.add(tallBox); + + const shortBoxGeometry = new THREE.BoxGeometry(4, 4, 4); + const shortBox = new THREE.Mesh(shortBoxGeometry, whiteMaterial); + shortBox.rotation.y = Math.PI * -0.1; + shortBox.position.set(4, 2, 4); + shortBox.castShadow = true; + shortBox.receiveShadow = true; + scene.add(shortBox); + + // Light source geometry + const lightSourceGeometry = new THREE.CylinderGeometry(2.5, 2.5, 1, 64); + const lightSourceMaterial = new THREE.MeshBasicMaterial(); + const lightSource = new THREE.Mesh(lightSourceGeometry, lightSourceMaterial); + lightSource.position.y = 15; + scene.add(lightSource); + + // Point light + const pointLight = new THREE.PointLight('#ffffff', 100); + pointLight.position.set(0, 13, 0); + pointLight.distance = 100; + pointLight.castShadow = true; + pointLight.shadow.mapSize.width = 1024; + pointLight.shadow.mapSize.height = 1024; + scene.add(pointLight); + + // Ambient light + const ambientLight = new THREE.AmbientLight('#0c0c0c'); + scene.add(ambientLight); + + window.addEventListener('resize', onWindowResize); + + // + + const params = { + output: 0, + }; + + const types = { Combined: 0, Direct: 3, AO: 1, GI: 2 }; + + const gui = renderer.inspector.createParameters('SSGI settings'); + gui.add(params, 'output', types).onChange(updatePostprocessing); + gui.add(giPass.sliceCount, 'value', 1, 4, 1).name('slice count'); + gui.add(giPass.stepCount, 'value', 1, 32, 1).name('step count'); + gui.add(giPass.radius, 'value', 1, 25).name('radius'); + gui.add(giPass.expFactor, 'value', 1, 3).name('exp factor'); + gui.add(giPass.thickness, 'value', 0.01, 10).name('thickness'); + gui.add(giPass.backfaceLighting, 'value', 0, 1).name('backface lighting'); + gui.add(giPass.aoIntensity, 'value', 0, 4).name('AO intensity'); + gui.add(giPass.giIntensity, 'value', 0, 100).name('GI intensity'); + gui.add(giPass.useLinearThickness, 'value').name('use linear thickness'); + gui.add(giPass.useScreenSpaceSampling, 'value').name('screen-space sampling'); + gui.add(giPass, 'useTemporalFiltering').name('temporal filtering').onChange(updatePostprocessing); + + function updatePostprocessing(value) { + if (value === 1) { + renderPipeline.outputNode = vec4(vec3(ao), 1); + } else if (value === 2) { + renderPipeline.outputNode = vec4(gi, 1); + } else if (value === 3) { + renderPipeline.outputNode = scenePassColor; + } else { + renderPipeline.outputNode = giPass.useTemporalFiltering ? traaPass : compositePass; + } + + renderPipeline.needsUpdate = true; + } +} + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); +} + +function animate() { + controls.update(); + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_postprocessing_ssgi_ballpool.ts b/examples-testing/examples/webgpu_postprocessing_ssgi_ballpool.ts new file mode 100644 index 000000000..4d1e617c1 --- /dev/null +++ b/examples-testing/examples/webgpu_postprocessing_ssgi_ballpool.ts @@ -0,0 +1,413 @@ +import * as THREE from 'three/webgpu'; +import { + pass, + mrt, + output, + normalView, + diffuseColor, + velocity, + add, + vec4, + directionToColor, + colorToDirection, + sample, +} from 'three/tsl'; +import { ssgi } from 'three/addons/tsl/display/SSGINode.js'; +import { traa } from 'three/addons/tsl/display/TRAANode.js'; +import { World } from '@perplexdotgg/bounce'; + +const BALL_RADIUS = 0.4; +const FILL_RATIO = 0.4; +const PACKING = 0.6; +const BOX_HEIGHT = 6; +const BOX_DEPTH = 8; +const WALL_THICKNESS = 0.5; +const CAM_FOV = 45; +const EASE_SPEED = 8; + +let camera, scene, renderer, renderPipeline; +let raycaster, pointer; +let mouseLight; + +let world; +let ballCount = 0; +let bodies = []; +let ballsMesh = null; +let wallMeshes = []; +const boxSize = { w: 8, h: BOX_HEIGHT, d: BOX_DEPTH }; + +let mouseRayOrigin = new THREE.Vector3(); +let mouseRayDir = new THREE.Vector3(); +let mouseMoving = false; +let mouseStopTimer = 0; +let pointerDown = false; +const activePointers = new Set(); +const mouseLightTarget = new THREE.Vector3(0, BOX_HEIGHT / 2, BOX_DEPTH / 2); +const mouseRayOriginTarget = new THREE.Vector3(); +const mouseRayDirTarget = new THREE.Vector3(); + +init(); + +async function init() { + camera = new THREE.PerspectiveCamera(CAM_FOV, window.innerWidth / window.innerHeight, 0.1, 100); + + scene = new THREE.Scene(); + + renderer = new THREE.WebGPURenderer({ antialias: false }); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = 0.5; + renderer.shadowMap.enabled = true; + document.body.appendChild(renderer.domElement); + + // + + renderPipeline = new THREE.RenderPipeline(renderer); + + const scenePass = pass(scene, camera); + scenePass.setMRT( + mrt({ + output: output, + diffuseColor: diffuseColor, + normal: directionToColor(normalView), + velocity: velocity, + }), + ); + + const scenePassColor = scenePass.getTextureNode('output'); + const scenePassDiffuse = scenePass.getTextureNode('diffuseColor'); + const scenePassDepth = scenePass.getTextureNode('depth'); + const scenePassNormal = scenePass.getTextureNode('normal'); + const scenePassVelocity = scenePass.getTextureNode('velocity'); + + // bandwidth optimization + + const diffuseTexture = scenePass.getTexture('diffuseColor'); + diffuseTexture.type = THREE.UnsignedByteType; + + const normalTexture = scenePass.getTexture('normal'); + normalTexture.type = THREE.UnsignedByteType; + + const sceneNormal = sample(uv => { + return colorToDirection(scenePassNormal.sample(uv)); + }); + + // gi + + const giPass = ssgi(scenePassColor, scenePassDepth, sceneNormal, camera); + giPass.sliceCount.value = 2; + giPass.stepCount.value = 8; + + // composite + + const gi = giPass.rgb; + const ao = giPass.a; + + const compositePass = vec4(add(scenePassColor.rgb.mul(ao), scenePassDiffuse.rgb.mul(gi)), scenePassColor.a); + + // traa + + const traaPass = traa(compositePass, scenePassDepth, scenePassVelocity, camera); + renderPipeline.outputNode = traaPass; + + // light + + mouseLight = new THREE.PointLight(0xffffff, 80); + mouseLight.position.set(0, BOX_HEIGHT / 2, BOX_DEPTH / 2); + mouseLight.castShadow = true; + mouseLight.shadow.mapSize.set(1024, 1024); + mouseLight.shadow.radius = 20; + scene.add(mouseLight); + + // + + raycaster = new THREE.Raycaster(); + pointer = new THREE.Vector2(); + + rebuildScene(); + + // + + renderer.domElement.addEventListener('pointerdown', onPointerDown); + renderer.domElement.addEventListener('pointermove', onPointerMove); + renderer.domElement.addEventListener('pointerup', onPointerUp); + renderer.domElement.addEventListener('pointercancel', onPointerUp); + window.addEventListener('resize', onWindowResize); +} + +function getBoxWidth() { + const aspect = window.innerWidth / window.innerHeight; + const vFov = THREE.MathUtils.degToRad(CAM_FOV / 2); + const dist = BOX_HEIGHT / 2 / Math.tan(vFov); + return Math.tan(vFov) * aspect * dist * 2; +} + +function getBallCount() { + const roomVolume = boxSize.w * boxSize.h * boxSize.d; + const ballVolume = (4 / 3) * Math.PI * Math.pow(BALL_RADIUS, 3); + return Math.floor((roomVolume * FILL_RATIO * PACKING) / ballVolume); +} + +function rebuildScene() { + for (const mesh of wallMeshes) { + scene.remove(mesh); + mesh.geometry.dispose(); + } + + wallMeshes = []; + + if (ballsMesh) { + scene.remove(ballsMesh); + ballsMesh.dispose(); + ballsMesh = null; + } + + bodies = []; + + boxSize.w = getBoxWidth(); + ballCount = getBallCount(); + + world = new World({ + gravity: [0, -9.81, 0], + solveVelocityIterations: 6, + solvePositionIterations: 2, + linearDamping: 0.1, + angularDamping: 0.1, + restitution: 0.4, + friction: 0.5, + }); + + fitCameraToBox(); + createBox(); + createBalls(); +} + +function createBox() { + const hw = boxSize.w / 2; + const hh = boxSize.h / 2; + const hd = boxSize.d / 2; + const t = WALL_THICKNESS; + + const whiteMaterial = new THREE.MeshPhysicalMaterial({ + color: 0xeeeeee, + roughness: 0.7, + metalness: 0.0, + }); + + const redMaterial = new THREE.MeshPhysicalMaterial({ + color: 0xff2222, + roughness: 0.7, + metalness: 0.0, + }); + + const greenMaterial = new THREE.MeshPhysicalMaterial({ + color: 0x22ff22, + roughness: 0.7, + metalness: 0.0, + }); + + const walls = [ + { size: [boxSize.w, t, boxSize.d], pos: [0, -t / 2, 0], mat: whiteMaterial }, + { size: [boxSize.w, t, boxSize.d], pos: [0, boxSize.h + t / 2, 0], mat: whiteMaterial }, + { size: [boxSize.w, boxSize.h, t], pos: [0, hh, -hd - t / 2], mat: whiteMaterial }, + { size: [boxSize.w, boxSize.h, t], pos: [0, hh, hd + t / 2], noMesh: true }, + { size: [t, boxSize.h, boxSize.d], pos: [-hw - t / 2, hh, 0], mat: redMaterial }, + { size: [t, boxSize.h, boxSize.d], pos: [hw + t / 2, hh, 0], mat: greenMaterial }, + ]; + + for (const w of walls) { + const shape = world.createBox({ width: w.size[0], height: w.size[1], depth: w.size[2] }); + world.createStaticBody({ shape, position: w.pos }); + + if (!w.noMesh) { + const geo = new THREE.BoxGeometry(w.size[0], w.size[1], w.size[2]); + const mesh = new THREE.Mesh(geo, w.mat); + mesh.position.set(...w.pos); + mesh.receiveShadow = true; + scene.add(mesh); + wallMeshes.push(mesh); + } + } +} + +function createBalls() { + const sphereGeo = new THREE.SphereGeometry(BALL_RADIUS, 32, 16); + const sphereShape = world.createSphere({ radius: BALL_RADIUS }); + + const material = new THREE.MeshPhysicalMaterial({ + roughness: 0.3, + metalness: 0.1, + }); + + ballsMesh = new THREE.InstancedMesh(sphereGeo, material, ballCount); + ballsMesh.castShadow = true; + ballsMesh.receiveShadow = true; + scene.add(ballsMesh); + + const colors = [0xff4444, 0x44ff44, 0x4488ff, 0xffaa00, 0xff44ff, 0x44ffff, 0xffff44, 0xff8844, 0x8844ff, 0x44ff88]; + + const color = new THREE.Color(); + const hw = boxSize.w / 2 - BALL_RADIUS - 0.1; + const hd = boxSize.d / 2 - BALL_RADIUS - 0.1; + + for (let i = 0; i < ballCount; i++) { + color.set(colors[i % colors.length]); + ballsMesh.setColorAt(i, color); + + const x = (Math.random() - 0.5) * 2 * hw; + const y = BALL_RADIUS + Math.random() * (boxSize.h - BALL_RADIUS * 2); + const z = (Math.random() - 0.5) * 2 * hd; + + const body = world.createDynamicBody({ + shape: sphereShape, + position: [x, y, z], + mass: 1, + restitution: 0.5, + friction: 0.4, + }); + + bodies.push(body); + } +} + +function onPointerDown(event) { + activePointers.add(event.pointerId); + + if (event.pointerType === 'touch') { + pointerDown = activePointers.size >= 2; + } else { + pointerDown = true; + } +} + +function onPointerUp(event) { + activePointers.delete(event.pointerId); + + if (event.pointerType === 'touch') { + pointerDown = activePointers.size >= 2; + } else { + pointerDown = false; + } +} + +function onPointerMove(event) { + pointer.x = (event.clientX / window.innerWidth) * 2 - 1; + pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; + + raycaster.setFromCamera(pointer, camera); + + mouseRayOriginTarget.copy(raycaster.ray.origin); + mouseRayDirTarget.copy(raycaster.ray.direction); + mouseMoving = true; + clearTimeout(mouseStopTimer); + mouseStopTimer = setTimeout(() => (mouseMoving = false), 50); + + const frontPlane = new THREE.Plane(new THREE.Vector3(0, 0, 1), -boxSize.d / 2); + const hit = new THREE.Vector3(); + if (raycaster.ray.intersectPlane(frontPlane, hit)) { + mouseLightTarget.copy(hit); + } +} + +function respawnBalls() { + const hw = boxSize.w / 2 - BALL_RADIUS - 0.1; + const hd = boxSize.d / 2 - BALL_RADIUS - 0.1; + const count = 5; + + for (let i = 0; i < count; i++) { + const body = bodies[Math.floor(Math.random() * bodies.length)]; + + body.position.set([ + (Math.random() - 0.5) * 2 * hw, + boxSize.h - BALL_RADIUS - Math.random() * 1, + (Math.random() - 0.5) * 2 * hd, + ]); + body.linearVelocity.set([0, 0, 0]); + body.angularVelocity.set([0, 0, 0]); + body.commitChanges(); + } +} + +function fitCameraToBox() { + const vFov = THREE.MathUtils.degToRad(CAM_FOV / 2); + const dist = boxSize.h / 2 / Math.tan(vFov); + + camera.aspect = window.innerWidth / window.innerHeight; + camera.position.set(0, boxSize.h / 2, dist + boxSize.d / 2); + camera.lookAt(0, boxSize.h / 2, 0); + camera.updateProjectionMatrix(); +} + +function onWindowResize() { + renderer.setSize(window.innerWidth, window.innerHeight); + rebuildScene(); +} + +const timer = new THREE.Timer(); +const _dummy = new THREE.Object3D(); + +function animate() { + timer.update(); + const dt = Math.min(timer.getDelta(), 1 / 30); + + const easeFactor = 1 - Math.exp(-EASE_SPEED * dt); + mouseRayOrigin.lerp(mouseRayOriginTarget, easeFactor); + mouseRayDir.lerp(mouseRayDirTarget, easeFactor); + mouseLight.position.lerp(mouseLightTarget, easeFactor); + + if (pointerDown) { + respawnBalls(); + } + + if (mouseMoving) { + const pushRadius = 1.5; + const pushStrength = 15; + const _closest = new THREE.Vector3(); + const _ballPos = new THREE.Vector3(); + const _pushDir = new THREE.Vector3(); + + for (const body of bodies) { + const bp = body.position; + _ballPos.set(bp.x, bp.y, bp.z); + + const ray = new THREE.Ray(mouseRayOrigin, mouseRayDir); + ray.closestPointToPoint(_ballPos, _closest); + + const dist = _closest.distanceTo(_ballPos); + + if (dist < pushRadius) { + _pushDir.subVectors(_ballPos, _closest); + if (_pushDir.lengthSq() < 0.001) _pushDir.set(0, 1, 0); + _pushDir.normalize(); + + const strength = pushStrength * (1 - dist / pushRadius); + body.applyLinearImpulse({ + x: _pushDir.x * strength, + y: _pushDir.y * strength, + z: _pushDir.z * strength, + }); + } + } + } + + if (world) { + world.advanceTime(1 / 60, dt); + } + + for (let i = 0; i < bodies.length; i++) { + const body = bodies[i]; + const p = body.position; + const q = body.orientation; + + _dummy.position.set(p.x, p.y, p.z); + _dummy.quaternion.set(q.x, q.y, q.z, q.w); + _dummy.updateMatrix(); + + ballsMesh.setMatrixAt(i, _dummy.matrix); + } + + ballsMesh.instanceMatrix.needsUpdate = true; + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_postprocessing_ssr.ts b/examples-testing/examples/webgpu_postprocessing_ssr.ts new file mode 100644 index 000000000..7ce11780a --- /dev/null +++ b/examples-testing/examples/webgpu_postprocessing_ssr.ts @@ -0,0 +1,211 @@ +import * as THREE from 'three/webgpu'; +import { + pass, + mrt, + output, + normalView, + metalness, + roughness, + screenUV, + color, + sample, + directionToColor, + colorToDirection, + vec2, + colorSpaceToWorking, +} from 'three/tsl'; +import { ssr } from 'three/addons/tsl/display/SSRNode.js'; +import { smaa } from 'three/addons/tsl/display/SMAANode.js'; + +import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +const params = { + quality: 0.5, + blurQuality: 1, + maxDistance: 1, + opacity: 1, + thickness: 0.03, + roughness: 1, + enabled: true, +}; + +let camera, scene, model, renderer, renderPipeline, ssrPass; +let controls; + +init(); + +async function init() { + camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 0.1, 50); + camera.position.set(3, 2, 3); + + scene = new THREE.Scene(); + scene.backgroundNode = screenUV.distance(0.5).remap(0, 0.5).mix(color(0x888877), color(0x776666)); + + const dracoLoader = new DRACOLoader(); + dracoLoader.setDecoderPath('jsm/libs/draco/'); + dracoLoader.setDecoderConfig({ type: 'js' }); + + const loader = new GLTFLoader(); + loader.setDRACOLoader(dracoLoader); + loader.load('models/gltf/steampunk_camera.glb', function (gltf) { + model = gltf.scene; + + model.traverse(function (object) { + if (object.material) { + if (object.material.name === 'Lense_Casing') { + object.material.transparent = true; + } + + // Avoid overdrawing + object.material.side = THREE.FrontSide; + } + }); + + model.position.y = 0.1; + scene.add(model); + }); + + // Add a reflective plane under the camera + + const floorGeometry = new THREE.CircleGeometry(2, 64); + const floorMaterial = new THREE.MeshStandardMaterial({ + color: 0xffffff, + metalness: 1, + roughness: 0.5, + }); + const floor = new THREE.Mesh(floorGeometry, floorMaterial); + floor.rotation.x = -Math.PI / 2; + floor.position.y = -0.8; + scene.add(floor); + + // + + renderer = new THREE.WebGPURenderer(); + // renderer.setPixelRatio( window.devicePixelRatio ); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + await renderer.init(); + + const environment = new RoomEnvironment(); + const pmremGenerator = new THREE.PMREMGenerator(renderer); + + scene.environment = pmremGenerator.fromScene(environment, 0.04).texture; + scene.environmentIntensity = 1.25; + pmremGenerator.dispose(); + + // + + renderPipeline = new THREE.RenderPipeline(renderer); + + const scenePass = pass(scene, camera); + scenePass.setMRT( + mrt({ + output: output, + normal: directionToColor(normalView), + metalrough: vec2(metalness, roughness), // pack metalness and roughness into a single attachment + }), + ); + + const scenePassColor = scenePass.getTextureNode('output').toInspector('Color'); + const scenePassNormal = scenePass.getTextureNode('normal').toInspector('Normal', node => { + return colorSpaceToWorking(node, THREE.SRGBColorSpace); + }); + const scenePassDepth = scenePass.getTextureNode('depth').toInspector('Depth', () => { + return scenePass.getLinearDepthNode(); + }); + const scenePassMetalRough = scenePass.getTextureNode('metalrough').toInspector('Metalness-Roughness'); + + // optional: optimize bandwidth by reducing the texture precision for normals and metal/roughness + + const normalTexture = scenePass.getTexture('normal'); + normalTexture.type = THREE.UnsignedByteType; + + const metalRoughTexture = scenePass.getTexture('metalrough'); + metalRoughTexture.type = THREE.UnsignedByteType; + + const sceneNormal = sample(uv => { + return colorToDirection(scenePassNormal.sample(uv)); + }); + + // + + ssrPass = ssr( + scenePassColor, + scenePassDepth, + sceneNormal, + scenePassMetalRough.r, + scenePassMetalRough.g, + ).toInspector('SSR'); + + // blend SSR over beauty (SSR outputs premultiplied color, so use additive blending) + + const outputNode = smaa(scenePassColor.add(ssrPass.rgb)); + + renderPipeline.outputNode = outputNode; + + // + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + controls.update(); + + window.addEventListener('resize', onWindowResize); + + // GUI + + const gui = renderer.inspector.createParameters('Settings'); + const ssrFolder = gui.addFolder('SSR'); + ssrFolder.add(params, 'quality', 0, 1).onChange(updateParameters); + ssrFolder.add(params, 'blurQuality', 1, 3, 1).onChange(updateParameters); + ssrFolder.add(params, 'maxDistance', 0, 1).onChange(updateParameters); + ssrFolder.add(params, 'opacity', 0, 1).onChange(updateParameters); + ssrFolder.add(params, 'thickness', 0, 0.05).onChange(updateParameters); + ssrFolder.add(params, 'enabled').onChange(() => { + if (params.enabled === true) { + renderPipeline.outputNode = outputNode; + } else { + renderPipeline.outputNode = scenePass; + } + + renderPipeline.needsUpdate = true; + }); + const modelFolder = gui.addFolder('Model'); + modelFolder.add(params, 'roughness', 0, 1).onChange(value => { + model.traverse(function (object) { + if (object.material) { + object.material.roughness = value; + } + }); + }); + + updateParameters(); +} + +function updateParameters() { + ssrPass.quality.value = params.quality; + ssrPass.blurQuality.value = params.blurQuality; + ssrPass.maxDistance.value = params.maxDistance; + ssrPass.opacity.value = params.opacity; + ssrPass.thickness.value = params.thickness; +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + controls.update(); + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_postprocessing_sss.ts b/examples-testing/examples/webgpu_postprocessing_sss.ts new file mode 100644 index 000000000..42cc26f39 --- /dev/null +++ b/examples-testing/examples/webgpu_postprocessing_sss.ts @@ -0,0 +1,185 @@ +import * as THREE from 'three/webgpu'; +import { pass, vec3, vec4, mrt, screenUV, velocity, builtinShadowContext } from 'three/tsl'; +import { sss } from 'three/addons/tsl/display/SSSNode.js'; +import { traa } from 'three/addons/tsl/display/TRAANode.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer, renderPipeline, controls; + +init(); + +async function init() { + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(1, 2.5, -3.5); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xa0a0a0); + scene.fog = new THREE.Fog(0xa0a0a0, 10, 50); + + // lights + + const hemiLight = new THREE.HemisphereLight(0xffffff, 0x8d8d8d, 2); + hemiLight.position.set(0, 20, 0); + scene.add(hemiLight); + + const dirLight = new THREE.DirectionalLight(0xffffff, 3); + dirLight.position.set(-3, 10, -10); + dirLight.castShadow = true; + dirLight.shadow.bias = -0.001; // remove self-shadowing artifacts + dirLight.shadow.camera.top = 4; + dirLight.shadow.camera.bottom = -4; + dirLight.shadow.camera.left = -4; + dirLight.shadow.camera.right = 4; + dirLight.shadow.camera.near = 0.1; + dirLight.shadow.camera.far = 40; + dirLight.shadow.mapSize.width = 1024; + dirLight.shadow.mapSize.height = 1024; + scene.add(dirLight); + + // scene.add( new THREE.CameraHelper( dirLight.shadow.camera ) ); + + // ground + + const mesh = new THREE.Mesh( + new THREE.PlaneGeometry(100, 100), + new THREE.MeshPhongMaterial({ color: 0xcbcbcb, depthWrite: false }), + ); + mesh.rotation.x = -Math.PI / 2; + mesh.receiveShadow = true; + scene.add(mesh); + + // + + const dracoLoader = new DRACOLoader(); + dracoLoader.setDecoderPath('jsm/libs/draco/gltf/'); + + const loader = new GLTFLoader(); + loader.setDRACOLoader(dracoLoader); + loader.load('models/gltf/nemetona.glb', function (gltf) { + const model = gltf.scene; + model.rotation.y = Math.PI; + model.scale.setScalar(10); + model.position.y = 0.45; + + scene.add(model); + + model.traverse(function (object) { + if (object.isMesh) { + object.castShadow = true; + object.receiveShadow = true; + object.material.aoMap = null; // remove AO to better see the effect of shadows + } + }); + }); + + // + + renderer = new THREE.WebGPURenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.shadowMap.enabled = true; + renderer.shadowMap.type = THREE.PCFSoftShadowMap; + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // post-processing + + renderPipeline = new THREE.RenderPipeline(renderer); + + // pre-pass + + const prePass = pass(scene, camera); + prePass.name = 'Pre-Pass'; + prePass.transparent = false; + + prePass.setMRT( + mrt({ + output: velocity, + }), + ); + + const prePassDepth = prePass.getTextureNode('depth').toInspector('Depth', () => prePass.getLinearDepthNode()); + const prePassVelocity = prePass.getTextureNode('output').toInspector('Velocity'); + + // scene pass + + const scenePass = pass(scene, camera).toInspector('Color'); + + // sss + + const sssPass = sss(prePassDepth, camera, dirLight); + sssPass.maxDistance.value = 0.2; + sssPass.useTemporalFiltering = true; + + // scene context + + const sssSample = sssPass.getTextureNode().sample(screenUV).r; + const sssContext = builtinShadowContext(sssSample, dirLight); + + scenePass.contextNode = sssContext; + + // traa + + const traaPass = traa(scenePass, prePassDepth, prePassVelocity, camera); + renderPipeline.outputNode = traaPass; + + // + + controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 1; + controls.maxDistance = 20; + controls.target.set(0, 2, 0); + controls.enableDamping = true; + controls.update(); + + // + + const params = { + output: 0, + }; + + const types = { 'Scene with Shadow Maps + SSS': 0, 'Scene with Shadow Maps': 1, SSS: 2 }; + + const gui = renderer.inspector.createParameters('SSS settings'); + gui.add(params, 'output', types).onChange(updatePostprocessing); + gui.add(sssPass.shadowIntensity, 'value', 0, 1).name('shadow intensity'); + gui.add(sssPass.maxDistance, 'value', 0.01, 1).name('max ray distance'); + gui.add(sssPass.quality, 'value', 0, 1).name('quality'); + gui.add(sssPass.thickness, 'value', 0.01, 0.1).name('thickness'); + gui.add(sssPass, 'useTemporalFiltering').name('temporal filtering').onChange(updatePostprocessing); + + function updatePostprocessing() { + scenePass.contextNode = params.output !== 1 ? sssContext : null; + + if (params.output === 2) { + renderPipeline.outputNode = vec4(vec3(sssPass.r), 1); + } else { + renderPipeline.outputNode = sssPass.useTemporalFiltering ? traaPass : scenePass; + } + + renderPipeline.needsUpdate = true; + } + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); +} + +function animate() { + controls.update(); + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_postprocessing_traa.ts b/examples-testing/examples/webgpu_postprocessing_traa.ts new file mode 100644 index 000000000..01d17ca17 --- /dev/null +++ b/examples-testing/examples/webgpu_postprocessing_traa.ts @@ -0,0 +1,93 @@ +import * as THREE from 'three/webgpu'; +import { mrt, output, pass, velocity } from 'three/tsl'; +import { traa } from 'three/addons/tsl/display/TRAANode.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer, renderPipeline; +let index = 0; + +init(); + +function init() { + renderer = new THREE.WebGPURenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 10); + camera.position.z = 2.5; + + scene = new THREE.Scene(); + + const geometry = new THREE.BoxGeometry(); + const material1 = new THREE.MeshBasicMaterial({ color: 0xffffff, wireframe: true }); + + const mesh1 = new THREE.Mesh(geometry, material1); + mesh1.position.x = -1; + scene.add(mesh1); + + const texture = new THREE.TextureLoader().load('textures/brick_diffuse.jpg'); + texture.minFilter = THREE.NearestFilter; + texture.magFilter = THREE.NearestFilter; + texture.generateMipmaps = false; + texture.colorSpace = THREE.SRGBColorSpace; + + const material2 = new THREE.MeshBasicMaterial({ map: texture }); + + const mesh2 = new THREE.Mesh(geometry, material2); + mesh2.position.x = 1; + scene.add(mesh2); + + // postprocessing + + renderPipeline = new THREE.RenderPipeline(renderer); + const scenePass = pass(scene, camera); + scenePass.setMRT( + mrt({ + output: output, + velocity: velocity, + }), + ); + + const scenePassColor = scenePass.getTextureNode('output').toInspector('Color'); + const scenePassDepth = scenePass.getTextureNode('depth').toInspector('Depth', () => { + return scenePass.getLinearDepthNode(); + }); + const scenePassVelocity = scenePass.getTextureNode('velocity').toInspector('Velocity'); + + const traaNode = traa(scenePassColor, scenePassDepth, scenePassVelocity, camera); + + renderPipeline.outputNode = traaNode; + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); +} + +function animate() { + index++; + + if (Math.round(index / 200) % 2 === 0) { + for (let i = 0; i < scene.children.length; i++) { + const child = scene.children[i]; + + child.rotation.x += 0.005; + child.rotation.y += 0.01; + } + } + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_postprocessing_transition.ts b/examples-testing/examples/webgpu_postprocessing_transition.ts new file mode 100644 index 000000000..1a5a88fc1 --- /dev/null +++ b/examples-testing/examples/webgpu_postprocessing_transition.ts @@ -0,0 +1,207 @@ +import * as THREE from 'three/webgpu'; + +import TWEEN from 'three/addons/libs/tween.module.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { uniform, pass } from 'three/tsl'; +import { transition } from 'three/addons/tsl/display/TransitionNode.js'; + +let renderer, renderPipeline, transitionController, transitionPass; + +const textures = []; +const timer = new THREE.Timer(); +timer.connect(document); + +const effectController = { + animateScene: true, + animateTransition: true, + transition: 0, + _transition: uniform(0), + useTexture: true, + _useTexture: uniform(1), + texture: 5, + cycle: true, + threshold: uniform(0.1), +}; + +function generateInstancedMesh(geometry, material, count) { + const mesh = new THREE.InstancedMesh(geometry, material, count); + + const dummy = new THREE.Object3D(); + const color = new THREE.Color(); + + for (let i = 0; i < count; i++) { + dummy.position.x = Math.random() * 100 - 50; + dummy.position.y = Math.random() * 60 - 30; + dummy.position.z = Math.random() * 80 - 40; + + dummy.rotation.x = Math.random() * 2 * Math.PI; + dummy.rotation.y = Math.random() * 2 * Math.PI; + dummy.rotation.z = Math.random() * 2 * Math.PI; + + dummy.scale.x = Math.random() * 2 + 1; + + if (geometry.type === 'BoxGeometry') { + dummy.scale.y = Math.random() * 2 + 1; + dummy.scale.z = Math.random() * 2 + 1; + } else { + dummy.scale.y = dummy.scale.x; + dummy.scale.z = dummy.scale.x; + } + + dummy.updateMatrix(); + + mesh.setMatrixAt(i, dummy.matrix); + mesh.setColorAt(i, color.setScalar(0.1 + 0.9 * Math.random())); + } + + return mesh; +} + +function FXScene(geometry, rotationSpeed, backgroundColor) { + const camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.z = 20; + + // Setup scene + const scene = new THREE.Scene(); + scene.background = new THREE.Color(backgroundColor); + scene.add(new THREE.AmbientLight(0xaaaaaa, 3)); + + const light = new THREE.DirectionalLight(0xffffff, 3); + light.position.set(0, 1, 4); + scene.add(light); + + this.rotationSpeed = rotationSpeed; + + const color = geometry.type === 'BoxGeometry' ? 0x0000ff : 0xff0000; + const material = new THREE.MeshPhongNodeMaterial({ color: color, flatShading: true }); + const mesh = generateInstancedMesh(geometry, material, 500); + scene.add(mesh); + + this.scene = scene; + this.camera = camera; + this.mesh = mesh; + + this.update = function (delta) { + if (effectController.animateScene) { + mesh.rotation.x += this.rotationSpeed.x * delta; + mesh.rotation.y += this.rotationSpeed.y * delta; + mesh.rotation.z += this.rotationSpeed.z * delta; + } + }; + + this.resize = function () { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + }; +} + +const fxSceneA = new FXScene(new THREE.BoxGeometry(2, 2, 2), new THREE.Vector3(0, -0.4, 0), 0xffffff); +const fxSceneB = new FXScene(new THREE.IcosahedronGeometry(1, 1), new THREE.Vector3(0, 0.2, 0.1), 0x000000); + +function init() { + // Initialize textures + + const loader = new THREE.TextureLoader(); + + for (let i = 0; i < 6; i++) { + textures[i] = loader.load('textures/transition/transition' + (i + 1) + '.png'); + } + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.inspector = new Inspector(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + renderPipeline = new THREE.RenderPipeline(renderer); + + const scenePassA = pass(fxSceneA.scene, fxSceneA.camera); + const scenePassB = pass(fxSceneB.scene, fxSceneB.camera); + + transitionPass = transition( + scenePassA, + scenePassB, + new THREE.TextureNode(textures[effectController.texture]), + effectController._transition, + effectController.threshold, + effectController._useTexture, + ); + + renderPipeline.outputNode = transitionPass; + + const gui = renderer.inspector.createParameters('Settings'); + + gui.add(effectController, 'animateScene').name('Animate Scene'); + gui.add(effectController, 'animateTransition').name('Animate Transition'); + transitionController = gui + .add(effectController, 'transition', 0, 1, 0.01) + .name('transition') + .onChange(() => { + effectController._transition.value = effectController.transition; + }); + gui.add(effectController, 'useTexture').onChange(() => { + const value = effectController.useTexture ? 1 : 0; + effectController._useTexture.value = value; + }); + gui.add(effectController, 'texture', { Perlin: 0, Squares: 1, Cells: 2, Distort: 3, Gradient: 4, Radial: 5 }); + gui.add(effectController, 'cycle'); + gui.add(effectController.threshold, 'value', 0, 1, 0.01).name('threshold'); +} + +window.addEventListener('resize', onWindowResize); + +function onWindowResize() { + fxSceneA.resize(); + fxSceneB.resize(); + renderer.setSize(window.innerWidth, window.innerHeight); +} + +new TWEEN.Tween(effectController) + .to({ transition: 1 }, 1500) + .onUpdate(function () { + transitionController.setValue(effectController.transition); + + // Change the current alpha texture after each transition + if (effectController.cycle) { + if (effectController.transition == 0 || effectController.transition == 1) { + effectController.texture = (effectController.texture + 1) % textures.length; + } + } + }) + .repeat(Infinity) + .delay(2000) + .yoyo(true) + .start(); + +function animate() { + timer.update(); + + if (effectController.animateTransition) TWEEN.update(); + + if (textures[effectController.texture]) { + const mixTexture = textures[effectController.texture]; + transitionPass.mixTextureNode.value = mixTexture; + } + + const delta = timer.getDelta(); + fxSceneA.update(delta); + fxSceneB.update(delta); + + render(); +} + +function render() { + // Prevent render both scenes when it's not necessary + if (effectController.transition === 0) { + renderer.render(fxSceneB.scene, fxSceneB.camera); + } else if (effectController.transition === 1) { + renderer.render(fxSceneA.scene, fxSceneA.camera); + } else { + renderPipeline.render(); + } +} + +init(); diff --git a/examples-testing/examples/webgpu_procedural_texture.ts b/examples-testing/examples/webgpu_procedural_texture.ts new file mode 100644 index 000000000..21fb4a3c2 --- /dev/null +++ b/examples-testing/examples/webgpu_procedural_texture.ts @@ -0,0 +1,75 @@ +import * as THREE from 'three/webgpu'; +import { checker, uv, uniform, convertToTexture } from 'three/tsl'; +import { gaussianBlur } from 'three/addons/tsl/display/GaussianBlurNode.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer; + +init(); + +function init() { + const aspect = window.innerWidth / window.innerHeight; + camera = new THREE.OrthographicCamera(-aspect, aspect, 1, -1, 0, 2); + camera.position.z = 1; + + scene = new THREE.Scene(); + + // procedural to texture + + const uvScale = uniform(4); + const blurAmount = uniform(0.5); + + const procedural = checker(uv().mul(uvScale)); + const proceduralToTexture = convertToTexture(procedural, 512, 512); // ( node, width, height ) + + const colorNode = gaussianBlur(proceduralToTexture, blurAmount, 20); + + // extra + + //proceduralToTexture.autoUpdate = false; // update just once + //proceduralToTexture.textureNeedsUpdate = true; // manually update + + // scene + + const material = new THREE.MeshBasicNodeMaterial(); + material.colorNode = colorNode; + + const plane = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), material); + scene.add(plane); + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(render); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + window.addEventListener('resize', onWindowResize); + + // gui + + const gui = renderer.inspector.createParameters('Procedural Texture'); + gui.add(uvScale, 'value', 1, 10).name('uv scale ( before rtt )'); + gui.add(blurAmount, 'value', 0, 2).name('blur amount ( after rtt )'); + gui.add(proceduralToTexture, 'autoUpdate').name('auto update'); +} + +function onWindowResize() { + renderer.setSize(window.innerWidth, window.innerHeight); + + const aspect = window.innerWidth / window.innerHeight; + + const frustumHeight = camera.top - camera.bottom; + + camera.left = (-frustumHeight * aspect) / 2; + camera.right = (frustumHeight * aspect) / 2; + + camera.updateProjectionMatrix(); +} + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_reflection.ts b/examples-testing/examples/webgpu_reflection.ts new file mode 100644 index 000000000..d5dd23318 --- /dev/null +++ b/examples-testing/examples/webgpu_reflection.ts @@ -0,0 +1,345 @@ +import * as THREE from 'three/webgpu'; + +import { + abs, + blendOverlay, + color, + float, + Fn, + instancedBufferAttribute, + max, + normalWorldGeometry, + pass, + positionGeometry, + positionLocal, + pow2, + reflector, + screenUV, + sin, + sub, + texture, + time, + uniform, + uv, + vec2, + vec3, +} from 'three/tsl'; +import { gaussianBlur } from 'three/addons/tsl/display/GaussianBlurNode.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import TWEEN from 'three/addons/libs/tween.module.js'; + +let camera, scene, renderer; +let renderPipeline; +let controls; + +// below uniforms will be animated via TWEEN.js + +const uniformEffector1 = uniform(-0.2); +const uniformEffector2 = uniform(-0.2); + +init(); + +async function init() { + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.25, 30); + camera.position.set(4, 2, 4); + + scene = new THREE.Scene(); + scene.fog = new THREE.Fog(0x4195a4, 1, 20); + scene.backgroundNode = normalWorldGeometry.y.mix(color(0x4195a4), color(0x0066ff)); + camera.lookAt(0, 1, 0); + + const sunLight = new THREE.DirectionalLight(0xffe499, 2); + sunLight.position.set(7, 5, 7); + sunLight.castShadow = true; + sunLight.shadow.camera.zoom = 1.5; + sunLight.shadow.mapSize.set(1024, 1024); + sunLight.shadow.bias = -0.0001; // remove self-shadowing artifacts + scene.add(sunLight); + + const backLight = new THREE.DirectionalLight(0x0487e2, 0.5); + backLight.position.set(7, -5, 7); + scene.add(backLight); + + // textures + + const textureLoader = new THREE.TextureLoader(); + + const floorColor = await textureLoader.loadAsync('textures/floors/FloorsCheckerboard_S_Diffuse.jpg'); + floorColor.wrapS = THREE.RepeatWrapping; + floorColor.wrapT = THREE.RepeatWrapping; + floorColor.colorSpace = THREE.SRGBColorSpace; + floorColor.repeat.set(15, 15); + + const floorNormal = await textureLoader.loadAsync('textures/floors/FloorsCheckerboard_S_Normal.jpg'); + floorNormal.wrapS = THREE.RepeatWrapping; + floorNormal.wrapT = THREE.RepeatWrapping; + floorNormal.repeat.set(15, 15); + + // tree + + const treeMesh = createTreeMesh(); + treeMesh.castShadow = true; + treeMesh.receiveShadow = true; + scene.add(treeMesh); + + // floor + + const floorUV = uv().mul(15); + const floorNormalOffset = texture(floorNormal, floorUV).xy.mul(2).sub(1).mul(0.02); + + const reflection = reflector({ resolutionScale: 0.2 }); + reflection.target.rotateX(-Math.PI / 2); + reflection.uvNode = reflection.uvNode.add(floorNormalOffset); + scene.add(reflection.target); + + const floorMaterial = new THREE.MeshPhongNodeMaterial(); + floorMaterial.colorNode = texture(floorColor, floorUV); + floorMaterial.emissiveNode = reflection.mul(0.25); + floorMaterial.normalMap = floorNormal; + floorMaterial.normalScale.set(0.2, -0.2); + + const floor = new THREE.Mesh(new THREE.BoxGeometry(50, 0.001, 50), floorMaterial); + floor.receiveShadow = true; + scene.add(floor); + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.shadowMap.enabled = true; + renderer.shadowMap.type = THREE.PCFSoftShadowMap; + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // controls + + controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 1; + controls.maxDistance = 10; + controls.maxPolarAngle = Math.PI / 2; + controls.enableDamping = true; + controls.autoRotate = true; + controls.autoRotateSpeed = 1; + controls.target.set(0, 1, 0); + controls.update(); + + // post-processing + + const scenePass = pass(scene, camera); + const scenePassColor = scenePass.getTextureNode(); + const scenePassDepth = scenePass.getLinearDepthNode().remapClamp(0.3, 0.7); + + const scenePassColorBlurred = gaussianBlur(scenePassColor); + scenePassColorBlurred.directionNode = scenePassDepth; + + const vignette = screenUV.distance(0.5).mul(1.25).clamp().oneMinus().sub(0.2); + + renderPipeline = new THREE.RenderPipeline(renderer); + renderPipeline.outputNode = blendOverlay(scenePassColorBlurred, vignette); + + // tweens + + new TWEEN.Tween(uniformEffector1) + .to({ value: 1.2 }, 3000) + .delay(800) + .repeat(Infinity) + .easing(TWEEN.Easing.Sinusoidal.InOut) + .start(); + + new TWEEN.Tween(uniformEffector2) + .to({ value: 1.2 }, 3000) + .repeat(Infinity) + .easing(TWEEN.Easing.Sinusoidal.InOut) + .start(); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + controls.update(); + + TWEEN.update(); + + renderPipeline.render(); +} + +function random() { + return (Math.random() - 0.5) * 2.0; +} + +function createTreeMesh() { + const maxSteps = 5; + const lengthMult = 0.8; + + const positions = []; + const normals = []; + const colors = []; + const data = []; // will save seed, size and time + + let instanceCount = 0; + + const newPosition = new THREE.Vector3(); + const position = new THREE.Vector3(); + const normal = new THREE.Vector3(); + const color = new THREE.Color(); + + function createTreePart(angle, x, y, z, length, count) { + if (Math.random() > (maxSteps / count) * 0.25) return; + + if (count < maxSteps) { + const newLength = length * lengthMult; + const newX = x + Math.cos(angle) * length; + const newY = y + Math.sin(angle) * length; + const countSq = Math.min(3.2, count * count); + const newZ = z + (Math.random() * countSq - countSq / 2) * length; + + let size = 30 - count * 8; + if (size > 25) size = 25; + if (size < 10) size = 10; + + size = size / 100; + + const subSteps = 50; + + // below loop generates the instanced data for a tree part + + for (let i = 0; i < subSteps; i++) { + instanceCount++; + + const percent = i / subSteps; + const extra = 1 / maxSteps; + + // position + + newPosition.set(x, y, z).lerp(new THREE.Vector3(newX, newY, newZ), percent); + position.copy(newPosition); + + position.x += random() * size * 3; + position.y += random() * size * 3; + position.z += random() * size * 3; + + positions.push(position.x, position.y, position.z); + + const scale = Math.random() + 5; + + // normal + + normal.copy(position).sub(newPosition).normalize(); + normals.push(normal.x, normal.y, normal.z); + + // color + + color.setHSL((count / maxSteps) * 0.5 + Math.random() * 0.05, 0.75, 0.6 + Math.random() * 0.1); + colors.push(color.r, color.g, color.b); + + // to save vertex buffers, we store the size, time and seed in a single attribute + + const instanceSize = size * scale; + const instanceTime = count / maxSteps + percent * extra; + const instanceSeed = Math.random(); + + data.push(instanceSize, instanceTime, instanceSeed); + } + + createTreePart(angle + random(), newX, newY, newZ, newLength + random(), count + 1); + createTreePart(angle + random(), newX, newY, newZ, newLength + random(), count + 1); + createTreePart(angle + random(), newX, newY, newZ, newLength + random(), count + 1); + createTreePart(angle + random(), newX, newY, newZ, newLength + random(), count + 1); + createTreePart(angle + random(), newX, newY, newZ, newLength + random(), count + 1); + createTreePart(angle + random(), newX, newY, newZ, newLength + random(), count + 1); + } + } + + const angle = Math.PI * 0.5; + + // the tree is represented as a collection of instances boxes generated with below recursive function + + createTreePart(angle, 0, 0, 0, 16, 0); + + const geometry = new THREE.BoxGeometry(); + const material = new THREE.MeshStandardNodeMaterial(); + const mesh = new THREE.Mesh(geometry, material); + mesh.scale.setScalar(0.05); + mesh.count = instanceCount; + mesh.frustumCulled = false; + + // instanced data + + const attributePosition = new THREE.InstancedBufferAttribute(new Float32Array(positions), 3); + const attributeNormal = new THREE.InstancedBufferAttribute(new Float32Array(normals), 3); + const attributeColor = new THREE.InstancedBufferAttribute(new Float32Array(colors), 3); + const attributeData = new THREE.InstancedBufferAttribute(new Float32Array(data), 3); + + // TSL + + const instancePosition = instancedBufferAttribute(attributePosition); + const instanceNormal = instancedBufferAttribute(attributeNormal); + const instanceColor = instancedBufferAttribute(attributeColor); + const instanceData = instancedBufferAttribute(attributeData); + + material.positionNode = Fn(() => { + const instanceSize = instanceData.x; + const instanceTime = instanceData.y; + const instanceSeed = instanceData.z; + + // effectors (these are responsible for the blob-like scale effects) + + const dif1 = abs(instanceTime.sub(uniformEffector1)).toConst(); + let effect = dif1.lessThanEqual(0.15).select(sub(0.15, dif1).mul(sub(1.7, instanceTime).mul(10)), float(0)); + + const dif2 = abs(instanceTime.sub(uniformEffector2)).toConst(); + effect = dif2.lessThanEqual(0.15).select(sub(0.15, dif2).mul(sub(1.7, instanceTime).mul(10)), effect); + + // accumulate different vertex animations + + let animated = positionLocal.add(instancePosition).toVar(); + const direction = positionGeometry.normalize().toConst(); + + animated = animated.add(direction.mul(effect.add(instanceSize))); + animated = animated.sub(direction.mul(effect)); + animated = animated.add(instanceNormal.mul(effect.mul(1))); + animated = animated.add(instanceNormal.mul(abs(sin(time.add(instanceSeed.mul(2))).mul(1.5)))); + + return animated; + })(); + + const squareEdge = Fn(() => { + const pos = uv().sub(vec2(0.5, 0.5)); + const squareDistance = max(abs(pos.x), abs(pos.y)); + return squareDistance.div(0.5).clamp(0.85, 1).sub(0.5).mul(2.0); + })(); + + material.colorNode = Fn(() => { + return squareEdge.sub(instanceColor); + })(); + + material.emissiveNode = Fn(() => { + const instanceTime = instanceData.y; + + const dif1 = abs(instanceTime.sub(uniformEffector1)).toConst(); + const effect1 = dif1.lessThanEqual(0.15).select(sub(0.15, dif1).mul(sub(1.7, instanceTime).mul(10)), float(0)); + + const dif2 = abs(instanceTime.sub(uniformEffector2)).toConst(); + const effect2 = dif2.lessThanEqual(0.15).select(sub(0.15, dif2).mul(sub(1.7, instanceTime).mul(10)), effect1); + + return pow2(vec3(effect1, 0, effect2)).mul(instanceColor); + })(); + + return mesh; +} diff --git a/examples-testing/examples/webgpu_reflection_roughness.ts b/examples-testing/examples/webgpu_reflection_roughness.ts new file mode 100644 index 000000000..2b6b4a78d --- /dev/null +++ b/examples-testing/examples/webgpu_reflection_roughness.ts @@ -0,0 +1,121 @@ +import * as THREE from 'three/webgpu'; +import { Fn, vec2, vec4, texture, uv, textureBicubic, rangeFogFactor, reflector, time } from 'three/tsl'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer; +let controls; + +init(); + +async function init() { + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.25, 30); + camera.position.set(-4, 1, 4); + + scene = new THREE.Scene(); + + const loader = new UltraHDRLoader(); + loader.load('textures/equirectangular/spruit_sunrise_2k.hdr.jpg', function (texture) { + texture.mapping = THREE.EquirectangularReflectionMapping; + texture.needsUpdate = true; + + scene.background = texture; + scene.environment = texture; + }); + + // textures + + const textureLoader = new THREE.TextureLoader(); + + const uvMap = textureLoader.load('textures/uv_grid_directx.jpg'); + uvMap.colorSpace = THREE.SRGBColorSpace; + + const perlinMap = textureLoader.load('./textures/noises/perlin/rgb-256x256.png'); + perlinMap.wrapS = THREE.RepeatWrapping; + perlinMap.wrapT = THREE.RepeatWrapping; + perlinMap.colorSpace = THREE.SRGBColorSpace; + + // uv box for debugging + + const mesh = new THREE.Mesh( + new THREE.BoxGeometry(1, 1, 1), + new THREE.MeshStandardNodeMaterial({ + map: uvMap, + roughnessMap: uvMap, + emissiveMap: uvMap, + emissive: 0xffffff, + }), + ); + mesh.position.set(0, 1.25, 0); + mesh.scale.setScalar(2); + scene.add(mesh); + + // reflection + + const reflection = reflector({ resolutionScale: 0.5, bounces: false, generateMipmaps: true }); // 0.5 is half of the rendering view + reflection.target.rotateX(-Math.PI / 2); + scene.add(reflection.target); + + const animatedUV = uv() + .mul(10) + .add(vec2(time.mul(0.1), 0)); + const roughness = texture(perlinMap, animatedUV).r.mul(2).saturate(); + + const floorMaterial = new THREE.MeshStandardNodeMaterial(); + floorMaterial.transparent = true; + floorMaterial.metalness = 1; + floorMaterial.roughnessNode = roughness.mul(0.2); + floorMaterial.colorNode = Fn(() => { + // blur reflection using textureBicubic() + const dirtyReflection = textureBicubic(reflection, roughness.mul(0.9)); + + // falloff opacity by distance like an opacity-fog + const opacity = rangeFogFactor(7, 25).oneMinus(); + + return vec4(dirtyReflection.rgb, opacity); + })(); + + const floor = new THREE.Mesh(new THREE.BoxGeometry(50, 0.001, 50), floorMaterial); + floor.position.set(0, 0, 0); + scene.add(floor); + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.toneMapping = THREE.NeutralToneMapping; + renderer.toneMappingExposure = 1.5; + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 1; + controls.maxDistance = 10; + controls.maxPolarAngle = Math.PI / 2; + controls.autoRotate = true; + controls.autoRotateSpeed = -0.1; + controls.target.set(0, 0.75, 0); + controls.update(); + + // events + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + controls.update(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_refraction.ts b/examples-testing/examples/webgpu_refraction.ts new file mode 100644 index 000000000..d475ed2de --- /dev/null +++ b/examples-testing/examples/webgpu_refraction.ts @@ -0,0 +1,144 @@ +import * as THREE from 'three/webgpu'; +import { viewportSafeUV, viewportSharedTexture, screenUV, texture, uv } from 'three/tsl'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer; + +let cameraControls; + +let smallSphere; + +init(); + +function init() { + // scene + scene = new THREE.Scene(); + + // camera + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 500); + camera.position.set(0, 50, 160); + + // + + const geometry = new THREE.IcosahedronGeometry(5, 0); + const material = new THREE.MeshPhongMaterial({ color: 0xffffff, emissive: 0x7b7b7b, flatShading: true }); + smallSphere = new THREE.Mesh(geometry, material); + scene.add(smallSphere); + + // textures + + const loader = new THREE.TextureLoader(); + + const floorNormal = loader.load('textures/floors/FloorsCheckerboard_S_Normal.jpg'); + floorNormal.wrapS = THREE.RepeatWrapping; + floorNormal.wrapT = THREE.RepeatWrapping; + + // refractor + + const verticalNormalScale = 0.1; + const verticalUVOffset = texture(floorNormal, uv().mul(5)).xy.mul(2).sub(1).mul(verticalNormalScale); + + const refractorUV = screenUV.add(verticalUVOffset); + const verticalRefractor = viewportSharedTexture(viewportSafeUV(refractorUV)).toInspector('Viewport Texture'); + + const planeGeo = new THREE.PlaneGeometry(100.1, 100.1); + + const planeRefractor = new THREE.Mesh( + planeGeo, + new THREE.MeshBasicNodeMaterial({ + backdropNode: verticalRefractor, + }), + ); + planeRefractor.material.transparent = true; + planeRefractor.position.y = 50; + scene.add(planeRefractor); + + // walls + + const planeTop = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xffffff })); + planeTop.position.y = 100; + planeTop.rotateX(Math.PI / 2); + scene.add(planeTop); + + const planeBottom = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xffffff })); + planeBottom.rotateX(-Math.PI / 2); + scene.add(planeBottom); + + const planeBack = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0x7f7fff })); + planeBack.position.z = -50; + planeBack.position.y = 50; + scene.add(planeBack); + + const planeRight = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0x00ff00 })); + planeRight.position.x = 50; + planeRight.position.y = 50; + planeRight.rotateY(-Math.PI / 2); + scene.add(planeRight); + + const planeLeft = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xff0000 })); + planeLeft.position.x = -50; + planeLeft.position.y = 50; + planeLeft.rotateY(Math.PI / 2); + scene.add(planeLeft); + + // lights + + const mainLight = new THREE.PointLight(0xe7e7e7, 2.5, 250, 0); + mainLight.position.y = 60; + scene.add(mainLight); + + const greenLight = new THREE.PointLight(0x00ff00, 0.5, 1000, 0); + greenLight.position.set(550, 50, 0); + scene.add(greenLight); + + const redLight = new THREE.PointLight(0xff0000, 0.5, 1000, 0); + redLight.position.set(-550, 50, 0); + scene.add(redLight); + + const blueLight = new THREE.PointLight(0xbbbbfe, 0.5, 1000, 0); + blueLight.position.set(0, 50, 550); + scene.add(blueLight); + + // renderer + + renderer = new THREE.WebGPURenderer(/*{ antialias: true }*/); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // controls + + cameraControls = new OrbitControls(camera, renderer.domElement); + cameraControls.target.set(0, 50, 0); + cameraControls.maxDistance = 400; + cameraControls.minDistance = 10; + cameraControls.update(); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + const timer = Date.now() * 0.01; + + smallSphere.position.set( + Math.cos(timer * 0.1) * 30, + Math.abs(Math.cos(timer * 0.2)) * 20 + 5, + Math.sin(timer * 0.1) * 30, + ); + smallSphere.rotation.y = Math.PI / 2 - timer * 0.1; + smallSphere.rotation.z = timer * 0.8; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_rendertarget_2d-array_3d.ts b/examples-testing/examples/webgpu_rendertarget_2d-array_3d.ts new file mode 100644 index 000000000..627fd6b18 --- /dev/null +++ b/examples-testing/examples/webgpu_rendertarget_2d-array_3d.ts @@ -0,0 +1,237 @@ +import * as THREE from 'three/webgpu'; +import { vec2, uniform, screenUV, color, texture, diffuseColor, attribute, vec3, vec4 } from 'three/tsl'; + +import { TextureHelper } from 'three/addons/helpers/TextureHelperGPU.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { unzipSync } from 'three/addons/libs/fflate.module.js'; + +let renderer; +let views = []; + +class View { + constructor(left, top, width, height) { + this.left = left; + this.top = top; + this.width = width; + this.height = height; + + const aspect = (window.innerWidth * width) / (window.innerHeight * height); + + // Set up perspective camera + this.camera = new THREE.PerspectiveCamera(50, aspect, 0.1, 100); + this.camera.position.set(-7, 0, 10); + this.camera.lookAt(0, 0, 0); + this.camera.updateProjectionMatrix(); + + this.scene = new THREE.Scene(); + + const normalizedUV = screenUV.mul(vec2(1, -1)).add(vec2(0, 1)); // Flip Y and offset + + // Calculate viewport center in normalized coordinates + const viewportCenter = vec2( + this.left + this.width * 0.5, + this.top + this.height * 0.5, // Invert Y coordinate for proper alignment + ); + + const distanceEffect = normalizedUV.distance(viewportCenter).smoothstep(0, 0.2); + + const backgroundEffect = color(this.top > 0 ? 0x212121 : 0x616161).sub(distanceEffect.pow(0.3).mul(0.1)); + + this.scene.backgroundNode = backgroundEffect; + } + + // Method to handle viewport resize + updateSize(left, top, width, height) { + this.left = left; + this.top = top; + this.width = width; + this.height = height; + + const aspect = (window.innerWidth * width) / (window.innerHeight * height); + this.camera.aspect = aspect; + this.camera.updateProjectionMatrix(); + } +} + +async function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + renderer = new THREE.WebGPURenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.autoClear = false; + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + await renderer.init(); + + // Create views after renderer initialization + views = [ + new View(0.0, 0.0, 0.5, 0.5), + new View(0.5, 0.0, 0.5, 0.5), + new View(0.0, 0.5, 0.5, 0.5), + new View(0.5, 0.5, 0.5, 0.5), + ]; + + // Add OrbitControls after views and renderer are created + views.forEach(view => { + view.controls = new OrbitControls(view.camera, renderer.domElement); + view.controls.minDistance = 1; + view.controls.maxDistance = 20; + view.controls.minAzimuthAngle = -Math.PI / 3; + view.controls.maxAzimuthAngle = Math.PI / 3; + view.controls.minPolarAngle = Math.PI / 4; + view.controls.maxPolarAngle = Math.PI / 1.25; + view.controls.enableDamping = true; + }); + + const size = { + width: 256, + height: 256, + depth: 109, + }; + + new THREE.FileLoader().setResponseType('arraybuffer').load('textures/3d/head256x256x109.zip', function (data) { + const zip = unzipSync(new Uint8Array(data)); + const array = new Uint8Array(zip['head256x256x109'].buffer); + + const map3D = new THREE.Data3DTexture(array, size.width, size.height, size.depth); + map3D.name = 'Data3DTexture'; + map3D.format = THREE.RedFormat; + map3D.minFilter = THREE.LinearFilter; + map3D.magFilter = THREE.LinearFilter; + map3D.unpackAlignment = 1; + map3D.needsUpdate = true; + + const depth = size.depth / 20; + + // 3D + const helper3D = new TextureHelper(map3D, 10, 10, depth); + helper3D.material.outputNode = vec4( + vec3(diffuseColor.r.mul(attribute('uvw').z.mul(diffuseColor.r))), + diffuseColor.r.mul(diffuseColor.a), + ); + views[1].scene.add(helper3D); + + const fbo3D = new THREE.RenderTarget3D(size.width, size.height, size.depth, { + depthBuffer: false, + }); + fbo3D.texture.name = 'RenderTarget3D'; + + const fbo3DHelper = new TextureHelper(fbo3D.texture, 10, 10, depth); + fbo3DHelper.material.outputNode = vec4(vec3(diffuseColor.r), diffuseColor.r); + views[3].scene.add(fbo3DHelper); + + // 2D Array + + const mapArray = new THREE.DataArrayTexture(array, size.width, size.height, size.depth); + mapArray.name = 'DataArrayTexture'; + mapArray.format = THREE.RedFormat; + mapArray.minFilter = THREE.LinearFilter; + mapArray.magFilter = THREE.LinearFilter; + mapArray.unpackAlignment = 1; + mapArray.needsUpdate = true; + + const helperArray = new TextureHelper(mapArray, 10, 10, depth); + helperArray.material.outputNode = vec4( + vec3(diffuseColor.r.mul(attribute('uvw').z.div(size.depth).mul(diffuseColor.r))), + diffuseColor.r.mul(diffuseColor.a), + ); + views[0].scene.add(helperArray); + + // Setup render targets + const materialQuad = new THREE.NodeMaterial(); + const uZCoord = uniform(0); + materialQuad.depthTest = false; + materialQuad.outputNode = vec4(texture(mapArray).depth(uZCoord).rgb, 1); + + const fboArray = new THREE.RenderTarget(size.width, size.height, { + depthBuffer: false, + depth: size.depth, + }); + fboArray.texture.name = 'RenderTargetArray'; + + const fboArrayHelper = new TextureHelper(fboArray.texture, 10, 10, depth); + fboArrayHelper.material.outputNode = vec4(vec3(diffuseColor.r), diffuseColor.r); + views[2].scene.add(fboArrayHelper); + + const quadMesh = new THREE.QuadMesh(materialQuad); + + // In WebGPU we need to clear all the layers of the 3D render target before rendering to it (WebGPU limitation?) + if (renderer.backend.isWebGPUBackend) { + const materialClear = new THREE.NodeMaterial(); + materialClear.outputNode = vec4(0); + const clearQuadMesh = new THREE.QuadMesh(materialClear); + for (let i = 0; i < size.depth; i++) { + renderer.setRenderTarget(fbo3D, i); + clearQuadMesh.render(renderer); + } + } + + let j = 0; + + const loop = () => { + if (j === size.depth) { + clearInterval(interval); + return; + } + + // Disable viewport and scissor for FBO rendering + renderer.setViewport(0, 0, size.width, size.height); + renderer.setScissor(0, 0, size.width, size.height); + renderer.setScissorTest(false); + + uZCoord.value = j; + + renderer.setRenderTarget(fboArray, j); + renderer.clear(); + quadMesh.render(renderer); + + renderer.setRenderTarget(fbo3D, j); + renderer.clear(); + quadMesh.render(renderer); + + renderer.setRenderTarget(null); + + j = (j + 1) % size.depth; + }; + + const interval = setInterval(loop, 50); + + loop(); + }); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + renderer.setSize(width, height); + + views.forEach(view => { + view.updateSize(view.left, view.top, view.width, view.height); + }); +} + +function animate() { + views.forEach(view => { + view.controls.update(); + + const left = Math.floor(view.left * window.innerWidth); + const bottom = Math.floor((1 - view.top - view.height) * window.innerHeight); + const width = Math.floor(view.width * window.innerWidth); + const height = Math.floor(view.height * window.innerHeight); + + renderer.setViewport(left, bottom, width, height); + renderer.setScissor(left, bottom, width, height); + renderer.setScissorTest(true); + + renderer.clear(); + renderer.render(view.scene, view.camera); + }); +} + +init(); diff --git a/examples-testing/examples/webgpu_reversed_depth_buffer.ts b/examples-testing/examples/webgpu_reversed_depth_buffer.ts new file mode 100644 index 000000000..d159748f5 --- /dev/null +++ b/examples-testing/examples/webgpu_reversed_depth_buffer.ts @@ -0,0 +1,169 @@ +import * as THREE from 'three/webgpu'; +import { pass } from 'three/tsl'; + +let camera, reversedCamera, scene; +let normalRenderer, logarithmicRenderer, reverseRenderer; +let normalRenderPipeline, logarithmicRenderPipeline, reverseRenderPipeline; +const meshes = []; + +init().then(animate); + +async function init() { + camera = new THREE.PerspectiveCamera(72, (0.33 * window.innerWidth) / window.innerHeight, 5, 9999); + camera.position.z = 12; + + reversedCamera = camera.clone(); + + scene = new THREE.Scene(); + + const xCount = 1; + const yCount = 5; + const numInstances = xCount * yCount; + + const d = 0.0001; // half distance between two planes + const o = 0.5; // half x offset to shift planes so they are only partially overlapping + + const positions = new Float32Array([ + -1 - o, + -1, + d, + 1 - o, + -1, + d, + -1 - o, + 1, + d, + 1 - o, + -1, + d, + 1 - o, + 1, + d, + -1 - o, + 1, + d, + + -1 + o, + -1, + -d, + 1 + o, + -1, + -d, + -1 + o, + 1, + -d, + 1 + o, + -1, + -d, + 1 + o, + 1, + -d, + -1 + o, + 1, + -d, + ]); + + const colors = new Float32Array([ + 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, + + 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, + ]); + + const geometry = new THREE.BufferGeometry(); + geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); + geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3)); + + const material = new THREE.MeshBasicMaterial({ vertexColors: true }); + + for (let i = 0; i < numInstances; i++) { + const mesh = new THREE.Mesh(geometry, material); + meshes.push(mesh); + scene.add(mesh); + } + + let i = 0; + for (let x = 0; x < xCount; x++) { + for (let y = 0; y < yCount; y++) { + const z = -800 * i; + const s = 1 + 50 * i; + + const mesh = meshes[i]; + mesh.position.set(x - xCount / 2 + 0.5, (4.0 - 0.2 * z) * (y - yCount / 2 + 1.0), z); + mesh.scale.setScalar(s); + + i++; + } + } + + const normalContainer = document.getElementById('container_normal'); + normalRenderer = new THREE.WebGPURenderer(); + normalRenderer.setPixelRatio(window.devicePixelRatio); + normalRenderer.setSize(0.33 * window.innerWidth, window.innerHeight); + normalRenderer.domElement.style.position = 'relative'; + normalContainer.appendChild(normalRenderer.domElement); + + const logarithmicContainer = document.getElementById('container_logarithmic'); + logarithmicRenderer = new THREE.WebGPURenderer({ logarithmicDepthBuffer: true }); + logarithmicRenderer.setPixelRatio(window.devicePixelRatio); + logarithmicRenderer.setSize(0.33 * window.innerWidth, window.innerHeight); + logarithmicRenderer.domElement.style.position = 'relative'; + logarithmicContainer.appendChild(logarithmicRenderer.domElement); + + const reverseContainer = document.getElementById('container_reverse'); + reverseRenderer = new THREE.WebGPURenderer({ reversedDepthBuffer: true }); + reverseRenderer.setPixelRatio(window.devicePixelRatio); + reverseRenderer.setSize(0.33 * window.innerWidth, window.innerHeight); + reverseRenderer.domElement.style.position = 'relative'; + reverseContainer.appendChild(reverseRenderer.domElement); + + // + + normalRenderPipeline = new THREE.RenderPipeline(normalRenderer); + normalRenderPipeline.outputNode = pass(scene, camera); + + logarithmicRenderPipeline = new THREE.RenderPipeline(logarithmicRenderer); + logarithmicRenderPipeline.outputNode = pass(scene, camera); + + reverseRenderPipeline = new THREE.RenderPipeline(reverseRenderer); + reverseRenderPipeline.outputNode = pass(scene, reversedCamera); + + await Promise.all([normalRenderer.init(), logarithmicRenderer.init(), reverseRenderer.init()]); + + window.addEventListener('resize', onWindowResize); + onWindowResize(); +} + +function animate() { + requestAnimationFrame(animate); + + const now = performance.now() / 1000; + + for (let i = 0; i < meshes.length; i++) { + const angle = THREE.MathUtils.degToRad(30); + const axis = new THREE.Vector3(Math.sin(now), Math.cos(now), 0); + meshes[i].quaternion.setFromAxisAngle(axis, angle); + } + + render(); +} + +function render() { + normalRenderPipeline.render(); + logarithmicRenderPipeline.render(); + reverseRenderPipeline.render(); +} + +function onWindowResize() { + const width = 0.33 * window.innerWidth; + const height = window.innerHeight; + + normalRenderer.setSize(width, height); + logarithmicRenderer.setSize(width, height); + reverseRenderer.setSize(width, height); + + camera.aspect = (0.33 * window.innerWidth) / window.innerHeight; + camera.updateProjectionMatrix(); + + reversedCamera.aspect = (0.33 * window.innerWidth) / window.innerHeight; + reversedCamera.updateProjectionMatrix(); +} diff --git a/examples-testing/examples/webgpu_rtt.ts b/examples-testing/examples/webgpu_rtt.ts new file mode 100644 index 000000000..fa7577ea8 --- /dev/null +++ b/examples-testing/examples/webgpu_rtt.ts @@ -0,0 +1,90 @@ +import * as THREE from 'three/webgpu'; +import { texture, uniform, saturation, hue } from 'three/tsl'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer; +const mouse = new THREE.Vector2(); + +let quadMesh, renderTarget; + +let box; + +const dpr = window.devicePixelRatio; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 10); + camera.position.z = 3; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x0066ff); + + // textured mesh + + const loader = new THREE.TextureLoader(); + const uvTexture = loader.load('./textures/uv_grid_opengl.jpg'); + + const geometryBox = new THREE.BoxGeometry(); + const materialBox = new THREE.MeshBasicNodeMaterial(); + materialBox.colorNode = texture(uvTexture); + + // + + box = new THREE.Mesh(geometryBox, materialBox); + scene.add(box); + + // + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(dpr); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + renderer.inspector = new Inspector(); + + renderTarget = new THREE.RenderTarget(window.innerWidth * dpr, window.innerHeight * dpr); + + window.addEventListener('mousemove', onWindowMouseMove); + window.addEventListener('resize', onWindowResize); + + // FX + + // modulate the final color based on the mouse position + + const screenFXNode = uniform(mouse); + + const materialFX = new THREE.MeshBasicNodeMaterial(); + + const scenePassTexture = texture(renderTarget.texture).toInspector('Scene Pass'); + materialFX.colorNode = hue(saturation(scenePassTexture.rgb, screenFXNode.x.oneMinus()), screenFXNode.y); + + quadMesh = new THREE.QuadMesh(materialFX); + quadMesh.name = 'Post-Processing'; +} + +function onWindowMouseMove(e) { + mouse.x = e.offsetX / window.innerWidth; + mouse.y = e.offsetY / window.innerHeight; +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); + renderTarget.setSize(window.innerWidth * dpr, window.innerHeight * dpr); +} + +function animate() { + box.rotation.x += 0.01; + box.rotation.y += 0.02; + + renderer.setRenderTarget(renderTarget); + renderer.render(scene, camera); + + renderer.setRenderTarget(null); + quadMesh.render(renderer); +} diff --git a/examples-testing/examples/webgpu_shadow_contact.ts b/examples-testing/examples/webgpu_shadow_contact.ts new file mode 100644 index 000000000..ac3e4fbd8 --- /dev/null +++ b/examples-testing/examples/webgpu_shadow_contact.ts @@ -0,0 +1,205 @@ +import * as THREE from 'three/webgpu'; +import { vec3, uniform, texture, depth, float } from 'three/tsl'; +import { gaussianBlur } from 'three/addons/tsl/display/GaussianBlurNode.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer; +let shadowCamera, shadowGroup; +let renderTarget; +let plane, fillPlane, cameraHelper; +let depthMaterial, shadowPlaneMaterial, fillPlaneMaterial; + +const meshes = []; + +const PLANE_WIDTH = 2.5; +const PLANE_HEIGHT = 2.5; +const CAMERA_HEIGHT = 0.3; + +const state = { + shadow: { blur: 3.5, darkness: 1.0, opacity: 1.0 }, + plane: { color: '#ffffff', opacity: 1.0 }, + showWireframe: false, +}; + +const uBlur = uniform(state.shadow.blur); +const uDarkness = uniform(state.shadow.darkness); +const uShadowOpacity = uniform(state.shadow.opacity); +const uPlaneOpacity = uniform(state.plane.opacity); +const uPlaneColor = uniform(new THREE.Color(state.plane.color)); +const uPlaneY = uniform(-0.3); + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(0.5, 1, 2); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xffffff); + + window.addEventListener('resize', onWindowResize); + + const geometries = [ + new THREE.BoxGeometry(0.4, 0.4, 0.4), + new THREE.IcosahedronGeometry(0.3), + new THREE.TorusKnotGeometry(0.4, 0.05, 256, 24, 1, 3), + ]; + + const material = new THREE.MeshNormalMaterial(); + + for (let i = 0, l = geometries.length; i < l; i++) { + const angle = (i / l) * Math.PI * 2; + const mesh = new THREE.Mesh(geometries[i], material); + mesh.position.y = 0.1; + mesh.position.x = Math.cos(angle) / 2.0; + mesh.position.z = Math.sin(angle) / 2.0; + scene.add(mesh); + meshes.push(mesh); + } + + shadowGroup = new THREE.Group(); + shadowGroup.position.y = uPlaneY.value; + scene.add(shadowGroup); + + renderTarget = new THREE.RenderTarget(512, 512, { depthBuffer: true }); + renderTarget.texture.generateMipmaps = false; + + const planeGeometry = new THREE.PlaneGeometry(PLANE_WIDTH, PLANE_HEIGHT).rotateX(Math.PI / 2); + + depthMaterial = new THREE.NodeMaterial(); + + const alphaDepth = float(1).sub(depth).mul(uDarkness); + + depthMaterial.colorNode = vec3(0); + depthMaterial.opacityNode = alphaDepth; + depthMaterial.depthTest = false; + depthMaterial.depthWrite = false; + + shadowPlaneMaterial = new THREE.NodeMaterial(); + shadowPlaneMaterial.transparent = true; + shadowPlaneMaterial.depthWrite = false; + + if (!renderTarget.texture.image) renderTarget.texture.image = { width: 512, height: 512 }; + + const blurredShadow = gaussianBlur(texture(renderTarget.texture), uBlur, 4, { premultipliedAlpha: false }); + shadowPlaneMaterial.colorNode = vec3(0); + shadowPlaneMaterial.opacityNode = blurredShadow.a.mul(uShadowOpacity); + + plane = new THREE.Mesh(planeGeometry, shadowPlaneMaterial); + plane.renderOrder = 1; + plane.scale.y = -1; + plane.scale.z = -1; + shadowGroup.add(plane); + + fillPlaneMaterial = new THREE.NodeMaterial(); + fillPlaneMaterial.transparent = true; + fillPlaneMaterial.depthWrite = false; + fillPlaneMaterial.colorNode = uPlaneColor; + fillPlaneMaterial.opacityNode = uPlaneOpacity; + fillPlane = new THREE.Mesh(planeGeometry, fillPlaneMaterial); + fillPlane.rotateX(Math.PI); + shadowGroup.add(fillPlane); + + shadowCamera = new THREE.OrthographicCamera( + -PLANE_WIDTH / 2, + PLANE_WIDTH / 2, + PLANE_HEIGHT / 2, + -PLANE_HEIGHT / 2, + 0, + CAMERA_HEIGHT, + ); + shadowCamera.rotation.x = Math.PI / 2; + shadowGroup.add(shadowCamera); + + cameraHelper = new THREE.CameraHelper(shadowCamera); + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + renderer.inspector = new Inspector(); + + const params = { + shadowBlur: state.shadow.blur, + shadowDarkness: state.shadow.darkness, + shadowOpacity: state.shadow.opacity, + planeColor: state.plane.color, + planeOpacity: state.plane.opacity, + showWireframe: state.showWireframe, + }; + + const gui = renderer.inspector.createParameters('Settings'); + + gui.add(params, 'shadowBlur', 0, 15, 0.1).onChange(() => { + state.shadow.blur = params.shadowBlur; + uBlur.value = state.shadow.blur; + }); + + gui.add(params, 'shadowDarkness', 0.1, 5, 0.1).onChange(() => { + state.shadow.darkness = params.shadowDarkness; + uDarkness.value = state.shadow.darkness; + }); + + gui.add(params, 'shadowOpacity', 0, 1, 0.01).onChange(() => { + state.shadow.opacity = params.shadowOpacity; + uShadowOpacity.value = state.shadow.opacity; + }); + + gui.addColor(params, 'planeColor').onChange(() => { + state.plane.color = params.planeColor; + uPlaneColor.value.set(state.plane.color); + }); + + gui.add(params, 'planeOpacity', 0, 1, 0.01).onChange(() => { + state.plane.opacity = params.planeOpacity; + uPlaneOpacity.value = state.plane.opacity; + }); + + gui.add(params, 'showWireframe').onChange(() => { + if (params.showWireframe) scene.add(cameraHelper); + else scene.remove(cameraHelper); + }); + + new OrbitControls(camera, renderer.domElement); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + meshes.forEach(m => { + m.rotation.x += 0.01; + m.rotation.y += 0.02; + }); + + const initialBackground = scene.background; + scene.background = null; + + const prevOverride = scene.overrideMaterial; + const prevHelperVisible = cameraHelper.visible; + cameraHelper.visible = false; + scene.overrideMaterial = depthMaterial; + const initialAutoClear = renderer.autoClear; + renderer.autoClear = true; + const initialClearAlpha = renderer.getClearAlpha ? renderer.getClearAlpha() : undefined; + if (initialClearAlpha !== undefined) renderer.setClearAlpha(0); + + renderer.setRenderTarget(renderTarget); + renderer.clear(); + renderer.render(scene, shadowCamera); + + scene.overrideMaterial = prevOverride; + renderer.setRenderTarget(null); + renderer.autoClear = initialAutoClear; + if (initialClearAlpha !== undefined) renderer.setClearAlpha(initialClearAlpha); + scene.background = initialBackground; + cameraHelper.visible = prevHelperVisible; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_shadowmap.ts b/examples-testing/examples/webgpu_shadowmap.ts new file mode 100644 index 000000000..f6edec750 --- /dev/null +++ b/examples-testing/examples/webgpu_shadowmap.ts @@ -0,0 +1,166 @@ +import * as THREE from 'three/webgpu'; +import { mx_fractal_noise_float, mx_fractal_noise_vec3, positionLocal, positionWorld, Fn, color } from 'three/tsl'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer, timer; +let dirLight, spotLight; +let torusKnot, dirGroup; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(0, 10, 20); + + scene = new THREE.Scene(); + scene.backgroundNode = color(0x222244); + scene.fog = new THREE.Fog(0x222244, 50, 100); + + // lights + + scene.add(new THREE.AmbientLight(0x444444, 2)); + + spotLight = new THREE.SpotLight(0xff8888, 400); + spotLight.angle = Math.PI / 5; + spotLight.penumbra = 0.3; + spotLight.position.set(8, 10, 5); + spotLight.castShadow = true; + spotLight.shadow.camera.near = 8; + spotLight.shadow.camera.far = 200; + spotLight.shadow.mapSize.width = 2048; + spotLight.shadow.mapSize.height = 2048; + spotLight.shadow.radius = 4; + scene.add(spotLight); + + dirLight = new THREE.DirectionalLight(0x8888ff, 3); + dirLight.position.set(3, 12, 17); + dirLight.castShadow = true; + dirLight.shadow.camera.near = 0.1; + dirLight.shadow.camera.far = 500; + dirLight.shadow.camera.right = 17; + dirLight.shadow.camera.left = -17; + dirLight.shadow.camera.top = 17; + dirLight.shadow.camera.bottom = -17; + dirLight.shadow.mapSize.width = 2048; + dirLight.shadow.mapSize.height = 2048; + dirLight.shadow.radius = 4; + + dirGroup = new THREE.Group(); + dirGroup.add(dirLight); + scene.add(dirGroup); + + // geometry + + const geometry = new THREE.TorusKnotGeometry(25, 8, 75, 80); + const material = new THREE.MeshPhongNodeMaterial({ + color: 0x999999, + shininess: 0, + specular: 0x222222, + }); + + const materialCustomShadow = material.clone(); + materialCustomShadow.transparent = true; + + const discardNode = mx_fractal_noise_float(positionLocal.mul(0.1)).x.greaterThan(0.0); + + materialCustomShadow.maskNode = discardNode; + + torusKnot = new THREE.Mesh(geometry, materialCustomShadow); + torusKnot.scale.multiplyScalar(1 / 18); + torusKnot.position.y = 3; + torusKnot.castShadow = true; + torusKnot.receiveShadow = true; + scene.add(torusKnot); + + const cylinderGeometry = new THREE.CylinderGeometry(0.75, 0.75, 7, 32); + + const pillar1 = new THREE.Mesh(cylinderGeometry, material); + pillar1.position.set(8, 3.5, 8); + pillar1.castShadow = true; + + const pillar2 = pillar1.clone(); + pillar2.position.set(8, 3.5, -8); + const pillar3 = pillar1.clone(); + pillar3.position.set(-8, 3.5, 8); + const pillar4 = pillar1.clone(); + pillar4.position.set(-8, 3.5, -8); + + scene.add(pillar1); + scene.add(pillar2); + scene.add(pillar3); + scene.add(pillar4); + + const planeGeometry = new THREE.PlaneGeometry(200, 200); + + const planeMaterial = new THREE.MeshPhongNodeMaterial(); + planeMaterial.color.setHex(0x999999); + planeMaterial.shininess = 0; + planeMaterial.specular.setHex(0x111111); + + planeMaterial.receivedShadowPositionNode = Fn(() => { + const pos = positionWorld.toVar(); + pos.xz.addAssign(mx_fractal_noise_vec3(positionWorld.mul(2)).saturate().xz); + return pos; + })(); + + planeMaterial.colorNode = Fn(() => { + const pos = positionWorld.toVar(); + pos.xz.addAssign(mx_fractal_noise_vec3(positionWorld.mul(2)).saturate().xz); + return mx_fractal_noise_vec3(positionWorld.mul(2)).saturate().zzz.mul(0.2).add(0.5); + })(); + + const ground = new THREE.Mesh(planeGeometry, planeMaterial); + ground.rotation.x = -Math.PI / 2; + ground.scale.multiplyScalar(3); + ground.castShadow = true; + ground.receiveShadow = true; + scene.add(ground); + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.shadowMap.enabled = true; + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // Mouse control + const controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, 2, 0); + controls.minDistance = 7; + controls.maxDistance = 40; + controls.update(); + + timer = new THREE.Timer(); + timer.connect(document); + + window.addEventListener('resize', resize); +} + +function resize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate(time) { + timer.update(); + + const delta = timer.getDelta(); + + torusKnot.rotation.x += 0.25 * delta; + torusKnot.rotation.y += 0.5 * delta; + torusKnot.rotation.z += 1 * delta; + + dirGroup.rotation.y += 0.7 * delta; + dirLight.position.z = 17 + Math.sin(time * 0.001) * 5; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_shadowmap_array.ts b/examples-testing/examples/webgpu_shadowmap_array.ts new file mode 100644 index 000000000..0dfe8084f --- /dev/null +++ b/examples-testing/examples/webgpu_shadowmap_array.ts @@ -0,0 +1,330 @@ +import * as THREE from 'three/webgpu'; +import { mx_fractal_noise_vec3, positionWorld, Fn, color } from 'three/tsl'; + +import { TileShadowNode } from 'three/addons/tsl/shadows/TileShadowNode.js'; +import { TileShadowNodeHelper } from 'three/addons/tsl/shadows/TileShadowNodeHelper.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer, timer; +let dirLight; +let torusKnot, dirGroup; +let tsmHelper; + +init(); + +async function init() { + // Renderer setup + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + + renderer.inspector = new Inspector(); + + renderer.shadowMap.enabled = true; + renderer.shadowMap.type = THREE.BasicShadowMap; + // renderer.shadowMap.type = THREE.PCFSoftShadowMap; + + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = 1.2; + document.body.appendChild(renderer.domElement); + + await renderer.init(); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(45, 60, 100); + + scene = new THREE.Scene(); + scene.backgroundNode = color(0xccccff); // Brighter blue sky + scene.fog = new THREE.Fog(0xccccff, 700, 1000); + + // Enhanced lighting for a brighter scene + scene.add(new THREE.AmbientLight(0xccccff, 3)); + + // Main directional light (sun) + dirLight = new THREE.DirectionalLight(0xffffaa, 5); + dirLight.position.set(0, 80, 30); + dirLight.castShadow = true; + dirLight.shadow.camera.near = 1; + dirLight.shadow.camera.far = 200; + dirLight.shadow.camera.right = 180; + dirLight.shadow.camera.left = -180; + dirLight.shadow.camera.top = 180; + dirLight.shadow.camera.bottom = -160; + dirLight.shadow.mapSize.width = 1024 * 4; + dirLight.shadow.mapSize.height = 1024 * 4; + dirLight.shadow.radius = 1; + + // Set up the tile shadow mapping + const tsm = new TileShadowNode(dirLight, { + tilesX: 2, + tilesY: 2, + }); + + dirLight.shadow.shadowNode = tsm; + scene.add(dirLight); + + tsmHelper = new TileShadowNodeHelper(tsm); + scene.add(tsmHelper); + + dirGroup = new THREE.Group(); + dirGroup.add(dirLight); + scene.add(dirGroup); + + // Create the ground with enhanced texture + const planeGeometry = new THREE.PlaneGeometry(1500, 1500, 2, 2); + const planeMaterial = new THREE.MeshPhongMaterial({ + color: 0x88aa44, + shininess: 5, + specular: 0x222222, + }); + + planeMaterial.colorNode = Fn(() => { + const noise = mx_fractal_noise_vec3(positionWorld.mul(0.05)).saturate(); + // Mix of greens and browns for a more natural ground + const green = color(0.4, 0.7, 0.3); + const brown = color(0.6, 0.5, 0.3); + return noise.x.mix(green, brown); + })(); + + const ground = new THREE.Mesh(planeGeometry, planeMaterial); + ground.rotation.x = -Math.PI / 2; + ground.receiveShadow = true; + scene.add(ground); + + // Spread various objects across the scene + createScenery(); + + // Camera controls + const controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, 5, 0); + controls.minDistance = 0.01; + controls.maxDistance = 400; + controls.maxPolarAngle = Math.PI / 2 - 0.1; // Prevent camera from going below ground + controls.update(); + + timer = new THREE.Timer(); + timer.connect(document); + + window.addEventListener('resize', resize); +} + +function createScenery() { + // 1. Columns using instanced mesh + const columnGeometry = new THREE.CylinderGeometry(0.8, 1, 1, 16); + const columnMaterial = new THREE.MeshPhongMaterial({ + color: 0xdddddd, + shininess: 20, + }); + + const columnPositions = []; + const columnScales = []; + + for (let x = -100; x <= 100; x += 40) { + for (let z = -100; z <= 100; z += 40) { + if (Math.random() > 0.3) { + const height = 5 + Math.random() * 10; + const posX = x + (Math.random() * 10 - 5); + const posY = height / 2; + const posZ = z + (Math.random() * 10 - 5); + + columnPositions.push(posX, posY, posZ); + columnScales.push(1, height, 1); // Only scale Y to match height + } + } + } + + const columnCount = columnPositions.length / 3; + const columnInstancedMesh = new THREE.InstancedMesh(columnGeometry, columnMaterial, columnCount); + + const matrix = new THREE.Matrix4(); + for (let i = 0; i < columnCount; i++) { + const x = columnPositions[i * 3]; + const y = columnPositions[i * 3 + 1]; + const z = columnPositions[i * 3 + 2]; + const scaleY = columnScales[i * 3 + 1]; + + matrix.makeScale(1, scaleY, 1); + matrix.setPosition(x, y, z); + columnInstancedMesh.setMatrixAt(i, matrix); + } + + columnInstancedMesh.castShadow = true; + columnInstancedMesh.receiveShadow = true; + scene.add(columnInstancedMesh); + + // 2. Add a central feature - the torus knot (kept as regular mesh for animation) + const torusKnotGeometry = new THREE.TorusKnotGeometry(25, 8, 100, 30); + const torusKnotMaterial = new THREE.MeshPhongNodeMaterial({ + color: 0xff6347, // Tomato color + shininess: 30, + }); + + torusKnot = new THREE.Mesh(torusKnotGeometry, torusKnotMaterial); + torusKnot.scale.multiplyScalar(1 / 18); + torusKnot.position.x = 5; + torusKnot.position.y = 5; + torusKnot.castShadow = true; + torusKnot.receiveShadow = true; + scene.add(torusKnot); + + // 3. Cubes using instanced mesh + const cubeGeometry = new THREE.BoxGeometry(3, 3, 3); + const cubeMaterials = [ + new THREE.MeshPhongMaterial({ color: 0x6699cc, shininess: 20 }), + new THREE.MeshPhongMaterial({ color: 0xcc6666, shininess: 20 }), + new THREE.MeshPhongMaterial({ color: 0xcccc66, shininess: 20 }), + ]; + + const cubeCount = 10; + const cubeInstances = cubeMaterials.map(material => { + return new THREE.InstancedMesh(cubeGeometry, material, cubeCount); + }); + + for (let i = 0; i < 30; i++) { + const materialIndex = i % 3; + const instanceIndex = Math.floor(i / 3); + + const x = Math.random() * 300 - 150; + const y = 1.5; + const z = Math.random() * 300 - 150; + const rotY = Math.random() * Math.PI * 2; + + matrix.makeRotationY(rotY); + matrix.setPosition(x, y, z); + + cubeInstances[materialIndex].setMatrixAt(instanceIndex, matrix); + } + + cubeInstances.forEach(instance => { + instance.castShadow = true; + instance.receiveShadow = true; + scene.add(instance); + }); + + // 4. Spheres using instanced mesh + const sphereGeometry = new THREE.SphereGeometry(2, 32, 32); + const sphereMaterial = new THREE.MeshPhongMaterial({ + color: 0x88ccaa, + shininess: 40, + }); + + const sphereCount = 25; + const sphereInstancedMesh = new THREE.InstancedMesh(sphereGeometry, sphereMaterial, sphereCount); + + for (let i = 0; i < sphereCount; i++) { + const x = Math.random() * 180 - 90; + const y = 2; + const z = Math.random() * 180 - 90; + + matrix.makeScale(1, 1, 1); + matrix.setPosition(x, y, z); + sphereInstancedMesh.setMatrixAt(i, matrix); + } + + sphereInstancedMesh.castShadow = true; + sphereInstancedMesh.receiveShadow = true; + scene.add(sphereInstancedMesh); + + // 5. Trees using instanced mesh for trunks and tops separately + const trunkGeometry = new THREE.CylinderGeometry(0.5, 0.5, 2, 8); + const topGeometry = new THREE.ConeGeometry(2, 8, 8); + const treeMaterial = new THREE.MeshPhongMaterial({ + vertexColors: true, + shininess: 5, + }); + + const treeCount = 40; + const totalInstanceCount = treeCount * 2; + + const trunkVertexCount = trunkGeometry.attributes.position.count; + const trunkIndexCount = trunkGeometry.index ? trunkGeometry.index.count : 0; + const topVertexCount = topGeometry.attributes.position.count; + const topIndexCount = topGeometry.index ? topGeometry.index.count : 0; + + const totalVertexCount = (trunkVertexCount + topVertexCount) * 2; // Multiple for safety + const totalIndexCount = (trunkIndexCount + topIndexCount) * 2; + const treeBatchedMesh = new THREE.BatchedMesh(totalInstanceCount, totalVertexCount, totalIndexCount, treeMaterial); + treeBatchedMesh.castShadow = true; + treeBatchedMesh.perObjectFrustumCulled = false; + const trunkGeometryId = treeBatchedMesh.addGeometry(trunkGeometry); + const topGeometryId = treeBatchedMesh.addGeometry(topGeometry); + + const trunkColor = new THREE.Color(0x8b4513); + const topColor = new THREE.Color(0x336633); + + for (let i = 0; i < treeCount; i++) { + const x = Math.random() * 300 - 150; + const z = Math.random() * 300 - 150; + + const trunkId = treeBatchedMesh.addInstance(trunkGeometryId); + matrix.makeScale(1, 1, 1); + matrix.setPosition(x, 1, z); + treeBatchedMesh.setMatrixAt(trunkId, matrix); + treeBatchedMesh.setColorAt(trunkId, trunkColor); + + const topId = treeBatchedMesh.addInstance(topGeometryId); + matrix.makeScale(1, 1, 1); + matrix.setPosition(x, 6, z); + treeBatchedMesh.setMatrixAt(topId, matrix); + treeBatchedMesh.setColorAt(topId, topColor); + } + + scene.add(treeBatchedMesh); + + // 6. Torus shapes using instanced mesh + const torusGeometry = new THREE.TorusGeometry(3, 1, 16, 50); + const torusMaterial = new THREE.MeshPhongMaterial({ + color: 0xff99cc, + shininess: 30, + }); + + const torusCount = 15; + const torusInstancedMesh = new THREE.InstancedMesh(torusGeometry, torusMaterial, torusCount); + + for (let i = 0; i < torusCount; i++) { + const x = Math.random() * 320 - 160; + const y = 2; + const z = Math.random() * 320 - 160; + const rotZ = Math.random() * Math.PI * 2; + + // Apply rotation (PI/2 on X-axis and random on Z-axis) + matrix.makeRotationX(Math.PI / 2); + const rotMatrix = new THREE.Matrix4().makeRotationZ(rotZ); + matrix.multiply(rotMatrix); + matrix.setPosition(x, y, z); + + torusInstancedMesh.setMatrixAt(i, matrix); + } + + torusInstancedMesh.castShadow = true; + torusInstancedMesh.receiveShadow = true; + scene.add(torusInstancedMesh); +} + +function resize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + renderer.setSize(window.innerWidth, window.innerHeight); +} + +async function animate(time) { + timer.update(); + + const delta = timer.getDelta(); + + // Rotate the central torus knot + torusKnot.rotation.x += 0.25 * delta; + torusKnot.rotation.y += 0.5 * delta; + torusKnot.rotation.z += 1 * delta; + + dirLight.position.x = Math.sin(time * 0.0001) * 30; + dirLight.position.z = Math.cos(time * 0.0001) * 30; + + renderer.render(scene, camera); + + tsmHelper.update(); +} diff --git a/examples-testing/examples/webgpu_shadowmap_csm.ts b/examples-testing/examples/webgpu_shadowmap_csm.ts new file mode 100644 index 000000000..5be061456 --- /dev/null +++ b/examples-testing/examples/webgpu_shadowmap_csm.ts @@ -0,0 +1,268 @@ +import * as THREE from 'three/webgpu'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { CSMShadowNode } from 'three/addons/csm/CSMShadowNode.js'; +import { CSMHelper } from 'three/addons/csm/CSMHelper.js'; + +let renderer, scene, camera, orthoCamera, controls, csm, csmHelper, csmDirectionalLight; + +const params = { + orthographic: false, + fade: false, + shadows: true, + maxFar: 1000, + mode: 'practical', + lightX: -1, + lightY: -1, + lightZ: -1, + margin: 100, + shadowNear: 1, + shadowFar: 2000, + autoUpdateHelper: true, + updateHelper: function () { + csmHelper.update(); + }, +}; + +init(); + +function updateOrthoCamera() { + const size = controls.target.distanceTo(camera.position); + const aspect = camera.aspect; + + orthoCamera.left = (size * aspect) / -2; + orthoCamera.right = (size * aspect) / 2; + + orthoCamera.top = size / 2; + orthoCamera.bottom = size / -2; + orthoCamera.position.copy(camera.position); + orthoCamera.rotation.copy(camera.rotation); + orthoCamera.updateProjectionMatrix(); +} + +function init() { + scene = new THREE.Scene(); + scene.background = new THREE.Color('#454e61'); + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 5000); + orthoCamera = new THREE.OrthographicCamera(); + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + + renderer.shadowMap.enabled = params.shadows; + renderer.shadowMap.type = THREE.PCFSoftShadowMap; + + renderer.inspector = new Inspector(); + + document.body.appendChild(renderer.domElement); + + controls = new OrbitControls(camera, renderer.domElement); + controls.maxPolarAngle = Math.PI / 2; + camera.position.set(60, 60, 0); + controls.target = new THREE.Vector3(-100, 10, 0); + controls.update(); + + const ambientLight = new THREE.AmbientLight(0xffffff, 1.5); + scene.add(ambientLight); + + const additionalDirectionalLight = new THREE.DirectionalLight(0x000020, 1.5); + additionalDirectionalLight.position + .set(params.lightX, params.lightY, params.lightZ) + .normalize() + .multiplyScalar(-200); + scene.add(additionalDirectionalLight); + + csmDirectionalLight = new THREE.DirectionalLight(0xffffff, 3.0); + + csmDirectionalLight.castShadow = true; + csmDirectionalLight.shadow.mapSize.width = 2048; + csmDirectionalLight.shadow.mapSize.height = 2048; + csmDirectionalLight.shadow.camera.near = params.shadowNear; + csmDirectionalLight.shadow.camera.far = params.shadowFar; + csmDirectionalLight.shadow.camera.top = 1000; + csmDirectionalLight.shadow.camera.bottom = -1000; + csmDirectionalLight.shadow.camera.left = -1000; + csmDirectionalLight.shadow.camera.right = 1000; + + csm = new CSMShadowNode(csmDirectionalLight, { cascades: 4, maxFar: params.maxFar, mode: params.mode }); + + csmDirectionalLight.position.set(params.lightX, params.lightY, params.lightZ).normalize().multiplyScalar(-200); + + csmDirectionalLight.shadow.shadowNode = csm; + + scene.add(csmDirectionalLight); + + csmHelper = new CSMHelper(csm); + csmHelper.visible = false; + scene.add(csmHelper); + + const floorMaterial = new THREE.MeshPhongMaterial({ color: '#252a34' }); + + const floor = new THREE.Mesh(new THREE.PlaneGeometry(10000, 10000, 8, 8), floorMaterial); + floor.rotation.x = -Math.PI / 2; + floor.castShadow = true; + floor.receiveShadow = true; + scene.add(floor); + + const material1 = new THREE.MeshPhongMaterial({ color: '#08d9d6' }); + + const material2 = new THREE.MeshPhongMaterial({ color: '#ff2e63' }); + + const geometry = new THREE.BoxGeometry(10, 10, 10); + + for (let i = 0; i < 40; i++) { + const cube1 = new THREE.Mesh(geometry, i % 2 === 0 ? material1 : material2); + cube1.castShadow = true; + cube1.receiveShadow = true; + scene.add(cube1); + cube1.position.set(-i * 25, 20, 30); + cube1.scale.y = Math.random() * 2 + 6; + + const cube2 = new THREE.Mesh(geometry, i % 2 === 0 ? material2 : material1); + cube2.castShadow = true; + cube2.receiveShadow = true; + scene.add(cube2); + cube2.position.set(-i * 25, 20, -30); + cube2.scale.y = Math.random() * 2 + 6; + } + + const gui = renderer.inspector.createParameters('Settings'); + + gui.add(params, 'orthographic').onChange(function (value) { + csm.camera = value ? orthoCamera : camera; + csm.updateFrustums(); + }); + + // gui.add( params, 'fade' ).onChange( function ( value ) { + + // csm.fade = value; + // csm.updateFrustums(); + // TODO: Changing "fade" requires toggling shadows right now + + // } ); + + gui.add(params, 'shadows').onChange(function (value) { + csmDirectionalLight.castShadow = value; + }); + + gui.add(params, 'maxFar', 1, 5000, 1) + .name('max shadow far') + .onChange(function (value) { + csm.maxFar = value; + csm.updateFrustums(); + }); + + gui.add(params, 'mode', ['uniform', 'logarithmic', 'practical']) + .name('frustum split mode') + .onChange(function (value) { + csm.mode = value; + csm.updateFrustums(); + }); + + gui.add(params, 'lightX', -1, 1) + .name('light direction x') + .onChange(function () { + csmDirectionalLight.position + .set(params.lightX, params.lightY, params.lightZ) + .normalize() + .multiplyScalar(-200); + }); + + gui.add(params, 'lightY', -1, 1) + .name('light direction y') + .onChange(function () { + csmDirectionalLight.position + .set(params.lightX, params.lightY, params.lightZ) + .normalize() + .multiplyScalar(-200); + }); + + gui.add(params, 'lightZ', -1, 1) + .name('light direction z') + .onChange(function () { + csmDirectionalLight.position + .set(params.lightX, params.lightY, params.lightZ) + .normalize() + .multiplyScalar(-200); + }); + + gui.add(params, 'margin', 0, 200) + .name('light margin') + .onChange(function (value) { + csm.lightMargin = value; + }); + + gui.add(params, 'shadowNear', 1, 10000) + .name('shadow near') + .onChange(function (value) { + for (let i = 0; i < csm.lights.length; i++) { + csm.lights[i].shadow.camera.near = value; + csm.lights[i].shadow.camera.updateProjectionMatrix(); + } + }); + + gui.add(params, 'shadowFar', 1, 10000) + .name('shadow far') + .onChange(function (value) { + for (let i = 0; i < csm.lights.length; i++) { + csm.lights[i].shadow.camera.far = value; + csm.lights[i].shadow.camera.updateProjectionMatrix(); + } + }); + + const helperFolder = gui.addFolder('helper'); + + helperFolder.add(csmHelper, 'visible'); + + helperFolder.add(csmHelper, 'displayFrustum').onChange(function () { + csmHelper.updateVisibility(); + }); + + helperFolder.add(csmHelper, 'displayPlanes').onChange(function () { + csmHelper.updateVisibility(); + }); + + helperFolder.add(csmHelper, 'displayShadowBounds').onChange(function () { + csmHelper.updateVisibility(); + }); + + helperFolder.add(params, 'autoUpdateHelper').name('auto update'); + + helperFolder.add(params, 'updateHelper').name('update'); + + window.addEventListener('resize', function () { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + updateOrthoCamera(); + csm.updateFrustums(); + + renderer.setSize(window.innerWidth, window.innerHeight); + }); +} + +function animate() { + camera.updateMatrixWorld(); + controls.update(); + + if (params.orthographic) { + updateOrthoCamera(); + csm.updateFrustums(); + + if (params.autoUpdateHelper) { + csmHelper.update(); + } + + renderer.render(scene, orthoCamera); + } else { + if (params.autoUpdateHelper) { + csmHelper.update(); + } + + renderer.render(scene, camera); + } +} diff --git a/examples-testing/examples/webgpu_shadowmap_opacity.ts b/examples-testing/examples/webgpu_shadowmap_opacity.ts new file mode 100644 index 000000000..e508e1039 --- /dev/null +++ b/examples-testing/examples/webgpu_shadowmap_opacity.ts @@ -0,0 +1,117 @@ +import * as THREE from 'three/webgpu'; +import { Fn, mix } from 'three/tsl'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +let camera, scene, renderer; + +init(); + +async function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 40); + camera.position.set(-4, 2, 6); + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(render); + renderer.toneMapping = THREE.AgXToneMapping; + renderer.toneMappingExposure = 1.5; + renderer.shadowMap.enabled = true; + renderer.shadowMap.transmitted = true; + renderer.inspector = new Inspector(); + container.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x9e9eff); + + // light + shadow + + const hemi = new THREE.AmbientLight(0xffffff, 0.5); + scene.add(hemi); + + const dirLight = new THREE.DirectionalLight(0x6666ff, 10); + dirLight.position.set(3, 5, 17); + dirLight.castShadow = true; + dirLight.shadow.camera.near = 0.1; + dirLight.shadow.camera.far = 50; + dirLight.shadow.camera.right = 5; + dirLight.shadow.camera.left = -5; + dirLight.shadow.camera.top = 5; + dirLight.shadow.camera.bottom = -5; + dirLight.shadow.mapSize.width = 2048; + dirLight.shadow.mapSize.height = 2048; + dirLight.shadow.radius = 4; + + dirLight.shadow.autoUpdate = false; + dirLight.shadow.needsUpdate = true; + + scene.add(dirLight); + + // + + const loader = new GLTFLoader(); + const gltf = await loader.loadAsync('models/gltf/DragonAttenuation.glb'); + gltf.scene.position.set(0, 0, -0.5); + + const floor = gltf.scene.children[0]; + floor.scale.x += 4; + floor.scale.y += 4; + + const dragon = gltf.scene.children[1]; + dragon.position.set(-1.5, -0.8, 1); + + const dragon2 = dragon.clone(); + dragon2.material = dragon.material.clone(); + dragon2.material.attenuationColor = new THREE.Color(0xff0000); + dragon2.position.x += 4; + gltf.scene.add(dragon2); + + // shadow node + + const customShadow = Fn(([color, opacity = 1]) => { + //return vec4( color, opacity ); // opacity by blending + return mix(1, color, opacity); // opacity by color + }); + + // apply shadow + + floor.receiveShadow = true; + + dragon.castShadow = dragon2.castShadow = true; + dragon.receiveShadow = dragon2.receiveShadow = true; + + dragon.material.castShadowNode = customShadow(dragon.material.attenuationColor); + dragon2.material.castShadowNode = customShadow(dragon2.material.attenuationColor); + + // + + scene.add(gltf.scene); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 0.1; + controls.maxDistance = 10; + controls.target.set(0, 0, 0); + controls.update(); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_shadowmap_pointlight.ts b/examples-testing/examples/webgpu_shadowmap_pointlight.ts new file mode 100644 index 000000000..3cb878e6f --- /dev/null +++ b/examples-testing/examples/webgpu_shadowmap_pointlight.ts @@ -0,0 +1,142 @@ +import * as THREE from 'three'; + +import Stats from 'three/addons/libs/stats.module.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let camera, scene, renderer, stats; +let pointLight, pointLight2; + +init(); + +async function init() { + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(0, 10, 40); + + scene = new THREE.Scene(); + scene.add(new THREE.AmbientLight(0x111122, 3)); + + // lights + + function createLight(color) { + const intensity = 200; + + const light = new THREE.PointLight(color, intensity, 20); + light.castShadow = true; + light.shadow.bias = -0.005; // reduces self-shadowing on double-sided objects + light.shadow.mapSize.width = 128; + light.shadow.radius = 10; + + let geometry = new THREE.SphereGeometry(0.3, 12, 6); + let material = new THREE.MeshBasicMaterial({ color: color }); + material.color.multiplyScalar(intensity); + let sphere = new THREE.Mesh(geometry, material); + light.add(sphere); + + const texture = new THREE.CanvasTexture(generateTexture()); + texture.magFilter = THREE.NearestFilter; + texture.wrapT = THREE.RepeatWrapping; + texture.wrapS = THREE.RepeatWrapping; + texture.repeat.set(1, 4.5); + + geometry = new THREE.SphereGeometry(2, 32, 8); + material = new THREE.MeshPhongNodeMaterial({ + side: THREE.DoubleSide, + alphaMap: texture, + alphaTest: 0.5, + }); + + sphere = new THREE.Mesh(geometry, material); + sphere.castShadow = true; + sphere.receiveShadow = true; + light.add(sphere); + + return light; + } + + pointLight = createLight(0x0088ff); + scene.add(pointLight); + + pointLight2 = createLight(0xff8888); + scene.add(pointLight2); + // + + const geometry = new THREE.BoxGeometry(30, 30, 30); + + const material = new THREE.MeshPhongNodeMaterial({ + color: 0xa0adaf, + shininess: 10, + specular: 0x111111, + side: THREE.BackSide, + }); + + const mesh = new THREE.Mesh(geometry, material); + mesh.position.y = 10; + mesh.receiveShadow = true; + scene.add(mesh); + + // + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.shadowMap.enabled = true; + // renderer.shadowMap.type = THREE.BasicShadowMap; + await renderer.init(); + document.body.appendChild(renderer.domElement); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, 10, 0); + controls.update(); + + stats = new Stats(); + document.body.appendChild(stats.dom); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function generateTexture() { + const canvas = document.createElement('canvas'); + canvas.width = 2; + canvas.height = 2; + + const context = canvas.getContext('2d'); + context.fillStyle = 'white'; + context.fillRect(0, 1, 2, 1); + + return canvas; +} + +function animate() { + let time = performance.now() * 0.001; + + pointLight.position.x = Math.sin(time * 0.6) * 9; + pointLight.position.y = Math.sin(time * 0.7) * 9 + 6; + pointLight.position.z = Math.sin(time * 0.8) * 9; + + pointLight.rotation.x = time; + pointLight.rotation.z = time; + + time += 10000; + + pointLight2.position.x = Math.sin(time * 0.6) * 9; + pointLight2.position.y = Math.sin(time * 0.7) * 9 + 6; + pointLight2.position.z = Math.sin(time * 0.8) * 9; + + pointLight2.rotation.x = time; + pointLight2.rotation.z = time; + + renderer.render(scene, camera); + + stats.update(); +} diff --git a/examples-testing/examples/webgpu_shadowmap_progressive.ts b/examples-testing/examples/webgpu_shadowmap_progressive.ts new file mode 100644 index 000000000..ca5ee0f72 --- /dev/null +++ b/examples-testing/examples/webgpu_shadowmap_progressive.ts @@ -0,0 +1,203 @@ +import * as THREE from 'three/webgpu'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { TransformControls } from 'three/addons/controls/TransformControls.js'; +import { ProgressiveLightMap } from 'three/addons/misc/ProgressiveLightMapGPU.js'; + +// ShadowMap + LightMap Res and Number of Directional Lights +const shadowMapRes = 1024, + lightMapRes = 1024, + lightCount = 4; +let camera, + scene, + renderer, + controls, + control, + control2, + object = new THREE.Mesh(), + lightOrigin = null, + progressiveSurfacemap; +const dirLights = [], + lightmapObjects = []; +const params = { + Enable: true, + 'Blur Edges': true, + 'Blend Window': 200, + 'Light Radius': 50, + 'Ambient Weight': 0.5, + 'Debug Lightmap': false, +}; +init(); +createGUI(); + +function init() { + // renderer + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.shadowMap.enabled = true; + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // camera + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(0, 100, 200); + camera.name = 'Camera'; + + // scene + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x949494); + scene.fog = new THREE.Fog(0x949494, 1000, 3000); + + // progressive lightmap + progressiveSurfacemap = new ProgressiveLightMap(renderer, lightMapRes); + + // directional lighting "origin" + lightOrigin = new THREE.Group(); + lightOrigin.position.set(60, 150, 100); + scene.add(lightOrigin); + + // transform gizmo + control = new TransformControls(camera, renderer.domElement); + control.addEventListener('dragging-changed', event => { + controls.enabled = !event.value; + }); + control.attach(lightOrigin); + scene.add(control.getHelper()); + + // create 8 directional lights to speed up the convergence + for (let l = 0; l < lightCount; l++) { + const dirLight = new THREE.DirectionalLight(0xffffff, Math.PI / lightCount); + dirLight.name = 'Dir. Light ' + l; + dirLight.position.set(200, 200, 200); + dirLight.castShadow = true; + dirLight.shadow.camera.near = 100; + dirLight.shadow.camera.far = 5000; + dirLight.shadow.camera.right = 150; + dirLight.shadow.camera.left = -150; + dirLight.shadow.camera.top = 150; + dirLight.shadow.camera.bottom = -150; + dirLight.shadow.mapSize.width = shadowMapRes; + dirLight.shadow.mapSize.height = shadowMapRes; + lightmapObjects.push(dirLight); + dirLights.push(dirLight); + } + + // ground + const groundMesh = new THREE.Mesh( + new THREE.PlaneGeometry(600, 600), + new THREE.MeshPhongMaterial({ color: 0xffffff, depthWrite: true }), + ); + groundMesh.position.y = -0.1; + groundMesh.rotation.x = -Math.PI / 2; + groundMesh.name = 'Ground Mesh'; + lightmapObjects.push(groundMesh); + scene.add(groundMesh); + + // model + function loadModel() { + object.traverse(function (child) { + if (child.isMesh) { + child.name = 'Loaded Mesh'; + child.castShadow = true; + child.receiveShadow = true; + child.material = new THREE.MeshPhongMaterial(); + + // This adds the model to the lightmap + lightmapObjects.push(child); + progressiveSurfacemap.addObjectsToLightMap(lightmapObjects); + } else { + child.layers.disableAll(); // Disable Rendering for this + } + }); + scene.add(object); + object.scale.set(2, 2, 2); + object.position.set(0, -16, 0); + control2 = new TransformControls(camera, renderer.domElement); + control2.addEventListener('dragging-changed', event => { + controls.enabled = !event.value; + }); + control2.attach(object); + scene.add(control2.getHelper()); + const lightTarget = new THREE.Group(); + lightTarget.position.set(0, 20, 0); + for (let l = 0; l < dirLights.length; l++) { + dirLights[l].target = lightTarget; + } + + object.add(lightTarget); + } + + const manager = new THREE.LoadingManager(loadModel); + const loader = new GLTFLoader(manager); + loader.load('models/gltf/ShadowmappableMesh.glb', function (obj) { + object = obj.scene.children[0]; + }); + + // controls + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; // an animation loop is required when either damping or auto-rotation are enabled + controls.dampingFactor = 0.05; + controls.screenSpacePanning = true; + controls.minDistance = 100; + controls.maxDistance = 500; + controls.maxPolarAngle = Math.PI / 1.5; + controls.target.set(0, 100, 0); + + window.addEventListener('resize', onWindowResize); +} + +function createGUI() { + const gui = renderer.inspector.createParameters('Accumulation Settings'); + gui.add(params, 'Enable'); + gui.add(params, 'Blur Edges'); + gui.add(params, 'Blend Window', 1, 500, 1); + gui.add(params, 'Light Radius', 0, 200, 10); + gui.add(params, 'Ambient Weight', 0, 1, 0.1); + gui.add(params, 'Debug Lightmap').onChange(value => progressiveSurfacemap.showDebugLightmap(value)); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + // Update the inertia on the orbit controls + controls.update(); + + // Accumulate Surface Maps + if (params['Enable']) { + progressiveSurfacemap.update(camera, params['Blend Window'], params['Blur Edges']); + } + + // Manually Update the Directional Lights + for (let l = 0; l < dirLights.length; l++) { + // Sometimes they will be sampled from the target direction + // Sometimes they will be uniformly sampled from the upper hemisphere + if (Math.random() > params['Ambient Weight']) { + dirLights[l].position.set( + lightOrigin.position.x + Math.random() * params['Light Radius'], + lightOrigin.position.y + Math.random() * params['Light Radius'], + lightOrigin.position.z + Math.random() * params['Light Radius'], + ); + } else { + // Uniform Hemispherical Surface Distribution for Ambient Occlusion + const lambda = Math.acos(2 * Math.random() - 1) - 3.14159 / 2.0; + const phi = 2 * 3.14159 * Math.random(); + dirLights[l].position.set( + Math.cos(lambda) * Math.cos(phi) * 300 + object.position.x, + Math.abs(Math.cos(lambda) * Math.sin(phi) * 300) + object.position.y + 20, + Math.sin(lambda) * 300 + object.position.z, + ); + } + } + + // Render Scene + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_shadowmap_vsm.ts b/examples-testing/examples/webgpu_shadowmap_vsm.ts new file mode 100644 index 000000000..edaf4fd9e --- /dev/null +++ b/examples-testing/examples/webgpu_shadowmap_vsm.ts @@ -0,0 +1,197 @@ +import * as THREE from 'three/webgpu'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let camera, scene, renderer, timer; +let dirLight, spotLight; +let torusKnot, dirGroup; + +const config = { + spotlightRadius: 4, + spotlightSamples: 8, + dirlightRadius: 4, + dirlightSamples: 8, + animate: true, +}; + +init(); + +function init() { + initScene(); + initMisc(); + + // Init gui + const gui = renderer.inspector.createParameters('Settings'); + + const spotlightFolder = gui.addFolder('Spotlight'); + spotlightFolder + .add(config, 'spotlightRadius', 0, 25) + .name('radius') + .onChange(function (value) { + spotLight.shadow.radius = value; + }); + + spotlightFolder + .add(config, 'spotlightSamples', 1, 25, 1) + .name('samples') + .onChange(function (value) { + spotLight.shadow.blurSamples = value; + }); + + const dirlightFolder = gui.addFolder('Directional Light'); + dirlightFolder + .add(config, 'dirlightRadius', 0, 25) + .name('radius') + .onChange(function (value) { + dirLight.shadow.radius = value; + }); + + dirlightFolder + .add(config, 'dirlightSamples', 1, 25, 1) + .name('samples') + .onChange(function (value) { + dirLight.shadow.blurSamples = value; + }); + + gui.add(config, 'animate'); + + document.body.appendChild(renderer.domElement); + window.addEventListener('resize', onWindowResize); +} + +function initScene() { + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000); + camera.position.set(0, 10, 30); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x222244); + scene.fog = new THREE.Fog(0x222244, 50, 100); + + // Lights + + scene.add(new THREE.AmbientLight(0x444444)); + + spotLight = new THREE.SpotLight(0xff8888, 400); + spotLight.angle = Math.PI / 5; + spotLight.penumbra = 0.3; + spotLight.position.set(8, 10, 5); + spotLight.castShadow = true; + spotLight.shadow.camera.near = 8; + spotLight.shadow.camera.far = 200; + spotLight.shadow.mapSize.width = 256; + spotLight.shadow.mapSize.height = 256; + spotLight.shadow.bias = -0.002; + spotLight.shadow.radius = 4; + scene.add(spotLight); + + dirLight = new THREE.DirectionalLight(0x8888ff, 3); + dirLight.position.set(3, 12, 17); + dirLight.castShadow = true; + dirLight.shadow.camera.near = 0.1; + dirLight.shadow.camera.far = 500; + dirLight.shadow.camera.right = 17; + dirLight.shadow.camera.left = -17; + dirLight.shadow.camera.top = 17; + dirLight.shadow.camera.bottom = -17; + dirLight.shadow.mapSize.width = 512; + dirLight.shadow.mapSize.height = 512; + dirLight.shadow.radius = 4; + dirLight.shadow.bias = -0.0005; + + dirGroup = new THREE.Group(); + dirGroup.add(dirLight); + scene.add(dirGroup); + + // Geometry + + const geometry = new THREE.TorusKnotGeometry(25, 8, 75, 20); + const material = new THREE.MeshPhongMaterial({ + color: 0x999999, + shininess: 0, + specular: 0x222222, + }); + + torusKnot = new THREE.Mesh(geometry, material); + torusKnot.scale.multiplyScalar(1 / 18); + torusKnot.position.y = 3; + torusKnot.castShadow = true; + torusKnot.receiveShadow = true; + scene.add(torusKnot); + + const cylinderGeometry = new THREE.CylinderGeometry(0.75, 0.75, 7, 32); + + const pillar1 = new THREE.Mesh(cylinderGeometry, material); + pillar1.position.set(8, 3.5, 8); + pillar1.castShadow = true; + pillar1.receiveShadow = true; + + const pillar2 = pillar1.clone(); + pillar2.position.set(8, 3.5, -8); + const pillar3 = pillar1.clone(); + pillar3.position.set(-8, 3.5, 8); + const pillar4 = pillar1.clone(); + pillar4.position.set(-8, 3.5, -8); + + scene.add(pillar1); + scene.add(pillar2); + scene.add(pillar3); + scene.add(pillar4); + + const planeGeometry = new THREE.PlaneGeometry(200, 200); + const planeMaterial = new THREE.MeshPhongMaterial({ + color: 0x999999, + shininess: 0, + specular: 0x111111, + }); + + const ground = new THREE.Mesh(planeGeometry, planeMaterial); + ground.rotation.x = -Math.PI / 2; + ground.scale.multiplyScalar(3); + ground.castShadow = true; + ground.receiveShadow = true; + scene.add(ground); +} + +function initMisc() { + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + renderer.shadowMap.enabled = true; + renderer.shadowMap.type = THREE.VSMShadowMap; + + // Mouse control + const controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, 2, 0); + controls.update(); + + timer = new THREE.Timer(); + timer.connect(document); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate(time) { + timer.update(); + + const delta = timer.getDelta(); + + if (config.animate === true) { + torusKnot.rotation.x += 0.25 * delta; + torusKnot.rotation.y += 0.5 * delta; + torusKnot.rotation.z += 1 * delta; + + dirGroup.rotation.y += 0.7 * delta; + dirLight.position.z = 17 + Math.sin(time * 0.001) * 5; + } + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_skinning.ts b/examples-testing/examples/webgpu_skinning.ts new file mode 100644 index 000000000..36669a5d4 --- /dev/null +++ b/examples-testing/examples/webgpu_skinning.ts @@ -0,0 +1,72 @@ +import * as THREE from 'three/webgpu'; +import { color, screenUV } from 'three/tsl'; + +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +let camera, scene, renderer; + +let mixer, timer; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.01, 100); + camera.position.set(1, 2, 3); + + scene = new THREE.Scene(); + scene.backgroundNode = screenUV.y.mix(color(0x66bbff), color(0x4466ff)); + camera.lookAt(0, 1, 0); + + timer = new THREE.Timer(); + timer.connect(document); + + //lights + + const light = new THREE.PointLight(0xffffff, 1, 100); + light.power = 2500; + camera.add(light); + scene.add(camera); + + const ambient = new THREE.AmbientLight(0x4466ff, 1); + scene.add(ambient); + + const loader = new GLTFLoader(); + loader.load('models/gltf/Michelle.glb', function (gltf) { + const object = gltf.scene; + mixer = new THREE.AnimationMixer(object); + + const action = mixer.clipAction(gltf.animations[0]); + action.play(); + + scene.add(object); + }); + + //renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.toneMapping = THREE.LinearToneMapping; + renderer.toneMappingExposure = 0.4; + document.body.appendChild(renderer.domElement); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + const delta = timer.getDelta(); + + if (mixer) mixer.update(delta); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_skinning_instancing.ts b/examples-testing/examples/webgpu_skinning_instancing.ts new file mode 100644 index 000000000..877ee46cb --- /dev/null +++ b/examples-testing/examples/webgpu_skinning_instancing.ts @@ -0,0 +1,128 @@ +import * as THREE from 'three/webgpu'; +import { pass, mix, range, color, oscSine, time } from 'three/tsl'; +import { gaussianBlur } from 'three/addons/tsl/display/GaussianBlurNode.js'; + +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +let camera, scene, renderer; +let renderPipeline; + +let mixer, timer; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.01, 40); + camera.position.set(1, 2, 3); + + scene = new THREE.Scene(); + camera.lookAt(0, 1, 0); + + timer = new THREE.Timer(); + timer.connect(document); + + // lights + + const centerLight = new THREE.PointLight(0xff9900, 1, 100); + centerLight.position.y = 4.5; + centerLight.position.z = -2; + centerLight.power = 400; + scene.add(centerLight); + + const cameraLight = new THREE.PointLight(0x0099ff, 1, 100); + cameraLight.power = 400; + camera.add(cameraLight); + scene.add(camera); + + const geometry = new THREE.PlaneGeometry(1000, 1000); + geometry.rotateX(-Math.PI / 2); + + const plane = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ color: 0x000000, visible: true })); + scene.add(plane); + + const loader = new GLTFLoader(); + loader.load('models/gltf/Michelle.glb', function (gltf) { + const object = gltf.scene; + + mixer = new THREE.AnimationMixer(object); + + const action = mixer.clipAction(gltf.animations[0]); + action.play(); + + const instanceCount = 30; + const dummy = new THREE.Object3D(); + + object.traverse(child => { + if (child.isMesh) { + const oscNode = oscSine(time.mul(0.1)); + + // random colors between instances from 0x000000 to 0xFFFFFF + const randomColors = range(new THREE.Color(0x000000), new THREE.Color(0xffffff)); + + // random [ 0, 1 ] values between instances + const randomMetalness = range(0, 1); + + child.material = new THREE.MeshStandardNodeMaterial(); + child.material.roughness = 0.1; + child.material.metalnessNode = mix(0.0, randomMetalness, oscNode); + child.material.colorNode = mix(color(0xffffff), randomColors, oscNode); + + child.isInstancedMesh = true; + child.instanceMatrix = new THREE.InstancedBufferAttribute(new Float32Array(instanceCount * 16), 16); + child.count = instanceCount; + + for (let i = 0; i < instanceCount; i++) { + dummy.position.x = -200 + (i % 5) * 70; + dummy.position.y = Math.floor(i / 5) * -200; + + dummy.updateMatrix(); + + dummy.matrix.toArray(child.instanceMatrix.array, i * 16); + } + } + }); + + scene.add(object); + }); + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // post processing + + const scenePass = pass(scene, camera); + const scenePassColor = scenePass.getTextureNode(); + const scenePassDepth = scenePass.getLinearDepthNode().remapClamp(0.15, 0.3); + + const scenePassColorBlurred = gaussianBlur(scenePassColor); + scenePassColorBlurred.directionNode = scenePassDepth; + + renderPipeline = new THREE.RenderPipeline(renderer); + renderPipeline.outputNode = scenePassColorBlurred; + + // events + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + const delta = timer.getDelta(); + + if (mixer) mixer.update(delta); + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_sprites.ts b/examples-testing/examples/webgpu_sprites.ts new file mode 100644 index 000000000..de9f219b8 --- /dev/null +++ b/examples-testing/examples/webgpu_sprites.ts @@ -0,0 +1,102 @@ +import * as THREE from 'three/webgpu'; +import { texture, uv, userData, fog, rangeFogFactor, color } from 'three/tsl'; + +let camera, scene, renderer; + +let map; + +let group; + +let imageWidth = 1, + imageHeight = 1; + +init(); + +function init() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera = new THREE.PerspectiveCamera(60, width / height, 1, 2100); + camera.position.z = 1500; + + scene = new THREE.Scene(); + scene.fogNode = fog(color(0x0000ff), rangeFogFactor(1500, 2100)); + + // create sprites + + const amount = 200; + const radius = 500; + + const textureLoader = new THREE.TextureLoader(); + + map = textureLoader.load('textures/sprite1.png', map => { + imageWidth = map.image.width; + imageHeight = map.image.height; + }); + + group = new THREE.Group(); + + const textureNode = texture(map); + + const material = new THREE.SpriteNodeMaterial(); + material.colorNode = textureNode.mul(uv()).mul(2).saturate(); + material.opacityNode = textureNode.a; + material.rotationNode = userData('rotation', 'float'); // get value of: sprite.userData.rotation + + for (let a = 0; a < amount; a++) { + const x = Math.random() - 0.5; + const y = Math.random() - 0.5; + const z = Math.random() - 0.5; + + const sprite = new THREE.Sprite(material); + + sprite.position.set(x, y, z); + sprite.position.normalize(); + sprite.position.multiplyScalar(radius); + + // individual rotation per sprite + sprite.userData.rotation = 0; + + group.add(sprite); + } + + scene.add(group); + + // + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(render); + document.body.appendChild(renderer.domElement); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function render() { + const time = Date.now() / 1000; + + for (let i = 0, l = group.children.length; i < l; i++) { + const sprite = group.children[i]; + const scale = Math.sin(time + sprite.position.x * 0.01) * 0.3 + 1.0; + + sprite.userData.rotation += 0.1 * (i / l); + sprite.scale.set(scale * imageWidth, scale * imageHeight, 1.0); + } + + group.rotation.x = time * 0.5; + group.rotation.y = time * 0.75; + group.rotation.z = time * 1.0; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_storage_buffer.ts b/examples-testing/examples/webgpu_storage_buffer.ts new file mode 100644 index 000000000..1b4461d33 --- /dev/null +++ b/examples-testing/examples/webgpu_storage_buffer.ts @@ -0,0 +1,169 @@ +import * as THREE from 'three/webgpu'; +import { storage, If, vec3, uv, uint, float, Fn, instanceIndex, workgroupBarrier } from 'three/tsl'; + +const timestamps = { + webgpu: document.getElementById('timestamps'), + webgl: document.getElementById('timestamps_webgl'), +}; + +// WebGPU Backend +init(); + +// WebGL Backend +init(true); + +async function init(forceWebGL = false) { + const aspect = window.innerWidth / 2 / window.innerHeight; + const camera = new THREE.OrthographicCamera(-aspect, aspect, 1, -1, 0, 2); + camera.position.z = 1; + + const scene = new THREE.Scene(); + + // texture + + const size = 32; // non power of two buffer size is not well supported in WebGPU + const barCount = 32; + + const type = ['float', 'vec2', 'vec3', 'vec4']; + + const arrayBufferNodes = []; + + for (let i = 0; i < type.length; i++) { + const typeSize = i + 1; + const array = new Array(size * typeSize).fill(0); + + const arrayBuffer = new THREE.StorageInstancedBufferAttribute(new Float32Array(array), typeSize); + + arrayBufferNodes.push(storage(arrayBuffer, type[i], size).setPBO(true)); + } + + const computeInitOrder = Fn(() => { + for (let i = 0; i < type.length; i++) { + arrayBufferNodes[i].element(instanceIndex).assign(instanceIndex); + } + }); + + const computeInvertOrder = Fn(() => { + for (let i = 0; i < type.length; i++) { + const invertIndex = arrayBufferNodes[i].element(uint(size - 1).sub(instanceIndex)).toVar(); + workgroupBarrier(); + arrayBufferNodes[i].element(instanceIndex).assign(invertIndex); + } + }); + + // compute + + const computeInit = computeInitOrder().compute(size); + + const compute = computeInvertOrder().compute(size); + + const material = new THREE.MeshBasicNodeMaterial({ color: 0x00ff00 }); + + material.colorNode = Fn(() => { + const index = uint(uv().x.mul(size).floor()).toVar(); + + If(index.greaterThanEqual(size), () => { + index.assign(uint(size).sub(1)); + }); + + const color = vec3(0, 0, 0).toVar(); + + If(uv().y.greaterThan(0.0), () => { + const indexValue = arrayBufferNodes[0].element(index).toVar(); + const value = float(indexValue).div(float(size)).mul(barCount).floor().div(barCount); + + color.assign(vec3(value, 0, 0)); + }); + + If(uv().y.greaterThan(0.25), () => { + const indexValue = arrayBufferNodes[1].element(index).toVar(); + const value = float(indexValue).div(float(size)).mul(barCount).floor().div(barCount); + + color.assign(vec3(0, value, 0)); + }); + + If(uv().y.greaterThan(0.5), () => { + const indexValue = arrayBufferNodes[2].element(index).toVar(); + const value = float(indexValue).div(float(size)).mul(barCount).floor().div(barCount); + + color.assign(vec3(0, 0, value)); + }); + + If(uv().y.greaterThan(0.75), () => { + const indexValue = arrayBufferNodes[3].element(index).toVar(); + const value = float(indexValue).div(float(size)).mul(barCount).floor().div(barCount); + + color.assign(vec3(value, value, value)); + }); + + return color; + })(); + + // + + const plane = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), material); + scene.add(plane); + + const renderer = new THREE.WebGPURenderer({ antialias: false, forceWebGL: forceWebGL, trackTimestamp: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth / 2, window.innerHeight); + + await renderer.init(); + + document.body.appendChild(renderer.domElement); + renderer.domElement.style.position = 'absolute'; + renderer.domElement.style.top = '0'; + renderer.domElement.style.left = '0'; + renderer.domElement.style.width = '50%'; + renderer.domElement.style.height = '100%'; + + if (forceWebGL) { + renderer.domElement.style.left = '50%'; + + scene.background = new THREE.Color(0x212121); + } else { + scene.background = new THREE.Color(0x313131); + } + + renderer.compute(computeInit); + + // + + renderer.info.autoReset = false; + + const stepAnimation = async function () { + renderer.info.reset(); + + renderer.compute(compute); + renderer.render(scene, camera); + + renderer.resolveTimestampsAsync(THREE.TimestampQuery.COMPUTE); + renderer.resolveTimestampsAsync(THREE.TimestampQuery.RENDER); + + timestamps[forceWebGL ? 'webgl' : 'webgpu'].innerHTML = ` + + Compute ${renderer.info.compute.frameCalls} pass in ${renderer.info.compute.timestamp.toFixed(6)}ms
+ Draw ${renderer.info.render.drawCalls} pass in ${renderer.info.render.timestamp.toFixed(6)}ms`; + + setTimeout(stepAnimation, 1000); + }; + + stepAnimation(); + + window.addEventListener('resize', onWindowResize); + + function onWindowResize() { + renderer.setSize(window.innerWidth / 2, window.innerHeight); + + const aspect = window.innerWidth / 2 / window.innerHeight; + + const frustumHeight = camera.top - camera.bottom; + + camera.left = (-frustumHeight * aspect) / 2; + camera.right = (frustumHeight * aspect) / 2; + + camera.updateProjectionMatrix(); + + renderer.render(scene, camera); + } +} diff --git a/examples-testing/examples/webgpu_struct_drawindirect.ts b/examples-testing/examples/webgpu_struct_drawindirect.ts new file mode 100644 index 000000000..b0d1139db --- /dev/null +++ b/examples-testing/examples/webgpu_struct_drawindirect.ts @@ -0,0 +1,212 @@ +import * as THREE from 'three/webgpu'; +import { + struct, + storage, + sin, + cross, + normalize, + abs, + mix, + Fn, + vec4, + max, + pow, + time, + varyingProperty, + attribute, + uint, + atomicStore, +} from 'three/tsl'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import WebGPU from 'three/addons/capabilities/WebGPU.js'; + +if (WebGPU.isAvailable() === false) { + document.body.appendChild(WebGPU.getErrorMessage()); + + throw new Error('No WebGPU support'); +} + +const renderer = new THREE.WebGPURenderer({ antialias: true }); +renderer.outputColorSpace = THREE.SRGBColorSpace; +renderer.setPixelRatio(window.devicePixelRatio); +renderer.setSize(window.innerWidth, window.innerHeight); +renderer.setClearColor(0x000000); +renderer.setClearAlpha(0); +document.body.appendChild(renderer.domElement); + +const aspect = window.innerWidth / window.innerHeight; + +const camera = new THREE.PerspectiveCamera(50.0, aspect, 0.1, 10000); +const scene = new THREE.Scene(); + +scene.background = new THREE.Color(0x00001f); +camera.position.set(1, 1, 1); +const controls = new OrbitControls(camera, renderer.domElement); + +let computeDrawBuffer, computeInitDrawBuffer; + +init(); + +async function init() { + await renderer.init(); + + // geometry + + const vector = new THREE.Vector4(); + + const instances = 100000; + + const positions = []; + const offsets = []; + const colors = []; + const orientationsStart = []; + const orientationsEnd = []; + + positions.push(0.025, -0.025, 0); + positions.push(-0.025, 0.025, 0); + positions.push(0, 0, 0.025); + + // instanced attributes + + for (let i = 0; i < instances; i++) { + // offsets + + offsets.push(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5); + + // colors + + colors.push(Math.random(), Math.random(), Math.random(), Math.random()); + + // orientation start + + vector.set(Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1); + vector.normalize(); + + orientationsStart.push(vector.x, vector.y, vector.z, vector.w); + + // orientation end + + vector.set(Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1); + vector.normalize(); + + orientationsEnd.push(vector.x, vector.y, vector.z, vector.w); + } + + const geometry = new THREE.InstancedBufferGeometry(); + geometry.instanceCount = instances; + + geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)); + geometry.setAttribute('offset', new THREE.InstancedBufferAttribute(new Float32Array(offsets), 3)); + geometry.setAttribute('color', new THREE.InstancedBufferAttribute(new Float32Array(colors), 4)); + geometry.setAttribute( + 'orientationStart', + new THREE.InstancedBufferAttribute(new Float32Array(orientationsStart), 4), + ); + geometry.setAttribute('orientationEnd', new THREE.InstancedBufferAttribute(new Float32Array(orientationsEnd), 4)); + + const drawBuffer = new THREE.IndirectStorageBufferAttribute(new Uint32Array(5), 5); + geometry.setIndirect(drawBuffer); + + const drawBufferStruct = struct( + { + vertexCount: 'uint', + instanceCount: { type: 'uint', atomic: true }, + firstVertex: 'uint', + firstInstance: 'uint', + offset: 'uint', + }, + 'DrawBuffer', + ); + + const drawStorage = storage(drawBuffer, drawBufferStruct, drawBuffer.count); + + computeDrawBuffer = Fn(() => { + const halfTime = sin(time.mul(0.5)); + + const instanceCount = max(pow(halfTime.add(1), 4.0).mul(instances), 100).toVar('instanceCount'); + atomicStore(drawStorage.get('instanceCount'), instanceCount); + })().compute(instances); + + computeInitDrawBuffer = Fn(() => { + const drawInfo = drawStorage; + + drawInfo.get('vertexCount').assign(3); + atomicStore(drawInfo.get('instanceCount'), uint(0)); + drawInfo.get('firstVertex').assign(0); + drawInfo.get('firstInstance').assign(0); + drawInfo.get('offset').assign(0); + })().compute(1); + + const vPosition = varyingProperty('vec3', 'vPosition'); + const vColor = varyingProperty('vec4', 'vColor'); + + const positionShaderParams = { + position: attribute('position'), + offset: attribute('offset'), + color: attribute('color'), + orientationStart: attribute('orientationStart'), + orientationEnd: attribute('orientationEnd'), + time: time, + }; + + const positionFn = Fn(() => { + const { position, offset, color, orientationStart, orientationEnd } = positionShaderParams; + + const halfTime = sin(time.mul(0.5)); + + // Convert slowed sign range of (-1 to 1) to range of (1 -> 0 / 0.5 -> 3) + const oscilationRange = max(abs(halfTime.mul(2.0).add(1.0)), 0.5); + + const sphereOscilation = offset.mul(oscilationRange).add(position).toVar(); + + const orientation = normalize(mix(orientationStart, orientationEnd, halfTime)); + const vcV = cross(orientation.xyz, sphereOscilation); + const crossvcV = cross(orientation.xyz, vcV); + + vPosition.assign(vcV.mul(orientation.w.mul(2.0)).add(crossvcV.mul(2.0).add(sphereOscilation))); + vColor.assign(color); + + return vPosition; + })(); + + const fragmentFn = Fn(() => { + const color = vec4(vColor).toVar(); + + color.r.addAssign(sin(vPosition.x.mul(10.0).add(time)).mul(0.5)); + + return color; + })(); + + const material = new THREE.MeshBasicNodeMaterial({ + side: THREE.DoubleSide, + forceSinglePass: true, + transparent: true, + }); + + material.positionNode = positionFn; + material.fragmentNode = fragmentFn; + + const mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + renderer.setAnimationLoop(render); + + window.addEventListener('resize', onWindowResize, false); +} + +function render() { + controls.update(); + + renderer.render(scene, camera); + + renderer.compute(computeInitDrawBuffer); + renderer.compute(computeDrawBuffer); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + renderer.setSize(window.innerWidth, window.innerHeight); +} diff --git a/examples-testing/examples/webgpu_test_memory.ts b/examples-testing/examples/webgpu_test_memory.ts new file mode 100644 index 000000000..aadde4dc6 --- /dev/null +++ b/examples-testing/examples/webgpu_test_memory.ts @@ -0,0 +1,223 @@ +import * as THREE from 'three/webgpu'; +import { pass, mrt, directionToColor, normalView, screenUV, context, sample, colorToDirection } from 'three/tsl'; +import { outline } from 'three/addons/tsl/display/OutlineNode.js'; +import { ao } from 'three/addons/tsl/display/GTAONode.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer, light; +let generateMeshes = true; +let mesh; +let renderPipeline; +let aoNode, outlineNode, scenePass, prePass; +const selectedObjects = []; + +const params = { + castShadow: true, + enablePP: false, + enableOutline: true, + enableAO: true, + recreateLight: () => { + // Remove existing light + if (light) { + scene.remove(light); + light.dispose(); + light = null; + } + + // Create new directional light + light = new THREE.DirectionalLight(0xffffff, 1); + light.position.set(Math.random() * 200 - 100, 100, Math.random() * 200 - 100); + light.castShadow = params.castShadow; + scene.add(light); + }, + start: () => { + generateMeshes = true; + }, + stop: () => { + generateMeshes = false; + }, +}; + +init(); + +async function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 10000); + camera.position.z = 200; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xffffff); + + renderer = new THREE.WebGPURenderer(); + renderer.shadowMap.enabled = true; + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + container.appendChild(renderer.domElement); + + await renderer.init(); + + light = new THREE.DirectionalLight(0xffffff, 1); + light.position.set(0, 100, 0); + light.castShadow = true; + scene.add(light); + + const planeGeometry = new THREE.PlaneGeometry(1000, 1000); + const planeMaterial = new THREE.MeshLambertMaterial({ color: 0xcccccc }); + const plane = new THREE.Mesh(planeGeometry, planeMaterial); + plane.rotation.x = -Math.PI / 2; + plane.position.y = -100; + plane.receiveShadow = true; + scene.add(plane); + + // Inspector UI + const gui = renderer.inspector.createParameters('Settings'); + + gui.add(params, 'castShadow') + .name('Cast Shadow') + .onChange(value => { + if (light) light.castShadow = value; + }); + + const ppFolder = gui.addFolder('Post Processing'); + ppFolder.add(params, 'enablePP').name('Enable').onChange(updatePostProcessing); + ppFolder.add(params, 'enableOutline').name('Outline').onChange(updatePostProcessing); + ppFolder.add(params, 'enableAO').name('AO').onChange(updatePostProcessing); + + gui.add(params, 'recreateLight').name('Recreate Directional Light'); + gui.add(params, 'start').name('Start Creating Meshes'); + gui.add(params, 'stop').name('Stop Creating Meshes'); + + window.addEventListener('resize', onWindowResize); +} + +function updatePostProcessing() { + if (renderPipeline) { + renderPipeline.dispose(); + renderPipeline = null; + } + + if (scenePass) { + scenePass.dispose(); + scenePass = null; + } + + if (prePass) { + prePass.dispose(); + prePass = null; + } + + if (aoNode) { + aoNode.dispose(); + aoNode = null; + } + + if (outlineNode) { + outlineNode.dispose(); + outlineNode = null; + } + + if (params.enablePP) { + renderPipeline = new THREE.RenderPipeline(renderer); + + scenePass = pass(scene, camera); + let colorNode = scenePass; + + if (params.enableAO) { + prePass = pass(scene, camera); + prePass.setMRT( + mrt({ + output: directionToColor(normalView), + }), + ); + + const prePassNormal = sample(uv => { + return colorToDirection(prePass.getTextureNode().sample(uv)); + }); + const prePassDepth = prePass.getTextureNode('depth'); + + aoNode = ao(prePassDepth, prePassNormal, camera); + + scenePass.contextNode = context({ + ao: aoNode.getTextureNode().sample(screenUV).r, + }); + } + + if (params.enableOutline) { + outlineNode = outline(scene, camera, { + selectedObjects: selectedObjects, + }); + colorNode = colorNode.add(outlineNode); + } + + renderPipeline.outputNode = colorNode; + } +} + +function onWindowResize() { + const width = window.innerWidth; + const height = window.innerHeight; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); +} + +function createImage() { + const canvas = document.createElement('canvas'); + canvas.width = 256; + canvas.height = 256; + + const canvas2DContext = canvas.getContext('2d'); + canvas2DContext.fillStyle = + 'rgb(' + + Math.floor(Math.random() * 256) + + ',' + + Math.floor(Math.random() * 256) + + ',' + + Math.floor(Math.random() * 256) + + ')'; + canvas2DContext.fillRect(0, 0, 256, 256); + + return canvas; +} + +// + +function animate() { + if (generateMeshes) { + if (mesh) { + scene.remove(mesh); + + mesh.geometry.dispose(); + mesh.material.map.dispose(); + mesh.material.dispose(); + } + + const geometry = new THREE.SphereGeometry(50, Math.random() * 64, Math.random() * 32); + + const texture = new THREE.CanvasTexture(createImage()); + + const material = new THREE.MeshLambertMaterial({ map: texture }); + + mesh = new THREE.Mesh(geometry, material); + mesh.castShadow = true; + + scene.add(mesh); + + if (outlineNode) { + selectedObjects[0] = mesh; + } + } + + if (renderPipeline) { + renderPipeline.render(); + } else { + renderer.render(scene, camera); + } +} diff --git a/examples-testing/examples/webgpu_texturegrad.ts b/examples-testing/examples/webgpu_texturegrad.ts new file mode 100644 index 000000000..da07b4d31 --- /dev/null +++ b/examples-testing/examples/webgpu_texturegrad.ts @@ -0,0 +1,116 @@ +import * as THREE from 'three/webgpu'; +import { If, vec4, float, time, cos, pow, vec2, uv, texture, Fn } from 'three/tsl'; + +// WebGPU Backend +init(); + +// WebGL Backend +init(true); + +async function init(forceWebGL = false) { + const aspect = window.innerWidth / 2 / window.innerHeight; + const camera = new THREE.OrthographicCamera(-aspect, aspect); + camera.position.z = 2; + + const scene = new THREE.Scene(); + + // texture + + const material = new THREE.MeshBasicNodeMaterial({ color: 0xffffff }); + + // load async brick_diffuse + const map = await new THREE.TextureLoader().loadAsync('textures/uv_grid_opengl.jpg'); + + material.colorNode = Fn(() => { + const color = vec4(1).toVar(); + + const vuv = uv().toVar(); + const blur = pow( + float(0.0625) + .sub(cos(vuv.x.mul(20.0).add(time))) + .mul(0.0625), + 2.0, + ); + + const grad = vec2(blur).toVar(); + + If(vuv.y.greaterThan(0.5), () => { + grad.assign(0); + }); + + color.assign( + texture(map, vuv.add(vec2(blur, blur).mul(0.5))) + .grad(grad, grad) + .mul(0.25) + .add( + texture(map, vuv.add(vec2(blur, blur.negate()).mul(0.5))) + .grad(grad, grad) + .mul(0.25), + ) + .add( + texture(map, vuv.add(vec2(blur.negate(), blur).mul(0.5))) + .grad(grad, grad) + .mul(0.25), + ) + .add( + texture(map, vuv.add(vec2(blur.negate(), blur.negate()).mul(0.5))) + .grad(grad, grad) + .mul(0.25), + ), + ); + + If(vuv.y.greaterThan(0.497).and(vuv.y.lessThan(0.503)), () => { + color.assign(1); + }); + + return color; + })(); + + // + + const box = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), material); + scene.add(box); + + const renderer = new THREE.WebGPURenderer({ antialias: false, forceWebGL: forceWebGL }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth / 2, window.innerHeight); + renderer.setAnimationLoop(animate); + + document.body.appendChild(renderer.domElement); + renderer.domElement.style.position = 'absolute'; + renderer.domElement.style.top = '0'; + renderer.domElement.style.left = '0'; + renderer.domElement.style.width = '50%'; + renderer.domElement.style.height = '100%'; + + if (forceWebGL) { + renderer.domElement.style.left = '50%'; + + scene.background = new THREE.Color(0x212121); + } else { + scene.background = new THREE.Color(0x313131); + } + + // + + function animate() { + renderer.render(scene, camera); + } + + window.addEventListener('resize', onWindowResize); + + function onWindowResize() { + renderer.setSize(window.innerWidth / 2, window.innerHeight); + + const aspect = window.innerWidth / 2 / window.innerHeight; + + const frustumHeight = camera.top - camera.bottom; + + camera.left = (-frustumHeight * aspect) / 2; + camera.right = (frustumHeight * aspect) / 2; + + camera.updateProjectionMatrix(); + + renderer.render(scene, camera); + } +} diff --git a/examples-testing/examples/webgpu_textures_2d-array.ts b/examples-testing/examples/webgpu_textures_2d-array.ts new file mode 100644 index 000000000..a1a354ff7 --- /dev/null +++ b/examples-testing/examples/webgpu_textures_2d-array.ts @@ -0,0 +1,69 @@ +import * as THREE from 'three/webgpu'; +import { texture, uv, time, oscTriangle } from 'three/tsl'; + +import { unzipSync } from 'three/addons/libs/fflate.module.js'; + +// + +let camera, scene, mesh, renderer; + +const planeWidth = 50; +const planeHeight = 50; + +init(); + +function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 2000); + camera.position.z = 70; + + scene = new THREE.Scene(); + + // width 256, height 256, depth 109, 8-bit, zip archived raw data + + new THREE.FileLoader().setResponseType('arraybuffer').load('textures/3d/head256x256x109.zip', function (data) { + const zip = unzipSync(new Uint8Array(data)); + const array = new Uint8Array(zip['head256x256x109'].buffer); + + const map = new THREE.DataArrayTexture(array, 256, 256, 109); + map.format = THREE.RedFormat; + map.needsUpdate = true; + + let coord = uv(); + coord = coord.setY(coord.y.oneMinus()); // flip y + + let oscLayers = oscTriangle(time.mul(0.5)); // [ /\/ ] triangle osc animation + oscLayers = oscLayers.add(1).mul(0.5); // convert osc range of [ -1, 1 ] to [ 0, 1 ] + oscLayers = oscLayers.mul(map.image.depth); // scale osc range to texture depth + + const material = new THREE.MeshBasicNodeMaterial(); + material.colorNode = texture(map, coord).depth(oscLayers).r.remap(0, 1, -0.1, 1.8); // remap to make it more visible + + const geometry = new THREE.PlaneGeometry(planeWidth, planeHeight); + + mesh = new THREE.Mesh(geometry, material); + + scene.add(mesh); + }); + + renderer = new THREE.WebGPURenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(render); + container.appendChild(renderer.domElement); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_textures_2d-array_compressed.ts b/examples-testing/examples/webgpu_textures_2d-array_compressed.ts new file mode 100644 index 000000000..62b516458 --- /dev/null +++ b/examples-testing/examples/webgpu_textures_2d-array_compressed.ts @@ -0,0 +1,86 @@ +import * as THREE from 'three/webgpu'; + +import { texture, uniform, uv } from 'three/tsl'; + +import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js'; + +// + +let camera, scene, mesh, renderer, timer; + +const depth = uniform(0); + +const planeWidth = 50; +const planeHeight = 25; + +let depthStep = 1; + +init(); + +async function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 2000); + camera.position.z = 70; + + scene = new THREE.Scene(); + + // + timer = new THREE.Timer(); + timer.connect(document); + + renderer = new THREE.WebGPURenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + await renderer.init(); + + // + + const ktx2Loader = new KTX2Loader(); + ktx2Loader.setTranscoderPath('jsm/libs/basis/'); + ktx2Loader.detectSupport(renderer); + + ktx2Loader.load('textures/spiritedaway.ktx2', function (texturearray) { + const material = new THREE.NodeMaterial(); + + material.colorNode = texture(texturearray, uv().flipY()).depth(depth); + const geometry = new THREE.PlaneGeometry(planeWidth, planeHeight); + + mesh = new THREE.Mesh(geometry, material); + + scene.add(mesh); + }); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + if (mesh) { + const delta = timer.getDelta() * 10; + + depthStep += delta; + + const value = depthStep % 5; + + depth.value = value; + } + + render(); +} + +function render() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_textures_anisotropy.ts b/examples-testing/examples/webgpu_textures_anisotropy.ts new file mode 100644 index 000000000..0444823f5 --- /dev/null +++ b/examples-testing/examples/webgpu_textures_anisotropy.ts @@ -0,0 +1,145 @@ +import * as THREE from 'three/webgpu'; + +let container; + +let camera, scene1, scene2, renderer; + +let mouseX = 0, + mouseY = 0; + +init(); + +function init() { + const SCREEN_WIDTH = window.innerWidth; + const SCREEN_HEIGHT = window.innerHeight; + + container = document.createElement('div'); + document.body.appendChild(container); + + renderer = new THREE.WebGPURenderer({ antialias: true, forceWebGL: false }); + + // RENDERER + + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); + renderer.setAnimationLoop(render); + renderer.autoClear = false; + + renderer.domElement.style.position = 'relative'; + container.appendChild(renderer.domElement); + + // + + camera = new THREE.PerspectiveCamera(35, SCREEN_WIDTH / SCREEN_HEIGHT, 1, 25000); + camera.position.z = 1500; + + scene1 = new THREE.Scene(); + scene1.fog = new THREE.Fog(0xf2f7ff, 1, 25000); + + scene2 = new THREE.Scene(); + scene2.fog = new THREE.Fog(0xf2f7ff, 1, 25000); + + scene1.add(new THREE.AmbientLight(0xeef0ff, 3)); + scene2.add(new THREE.AmbientLight(0xeef0ff, 3)); + + const light1 = new THREE.DirectionalLight(0xffffff, 6); + light1.position.set(1, 1, 1); + scene1.add(light1); + + const light2 = new THREE.DirectionalLight(0xffffff, 6); + light2.position.set(1, 1, 1); + scene2.add(light2); + + // GROUND + + const textureLoader = new THREE.TextureLoader(); + + const maxAnisotropy = renderer.getMaxAnisotropy(); + + const texture1 = textureLoader.load('textures/crate.gif'); + const material1 = new THREE.MeshPhongMaterial({ color: 0xffffff, map: texture1 }); + + texture1.colorSpace = THREE.SRGBColorSpace; + texture1.anisotropy = renderer.getMaxAnisotropy(); + texture1.wrapS = texture1.wrapT = THREE.RepeatWrapping; + texture1.repeat.set(512, 512); + + const texture2 = textureLoader.load('textures/crate.gif'); + const material2 = new THREE.MeshPhongMaterial({ color: 0xffffff, map: texture2 }); + + texture2.colorSpace = THREE.SRGBColorSpace; + texture2.anisotropy = 1; + texture2.wrapS = texture2.wrapT = THREE.RepeatWrapping; + texture2.repeat.set(512, 512); + + if (maxAnisotropy > 0) { + document.getElementById('val_left').innerHTML = texture1.anisotropy; + document.getElementById('val_right').innerHTML = texture2.anisotropy; + } else { + document.getElementById('val_left').innerHTML = 'not supported'; + document.getElementById('val_right').innerHTML = 'not supported'; + } + + // + + const geometry = new THREE.PlaneGeometry(100, 100); + + const mesh1 = new THREE.Mesh(geometry, material1); + mesh1.rotation.x = -Math.PI / 2; + mesh1.scale.set(1000, 1000, 1000); + + const mesh2 = new THREE.Mesh(geometry, material2); + mesh2.rotation.x = -Math.PI / 2; + mesh2.scale.set(1000, 1000, 1000); + + scene1.add(mesh1); + scene2.add(mesh2); + + // + + document.addEventListener('mousemove', onDocumentMouseMove); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function onDocumentMouseMove(event) { + const windowHalfX = window.innerWidth / 2; + const windowHalfY = window.innerHeight / 2; + + mouseX = event.clientX - windowHalfX; + mouseY = event.clientY - windowHalfY; +} + +function render() { + const SCREEN_WIDTH = window.innerWidth; + const SCREEN_HEIGHT = window.innerHeight; + + camera.position.x += (mouseX - camera.position.x) * 0.05; + camera.position.y = THREE.MathUtils.clamp( + camera.position.y + (-(mouseY - 200) - camera.position.y) * 0.05, + 50, + 1000, + ); + + camera.lookAt(scene1.position); + renderer.clear(); + + renderer.setScissorTest(true); + + renderer.setScissor(0, 0, SCREEN_WIDTH / 2 - 2, SCREEN_HEIGHT); + renderer.render(scene1, camera); + + renderer.setScissorTest(true); + + renderer.setScissor(SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 2 - 2, SCREEN_HEIGHT); + renderer.render(scene2, camera); + + renderer.setScissorTest(false); +} diff --git a/examples-testing/examples/webgpu_textures_partialupdate.ts b/examples-testing/examples/webgpu_textures_partialupdate.ts new file mode 100644 index 000000000..d893a041d --- /dev/null +++ b/examples-testing/examples/webgpu_textures_partialupdate.ts @@ -0,0 +1,105 @@ +import * as THREE from 'three/webgpu'; + +let camera, scene, renderer, timer, dataTexture, diffuseMap; + +let last = 0; +const position = new THREE.Vector2(); +const color = new THREE.Color(); + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 10); + camera.position.z = 2; + + scene = new THREE.Scene(); + + timer = new THREE.Timer(); + timer.connect(document); + + const loader = new THREE.TextureLoader(); + diffuseMap = loader.load('textures/carbon/Carbon.png'); + diffuseMap.colorSpace = THREE.SRGBColorSpace; + diffuseMap.minFilter = THREE.LinearFilter; + diffuseMap.generateMipmaps = false; + + const geometry = new THREE.PlaneGeometry(2, 2); + const material = new THREE.MeshBasicMaterial({ map: diffuseMap }); + + const mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + // + + const width = 32; + const height = 32; + + const data = new Uint8Array(width * height * 4); + dataTexture = new THREE.DataTexture(data, width, height); + dataTexture.colorSpace = THREE.SRGBColorSpace; + + // + + renderer = new THREE.WebGPURenderer({ antialias: true, forceWebGL: false }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + const elapsedTime = timer.getElapsed(); + + renderer.render(scene, camera); + + if (elapsedTime - last > 0.1) { + last = elapsedTime; + + position.x = 32 * THREE.MathUtils.randInt(1, 16) - 32; + position.y = 32 * THREE.MathUtils.randInt(1, 16) - 32; + + // generate new color data + updateDataTexture(dataTexture); + + // perform copy from src to dest texture to a random position + + renderer.copyTextureToTexture(dataTexture, diffuseMap, null, position); + } +} + +function updateDataTexture(texture) { + const size = texture.image.width * texture.image.height; + const data = texture.image.data; + + // generate a random color and update texture data + + color.setHex(Math.random() * 0xffffff); + + const r = Math.floor(color.r * 255); + const g = Math.floor(color.g * 255); + const b = Math.floor(color.b * 255); + + for (let i = 0; i < size; i++) { + const stride = i * 4; + + data[stride] = r; + data[stride + 1] = g; + data[stride + 2] = b; + data[stride + 3] = 1; + } + + texture.needsUpdate = true; +} diff --git a/examples-testing/examples/webgpu_tonemapping.ts b/examples-testing/examples/webgpu_tonemapping.ts new file mode 100644 index 000000000..4140a49fd --- /dev/null +++ b/examples-testing/examples/webgpu_tonemapping.ts @@ -0,0 +1,141 @@ +import * as THREE from 'three/webgpu'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; +import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; + +let renderer, scene, camera, controls; +let gui; + +const params = { + exposure: 1.0, + toneMapping: 'Neutral', + blurriness: 0.3, + intensity: 1.0, +}; + +const toneMappingOptions = { + None: THREE.NoToneMapping, + Linear: THREE.LinearToneMapping, + Reinhard: THREE.ReinhardToneMapping, + Cineon: THREE.CineonToneMapping, + ACESFilmic: THREE.ACESFilmicToneMapping, + AgX: THREE.AgXToneMapping, + Neutral: THREE.NeutralToneMapping, +}; + +init().catch(function (err) { + console.error(err); +}); + +async function init() { + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + renderer.toneMapping = toneMappingOptions[params.toneMapping]; + renderer.toneMappingExposure = params.exposure; + + scene = new THREE.Scene(); + scene.backgroundBlurriness = params.blurriness; + + const light = new THREE.DirectionalLight(0xfff3ee, 3); // simulate sun + light.position.set(1, 0.05, 0.7); + scene.add(light); + + // scene.add( new THREE.DirectionalLightHelper( light, 1, 0x000000 ) ); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.01, 10); + camera.position.set(-0.02, 0.03, 0.05); + + controls = new OrbitControls(camera, renderer.domElement); + controls.enablePan = false; + controls.enableDamping = true; + controls.minDistance = 0.03; + controls.maxDistance = 0.2; + controls.target.set(0, 0.03, 0); + controls.update(); + + const hdrLoader = new HDRLoader().setPath('textures/equirectangular/'); + + const dracoLoader = new DRACOLoader(); + dracoLoader.setDecoderPath('jsm/libs/draco/gltf/'); + + const gltfLoader = new GLTFLoader(); + gltfLoader.setDRACOLoader(dracoLoader); + gltfLoader.setPath('models/gltf/'); + + const [texture, gltf] = await Promise.all([ + hdrLoader.loadAsync('venice_sunset_1k.hdr'), + gltfLoader.loadAsync('venice_mask.glb'), + ]); + + // environment + + texture.mapping = THREE.EquirectangularReflectionMapping; + + scene.background = texture; + scene.environment = texture; + + // model + + scene.add(gltf.scene); + + window.addEventListener('resize', onWindowResize); + + // + + gui = renderer.inspector.createParameters('Settings'); + const toneMappingFolder = gui.addFolder('Tone Mapping'); + + toneMappingFolder + .add(params, 'toneMapping', Object.keys(toneMappingOptions)) + + .name('type') + .onChange(function () { + renderer.toneMapping = toneMappingOptions[params.toneMapping]; + }); + + toneMappingFolder + .add(params, 'exposure', 0, 2) + + .onChange(function (value) { + renderer.toneMappingExposure = value; + }); + + const backgroundFolder = gui.addFolder('Background'); + + backgroundFolder + .add(params, 'blurriness', 0, 1) + + .onChange(function (value) { + scene.backgroundBlurriness = value; + }); + + backgroundFolder + .add(params, 'intensity', 0, 1) + + .onChange(function (value) { + scene.backgroundIntensity = value; + }); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + controls.update(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_tsl_angular_slicing.ts b/examples-testing/examples/webgpu_tsl_angular_slicing.ts new file mode 100644 index 000000000..d74e1bc0a --- /dev/null +++ b/examples-testing/examples/webgpu_tsl_angular_slicing.ts @@ -0,0 +1,175 @@ +import * as THREE from 'three/webgpu'; +import { If, TWO_PI, atan, color, frontFacing, output, positionLocal, Fn, uniform, vec4 } from 'three/tsl'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let camera, scene, renderer, controls; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(-5, 5, 12); + + scene = new THREE.Scene(); + + // environment + + const hdrLoader = new UltraHDRLoader(); + hdrLoader.load('textures/equirectangular/royal_esplanade_2k.hdr.jpg', environmentMap => { + environmentMap.mapping = THREE.EquirectangularReflectionMapping; + + scene.background = environmentMap; + scene.environment = environmentMap; + }); + + // lights + + const directionalLight = new THREE.DirectionalLight('#ffffff', 4); + directionalLight.position.set(6.25, 3, 4); + directionalLight.castShadow = true; + directionalLight.shadow.mapSize.set(2048, 2048); + directionalLight.shadow.camera.near = 0.1; + directionalLight.shadow.camera.far = 30; + directionalLight.shadow.camera.top = 8; + directionalLight.shadow.camera.right = 8; + directionalLight.shadow.camera.bottom = -8; + directionalLight.shadow.camera.left = -8; + directionalLight.shadow.normalBias = 0.05; + scene.add(directionalLight); + + // TSL functions + + const inAngle = Fn(([position, angleStart, angleArc]) => { + const angle = atan(position.y, position.x).sub(angleStart).mod(TWO_PI).toVar(); + return angle.greaterThan(0).and(angle.lessThan(angleArc)); + }); + + // materials + + const defaultMaterial = new THREE.MeshPhysicalNodeMaterial({ + metalness: 0.5, + roughness: 0.25, + envMapIntensity: 0.5, + color: '#858080', + }); + + const slicedMaterial = new THREE.MeshPhysicalNodeMaterial({ + metalness: 0.5, + roughness: 0.25, + envMapIntensity: 0.5, + color: '#858080', + side: THREE.DoubleSide, + }); + + // uniforms + + const sliceStart = uniform(1.75); + const sliceArc = uniform(1.25); + const sliceColor = uniform(color('#b62f58')); + + // mask + + const mask = inAngle(positionLocal.xy, sliceStart, sliceArc).not(); + + slicedMaterial.maskNode = mask; + //slicedMaterial.maskShadowNode = mask; // optional: custom mask shadows + + // output + + slicedMaterial.outputNode = Fn(() => { + // backface color + + const finalOutput = output; + If(frontFacing.not(), () => { + finalOutput.assign(vec4(sliceColor, 1)); + }); + + return finalOutput; + })(); + + // model + + const dracoLoader = new DRACOLoader(); + dracoLoader.setDecoderPath('jsm/libs/draco/'); + + const gltfLoader = new GLTFLoader(); + gltfLoader.setDRACOLoader(dracoLoader); + + gltfLoader.load('./models/gltf/gears.glb', gltf => { + const model = gltf.scene; + + model.traverse(child => { + if (child.isMesh) { + if (child.name === 'outerHull') child.material = slicedMaterial; + else child.material = defaultMaterial; + + child.castShadow = true; + child.receiveShadow = true; + } + }); + + scene.add(model); + }); + + // plane + + const plane = new THREE.Mesh( + new THREE.PlaneGeometry(10, 10, 10), + new THREE.MeshStandardMaterial({ color: '#aaaaaa' }), + ); + plane.receiveShadow = true; + plane.position.set(-4, -3, -4); + plane.lookAt(new THREE.Vector3(0, 0, 0)); + scene.add(plane); + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = 1; + renderer.shadowMap.enabled = true; + renderer.inspector = new Inspector(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // controls + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + controls.minDistance = 0.1; + controls.maxDistance = 50; + + // events + + window.addEventListener('resize', onWindowResize); + + // debug + + const gui = renderer.inspector.createParameters('Parameters'); + gui.add(sliceStart, 'value', -Math.PI, Math.PI, 0.001).name('sliceStart'); + gui.add(sliceArc, 'value', 0, Math.PI * 2, 0.001).name('sliceArc'); + gui.addColor({ color: sliceColor.value.getHexString(THREE.SRGBColorSpace) }, 'color').onChange(value => + sliceColor.value.set(value), + ); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +async function animate() { + controls.update(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_tsl_earth.ts b/examples-testing/examples/webgpu_tsl_earth.ts new file mode 100644 index 000000000..dece09afb --- /dev/null +++ b/examples-testing/examples/webgpu_tsl_earth.ts @@ -0,0 +1,174 @@ +import * as THREE from 'three/webgpu'; +import { + step, + normalWorldGeometry, + output, + texture, + vec3, + vec4, + normalize, + positionWorld, + bumpMap, + cameraPosition, + color, + uniform, + mix, + uv, + max, +} from 'three/tsl'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer, controls, globe, timer; + +init(); + +function init() { + timer = new THREE.Timer(); + timer.connect(document); + + camera = new THREE.PerspectiveCamera(25, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(4.5, 2, 3); + + scene = new THREE.Scene(); + + // sun + + const sun = new THREE.DirectionalLight('#ffffff', 2); + sun.position.set(0, 0, 3); + scene.add(sun); + + // uniforms + + const atmosphereDayColor = uniform(color('#4db2ff')); + const atmosphereTwilightColor = uniform(color('#bc490b')); + const roughnessLow = uniform(0.25); + const roughnessHigh = uniform(0.35); + + // textures + + const textureLoader = new THREE.TextureLoader(); + + const dayTexture = textureLoader.load('./textures/planets/earth_day_4096.jpg'); + dayTexture.colorSpace = THREE.SRGBColorSpace; + dayTexture.anisotropy = 8; + + const nightTexture = textureLoader.load('./textures/planets/earth_night_4096.jpg'); + nightTexture.colorSpace = THREE.SRGBColorSpace; + nightTexture.anisotropy = 8; + + const bumpRoughnessCloudsTexture = textureLoader.load('./textures/planets/earth_bump_roughness_clouds_4096.jpg'); + bumpRoughnessCloudsTexture.anisotropy = 8; + + // fresnel + + const viewDirection = positionWorld.sub(cameraPosition).normalize(); + const fresnel = viewDirection.dot(normalWorldGeometry).abs().oneMinus().toVar(); + + // sun orientation + + const sunOrientation = normalWorldGeometry.dot(normalize(sun.position)).toVar(); + + // atmosphere color + + const atmosphereColor = mix(atmosphereTwilightColor, atmosphereDayColor, sunOrientation.smoothstep(-0.25, 0.75)); + + // globe + + const globeMaterial = new THREE.MeshStandardNodeMaterial(); + + const cloudsStrength = texture(bumpRoughnessCloudsTexture, uv()).b.smoothstep(0.2, 1); + + globeMaterial.colorNode = mix(texture(dayTexture), vec3(1), cloudsStrength.mul(2)); + + const roughness = max(texture(bumpRoughnessCloudsTexture).g, step(0.01, cloudsStrength)); + globeMaterial.roughnessNode = roughness.remap(0, 1, roughnessLow, roughnessHigh); + + const night = texture(nightTexture); + const dayStrength = sunOrientation.smoothstep(-0.25, 0.5); + + const atmosphereDayStrength = sunOrientation.smoothstep(-0.5, 1); + const atmosphereMix = atmosphereDayStrength.mul(fresnel.pow(2)).clamp(0, 1); + + let finalOutput = mix(night.rgb, output.rgb, dayStrength); + finalOutput = mix(finalOutput, atmosphereColor, atmosphereMix); + + globeMaterial.outputNode = vec4(finalOutput, output.a); + + const bumpElevation = max(texture(bumpRoughnessCloudsTexture).r, cloudsStrength); + globeMaterial.normalNode = bumpMap(bumpElevation); + + const sphereGeometry = new THREE.SphereGeometry(1, 64, 64); + globe = new THREE.Mesh(sphereGeometry, globeMaterial); + scene.add(globe); + + // atmosphere + + const atmosphereMaterial = new THREE.MeshBasicNodeMaterial({ side: THREE.BackSide, transparent: true }); + let alpha = fresnel.remap(0.73, 1, 1, 0).pow(3); + alpha = alpha.mul(sunOrientation.smoothstep(-0.5, 1)); + atmosphereMaterial.outputNode = vec4(atmosphereColor, alpha); + + const atmosphere = new THREE.Mesh(sphereGeometry, atmosphereMaterial); + atmosphere.scale.setScalar(1.04); + scene.add(atmosphere); + + // renderer + + renderer = new THREE.WebGPURenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // controls + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + controls.minDistance = 0.1; + controls.maxDistance = 50; + + // events + + window.addEventListener('resize', onWindowResize); + + // debug + + const gui = renderer.inspector.createParameters('Parameters'); + + gui.addColor({ color: atmosphereDayColor.value.getHex(THREE.SRGBColorSpace) }, 'color') + .onChange(value => { + atmosphereDayColor.value.set(value); + }) + .name('atmosphereDayColor'); + + gui.addColor({ color: atmosphereTwilightColor.value.getHex(THREE.SRGBColorSpace) }, 'color') + .onChange(value => { + atmosphereTwilightColor.value.set(value); + }) + .name('atmosphereTwilightColor'); + + gui.add(roughnessLow, 'value', 0, 1, 0.001).name('roughnessLow'); + gui.add(roughnessHigh, 'value', 0, 1, 0.001).name('roughnessHigh'); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +async function animate() { + timer.update(); + + const delta = timer.getDelta(); + globe.rotation.y += delta * 0.025; + + controls.update(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_tsl_galaxy.ts b/examples-testing/examples/webgpu_tsl_galaxy.ts new file mode 100644 index 000000000..739138504 --- /dev/null +++ b/examples-testing/examples/webgpu_tsl_galaxy.ts @@ -0,0 +1,99 @@ +import * as THREE from 'three/webgpu'; +import { color, cos, float, mix, range, sin, time, uniform, uv, vec3, vec4, TWO_PI } from 'three/tsl'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +let camera, scene, renderer, controls; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(4, 2, 5); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x201919); + + // galaxy + + const material = new THREE.SpriteNodeMaterial({ + depthWrite: false, + blending: THREE.AdditiveBlending, + }); + + const size = uniform(0.08); + material.scaleNode = range(0, 1).mul(size); + + const radiusRatio = range(0, 1); + const radius = radiusRatio.pow(1.5).mul(5).toVar(); + + const branches = 3; + const branchAngle = range(0, branches).floor().mul(TWO_PI.div(branches)); + const angle = branchAngle.add(time.mul(radiusRatio.oneMinus())); + + const position = vec3(cos(angle), 0, sin(angle)).mul(radius); + + const randomOffset = range(vec3(-1), vec3(1)).pow3().mul(radiusRatio).add(0.2); + + material.positionNode = position.add(randomOffset); + + const colorInside = uniform(color('#ffa575')); + const colorOutside = uniform(color('#311599')); + const colorFinal = mix(colorInside, colorOutside, radiusRatio.oneMinus().pow(2).oneMinus()); + const alpha = float(0.1).div(uv().sub(0.5).length()).sub(0.2); + material.colorNode = vec4(colorFinal, alpha); + + const mesh = new THREE.InstancedMesh(new THREE.PlaneGeometry(1, 1), material, 20000); + scene.add(mesh); + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + controls.minDistance = 0.1; + controls.maxDistance = 50; + + // events + + window.addEventListener('resize', onWindowResize); + + // debug + + const gui = renderer.inspector.createParameters('Parameters'); + + gui.add(size, 'value', 0, 1, 0.001).name('size'); + + gui.addColor({ color: colorInside.value.getHex(THREE.SRGBColorSpace) }, 'color') + .name('colorInside') + .onChange(function (value) { + colorInside.value.set(value); + }); + + gui.addColor({ color: colorOutside.value.getHex(THREE.SRGBColorSpace) }, 'color') + .name('colorOutside') + .onChange(function (value) { + colorOutside.value.set(value); + }); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + controls.update(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_tsl_graph.ts b/examples-testing/examples/webgpu_tsl_graph.ts new file mode 100644 index 000000000..cdde58a9a --- /dev/null +++ b/examples-testing/examples/webgpu_tsl_graph.ts @@ -0,0 +1,228 @@ +import * as THREE from 'three/webgpu'; + +import { Fn, abs, fract, fwidth, length, max, saturate, smoothstep, vec3, vec4, positionWorld, float } from 'three/tsl'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { TSLGraphLoader } from 'three/addons/inspector/extensions/tsl-graph/TSLGraphLoader.js'; + +let camera, scene, renderer; +let controls; +let prefab; + +init(); + +async function initTSLGraph() { + // Create Materials + + const m1 = new THREE.MeshPhysicalNodeMaterial(); + m1.userData.graphId = 'mat_1-physical'; + + const m2 = new THREE.MeshStandardNodeMaterial(); + m2.userData.graphId = 'mat_2-standard'; + + const m3 = new THREE.MeshPhongNodeMaterial(); + m3.userData.graphId = 'mat_3-phong'; + + const m4 = new THREE.MeshBasicNodeMaterial(); + m4.userData.graphId = 'mat_4-basic'; + + const materials = [m1, m2, m3, m4]; + + for (let i = 0; i < materials.length; i++) { + createShaderBall(materials[i], new THREE.Vector3((i & 1) * 4 - 2, 0, (i & 2) * 2 - 2)); + } + + // TSL Graph Editor + + renderer.inspector.onExtension('TSL Graph', async tslGraph => { + renderer.inspector.setActiveTab(tslGraph); + + // Apply TSL Graph from Local Storage if exists + // Every time a TSL Graph is changed, it will be stored in the local storage + + if (tslGraph.hasGraphs) { + tslGraph.apply(scene); + } else { + // Load a TSL Graph from a file + // Use it for production + + const tslLoader = new TSLGraphLoader(); + const applier = await tslLoader.setPath('./shaders/').loadAsync('tsl-graphs.json'); + + applier.apply(scene); + } + }); + + // Active TSL Graph Editor + // Only is needed if you don't activate it from the GUI + + renderer.inspector.setActiveExtension('TSL Graph', true); +} + +async function init() { + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.toneMapping = THREE.NeutralToneMapping; + renderer.toneMappingExposure = 0.9; + renderer.inspector = new Inspector(); + renderer.setAnimationLoop(render); + document.body.appendChild(renderer.domElement); + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 200); + camera.position.set(3, 5, 8); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x444444); + + await renderer.init(); + + // Ground plane + + const plane = new THREE.Mesh(new THREE.CircleGeometry(40), createGroudMaterial()); + plane.rotation.x = -Math.PI / 2; + plane.renderOrder = -1; + scene.add(plane); + + // + + controls = new OrbitControls(camera); + controls.connect(renderer.domElement); + controls.enableDamping = true; + controls.minDistance = 2; + controls.maxDistance = 40; + + // + + const environment = await new HDRLoader() + .setPath('textures/equirectangular/') + .loadAsync('monochrome_studio_02_1k.hdr'); + environment.mapping = THREE.EquirectangularReflectionMapping; + + const light = new THREE.DirectionalLight(0xffffff, 1); + light.position.set(1, 1, 1); + scene.add(light); + + scene.environment = environment; + + prefab = (await new GLTFLoader().loadAsync('./models/gltf/ShaderBall.glb')).scene; + + await initTSLGraph(); + + initGUI(); + + // Events + + resize(); + + window.addEventListener('resize', resize); + renderer.inspector.addEventListener('resize', resize); +} + +async function createShaderBall(material, position = new THREE.Vector3()) { + const model = prefab.clone(); + model.position.copy(position); + scene.add(model); + + // + + const calibrationMesh = model.getObjectByName('Calibration_Mesh'); + calibrationMesh.material = material; + + const previewMesh = model.getObjectByName('Preview_Mesh'); + previewMesh.material = material; + + calibrationMesh.renderOrder = 1; + previewMesh.renderOrder = 2; +} + +function initGUI() { + const gui = renderer.inspector.createParameters('Shader Ball'); + + const API = { + showCalibrationMesh: true, + showPreviewMesh: true, + }; + + gui.add(API, 'showCalibrationMesh') + .name('Calibration Mesh') + .onChange(function (value) { + setVisibility('Calibration_Mesh', value); + }); + + gui.add(API, 'showPreviewMesh') + .name('Preview Mesh') + .onChange(function (value) { + setVisibility('Preview_Mesh', value); + }); + + renderer.inspector.hide(); +} + +function resize() { + const size = renderer.inspector.getSize(); + const width = window.innerWidth - size.width; + const height = window.innerHeight - size.height; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); +} + +function render() { + if (controls) controls.update(); + + renderer.render(scene, camera); +} + +// + +function setVisibility(name, visible) { + scene.traverse(function (node) { + if (node.isMesh) { + if (node.name == name) node.visible = visible; + } + }); +} + +function createGroudMaterial() { + const material = new THREE.MeshBasicNodeMaterial(); + + const grid = Fn(([coord, lineWidth = float(0.01), dotSize = float(0.03)]) => { + // https://bgolus.medium.com/the-best-darn-grid-shader-yet-727f9278b9d8 + + const g = fract(coord); + const fw = fwidth(coord); + const gx = abs(g.x.sub(0.5)); + const gy = abs(g.y.sub(0.5)); + + const lineX = saturate(lineWidth.sub(gx).div(fw.x).add(0.5)); + const lineY = saturate(lineWidth.sub(gy).div(fw.y).add(0.5)); + const lines = max(lineX, lineY); + + const squareDist = max(gx, gy); + const aa = max(fw.x, fw.y); + const dots = smoothstep(dotSize.add(aa), dotSize.sub(aa), squareDist); + + return max(dots, lines); + }); + + const fade = Fn(([radius = float(10.0), falloff = float(1.0)]) => { + return smoothstep(radius, radius.sub(falloff), length(positionWorld)); + }); + + const gridColor = vec4(vec3(0.2), 1.0); + const baseColor = vec4(vec3(0.4), 0.0); + + material.colorNode = grid(positionWorld.xz, 0.007, 0.03).mix(baseColor, gridColor).mul(fade(30.0, 20.0)); + material.transparent = true; + + return material; +} diff --git a/examples-testing/examples/webgpu_tsl_halftone.ts b/examples-testing/examples/webgpu_tsl_halftone.ts new file mode 100644 index 000000000..682e1df64 --- /dev/null +++ b/examples-testing/examples/webgpu_tsl_halftone.ts @@ -0,0 +1,214 @@ +import * as THREE from 'three/webgpu'; +import { color, mix, normalWorld, output, Fn, uniform, vec4, rotate, screenCoordinate, screenSize } from 'three/tsl'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +let camera, scene, renderer, controls, timer, halftoneSettings; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(25, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(6, 3, 10); + + scene = new THREE.Scene(); + + timer = new THREE.Timer(); + timer.connect(document); + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.setClearColor('#000000'); + document.body.appendChild(renderer.domElement); + + // inspector/gui + + renderer.inspector = new Inspector(); + + const gui = renderer.inspector.createParameters('Parameters'); + + // lights + + const ambientLight = new THREE.AmbientLight('#ffffff', 3); + scene.add(ambientLight); + + const directionalLight = new THREE.DirectionalLight('#ffffff', 8); + directionalLight.position.set(4, 3, 1); + scene.add(directionalLight); + + const lightsFolder = gui.addFolder('💡 lights'); + lightsFolder.add(ambientLight, 'intensity', 0, 10, 0.001).name('ambient intensity'); + lightsFolder.add(directionalLight, 'intensity', 0, 20, 0.001).name('directional intensity'); + + // halftone settings + + halftoneSettings = [ + // purple shade + + { + count: 140, + color: '#fb00ff', + direction: new THREE.Vector3(-0.4, -1, 0.5), + start: 1, + end: 0, + mixLow: 0, + mixHigh: 0.5, + radius: 0.8, + }, + + // cyan highlight + + { + count: 180, + color: '#94ffd1', + direction: new THREE.Vector3(0.5, 0.5, -0.2), + start: 0.55, + end: 0.2, + mixLow: 0.5, + mixHigh: 1, + radius: 0.8, + }, + ]; + + for (const index in halftoneSettings) { + const settings = halftoneSettings[index]; + + // uniforms + + const uniforms = {}; + + uniforms.count = uniform(settings.count); + uniforms.color = uniform(color(settings.color)); + uniforms.direction = uniform(settings.direction); + uniforms.start = uniform(settings.start); + uniforms.end = uniform(settings.end); + uniforms.mixLow = uniform(settings.mixLow); + uniforms.mixHigh = uniform(settings.mixHigh); + uniforms.radius = uniform(settings.radius); + + settings.uniforms = uniforms; + + // debug + + const folder = gui.addFolder(`⚪️ halftone ${index}`); + + folder.addColor({ color: uniforms.color.value }, 'color'); + folder.add(uniforms.count, 'value', 1, 200, 1).name('count'); + folder.add(uniforms.direction.value, 'x', -1, 1, 0.01).listen(); + folder.add(uniforms.direction.value, 'y', -1, 1, 0.01).listen(); + folder.add(uniforms.direction.value, 'z', -1, 1, 0.01).listen(); + folder.add(uniforms.start, 'value', -1, 1, 0.01).name('start'); + folder.add(uniforms.end, 'value', -1, 1, 0.01).name('end'); + folder.add(uniforms.mixLow, 'value', 0, 1, 0.01).name('mix low'); + folder.add(uniforms.mixHigh, 'value', 0, 1, 0.01).name('mix high'); + folder.add(uniforms.radius, 'value', 0, 1, 0.01).name('radius'); + } + + // halftone functions + + const halftone = Fn(([count, color, direction, start, end, radius, mixLow, mixHigh]) => { + // grid pattern + + let gridUv = screenCoordinate.xy.div(screenSize.yy).mul(count); + gridUv = rotate(gridUv, Math.PI * 0.25).mod(1); + + // orientation strength + + const orientationStrength = normalWorld.dot(direction.normalize()).remapClamp(end, start, 0, 1); + + // mask + + const mask = orientationStrength + .mul(radius) + .mul(0.5) + .step(gridUv.sub(0.5).length()) + .mul(mix(mixLow, mixHigh, orientationStrength)); + + return vec4(color, mask); + }); + + const halftones = Fn(([input]) => { + const halftonesOutput = input; + + for (const settings of halftoneSettings) { + const halfToneOutput = halftone( + settings.uniforms.count, + settings.uniforms.color, + settings.uniforms.direction, + settings.uniforms.start, + settings.uniforms.end, + settings.uniforms.radius, + settings.uniforms.mixLow, + settings.uniforms.mixHigh, + ); + halftonesOutput.rgb.assign(mix(halftonesOutput.rgb, halfToneOutput.rgb, halfToneOutput.a)); + } + + return halftonesOutput; + }); + + // default material + + const defaultMaterial = new THREE.MeshStandardNodeMaterial({ color: '#ff622e' }); + defaultMaterial.outputNode = halftones(output); + + const folder = gui.addFolder('🎨 default material'); + folder.addColor({ color: defaultMaterial.color }, 'color'); + + // objects + + const torusKnot = new THREE.Mesh(new THREE.TorusKnotGeometry(0.6, 0.25, 128, 32), defaultMaterial); + torusKnot.position.x = 3; + scene.add(torusKnot); + + const sphere = new THREE.Mesh(new THREE.SphereGeometry(1, 64, 64), defaultMaterial); + sphere.position.x = -3; + scene.add(sphere); + + const gltfLoader = new GLTFLoader(); + gltfLoader.load('./models/gltf/Michelle.glb', gltf => { + const model = gltf.scene; + model.position.y = -2; + model.scale.setScalar(2.5); + model.traverse(child => { + if (child.isMesh) child.material.outputNode = halftones(output); + }); + + scene.add(model); + }); + + // controls + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + controls.minDistance = 0.1; + controls.maxDistance = 50; + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +async function animate() { + timer.update(); + + controls.update(); + + const time = timer.getElapsed(); + halftoneSettings[1].uniforms.direction.value.x = Math.cos(time); + halftoneSettings[1].uniforms.direction.value.y = Math.sin(time); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_tsl_interoperability.ts b/examples-testing/examples/webgpu_tsl_interoperability.ts new file mode 100644 index 000000000..e57c66eeb --- /dev/null +++ b/examples-testing/examples/webgpu_tsl_interoperability.ts @@ -0,0 +1,263 @@ +import * as THREE from 'three/webgpu'; +import { + Fn, + attribute, + varyingProperty, + time, + uniform, + wgslFn, + texture, + sampler, + uv, + clamp, + float, + vec2, + vec3, + fract, + floor, + positionGeometry, + sin, +} from 'three/tsl'; + +import WebGPU from 'three/addons/capabilities/WebGPU.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let renderer, camera, scene; +const dpr = window.devicePixelRatio; + +const crtWidthUniform = uniform(1608); +const crtHeightUniform = uniform(1608); + +const canvas = document.getElementById('c'); + +function onWindowResize() { + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + renderer.render(scene, camera); +} + +function init() { + if (WebGPU.isAvailable() === false) { + document.body.appendChild(WebGPU.getErrorMessage()); + + throw new Error('No WebGPU support'); + } + + const vUv = varyingProperty('vec2', 'vUv'); + + // In WGSL, access varying properties from the varying struct + const wgslVertexShader = wgslFn( + ` + fn crtVertex( + position: vec3f, + uv: vec2f + ) -> vec3 { + varyings.vUv = uv; + return position; + } + `, + [vUv], + ); + + // Only wgsl vertex shaders take varyings arguments when defined. + // For a wgsl fragment shader, pass the varyingProperty node to the + // fragment shader's constructor to access the varying value computed + // by the vertex shader. + const wgslFragmentShader = wgslFn(` + fn crtFragment( + vUv: vec2f, + tex: texture_2d, + texSampler: sampler, + crtWidth: f32, + crtHeight: f32, + cellOffset: f32, + cellSize: f32, + borderMask: f32, + time: f32, + speed: f32, + pulseIntensity: f32, + pulseWidth: f32, + pulseRate: f32 + ) -> vec3 { + // Convert uv into map of pixels + var pixel = ( vUv * 0.5 + 0.5 ) * vec2( + crtWidth, + crtHeight + ); + // Coordinate for each cell in the pixel map + let coord = pixel / cellSize; + // Three color values for each cell (r, g, b) + let subcoord = coord * vec2f( 3.0, 1.0 ); + let offset = vec2( 0, fract( floor( coord.x ) * cellOffset ) ); + + let maskCoord = floor( coord + offset ) * cellSize; + + var samplePoint = maskCoord / vec2(crtWidth, crtHeight); + samplePoint.x += fract( time * speed / 20 ); + + var color = textureSample( + tex, + texSampler, + samplePoint + ).xyz; + + // Current implementation does not give an even amount of space to each r, g, b unit of a cell + // Fix/hack this by multiplying subCoord.x by cellSize at cellSizes below 6 + let ind = floor( subcoord.x ) % 3; + + var maskColor = vec3( + f32( ind == 0.0 ), + f32( ind == 1.0 ), + f32( ind == 2.0 ) + ) * 3.0; + + let cellUV = fract( subcoord + offset ) * 2.0 - 1.0; + var border: vec2 = 1.0 - cellUV * cellUV * borderMask; + + maskColor *= vec3f( clamp( border.x, 0.0, 1.0 ) * clamp( border.y, 0.0, 1.0) ); + + color *= maskColor; + + color.r *= 1.0 + pulseIntensity * sin( pixel.y / pulseWidth + time * pulseRate ); + color.b *= 1.0 + pulseIntensity * sin( pixel.y / pulseWidth + time * pulseRate ); + color.g *= 1.0 + pulseIntensity * sin( pixel.y / pulseWidth + time * pulseRate ); + + return color; + } + `); + + const textureLoader = new THREE.TextureLoader(); + const planetTexture = textureLoader.load('textures/planets/earth_lights_2048.png'); + planetTexture.wrapS = THREE.RepeatWrapping; + planetTexture.wrapT = THREE.RepeatWrapping; + + // Node Uniforms: + // Passed to WGSL Functions. + // Manipulated directly in TSL Functions. + const cellOffsetUniform = uniform(0.5); + const cellSizeUniform = uniform(6); + const borderMaskUniform = uniform(1); + const pulseIntensityUniform = uniform(0.06); + const pulseWidthUniform = uniform(60); + const pulseRateUniform = uniform(20); + const wgslShaderSpeedUniform = uniform(1.0); + const tslShaderSpeedUniform = uniform(1.0); + + // + + const wgslShaderMaterial = new THREE.MeshBasicNodeMaterial(); + + // Accessed attributes correspond to a Mesh or BufferGeometry's setAttribute() calls. + wgslShaderMaterial.positionNode = wgslVertexShader({ + position: attribute('position'), + uv: attribute('uv'), + }); + + wgslShaderMaterial.fragmentNode = wgslFragmentShader({ + vUv: vUv, + tex: texture(planetTexture), + texSampler: sampler(planetTexture), + crtWidth: crtWidthUniform, + crtHeight: crtHeightUniform, + cellOffset: cellOffsetUniform, + cellSize: cellSizeUniform, + borderMask: borderMaskUniform, + time: time, + speed: wgslShaderSpeedUniform, + pulseIntensity: pulseIntensityUniform, + pulseWidth: pulseWidthUniform, + pulseRate: pulseRateUniform, + }); + + // + + const tslVertexShader = Fn(() => { + vUv.assign(uv()); + return positionGeometry; + }); + + const tslFragmentShader = Fn(() => { + const dimensions = vec2(crtWidthUniform, crtHeightUniform); + const translatedUV = vUv.mul(0.5).add(0.5); + const pixel = translatedUV.mul(dimensions); + + const coord = pixel.div(cellSizeUniform); + const subCoord = coord.mul(vec2(3.0, 1.0)); + + const cellOffset = vec2(0.0, fract(floor(coord.x).mul(cellOffsetUniform))); + + const maskCoord = floor(coord.add(cellOffset)).mul(cellSizeUniform); + const samplePoint = maskCoord.div(dimensions); + const scaledTime = time.mul(tslShaderSpeedUniform); + samplePoint.x = samplePoint.x.add(fract(scaledTime.div(20))); + samplePoint.y = samplePoint.y.sub(1.5); + + let color = texture(planetTexture, samplePoint); + + const ind = floor(subCoord.x).mod(3); + + let maskColor = vec3(ind.equal(0.0), ind.equal(1.0), ind.equal(2.0)).mul(3.0); + + const subCoordOffset = fract(subCoord.add(cellOffset)); + let cellUV = subCoordOffset.mul(2.0); + cellUV = cellUV.sub(1.0); + + const border = float(1.0).sub(cellUV.mul(cellUV).mul(borderMaskUniform)); + + const clampX = clamp(border.x, 0.0, 1.0); + const clampY = clamp(border.y, 0.0, 1.0); + const borderClamp = clampX.mul(clampY); + maskColor = maskColor.mul(borderClamp); + + color = color.mul(maskColor); + + const pixelDampen = pixel.y.div(pulseWidthUniform); + let pulse = sin(pixelDampen.add(time.mul(pulseRateUniform))); + pulse = pulse.mul(pulseIntensityUniform); + color = color.mul(float(1.0).add(pulse)); + + return color; + }); + + const tslShaderMaterial = new THREE.MeshBasicNodeMaterial(); + tslShaderMaterial.positionNode = tslVertexShader(); + tslShaderMaterial.colorNode = tslFragmentShader(); + + camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1); + scene = new THREE.Scene(); + + const geometry = new THREE.PlaneGeometry(2, 1); + + const wgslQuad = new THREE.Mesh(geometry, wgslShaderMaterial); + wgslQuad.position.y += 0.5; + scene.add(wgslQuad); + + const tslQuad = new THREE.Mesh(geometry, tslShaderMaterial); + tslQuad.position.y -= 0.5; + scene.add(tslQuad); + + renderer = new THREE.WebGPURenderer({ antialias: true, canvas: canvas }); + renderer.setPixelRatio(dpr); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.outputColorSpace = THREE.LinearSRGBColorSpace; + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + window.addEventListener('resize', onWindowResize); + + const gui = renderer.inspector.createParameters('Parameters'); + + gui.add(cellSizeUniform, 'value', 6, 50, 1).name('Cell Size'); + gui.add(cellOffsetUniform, 'value', 0, 1, 0.1).name('Cell Offset'); + gui.add(borderMaskUniform, 'value', 0, 5, 0.1).name('Border Mask'); + gui.add(pulseIntensityUniform, 'value', 0, 0.5, 0.01).name('Pulse Intensity'); + gui.add(pulseWidthUniform, 'value', 10, 100, 5).name('Pulse Width'); + gui.add(wgslShaderSpeedUniform, 'value', 1, 10).name('WGSL Shader Speed'); + gui.add(tslShaderSpeedUniform, 'value', 1, 10).name('TSL Shader Speed'); +} + +init(); diff --git a/examples-testing/examples/webgpu_tsl_procedural_terrain.ts b/examples-testing/examples/webgpu_tsl_procedural_terrain.ts new file mode 100644 index 000000000..5630137b8 --- /dev/null +++ b/examples-testing/examples/webgpu_tsl_procedural_terrain.ts @@ -0,0 +1,318 @@ +import * as THREE from 'three/webgpu'; +import { + mx_noise_float, + color, + cross, + dot, + float, + transformNormalToView, + positionLocal, + sign, + step, + Fn, + uniform, + varying, + vec2, + vec3, + Loop, +} from 'three/tsl'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; + +let camera, scene, renderer, controls, drag; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(-10, 8, -2.2); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x201919); + + // environment + + const hdrLoader = new HDRLoader(); + hdrLoader.load('./textures/equirectangular/pedestrian_overpass_1k.hdr', environmentMap => { + environmentMap.mapping = THREE.EquirectangularReflectionMapping; + + scene.background = environmentMap; + scene.backgroundBlurriness = 0.5; + scene.environment = environmentMap; + }); + + // lights + + const directionalLight = new THREE.DirectionalLight('#ffffff', 2); + directionalLight.position.set(6.25, 3, 4); + directionalLight.castShadow = true; + directionalLight.shadow.mapSize.set(1024, 1024); + directionalLight.shadow.camera.near = 0.1; + directionalLight.shadow.camera.far = 30; + directionalLight.shadow.camera.top = 8; + directionalLight.shadow.camera.right = 8; + directionalLight.shadow.camera.bottom = -8; + directionalLight.shadow.camera.left = -8; + directionalLight.shadow.normalBias = 0.05; + scene.add(directionalLight); + + // terrain + + const material = new THREE.MeshStandardNodeMaterial({ + metalness: 0, + roughness: 0.5, + color: '#85d534', + }); + + const noiseIterations = uniform(3); + const positionFrequency = uniform(0.175); + const warpFrequency = uniform(6); + const warpStrength = uniform(1); + const strength = uniform(10); + const offset = uniform(vec2(0, 0)); + const normalLookUpShift = uniform(0.01); + const colorSand = uniform(color('#ffe894')); + const colorGrass = uniform(color('#85d534')); + const colorSnow = uniform(color('#ffffff')); + const colorRock = uniform(color('#bfbd8d')); + + const vNormal = varying(vec3()); + const vPosition = varying(vec3()); + + const terrainElevation = Fn(([position]) => { + const warpedPosition = position.add(offset).toVar(); + warpedPosition.addAssign( + mx_noise_float(warpedPosition.mul(positionFrequency).mul(warpFrequency), 1, 0).mul(warpStrength), + ); + + const elevation = float(0).toVar(); + Loop({ type: 'float', start: float(1), end: noiseIterations.toFloat(), condition: '<=' }, ({ i }) => { + const noiseInput = warpedPosition.mul(positionFrequency).mul(i.mul(2)).add(i.mul(987)); + const noise = mx_noise_float(noiseInput, 1, 0).div(i.add(1).mul(2)); + elevation.addAssign(noise); + }); + + const elevationSign = sign(elevation); + elevation.assign(elevation.abs().pow(2).mul(elevationSign).mul(strength)); + + return elevation; + }); + + material.positionNode = Fn(() => { + // neighbours positions + + const neighbourA = positionLocal.xyz.add(vec3(normalLookUpShift, 0.0, 0.0)).toVar(); + const neighbourB = positionLocal.xyz.add(vec3(0.0, 0.0, normalLookUpShift.negate())).toVar(); + + // elevations + + const position = positionLocal.xyz.toVar(); + const elevation = terrainElevation(positionLocal.xz); + position.y.addAssign(elevation); + + neighbourA.y.addAssign(terrainElevation(neighbourA.xz)); + neighbourB.y.addAssign(terrainElevation(neighbourB.xz)); + + // compute normal + + const toA = neighbourA.sub(position).normalize(); + const toB = neighbourB.sub(position).normalize(); + vNormal.assign(cross(toA, toB)); + + // varyings + + vPosition.assign(position.add(vec3(offset.x, 0, offset.y))); + + return position; + })(); + + material.normalNode = transformNormalToView(vNormal); + + material.colorNode = Fn(() => { + const finalColor = colorSand.toVar(); + + // grass + + const grassMix = step(-0.06, vPosition.y); + finalColor.assign(grassMix.mix(finalColor, colorGrass)); + + // rock + + const rockMix = step(0.5, dot(vNormal, vec3(0, 1, 0))) + .oneMinus() + .mul(step(-0.06, vPosition.y)); + finalColor.assign(rockMix.mix(finalColor, colorRock)); + + // snow + + const snowThreshold = mx_noise_float(vPosition.xz.mul(25), 1, 0).mul(0.1).add(0.45); + const snowMix = step(snowThreshold, vPosition.y); + finalColor.assign(snowMix.mix(finalColor, colorSnow)); + + return finalColor; + })(); + + const geometry = new THREE.PlaneGeometry(10, 10, 500, 500); + geometry.deleteAttribute('uv'); + geometry.deleteAttribute('normal'); + geometry.rotateX(-Math.PI * 0.5); + + const terrain = new THREE.Mesh(geometry, material); + terrain.receiveShadow = true; + terrain.castShadow = true; + scene.add(terrain); + + // water + + const water = new THREE.Mesh( + new THREE.PlaneGeometry(10, 10, 1, 1), + new THREE.MeshPhysicalMaterial({ + transmission: 1, + roughness: 0.5, + ior: 1.333, + color: '#4db2ff', + }), + ); + water.rotation.x = -Math.PI * 0.5; + water.position.y = -0.1; + scene.add(water); + + // drag + + drag = {}; + drag.screenCoords = new THREE.Vector2(); + drag.prevWorldCoords = new THREE.Vector3(); + drag.worldCoords = new THREE.Vector3(); + drag.raycaster = new THREE.Raycaster(); + drag.down = false; + drag.hover = false; + + drag.object = new THREE.Mesh(new THREE.PlaneGeometry(10, 10, 1, 1), new THREE.MeshBasicMaterial()); + drag.object.rotation.x = -Math.PI * 0.5; + drag.object.visible = false; + scene.add(drag.object); + + drag.getIntersect = () => { + drag.raycaster.setFromCamera(drag.screenCoords, camera); + const intersects = drag.raycaster.intersectObject(drag.object); + if (intersects.length) return intersects[0]; + + return null; + }; + + drag.update = () => { + const intersect = drag.getIntersect(); + + if (intersect) { + drag.hover = true; + + if (!drag.down) renderer.domElement.style.cursor = 'grab'; + } else { + drag.hover = false; + renderer.domElement.style.cursor = 'default'; + } + + if (drag.hover && drag.down) { + drag.worldCoords.copy(intersect.point); + const delta = drag.prevWorldCoords.sub(drag.worldCoords); + + offset.value.x += delta.x; + offset.value.y += delta.z; + } + + drag.prevWorldCoords.copy(drag.worldCoords); + }; + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.shadowMap.enabled = true; + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // inspector + + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.inspector.domElement); + + // controls + + controls = new OrbitControls(camera, renderer.domElement); + controls.maxPolarAngle = Math.PI * 0.45; + controls.target.y = -0.5; + controls.enableDamping = true; + controls.minDistance = 0.1; + controls.maxDistance = 50; + + // debug + + const gui = renderer.inspector.createParameters('Parameters'); + + const terrainGui = gui.addFolder('🏔️ terrain'); + + terrainGui.add(noiseIterations, 'value', 0, 10, 1).name('noiseIterations'); + terrainGui.add(positionFrequency, 'value', 0, 1, 0.001).name('positionFrequency'); + terrainGui.add(strength, 'value', 0, 20, 0.001).name('strength'); + terrainGui.add(warpFrequency, 'value', 0, 20, 0.001).name('warpFrequency'); + terrainGui.add(warpStrength, 'value', 0, 2, 0.001).name('warpStrength'); + + terrainGui.addColor({ color: colorSand.value }, 'color').name('colorSand'); + terrainGui.addColor({ color: colorGrass.value }, 'color').name('colorGrass'); + terrainGui.addColor({ color: colorSnow.value }, 'color').name('colorSnow'); + terrainGui.addColor({ color: colorRock.value }, 'color').name('colorRock'); + + const waterGui = gui.addFolder('💧 water'); + + waterGui.add(water.material, 'roughness', 0, 1, 0.01); + waterGui.add(water.material, 'ior', 1, 2, 0.001); + waterGui.addColor({ color: water.material.color }, 'color').name('color'); + + // events + + window.addEventListener('pointermove', event => { + drag.screenCoords.x = (event.clientX / window.innerWidth - 0.5) * 2; + drag.screenCoords.y = -(event.clientY / window.innerHeight - 0.5) * 2; + }); + + renderer.domElement.addEventListener('pointerdown', () => { + if (drag.hover) { + renderer.domElement.style.cursor = 'grabbing'; + controls.enabled = false; + drag.down = true; + drag.object.scale.setScalar(10); + + const intersect = drag.getIntersect(); + drag.prevWorldCoords.copy(intersect.point); + drag.worldCoords.copy(intersect.point); + } + }); + + window.addEventListener('pointerup', () => { + drag.down = false; + controls.enabled = true; + drag.object.scale.setScalar(1); + }); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +async function animate() { + controls.update(); + + drag.update(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_tsl_raging_sea.ts b/examples-testing/examples/webgpu_tsl_raging_sea.ts new file mode 100644 index 000000000..cb513d075 --- /dev/null +++ b/examples-testing/examples/webgpu_tsl_raging_sea.ts @@ -0,0 +1,178 @@ +import * as THREE from 'three/webgpu'; +import { + float, + mx_noise_float, + Loop, + color, + positionLocal, + sin, + vec2, + vec3, + mul, + time, + uniform, + Fn, + transformNormalToView, +} from 'three/tsl'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer, controls; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 10); + camera.position.set(1.25, 1.25, 1.25); + + scene = new THREE.Scene(); + + // lights + + const directionalLight = new THREE.DirectionalLight('#ffffff', 3); + directionalLight.position.set(-4, 2, 0); + scene.add(directionalLight); + + // material + + const material = new THREE.MeshStandardNodeMaterial({ color: '#271442', roughness: 0.15 }); + + const emissiveColor = uniform(color('#ff0a81')); + const emissiveLow = uniform(-0.25); + const emissiveHigh = uniform(0.2); + const emissivePower = uniform(7); + const largeWavesFrequency = uniform(vec2(3, 1)); + const largeWavesSpeed = uniform(1.25); + const largeWavesMultiplier = uniform(0.15); + const smallWavesIterations = uniform(3); + const smallWavesFrequency = uniform(2); + const smallWavesSpeed = uniform(0.3); + const smallWavesMultiplier = uniform(0.18); + const normalComputeShift = uniform(0.01); + + // TSL functions + + const wavesElevation = Fn(([position]) => { + // large waves + + const elevation = mul( + sin(position.x.mul(largeWavesFrequency.x).add(time.mul(largeWavesSpeed))), + sin(position.z.mul(largeWavesFrequency.y).add(time.mul(largeWavesSpeed))), + largeWavesMultiplier, + ).toVar(); + + Loop({ start: float(1), end: smallWavesIterations.add(1) }, ({ i }) => { + const noiseInput = vec3( + position.xz + .add(2) // avoids a-hole pattern + .mul(smallWavesFrequency) + .mul(i), + time.mul(smallWavesSpeed), + ); + + const wave = mx_noise_float(noiseInput, 1, 0).mul(smallWavesMultiplier).div(i).abs(); + + elevation.subAssign(wave); + }); + + return elevation; + }); + + // position + + const elevation = wavesElevation(positionLocal); + const position = positionLocal.add(vec3(0, elevation, 0)); + + material.positionNode = position; + + // normals + + let positionA = positionLocal.add(vec3(normalComputeShift, 0, 0)); + let positionB = positionLocal.add(vec3(0, 0, normalComputeShift.negate())); + + positionA = positionA.add(vec3(0, wavesElevation(positionA), 0)); + positionB = positionB.add(vec3(0, wavesElevation(positionB), 0)); + + const toA = positionA.sub(position).normalize(); + const toB = positionB.sub(position).normalize(); + const normal = toA.cross(toB); + + material.normalNode = transformNormalToView(normal); + + // emissive + + const emissive = elevation.remap(emissiveHigh, emissiveLow).pow(emissivePower); + material.emissiveNode = emissiveColor.mul(emissive); + + // mesh + + const geometry = new THREE.PlaneGeometry(2, 2, 256, 256); + geometry.rotateX(-Math.PI * 0.5); + const mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // controls + + controls = new OrbitControls(camera, renderer.domElement); + controls.target.y = -0.25; + controls.enableDamping = true; + controls.minDistance = 0.1; + controls.maxDistance = 50; + + // debug + + const gui = renderer.inspector.createParameters('Parameters'); + + gui.addColor({ color: material.color.getHex(THREE.SRGBColorSpace) }, 'color') + .name('color') + .onChange(value => material.color.set(value)); + gui.add(material, 'roughness', 0, 1, 0.001); + + const colorGui = gui.addFolder('emissive'); + colorGui + .addColor({ color: emissiveColor.value.getHex(THREE.SRGBColorSpace) }, 'color') + .name('color') + .onChange(value => emissiveColor.value.set(value)); + colorGui.add(emissiveLow, 'value', -1, 0, 0.001).name('low'); + colorGui.add(emissiveHigh, 'value', 0, 1, 0.001).name('high'); + colorGui.add(emissivePower, 'value', 1, 10, 1).name('power'); + + const wavesGui = gui.addFolder('waves'); + wavesGui.add(largeWavesSpeed, 'value', 0, 5).name('largeSpeed'); + wavesGui.add(largeWavesMultiplier, 'value', 0, 1).name('largeMultiplier'); + wavesGui.add(largeWavesFrequency.value, 'x', 0, 10).name('largeFrequencyX'); + wavesGui.add(largeWavesFrequency.value, 'y', 0, 10).name('largeFrequencyY'); + wavesGui.add(smallWavesIterations, 'value', 0, 5, 1).name('smallIterations'); + wavesGui.add(smallWavesFrequency, 'value', 0, 10).name('smallFrequency'); + wavesGui.add(smallWavesSpeed, 'value', 0, 1).name('smallSpeed'); + wavesGui.add(smallWavesMultiplier, 'value', 0, 1).name('smallMultiplier'); + wavesGui.add(normalComputeShift, 'value', 0, 0.1, 0.0001).name('normalComputeShift'); + + // events + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +async function animate() { + controls.update(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_tsl_vfx_flames.ts b/examples-testing/examples/webgpu_tsl_vfx_flames.ts new file mode 100644 index 000000000..f1e423444 --- /dev/null +++ b/examples-testing/examples/webgpu_tsl_vfx_flames.ts @@ -0,0 +1,203 @@ +import * as THREE from 'three/webgpu'; +import { + TWO_PI, + oneMinus, + spherizeUV, + sin, + step, + texture, + time, + Fn, + uv, + vec2, + vec3, + vec4, + mix, + billboarding, +} from 'three/tsl'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer, controls; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(25, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(1, 1, 3); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x201919); + + // textures + + const textureLoader = new THREE.TextureLoader(); + + const cellularTexture = textureLoader.load('./textures/noises/voronoi/grayscale-256x256.png'); + const perlinTexture = textureLoader.load('./textures/noises/perlin/rgb-256x256.png'); + + // gradient canvas + + const gradient = {}; + gradient.element = document.createElement('canvas'); + gradient.element.width = 128; + gradient.element.height = 1; + gradient.context = gradient.element.getContext('2d'); + + gradient.colors = ['#090033', '#5f1f93', '#e02e96', '#ffbd80', '#fff0db']; + + gradient.texture = new THREE.CanvasTexture(gradient.element); + gradient.texture.colorSpace = THREE.SRGBColorSpace; + + gradient.update = () => { + const fillGradient = gradient.context.createLinearGradient(0, 0, gradient.element.width, 0); + + for (let i = 0; i < gradient.colors.length; i++) { + const progress = i / (gradient.colors.length - 1); + const color = gradient.colors[i]; + fillGradient.addColorStop(progress, color); + } + + gradient.context.fillStyle = fillGradient; + gradient.context.fillRect(0, 0, gradient.element.width, gradient.element.height); + + gradient.texture.needsUpdate = true; + }; + + gradient.update(); + + // flame 1 material + + const flame1Material = new THREE.SpriteNodeMaterial({ side: THREE.DoubleSide }); + + flame1Material.colorNode = Fn(() => { + // main UV + const mainUv = uv().toVar(); + mainUv.assign(spherizeUV(mainUv, 10).mul(0.6).add(0.2)); // spherize + mainUv.assign(mainUv.pow(vec2(1, 2))); // stretch + mainUv.assign(mainUv.mul(2, 1).sub(vec2(0.5, 0))); // scale + + // gradients + const gradient1 = sin(time.mul(10).sub(mainUv.y.mul(TWO_PI).mul(2))).toVar(); + const gradient2 = mainUv.y.smoothstep(0, 1).toVar(); + mainUv.x.addAssign(gradient1.mul(gradient2).mul(0.2)); + + // cellular noise + const cellularUv = mainUv + .mul(0.5) + .add(vec2(0, time.negate().mul(0.5))) + .mod(1); + const cellularNoise = texture(cellularTexture, cellularUv, 0).r.oneMinus().smoothstep(0, 0.5).oneMinus(); + cellularNoise.mulAssign(gradient2); + + // shape + const shape = mainUv.sub(0.5).mul(vec2(3, 2)).length().oneMinus().toVar(); + shape.assign(shape.sub(cellularNoise)); + + // gradient color + const gradientColor = texture(gradient.texture, vec2(shape.remap(0, 1, 0, 1), 0)); + + // output + const color = mix(gradientColor, vec3(1), shape.step(0.8)); + const alpha = shape.smoothstep(0, 0.3); + return vec4(color.rgb, alpha); + })(); + + // flame 2 material + + const flame2Material = new THREE.SpriteNodeMaterial({ side: THREE.DoubleSide }); + + flame2Material.colorNode = Fn(() => { + // main UV + const mainUv = uv().toVar(); + mainUv.assign(spherizeUV(mainUv, 10).mul(0.6).add(0.2)); // spherize + mainUv.assign(mainUv.abs().pow(vec2(1, 3)).mul(mainUv.sign())); // stretch + mainUv.assign(mainUv.mul(2, 1).sub(vec2(0.5, 0))); // scale + + // perlin noise + const perlinUv = mainUv.add(vec2(0, time.negate().mul(1))).mod(1); + const perlinNoise = texture(perlinTexture, perlinUv, 0).sub(0.5).mul(1); + mainUv.x.addAssign(perlinNoise.x.mul(0.5)); + + // gradients + const gradient1 = sin(time.mul(10).sub(mainUv.y.mul(TWO_PI).mul(2))); + const gradient2 = mainUv.y.smoothstep(0, 1); + const gradient3 = oneMinus(mainUv.y).smoothstep(0, 0.3); + mainUv.x.addAssign(gradient1.mul(gradient2).mul(0.2)); + + // displaced perlin noise + const displacementPerlinUv = mainUv + .mul(0.5) + .add(vec2(0, time.negate().mul(0.25))) + .mod(1); + const displacementPerlinNoise = texture(perlinTexture, displacementPerlinUv, 0).sub(0.5).mul(1); + const displacedPerlinUv = mainUv + .add(vec2(0, time.negate().mul(0.5))) + .add(displacementPerlinNoise) + .mod(1); + const displacedPerlinNoise = texture(perlinTexture, displacedPerlinUv, 0).sub(0.5).mul(1); + mainUv.x.addAssign(displacedPerlinNoise.mul(0.5)); + + // cellular noise + const cellularUv = mainUv.add(vec2(0, time.negate().mul(1.5))).mod(1); + const cellularNoise = texture(cellularTexture, cellularUv, 0).r.oneMinus().smoothstep(0.25, 1); + + // shape + const shape = step(mainUv.sub(0.5).mul(vec2(6, 1)).length(), 0.5); + shape.assign(shape.mul(cellularNoise)); + shape.mulAssign(gradient3); + shape.assign(step(0.01, shape)); + + // output + return vec4(vec3(1), shape); + })(); + + // billboarding - follow the camera rotation only horizontally + + flame1Material.vertexNode = billboarding(); + flame2Material.vertexNode = billboarding(); + + // meshes + + const flame1 = new THREE.Sprite(flame1Material); + flame1.center.set(0.5, 0); + flame1.scale.x = 0.5; // optional + flame1.position.x = -0.5; + scene.add(flame1); + + const flame2 = new THREE.Sprite(flame2Material); + flame2.center.set(0.5, 0); + flame2.position.x = 0.5; + scene.add(flame2); + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + controls.minDistance = 0.1; + controls.maxDistance = 50; + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +async function animate() { + controls.update(); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_tsl_vfx_linkedparticles.ts b/examples-testing/examples/webgpu_tsl_vfx_linkedparticles.ts new file mode 100644 index 000000000..e220c164b --- /dev/null +++ b/examples-testing/examples/webgpu_tsl_vfx_linkedparticles.ts @@ -0,0 +1,445 @@ +import * as THREE from 'three/webgpu'; +import { + atan, + cos, + float, + max, + min, + mix, + PI, + TWO_PI, + sin, + vec2, + vec3, + color, + Fn, + hash, + hue, + If, + instanceIndex, + Loop, + mx_fractal_noise_float, + mx_fractal_noise_vec3, + pass, + pcurve, + storage, + deltaTime, + time, + uv, + uniform, + step, +} from 'three/tsl'; +import { bloom } from 'three/addons/tsl/display/BloomNode.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import WebGPU from 'three/addons/capabilities/WebGPU.js'; + +let camera, scene, renderer, renderPipeline, controls, timer, light; + +let updateParticles, spawnParticles; // TSL compute nodes +let getInstanceColor; // TSL function + +const screenPointer = new THREE.Vector2(); +const scenePointer = new THREE.Vector3(); +const raycastPlane = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0); +const raycaster = new THREE.Raycaster(); + +const nbParticles = Math.pow(2, 13); + +const timeScale = uniform(1.0); +const particleLifetime = uniform(0.5); +const particleSize = uniform(1.0); +const linksWidth = uniform(0.005); + +const colorOffset = uniform(0.0); +const colorVariance = uniform(2.0); +const colorRotationSpeed = uniform(1.0); + +const spawnIndex = uniform(0); +const nbToSpawn = uniform(5); +const spawnPosition = uniform(vec3(0.0)); +const previousSpawnPosition = uniform(vec3(0.0)); + +const turbFrequency = uniform(0.5); +const turbAmplitude = uniform(0.5); +const turbOctaves = uniform(2); +const turbLacunarity = uniform(2.0); +const turbGain = uniform(0.5); +const turbFriction = uniform(0.01); + +init(); + +async function init() { + if (WebGPU.isAvailable() === false) { + document.body.appendChild(WebGPU.getErrorMessage()); + + throw new Error('No WebGPU support'); + } + + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 200); + camera.position.set(0, 0, 10); + + scene = new THREE.Scene(); + + timer = new THREE.Timer(); + timer.connect(document); + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setClearColor(0x14171a); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + document.body.appendChild(renderer.domElement); + + await renderer.init(); + + // TSL function + // current color from index + getInstanceColor = Fn(([i]) => { + return hue( + color(0x0000ff), + colorOffset.add(mx_fractal_noise_float(i.toFloat().mul(0.1), 2, 2.0, 0.5, colorVariance)), + ); + }); + + // Particles + // storage buffers + const particlePositions = storage(new THREE.StorageInstancedBufferAttribute(nbParticles, 4), 'vec4', nbParticles); + const particleVelocities = storage(new THREE.StorageInstancedBufferAttribute(nbParticles, 4), 'vec4', nbParticles); + + // init particles buffers + renderer.compute( + Fn(() => { + particlePositions.element(instanceIndex).xyz.assign(vec3(10000.0)); + particlePositions.element(instanceIndex).w.assign(vec3(-1.0)); // life is stored in w component; x<0 means dead + })().compute(nbParticles), + ); + + // particles output + const particleQuadSize = 0.05; + const particleGeom = new THREE.PlaneGeometry(particleQuadSize, particleQuadSize); + + const particleMaterial = new THREE.SpriteNodeMaterial(); + particleMaterial.blending = THREE.AdditiveBlending; + particleMaterial.depthWrite = false; + particleMaterial.positionNode = particlePositions.toAttribute(); + particleMaterial.scaleNode = vec2(particleSize); + particleMaterial.rotationNode = atan(particleVelocities.toAttribute().y, particleVelocities.toAttribute().x); + + particleMaterial.colorNode = Fn(() => { + const life = particlePositions.toAttribute().w; + const modLife = pcurve(life.oneMinus(), 8.0, 1.0); + const pulse = pcurve( + sin(hash(instanceIndex).mul(TWO_PI).add(time.mul(0.5).mul(TWO_PI))) + .mul(0.5) + .add(0.5), + 0.25, + 0.25, + ) + .mul(10.0) + .add(1.0); + + return getInstanceColor(instanceIndex).mul(pulse.mul(modLife)); + })(); + + particleMaterial.opacityNode = Fn(() => { + const circle = step(uv().xy.sub(0.5).length(), 0.5); + const life = particlePositions.toAttribute().w; + + return circle.mul(life); + })(); + + const particleMesh = new THREE.InstancedMesh(particleGeom, particleMaterial, nbParticles); + particleMesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage); + particleMesh.frustumCulled = false; + + scene.add(particleMesh); + + // Links between particles + // first, we define the indices for the links, 2 quads per particle, the indexation is fixed + const linksIndices = []; + for (let i = 0; i < nbParticles; i++) { + const baseIndex = i * 8; + for (let j = 0; j < 2; j++) { + const offset = baseIndex + j * 4; + linksIndices.push(offset, offset + 1, offset + 2, offset, offset + 2, offset + 3); + } + } + + // storage buffers attributes for the links + const nbVertices = nbParticles * 8; + const linksVerticesSBA = new THREE.StorageBufferAttribute(nbVertices, 4); + const linksColorsSBA = new THREE.StorageBufferAttribute(nbVertices, 4); + + // links output + const linksGeom = new THREE.BufferGeometry(); + linksGeom.setAttribute('position', linksVerticesSBA); + linksGeom.setAttribute('color', linksColorsSBA); + linksGeom.setIndex(linksIndices); + + const linksMaterial = new THREE.MeshBasicNodeMaterial(); + linksMaterial.vertexColors = true; + linksMaterial.side = THREE.DoubleSide; + linksMaterial.transparent = true; + linksMaterial.depthWrite = false; + linksMaterial.depthTest = false; + linksMaterial.blending = THREE.AdditiveBlending; + linksMaterial.opacityNode = storage(linksColorsSBA, 'vec4', linksColorsSBA.count).toAttribute().w; + + const linksMesh = new THREE.Mesh(linksGeom, linksMaterial); + linksMesh.frustumCulled = false; + scene.add(linksMesh); + + // compute nodes + updateParticles = Fn(() => { + const position = particlePositions.element(instanceIndex).xyz; + const life = particlePositions.element(instanceIndex).w; + const velocity = particleVelocities.element(instanceIndex).xyz; + const dt = deltaTime.mul(0.1).mul(timeScale); + + If(life.greaterThan(0.0), () => { + // first we update the particles positions and velocities + // velocity comes from a turbulence field, and is multiplied by the particle lifetime so that it slows down over time + const localVel = mx_fractal_noise_vec3( + position.mul(turbFrequency), + turbOctaves, + turbLacunarity, + turbGain, + turbAmplitude, + ).mul(life.add(0.01)); + velocity.addAssign(localVel); + velocity.mulAssign(turbFriction.oneMinus()); + position.addAssign(velocity.mul(dt)); + + // then we decrease the lifetime + life.subAssign(dt.mul(particleLifetime.reciprocal())); + + // then we find the two closest particles and set a quad to each of them + const closestDist1 = float(10000.0).toVar(); + const closestPos1 = vec3(0.0).toVar(); + const closestLife1 = float(0.0).toVar(); + const closestDist2 = float(10000.0).toVar(); + const closestPos2 = vec3(0.0).toVar(); + const closestLife2 = float(0.0).toVar(); + + Loop(nbParticles, ({ i }) => { + const otherPart = particlePositions.element(i); + + If(i.notEqual(instanceIndex).and(otherPart.w.greaterThan(0.0)), () => { + // if not self and other particle is alive + + const otherPosition = otherPart.xyz; + const dist = position.sub(otherPosition).lengthSq(); + const moreThanZero = dist.greaterThan(0.0); + + If(dist.lessThan(closestDist1).and(moreThanZero), () => { + closestDist1.assign(dist); + closestPos1.assign(otherPosition.xyz); + closestLife1.assign(otherPart.w); + }).ElseIf(dist.lessThan(closestDist2).and(moreThanZero), () => { + closestDist2.assign(dist); + closestPos2.assign(otherPosition.xyz); + closestLife2.assign(otherPart.w); + }); + }); + }); + + // then we update the links correspondingly + const linksPositions = storage(linksVerticesSBA, 'vec4', linksVerticesSBA.count); + const linksColors = storage(linksColorsSBA, 'vec4', linksColorsSBA.count); + const firstLinkIndex = instanceIndex.mul(8); + const secondLinkIndex = firstLinkIndex.add(4); + + // positions link 1 + linksPositions.element(firstLinkIndex).xyz.assign(position); + linksPositions.element(firstLinkIndex).y.addAssign(linksWidth); + linksPositions.element(firstLinkIndex.add(1)).xyz.assign(position); + linksPositions.element(firstLinkIndex.add(1)).y.addAssign(linksWidth.negate()); + linksPositions.element(firstLinkIndex.add(2)).xyz.assign(closestPos1); + linksPositions.element(firstLinkIndex.add(2)).y.addAssign(linksWidth.negate()); + linksPositions.element(firstLinkIndex.add(3)).xyz.assign(closestPos1); + linksPositions.element(firstLinkIndex.add(3)).y.addAssign(linksWidth); + + // positions link 2 + linksPositions.element(secondLinkIndex).xyz.assign(position); + linksPositions.element(secondLinkIndex).y.addAssign(linksWidth); + linksPositions.element(secondLinkIndex.add(1)).xyz.assign(position); + linksPositions.element(secondLinkIndex.add(1)).y.addAssign(linksWidth.negate()); + linksPositions.element(secondLinkIndex.add(2)).xyz.assign(closestPos2); + linksPositions.element(secondLinkIndex.add(2)).y.addAssign(linksWidth.negate()); + linksPositions.element(secondLinkIndex.add(3)).xyz.assign(closestPos2); + linksPositions.element(secondLinkIndex.add(3)).y.addAssign(linksWidth); + + // colors are the same for all vertices of both quads + const linkColor = getInstanceColor(instanceIndex); + + // store the minimum lifetime of the closest particles in the w component of colors + const l1 = max(0.0, min(closestLife1, life)).pow(0.8); // pow is here to apply a slight curve to the opacity + const l2 = max(0.0, min(closestLife2, life)).pow(0.8); + + Loop(4, ({ i }) => { + linksColors.element(firstLinkIndex.add(i)).xyz.assign(linkColor); + linksColors.element(firstLinkIndex.add(i)).w.assign(l1); + linksColors.element(secondLinkIndex.add(i)).xyz.assign(linkColor); + linksColors.element(secondLinkIndex.add(i)).w.assign(l2); + }); + }); + })() + .compute(nbParticles) + .setName('Update Particles'); + + spawnParticles = Fn(() => { + const particleIndex = spawnIndex.add(instanceIndex).mod(nbParticles).toInt(); + const position = particlePositions.element(particleIndex).xyz; + const life = particlePositions.element(particleIndex).w; + const velocity = particleVelocities.element(particleIndex).xyz; + + life.assign(1.0); // sets it alive + + // random spherical direction + const rRange = float(0.01); + const rTheta = hash(particleIndex).mul(TWO_PI); + const rPhi = hash(particleIndex.add(1)).mul(PI); + const rx = sin(rTheta).mul(cos(rPhi)); + const ry = sin(rTheta).mul(sin(rPhi)); + const rz = cos(rTheta); + const rDir = vec3(rx, ry, rz); + + // position is interpolated between the previous cursor position and the current one over the number of particles spawned + const pos = mix( + previousSpawnPosition, + spawnPosition, + instanceIndex.toFloat().div(nbToSpawn.sub(1).toFloat()).clamp(), + ); + position.assign(pos.add(rDir.mul(rRange))); + + // start in that direction + velocity.assign(rDir.mul(5.0)); + })() + .compute(nbToSpawn.value) + .setName('Spawn Particles'); + + // background , an inverted icosahedron + const backgroundGeom = new THREE.IcosahedronGeometry(100, 5).applyMatrix4(new THREE.Matrix4().makeScale(-1, 1, 1)); + const backgroundMaterial = new THREE.MeshStandardNodeMaterial(); + backgroundMaterial.roughness = 0.4; + backgroundMaterial.metalness = 0.9; + backgroundMaterial.flatShading = true; + backgroundMaterial.colorNode = color(0x0); + + const backgroundMesh = new THREE.Mesh(backgroundGeom, backgroundMaterial); + scene.add(backgroundMesh); + + // light for the background + light = new THREE.PointLight(0xffffff, 3000); + scene.add(light); + + // post processing + + renderPipeline = new THREE.RenderPipeline(renderer); + + const scenePass = pass(scene, camera); + const scenePassColor = scenePass.getTextureNode('output'); + + const bloomPass = bloom(scenePassColor, 0.75, 0.1, 0.5); + + renderPipeline.outputNode = scenePassColor.add(bloomPass); + + // controls + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + controls.autoRotate = true; + controls.maxDistance = 75; + window.addEventListener('resize', onWindowResize); + + // pointer handling + + window.addEventListener('pointermove', onPointerMove); + + // GUI + + const gui = renderer.inspector.createParameters('Parameters'); + + gui.add(controls, 'autoRotate').name('Auto Rotate'); + gui.add(controls, 'autoRotateSpeed', -10.0, 10.0, 0.01).name('Auto Rotate Speed'); + + const partFolder = gui.addFolder('Particles'); + partFolder.add(timeScale, 'value', 0.0, 4.0, 0.01).name('timeScale'); + partFolder.add(nbToSpawn, 'value', 1, 100, 1).name('Spawn rate'); + partFolder.add(particleSize, 'value', 0.01, 3.0, 0.01).name('Size'); + partFolder.add(particleLifetime, 'value', 0.01, 2.0, 0.01).name('Lifetime'); + partFolder.add(linksWidth, 'value', 0.001, 0.1, 0.001).name('Links width'); + partFolder.add(colorVariance, 'value', 0.0, 10.0, 0.01).name('Color variance'); + partFolder.add(colorRotationSpeed, 'value', 0.0, 5.0, 0.01).name('Color rotation speed'); + + const turbFolder = gui.addFolder('Turbulence'); + turbFolder.add(turbFriction, 'value', 0.0, 0.3, 0.01).name('Friction'); + turbFolder.add(turbFrequency, 'value', 0.0, 1.0, 0.01).name('Frequency'); + turbFolder.add(turbAmplitude, 'value', 0.0, 10.0, 0.01).name('Amplitude'); + turbFolder.add(turbOctaves, 'value', 1, 9, 1).name('Octaves'); + turbFolder.add(turbLacunarity, 'value', 1.0, 5.0, 0.01).name('Lacunarity'); + turbFolder.add(turbGain, 'value', 0.0, 1.0, 0.01).name('Gain'); + + const bloomFolder = gui.addFolder('bloom'); + bloomFolder.add(bloomPass.threshold, 'value', 0, 2.0, 0.01).name('Threshold'); + bloomFolder.add(bloomPass.strength, 'value', 0, 10, 0.01).name('Strength'); + bloomFolder.add(bloomPass.radius, 'value', 0, 1, 0.01).name('Radius'); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function onPointerMove(e) { + screenPointer.x = (e.clientX / window.innerWidth) * 2 - 1; + screenPointer.y = -(e.clientY / window.innerHeight) * 2 + 1; +} + +function updatePointer() { + raycaster.setFromCamera(screenPointer, camera); + raycaster.ray.intersectPlane(raycastPlane, scenePointer); +} + +function animate() { + timer.update(); + + // compute particles + renderer.compute(updateParticles); + renderer.compute(spawnParticles); + + // update particle index for next spawn + spawnIndex.value = (spawnIndex.value + nbToSpawn.value) % nbParticles; + + // update raycast plane to face camera + raycastPlane.normal.applyEuler(camera.rotation); + updatePointer(); + + // lerping spawn position + previousSpawnPosition.value.copy(spawnPosition.value); + spawnPosition.value.lerp(scenePointer, 0.1); + + // rotating colors + colorOffset.value += timer.getDelta() * colorRotationSpeed.value * timeScale.value; + + const elapsedTime = timer.getElapsed(); + light.position.set( + Math.sin(elapsedTime * 0.5) * 30, + Math.cos(elapsedTime * 0.3) * 30, + Math.sin(elapsedTime * 0.2) * 30, + ); + + controls.update(); + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_tsl_vfx_tornado.ts b/examples-testing/examples/webgpu_tsl_vfx_tornado.ts new file mode 100644 index 000000000..625a9d209 --- /dev/null +++ b/examples-testing/examples/webgpu_tsl_vfx_tornado.ts @@ -0,0 +1,289 @@ +import * as THREE from 'three/webgpu'; +import { + luminance, + cos, + min, + time, + atan, + uniform, + pass, + PI, + TWO_PI, + color, + positionLocal, + sin, + texture, + Fn, + uv, + vec2, + vec3, + vec4, +} from 'three/tsl'; +import { bloom } from 'three/addons/tsl/display/BloomNode.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let camera, scene, renderer, renderPipeline, controls; + +init(); + +function init() { + camera = new THREE.PerspectiveCamera(25, window.innerWidth / window.innerHeight, 0.1, 50); + camera.position.set(1, 1, 3); + + scene = new THREE.Scene(); + + // textures + + const textureLoader = new THREE.TextureLoader(); + const perlinTexture = textureLoader.load('./textures/noises/perlin/rgb-256x256.png'); + perlinTexture.wrapS = THREE.RepeatWrapping; + perlinTexture.wrapT = THREE.RepeatWrapping; + + // TSL functions + + const toRadialUv = Fn(([uv, multiplier, rotation, offset]) => { + const centeredUv = uv.sub(0.5).toVar(); + const distanceToCenter = centeredUv.length(); + const angle = atan(centeredUv.y, centeredUv.x); + const radialUv = vec2(angle.add(PI).div(TWO_PI), distanceToCenter).toVar(); + radialUv.mulAssign(multiplier); + radialUv.x.addAssign(rotation); + radialUv.y.addAssign(offset); + + return radialUv; + }); + + const toSkewedUv = Fn(([uv, skew]) => { + return vec2(uv.x.add(uv.y.mul(skew.x)), uv.y.add(uv.x.mul(skew.y))); + }); + + const twistedCylinder = Fn(([position, parabolStrength, parabolOffset, parabolAmplitude, time]) => { + const angle = atan(position.z, position.x).toVar(); + const elevation = position.y; + + // parabol + const radius = parabolStrength.mul(position.y.sub(parabolOffset)).pow(2).add(parabolAmplitude).toVar(); + + // turbulences + radius.addAssign(sin(elevation.sub(time).mul(20).add(angle.mul(2))).mul(0.05)); + + const twistedPosition = vec3(cos(angle).mul(radius), elevation, sin(angle).mul(radius)); + + return twistedPosition; + }); + + // uniforms + + const emissiveColor = uniform(color('#ff8b4d')); + const timeScale = uniform(0.2); + const parabolStrength = uniform(1); + const parabolOffset = uniform(0.3); + const parabolAmplitude = uniform(0.2); + + // tornado floor + + const floorMaterial = new THREE.MeshBasicNodeMaterial({ transparent: true, wireframe: false }); + + floorMaterial.outputNode = Fn(() => { + const scaledTime = time.mul(timeScale); + + // noise 1 + const noise1Uv = toRadialUv(uv(), vec2(0.5, 0.5), scaledTime, scaledTime); + noise1Uv.assign(toSkewedUv(noise1Uv, vec2(-1, 0))); + noise1Uv.mulAssign(vec2(4, 1)); + const noise1 = texture(perlinTexture, noise1Uv, 1).r.remap(0.45, 0.7); + + // noise 2 + const noise2Uv = toRadialUv(uv(), vec2(2, 8), scaledTime.mul(2), scaledTime.mul(8)); + noise2Uv.assign(toSkewedUv(noise2Uv, vec2(-0.25, 0))); + noise2Uv.mulAssign(vec2(2, 0.25)); + const noise2 = texture(perlinTexture, noise2Uv, 1).b.remap(0.45, 0.7); + + // outer fade + const distanceToCenter = uv().sub(0.5).toVar(); + const outerFade = min( + distanceToCenter.length().oneMinus().smoothstep(0.5, 0.9), + distanceToCenter.length().smoothstep(0, 0.2), + ); + + // effect + const effect = noise1.mul(noise2).mul(outerFade).toVar(); + + // output + return vec4( + emissiveColor.mul(effect.step(0.2)).mul(3), // Emissive + effect.smoothstep(0, 0.01), // Alpha + ); + })(); + + const floor = new THREE.Mesh(new THREE.PlaneGeometry(2, 2), floorMaterial); + floor.rotation.x = -Math.PI * 0.5; + scene.add(floor); + + // tornado cylinder geometry + + const cylinderGeometry = new THREE.CylinderGeometry(1, 1, 1, 20, 20, true); + cylinderGeometry.translate(0, 0.5, 0); + + // tornado emissive cylinder + + const emissiveMaterial = new THREE.MeshBasicNodeMaterial({ + transparent: true, + side: THREE.DoubleSide, + wireframe: false, + }); + + emissiveMaterial.positionNode = twistedCylinder( + positionLocal, + parabolStrength, + parabolOffset, + parabolAmplitude.sub(0.05), + time.mul(timeScale), + ); + + emissiveMaterial.outputNode = Fn(() => { + const scaledTime = time.mul(timeScale); + + // noise 1 + const noise1Uv = uv().add(vec2(scaledTime, scaledTime.negate())).toVar(); + noise1Uv.assign(toSkewedUv(noise1Uv, vec2(-1, 0))); + noise1Uv.mulAssign(vec2(2, 0.25)); + const noise1 = texture(perlinTexture, noise1Uv, 1).r.remap(0.45, 0.7); + + // noise 2 + const noise2Uv = uv() + .add(vec2(scaledTime.mul(0.5), scaledTime.negate())) + .toVar(); + noise2Uv.assign(toSkewedUv(noise2Uv, vec2(-1, 0))); + noise2Uv.mulAssign(vec2(5, 1)); + const noise2 = texture(perlinTexture, noise2Uv, 1).g.remap(0.45, 0.7); + + // outer fade + const outerFade = min(uv().y.smoothstep(0, 0.1), uv().y.oneMinus().smoothstep(0, 0.4)); + + // effect + const effect = noise1.mul(noise2).mul(outerFade); + + const emissiveColorLuminance = luminance(emissiveColor); + + // output + return vec4( + emissiveColor.mul(1.2).div(emissiveColorLuminance), // emissive + effect.smoothstep(0, 0.1), // alpha + ); + })(); + + const emissive = new THREE.Mesh(cylinderGeometry, emissiveMaterial); + emissive.scale.set(1, 1, 1); + scene.add(emissive); + + // tornado dark cylinder + + const darkMaterial = new THREE.MeshBasicNodeMaterial({ + transparent: true, + side: THREE.DoubleSide, + wireframe: false, + }); + + darkMaterial.positionNode = twistedCylinder( + positionLocal, + parabolStrength, + parabolOffset, + parabolAmplitude, + time.mul(timeScale), + ); + + darkMaterial.outputNode = Fn(() => { + const scaledTime = time.mul(timeScale).add(123.4); + + // noise 1 + const noise1Uv = uv().add(vec2(scaledTime, scaledTime.negate())).toVar(); + noise1Uv.assign(toSkewedUv(noise1Uv, vec2(-1, 0))); + noise1Uv.mulAssign(vec2(2, 0.25)); + const noise1 = texture(perlinTexture, noise1Uv, 1).g.remap(0.45, 0.7); + + // noise 2 + const noise2Uv = uv() + .add(vec2(scaledTime.mul(0.5), scaledTime.negate())) + .toVar(); + noise2Uv.assign(toSkewedUv(noise2Uv, vec2(-1, 0))); + noise2Uv.mulAssign(vec2(5, 1)); + const noise2 = texture(perlinTexture, noise2Uv, 1).b.remap(0.45, 0.7); + + // outer fade + const outerFade = min(uv().y.smoothstep(0, 0.2), uv().y.oneMinus().smoothstep(0, 0.4)); + + // effect + const effect = noise1.mul(noise2).mul(outerFade); + + return vec4(vec3(0), effect.smoothstep(0, 0.01)); + })(); + + const dark = new THREE.Mesh(cylinderGeometry, darkMaterial); + dark.scale.set(1, 1, 1); + scene.add(dark); + + // renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setClearColor(0x201919); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // post processing + + renderPipeline = new THREE.RenderPipeline(renderer); + + const scenePass = pass(scene, camera); + const scenePassColor = scenePass.getTextureNode('output'); + + const bloomPass = bloom(scenePassColor, 1, 0.1, 1); + + renderPipeline.outputNode = scenePassColor.add(bloomPass); + + // controls + + controls = new OrbitControls(camera, renderer.domElement); + controls.target.y = 0.4; + controls.enableDamping = true; + controls.minDistance = 0.1; + controls.maxDistance = 50; + + window.addEventListener('resize', onWindowResize); + + // debug + + const gui = renderer.inspector.createParameters('Parameters'); + + gui.addColor({ color: emissiveColor.value.getHexString(THREE.SRGBColorSpace) }, 'color') + .onChange(value => emissiveColor.value.set(value)) + .name('emissiveColor'); + gui.add(timeScale, 'value', -1, 1, 0.01).name('timeScale'); + gui.add(parabolStrength, 'value', 0, 2, 0.01).name('parabolStrength'); + gui.add(parabolOffset, 'value', 0, 1, 0.01).name('parabolOffset'); + gui.add(parabolAmplitude, 'value', 0, 2, 0.01).name('parabolAmplitude'); + + const bloomGui = gui.addFolder('bloom'); + bloomGui.add(bloomPass.strength, 'value', 0, 10, 0.01).name('strength'); + bloomGui.add(bloomPass.radius, 'value', 0, 1, 0.01).name('radius'); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +async function animate() { + controls.update(); + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_tsl_wood.ts b/examples-testing/examples/webgpu_tsl_wood.ts new file mode 100644 index 000000000..5a4e06502 --- /dev/null +++ b/examples-testing/examples/webgpu_tsl_wood.ts @@ -0,0 +1,249 @@ +import * as THREE from 'three'; +import * as TSL from 'three/tsl'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import WebGPU from 'three/addons/capabilities/WebGPU.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; +import { FontLoader } from 'three/addons/loaders/FontLoader.js'; +import { TextGeometry } from 'three/addons/geometries/TextGeometry.js'; + +import { RoundedBoxGeometry } from 'three/addons/geometries/RoundedBoxGeometry.js'; +import { WoodNodeMaterial, WoodGenuses, Finishes } from 'three/addons/materials/WoodNodeMaterial.js'; + +let scene, base, camera, renderer, controls, font, blockGeometry, gui; + +// Helper function to get grid position +function getGridPosition(woodIndex, finishIndex) { + return { + x: 0, + y: (finishIndex - Finishes.length / 2) * 1.0, + z: (woodIndex - WoodGenuses.length / 2 + 0.45) * 1.0, + }; +} + +// Helper function to create the grid plane +function createGridPlane() { + const material = new THREE.MeshBasicNodeMaterial(); + + const gridXZ = TSL.Fn(([gridSize = TSL.float(1.0), dotWidth = TSL.float(0.1), lineWidth = TSL.float(0.02)]) => { + const coord = TSL.positionWorld.xz.div(gridSize); + const grid = TSL.fract(coord); + + // Screen-space derivative for automatic antialiasing + const fw = TSL.fwidth(coord); + const smoothing = TSL.max(fw.x, fw.y).mul(0.5); + + // Create squares at cell centers + const squareDist = TSL.max(TSL.abs(grid.x.sub(0.5)), TSL.abs(grid.y.sub(0.5))); + const dots = TSL.smoothstep(dotWidth.add(smoothing), dotWidth.sub(smoothing), squareDist); + + // Create grid lines + const lineX = TSL.smoothstep(lineWidth.add(smoothing), lineWidth.sub(smoothing), TSL.abs(grid.x.sub(0.5))); + const lineZ = TSL.smoothstep(lineWidth.add(smoothing), lineWidth.sub(smoothing), TSL.abs(grid.y.sub(0.5))); + const lines = TSL.max(lineX, lineZ); + + return TSL.max(dots, lines); + }); + + const radialGradient = TSL.Fn(([radius = TSL.float(10.0), falloff = TSL.float(1.0)]) => { + return TSL.smoothstep(radius, radius.sub(falloff), TSL.length(TSL.positionWorld)); + }); + + // Create grid pattern + const gridPattern = gridXZ(1.0, 0.03, 0.005); + const baseColor = TSL.vec4(1.0, 1.0, 1.0, 0.0); + const gridColor = TSL.vec4(0.5, 0.5, 0.5, 1.0); + + // Mix base color with grid lines + material.colorNode = gridPattern.mix(baseColor, gridColor).mul(radialGradient(30.0, 20.0)); + material.transparent = true; + + const plane = new THREE.Mesh(new THREE.CircleGeometry(40), material); + plane.rotation.x = -Math.PI / 2; + plane.renderOrder = -1; + + return plane; +} + +// Helper function to create and position labels +function createLabel(text, font, material, position) { + const txt_geo = new TextGeometry(text, { + font: font, + size: 0.1, + depth: 0.001, + curveSegments: 12, + bevelEnabled: false, + }); + + txt_geo.computeBoundingBox(); + const offx = -0.5 * (txt_geo.boundingBox.max.x - txt_geo.boundingBox.min.x); + const offy = -0.5 * (txt_geo.boundingBox.max.y - txt_geo.boundingBox.min.y); + const offz = -0.5 * (txt_geo.boundingBox.max.z - txt_geo.boundingBox.min.z); + txt_geo.translate(offx, offy, offz); + + const label = new THREE.Group(); + const mesh = new THREE.Mesh(txt_geo); + label.add(mesh); + + // Apply default rotation for labels + label.rotateY(-Math.PI / 2); + + label.children[0].material = material; + label.position.copy(position); + base.add(label); +} + +async function init() { + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xffffff); + + camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); + camera.position.set(-0.1, 5, 0.548); + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(1.0); // important for performance + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.toneMapping = THREE.NeutralToneMapping; + renderer.toneMappingExposure = 1.0; + renderer.inspector = new Inspector(); + renderer.setAnimationLoop(render); + document.body.appendChild(renderer.domElement); + + controls = new OrbitControls(camera, renderer.domElement); + controls.target.set(0, 0, 0.548); + + gui = renderer.inspector.createParameters('Parameters'); + + font = await new FontLoader().loadAsync('./fonts/helvetiker_regular.typeface.json'); + + // Create shared block geometry + blockGeometry = new RoundedBoxGeometry(0.125, 0.9, 0.9, 10, 0.02); + + base = new THREE.Group(); + base.rotation.set(0, 0, -Math.PI / 2); + base.position.set(0, 0, 0.548); + scene.add(base); + + const text_mat = new THREE.MeshStandardMaterial(); + text_mat.colorNode = TSL.color('#000000'); + + // Create finish labels (using negative wood index for left column) + for (let y = 0; y < Finishes.length; y++) { + createLabel(Finishes[y], font, text_mat, getGridPosition(-1, y)); + } + + // Create and add the grid plane + const plane = createGridPlane(); + scene.add(plane); + + await new HDRLoader() + .setPath('textures/equirectangular/') + .loadAsync('san_giuseppe_bridge_2k.hdr') + .then(texture => { + texture.mapping = THREE.EquirectangularReflectionMapping; + + scene.environment = texture; + scene.environmentIntensity = 2; + }); + + // Create wood labels (using negative finish index for top row) + for (let x = 0; x < WoodGenuses.length; x++) { + createLabel(WoodGenuses[x], font, text_mat, getGridPosition(x, -1)); + } + + // Create wood blocks + for (let x = 0; x < WoodGenuses.length; x++) { + for (let y = 0; y < Finishes.length; y++) { + const material = WoodNodeMaterial.fromPreset(WoodGenuses[x], Finishes[y]); + const cube = new THREE.Mesh(blockGeometry, material); + + cube.position.copy(getGridPosition(x, y)); + material.transformationMatrix = new THREE.Matrix4().setPosition(new THREE.Vector3(-0.1, 0, Math.random())); + base.add(cube); + + await new Promise(resolve => setTimeout(resolve, 0)); + } + } + + add_custom_wood(text_mat); +} + +function render() { + controls.update(); + + renderer.render(scene, camera); +} + +window.addEventListener('resize', () => { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + renderer.setSize(window.innerWidth, window.innerHeight); +}); + +if (WebGPU.isAvailable()) { + init(); +} else { + document.body.appendChild(WebGPU.getErrorMessage()); +} + +function add_custom_wood(text_mat) { + // Add "Custom" label (positioned at the end of the grid) + createLabel('custom', font, text_mat, getGridPosition(Math.round(WoodGenuses.length / 2 - 1), 5)); + + // Create custom wood material with unique parameters + const customMaterial = new WoodNodeMaterial({ + centerSize: 1.11, + largeWarpScale: 0.32, + largeGrainStretch: 0.24, + smallWarpStrength: 0.059, + smallWarpScale: 2, + fineWarpStrength: 0.006, + fineWarpScale: 32.8, + ringThickness: 1 / 34, + ringBias: 0.03, + ringSizeVariance: 0.03, + ringVarianceScale: 4.4, + barkThickness: 0.3, + splotchScale: 0.2, + splotchIntensity: 0.541, + cellScale: 910, + cellSize: 0.1, + darkGrainColor: new THREE.Color('#0c0504'), + lightGrainColor: new THREE.Color('#926c50'), + clearcoat: 1, + clearcoatRoughness: 0.2, + }); + + gui.add(customMaterial, 'centerSize', 0.0, 2.0, 0.01).name('centerSize'); + gui.add(customMaterial, 'largeWarpScale', 0.0, 1.0, 0.001).name('largeWarpScale'); + gui.add(customMaterial, 'largeGrainStretch', 0.0, 1.0, 0.001).name('largeGrainStretch'); + gui.add(customMaterial, 'smallWarpStrength', 0.0, 0.2, 0.001).name('smallWarpStrength'); + gui.add(customMaterial, 'smallWarpScale', 0.0, 5.0, 0.01).name('smallWarpScale'); + gui.add(customMaterial, 'fineWarpStrength', 0.0, 0.05, 0.001).name('fineWarpStrength'); + gui.add(customMaterial, 'fineWarpScale', 0.0, 50.0, 0.1).name('fineWarpScale'); + gui.add(customMaterial, 'ringThickness', 0.0, 0.1, 0.001).name('ringThickness'); + gui.add(customMaterial, 'ringBias', -0.2, 0.2, 0.001).name('ringBias'); + gui.add(customMaterial, 'ringSizeVariance', 0.0, 0.2, 0.001).name('ringSizeVariance'); + gui.add(customMaterial, 'ringVarianceScale', 0.0, 10.0, 0.1).name('ringVarianceScale'); + gui.add(customMaterial, 'barkThickness', 0.0, 1.0, 0.01).name('barkThickness'); + gui.add(customMaterial, 'splotchScale', 0.0, 1.0, 0.01).name('splotchScale'); + gui.add(customMaterial, 'splotchIntensity', 0.0, 1.0, 0.01).name('splotchIntensity'); + gui.add(customMaterial, 'cellScale', 100, 2000, 1).name('cellScale'); + gui.add(customMaterial, 'cellSize', 0.01, 0.5, 0.001).name('cellSize'); + gui.addColor({ darkGrainColor: '#0c0504' }, 'darkGrainColor').onChange(v => customMaterial.darkGrainColor.set(v)); + gui.addColor({ lightGrainColor: '#926c50' }, 'lightGrainColor').onChange(v => + customMaterial.lightGrainColor.set(v), + ); + gui.add(customMaterial, 'clearcoat', 0.0, 1.0, 0.01).name('clearcoat'); + gui.add(customMaterial, 'clearcoatRoughness', 0.0, 1.0, 0.01).name('clearcoatRoughness'); + + const cube = new THREE.Mesh(blockGeometry, customMaterial); + + customMaterial.transformationMatrix = new THREE.Matrix4().setPosition(new THREE.Vector3(-0.1, 0, Math.random())); + cube.position.copy(getGridPosition(Math.round(WoodGenuses.length / 2), 5)); + + base.add(cube); +} diff --git a/examples-testing/examples/webgpu_video_panorama.ts b/examples-testing/examples/webgpu_video_panorama.ts new file mode 100644 index 000000000..f52b15ffe --- /dev/null +++ b/examples-testing/examples/webgpu_video_panorama.ts @@ -0,0 +1,99 @@ +import * as THREE from 'three/webgpu'; + +let camera, scene, renderer; + +let isUserInteracting = false, + lon = 0, + lat = 0, + phi = 0, + theta = 0, + onPointerDownPointerX = 0, + onPointerDownPointerY = 0, + onPointerDownLon = 0, + onPointerDownLat = 0; + +const distance = 0.5; + +init(); + +function init() { + const container = document.getElementById('container'); + + camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.25, 10); + + scene = new THREE.Scene(); + + const geometry = new THREE.SphereGeometry(5, 60, 40); + // invert the geometry on the x-axis so that all of the faces point inward + geometry.scale(-1, 1, 1); + + const video = document.getElementById('video'); + video.play(); + + const texture = new THREE.VideoTexture(video); + texture.colorSpace = THREE.SRGBColorSpace; + const material = new THREE.MeshBasicMaterial({ map: texture }); + + const mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + container.appendChild(renderer.domElement); + + document.addEventListener('pointerdown', onPointerDown); + document.addEventListener('pointermove', onPointerMove); + document.addEventListener('pointerup', onPointerUp); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function onPointerDown(event) { + isUserInteracting = true; + + onPointerDownPointerX = event.clientX; + onPointerDownPointerY = event.clientY; + + onPointerDownLon = lon; + onPointerDownLat = lat; +} + +function onPointerMove(event) { + if (isUserInteracting === true) { + lon = (onPointerDownPointerX - event.clientX) * 0.1 + onPointerDownLon; + lat = (onPointerDownPointerY - event.clientY) * 0.1 + onPointerDownLat; + } +} + +function onPointerUp() { + isUserInteracting = false; +} + +function animate() { + update(); +} + +function update() { + lat = Math.max(-85, Math.min(85, lat)); + phi = THREE.MathUtils.degToRad(90 - lat); + theta = THREE.MathUtils.degToRad(lon); + + camera.position.x = distance * Math.sin(phi) * Math.cos(theta); + camera.position.y = distance * Math.cos(phi); + camera.position.z = distance * Math.sin(phi) * Math.sin(theta); + + camera.lookAt(0, 0, 0); + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_volume_caustics.ts b/examples-testing/examples/webgpu_volume_caustics.ts new file mode 100644 index 000000000..0c85390dd --- /dev/null +++ b/examples-testing/examples/webgpu_volume_caustics.ts @@ -0,0 +1,304 @@ +import * as THREE from 'three/webgpu'; + +import { + uniform, + refract, + div, + frameId, + lightViewPosition, + float, + positionView, + positionViewDirection, + screenUV, + pass, + texture3D, + time, + screenCoordinate, + normalView, + texture, + Fn, + vec2, + vec3, +} from 'three/tsl'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; +import { ImprovedNoise } from 'three/addons/math/ImprovedNoise.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { bayer16 } from 'three/addons/tsl/math/Bayer.js'; +import { bloom } from 'three/addons/tsl/display/BloomNode.js'; + +let camera, scene, renderer, controls; +let renderPipeline; +let gltf; + +init(); + +async function init() { + const LAYER_VOLUMETRIC_LIGHTING = 10; + + camera = new THREE.PerspectiveCamera(25, window.innerWidth / window.innerHeight, 0.025, 5); + camera.position.set(-0.7, 0.2, 0.2); + + scene = new THREE.Scene(); + + // Light + + const spotLight = new THREE.SpotLight(0xffffff, 1); + spotLight.position.set(0.2, 0.3, 0.2); + spotLight.castShadow = true; + spotLight.angle = Math.PI / 6; + spotLight.penumbra = 1; + spotLight.decay = 2; + spotLight.distance = 0; + spotLight.shadow.mapType = THREE.HalfFloatType; // For HDR Caustics + spotLight.shadow.mapSize.width = 1024; + spotLight.shadow.mapSize.height = 1024; + spotLight.shadow.camera.near = 0.1; + spotLight.shadow.camera.far = 1; + spotLight.shadow.intensity = 0.95; + spotLight.layers.enable(LAYER_VOLUMETRIC_LIGHTING); + scene.add(spotLight); + + // Model / Textures + + const dracoLoader = new DRACOLoader(); + dracoLoader.setDecoderPath('jsm/libs/draco/'); + dracoLoader.setDecoderConfig({ type: 'js' }); + + gltf = (await new GLTFLoader().setDRACOLoader(dracoLoader).loadAsync('./models/gltf/duck.glb')).scene; + gltf.scale.setScalar(0.5); + scene.add(gltf); + + const causticMap = new THREE.TextureLoader().load('./textures/opengameart/Caustic_Free.jpg'); + causticMap.wrapS = causticMap.wrapT = THREE.RepeatWrapping; + causticMap.colorSpace = THREE.SRGBColorSpace; + + // Material + + const duck = gltf.children[0]; + duck.material = new THREE.MeshPhysicalNodeMaterial(); + duck.material.side = THREE.DoubleSide; + duck.material.transparent = true; + duck.material.color = new THREE.Color(0xffd700); + duck.material.transmission = 1; + duck.material.thickness = 0.25; + duck.material.ior = 1.5; + duck.material.metalness = 0; + duck.material.roughness = 0.1; + duck.castShadow = true; + + // TSL Shader + + const causticOcclusion = uniform(1); + + const causticEffect = Fn(() => { + const refractionVector = refract( + positionViewDirection.negate(), + normalView, + div(1.0, duck.material.ior), + ).normalize(); + const viewZ = normalView.z.pow(causticOcclusion); + + const textureUV = refractionVector.xy.mul(0.6); + + const causticColor = uniform(duck.material.color); + const chromaticAberrationOffset = normalView.z.pow(-0.9).mul(0.004); + + const causticProjection = vec3( + texture(causticMap, textureUV.add(vec2(chromaticAberrationOffset.negate(), 0))).r, + texture(causticMap, textureUV.add(vec2(0, chromaticAberrationOffset.negate()))).g, + texture(causticMap, textureUV.add(vec2(chromaticAberrationOffset, chromaticAberrationOffset))).b, + ); + + return causticProjection.mul(viewZ.mul(60)).add(viewZ).mul(causticColor); + })().toVar(); + + duck.material.castShadowNode = causticEffect; + + duck.material.emissiveNode = Fn(() => { + // Custom emissive for illuminating backside of the mesh based on the caustic effect and light direction + + const thicknessPowerNode = float(3.0); + + const scatteringHalf = lightViewPosition(spotLight).sub(positionView).normalize(); + const scatteringDot = float( + positionViewDirection.dot(scatteringHalf.negate()).saturate().pow(thicknessPowerNode), + ); + + return causticEffect.mul(scatteringDot.add(0.1)).mul(0.02); + })(); + + // Ground + + const textureLoader = new THREE.TextureLoader(); + const map = textureLoader.load('textures/hardwood2_diffuse.jpg'); + map.wrapS = map.wrapT = THREE.RepeatWrapping; + map.repeat.set(10, 10); + + const geometry = new THREE.PlaneGeometry(2, 2); + const material = new THREE.MeshStandardMaterial({ color: 0 }); + + const ground = new THREE.Mesh(geometry, material); + ground.rotation.x = -Math.PI / 2; + ground.receiveShadow = true; + scene.add(ground); + + // Renderer + + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.shadowMap.enabled = true; + renderer.shadowMap.transmitted = true; + renderer.inspector = new Inspector(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + document.body.appendChild(renderer.domElement); + + // GUI + + const gui = renderer.inspector.createParameters('Volumetric Caustics'); + gui.add(causticOcclusion, 'value', 0, 20).name('caustic occlusion'); + gui.addColor(duck.material, 'color').name('material color'); + + // Post-Processing + + renderPipeline = new THREE.RenderPipeline(renderer); + + // Layers + + const volumetricLightingIntensity = uniform(0.7); + + const volumetricLayer = new THREE.Layers(); + volumetricLayer.disableAll(); + volumetricLayer.enable(LAYER_VOLUMETRIC_LIGHTING); + + // Volumetric Fog Area + + function createTexture3D() { + let i = 0; + + const size = 128; + const data = new Uint8Array(size * size * size); + + const scale = 10; + const perlin = new ImprovedNoise(); + + const repeatFactor = 5.0; + + for (let z = 0; z < size; z++) { + for (let y = 0; y < size; y++) { + for (let x = 0; x < size; x++) { + const nx = (x / size) * repeatFactor; + const ny = (y / size) * repeatFactor; + const nz = (z / size) * repeatFactor; + + const noiseValue = perlin.noise(nx * scale, ny * scale, nz * scale); + + data[i] = 128 + 128 * noiseValue; + + i++; + } + } + } + + const texture = new THREE.Data3DTexture(data, size, size, size); + texture.format = THREE.RedFormat; + texture.minFilter = THREE.LinearFilter; + texture.magFilter = THREE.LinearFilter; + texture.wrapS = THREE.RepeatWrapping; + texture.wrapT = THREE.RepeatWrapping; + texture.unpackAlignment = 1; + texture.needsUpdate = true; + + return texture; + } + + const noiseTexture3D = createTexture3D(); + + const smokeAmount = uniform(3); + + const volumetricMaterial = new THREE.VolumeNodeMaterial(); + volumetricMaterial.steps = 20; + volumetricMaterial.offsetNode = bayer16(screenCoordinate.add(frameId)); // Add dithering to reduce banding + volumetricMaterial.scatteringNode = Fn(({ positionRay }) => { + // Return the amount of fog based on the noise texture + + const timeScaled = vec3(time.mul(0.01), 0, time.mul(0.03)); + + const sampleGrain = (scale, timeScale = 1) => + texture3D(noiseTexture3D, positionRay.add(timeScaled.mul(timeScale)).mul(scale).mod(1), 0).r.add(0.5); + + let density = sampleGrain(1); + density = density.mul(sampleGrain(0.5, 1)); + density = density.mul(sampleGrain(0.2, 2)); + + return smokeAmount.mix(1, density); + }); + + const volumetricMesh = new THREE.Mesh(new THREE.BoxGeometry(1.5, 0.5, 1.5), volumetricMaterial); + volumetricMesh.receiveShadow = true; + volumetricMesh.position.y = 0.25; + volumetricMesh.layers.disableAll(); + volumetricMesh.layers.enable(LAYER_VOLUMETRIC_LIGHTING); + scene.add(volumetricMesh); + + // Scene Pass + + const scenePass = pass(scene, camera).toInspector(); + scenePass.name = 'Scene'; + + const sceneDepth = scenePass.getTextureNode('depth'); + sceneDepth.name = 'Scene Depth'; + + // Material - Apply occlusion depth of volumetric lighting based on the scene depth + + volumetricMaterial.depthNode = sceneDepth.sample(screenUV); + + // Volumetric Lighting Pass + + const volumetricPass = pass(scene, camera, { depthBuffer: false, samples: 0 }).toInspector( + 'Volumetric Lighting / Raw', + ); + volumetricPass.name = 'Volumetric Lighting'; + volumetricPass.setLayers(volumetricLayer); + volumetricPass.setResolutionScale(0.5); + + // Compose and Denoise + + const bloomPass = bloom(volumetricPass, 1, 1, 0).toInspector('Volumetric Lighting / Mip-Chain Gaussian Blur'); + bloomPass.name = 'Bloom'; + + const scenePassColor = scenePass.add(bloomPass.mul(volumetricLightingIntensity)); + + renderPipeline.outputNode = scenePassColor; + + // Controls + + controls = new OrbitControls(camera, renderer.domElement); + controls.target.z = -0.05; + controls.target.y = 0.02; + controls.maxDistance = 1; + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + for (const mesh of gltf.children) { + mesh.rotation.y -= 0.01; + } + + controls.update(); + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_volume_cloud.ts b/examples-testing/examples/webgpu_volume_cloud.ts new file mode 100644 index 000000000..1d46c3543 --- /dev/null +++ b/examples-testing/examples/webgpu_volume_cloud.ts @@ -0,0 +1,165 @@ +import * as THREE from 'three/webgpu'; +import { float, vec3, vec4, If, Break, Fn, smoothstep, texture3D, uniform } from 'three/tsl'; + +import { RaymarchingBox } from 'three/addons/tsl/utils/Raymarching.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { ImprovedNoise } from 'three/addons/math/ImprovedNoise.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let renderer, scene, camera; +let mesh; + +init(); + +function init() { + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(0, 0, 1.5); + + new OrbitControls(camera, renderer.domElement); + + // Sky + + const canvas = document.createElement('canvas'); + canvas.width = 1; + canvas.height = 32; + + const context = canvas.getContext('2d'); + const gradient = context.createLinearGradient(0, 0, 0, 32); + gradient.addColorStop(0.0, '#014a84'); + gradient.addColorStop(0.5, '#0561a0'); + gradient.addColorStop(1.0, '#437ab6'); + context.fillStyle = gradient; + context.fillRect(0, 0, 1, 32); + + const skyMap = new THREE.CanvasTexture(canvas); + skyMap.colorSpace = THREE.SRGBColorSpace; + + const sky = new THREE.Mesh( + new THREE.SphereGeometry(10), + new THREE.MeshBasicNodeMaterial({ map: skyMap, side: THREE.BackSide }), + ); + scene.add(sky); + + // Texture + + const size = 128; + const data = new Uint8Array(size * size * size); + + let i = 0; + const scale = 0.05; + const perlin = new ImprovedNoise(); + const vector = new THREE.Vector3(); + + for (let z = 0; z < size; z++) { + for (let y = 0; y < size; y++) { + for (let x = 0; x < size; x++) { + const d = + 1.0 - + vector + .set(x, y, z) + .subScalar(size / 2) + .divideScalar(size) + .length(); + data[i] = (128 + 128 * perlin.noise((x * scale) / 1.5, y * scale, (z * scale) / 1.5)) * d * d; + i++; + } + } + } + + const texture = new THREE.Data3DTexture(data, size, size, size); + texture.format = THREE.RedFormat; + texture.minFilter = THREE.LinearFilter; + texture.magFilter = THREE.LinearFilter; + texture.unpackAlignment = 1; + texture.needsUpdate = true; + + // Shader + + const transparentRaymarchingTexture = Fn( + ({ texture, range = float(0.1), threshold = float(0.25), opacity = float(0.25), steps = float(100) }) => { + const finalColor = vec4(0).toVar(); + + RaymarchingBox(steps, ({ positionRay }) => { + const mapValue = float(texture.sample(positionRay.add(0.5)).r).toVar(); + + mapValue.assign(smoothstep(threshold.sub(range), threshold.add(range), mapValue).mul(opacity)); + + const shading = texture + .sample(positionRay.add(vec3(-0.01))) + .r.sub(texture.sample(positionRay.add(vec3(0.01))).r); + + const col = shading.mul(3.0).add(positionRay.x.add(positionRay.y).mul(0.25)).add(0.2); + + finalColor.rgb.addAssign(finalColor.a.oneMinus().mul(mapValue).mul(col)); + + finalColor.a.addAssign(finalColor.a.oneMinus().mul(mapValue)); + + If(finalColor.a.greaterThanEqual(0.95), () => { + Break(); + }); + }); + + return finalColor; + }, + ); + + // Material + + const baseColor = uniform(new THREE.Color(0x798aa0)); + const range = uniform(0.1); + const threshold = uniform(0.25); + const opacity = uniform(0.25); + const steps = uniform(100); + + const cloud3d = transparentRaymarchingTexture({ + texture: texture3D(texture, null, 0), + range, + threshold, + opacity, + steps, + }); + + const finalCloud = cloud3d.setRGB(cloud3d.rgb.add(baseColor)); + + const material = new THREE.NodeMaterial(); + material.colorNode = finalCloud; + material.side = THREE.BackSide; + material.transparent = true; + + mesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), material); + scene.add(mesh); + + // + + const gui = renderer.inspector.createParameters('Parameters'); + gui.add(threshold, 'value', 0, 1, 0.01).name('threshold'); + gui.add(opacity, 'value', 0, 1, 0.01).name('opacity'); + gui.add(range, 'value', 0, 1, 0.01).name('range'); + gui.add(steps, 'value', 0, 200, 1).name('steps'); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + mesh.rotation.y = -performance.now() / 7500; + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_volume_lighting.ts b/examples-testing/examples/webgpu_volume_lighting.ts new file mode 100644 index 000000000..38099c6a5 --- /dev/null +++ b/examples-testing/examples/webgpu_volume_lighting.ts @@ -0,0 +1,248 @@ +import * as THREE from 'three/webgpu'; +import { vec3, Fn, time, texture3D, screenUV, uniform, screenCoordinate, pass } from 'three/tsl'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { ImprovedNoise } from 'three/addons/math/ImprovedNoise.js'; +import { TeapotGeometry } from 'three/addons/geometries/TeapotGeometry.js'; + +import { bayer16 } from 'three/addons/tsl/math/Bayer.js'; +import { gaussianBlur } from 'three/addons/tsl/display/GaussianBlurNode.js'; + +let renderer, scene, camera; +let volumetricMesh, teapot, pointLight, spotLight; +let renderPipeline; + +init(); + +function createTexture3D() { + let i = 0; + + const size = 128; + const data = new Uint8Array(size * size * size); + + const scale = 10; + const perlin = new ImprovedNoise(); + + const repeatFactor = 5.0; + + for (let z = 0; z < size; z++) { + for (let y = 0; y < size; y++) { + for (let x = 0; x < size; x++) { + const nx = (x / size) * repeatFactor; + const ny = (y / size) * repeatFactor; + const nz = (z / size) * repeatFactor; + + const noiseValue = perlin.noise(nx * scale, ny * scale, nz * scale); + + data[i] = 128 + 128 * noiseValue; + + i++; + } + } + } + + const texture = new THREE.Data3DTexture(data, size, size, size); + texture.format = THREE.RedFormat; + texture.minFilter = THREE.LinearFilter; + texture.magFilter = THREE.LinearFilter; + texture.wrapS = THREE.RepeatWrapping; + texture.wrapT = THREE.RepeatWrapping; + texture.unpackAlignment = 1; + texture.needsUpdate = true; + + return texture; +} + +function init() { + const LAYER_VOLUMETRIC_LIGHTING = 10; + + renderer = new THREE.WebGPURenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.toneMapping = THREE.NeutralToneMapping; + renderer.toneMappingExposure = 2; + renderer.shadowMap.enabled = true; + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(-8, 1, -6); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.maxDistance = 40; + controls.minDistance = 2; + + // Volumetric Fog Area + + const noiseTexture3D = createTexture3D(); + + const smokeAmount = uniform(2); + + const volumetricMaterial = new THREE.VolumeNodeMaterial(); + volumetricMaterial.steps = 12; + volumetricMaterial.offsetNode = bayer16(screenCoordinate); // Add dithering to reduce banding + volumetricMaterial.scatteringNode = Fn(({ positionRay }) => { + // Return the amount of fog based on the noise texture + + const timeScaled = vec3(time, 0, time.mul(0.3)); + + const sampleGrain = (scale, timeScale = 1) => + texture3D(noiseTexture3D, positionRay.add(timeScaled.mul(timeScale)).mul(scale).mod(1), 0).r.add(0.5); + + let density = sampleGrain(0.1); + density = density.mul(sampleGrain(0.05, 1)); + density = density.mul(sampleGrain(0.02, 2)); + + return smokeAmount.mix(1, density); + }); + + volumetricMesh = new THREE.Mesh(new THREE.BoxGeometry(20, 10, 20), volumetricMaterial); + volumetricMesh.receiveShadow = true; + volumetricMesh.position.y = 2; + volumetricMesh.layers.disableAll(); + volumetricMesh.layers.enable(LAYER_VOLUMETRIC_LIGHTING); + scene.add(volumetricMesh); + + // Objects + + teapot = new THREE.Mesh( + new TeapotGeometry(0.8, 18), + new THREE.MeshStandardMaterial({ color: 0xffffff, side: THREE.DoubleSide }), + ); + teapot.castShadow = true; + scene.add(teapot); + + const floor = new THREE.Mesh( + new THREE.PlaneGeometry(100, 100), + new THREE.MeshStandardMaterial({ color: 0xffffff }), + ); + floor.rotation.x = -Math.PI / 2; + floor.position.y = -3; + floor.receiveShadow = true; + scene.add(floor); + + // Lights + + pointLight = new THREE.PointLight(0xf9bb50, 3, 100); + pointLight.castShadow = true; + pointLight.position.set(0, 1.4, 0); + pointLight.layers.enable(LAYER_VOLUMETRIC_LIGHTING); + //lightBase.add( new THREE.Mesh( new THREE.SphereGeometry( 0.1, 16, 16 ), new THREE.MeshBasicMaterial( { color: 0xf9bb50 } ) ) ); + scene.add(pointLight); + + spotLight = new THREE.SpotLight(0xffffff, 100); + spotLight.position.set(2.5, 5, 2.5); + spotLight.angle = Math.PI / 6; + spotLight.penumbra = 1; + spotLight.decay = 2; + spotLight.distance = 0; + spotLight.map = new THREE.TextureLoader().setPath('textures/').load('colors.png'); + spotLight.castShadow = true; + spotLight.shadow.intensity = 0.98; + spotLight.shadow.mapSize.width = 1024; + spotLight.shadow.mapSize.height = 1024; + spotLight.shadow.camera.near = 1; + spotLight.shadow.camera.far = 15; + spotLight.shadow.focus = 1; + spotLight.layers.enable(LAYER_VOLUMETRIC_LIGHTING); + //sunLight.add( new THREE.Mesh( new THREE.SphereGeometry( 0.1, 16, 16 ), new THREE.MeshBasicMaterial( { color: 0xffffff } ) ) ); + scene.add(spotLight); + + // Post-Processing + + renderPipeline = new THREE.RenderPipeline(renderer); + + // Layers + + const volumetricLightingIntensity = uniform(1); + + const volumetricLayer = new THREE.Layers(); + volumetricLayer.disableAll(); + volumetricLayer.enable(LAYER_VOLUMETRIC_LIGHTING); + + // Scene Pass + + const scenePass = pass(scene, camera); + const sceneDepth = scenePass.getTextureNode('depth'); + + // Material - Apply occlusion depth of volumetric lighting based on the scene depth + + volumetricMaterial.depthNode = sceneDepth.sample(screenUV); + + // Volumetric Lighting Pass + + const volumetricPass = pass(scene, camera, { depthBuffer: false }); + volumetricPass.name = 'Volumetric Lighting'; + volumetricPass.setLayers(volumetricLayer); + volumetricPass.setResolutionScale(0.25); + + // Compose and Denoise + + const denoiseStrength = uniform(0.6); + + const blurredVolumetricPass = gaussianBlur(volumetricPass, denoiseStrength); + + const scenePassColor = scenePass.add(blurredVolumetricPass.mul(volumetricLightingIntensity)); + + renderPipeline.outputNode = scenePassColor; + + // GUI + + const params = { + resolution: volumetricPass.getResolutionScale(), + denoise: true, + }; + + const gui = renderer.inspector.createParameters('Volumetric Lighting'); + + const rayMarching = gui.addFolder('Ray Marching'); + rayMarching.add(params, 'resolution', 0.1, 1).onChange(resolution => { + volumetricPass.setResolutionScale(resolution); + }); + rayMarching.add(volumetricMaterial, 'steps', 2, 16).name('step count'); + rayMarching.add(denoiseStrength, 'value', 0, 1).name('denoise strength'); + rayMarching.add(params, 'denoise').onChange(denoise => { + const volumetric = denoise ? blurredVolumetricPass : volumetricPass; + + const scenePassColor = scenePass.add(volumetric.mul(volumetricLightingIntensity)); + + renderPipeline.outputNode = scenePassColor; + renderPipeline.needsUpdate = true; + }); + + const lighting = gui.addFolder('Lighting / Scene'); + lighting.add(pointLight, 'intensity', 0, 6).name('light intensity'); + lighting.add(spotLight, 'intensity', 0, 200).name('spot intensity'); + lighting.add(volumetricLightingIntensity, 'value', 0, 2).name('fog intensity'); + lighting.add(smokeAmount, 'value', 0, 3).name('smoke amount'); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + const time = performance.now() * 0.001; + const scale = 2.4; + + pointLight.position.x = Math.sin(time * 0.7) * scale; + pointLight.position.y = Math.cos(time * 0.5) * scale; + pointLight.position.z = Math.cos(time * 0.3) * scale; + + spotLight.position.x = Math.cos(time * 0.3) * scale; + spotLight.lookAt(0, 0, 0); + + teapot.rotation.y = time * 0.2; + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_volume_lighting_rectarea.ts b/examples-testing/examples/webgpu_volume_lighting_rectarea.ts new file mode 100644 index 000000000..47c82841c --- /dev/null +++ b/examples-testing/examples/webgpu_volume_lighting_rectarea.ts @@ -0,0 +1,256 @@ +import * as THREE from 'three/webgpu'; +import { vec3, Fn, time, texture3D, screenUV, uniform, screenCoordinate, pass, checker, uv } from 'three/tsl'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { ImprovedNoise } from 'three/addons/math/ImprovedNoise.js'; +import { RectAreaLightTexturesLib } from 'three/addons/lights/RectAreaLightTexturesLib.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { bayer16 } from 'three/addons/tsl/math/Bayer.js'; +import { gaussianBlur } from 'three/addons/tsl/display/GaussianBlurNode.js'; + +let renderer, scene, camera; +let volumetricMesh; +let rectLight1, rectLight2, rectLight3; +let timer; +let renderPipeline; + +init(); + +function createTexture3D() { + let i = 0; + + const size = 128; + const data = new Uint8Array(size * size * size); + + const scale = 10; + const perlin = new ImprovedNoise(); + + const repeatFactor = 5.0; + + for (let z = 0; z < size; z++) { + for (let y = 0; y < size; y++) { + for (let x = 0; x < size; x++) { + const nx = (x / size) * repeatFactor; + const ny = (y / size) * repeatFactor; + const nz = (z / size) * repeatFactor; + + const noiseValue = perlin.noise(nx * scale, ny * scale, nz * scale); + + data[i] = 128 + 128 * noiseValue; + + i++; + } + } + } + + const texture = new THREE.Data3DTexture(data, size, size, size); + texture.format = THREE.RedFormat; + texture.minFilter = THREE.LinearFilter; + texture.magFilter = THREE.LinearFilter; + texture.wrapS = THREE.RepeatWrapping; + texture.wrapT = THREE.RepeatWrapping; + texture.unpackAlignment = 1; + texture.needsUpdate = true; + + return texture; +} + +function init() { + THREE.RectAreaLightNode.setLTC(RectAreaLightTexturesLib.init()); + + const LAYER_VOLUMETRIC_LIGHTING = 10; + + timer = new THREE.Timer(); + + renderer = new THREE.WebGPURenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.toneMapping = THREE.NeutralToneMapping; + renderer.toneMappingExposure = 2; + renderer.shadowMap.enabled = true; + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 250); + camera.position.set(0, 5, -15); + + // Volumetric Fog Area + + const noiseTexture3D = createTexture3D(); + + const smokeAmount = uniform(2); + + const volumetricMaterial = new THREE.VolumeNodeMaterial(); + volumetricMaterial.steps = 12; + volumetricMaterial.offsetNode = bayer16(screenCoordinate); // Add dithering to reduce banding + volumetricMaterial.scatteringNode = Fn(({ positionRay }) => { + // Return the amount of fog based on the noise texture + + const timeScaled = vec3(time, 0, time.mul(0.3)); + + const sampleGrain = (scale, timeScale = 1) => + texture3D(noiseTexture3D, positionRay.add(timeScaled.mul(timeScale)).mul(scale).mod(1), 0).r.add(0.5); + + let density = sampleGrain(0.1); + density = density.mul(sampleGrain(0.05, 1)); + density = density.mul(sampleGrain(0.02, 2)); + + return smokeAmount.mix(1, density); + }); + + volumetricMesh = new THREE.Mesh(new THREE.BoxGeometry(50, 40, 50), volumetricMaterial); + volumetricMesh.receiveShadow = true; + volumetricMesh.position.y = 20; + volumetricMesh.layers.disableAll(); + volumetricMesh.layers.enable(LAYER_VOLUMETRIC_LIGHTING); + scene.add(volumetricMesh); + + // Objects + + rectLight1 = new THREE.RectAreaLight(0xff0000, 5, 4, 10); + rectLight1.layers.enable(LAYER_VOLUMETRIC_LIGHTING); + rectLight1.position.set(-5, 6, 5); + scene.add(rectLight1); + + rectLight2 = new THREE.RectAreaLight(0x00ff00, 5, 4, 10); + rectLight2.layers.enable(LAYER_VOLUMETRIC_LIGHTING); + rectLight2.position.set(0, 6, 5); + scene.add(rectLight2); + + rectLight3 = new THREE.RectAreaLight(0x0000ff, 5, 4, 10); + rectLight3.layers.enable(LAYER_VOLUMETRIC_LIGHTING); + rectLight3.position.set(5, 6, 5); + scene.add(rectLight3); + + // + + const createRectLightMesh = rectLight => { + const geometry = new THREE.PlaneGeometry(4, 10); + const frontMaterial = new THREE.MeshBasicMaterial({ color: rectLight.color, side: THREE.BackSide }); + const backMaterial = new THREE.MeshStandardMaterial({ color: 0x111111 }); + + const backSide = new THREE.Mesh(geometry, backMaterial); + backSide.position.set(0, 0, 0.08); + + const frontSide = new THREE.Mesh(geometry, frontMaterial); + frontSide.position.set(0, 0, 0.01); + + rectLight.add(backSide); + rectLight.add(frontSide); + }; + + createRectLightMesh(rectLight1); + createRectLightMesh(rectLight2); + createRectLightMesh(rectLight3); + + // + + const geoFloor = new THREE.BoxGeometry(2000, 0.1, 2000); + const matStdFloor = new THREE.MeshStandardMaterial({ color: 0x444444 }); + matStdFloor.roughnessNode = checker(uv().mul(400)); + const mshStdFloor = new THREE.Mesh(geoFloor, matStdFloor); + scene.add(mshStdFloor); + + const geoKnot = new THREE.TorusKnotGeometry(1.5, 0.5, 200, 16); + const matKnot = new THREE.MeshStandardMaterial({ color: 0xffffff, roughness: 0, metalness: 0 }); + const meshKnot = new THREE.Mesh(geoKnot, matKnot); + meshKnot.position.set(0, 5.5, 0); + scene.add(meshKnot); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.minDistance = 5; + controls.maxDistance = 200; + controls.target.copy(meshKnot.position); + controls.update(); + + // Post-Processing + + renderPipeline = new THREE.RenderPipeline(renderer); + + // Layers + + const volumetricLightingIntensity = uniform(1); + + const volumetricLayer = new THREE.Layers(); + volumetricLayer.disableAll(); + volumetricLayer.enable(LAYER_VOLUMETRIC_LIGHTING); + + // Scene Pass + + const scenePass = pass(scene, camera); + const sceneDepth = scenePass.getTextureNode('depth'); + + // Material - Apply occlusion depth of volumetric lighting based on the scene depth + + volumetricMaterial.depthNode = sceneDepth.sample(screenUV); + + // Volumetric Lighting Pass + + const volumetricPass = pass(scene, camera, { depthBuffer: false }); + volumetricPass.setLayers(volumetricLayer); + volumetricPass.setResolutionScale(0.25); + + // Compose and Denoise + + const denoiseStrength = uniform(0.6); + + const blurredVolumetricPass = gaussianBlur(volumetricPass, denoiseStrength); + + const scenePassColor = scenePass.add(blurredVolumetricPass.mul(volumetricLightingIntensity)); + + renderPipeline.outputNode = scenePassColor; + + // GUI + + const params = { + resolution: volumetricPass.getResolutionScale(), + denoise: true, + }; + + const gui = renderer.inspector.createParameters('Volumetric Lighting'); + + const rayMarching = gui.addFolder('Ray Marching'); + rayMarching.add(params, 'resolution', 0.1, 1).onChange(resolution => { + volumetricPass.setResolutionScale(resolution); + }); + rayMarching.add(volumetricMaterial, 'steps', 2, 16).name('step count'); + rayMarching.add(denoiseStrength, 'value', 0, 1).name('denoise strength'); + rayMarching.add(params, 'denoise').onChange(denoise => { + const volumetric = denoise ? blurredVolumetricPass : volumetricPass; + + const scenePassColor = scenePass.add(volumetric.mul(volumetricLightingIntensity)); + + renderPipeline.outputNode = scenePassColor; + renderPipeline.needsUpdate = true; + }); + + const lighting = gui.addFolder('Lighting / Scene'); + lighting.add(volumetricLightingIntensity, 'value', 0, 2).name('fog intensity'); + lighting.add(smokeAmount, 'value', 0, 3).name('smoke amount'); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + const delta = timer.getDelta(); + + rectLight1.rotation.y += -delta; + rectLight2.rotation.y += delta * 0.5; + rectLight3.rotation.y += delta; + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_volume_lighting_traa.ts b/examples-testing/examples/webgpu_volume_lighting_traa.ts new file mode 100644 index 000000000..c5cc55e71 --- /dev/null +++ b/examples-testing/examples/webgpu_volume_lighting_traa.ts @@ -0,0 +1,298 @@ +import * as THREE from 'three/webgpu'; +import { + vec2, + vec3, + Fn, + texture3D, + screenUV, + uniform, + screenCoordinate, + pass, + depthPass, + mrt, + output, + velocity, + fract, + interleavedGradientNoise, +} from 'three/tsl'; + +import { traa } from 'three/addons/tsl/display/TRAANode.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { ImprovedNoise } from 'three/addons/math/ImprovedNoise.js'; +import { TeapotGeometry } from 'three/addons/geometries/TeapotGeometry.js'; + +// Halton sequence for temporal offset - matches TRAA's 32-sample Halton jitter +// This creates optimal low-discrepancy distribution that accumulates well with TRAA +function halton(index, base) { + let result = 0; + let f = 1; + + while (index > 0) { + f /= base; + result += f * (index % base); + index = Math.floor(index / base); + } + + return result; +} + +// Generate 32 Halton offsets (base 2, 3) - same length as TRAA +const _haltonOffsets = Array.from({ length: 32 }, (_, i) => [halton(i + 1, 2), halton(i + 1, 3)]); + +let renderer, scene, camera; +let volumetricMesh, teapot, pointLight, spotLight; +let renderPipeline; +let temporalOffset, temporalRotation, shaderTime; +let params; + +init(); + +function createTexture3D() { + let i = 0; + + const size = 128; + const data = new Uint8Array(size * size * size); + + const scale = 10; + const perlin = new ImprovedNoise(); + + const repeatFactor = 5.0; + + for (let z = 0; z < size; z++) { + for (let y = 0; y < size; y++) { + for (let x = 0; x < size; x++) { + const nx = (x / size) * repeatFactor; + const ny = (y / size) * repeatFactor; + const nz = (z / size) * repeatFactor; + + const noiseValue = perlin.noise(nx * scale, ny * scale, nz * scale); + + data[i] = 128 + 128 * noiseValue; + + i++; + } + } + } + + const texture = new THREE.Data3DTexture(data, size, size, size); + texture.format = THREE.RedFormat; + texture.minFilter = THREE.LinearFilter; + texture.magFilter = THREE.LinearFilter; + texture.wrapS = THREE.RepeatWrapping; + texture.wrapT = THREE.RepeatWrapping; + texture.unpackAlignment = 1; + texture.needsUpdate = true; + + return texture; +} + +function init() { + renderer = new THREE.WebGPURenderer(); + // renderer.setPixelRatio( window.devicePixelRatio ); // Disable DPR for performance + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.toneMapping = THREE.NeutralToneMapping; + renderer.toneMappingExposure = 2; + renderer.shadowMap.enabled = true; + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(-8, 1, -6); + + const controls = new OrbitControls(camera, renderer.domElement); + controls.maxDistance = 40; + controls.minDistance = 2; + + // Volumetric Fog Area + + const noiseTexture3D = createTexture3D(); + + const smokeAmount = uniform(2); + + const volumetricMaterial = new THREE.VolumeNodeMaterial(); + volumetricMaterial.steps = 12; + volumetricMaterial.transparent = true; + volumetricMaterial.blending = THREE.AdditiveBlending; + + // Temporal dithering using Interleaved Gradient Noise (IGN) + Halton sequence + temporalOffset = uniform(0); + temporalRotation = uniform(0); + shaderTime = uniform(0); + + const temporalJitter2D = vec2(temporalOffset, temporalRotation); + volumetricMaterial.offsetNode = fract( + interleavedGradientNoise(screenCoordinate.add(temporalJitter2D.mul(100))).add(temporalOffset), + ); + volumetricMaterial.scatteringNode = Fn(({ positionRay }) => { + const timeScaled = vec3(shaderTime, 0, shaderTime.mul(0.3)); + + const sampleGrain = (scale, timeScale = 1) => + texture3D(noiseTexture3D, positionRay.add(timeScaled.mul(timeScale)).mul(scale).mod(1), 0).r.add(0.5); + + let density = sampleGrain(0.1); + density = density.mul(sampleGrain(0.05, 1)); + density = density.mul(sampleGrain(0.02, 2)); + + return smokeAmount.mix(1, density); + }); + + volumetricMesh = new THREE.Mesh(new THREE.BoxGeometry(20, 10, 20), volumetricMaterial); + volumetricMesh.receiveShadow = true; + volumetricMesh.position.y = 2; + scene.add(volumetricMesh); + + // Objects + + teapot = new THREE.Mesh( + new TeapotGeometry(0.8, 18), + new THREE.MeshStandardMaterial({ color: 0xffffff, side: THREE.DoubleSide }), + ); + teapot.castShadow = true; + scene.add(teapot); + + const floor = new THREE.Mesh( + new THREE.PlaneGeometry(100, 100), + new THREE.MeshStandardMaterial({ color: 0xffffff }), + ); + floor.rotation.x = -Math.PI / 2; + floor.position.y = -3; + floor.receiveShadow = true; + scene.add(floor); + + // Lights + + pointLight = new THREE.PointLight(0xf9bb50, 3, 100); + pointLight.castShadow = true; + pointLight.position.set(0, 1.4, 0); + scene.add(pointLight); + + spotLight = new THREE.SpotLight(0xffffff, 100); + spotLight.position.set(2.5, 5, 2.5); + spotLight.angle = Math.PI / 6; + spotLight.penumbra = 1; + spotLight.decay = 2; + spotLight.distance = 0; + spotLight.map = new THREE.TextureLoader().setPath('textures/').load('colors.png'); + spotLight.castShadow = true; + spotLight.shadow.intensity = 0.98; + spotLight.shadow.mapSize.width = 1024; + spotLight.shadow.mapSize.height = 1024; + spotLight.shadow.camera.near = 1; + spotLight.shadow.camera.far = 15; + spotLight.shadow.focus = 1; + scene.add(spotLight); + + // Render Pipeline + + renderPipeline = new THREE.RenderPipeline(renderer); + + const volumetricIntensity = uniform(1); + + // Pre-Pass: Opaque objects only (volumetric is transparent, excluded automatically) + + const prePass = depthPass(scene, camera); + prePass.name = 'Pre Pass'; + prePass.transparent = false; + + const prePassDepth = prePass.getTextureNode('depth').toInspector('Depth', () => prePass.getLinearDepthNode()); + + // Apply depth to volumetric material for proper occlusion + + volumetricMaterial.depthNode = prePassDepth.sample(screenUV); + + // Scene Pass: Full scene including volumetric with MRT + + const scenePass = pass(scene, camera).toInspector('Scene'); + scenePass.name = 'Scene Pass'; + scenePass.setMRT( + mrt({ + output: output, + velocity: velocity, + }), + ); + + const scenePassColor = scenePass.getTextureNode().toInspector('Output'); + const scenePassVelocity = scenePass.getTextureNode('velocity').toInspector('Velocity'); + + // TRAA with scene pass depth/velocity (includes volumetric) + + const traaPass = traa(scenePassColor, prePassDepth, scenePassVelocity, camera); + + renderPipeline.outputNode = traaPass; + + // GUI + + params = { + traa: true, + animated: true, + }; + + const gui = renderer.inspector.createParameters('Volumetric Lighting'); + + gui.add(params, 'animated'); + gui.add(params, 'traa').name('TRAA').onChange(updatePostProcessing); + + const rayMarching = gui.addFolder('Ray Marching'); + rayMarching.add(volumetricMaterial, 'steps', 2, 16, 1).name('step count'); + + function updatePostProcessing() { + renderPipeline.outputNode = params.traa ? traaPass : scenePassColor; + renderPipeline.needsUpdate = true; + } + + const lighting = gui.addFolder('Lighting / Scene'); + lighting.add(pointLight, 'intensity', 0, 6).name('light intensity'); + lighting.add(spotLight, 'intensity', 0, 200).name('spot intensity'); + lighting.add(volumetricIntensity, 'value', 0, 2).name('volumetric intensity'); + lighting.add(smokeAmount, 'value', 0, 3).name('smoke amount'); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +let frameCount = 0; +let animationTime = 0; +let lastTime = performance.now(); + +function animate() { + const currentTime = performance.now(); + const delta = (currentTime - lastTime) * 0.001; + lastTime = currentTime; + + // Update temporal uniforms - synced with TRAA's Halton sequence for optimal accumulation + const haltonIndex = frameCount % 32; + temporalOffset.value = _haltonOffsets[haltonIndex][0]; + temporalRotation.value = _haltonOffsets[haltonIndex][1]; + frameCount++; + + if (params.animated) { + animationTime += delta; + } + + shaderTime.value = animationTime; + + const scale = 2.4; + + pointLight.position.x = Math.sin(animationTime * 0.7) * scale; + pointLight.position.y = Math.cos(animationTime * 0.5) * scale; + pointLight.position.z = Math.cos(animationTime * 0.3) * scale; + + spotLight.position.x = Math.cos(animationTime * 0.3) * scale; + spotLight.lookAt(0, 0, 0); + + teapot.rotation.y = animationTime * 0.2; + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_volume_perlin.ts b/examples-testing/examples/webgpu_volume_perlin.ts new file mode 100644 index 000000000..fb6f2bbd5 --- /dev/null +++ b/examples-testing/examples/webgpu_volume_perlin.ts @@ -0,0 +1,114 @@ +import * as THREE from 'three/webgpu'; +import { Break, If, vec3, vec4, texture3D, uniform, Fn } from 'three/tsl'; + +import { RaymarchingBox } from 'three/addons/tsl/utils/Raymarching.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { ImprovedNoise } from 'three/addons/math/ImprovedNoise.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +let renderer, scene, camera; +let mesh; + +init(); + +function init() { + renderer = new THREE.WebGPURenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); + camera.position.set(0, 0, 2); + + new OrbitControls(camera, renderer.domElement); + + // Texture + + const size = 128; + const data = new Uint8Array(size * size * size); + + let i = 0; + const perlin = new ImprovedNoise(); + const vector = new THREE.Vector3(); + + for (let z = 0; z < size; z++) { + for (let y = 0; y < size; y++) { + for (let x = 0; x < size; x++) { + vector.set(x, y, z).divideScalar(size); + + const d = perlin.noise(vector.x * 6.5, vector.y * 6.5, vector.z * 6.5); + + data[i++] = d * 128 + 128; + } + } + } + + const texture = new THREE.Data3DTexture(data, size, size, size); + texture.format = THREE.RedFormat; + texture.minFilter = THREE.LinearFilter; + texture.magFilter = THREE.LinearFilter; + texture.unpackAlignment = 1; + texture.needsUpdate = true; + + // Shader + + const opaqueRaymarchingTexture = Fn(({ texture, steps, threshold }) => { + const finalColor = vec4(0).toVar(); + + RaymarchingBox(steps, ({ positionRay }) => { + const mapValue = texture.sample(positionRay.add(0.5)).r.toVar(); + + If(mapValue.greaterThan(threshold), () => { + const p = vec3(positionRay).add(0.5); + + finalColor.rgb.assign(texture.normal(p).mul(0.5).add(positionRay.mul(1.5).add(0.25))); + finalColor.a.assign(1); + Break(); + }); + }); + + return finalColor; + }); + + // + + const threshold = uniform(0.6); + const steps = uniform(200); + + const material = new THREE.NodeMaterial(); + material.colorNode = opaqueRaymarchingTexture({ + texture: texture3D(texture, null, 0), + steps, + threshold, + }); + material.side = THREE.BackSide; + material.transparent = true; + + mesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), material); + scene.add(mesh); + + // + + const gui = renderer.inspector.createParameters('Parameters'); + gui.add(threshold, 'value', 0, 1, 0.01).name('threshold'); + gui.add(steps, 'value', 0, 300, 1).name('steps'); + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_water.ts b/examples-testing/examples/webgpu_water.ts new file mode 100644 index 000000000..88a9e5f60 --- /dev/null +++ b/examples-testing/examples/webgpu_water.ts @@ -0,0 +1,199 @@ +import * as THREE from 'three/webgpu'; + +import { pass, mrt, output, emissive, renderOutput } from 'three/tsl'; +import { bloom } from 'three/addons/tsl/display/BloomNode.js'; +import { fxaa } from 'three/addons/tsl/display/FXAANode.js'; + +import { Inspector } from 'three/addons/inspector/Inspector.js'; + +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; + +import { WaterMesh } from 'three/addons/objects/Water2Mesh.js'; +import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +let scene, camera, renderer, water, renderPipeline, controls; + +const params = { + color: '#99e0ff', + scale: 2, + flowX: 1, + flowY: 1, +}; + +init(); + +async function init() { + scene = new THREE.Scene(); + + const loader = new UltraHDRLoader(); + loader.load('textures/equirectangular/moonless_golf_2k.hdr.jpg', function (texture) { + texture.mapping = THREE.EquirectangularReflectionMapping; + texture.needsUpdate = true; + + scene.background = texture; + scene.environment = texture; + }); + + // camera + + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 200); + camera.position.set(-20, 6, -30); + + // asset loading + + const dracoLoader = new DRACOLoader(); + dracoLoader.setDecoderPath('jsm/libs/draco/gltf/'); + + const gltfLoader = new GLTFLoader(); + gltfLoader.setDRACOLoader(dracoLoader); + + const textureLoader = new THREE.TextureLoader(); + + const [gltf, normalMap0, normalMap1] = await Promise.all([ + gltfLoader.loadAsync('models/gltf/pool.glb'), + textureLoader.loadAsync('textures/water/Water_1_M_Normal.jpg'), + textureLoader.loadAsync('textures/water/Water_2_M_Normal.jpg'), + ]); + + gltf.scene.position.z = 2; + gltf.scene.scale.setScalar(0.1); + scene.add(gltf.scene); + + // water + + normalMap0.wrapS = normalMap0.wrapT = THREE.RepeatWrapping; + normalMap1.wrapS = normalMap1.wrapT = THREE.RepeatWrapping; + + const waterGeometry = new THREE.PlaneGeometry(30, 40); + + water = new WaterMesh(waterGeometry, { + color: params.color, + scale: params.scale, + flowDirection: new THREE.Vector2(params.flowX, params.flowY), + normalMap0: normalMap0, + normalMap1: normalMap1, + }); + + water.position.set(0, 0.2, -2); + water.rotation.x = Math.PI * -0.5; + water.renderOrder = Infinity; + scene.add(water); + + // floor + + const floorGeometry = new THREE.PlaneGeometry(1, 1); + floorGeometry.rotateX(-Math.PI * 0.5); + const floorMaterial = new THREE.MeshStandardMaterial({ + color: 0x444444, + roughness: 1, + metalness: 0, + side: THREE.DoubleSide, + }); + + { + const floor = new THREE.Mesh(floorGeometry, floorMaterial); + floor.position.set(20, 0, 0); + floor.scale.set(15, 1, 80); + scene.add(floor); + } + + { + const floor = new THREE.Mesh(floorGeometry, floorMaterial); + floor.position.set(-20, 0, 0); + floor.scale.set(15, 1, 80); + scene.add(floor); + } + + { + const floor = new THREE.Mesh(floorGeometry, floorMaterial); + floor.position.set(0, 0, 30); + floor.scale.set(30, 1, 20); + scene.add(floor); + } + + { + const floor = new THREE.Mesh(floorGeometry, floorMaterial); + floor.position.set(0, 0, -30); + floor.scale.set(30, 1, 20); + scene.add(floor); + } + + // renderer + + renderer = new THREE.WebGPURenderer(); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setAnimationLoop(animate); + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = 0.5; + renderer.inspector = new Inspector(); + document.body.appendChild(renderer.domElement); + + // postprocessing + + renderPipeline = new THREE.RenderPipeline(renderer); + renderPipeline.outputColorTransform = false; + + const scenePass = pass(scene, camera); + scenePass.setMRT( + mrt({ + output, + emissive, + }), + ); + + const beautyPass = scenePass.getTextureNode(); + const emissivePass = scenePass.getTextureNode('emissive'); + + const bloomPass = bloom(emissivePass, 2); + + const outputPass = renderOutput(beautyPass.add(bloomPass)); + + const fxaaPass = fxaa(outputPass); + renderPipeline.outputNode = fxaaPass; + + // gui + + const gui = renderer.inspector.createParameters('Water'); + const waterNode = water.material.colorNode; + + gui.addColor(params, 'color').onChange(function (value) { + waterNode.color.value.set(value); + }); + gui.add(params, 'scale', 1, 10).onChange(function (value) { + waterNode.scale.value = value; + }); + gui.add(params, 'flowX', -1, 1, 0.01).onChange(function (value) { + waterNode.flowDirection.value.x = value; + waterNode.flowDirection.value.normalize(); + }); + gui.add(params, 'flowY', -1, 1, 0.01).onChange(function (value) { + waterNode.flowDirection.value.y = value; + waterNode.flowDirection.value.normalize(); + }); + + // + + controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + controls.target.set(0, 0, -5); + controls.update(); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + controls.update(); + + renderPipeline.render(); +} diff --git a/examples-testing/examples/webgpu_xr_cubes.ts b/examples-testing/examples/webgpu_xr_cubes.ts new file mode 100644 index 000000000..d164eb843 --- /dev/null +++ b/examples-testing/examples/webgpu_xr_cubes.ts @@ -0,0 +1,216 @@ +import * as THREE from 'three/webgpu'; + +import { BoxLineGeometry } from 'three/addons/geometries/BoxLineGeometry.js'; +import { XRButton } from 'three/addons/webxr/XRButton.js'; +import { XRControllerModelFactory } from 'three/addons/webxr/XRControllerModelFactory.js'; + +const timer = new THREE.Timer(); +timer.connect(document); + +let container; +let camera, scene, raycaster, renderer; + +let room; + +let controller, controllerGrip; +let INTERSECTED; + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x505050); + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 10); + camera.position.set(0, 1.6, 3); + scene.add(camera); + + room = new THREE.LineSegments( + new BoxLineGeometry(6, 6, 6, 10, 10, 10).translate(0, 3, 0), + new THREE.LineBasicMaterial({ color: 0xbcbcbc }), + ); + scene.add(room); + + scene.add(new THREE.HemisphereLight(0xa5a5a5, 0x898989, 3)); + + const light = new THREE.DirectionalLight(0xffffff, 3); + light.position.set(1, 1, 1).normalize(); + scene.add(light); + + const geometry = new THREE.BoxGeometry(0.15, 0.15, 0.15); + + for (let i = 0; i < 200; i++) { + const object = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({ color: Math.random() * 0xffffff })); + + object.position.x = Math.random() * 4 - 2; + object.position.y = Math.random() * 4; + object.position.z = Math.random() * 4 - 2; + + object.rotation.x = Math.random() * 2 * Math.PI; + object.rotation.y = Math.random() * 2 * Math.PI; + object.rotation.z = Math.random() * 2 * Math.PI; + + object.scale.x = Math.random() + 0.5; + object.scale.y = Math.random() + 0.5; + object.scale.z = Math.random() + 0.5; + + object.userData.velocity = new THREE.Vector3(); + object.userData.velocity.x = Math.random() * 0.01 - 0.005; + object.userData.velocity.y = Math.random() * 0.01 - 0.005; + object.userData.velocity.z = Math.random() * 0.01 - 0.005; + + room.add(object); + } + + raycaster = new THREE.Raycaster(); + + renderer = new THREE.WebGPURenderer({ + antialias: true, + forceWebGL: true, + outputBufferType: THREE.UnsignedByteType, + multiview: true, + }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.xr.enabled = true; + container.appendChild(renderer.domElement); + + // + + function onSelectStart() { + this.userData.isSelecting = true; + } + + function onSelectEnd() { + this.userData.isSelecting = false; + } + + controller = renderer.xr.getController(0); + controller.addEventListener('selectstart', onSelectStart); + controller.addEventListener('selectend', onSelectEnd); + controller.addEventListener('connected', function (event) { + const targetRayMode = event.data.targetRayMode; + + if (targetRayMode === 'tracked-pointer' || targetRayMode === 'gaze') { + this.add(buildController(event.data)); + } + }); + controller.addEventListener('disconnected', function () { + this.remove(this.children[0]); + }); + scene.add(controller); + + const controllerModelFactory = new XRControllerModelFactory(); + + controllerGrip = renderer.xr.getControllerGrip(0); + controllerGrip.add(controllerModelFactory.createControllerModel(controllerGrip)); + scene.add(controllerGrip); + + window.addEventListener('resize', onWindowResize); + + // + + document.body.appendChild(XRButton.createButton(renderer)); +} + +function buildController(data) { + let geometry, material; + + switch (data.targetRayMode) { + case 'tracked-pointer': + geometry = new THREE.BufferGeometry(); + geometry.setAttribute('position', new THREE.Float32BufferAttribute([0, 0, 0, 0, 0, -1], 3)); + geometry.setAttribute('color', new THREE.Float32BufferAttribute([0.5, 0.5, 0.5, 0, 0, 0], 3)); + + material = new THREE.LineBasicMaterial({ vertexColors: true, blending: THREE.AdditiveBlending }); + + return new THREE.Line(geometry, material); + + case 'gaze': + geometry = new THREE.RingGeometry(0.02, 0.04, 32).translate(0, 0, -1); + material = new THREE.MeshBasicMaterial({ opacity: 0.5, transparent: true }); + return new THREE.Mesh(geometry, material); + } +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + timer.update(); + + const delta = timer.getDelta() * 60; + + if (controller.userData.isSelecting === true) { + const cube = room.children[0]; + room.remove(cube); + + cube.position.copy(controller.position); + cube.userData.velocity.x = (Math.random() - 0.5) * 0.02 * delta; + cube.userData.velocity.y = (Math.random() - 0.5) * 0.02 * delta; + cube.userData.velocity.z = (Math.random() * 0.01 - 0.05) * delta; + cube.userData.velocity.applyQuaternion(controller.quaternion); + room.add(cube); + } + + // find intersections + + raycaster.setFromXRController(controller); + + const intersects = raycaster.intersectObjects(room.children, false); + + if (intersects.length > 0) { + if (INTERSECTED != intersects[0].object) { + if (INTERSECTED) INTERSECTED.material.emissive.setHex(INTERSECTED.currentHex); + + INTERSECTED = intersects[0].object; + INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex(); + INTERSECTED.material.emissive.setHex(0xff0000); + } + } else { + if (INTERSECTED) INTERSECTED.material.emissive.setHex(INTERSECTED.currentHex); + + INTERSECTED = undefined; + } + + // Keep cubes inside room + + for (let i = 0; i < room.children.length; i++) { + const cube = room.children[i]; + + cube.userData.velocity.multiplyScalar(1 - 0.001 * delta); + + cube.position.add(cube.userData.velocity); + + if (cube.position.x < -3 || cube.position.x > 3) { + cube.position.x = THREE.MathUtils.clamp(cube.position.x, -3, 3); + cube.userData.velocity.x = -cube.userData.velocity.x; + } + + if (cube.position.y < 0 || cube.position.y > 6) { + cube.position.y = THREE.MathUtils.clamp(cube.position.y, 0, 6); + cube.userData.velocity.y = -cube.userData.velocity.y; + } + + if (cube.position.z < -3 || cube.position.z > 3) { + cube.position.z = THREE.MathUtils.clamp(cube.position.z, -3, 3); + cube.userData.velocity.z = -cube.userData.velocity.z; + } + + cube.rotation.x += cube.userData.velocity.x * 2 * delta; + cube.rotation.y += cube.userData.velocity.y * 2 * delta; + cube.rotation.z += cube.userData.velocity.z * 2 * delta; + } + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_xr_native_layers.ts b/examples-testing/examples/webgpu_xr_native_layers.ts new file mode 100644 index 000000000..cd7f3fb2d --- /dev/null +++ b/examples-testing/examples/webgpu_xr_native_layers.ts @@ -0,0 +1,638 @@ +import * as THREE from 'three/webgpu'; + +import { BoxLineGeometry } from 'three/addons/geometries/BoxLineGeometry.js'; +import { VRButton } from 'three/addons/webxr/VRButton.js'; +import { XRControllerModelFactory } from 'three/addons/webxr/XRControllerModelFactory.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; +import { InteractiveGroup } from 'three/addons/interactive/InteractiveGroup.js'; +import { + RollerCoasterGeometry, + RollerCoasterShadowGeometry, + RollerCoasterLiftersGeometry, + TreesGeometry, + SkyGeometry, +} from 'three/addons/misc/RollerCoaster.js'; +import { HTMLMesh } from 'three/addons/interactive/HTMLMesh.js'; +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; + +let camera, scene, renderer; +let controller1, controller2; +let controllerGrip1, controllerGrip2; + +let room; + +let count = 0; +const radius = 0.08; +let normal = new THREE.Vector3(); +const relativeVelocity = new THREE.Vector3(); + +const timer = new THREE.Timer(); +timer.connect(document); +const funfairs = []; +const train = new THREE.Object3D(); +const rcdelta = timer.getDelta() * 0.8; // slow down simulation +const PI2 = Math.PI * 2; +let rccamera = null; +let rcscene = null; + +const tempMatrix = new THREE.Matrix4(); +let raycaster = null; + +const curve = (function () { + const vector = new THREE.Vector3(); + const vector2 = new THREE.Vector3(); + + return { + getPointAt: function (t) { + t = t * PI2; + + const x = Math.sin(t * 3) * Math.cos(t * 4) * 50; + const y = Math.sin(t * 10) * 2 + Math.cos(t * 17) * 2 + 5; + const z = Math.sin(t) * Math.sin(t * 4) * 50; + + return vector.set(x, y, z).multiplyScalar(2); + }, + + getTangentAt: function (t) { + const delta = 0.0001; + const t1 = Math.max(0, t - delta); + const t2 = Math.min(1, t + delta); + + return vector2.copy(this.getPointAt(t2)).sub(this.getPointAt(t1)).normalize(); + }, + }; +})(); + +let horseCamera = null; +let horseScene = null; +let horseMixer = null; +let horseTheta = 0; +let horseMesh = null; +const horseRadius = 600; + +let guiScene = null; +let guiCamera = null; +let guiGroup = null; + +let rollercoasterLayer = null; +let horseLayer = null; +let guiLayer = null; + +const parameters = { + radius: 0.6, + tube: 0.2, + tubularSegments: 150, + radialSegments: 20, + p: 2, + q: 3, + thickness: 0.5, +}; + +init(); + +function getIntersections(controller) { + tempMatrix.identity().extractRotation(controller.matrixWorld); + + raycaster.ray.origin.setFromMatrixPosition(controller.matrixWorld); + raycaster.ray.direction.set(0, 0, -1).applyMatrix4(tempMatrix); + + return raycaster.intersectObjects(scene.children, false); +} + +function init() { + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x505050); + + raycaster = new THREE.Raycaster(); + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 10); + camera.position.set(0, 1.6, 3); + + room = new THREE.LineSegments( + new BoxLineGeometry(6, 6, 6, 10, 10, 10), + new THREE.LineBasicMaterial({ color: 0x808080 }), + ); + room.geometry.translate(0, 3, 0); + scene.add(room); + + scene.add(new THREE.HemisphereLight(0x606060, 0x404040)); + + const light = new THREE.DirectionalLight(0xffffff); + light.position.set(1, 1, 1).normalize(); + scene.add(light); + + const geometry = new THREE.IcosahedronGeometry(radius, 3); + + for (let i = 0; i < 200; i++) { + const object = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({ color: Math.random() * 0xffffff })); + + object.position.x = Math.random() * 4 - 2; + object.position.y = Math.random() * 4; + object.position.z = Math.random() * 4 - 2; + + object.userData.velocity = new THREE.Vector3(); + object.userData.velocity.x = Math.random() * 0.01 - 0.005; + object.userData.velocity.y = Math.random() * 0.01 - 0.005; + object.userData.velocity.z = Math.random() * 0.01 - 0.005; + + room.add(object); + } + + // + + renderer = new THREE.WebGPURenderer({ + antialias: true, + forceWebGL: true, + outputBufferType: THREE.UnsignedByteType, + multiview: true, + }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(render); + renderer.xr.enabled = true; + document.body.appendChild(renderer.domElement); + + // + + document.body.appendChild(VRButton.createButton(renderer)); + + // controllers + + function onSqueezeStart() { + this.userData.isSelecting = true; + } + + function onSqueezeEnd() { + this.userData.isSelecting = false; + } + + function onSelectStart(event) { + const controller = event.target; + + const intersections = getIntersections(controller); + let hadSelection = false; + + for (let x = 0; x < intersections.length; x++) { + if (intersections[x].object == horseLayer) { + horseLayer.visible = false; + hadSelection = true; + } + + if (intersections[x].object == rollercoasterLayer) { + controller.attach(rollercoasterLayer); + hadSelection = true; + } + + if (intersections[x].object == guiLayer) { + const uv = intersections[x].uv; + guiGroup.children[0].dispatchEvent({ + type: 'mousedown', + data: { x: uv.x, y: 1 - uv.y }, + target: guiGroup, + }); + hadSelection = true; + } + } + + this.userData.isSelecting = hadSelection === false; + } + + function onSelectEnd() { + horseLayer.visible = true; + scene.attach(rollercoasterLayer); + guiGroup.children[0].dispatchEvent({ type: 'mouseup', data: { x: 0, y: 0 }, target: guiGroup }); + this.userData.isSelecting = false; + } + + controller1 = renderer.xr.getController(0); + controller1.addEventListener('selectstart', onSelectStart); + controller1.addEventListener('selectend', onSelectEnd); + controller1.addEventListener('squeezestart', onSqueezeStart); + controller1.addEventListener('squeezeend', onSqueezeEnd); + controller1.addEventListener('connected', function (event) { + this.add(buildController(event.data)); + }); + controller1.addEventListener('disconnected', function () { + this.remove(this.children[0]); + }); + scene.add(controller1); + + controller2 = renderer.xr.getController(1); + controller2.addEventListener('selectstart', onSelectStart); + controller2.addEventListener('selectend', onSelectEnd); + controller2.addEventListener('squeezestart', onSqueezeStart); + controller2.addEventListener('squeezeend', onSqueezeEnd); + controller2.addEventListener('connected', function (event) { + this.add(buildController(event.data)); + }); + controller2.addEventListener('disconnected', function () { + this.remove(this.children[0]); + }); + scene.add(controller2); + + // The XRControllerModelFactory will automatically fetch controller models + // that match what the user is holding as closely as possible. The models + // should be attached to the object returned from getControllerGrip in + // order to match the orientation of the held device. + + const controllerModelFactory = new XRControllerModelFactory(); + + controllerGrip1 = renderer.xr.getControllerGrip(0); + controllerGrip1.add(controllerModelFactory.createControllerModel(controllerGrip1)); + scene.add(controllerGrip1); + + controllerGrip2 = renderer.xr.getControllerGrip(1); + controllerGrip2.add(controllerModelFactory.createControllerModel(controllerGrip2)); + scene.add(controllerGrip2); + + // + + window.addEventListener('resize', onWindowResize); + + // set up rollercoaster + rollercoasterLayer = renderer.xr.createCylinderLayer( + 1, + Math.PI / 2, + 2, + new THREE.Vector3(0, 1.5, -0.5), + new THREE.Quaternion(), + 1500, + 1000, + renderRollercoaster, + ); + scene.add(rollercoasterLayer); + + rcscene = new THREE.Scene(); + rcscene.background = new THREE.Color(0xf0f0ff); + + const rclight = new THREE.HemisphereLight(0xfff0f0, 0x606066); + rclight.position.set(1, 1, 1); + rcscene.add(rclight); + + rcscene.add(train); + + rccamera = new THREE.PerspectiveCamera(50, 1, 0.1, 500); + train.add(rccamera); + + // environment + + let rcgeometry = new THREE.PlaneGeometry(500, 500, 15, 15); + rcgeometry.rotateX(-Math.PI / 2); + + const positions = rcgeometry.attributes.position.array; + const vertex = new THREE.Vector3(); + + for (let i = 0; i < positions.length; i += 3) { + vertex.fromArray(positions, i); + + vertex.x += Math.random() * 10 - 5; + vertex.z += Math.random() * 10 - 5; + + const distance = vertex.distanceTo(scene.position) / 5 - 25; + vertex.y = Math.random() * Math.max(0, distance); + + vertex.toArray(positions, i); + } + + rcgeometry.computeVertexNormals(); + + let rcmaterial = new THREE.MeshLambertMaterial({ + color: 0x407000, + }); + + let rcmesh = new THREE.Mesh(rcgeometry, rcmaterial); + rcscene.add(rcmesh); + + rcgeometry = new TreesGeometry(rcmesh); + rcmaterial = new THREE.MeshBasicMaterial({ + side: THREE.DoubleSide, + vertexColors: true, + }); + rcmesh = new THREE.Mesh(rcgeometry, rcmaterial); + rcscene.add(rcmesh); + + rcgeometry = new SkyGeometry(); + rcmaterial = new THREE.MeshBasicMaterial({ color: 0xffffff }); + rcmesh = new THREE.Mesh(rcgeometry, rcmaterial); + rcscene.add(rcmesh); + + // + + rcgeometry = new RollerCoasterGeometry(curve, 1500); + rcmaterial = new THREE.MeshPhongMaterial({ + vertexColors: true, + }); + rcmesh = new THREE.Mesh(rcgeometry, rcmaterial); + rcscene.add(rcmesh); + + rcgeometry = new RollerCoasterLiftersGeometry(curve, 100); + rcmaterial = new THREE.MeshPhongMaterial(); + rcmesh = new THREE.Mesh(rcgeometry, rcmaterial); + rcmesh.position.y = 0.1; + rcscene.add(rcmesh); + + rcgeometry = new RollerCoasterShadowGeometry(curve, 500); + rcmaterial = new THREE.MeshBasicMaterial({ + color: 0x305000, + depthWrite: false, + transparent: true, + }); + rcmesh = new THREE.Mesh(rcgeometry, rcmaterial); + rcmesh.position.y = 0.1; + rcscene.add(rcmesh); + + // + + rcgeometry = new THREE.CylinderGeometry(10, 10, 5, 15); + rcmaterial = new THREE.MeshLambertMaterial({ + color: 0xff8080, + }); + rcmesh = new THREE.Mesh(rcgeometry, rcmaterial); + rcmesh.position.set(-80, 10, -70); + rcmesh.rotation.x = Math.PI / 2; + rcscene.add(rcmesh); + + funfairs.push(rcmesh); + + rcgeometry = new THREE.CylinderGeometry(5, 6, 4, 10); + rcmaterial = new THREE.MeshLambertMaterial({ + color: 0x8080ff, + }); + rcmesh = new THREE.Mesh(rcgeometry, rcmaterial); + rcmesh.position.set(50, 2, 30); + rcscene.add(rcmesh); + + funfairs.push(rcmesh); + + // set up horse animation + horseLayer = renderer.xr.createQuadLayer( + 1, + 1, + new THREE.Vector3(-1.5, 1.5, -1.5), + new THREE.Quaternion(), + 800, + 800, + renderQuad, + ); + scene.add(horseLayer); + + horseLayer.geometry = new THREE.CircleGeometry(0.5, 64); + + horseCamera = new THREE.PerspectiveCamera(50, 1, 1, 10000); + horseCamera.position.y = 300; + + horseScene = new THREE.Scene(); + horseScene.background = new THREE.Color(0xf0f0f0); + + // + + const light1 = new THREE.DirectionalLight(0xefefff, 1.5); + light1.position.set(1, 1, 1).normalize(); + horseScene.add(light1); + + const light2 = new THREE.DirectionalLight(0xffefef, 1.5); + light2.position.set(-1, -1, -1).normalize(); + horseScene.add(light2); + + const loader = new GLTFLoader(); + loader.load('models/gltf/Horse.glb', function (gltf) { + horseMesh = gltf.scene.children[0]; + horseMesh.scale.set(1.5, 1.5, 1.5); + horseScene.add(horseMesh); + + horseMixer = new THREE.AnimationMixer(horseMesh); + + horseMixer.clipAction(gltf.animations[0]).setDuration(1).play(); + }); + + function onChange() {} + + function onThicknessChange() {} + + // set up ui + guiScene = new THREE.Scene(); + guiScene.background = new THREE.Color(0x0); + + guiCamera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1); + guiScene.add(guiCamera); + + const gui = new GUI({ width: 300 }); + gui.add(parameters, 'radius', 0.0, 1.0).onChange(onChange); + gui.add(parameters, 'tube', 0.0, 1.0).onChange(onChange); + gui.add(parameters, 'tubularSegments', 10, 150, 1).onChange(onChange); + gui.add(parameters, 'radialSegments', 2, 20, 1).onChange(onChange); + gui.add(parameters, 'p', 1, 10, 1).onChange(onChange); + gui.add(parameters, 'q', 0, 10, 1).onChange(onChange); + gui.add(parameters, 'thickness', 0, 1).onChange(onThicknessChange); + gui.domElement.style.visibility = 'hidden'; + + guiGroup = new InteractiveGroup(); + guiScene.add(guiGroup); + + const mesh = new HTMLMesh(gui.domElement); + guiGroup.add(mesh); + + const bbox = new THREE.Box3().setFromObject(guiScene); + + guiLayer = renderer.xr.createQuadLayer( + 1.2, + 0.8, + new THREE.Vector3(1.5, 1.5, -1.5), + new THREE.Quaternion(), + 1280, + 800, + renderGui, + ); + scene.add(guiLayer); + + guiCamera.left = bbox.min.x; + guiCamera.right = bbox.max.x; + guiCamera.top = bbox.max.y; + guiCamera.bottom = bbox.min.y; + guiCamera.updateProjectionMatrix(); +} + +function renderGui() { + renderer.render(guiScene, guiCamera); +} + +function renderQuad() { + horseTheta += 0.1; + + horseCamera.position.x = horseRadius * Math.sin(THREE.MathUtils.degToRad(horseTheta)); + horseCamera.position.z = horseRadius * Math.cos(THREE.MathUtils.degToRad(horseTheta)); + + horseCamera.lookAt(0, 150, 0); + + if (horseMixer) { + const time = Date.now(); + + horseMixer.update((time - prevTime) * 0.001); + + prevTime = time; + } + + renderer.render(horseScene, horseCamera); +} + +const rcposition = new THREE.Vector3(); +const tangent = new THREE.Vector3(); + +const lookAt = new THREE.Vector3(); + +let rcvelocity = 0; +let progress = 0; + +let prevTime = performance.now(); + +function renderRollercoaster() { + const time = performance.now(); + for (let i = 0; i < funfairs.length; i++) { + funfairs[i].rotation.y = time * 0.0004; + } + + // + + progress += rcvelocity; + progress = progress % 1; + + rcposition.copy(curve.getPointAt(progress)); + rcposition.y += 0.3; + + train.position.copy(rcposition); + + tangent.copy(curve.getTangentAt(progress)); + + rcvelocity -= tangent.y * 0.0000001 * rcdelta; + rcvelocity = Math.max(0.00004, Math.min(0.0002, rcvelocity)); + + train.lookAt(lookAt.copy(rcposition).sub(tangent)); + + // + + renderer.render(rcscene, rccamera); +} + +function buildController(data) { + let geometry, material; + + switch (data.targetRayMode) { + case 'tracked-pointer': + geometry = new THREE.BufferGeometry(); + geometry.setAttribute('position', new THREE.Float32BufferAttribute([0, 0, 0, 0, 0, -1], 3)); + geometry.setAttribute('color', new THREE.Float32BufferAttribute([0.5, 0.5, 0.5, 0, 0, 0], 3)); + + material = new THREE.LineBasicMaterial({ vertexColors: true, blending: THREE.AdditiveBlending }); + + return new THREE.Line(geometry, material); + + case 'gaze': + geometry = new THREE.RingGeometry(0.02, 0.04, 32).translate(0, 0, -1); + material = new THREE.MeshBasicMaterial({ opacity: 0.5, transparent: true }); + return new THREE.Mesh(geometry, material); + } +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function handleController(controller) { + if (controller.userData.isSelecting) { + const object = room.children[count++]; + + object.position.copy(controller.position); + object.userData.velocity.x = (Math.random() - 0.5) * 3; + object.userData.velocity.y = (Math.random() - 0.5) * 3; + object.userData.velocity.z = Math.random() - 9; + object.userData.velocity.applyQuaternion(controller.quaternion); + + if (count === room.children.length) count = 0; + } + + const intersections = getIntersections(controller); + for (let x = 0; x < intersections.length; x++) { + if (intersections[x].object == guiLayer) { + const uv = intersections[x].uv; + guiGroup.children[0].dispatchEvent({ type: 'mousemove', data: { x: uv.x, y: 1 - uv.y }, target: guiGroup }); + } + } +} + +// + +function render() { + timer.update(); + + renderer.xr.renderLayers(); + + handleController(controller1); + handleController(controller2); + + // rotate horse + horseLayer.rotation.y -= 0.02; + + // + const delta = timer.getDelta() * 0.8; + + const range = 3 - radius; + + for (let i = 0; i < room.children.length; i++) { + const object = room.children[i]; + + object.position.x += object.userData.velocity.x * delta; + object.position.y += object.userData.velocity.y * delta; + object.position.z += object.userData.velocity.z * delta; + + // keep objects inside room + + if (object.position.x < -range || object.position.x > range) { + object.position.x = THREE.MathUtils.clamp(object.position.x, -range, range); + object.userData.velocity.x = -object.userData.velocity.x; + } + + if (object.position.y < radius || object.position.y > 6) { + object.position.y = Math.max(object.position.y, radius); + + object.userData.velocity.x *= 0.98; + object.userData.velocity.y = -object.userData.velocity.y * 0.8; + object.userData.velocity.z *= 0.98; + } + + if (object.position.z < -range || object.position.z > range) { + object.position.z = THREE.MathUtils.clamp(object.position.z, -range, range); + object.userData.velocity.z = -object.userData.velocity.z; + } + + for (let j = i + 1; j < room.children.length; j++) { + const object2 = room.children[j]; + + normal.copy(object.position).sub(object2.position); + + const distance = normal.length(); + + if (distance < 2 * radius) { + normal.multiplyScalar(0.5 * distance - radius); + + object.position.sub(normal); + object2.position.add(normal); + + normal.normalize(); + + relativeVelocity.copy(object.userData.velocity).sub(object2.userData.velocity); + + normal = normal.multiplyScalar(relativeVelocity.dot(normal)); + + object.userData.velocity.sub(normal); + object2.userData.velocity.add(normal); + } + } + + object.userData.velocity.y -= 9.8 * delta; + } + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webgpu_xr_rollercoaster.ts b/examples-testing/examples/webgpu_xr_rollercoaster.ts new file mode 100644 index 000000000..6035d0687 --- /dev/null +++ b/examples-testing/examples/webgpu_xr_rollercoaster.ts @@ -0,0 +1,216 @@ +import * as THREE from 'three/webgpu'; + +import { + RollerCoasterGeometry, + RollerCoasterShadowGeometry, + RollerCoasterLiftersGeometry, + TreesGeometry, + SkyGeometry, +} from 'three/addons/misc/RollerCoaster.js'; +import { VRButton } from 'three/addons/webxr/VRButton.js'; + +let mesh, material, geometry; + +const renderer = new THREE.WebGPURenderer({ + antialias: true, + forceWebGL: true, + outputBufferType: THREE.UnsignedByteType, + multiview: false, +}); +renderer.setPixelRatio(window.devicePixelRatio); +renderer.setSize(window.innerWidth, window.innerHeight); +renderer.setAnimationLoop(animate); +renderer.xr.enabled = true; +renderer.xr.setReferenceSpaceType('local'); +document.body.appendChild(renderer.domElement); + +document.body.appendChild(VRButton.createButton(renderer)); + +// + +const scene = new THREE.Scene(); +scene.background = new THREE.Color(0xf0f0ff); + +const light = new THREE.HemisphereLight(0xfff0f0, 0x60606, 3); +light.position.set(1, 1, 1); +scene.add(light); + +const train = new THREE.Object3D(); +scene.add(train); + +const camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 500); +train.add(camera); + +// environment + +geometry = new THREE.PlaneGeometry(500, 500, 15, 15); +geometry.rotateX(-Math.PI / 2); + +const positions = geometry.attributes.position.array; +const vertex = new THREE.Vector3(); + +for (let i = 0; i < positions.length; i += 3) { + vertex.fromArray(positions, i); + + vertex.x += Math.random() * 10 - 5; + vertex.z += Math.random() * 10 - 5; + + const distance = vertex.distanceTo(scene.position) / 5 - 25; + vertex.y = Math.random() * Math.max(0, distance); + + vertex.toArray(positions, i); +} + +geometry.computeVertexNormals(); + +material = new THREE.MeshLambertMaterial({ + color: 0x407000, +}); + +mesh = new THREE.Mesh(geometry, material); +scene.add(mesh); + +geometry = new TreesGeometry(mesh); +material = new THREE.MeshBasicMaterial({ + side: THREE.DoubleSide, + vertexColors: true, +}); +mesh = new THREE.Mesh(geometry, material); +scene.add(mesh); + +geometry = new SkyGeometry(); +material = new THREE.MeshBasicMaterial({ color: 0xffffff }); +mesh = new THREE.Mesh(geometry, material); +scene.add(mesh); + +// + +const PI2 = Math.PI * 2; + +const curve = (function () { + const vector = new THREE.Vector3(); + const vector2 = new THREE.Vector3(); + + return { + getPointAt: function (t) { + t = t * PI2; + + const x = Math.sin(t * 3) * Math.cos(t * 4) * 50; + const y = Math.sin(t * 10) * 2 + Math.cos(t * 17) * 2 + 5; + const z = Math.sin(t) * Math.sin(t * 4) * 50; + + return vector.set(x, y, z).multiplyScalar(2); + }, + + getTangentAt: function (t) { + const delta = 0.0001; + const t1 = Math.max(0, t - delta); + const t2 = Math.min(1, t + delta); + + return vector2.copy(this.getPointAt(t2)).sub(this.getPointAt(t1)).normalize(); + }, + }; +})(); + +geometry = new RollerCoasterGeometry(curve, 1500); +material = new THREE.MeshPhongMaterial({ + vertexColors: true, +}); +mesh = new THREE.Mesh(geometry, material); +scene.add(mesh); + +geometry = new RollerCoasterLiftersGeometry(curve, 100); +material = new THREE.MeshPhongMaterial(); +mesh = new THREE.Mesh(geometry, material); +mesh.position.y = 0.1; +scene.add(mesh); + +geometry = new RollerCoasterShadowGeometry(curve, 500); +material = new THREE.MeshBasicMaterial({ + color: 0x305000, + depthWrite: false, + transparent: true, +}); +mesh = new THREE.Mesh(geometry, material); +mesh.position.y = 0.1; +scene.add(mesh); + +const funfairs = []; + +// + +geometry = new THREE.CylinderGeometry(10, 10, 5, 15); +material = new THREE.MeshLambertMaterial({ + color: 0xff8080, +}); +mesh = new THREE.Mesh(geometry, material); +mesh.position.set(-80, 10, -70); +mesh.rotation.x = Math.PI / 2; +scene.add(mesh); + +funfairs.push(mesh); + +geometry = new THREE.CylinderGeometry(5, 6, 4, 10); +material = new THREE.MeshLambertMaterial({ + color: 0x8080ff, +}); +mesh = new THREE.Mesh(geometry, material); +mesh.position.set(50, 2, 30); +scene.add(mesh); + +funfairs.push(mesh); + +// + +window.addEventListener('resize', onWindowResize); + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +const position = new THREE.Vector3(); +const tangent = new THREE.Vector3(); + +const lookAt = new THREE.Vector3(); + +let velocity = 0; +let progress = 0; + +let prevTime = performance.now(); + +function animate() { + const time = performance.now(); + const delta = time - prevTime; + + for (let i = 0; i < funfairs.length; i++) { + funfairs[i].rotation.y = time * 0.0004; + } + + // + + progress += velocity; + progress = progress % 1; + + position.copy(curve.getPointAt(progress)); + position.y += 0.3; + + train.position.copy(position); + + tangent.copy(curve.getTangentAt(progress)); + + velocity -= tangent.y * 0.0000001 * delta; + velocity = Math.max(0.00004, Math.min(0.0002, velocity)); + + train.lookAt(lookAt.copy(position).sub(tangent)); + + // + + renderer.render(scene, camera); + + prevTime = time; +} diff --git a/examples-testing/examples/webxr_ar_cones.ts b/examples-testing/examples/webxr_ar_cones.ts new file mode 100644 index 000000000..95eb34393 --- /dev/null +++ b/examples-testing/examples/webxr_ar_cones.ts @@ -0,0 +1,66 @@ +import * as THREE from 'three'; +import { ARButton } from 'three/addons/webxr/ARButton.js'; + +let camera, scene, renderer; +let controller; + +init(); + +function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 20); + + const light = new THREE.HemisphereLight(0xffffff, 0xbbbbff, 3); + light.position.set(0.5, 1, 0.25); + scene.add(light); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.xr.enabled = true; + container.appendChild(renderer.domElement); + + // + + document.body.appendChild(ARButton.createButton(renderer)); + + // + + const geometry = new THREE.CylinderGeometry(0, 0.05, 0.2, 32).rotateX(Math.PI / 2); + + function onSelect() { + const material = new THREE.MeshPhongMaterial({ color: 0xffffff * Math.random() }); + const mesh = new THREE.Mesh(geometry, material); + mesh.position.set(0, 0, -0.3).applyMatrix4(controller.matrixWorld); + mesh.quaternion.setFromRotationMatrix(controller.matrixWorld); + scene.add(mesh); + } + + controller = renderer.xr.getController(0); + controller.addEventListener('select', onSelect); + scene.add(controller); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webxr_ar_hittest.ts b/examples-testing/examples/webxr_ar_hittest.ts new file mode 100644 index 000000000..009b4b976 --- /dev/null +++ b/examples-testing/examples/webxr_ar_hittest.ts @@ -0,0 +1,119 @@ +import * as THREE from 'three'; +import { ARButton } from 'three/addons/webxr/ARButton.js'; + +let container; +let camera, scene, renderer; +let controller1, controller2; + +let reticle; + +let hitTestSource = null; +let hitTestSourceRequested = false; + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 20); + + const light = new THREE.HemisphereLight(0xffffff, 0xbbbbff, 3); + light.position.set(0.5, 1, 0.25); + scene.add(light); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.xr.enabled = true; + container.appendChild(renderer.domElement); + + // + + document.body.appendChild(ARButton.createButton(renderer, { requiredFeatures: ['hit-test'] })); + + // + + const geometry = new THREE.CylinderGeometry(0.1, 0.1, 0.2, 32).translate(0, 0.1, 0); + + function onSelect() { + if (reticle.visible) { + const material = new THREE.MeshPhongMaterial({ color: 0xffffff * Math.random() }); + const mesh = new THREE.Mesh(geometry, material); + reticle.matrix.decompose(mesh.position, mesh.quaternion, mesh.scale); + mesh.scale.y = Math.random() * 2 + 1; + scene.add(mesh); + } + } + + controller1 = renderer.xr.getController(0); + controller1.addEventListener('select', onSelect); + scene.add(controller1); + + controller2 = renderer.xr.getController(1); + controller2.addEventListener('select', onSelect); + scene.add(controller2); + + reticle = new THREE.Mesh( + new THREE.RingGeometry(0.15, 0.2, 32).rotateX(-Math.PI / 2), + new THREE.MeshBasicMaterial(), + ); + reticle.matrixAutoUpdate = false; + reticle.visible = false; + scene.add(reticle); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate(timestamp, frame) { + if (frame) { + const referenceSpace = renderer.xr.getReferenceSpace(); + const session = renderer.xr.getSession(); + + if (hitTestSourceRequested === false) { + session.requestReferenceSpace('viewer').then(function (referenceSpace) { + session.requestHitTestSource({ space: referenceSpace }).then(function (source) { + hitTestSource = source; + }); + }); + + session.addEventListener('end', function () { + hitTestSourceRequested = false; + hitTestSource = null; + }); + + hitTestSourceRequested = true; + } + + if (hitTestSource) { + const hitTestResults = frame.getHitTestResults(hitTestSource); + + if (hitTestResults.length) { + const hit = hitTestResults[0]; + + reticle.visible = true; + reticle.matrix.fromArray(hit.getPose(referenceSpace).transform.matrix); + } else { + reticle.visible = false; + } + } + } + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webxr_ar_lighting.ts b/examples-testing/examples/webxr_ar_lighting.ts new file mode 100644 index 000000000..10f49f0bf --- /dev/null +++ b/examples-testing/examples/webxr_ar_lighting.ts @@ -0,0 +1,124 @@ +import * as THREE from 'three'; +import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; +import { ARButton } from 'three/addons/webxr/ARButton.js'; +import { XREstimatedLight } from 'three/addons/webxr/XREstimatedLight.js'; + +let camera, scene, renderer; +let controller; +let defaultEnvironment; + +init(); + +function init() { + const container = document.createElement('div'); + document.body.appendChild(container); + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 20); + + const defaultLight = new THREE.HemisphereLight(0xffffff, 0xbbbbff, 1); + defaultLight.position.set(0.5, 1, 0.25); + scene.add(defaultLight); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.xr.enabled = true; + container.appendChild(renderer.domElement); + + // Don't add the XREstimatedLight to the scene initially. + // It doesn't have any estimated lighting values until an AR session starts. + + const xrLight = new XREstimatedLight(renderer); + + xrLight.addEventListener('estimationstart', () => { + // Swap the default light out for the estimated one once we start getting some estimated values. + scene.add(xrLight); + scene.remove(defaultLight); + + // The estimated lighting also provides an environment cubemap, which we can apply here. + if (xrLight.environment) { + scene.environment = xrLight.environment; + } + }); + + xrLight.addEventListener('estimationend', () => { + // Swap the lights back when we stop receiving estimated values. + scene.add(defaultLight); + scene.remove(xrLight); + + // Revert back to the default environment. + scene.environment = defaultEnvironment; + }); + + // + + new UltraHDRLoader().setPath('textures/equirectangular/').load('royal_esplanade_2k.hdr.jpg', function (texture) { + texture.mapping = THREE.EquirectangularReflectionMapping; + + defaultEnvironment = texture; + + scene.environment = defaultEnvironment; + }); + + // + + // In order for lighting estimation to work, 'light-estimation' must be included as either an optional or required feature. + document.body.appendChild(ARButton.createButton(renderer, { optionalFeatures: ['light-estimation'] })); + + // + + const ballGeometry = new THREE.SphereGeometry(0.175, 32, 32); + const ballGroup = new THREE.Group(); + ballGroup.position.z = -2; + + const rows = 3; + const cols = 3; + + for (let i = 0; i < rows; i++) { + for (let j = 0; j < cols; j++) { + const ballMaterial = new THREE.MeshStandardMaterial({ + color: 0xdddddd, + roughness: i / rows, + metalness: j / cols, + }); + const ballMesh = new THREE.Mesh(ballGeometry, ballMaterial); + ballMesh.position.set((i + 0.5 - rows * 0.5) * 0.4, (j + 0.5 - cols * 0.5) * 0.4, 0); + ballGroup.add(ballMesh); + } + } + + scene.add(ballGroup); + + // + + function onSelect() { + ballGroup.position.set(0, 0, -2).applyMatrix4(controller.matrixWorld); + ballGroup.quaternion.setFromRotationMatrix(controller.matrixWorld); + } + + controller = renderer.xr.getController(0); + controller.addEventListener('select', onSelect); + scene.add(controller); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webxr_ar_plane_detection.ts b/examples-testing/examples/webxr_ar_plane_detection.ts new file mode 100644 index 000000000..841b6b04b --- /dev/null +++ b/examples-testing/examples/webxr_ar_plane_detection.ts @@ -0,0 +1,46 @@ +import * as THREE from 'three'; +import { ARButton } from 'three/addons/webxr/ARButton.js'; +import { XRPlanes } from 'three/addons/webxr/XRPlanes.js'; + +// + +const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); +renderer.setPixelRatio(window.devicePixelRatio); +renderer.setSize(window.innerWidth, window.innerHeight); +renderer.setAnimationLoop(animate); +renderer.xr.enabled = true; +document.body.appendChild(renderer.domElement); + +document.body.appendChild( + ARButton.createButton(renderer, { + requiredFeatures: ['plane-detection'], + }), +); + +window.addEventListener('resize', onWindowResize); + +// + +const scene = new THREE.Scene(); + +const planes = new XRPlanes(renderer); +scene.add(planes); + +const camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 20); + +const light = new THREE.HemisphereLight(0xffffff, 0xbbbbff, 3); +light.position.set(0.5, 1, 0.25); +scene.add(light); + +// + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webxr_vr_handinput.ts b/examples-testing/examples/webxr_vr_handinput.ts new file mode 100644 index 000000000..d746e4582 --- /dev/null +++ b/examples-testing/examples/webxr_vr_handinput.ts @@ -0,0 +1,126 @@ +import * as THREE from 'three'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { VRButton } from 'three/addons/webxr/VRButton.js'; +import { XRControllerModelFactory } from 'three/addons/webxr/XRControllerModelFactory.js'; +import { XRHandModelFactory } from 'three/addons/webxr/XRHandModelFactory.js'; + +let container; +let camera, scene, renderer; +let hand1, hand2; +let controller1, controller2; +let controllerGrip1, controllerGrip2; + +let controls; + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x444444); + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 10); + camera.position.set(0, 1.6, 3); + + controls = new OrbitControls(camera, container); + controls.target.set(0, 1.6, 0); + controls.update(); + + const floorGeometry = new THREE.PlaneGeometry(4, 4); + const floorMaterial = new THREE.MeshStandardMaterial({ color: 0x666666 }); + const floor = new THREE.Mesh(floorGeometry, floorMaterial); + floor.rotation.x = -Math.PI / 2; + floor.receiveShadow = true; + scene.add(floor); + + scene.add(new THREE.HemisphereLight(0xbcbcbc, 0xa5a5a5, 3)); + + const light = new THREE.DirectionalLight(0xffffff, 3); + light.position.set(0, 6, 0); + light.castShadow = true; + light.shadow.camera.top = 2; + light.shadow.camera.bottom = -2; + light.shadow.camera.right = 2; + light.shadow.camera.left = -2; + light.shadow.mapSize.set(4096, 4096); + scene.add(light); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.shadowMap.enabled = true; + renderer.xr.enabled = true; + + container.appendChild(renderer.domElement); + + const sessionInit = { + requiredFeatures: ['hand-tracking'], + }; + + document.body.appendChild(VRButton.createButton(renderer, sessionInit)); + + // controllers + + controller1 = renderer.xr.getController(0); + scene.add(controller1); + + controller2 = renderer.xr.getController(1); + scene.add(controller2); + + const controllerModelFactory = new XRControllerModelFactory(); + const handModelFactory = new XRHandModelFactory(); + + // Hand 1 + controllerGrip1 = renderer.xr.getControllerGrip(0); + controllerGrip1.add(controllerModelFactory.createControllerModel(controllerGrip1)); + scene.add(controllerGrip1); + + hand1 = renderer.xr.getHand(0); + hand1.add(handModelFactory.createHandModel(hand1)); + + scene.add(hand1); + + // Hand 2 + controllerGrip2 = renderer.xr.getControllerGrip(1); + controllerGrip2.add(controllerModelFactory.createControllerModel(controllerGrip2)); + scene.add(controllerGrip2); + + hand2 = renderer.xr.getHand(1); + hand2.add(handModelFactory.createHandModel(hand2)); + scene.add(hand2); + + // + + const geometry = new THREE.BufferGeometry().setFromPoints([ + new THREE.Vector3(0, 0, 0), + new THREE.Vector3(0, 0, -1), + ]); + + const line = new THREE.Line(geometry); + line.name = 'line'; + line.scale.z = 5; + + controller1.add(line.clone()); + controller2.add(line.clone()); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} +// + +function animate() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webxr_vr_panorama.ts b/examples-testing/examples/webxr_vr_panorama.ts new file mode 100644 index 000000000..535e1c937 --- /dev/null +++ b/examples-testing/examples/webxr_vr_panorama.ts @@ -0,0 +1,92 @@ +import * as THREE from 'three'; +import { VRButton } from 'three/addons/webxr/VRButton.js'; + +let camera; +let renderer; +let scene; + +init(); + +function init() { + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.xr.enabled = true; + renderer.xr.setReferenceSpaceType('local'); + document.body.appendChild(renderer.domElement); + + document.body.appendChild(VRButton.createButton(renderer)); + + // + + scene = new THREE.Scene(); + + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000); + camera.layers.enable(1); + + const geometry = new THREE.BoxGeometry(100, 100, 100); + geometry.scale(1, 1, -1); + + const textures = getTexturesFromAtlasFile('textures/cube/sun_temple_stripe_stereo.jpg', 12); + + const materials = []; + + for (let i = 0; i < 6; i++) { + materials.push(new THREE.MeshBasicMaterial({ map: textures[i] })); + } + + const skyBox = new THREE.Mesh(geometry, materials); + skyBox.layers.set(1); + scene.add(skyBox); + + const materialsR = []; + + for (let i = 6; i < 12; i++) { + materialsR.push(new THREE.MeshBasicMaterial({ map: textures[i] })); + } + + const skyBoxR = new THREE.Mesh(geometry, materialsR); + skyBoxR.layers.set(2); + scene.add(skyBoxR); + + window.addEventListener('resize', onWindowResize); +} + +function getTexturesFromAtlasFile(atlasImgUrl, tilesNum) { + const textures = []; + + for (let i = 0; i < tilesNum; i++) { + textures[i] = new THREE.Texture(); + } + + const loader = new THREE.ImageLoader(); + loader.load(atlasImgUrl, function (imageObj) { + let canvas, context; + const tileWidth = imageObj.height; + + for (let i = 0; i < textures.length; i++) { + canvas = document.createElement('canvas'); + context = canvas.getContext('2d'); + canvas.height = tileWidth; + canvas.width = tileWidth; + context.drawImage(imageObj, tileWidth * i, 0, tileWidth, tileWidth, 0, 0, tileWidth, tileWidth); + textures[i].colorSpace = THREE.SRGBColorSpace; + textures[i].image = canvas; + textures[i].needsUpdate = true; + } + }); + + return textures; +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webxr_vr_panorama_depth.ts b/examples-testing/examples/webxr_vr_panorama_depth.ts new file mode 100644 index 000000000..ea3d76199 --- /dev/null +++ b/examples-testing/examples/webxr_vr_panorama_depth.ts @@ -0,0 +1,93 @@ +import * as THREE from 'three'; +import { VRButton } from 'three/addons/webxr/VRButton.js'; + +let camera, scene, renderer, sphere, timer; + +init(); + +function init() { + const container = document.getElementById('container'); + + timer = new THREE.Timer(); + timer.connect(document); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x101010); + + const light = new THREE.AmbientLight(0xffffff, 3); + scene.add(light); + + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 2000); + scene.add(camera); + + // Create the panoramic sphere geometry + const panoSphereGeo = new THREE.SphereGeometry(6, 256, 256); + + // Create the panoramic sphere material + const panoSphereMat = new THREE.MeshStandardMaterial({ + side: THREE.BackSide, + displacementScale: -4.0, + }); + + // Create the panoramic sphere mesh + sphere = new THREE.Mesh(panoSphereGeo, panoSphereMat); + + // Load and assign the texture and depth map + const manager = new THREE.LoadingManager(); + const loader = new THREE.TextureLoader(manager); + + loader.load('./textures/kandao3.jpg', function (texture) { + texture.colorSpace = THREE.SRGBColorSpace; + texture.minFilter = THREE.NearestFilter; + texture.generateMipmaps = false; + sphere.material.map = texture; + }); + + loader.load('./textures/kandao3_depthmap.jpg', function (depth) { + depth.minFilter = THREE.NearestFilter; + depth.generateMipmaps = false; + sphere.material.displacementMap = depth; + }); + + // On load complete add the panoramic sphere to the scene + manager.onLoad = function () { + scene.add(sphere); + }; + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.xr.enabled = true; + renderer.xr.setReferenceSpaceType('local'); + container.appendChild(renderer.domElement); + + document.body.appendChild(VRButton.createButton(renderer)); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + timer.update(); + + // If we are not presenting move the camera a little so the effect is visible + + if (renderer.xr.isPresenting === false) { + const time = timer.getElapsed(); + + sphere.rotation.y += 0.001; + sphere.position.x = Math.sin(time) * 0.2; + sphere.position.z = Math.cos(time) * 0.2; + } + + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webxr_vr_rollercoaster.ts b/examples-testing/examples/webxr_vr_rollercoaster.ts new file mode 100644 index 000000000..b8c35a9e3 --- /dev/null +++ b/examples-testing/examples/webxr_vr_rollercoaster.ts @@ -0,0 +1,211 @@ +import * as THREE from 'three'; + +import { + RollerCoasterGeometry, + RollerCoasterShadowGeometry, + RollerCoasterLiftersGeometry, + TreesGeometry, + SkyGeometry, +} from 'three/addons/misc/RollerCoaster.js'; +import { VRButton } from 'three/addons/webxr/VRButton.js'; + +let mesh, material, geometry; + +const renderer = new THREE.WebGLRenderer({ antialias: true }); +renderer.setPixelRatio(window.devicePixelRatio); +renderer.setSize(window.innerWidth, window.innerHeight); +renderer.setAnimationLoop(animate); +renderer.xr.enabled = true; +renderer.xr.setReferenceSpaceType('local'); +document.body.appendChild(renderer.domElement); + +document.body.appendChild(VRButton.createButton(renderer)); + +// + +const scene = new THREE.Scene(); +scene.background = new THREE.Color(0xf0f0ff); + +const light = new THREE.HemisphereLight(0xfff0f0, 0x60606, 3); +light.position.set(1, 1, 1); +scene.add(light); + +const train = new THREE.Object3D(); +scene.add(train); + +const camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 500); +train.add(camera); + +// environment + +geometry = new THREE.PlaneGeometry(500, 500, 15, 15); +geometry.rotateX(-Math.PI / 2); + +const positions = geometry.attributes.position.array; +const vertex = new THREE.Vector3(); + +for (let i = 0; i < positions.length; i += 3) { + vertex.fromArray(positions, i); + + vertex.x += Math.random() * 10 - 5; + vertex.z += Math.random() * 10 - 5; + + const distance = vertex.distanceTo(scene.position) / 5 - 25; + vertex.y = Math.random() * Math.max(0, distance); + + vertex.toArray(positions, i); +} + +geometry.computeVertexNormals(); + +material = new THREE.MeshLambertMaterial({ + color: 0x407000, +}); + +mesh = new THREE.Mesh(geometry, material); +scene.add(mesh); + +geometry = new TreesGeometry(mesh); +material = new THREE.MeshBasicMaterial({ + side: THREE.DoubleSide, + vertexColors: true, +}); +mesh = new THREE.Mesh(geometry, material); +scene.add(mesh); + +geometry = new SkyGeometry(); +material = new THREE.MeshBasicMaterial({ color: 0xffffff }); +mesh = new THREE.Mesh(geometry, material); +scene.add(mesh); + +// + +const PI2 = Math.PI * 2; + +const curve = (function () { + const vector = new THREE.Vector3(); + const vector2 = new THREE.Vector3(); + + return { + getPointAt: function (t) { + t = t * PI2; + + const x = Math.sin(t * 3) * Math.cos(t * 4) * 50; + const y = Math.sin(t * 10) * 2 + Math.cos(t * 17) * 2 + 5; + const z = Math.sin(t) * Math.sin(t * 4) * 50; + + return vector.set(x, y, z).multiplyScalar(2); + }, + + getTangentAt: function (t) { + const delta = 0.0001; + const t1 = Math.max(0, t - delta); + const t2 = Math.min(1, t + delta); + + return vector2.copy(this.getPointAt(t2)).sub(this.getPointAt(t1)).normalize(); + }, + }; +})(); + +geometry = new RollerCoasterGeometry(curve, 1500); +material = new THREE.MeshPhongMaterial({ + vertexColors: true, +}); +mesh = new THREE.Mesh(geometry, material); +scene.add(mesh); + +geometry = new RollerCoasterLiftersGeometry(curve, 100); +material = new THREE.MeshPhongMaterial(); +mesh = new THREE.Mesh(geometry, material); +mesh.position.y = 0.1; +scene.add(mesh); + +geometry = new RollerCoasterShadowGeometry(curve, 500); +material = new THREE.MeshBasicMaterial({ + color: 0x305000, + depthWrite: false, + transparent: true, +}); +mesh = new THREE.Mesh(geometry, material); +mesh.position.y = 0.1; +scene.add(mesh); + +const funfairs = []; + +// + +geometry = new THREE.CylinderGeometry(10, 10, 5, 15); +material = new THREE.MeshLambertMaterial({ + color: 0xff8080, +}); +mesh = new THREE.Mesh(geometry, material); +mesh.position.set(-80, 10, -70); +mesh.rotation.x = Math.PI / 2; +scene.add(mesh); + +funfairs.push(mesh); + +geometry = new THREE.CylinderGeometry(5, 6, 4, 10); +material = new THREE.MeshLambertMaterial({ + color: 0x8080ff, +}); +mesh = new THREE.Mesh(geometry, material); +mesh.position.set(50, 2, 30); +scene.add(mesh); + +funfairs.push(mesh); + +// + +window.addEventListener('resize', onWindowResize); + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +const position = new THREE.Vector3(); +const tangent = new THREE.Vector3(); + +const lookAt = new THREE.Vector3(); + +let velocity = 0; +let progress = 0; + +let prevTime = performance.now(); + +function animate() { + const time = performance.now(); + const delta = time - prevTime; + + for (let i = 0; i < funfairs.length; i++) { + funfairs[i].rotation.y = time * 0.0004; + } + + // + + progress += velocity; + progress = progress % 1; + + position.copy(curve.getPointAt(progress)); + position.y += 0.3; + + train.position.copy(position); + + tangent.copy(curve.getTangentAt(progress)); + + velocity -= tangent.y * 0.0000001 * delta; + velocity = Math.max(0.00004, Math.min(0.0002, velocity)); + + train.lookAt(lookAt.copy(position).sub(tangent)); + + // + + renderer.render(scene, camera); + + prevTime = time; +} diff --git a/examples-testing/examples/webxr_vr_sandbox.ts b/examples-testing/examples/webxr_vr_sandbox.ts new file mode 100644 index 000000000..19108d589 --- /dev/null +++ b/examples-testing/examples/webxr_vr_sandbox.ts @@ -0,0 +1,192 @@ +import * as THREE from 'three'; + +import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; +import { Reflector } from 'three/addons/objects/Reflector.js'; +import { VRButton } from 'three/addons/webxr/VRButton.js'; + +import { HTMLMesh } from 'three/addons/interactive/HTMLMesh.js'; +import { InteractiveGroup } from 'three/addons/interactive/InteractiveGroup.js'; +import { XRControllerModelFactory } from 'three/addons/webxr/XRControllerModelFactory.js'; + +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import Stats from 'three/addons/libs/stats.module.js'; + +let camera, scene, renderer; +let reflector; +let stats, statsMesh; + +const parameters = { + radius: 0.6, + tube: 0.2, + tubularSegments: 150, + radialSegments: 20, + p: 2, + q: 3, + thickness: 0.5, +}; + +init(); + +function init() { + scene = new THREE.Scene(); + + new HDRLoader().setPath('textures/equirectangular/').load('moonless_golf_1k.hdr', function (texture) { + texture.mapping = THREE.EquirectangularReflectionMapping; + + scene.background = texture; + scene.environment = texture; + }); + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 10); + camera.position.set(0, 1.6, 1.5); + + // + + const torusGeometry = new THREE.TorusKnotGeometry(...Object.values(parameters)); + const torusMaterial = new THREE.MeshPhysicalMaterial({ + transmission: 1.0, + roughness: 0, + metalness: 0.25, + thickness: 0.5, + side: THREE.DoubleSide, + }); + const torus = new THREE.Mesh(torusGeometry, torusMaterial); + torus.name = 'torus'; + torus.position.y = 1.5; + torus.position.z = -2; + scene.add(torus); + + const cylinderGeometry = new THREE.CylinderGeometry(1, 1, 0.1, 50); + const cylinderMaterial = new THREE.MeshLambertMaterial(); + const cylinder = new THREE.Mesh(cylinderGeometry, cylinderMaterial); + cylinder.position.z = -2; + scene.add(cylinder); + + // + + reflector = new Reflector(new THREE.PlaneGeometry(2, 2), { + textureWidth: window.innerWidth, + textureHeight: window.innerHeight, + }); + reflector.position.x = 1; + reflector.position.y = 1.5; + reflector.position.z = -3; + reflector.rotation.y = -Math.PI / 4; + scene.add(reflector); + + const frameGeometry = new THREE.BoxGeometry(2.1, 2.1, 0.1); + const frameMaterial = new THREE.MeshLambertMaterial({ color: 0x888888 }); + const frame = new THREE.Mesh(frameGeometry, frameMaterial); + frame.position.z = -0.07; + reflector.add(frame); + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.autoClear = false; + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.xr.enabled = true; + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = 1; + document.body.appendChild(renderer.domElement); + + document.body.appendChild(VRButton.createButton(renderer)); + + window.addEventListener('resize', onWindowResize); + + // + + const geometry = new THREE.BufferGeometry(); + geometry.setFromPoints([new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 0, -5)]); + + const controller1 = renderer.xr.getController(0); + controller1.add(new THREE.Line(geometry)); + scene.add(controller1); + + const controller2 = renderer.xr.getController(1); + controller2.add(new THREE.Line(geometry)); + scene.add(controller2); + + // + + const controllerModelFactory = new XRControllerModelFactory(); + + const controllerGrip1 = renderer.xr.getControllerGrip(0); + controllerGrip1.add(controllerModelFactory.createControllerModel(controllerGrip1)); + scene.add(controllerGrip1); + + const controllerGrip2 = renderer.xr.getControllerGrip(1); + controllerGrip2.add(controllerModelFactory.createControllerModel(controllerGrip2)); + scene.add(controllerGrip2); + + // GUI + + function onChange() { + torus.geometry.dispose(); + torus.geometry = new THREE.TorusKnotGeometry(...Object.values(parameters)); + } + + function onThicknessChange() { + torus.material.thickness = parameters.thickness; + } + + const gui = new GUI({ width: 300 }); + gui.add(parameters, 'radius', 0.0, 1.0).onChange(onChange); + gui.add(parameters, 'tube', 0.0, 1.0).onChange(onChange); + gui.add(parameters, 'tubularSegments', 10, 150, 1).onChange(onChange); + gui.add(parameters, 'radialSegments', 2, 20, 1).onChange(onChange); + gui.add(parameters, 'p', 1, 10, 1).onChange(onChange); + gui.add(parameters, 'q', 0, 10, 1).onChange(onChange); + gui.add(parameters, 'thickness', 0, 1).onChange(onThicknessChange); + gui.domElement.style.visibility = 'hidden'; + + const group = new InteractiveGroup(); + group.listenToPointerEvents(renderer, camera); + group.listenToXRControllerEvents(controller1); + group.listenToXRControllerEvents(controller2); + scene.add(group); + + const mesh = new HTMLMesh(gui.domElement); + mesh.position.x = -0.75; + mesh.position.y = 1.5; + mesh.position.z = -0.5; + mesh.rotation.y = Math.PI / 4; + mesh.scale.setScalar(2); + group.add(mesh); + + // Add stats.js + stats = new Stats(); + stats.dom.style.width = '80px'; + stats.dom.style.height = '48px'; + document.body.appendChild(stats.dom); + + statsMesh = new HTMLMesh(stats.dom); + statsMesh.position.x = -0.75; + statsMesh.position.y = 2; + statsMesh.position.z = -0.6; + statsMesh.rotation.y = Math.PI / 4; + statsMesh.scale.setScalar(2.5); + group.add(statsMesh); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + const time = performance.now() * 0.0002; + const torus = scene.getObjectByName('torus'); + torus.rotation.x = time * 0.4; + torus.rotation.y = time; + + renderer.render(scene, camera); + stats.update(); + + // Canvas elements doesn't trigger DOM updates, so we have to update the texture + statsMesh.material.map.update(); +} diff --git a/examples-testing/examples/webxr_vr_video.ts b/examples-testing/examples/webxr_vr_video.ts new file mode 100644 index 000000000..50a990412 --- /dev/null +++ b/examples-testing/examples/webxr_vr_video.ts @@ -0,0 +1,92 @@ +import * as THREE from 'three'; +import { VRButton } from 'three/addons/webxr/VRButton.js'; + +let camera, scene, renderer; + +init(); + +function init() { + const container = document.getElementById('container'); + container.addEventListener('click', function () { + video.play(); + }); + + camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 2000); + camera.layers.enable(1); // render left view when no stereo available + + // video + + const video = document.getElementById('video'); + video.play(); + + const texture = new THREE.VideoTexture(video); + texture.colorSpace = THREE.SRGBColorSpace; + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x101010); + + // left + + const geometry1 = new THREE.SphereGeometry(500, 60, 40); + // invert the geometry on the x-axis so that all of the faces point inward + geometry1.scale(-1, 1, 1); + + const uvs1 = geometry1.attributes.uv.array; + + for (let i = 0; i < uvs1.length; i += 2) { + uvs1[i] *= 0.5; + } + + const material1 = new THREE.MeshBasicMaterial({ map: texture }); + + const mesh1 = new THREE.Mesh(geometry1, material1); + mesh1.rotation.y = -Math.PI / 2; + mesh1.layers.set(1); // display in left eye only + scene.add(mesh1); + + // right + + const geometry2 = new THREE.SphereGeometry(500, 60, 40); + geometry2.scale(-1, 1, 1); + + const uvs2 = geometry2.attributes.uv.array; + + for (let i = 0; i < uvs2.length; i += 2) { + uvs2[i] *= 0.5; + uvs2[i] += 0.5; + } + + const material2 = new THREE.MeshBasicMaterial({ map: texture }); + + const mesh2 = new THREE.Mesh(geometry2, material2); + mesh2.rotation.y = -Math.PI / 2; + mesh2.layers.set(2); // display in right eye only + scene.add(mesh2); + + // + + renderer = new THREE.WebGLRenderer(); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.xr.enabled = true; + renderer.xr.setReferenceSpaceType('local'); + container.appendChild(renderer.domElement); + + document.body.appendChild(VRButton.createButton(renderer)); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webxr_xr_controls_transform.ts b/examples-testing/examples/webxr_xr_controls_transform.ts new file mode 100644 index 000000000..cd9b1290d --- /dev/null +++ b/examples-testing/examples/webxr_xr_controls_transform.ts @@ -0,0 +1,218 @@ +import * as THREE from 'three'; +import { TransformControls } from 'three/addons/controls/TransformControls.js'; +import { XRButton } from 'three/addons/webxr/XRButton.js'; +import { XRControllerModelFactory } from 'three/addons/webxr/XRControllerModelFactory.js'; +import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js'; + +let container; +let camera, scene, renderer; +let bloomPass; +let controller1, controller2, line; +let controllerGrip1, controllerGrip2; + +let raycaster; + +let controls, group; + +init(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x808080); + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 10); + camera.position.set(0, 1.6, 0); + + const floorGeometry = new THREE.PlaneGeometry(6, 6); + const floorMaterial = new THREE.ShadowMaterial({ + opacity: 0.25, + blending: THREE.CustomBlending, + transparent: false, + }); + const floor = new THREE.Mesh(floorGeometry, floorMaterial); + floor.rotation.x = -Math.PI / 2; + floor.receiveShadow = true; + scene.add(floor); + + scene.add(new THREE.HemisphereLight(0xbcbcbc, 0xa5a5a5, 3)); + + const light = new THREE.DirectionalLight(0xffffff, 3); + light.position.set(0, 6, 0); + light.castShadow = true; + light.shadow.camera.top = 3; + light.shadow.camera.bottom = -3; + light.shadow.camera.right = 3; + light.shadow.camera.left = -3; + light.shadow.mapSize.set(4096, 4096); + scene.add(light); + + group = new THREE.Group(); + scene.add(group); + + const geometries = [ + new THREE.BoxGeometry(0.2, 0.2, 0.2), + new THREE.ConeGeometry(0.2, 0.4, 64), + new THREE.CylinderGeometry(0.2, 0.2, 0.2, 64), + new THREE.IcosahedronGeometry(0.2, 8), + new THREE.TorusGeometry(0.2, 0.04, 64, 32), + ]; + + for (let i = 0; i < 16; i++) { + const geometry = geometries[Math.floor(Math.random() * geometries.length)]; + const material = new THREE.MeshStandardMaterial({ + color: Math.random() * 0xffffff, + roughness: 0.7, + metalness: 0.0, + }); + + const object = new THREE.Mesh(geometry, material); + + object.position.x = Math.random() - 0.5; + object.position.y = Math.random() * 2 + 0.5; + object.position.z = Math.random() - 2.5; + + object.rotation.x = Math.random() * 2 * Math.PI; + object.rotation.y = Math.random() * 2 * Math.PI; + object.rotation.z = Math.random() * 2 * Math.PI; + + object.scale.setScalar(Math.random() + 0.5); + + object.castShadow = true; + object.receiveShadow = true; + + group.add(object); + } + + // + + renderer = new THREE.WebGLRenderer({ antialias: true, outputBufferType: THREE.HalfFloatType }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setAnimationLoop(animate); + renderer.shadowMap.enabled = true; + renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.xr.enabled = true; + container.appendChild(renderer.domElement); + + // post-processing + + bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85); + renderer.setEffects([bloomPass]); + + document.body.appendChild(XRButton.createButton(renderer)); + + // controllers + + controller1 = renderer.xr.getController(0); + controller1.addEventListener('select', onSelect); + controller1.addEventListener('selectstart', onControllerEvent); + controller1.addEventListener('selectend', onControllerEvent); + controller1.addEventListener('move', onControllerEvent); + controller1.userData.active = false; + scene.add(controller1); + + controller2 = renderer.xr.getController(1); + controller2.addEventListener('select', onSelect); + controller2.addEventListener('selectstart', onControllerEvent); + controller2.addEventListener('selectend', onControllerEvent); + controller2.addEventListener('move', onControllerEvent); + controller2.userData.active = true; + scene.add(controller2); + + const controllerModelFactory = new XRControllerModelFactory(); + + controllerGrip1 = renderer.xr.getControllerGrip(0); + controllerGrip1.add(controllerModelFactory.createControllerModel(controllerGrip1)); + scene.add(controllerGrip1); + + controllerGrip2 = renderer.xr.getControllerGrip(1); + controllerGrip2.add(controllerModelFactory.createControllerModel(controllerGrip2)); + scene.add(controllerGrip2); + + // + + const geometry = new THREE.BufferGeometry().setFromPoints([ + new THREE.Vector3(0, 0, 0), + new THREE.Vector3(0, 0, -1), + ]); + + line = new THREE.Line(geometry); + line.name = 'line'; + line.scale.z = 5; + + raycaster = new THREE.Raycaster(); + + // controls + + controls = new TransformControls(camera, renderer.domElement); + controls.attach(group.children[0]); + scene.add(controls.getHelper()); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onSelect(event) { + const controller = event.target; + + controller1.userData.active = false; + controller2.userData.active = false; + + if (controller === controller1) { + controller1.userData.active = true; + controller1.add(line); + } + + if (controller === controller2) { + controller2.userData.active = true; + controller2.add(line); + } + + raycaster.setFromXRController(controller); + + const intersects = raycaster.intersectObjects(group.children); + + if (intersects.length > 0) { + controls.attach(intersects[0].object); + } +} + +function onControllerEvent(event) { + const controller = event.target; + + if (controller.userData.active === false) return; + + controls.getRaycaster().setFromXRController(controller); + + switch (event.type) { + case 'selectstart': + controls.pointerDown(null); + break; + + case 'selectend': + controls.pointerUp(null); + break; + + case 'move': + controls.pointerHover(null); + controls.pointerMove(null); + break; + } +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// + +function animate() { + renderer.render(scene, camera); +} diff --git a/examples-testing/examples/webxr_xr_dragging_custom_depth.ts b/examples-testing/examples/webxr_xr_dragging_custom_depth.ts new file mode 100644 index 000000000..2cd50ba4c --- /dev/null +++ b/examples-testing/examples/webxr_xr_dragging_custom_depth.ts @@ -0,0 +1,395 @@ +import * as THREE from 'three'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { XRButton } from 'three/addons/webxr/XRButton.js'; +import { XRControllerModelFactory } from 'three/addons/webxr/XRControllerModelFactory.js'; + +let container; +let camera, scene, renderer; +let controller1, controller2; +let controllerGrip1, controllerGrip2; +let isDepthSupplied = false; + +let raycaster; + +const intersected = []; +const tempMatrix = new THREE.Matrix4(); + +let controls, group; + +init(); +animate(); + +function init() { + container = document.createElement('div'); + document.body.appendChild(container); + + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x808080); + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 10); + camera.position.set(0, 1.6, 3); + + controls = new OrbitControls(camera, container); + controls.target.set(0, 1.6, 0); + controls.update(); + + const floorGeometry = new THREE.PlaneGeometry(6, 6); + const floorMaterial = new THREE.ShadowMaterial({ + opacity: 0.25, + blending: THREE.CustomBlending, + transparent: false, + }); + const floor = new THREE.Mesh(floorGeometry, floorMaterial); + floor.rotation.x = -Math.PI / 2; + floor.receiveShadow = true; + scene.add(floor); + + scene.add(new THREE.HemisphereLight(0xbcbcbc, 0xa5a5a5, 3)); + + const light = new THREE.DirectionalLight(0xffffff, 3); + light.position.set(0, 6, 0); + light.castShadow = true; + light.shadow.camera.top = 3; + light.shadow.camera.bottom = -3; + light.shadow.camera.right = 3; + light.shadow.camera.left = -3; + light.shadow.mapSize.set(4096, 4096); + scene.add(light); + + group = new THREE.Group(); + scene.add(group); + + const geometries = [ + new THREE.BoxGeometry(0.2, 0.2, 0.2), + new THREE.ConeGeometry(0.2, 0.2, 64), + new THREE.CylinderGeometry(0.2, 0.2, 0.2, 64), + new THREE.IcosahedronGeometry(0.2, 8), + new THREE.TorusGeometry(0.2, 0.04, 64, 32), + ]; + + for (let i = 0; i < 50; i++) { + const geometry = geometries[Math.floor(Math.random() * geometries.length)]; + const material = new THREE.ShaderMaterial({ + vertexShader: /* glsl */ ` + varying vec3 vNormal; + varying vec2 vUv; + void main() { + vNormal = normalize(normalMatrix * normal); + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); + } + `, + + fragmentShader: /* glsl */ ` + uniform vec3 diffuseColor; + uniform float roughness; + uniform float metalness; + uniform float emissive; + varying vec3 vNormal; + varying vec2 vUv; + uniform sampler2DArray depthColor; + uniform float depthWidth; + uniform float depthHeight; + #define saturate( a ) clamp( a, 0.0, 1.0 ) + float Depth_GetCameraDepthInMeters(const sampler2DArray depthTexture, + const vec2 depthUv, int arrayIndex) { + return texture(depthColor, vec3(depthUv.x, depthUv.y, arrayIndex)).r; + } + float Depth_GetOcclusion(const sampler2DArray depthTexture, const vec2 depthUv, float assetDepthM, int arrayIndex) { + float depthMm = Depth_GetCameraDepthInMeters(depthTexture, depthUv, arrayIndex); + const float kDepthTolerancePerM = 0.001; + return clamp(1.0 - + 0.5 * (depthMm - assetDepthM) / + (kDepthTolerancePerM * assetDepthM) + + 0.5, 0.0, 1.0); + } + float Depth_GetBlurredOcclusionAroundUV(const sampler2DArray depthTexture, const vec2 uv, float assetDepthM, int arrayIndex) { + // Kernel used: + // 0 4 7 4 0 + // 4 16 26 16 4 + // 7 26 41 26 7 + // 4 16 26 16 4 + // 0 4 7 4 0 + const float kKernelTotalWeights = 269.0; + float sum = 0.0; + const float kOcclusionBlurAmount = 0.0005; + vec2 blurriness = + vec2(kOcclusionBlurAmount, kOcclusionBlurAmount /** u_DepthAspectRatio*/); + float current = 0.0; + current += Depth_GetOcclusion( + depthTexture, uv + vec2(-1.0, -2.0) * blurriness, assetDepthM, arrayIndex); + current += Depth_GetOcclusion( + depthTexture, uv + vec2(+1.0, -2.0) * blurriness, assetDepthM, arrayIndex); + current += Depth_GetOcclusion( + depthTexture, uv + vec2(-1.0, +2.0) * blurriness, assetDepthM, arrayIndex); + current += Depth_GetOcclusion( + depthTexture, uv + vec2(+1.0, +2.0) * blurriness, assetDepthM, arrayIndex); + current += Depth_GetOcclusion( + depthTexture, uv + vec2(-2.0, +1.0) * blurriness, assetDepthM, arrayIndex); + current += Depth_GetOcclusion( + depthTexture, uv + vec2(+2.0, +1.0) * blurriness, assetDepthM, arrayIndex); + current += Depth_GetOcclusion( + depthTexture, uv + vec2(-2.0, -1.0) * blurriness, assetDepthM, arrayIndex); + current += Depth_GetOcclusion( + depthTexture, uv + vec2(+2.0, -1.0) * blurriness, assetDepthM, arrayIndex); + sum += current * 4.0; + current = 0.0; + current += Depth_GetOcclusion( + depthTexture, uv + vec2(-2.0, -0.0) * blurriness, assetDepthM, arrayIndex); + current += Depth_GetOcclusion( + depthTexture, uv + vec2(+2.0, +0.0) * blurriness, assetDepthM, arrayIndex); + current += Depth_GetOcclusion( + depthTexture, uv + vec2(+0.0, +2.0) * blurriness, assetDepthM, arrayIndex); + current += Depth_GetOcclusion( + depthTexture, uv + vec2(-0.0, -2.0) * blurriness, assetDepthM, arrayIndex); + sum += current * 7.0; + current = 0.0; + current += Depth_GetOcclusion( + depthTexture, uv + vec2(-1.0, -1.0) * blurriness, assetDepthM, arrayIndex); + current += Depth_GetOcclusion( + depthTexture, uv + vec2(+1.0, -1.0) * blurriness, assetDepthM, arrayIndex); + current += Depth_GetOcclusion( + depthTexture, uv + vec2(-1.0, +1.0) * blurriness, assetDepthM, arrayIndex); + current += Depth_GetOcclusion( + depthTexture, uv + vec2(+1.0, +1.0) * blurriness, assetDepthM, arrayIndex); + sum += current * 16.0; + current = 0.0; + current += Depth_GetOcclusion( + depthTexture, uv + vec2(+0.0, +1.0) * blurriness, assetDepthM, arrayIndex); + current += Depth_GetOcclusion( + depthTexture, uv + vec2(-0.0, -1.0) * blurriness, assetDepthM, arrayIndex); + current += Depth_GetOcclusion( + depthTexture, uv + vec2(-1.0, -0.0) * blurriness, assetDepthM, arrayIndex); + current += Depth_GetOcclusion( + depthTexture, uv + vec2(+1.0, +0.0) * blurriness, assetDepthM, arrayIndex); + sum += current * 26.0; + sum += Depth_GetOcclusion(depthTexture, uv, assetDepthM, arrayIndex) * 41.0; + return sum / kKernelTotalWeights; + } + void main() { + vec3 normal = normalize(vNormal); + vec3 diffuse = diffuseColor; + float specularIntensity = pow(max(dot(normal, normalize(vec3(0, 0, 1))), 0.0), 64.0); + vec3 specular = vec3(specularIntensity) * mix(vec3(0.04), diffuse, metalness); + gl_FragColor = vec4(diffuse * (1.0 - specular) + specular, 1.0) * (1.0 + emissive); + + if (depthWidth > 0.0) { + int arrayIndex = 0; + vec2 depthUv; + if (gl_FragCoord.x>=depthWidth) { + arrayIndex = 1; + depthUv = vec2((gl_FragCoord.x-depthWidth)/depthWidth, gl_FragCoord.y/depthHeight); + } else { + depthUv = vec2(gl_FragCoord.x/depthWidth, gl_FragCoord.y/depthHeight); + } + float assetDepthM = gl_FragCoord.z; + + float occlusion = Depth_GetBlurredOcclusionAroundUV(depthColor, depthUv, assetDepthM, arrayIndex); + float depthMm = Depth_GetCameraDepthInMeters(depthColor, depthUv, arrayIndex); + + float absDistance = abs(assetDepthM - depthMm); + float v = 0.0025; + absDistance = saturate(v - absDistance) / v; + + gl_FragColor.rgb += vec3(absDistance * 2.0, absDistance * 2.0, absDistance * 12.0); + gl_FragColor = mix(gl_FragColor, vec4(0.0, 0.0, 0.0, 0.0), occlusion * 0.7); + } + } + `, + + uniforms: { + diffuseColor: { value: new THREE.Color(Math.random() * 0xffffff) }, + roughness: { value: 0.7 }, + metalness: { value: 0.0 }, + emissive: { value: 0.0 }, + depthWidth: { value: 0.0 }, + depthHeight: { value: 0.0 }, + depthColor: { value: new THREE.Texture() }, + }, + }); + + const object = new THREE.Mesh(geometry, material); + + object.position.x = Math.random() * 4 - 2; + object.position.y = Math.random() * 2; + object.position.z = Math.random() * 4 - 2; + + object.rotation.x = Math.random() * 2 * Math.PI; + object.rotation.y = Math.random() * 2 * Math.PI; + object.rotation.z = Math.random() * 2 * Math.PI; + + object.scale.setScalar(Math.random() + 0.5); + + group.add(object); + } + + // + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.shadowMap.enabled = true; + renderer.xr.enabled = true; + container.appendChild(renderer.domElement); + + document.body.appendChild( + XRButton.createButton(renderer, { + optionalFeatures: ['depth-sensing'], + depthSensing: { usagePreference: ['gpu-optimized'], dataFormatPreference: [] }, + }), + ); + + // controllers + + controller1 = renderer.xr.getController(0); + controller1.addEventListener('selectstart', onSelectStart); + controller1.addEventListener('selectend', onSelectEnd); + scene.add(controller1); + + controller2 = renderer.xr.getController(1); + controller2.addEventListener('selectstart', onSelectStart); + controller2.addEventListener('selectend', onSelectEnd); + scene.add(controller2); + + const controllerModelFactory = new XRControllerModelFactory(); + + controllerGrip1 = renderer.xr.getControllerGrip(0); + controllerGrip1.add(controllerModelFactory.createControllerModel(controllerGrip1)); + scene.add(controllerGrip1); + + controllerGrip2 = renderer.xr.getControllerGrip(1); + controllerGrip2.add(controllerModelFactory.createControllerModel(controllerGrip2)); + scene.add(controllerGrip2); + + // + + const geometry = new THREE.BufferGeometry().setFromPoints([ + new THREE.Vector3(0, 0, 0), + new THREE.Vector3(0, 0, -1), + ]); + + const line = new THREE.Line(geometry); + line.name = 'line'; + line.scale.z = 5; + + controller1.add(line.clone()); + controller2.add(line.clone()); + + raycaster = new THREE.Raycaster(); + + // + + window.addEventListener('resize', onWindowResize); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function onSelectStart(event) { + const controller = event.target; + + const intersections = getIntersections(controller); + + if (intersections.length > 0) { + const intersection = intersections[0]; + + const object = intersection.object; + object.material.uniforms.emissive.value = 1; + controller.attach(object); + + controller.userData.selected = object; + } + + controller.userData.targetRayMode = event.data.targetRayMode; +} + +function onSelectEnd(event) { + const controller = event.target; + + if (controller.userData.selected !== undefined) { + const object = controller.userData.selected; + object.material.uniforms.emissive.value = 0; + group.attach(object); + + controller.userData.selected = undefined; + } +} + +function getIntersections(controller) { + controller.updateMatrixWorld(); + + tempMatrix.identity().extractRotation(controller.matrixWorld); + + raycaster.ray.origin.setFromMatrixPosition(controller.matrixWorld); + raycaster.ray.direction.set(0, 0, -1).applyMatrix4(tempMatrix); + + return raycaster.intersectObjects(group.children, false); +} + +function intersectObjects(controller) { + // Do not highlight in mobile-ar + + if (controller.userData.targetRayMode === 'screen') return; + + // Do not highlight when already selected + + if (controller.userData.selected !== undefined) return; + + const line = controller.getObjectByName('line'); + const intersections = getIntersections(controller); + + if (intersections.length > 0) { + const intersection = intersections[0]; + + const object = intersection.object; + object.material.uniforms.emissive.value = 1; + intersected.push(object); + + line.scale.z = intersection.distance; + } else { + line.scale.z = 5; + } +} + +function cleanIntersected() { + while (intersected.length) { + const object = intersected.pop(); + object.material.uniforms.emissive.value = 0; + } +} + +// + +function animate() { + renderer.setAnimationLoop(render); +} + +function render() { + if (renderer.xr.hasDepthSensing() && !isDepthSupplied) { + group.children.forEach(child => { + child.material.uniforms.depthColor.value = renderer.xr.getDepthTexture(); + child.material.uniforms.depthWidth.value = 1680; + child.material.uniforms.depthHeight.value = 1760; + + isDepthSupplied = true; + }); + } else if (!renderer.xr.hasDepthSensing() && isDepthSupplied) { + group.children.forEach(child => { + child.material.uniforms.depthWidth.value = 0; + child.material.uniforms.depthHeight.value = 0; + + isDepthSupplied = false; + }); + } + + cleanIntersected(); + + intersectObjects(controller1); + intersectObjects(controller2); + + renderer.render(scene, camera); +} From 76d5482cc1e6506bb5cdb64f018009a8c9330125 Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Mon, 30 Mar 2026 22:01:00 -0400 Subject: [PATCH 4/7] Update --- examples-testing/package.json | 1 + pnpm-lock.yaml | 15 +++++++++++++++ renovate.json | 5 +++++ 3 files changed, 21 insertions(+) diff --git a/examples-testing/package.json b/examples-testing/package.json index 1738698c6..a4ce8eb66 100644 --- a/examples-testing/package.json +++ b/examples-testing/package.json @@ -15,6 +15,7 @@ "dependencies": { "@needle-tools/gltf-progressive": "~3.2.0", "@needle-tools/three-animation-pointer": "~1.0.7", + "@perplexdotgg/bounce": "~1.8.0", "@types/three": "file:../types/three", "3d-tiles-renderer": "~0.4.21", "postprocessing": "~6.38.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cb32b5551..853b3b5bc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -59,6 +59,9 @@ importers: '@needle-tools/three-animation-pointer': specifier: ~1.0.7 version: 1.0.7(three@0.183.1) + '@perplexdotgg/bounce': + specifier: ~1.8.0 + version: 1.8.0 '@types/three': specifier: file:../types/three version: link:../types/three @@ -621,6 +624,9 @@ packages: '@octokit/types@13.10.0': resolution: {integrity: sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==} + '@perplexdotgg/bounce@1.8.0': + resolution: {integrity: sha512-UZIuTVGXhxgxMQsF1ekeWAR8c0z3qFNHrEdRIWe7Bmh34tN+RYrMaPmIwONd+WHOA78YosARb13gjKF5bgiT6Q==} + '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} @@ -1787,6 +1793,9 @@ packages: resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} engines: {node: '>= 18'} + monomorph@2.3.1: + resolution: {integrity: sha512-kACbfiM0x7viZ00Ofgwy9HmElqqqY21w0S45hbH+W6BQUFhvs9mHIAmKEgBKo5hF5w4OUgRtABRgftDDVc5zFw==} + mrmime@2.0.1: resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} engines: {node: '>=10'} @@ -2999,6 +3008,10 @@ snapshots: dependencies: '@octokit/openapi-types': 24.2.0 + '@perplexdotgg/bounce@1.8.0': + dependencies: + monomorph: 2.3.1 + '@polka/url@1.0.0-next.29': {} '@rollup/rollup-android-arm-eabi@4.58.0': @@ -4305,6 +4318,8 @@ snapshots: dependencies: minipass: 7.1.2 + monomorph@2.3.1: {} + mrmime@2.0.1: {} ms@2.1.3: {} diff --git a/renovate.json b/renovate.json index 38ff676a1..7a72fe212 100644 --- a/renovate.json +++ b/renovate.json @@ -40,6 +40,11 @@ "matchUpdateTypes": ["major", "minor"], "enabled": false }, + { + "matchPackageNames": ["@perplexdotgg/bounce"], + "matchUpdateTypes": ["major", "minor"], + "enabled": false + }, { "matchPackageNames": ["3d-tiles-renderer"], "matchUpdateTypes": ["major", "minor"], From 709740adc8328498c01209563e295d4699533eca Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Mon, 30 Mar 2026 22:09:40 -0400 Subject: [PATCH 5/7] Update --- examples-testing/package.json | 1 + pnpm-lock.yaml | 3 +++ 2 files changed, 4 insertions(+) diff --git a/examples-testing/package.json b/examples-testing/package.json index a4ce8eb66..0560b0da7 100644 --- a/examples-testing/package.json +++ b/examples-testing/package.json @@ -18,6 +18,7 @@ "@perplexdotgg/bounce": "~1.8.0", "@types/three": "file:../types/three", "3d-tiles-renderer": "~0.4.21", + "monomorph": "^2.3.1", "postprocessing": "~6.38.3", "prettier": "^3.8.1", "stats-gl": "^3.8.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 853b3b5bc..a2bd22ae0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -65,6 +65,9 @@ importers: '@types/three': specifier: file:../types/three version: link:../types/three + monomorph: + specifier: ^2.3.1 + version: 2.3.1 postprocessing: specifier: ~6.38.3 version: 6.38.3(three@0.183.1) From 0816dfae39ba71c0eed859e23d583e7c49f9bbf2 Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Mon, 30 Mar 2026 22:12:31 -0400 Subject: [PATCH 6/7] Update patch and delete examples --- examples-testing/changes.patch | 1107 +++++++++-------- examples-testing/examples/css2d_label.ts | 189 --- examples-testing/examples/css3d_mixed.ts | 138 -- examples-testing/examples/css3d_molecules.ts | 353 ------ .../examples/css3d_orthographic.ts | 208 ---- .../examples/css3d_periodictable.ts | 793 ------------ examples-testing/examples/css3d_sandbox.ts | 180 --- examples-testing/examples/css3d_sprites.ts | 157 --- examples-testing/examples/css3d_youtube.ts | 79 -- examples-testing/examples/games_fps.ts | 377 ------ .../examples/misc_animation_groups.ts | 128 -- .../examples/misc_animation_keys.ts | 132 -- .../examples/misc_boxselection.ts | 137 -- .../examples/misc_controls_arcball.ts | 211 ---- .../examples/misc_controls_drag.ts | 153 --- .../examples/misc_controls_fly.ts | 218 ---- .../examples/misc_controls_map.ts | 102 -- .../examples/misc_controls_orbit.ts | 95 -- .../examples/misc_controls_pointerlock.ts | 245 ---- .../examples/misc_controls_trackball.ts | 138 -- .../examples/misc_controls_transform.ts | 182 --- .../examples/misc_exporter_draco.ts | 117 -- .../examples/misc_exporter_exr.ts | 158 --- .../examples/misc_exporter_gltf.ts | 534 -------- .../examples/misc_exporter_ktx2.ts | 145 --- .../examples/misc_exporter_obj.ts | 192 --- .../examples/misc_exporter_ply.ts | 156 --- .../examples/misc_exporter_stl.ts | 129 -- .../examples/misc_exporter_usdz.ts | 130 -- examples-testing/examples/misc_uv_tests.ts | 44 - .../examples/physics_ammo_instancing.ts | 132 -- .../examples/physics_jolt_instancing.ts | 133 -- .../examples/physics_rapier_basic.ts | 144 --- .../physics_rapier_character_controller.ts | 187 --- .../examples/physics_rapier_instancing.ts | 142 --- .../examples/physics_rapier_joints.ts | 130 -- .../examples/physics_rapier_terrain.ts | 295 ----- .../physics_rapier_vehicle_controller.ts | 297 ----- examples-testing/examples/svg_lines.ts | 87 -- examples-testing/examples/svg_sandbox.ts | 212 ---- .../examples/webaudio_orientation.ts | 141 --- examples-testing/examples/webaudio_sandbox.ts | 223 ---- examples-testing/examples/webaudio_timing.ts | 155 --- .../examples/webaudio_visualizer.ts | 86 -- .../examples/webgl_animation_keyframes.ts | 96 -- .../examples/webgl_animation_multiple.ts | 200 --- .../webgl_animation_skinning_morph.ts | 190 --- .../examples/webgl_animation_walk.ts | 379 ------ .../examples/webgl_buffergeometry.ts | 178 --- ...webgl_buffergeometry_attributes_integer.ts | 142 --- .../webgl_buffergeometry_attributes_none.ts | 56 - ...fergeometry_custom_attributes_particles.ts | 103 -- .../webgl_buffergeometry_drawrange.ts | 239 ---- .../webgl_buffergeometry_glbufferattribute.ts | 140 --- .../examples/webgl_buffergeometry_indexed.ts | 137 -- .../webgl_buffergeometry_instancing.ts | 138 -- ...gl_buffergeometry_instancing_billboards.ts | 86 -- ...l_buffergeometry_instancing_interleaved.ts | 152 --- .../examples/webgl_buffergeometry_lines.ts | 121 -- .../webgl_buffergeometry_lines_indexed.ts | 179 --- .../examples/webgl_buffergeometry_points.ts | 109 -- ...webgl_buffergeometry_points_interleaved.ts | 122 -- .../webgl_buffergeometry_rawshader.ts | 97 -- .../webgl_buffergeometry_selective_draw.ts | 150 --- .../examples/webgl_buffergeometry_uint.ts | 177 --- examples-testing/examples/webgl_camera.ts | 218 ---- .../examples/webgl_camera_array.ts | 104 -- .../webgl_camera_logarithmicdepthbuffer.ts | 248 ---- .../examples/webgl_clipculldistance.ts | 113 -- examples-testing/examples/webgl_clipping.ts | 195 --- .../examples/webgl_clipping_advanced.ts | 357 ------ .../examples/webgl_clipping_intersection.ts | 137 -- .../examples/webgl_clipping_stencil.ts | 263 ---- .../examples/webgl_custom_attributes.ts | 100 -- .../examples/webgl_custom_attributes_lines.ts | 121 -- .../webgl_custom_attributes_points.ts | 117 -- .../webgl_custom_attributes_points2.ts | 193 --- .../webgl_custom_attributes_points3.ts | 200 --- examples-testing/examples/webgl_decals.ts | 240 ---- .../examples/webgl_effects_anaglyph.ts | 120 -- .../examples/webgl_effects_ascii.ts | 81 -- .../examples/webgl_effects_parallaxbarrier.ts | 110 -- .../examples/webgl_effects_stereo.ts | 98 -- .../examples/webgl_framebuffer_texture.ts | 151 --- .../examples/webgl_furnace_test.ts | 96 -- examples-testing/examples/webgl_geometries.ts | 165 --- .../examples/webgl_geometry_colors.ts | 177 --- .../webgl_geometry_colors_lookuptable.ts | 148 --- .../examples/webgl_geometry_convex.ts | 117 -- .../examples/webgl_geometry_cube.ts | 46 - .../examples/webgl_geometry_extrude_shapes.ts | 159 --- .../webgl_geometry_extrude_splines.ts | 310 ----- .../examples/webgl_geometry_minecraft.ts | 184 --- .../examples/webgl_geometry_nurbs.ts | 298 ----- .../examples/webgl_geometry_shapes.ts | 363 ------ .../examples/webgl_geometry_teapot.ts | 185 --- .../examples/webgl_geometry_terrain.ts | 174 --- .../webgl_geometry_terrain_raycast.ts | 206 --- .../examples/webgl_geometry_text.ts | 312 ----- .../examples/webgl_geometry_text_shapes.ts | 112 -- .../examples/webgl_geometry_text_stroke.ts | 152 --- .../examples/webgl_gpgpu_birds.ts | 313 ----- .../examples/webgl_gpgpu_birds_gltf.ts | 413 ------ .../examples/webgl_gpgpu_protoplanet.ts | 280 ----- .../examples/webgl_gpgpu_water.ts | 583 --------- examples-testing/examples/webgl_helpers.ts | 117 -- .../examples/webgl_instancing_dynamic.ts | 175 --- .../examples/webgl_instancing_morph.ts | 150 --- .../examples/webgl_instancing_performance.ts | 262 ---- .../examples/webgl_instancing_raycast.ts | 116 -- .../examples/webgl_instancing_scatter.ts | 257 ---- .../webgl_interactive_buffergeometry.ts | 244 ---- .../examples/webgl_interactive_cubes.ts | 114 -- .../examples/webgl_interactive_cubes_gpu.ts | 231 ---- .../examples/webgl_interactive_cubes_ortho.ts | 129 -- .../examples/webgl_interactive_lines.ts | 160 --- .../examples/webgl_interactive_points.ts | 143 --- .../webgl_interactive_raycasting_points.ts | 223 ---- .../webgl_interactive_voxelpainter.ts | 158 --- examples-testing/examples/webgl_lensflares.ts | 140 --- examples-testing/examples/webgl_lightprobe.ts | 142 --- .../examples/webgl_lightprobe_cubecamera.ts | 85 -- .../examples/webgl_lights_hemisphere.ts | 191 --- .../examples/webgl_lights_physical.ts | 237 ---- .../examples/webgl_lights_rectarealight.ts | 110 -- .../examples/webgl_lights_spotlight.ts | 202 --- .../examples/webgl_lights_spotlights.ts | 133 -- .../examples/webgl_lines_colors.ts | 181 --- .../examples/webgl_lines_dashed.ts | 186 --- examples-testing/examples/webgl_lines_fat.ts | 235 ---- .../examples/webgl_lines_fat_raycasting.ts | 280 ----- .../examples/webgl_lines_fat_wireframe.ts | 210 ---- examples-testing/examples/webgl_loader_3dm.ts | 95 -- examples-testing/examples/webgl_loader_3ds.ts | 62 - .../examples/webgl_loader_3dtiles.ts | 78 -- examples-testing/examples/webgl_loader_3mf.ts | 105 -- .../examples/webgl_loader_3mf_materials.ts | 106 -- examples-testing/examples/webgl_loader_amf.ts | 62 - examples-testing/examples/webgl_loader_bvh.ts | 64 - .../examples/webgl_loader_collada.ts | 86 -- .../examples/webgl_loader_collada_skinning.ts | 100 -- .../examples/webgl_loader_draco.ts | 83 -- examples-testing/examples/webgl_loader_fbx.ts | 169 --- .../examples/webgl_loader_fbx_nurbs.ts | 61 - .../examples/webgl_loader_gcode.ts | 81 -- .../examples/webgl_loader_gltf.ts | 167 --- .../webgl_loader_gltf_animation_pointer.ts | 91 -- .../examples/webgl_loader_gltf_anisotropy.ts | 68 - .../examples/webgl_loader_gltf_avif.ts | 61 - .../examples/webgl_loader_gltf_compressed.ts | 83 -- .../examples/webgl_loader_gltf_dispersion.ts | 66 - .../examples/webgl_loader_gltf_instancing.ts | 69 - .../examples/webgl_loader_gltf_iridescence.ts | 66 - .../webgl_loader_gltf_progressive_lod.ts | 143 --- .../examples/webgl_loader_gltf_sheen.ts | 72 -- .../webgl_loader_gltf_transmission.ts | 83 -- .../examples/webgl_loader_imagebitmap.ts | 113 -- examples-testing/examples/webgl_loader_kmz.ts | 59 - examples-testing/examples/webgl_loader_lwo.ts | 69 - .../examples/webgl_loader_md2_control.ts | 292 ----- examples-testing/examples/webgl_loader_mdd.ts | 65 - examples-testing/examples/webgl_loader_obj.ts | 72 -- examples-testing/examples/webgl_loader_pcd.ts | 78 -- examples-testing/examples/webgl_loader_pdb.ts | 208 ---- examples-testing/examples/webgl_loader_ply.ts | 146 --- examples-testing/examples/webgl_loader_svg.ts | 206 --- .../examples/webgl_loader_texture_dds.ts | 218 ---- .../examples/webgl_loader_texture_ktx.ts | 165 --- .../examples/webgl_loader_texture_tga.ts | 90 -- .../examples/webgl_loader_texture_tiff.ts | 87 -- .../examples/webgl_loader_texture_ultrahdr.ts | 101 -- examples-testing/examples/webgl_loader_ttf.ts | 231 ---- .../examples/webgl_loader_usdz.ts | 68 - examples-testing/examples/webgl_loader_vox.ts | 99 -- .../examples/webgl_loader_vrml.ts | 119 -- examples-testing/examples/webgl_loader_vtk.ts | 123 -- examples-testing/examples/webgl_loader_xyz.ts | 65 - examples-testing/examples/webgl_lod.ts | 91 -- .../examples/webgl_marchingcubes.ts | 314 ----- .../examples/webgl_materials_alphahash.ts | 178 --- .../examples/webgl_materials_blending.ts | 149 --- .../webgl_materials_blending_custom.ts | 214 ---- .../examples/webgl_materials_bumpmap.ts | 134 -- .../examples/webgl_materials_car.ts | 168 --- .../examples/webgl_materials_cubemap.ts | 115 -- .../webgl_materials_cubemap_dynamic.ts | 115 -- .../webgl_materials_cubemap_mipmaps.ts | 119 -- .../webgl_materials_cubemap_refraction.ts | 126 -- ...bgl_materials_cubemap_render_to_mipmaps.ts | 183 --- .../webgl_materials_displacementmap.ts | 224 ---- .../examples/webgl_materials_envmaps.ts | 131 -- .../examples/webgl_materials_envmaps_exr.ts | 153 --- .../webgl_materials_envmaps_fasthdr.ts | 169 --- ...webgl_materials_envmaps_groundprojected.ts | 151 --- .../examples/webgl_materials_envmaps_hdr.ts | 163 --- .../examples/webgl_materials_modified.ts | 115 -- .../webgl_materials_normalmap_object_space.ts | 82 -- .../webgl_materials_physical_clearcoat.ts | 208 ---- .../webgl_materials_physical_transmission.ts | 192 --- ...l_materials_physical_transmission_alpha.ts | 194 --- .../webgl_materials_texture_anisotropy.ts | 143 --- .../webgl_materials_texture_canvas.ts | 92 -- .../webgl_materials_texture_filters.ts | 165 --- .../webgl_materials_texture_manualmipmap.ts | 175 --- .../webgl_materials_texture_partialupdate.ts | 104 -- .../webgl_materials_texture_rotation.ts | 112 -- .../examples/webgl_materials_toon.ts | 152 --- .../examples/webgl_materials_video.ts | 208 ---- .../examples/webgl_materials_video_webcam.ts | 79 -- .../examples/webgl_materials_wireframe.ts | 107 -- examples-testing/examples/webgl_math_obb.ts | 192 --- .../webgl_math_orientation_transform.ts | 120 -- examples-testing/examples/webgl_mesh_batch.ts | 347 ------ examples-testing/examples/webgl_mirror.ts | 193 --- .../examples/webgl_modifier_edgesplit.ts | 136 -- .../examples/webgl_modifier_simplifier.ts | 77 -- .../examples/webgl_modifier_tessellation.ts | 142 --- .../examples/webgl_morphtargets.ts | 116 -- .../examples/webgl_morphtargets_face.ts | 108 -- .../examples/webgl_morphtargets_horse.ts | 100 -- .../examples/webgl_morphtargets_sphere.ts | 105 -- .../examples/webgl_multiple_elements.ts | 139 --- .../examples/webgl_multiple_rendertargets.ts | 133 -- .../webgl_multiple_scenes_comparison.ts | 98 -- .../examples/webgl_multiple_views.ts | 235 ---- .../webgl_multisampled_renderbuffers.ts | 133 -- .../examples/webgl_panorama_cube.ts | 83 -- .../webgl_panorama_equirectangular.ts | 112 -- .../examples/webgl_performance.ts | 77 -- .../examples/webgl_pmrem_cubemap.ts | 76 -- .../examples/webgl_pmrem_equirectangular.ts | 76 -- examples-testing/examples/webgl_pmrem_test.ts | 141 --- .../examples/webgl_points_billboards.ts | 120 -- .../examples/webgl_points_sprites.ts | 167 --- .../examples/webgl_points_waves.ts | 145 --- examples-testing/examples/webgl_portal.ts | 218 ---- .../examples/webgl_postprocessing.ts | 86 -- .../examples/webgl_postprocessing_advanced.ts | 304 ----- .../webgl_postprocessing_afterimage.ts | 72 -- .../webgl_postprocessing_backgrounds.ts | 214 ---- .../examples/webgl_postprocessing_fxaa.ts | 122 -- .../examples/webgl_postprocessing_glitch.ts | 108 -- .../examples/webgl_postprocessing_godrays.ts | 176 --- .../examples/webgl_postprocessing_gtao.ts | 218 ---- .../examples/webgl_postprocessing_masking.ts | 101 -- .../examples/webgl_postprocessing_outline.ts | 282 ----- .../examples/webgl_postprocessing_pixel.ts | 231 ---- .../webgl_postprocessing_procedural.ts | 77 -- .../webgl_postprocessing_rgb_halftone.ts | 170 --- .../examples/webgl_postprocessing_sao.ts | 146 --- .../examples/webgl_postprocessing_smaa.ts | 106 -- .../examples/webgl_postprocessing_sobel.ts | 111 -- .../examples/webgl_postprocessing_ssaa.ts | 214 ---- .../examples/webgl_postprocessing_ssao.ts | 125 -- .../examples/webgl_postprocessing_ssr.ts | 262 ---- .../examples/webgl_postprocessing_taa.ts | 139 --- .../webgl_postprocessing_transition.ts | 214 ---- .../webgl_postprocessing_unreal_bloom.ts | 139 --- ...l_postprocessing_unreal_bloom_selective.ts | 208 ---- examples-testing/examples/webgl_random_uv.ts | 338 ----- .../examples/webgl_raycaster_sprite.ts | 103 -- .../examples/webgl_raycaster_texture.ts | 286 ----- .../examples/webgl_read_float_buffer.ts | 153 --- examples-testing/examples/webgl_refraction.ts | 138 -- examples-testing/examples/webgl_rtt.ts | 171 --- examples-testing/examples/webgl_shader.ts | 50 - .../examples/webgl_shader_lava.ts | 104 -- .../examples/webgl_shaders_ocean.ts | 195 --- .../examples/webgl_shaders_sky.ts | 114 -- .../examples/webgl_shadow_contact.ts | 272 ---- examples-testing/examples/webgl_shadowmap.ts | 310 ----- .../examples/webgl_shadowmap_csm.ts | 253 ---- .../examples/webgl_shadowmap_pcss.ts | 165 --- .../examples/webgl_shadowmap_performance.ts | 282 ----- .../examples/webgl_shadowmap_pointlight.ts | 141 --- .../examples/webgl_shadowmap_progressive.ts | 204 --- .../examples/webgl_shadowmap_viewer.ts | 181 --- .../examples/webgl_shadowmap_vsm.ts | 203 --- examples-testing/examples/webgl_shadowmesh.ts | 253 ---- examples-testing/examples/webgl_simple_gi.ts | 172 --- examples-testing/examples/webgl_sprites.ts | 187 --- .../examples/webgl_test_memory.ts | 65 - .../examples/webgl_test_memory2.ts | 81 -- .../examples/webgl_test_wide_gamut.ts | 130 -- .../webgl_texture2darray_compressed.ts | 91 -- .../webgl_texture2darray_layerupdate.ts | 131 -- examples-testing/examples/webgl_texture3d.ts | 128 -- .../examples/webgl_texture3d_partialupdate.ts | 326 ----- .../examples/webgl_tonemapping.ts | 172 --- .../examples/webgl_tsl_clearcoat.ts | 194 --- .../examples/webgl_tsl_instancing.ts | 286 ----- .../examples/webgl_tsl_shadowmap.ts | 160 --- .../examples/webgl_tsl_skinning.ts | 74 -- examples-testing/examples/webgl_ubo.ts | 140 --- examples-testing/examples/webgl_ubo_arrays.ts | 174 --- .../examples/webgl_video_kinect.ts | 114 -- .../webgl_video_panorama_equirectangular.ts | 95 -- .../examples/webgl_volume_cloud.ts | 279 ----- .../examples/webgl_volume_instancing.ts | 222 ---- .../examples/webgl_volume_perlin.ts | 205 --- examples-testing/examples/webgl_watch.ts | 243 ---- .../examples/webgpu_animation_retargeting.ts | 295 ----- ...ebgpu_animation_retargeting_readyplayer.ts | 165 --- examples-testing/examples/webgpu_backdrop.ts | 134 -- .../examples/webgpu_backdrop_area.ts | 157 --- .../examples/webgpu_backdrop_water.ts | 247 ---- examples-testing/examples/webgpu_camera.ts | 211 ---- .../examples/webgpu_camera_array.ts | 101 -- .../webgpu_camera_logarithmicdepthbuffer.ts | 239 ---- examples-testing/examples/webgpu_caustics.ts | 202 --- .../examples/webgpu_centroid_sampling.ts | 199 --- examples-testing/examples/webgpu_clearcoat.ts | 194 --- examples-testing/examples/webgpu_clipping.ts | 203 --- .../examples/webgpu_compile_async.ts | 265 ---- .../examples/webgpu_compute_audio.ts | 178 --- .../examples/webgpu_compute_birds.ts | 424 ------- .../examples/webgpu_compute_cloth.ts | 487 -------- .../examples/webgpu_compute_geometry.ts | 190 --- .../examples/webgpu_compute_particles.ts | 225 ---- .../examples/webgpu_compute_particles_rain.ts | 329 ----- .../examples/webgpu_compute_particles_snow.ts | 328 ----- .../examples/webgpu_compute_points.ts | 124 -- .../examples/webgpu_compute_sort_bitonic.ts | 457 ------- .../examples/webgpu_compute_texture.ts | 96 -- .../examples/webgpu_compute_texture_3d.ts | 193 --- .../webgpu_compute_texture_pingpong.ts | 170 --- .../examples/webgpu_cubemap_adjustments.ts | 165 --- .../examples/webgpu_cubemap_dynamic.ts | 130 -- .../examples/webgpu_cubemap_mix.ts | 78 -- .../examples/webgpu_custom_fog.ts | 135 -- .../examples/webgpu_custom_fog_background.ts | 86 -- .../examples/webgpu_custom_fog_scattering.ts | 161 --- .../examples/webgpu_display_stereo.ts | 191 --- .../examples/webgpu_equirectangular.ts | 57 - .../examples/webgpu_fog_height.ts | 99 -- examples-testing/examples/webgpu_hdr.ts | 124 -- .../examples/webgpu_instance_mesh.ts | 98 -- .../examples/webgpu_instance_path.ts | 147 --- .../examples/webgpu_instance_points.ts | 203 --- .../examples/webgpu_instancing_morph.ts | 145 --- examples-testing/examples/webgpu_layers.ts | 144 --- .../examples/webgpu_lensflares.ts | 137 -- .../examples/webgpu_lightprobe.ts | 136 -- .../examples/webgpu_lightprobe_cubecamera.ts | 90 -- .../examples/webgpu_lights_dynamic.ts | 242 ---- .../examples/webgpu_lights_ies_spotlight.ts | 168 --- .../examples/webgpu_lights_phong.ts | 136 -- .../examples/webgpu_lights_physical.ts | 236 ---- .../examples/webgpu_lights_pointlights.ts | 189 --- .../examples/webgpu_lights_rectarealight.ts | 87 -- .../examples/webgpu_lights_selective.ts | 150 --- .../examples/webgpu_lights_spotlight.ts | 201 --- .../examples/webgpu_lights_tiled.ts | 180 --- examples-testing/examples/webgpu_lines_fat.ts | 255 ---- .../examples/webgpu_lines_fat_raycasting.ts | 286 ----- .../examples/webgpu_lines_fat_wireframe.ts | 210 ---- .../examples/webgpu_loader_gltf.ts | 183 --- .../examples/webgpu_loader_gltf_anisotropy.ts | 64 - .../examples/webgpu_loader_gltf_compressed.ts | 69 - .../examples/webgpu_loader_gltf_dispersion.ts | 63 - .../webgpu_loader_gltf_iridescence.ts | 70 -- .../examples/webgpu_loader_gltf_sheen.ts | 81 -- .../webgpu_loader_gltf_transmission.ts | 83 -- .../examples/webgpu_loader_materialx.ts | 247 ---- .../examples/webgpu_loader_texture_ktx2.ts | 189 --- .../examples/webgpu_materials_alphahash.ts | 129 -- .../examples/webgpu_materials_arrays.ts | 135 -- .../examples/webgpu_materials_basic.ts | 129 -- .../webgpu_materials_cubemap_mipmaps.ts | 119 -- .../webgpu_materials_displacementmap.ts | 195 --- .../examples/webgpu_materials_envmaps.ts | 132 -- .../webgpu_materials_envmaps_bpcem.ts | 202 --- .../examples/webgpu_materials_lightmap.ts | 105 -- .../examples/webgpu_materials_matcap.ts | 194 --- .../examples/webgpu_materials_sss.ts | 153 --- .../webgpu_materials_texture_manualmipmap.ts | 170 --- .../examples/webgpu_materials_toon.ts | 149 --- .../examples/webgpu_materials_transmission.ts | 173 --- .../examples/webgpu_materials_video.ts | 185 --- .../examples/webgpu_materialx_noise.ts | 146 --- .../examples/webgpu_mesh_batch.ts | 265 ---- examples-testing/examples/webgpu_mirror.ts | 194 --- .../examples/webgpu_modifier_curve.ts | 154 --- .../examples/webgpu_morphtargets.ts | 119 -- .../examples/webgpu_morphtargets_face.ts | 98 -- examples-testing/examples/webgpu_mrt.ts | 123 -- examples-testing/examples/webgpu_mrt_mask.ts | 132 -- .../examples/webgpu_multiple_canvas.ts | 111 -- .../examples/webgpu_multiple_elements.ts | 142 --- .../examples/webgpu_multiple_rendertargets.ts | 97 -- .../webgpu_multiple_rendertargets_readback.ts | 161 --- .../webgpu_multisampled_renderbuffers.ts | 129 -- examples-testing/examples/webgpu_occlusion.ts | 97 -- examples-testing/examples/webgpu_ocean.ts | 181 --- .../examples/webgpu_parallax_uv.ts | 135 -- examples-testing/examples/webgpu_particles.ts | 151 --- .../examples/webgpu_performance.ts | 101 -- .../webgpu_performance_renderbundle.ts | 197 --- .../examples/webgpu_pmrem_cubemap.ts | 75 -- .../examples/webgpu_pmrem_equirectangular.ts | 75 -- .../examples/webgpu_pmrem_scene.ts | 106 -- .../examples/webgpu_pmrem_test.ts | 138 -- examples-testing/examples/webgpu_portal.ts | 156 --- .../examples/webgpu_postprocessing.ts | 81 -- .../examples/webgpu_postprocessing_3dlut.ts | 228 ---- .../webgpu_postprocessing_afterimage.ts | 151 --- .../webgpu_postprocessing_anamorphic.ts | 104 -- .../examples/webgpu_postprocessing_ao.ts | 210 ---- .../examples/webgpu_postprocessing_bloom.ts | 120 -- .../webgpu_postprocessing_bloom_emissive.ts | 115 -- .../webgpu_postprocessing_bloom_selective.ts | 124 -- .../examples/webgpu_postprocessing_ca.ts | 266 ---- .../webgpu_postprocessing_difference.ts | 93 -- .../examples/webgpu_postprocessing_dof.ts | 132 -- .../webgpu_postprocessing_dof_basic.ts | 154 --- .../examples/webgpu_postprocessing_fxaa.ts | 129 -- .../examples/webgpu_postprocessing_godrays.ts | 209 ---- .../webgpu_postprocessing_lensflare.ts | 135 -- .../examples/webgpu_postprocessing_masking.ts | 88 -- .../webgpu_postprocessing_motion_blur.ts | 199 --- .../examples/webgpu_postprocessing_outline.ts | 232 ---- .../examples/webgpu_postprocessing_pixel.ts | 237 ---- .../webgpu_postprocessing_radial_blur.ts | 149 --- .../examples/webgpu_postprocessing_retro.ts | 370 ------ .../examples/webgpu_postprocessing_smaa.ts | 98 -- .../examples/webgpu_postprocessing_sobel.ts | 94 -- .../examples/webgpu_postprocessing_ssaa.ts | 170 --- .../examples/webgpu_postprocessing_ssgi.ts | 244 ---- .../webgpu_postprocessing_ssgi_ballpool.ts | 413 ------ .../examples/webgpu_postprocessing_ssr.ts | 211 ---- .../examples/webgpu_postprocessing_sss.ts | 185 --- .../examples/webgpu_postprocessing_traa.ts | 93 -- .../webgpu_postprocessing_transition.ts | 207 --- .../examples/webgpu_procedural_texture.ts | 75 -- .../examples/webgpu_reflection.ts | 345 ----- .../examples/webgpu_reflection_roughness.ts | 121 -- .../examples/webgpu_refraction.ts | 144 --- .../webgpu_rendertarget_2d-array_3d.ts | 237 ---- .../examples/webgpu_reversed_depth_buffer.ts | 169 --- examples-testing/examples/webgpu_rtt.ts | 90 -- .../examples/webgpu_shadow_contact.ts | 205 --- examples-testing/examples/webgpu_shadowmap.ts | 166 --- .../examples/webgpu_shadowmap_array.ts | 330 ----- .../examples/webgpu_shadowmap_csm.ts | 268 ---- .../examples/webgpu_shadowmap_opacity.ts | 117 -- .../examples/webgpu_shadowmap_pointlight.ts | 142 --- .../examples/webgpu_shadowmap_progressive.ts | 203 --- .../examples/webgpu_shadowmap_vsm.ts | 197 --- examples-testing/examples/webgpu_skinning.ts | 72 -- .../examples/webgpu_skinning_instancing.ts | 128 -- examples-testing/examples/webgpu_sprites.ts | 102 -- .../examples/webgpu_storage_buffer.ts | 169 --- .../examples/webgpu_struct_drawindirect.ts | 212 ---- .../examples/webgpu_test_memory.ts | 223 ---- .../examples/webgpu_texturegrad.ts | 116 -- .../examples/webgpu_textures_2d-array.ts | 69 - .../webgpu_textures_2d-array_compressed.ts | 86 -- .../examples/webgpu_textures_anisotropy.ts | 145 --- .../examples/webgpu_textures_partialupdate.ts | 105 -- .../examples/webgpu_tonemapping.ts | 141 --- .../examples/webgpu_tsl_angular_slicing.ts | 175 --- examples-testing/examples/webgpu_tsl_earth.ts | 174 --- .../examples/webgpu_tsl_galaxy.ts | 99 -- examples-testing/examples/webgpu_tsl_graph.ts | 228 ---- .../examples/webgpu_tsl_halftone.ts | 214 ---- .../examples/webgpu_tsl_interoperability.ts | 263 ---- .../examples/webgpu_tsl_procedural_terrain.ts | 318 ----- .../examples/webgpu_tsl_raging_sea.ts | 178 --- .../examples/webgpu_tsl_vfx_flames.ts | 203 --- .../webgpu_tsl_vfx_linkedparticles.ts | 445 ------- .../examples/webgpu_tsl_vfx_tornado.ts | 289 ----- examples-testing/examples/webgpu_tsl_wood.ts | 249 ---- .../examples/webgpu_video_panorama.ts | 99 -- .../examples/webgpu_volume_caustics.ts | 304 ----- .../examples/webgpu_volume_cloud.ts | 165 --- .../examples/webgpu_volume_lighting.ts | 248 ---- .../webgpu_volume_lighting_rectarea.ts | 256 ---- .../examples/webgpu_volume_lighting_traa.ts | 298 ----- .../examples/webgpu_volume_perlin.ts | 114 -- examples-testing/examples/webgpu_water.ts | 199 --- examples-testing/examples/webgpu_xr_cubes.ts | 216 ---- .../examples/webgpu_xr_native_layers.ts | 638 ---------- .../examples/webgpu_xr_rollercoaster.ts | 216 ---- examples-testing/examples/webxr_ar_cones.ts | 66 - examples-testing/examples/webxr_ar_hittest.ts | 119 -- .../examples/webxr_ar_lighting.ts | 124 -- .../examples/webxr_ar_plane_detection.ts | 46 - .../examples/webxr_vr_handinput.ts | 126 -- .../examples/webxr_vr_panorama.ts | 92 -- .../examples/webxr_vr_panorama_depth.ts | 93 -- .../examples/webxr_vr_rollercoaster.ts | 211 ---- examples-testing/examples/webxr_vr_sandbox.ts | 192 --- examples-testing/examples/webxr_vr_video.ts | 92 -- .../examples/webxr_xr_controls_transform.ts | 218 ---- .../webxr_xr_dragging_custom_depth.ts | 395 ------ 495 files changed, 605 insertions(+), 83427 deletions(-) delete mode 100644 examples-testing/examples/css2d_label.ts delete mode 100644 examples-testing/examples/css3d_mixed.ts delete mode 100644 examples-testing/examples/css3d_molecules.ts delete mode 100644 examples-testing/examples/css3d_orthographic.ts delete mode 100644 examples-testing/examples/css3d_periodictable.ts delete mode 100644 examples-testing/examples/css3d_sandbox.ts delete mode 100644 examples-testing/examples/css3d_sprites.ts delete mode 100644 examples-testing/examples/css3d_youtube.ts delete mode 100644 examples-testing/examples/games_fps.ts delete mode 100644 examples-testing/examples/misc_animation_groups.ts delete mode 100644 examples-testing/examples/misc_animation_keys.ts delete mode 100644 examples-testing/examples/misc_boxselection.ts delete mode 100644 examples-testing/examples/misc_controls_arcball.ts delete mode 100644 examples-testing/examples/misc_controls_drag.ts delete mode 100644 examples-testing/examples/misc_controls_fly.ts delete mode 100644 examples-testing/examples/misc_controls_map.ts delete mode 100644 examples-testing/examples/misc_controls_orbit.ts delete mode 100644 examples-testing/examples/misc_controls_pointerlock.ts delete mode 100644 examples-testing/examples/misc_controls_trackball.ts delete mode 100644 examples-testing/examples/misc_controls_transform.ts delete mode 100644 examples-testing/examples/misc_exporter_draco.ts delete mode 100644 examples-testing/examples/misc_exporter_exr.ts delete mode 100644 examples-testing/examples/misc_exporter_gltf.ts delete mode 100644 examples-testing/examples/misc_exporter_ktx2.ts delete mode 100644 examples-testing/examples/misc_exporter_obj.ts delete mode 100644 examples-testing/examples/misc_exporter_ply.ts delete mode 100644 examples-testing/examples/misc_exporter_stl.ts delete mode 100644 examples-testing/examples/misc_exporter_usdz.ts delete mode 100644 examples-testing/examples/misc_uv_tests.ts delete mode 100644 examples-testing/examples/physics_ammo_instancing.ts delete mode 100644 examples-testing/examples/physics_jolt_instancing.ts delete mode 100644 examples-testing/examples/physics_rapier_basic.ts delete mode 100644 examples-testing/examples/physics_rapier_character_controller.ts delete mode 100644 examples-testing/examples/physics_rapier_instancing.ts delete mode 100644 examples-testing/examples/physics_rapier_joints.ts delete mode 100644 examples-testing/examples/physics_rapier_terrain.ts delete mode 100644 examples-testing/examples/physics_rapier_vehicle_controller.ts delete mode 100644 examples-testing/examples/svg_lines.ts delete mode 100644 examples-testing/examples/svg_sandbox.ts delete mode 100644 examples-testing/examples/webaudio_orientation.ts delete mode 100644 examples-testing/examples/webaudio_sandbox.ts delete mode 100644 examples-testing/examples/webaudio_timing.ts delete mode 100644 examples-testing/examples/webaudio_visualizer.ts delete mode 100644 examples-testing/examples/webgl_animation_keyframes.ts delete mode 100644 examples-testing/examples/webgl_animation_multiple.ts delete mode 100644 examples-testing/examples/webgl_animation_skinning_morph.ts delete mode 100644 examples-testing/examples/webgl_animation_walk.ts delete mode 100644 examples-testing/examples/webgl_buffergeometry.ts delete mode 100644 examples-testing/examples/webgl_buffergeometry_attributes_integer.ts delete mode 100644 examples-testing/examples/webgl_buffergeometry_attributes_none.ts delete mode 100644 examples-testing/examples/webgl_buffergeometry_custom_attributes_particles.ts delete mode 100644 examples-testing/examples/webgl_buffergeometry_drawrange.ts delete mode 100644 examples-testing/examples/webgl_buffergeometry_glbufferattribute.ts delete mode 100644 examples-testing/examples/webgl_buffergeometry_indexed.ts delete mode 100644 examples-testing/examples/webgl_buffergeometry_instancing.ts delete mode 100644 examples-testing/examples/webgl_buffergeometry_instancing_billboards.ts delete mode 100644 examples-testing/examples/webgl_buffergeometry_instancing_interleaved.ts delete mode 100644 examples-testing/examples/webgl_buffergeometry_lines.ts delete mode 100644 examples-testing/examples/webgl_buffergeometry_lines_indexed.ts delete mode 100644 examples-testing/examples/webgl_buffergeometry_points.ts delete mode 100644 examples-testing/examples/webgl_buffergeometry_points_interleaved.ts delete mode 100644 examples-testing/examples/webgl_buffergeometry_rawshader.ts delete mode 100644 examples-testing/examples/webgl_buffergeometry_selective_draw.ts delete mode 100644 examples-testing/examples/webgl_buffergeometry_uint.ts delete mode 100644 examples-testing/examples/webgl_camera.ts delete mode 100644 examples-testing/examples/webgl_camera_array.ts delete mode 100644 examples-testing/examples/webgl_camera_logarithmicdepthbuffer.ts delete mode 100644 examples-testing/examples/webgl_clipculldistance.ts delete mode 100644 examples-testing/examples/webgl_clipping.ts delete mode 100644 examples-testing/examples/webgl_clipping_advanced.ts delete mode 100644 examples-testing/examples/webgl_clipping_intersection.ts delete mode 100644 examples-testing/examples/webgl_clipping_stencil.ts delete mode 100644 examples-testing/examples/webgl_custom_attributes.ts delete mode 100644 examples-testing/examples/webgl_custom_attributes_lines.ts delete mode 100644 examples-testing/examples/webgl_custom_attributes_points.ts delete mode 100644 examples-testing/examples/webgl_custom_attributes_points2.ts delete mode 100644 examples-testing/examples/webgl_custom_attributes_points3.ts delete mode 100644 examples-testing/examples/webgl_decals.ts delete mode 100644 examples-testing/examples/webgl_effects_anaglyph.ts delete mode 100644 examples-testing/examples/webgl_effects_ascii.ts delete mode 100644 examples-testing/examples/webgl_effects_parallaxbarrier.ts delete mode 100644 examples-testing/examples/webgl_effects_stereo.ts delete mode 100644 examples-testing/examples/webgl_framebuffer_texture.ts delete mode 100644 examples-testing/examples/webgl_furnace_test.ts delete mode 100644 examples-testing/examples/webgl_geometries.ts delete mode 100644 examples-testing/examples/webgl_geometry_colors.ts delete mode 100644 examples-testing/examples/webgl_geometry_colors_lookuptable.ts delete mode 100644 examples-testing/examples/webgl_geometry_convex.ts delete mode 100644 examples-testing/examples/webgl_geometry_cube.ts delete mode 100644 examples-testing/examples/webgl_geometry_extrude_shapes.ts delete mode 100644 examples-testing/examples/webgl_geometry_extrude_splines.ts delete mode 100644 examples-testing/examples/webgl_geometry_minecraft.ts delete mode 100644 examples-testing/examples/webgl_geometry_nurbs.ts delete mode 100644 examples-testing/examples/webgl_geometry_shapes.ts delete mode 100644 examples-testing/examples/webgl_geometry_teapot.ts delete mode 100644 examples-testing/examples/webgl_geometry_terrain.ts delete mode 100644 examples-testing/examples/webgl_geometry_terrain_raycast.ts delete mode 100644 examples-testing/examples/webgl_geometry_text.ts delete mode 100644 examples-testing/examples/webgl_geometry_text_shapes.ts delete mode 100644 examples-testing/examples/webgl_geometry_text_stroke.ts delete mode 100644 examples-testing/examples/webgl_gpgpu_birds.ts delete mode 100644 examples-testing/examples/webgl_gpgpu_birds_gltf.ts delete mode 100644 examples-testing/examples/webgl_gpgpu_protoplanet.ts delete mode 100644 examples-testing/examples/webgl_gpgpu_water.ts delete mode 100644 examples-testing/examples/webgl_helpers.ts delete mode 100644 examples-testing/examples/webgl_instancing_dynamic.ts delete mode 100644 examples-testing/examples/webgl_instancing_morph.ts delete mode 100644 examples-testing/examples/webgl_instancing_performance.ts delete mode 100644 examples-testing/examples/webgl_instancing_raycast.ts delete mode 100644 examples-testing/examples/webgl_instancing_scatter.ts delete mode 100644 examples-testing/examples/webgl_interactive_buffergeometry.ts delete mode 100644 examples-testing/examples/webgl_interactive_cubes.ts delete mode 100644 examples-testing/examples/webgl_interactive_cubes_gpu.ts delete mode 100644 examples-testing/examples/webgl_interactive_cubes_ortho.ts delete mode 100644 examples-testing/examples/webgl_interactive_lines.ts delete mode 100644 examples-testing/examples/webgl_interactive_points.ts delete mode 100644 examples-testing/examples/webgl_interactive_raycasting_points.ts delete mode 100644 examples-testing/examples/webgl_interactive_voxelpainter.ts delete mode 100644 examples-testing/examples/webgl_lensflares.ts delete mode 100644 examples-testing/examples/webgl_lightprobe.ts delete mode 100644 examples-testing/examples/webgl_lightprobe_cubecamera.ts delete mode 100644 examples-testing/examples/webgl_lights_hemisphere.ts delete mode 100644 examples-testing/examples/webgl_lights_physical.ts delete mode 100644 examples-testing/examples/webgl_lights_rectarealight.ts delete mode 100644 examples-testing/examples/webgl_lights_spotlight.ts delete mode 100644 examples-testing/examples/webgl_lights_spotlights.ts delete mode 100644 examples-testing/examples/webgl_lines_colors.ts delete mode 100644 examples-testing/examples/webgl_lines_dashed.ts delete mode 100644 examples-testing/examples/webgl_lines_fat.ts delete mode 100644 examples-testing/examples/webgl_lines_fat_raycasting.ts delete mode 100644 examples-testing/examples/webgl_lines_fat_wireframe.ts delete mode 100644 examples-testing/examples/webgl_loader_3dm.ts delete mode 100644 examples-testing/examples/webgl_loader_3ds.ts delete mode 100644 examples-testing/examples/webgl_loader_3dtiles.ts delete mode 100644 examples-testing/examples/webgl_loader_3mf.ts delete mode 100644 examples-testing/examples/webgl_loader_3mf_materials.ts delete mode 100644 examples-testing/examples/webgl_loader_amf.ts delete mode 100644 examples-testing/examples/webgl_loader_bvh.ts delete mode 100644 examples-testing/examples/webgl_loader_collada.ts delete mode 100644 examples-testing/examples/webgl_loader_collada_skinning.ts delete mode 100644 examples-testing/examples/webgl_loader_draco.ts delete mode 100644 examples-testing/examples/webgl_loader_fbx.ts delete mode 100644 examples-testing/examples/webgl_loader_fbx_nurbs.ts delete mode 100644 examples-testing/examples/webgl_loader_gcode.ts delete mode 100644 examples-testing/examples/webgl_loader_gltf.ts delete mode 100644 examples-testing/examples/webgl_loader_gltf_animation_pointer.ts delete mode 100644 examples-testing/examples/webgl_loader_gltf_anisotropy.ts delete mode 100644 examples-testing/examples/webgl_loader_gltf_avif.ts delete mode 100644 examples-testing/examples/webgl_loader_gltf_compressed.ts delete mode 100644 examples-testing/examples/webgl_loader_gltf_dispersion.ts delete mode 100644 examples-testing/examples/webgl_loader_gltf_instancing.ts delete mode 100644 examples-testing/examples/webgl_loader_gltf_iridescence.ts delete mode 100644 examples-testing/examples/webgl_loader_gltf_progressive_lod.ts delete mode 100644 examples-testing/examples/webgl_loader_gltf_sheen.ts delete mode 100644 examples-testing/examples/webgl_loader_gltf_transmission.ts delete mode 100644 examples-testing/examples/webgl_loader_imagebitmap.ts delete mode 100644 examples-testing/examples/webgl_loader_kmz.ts delete mode 100644 examples-testing/examples/webgl_loader_lwo.ts delete mode 100644 examples-testing/examples/webgl_loader_md2_control.ts delete mode 100644 examples-testing/examples/webgl_loader_mdd.ts delete mode 100644 examples-testing/examples/webgl_loader_obj.ts delete mode 100644 examples-testing/examples/webgl_loader_pcd.ts delete mode 100644 examples-testing/examples/webgl_loader_pdb.ts delete mode 100644 examples-testing/examples/webgl_loader_ply.ts delete mode 100644 examples-testing/examples/webgl_loader_svg.ts delete mode 100644 examples-testing/examples/webgl_loader_texture_dds.ts delete mode 100644 examples-testing/examples/webgl_loader_texture_ktx.ts delete mode 100644 examples-testing/examples/webgl_loader_texture_tga.ts delete mode 100644 examples-testing/examples/webgl_loader_texture_tiff.ts delete mode 100644 examples-testing/examples/webgl_loader_texture_ultrahdr.ts delete mode 100644 examples-testing/examples/webgl_loader_ttf.ts delete mode 100644 examples-testing/examples/webgl_loader_usdz.ts delete mode 100644 examples-testing/examples/webgl_loader_vox.ts delete mode 100644 examples-testing/examples/webgl_loader_vrml.ts delete mode 100644 examples-testing/examples/webgl_loader_vtk.ts delete mode 100644 examples-testing/examples/webgl_loader_xyz.ts delete mode 100644 examples-testing/examples/webgl_lod.ts delete mode 100644 examples-testing/examples/webgl_marchingcubes.ts delete mode 100644 examples-testing/examples/webgl_materials_alphahash.ts delete mode 100644 examples-testing/examples/webgl_materials_blending.ts delete mode 100644 examples-testing/examples/webgl_materials_blending_custom.ts delete mode 100644 examples-testing/examples/webgl_materials_bumpmap.ts delete mode 100644 examples-testing/examples/webgl_materials_car.ts delete mode 100644 examples-testing/examples/webgl_materials_cubemap.ts delete mode 100644 examples-testing/examples/webgl_materials_cubemap_dynamic.ts delete mode 100644 examples-testing/examples/webgl_materials_cubemap_mipmaps.ts delete mode 100644 examples-testing/examples/webgl_materials_cubemap_refraction.ts delete mode 100644 examples-testing/examples/webgl_materials_cubemap_render_to_mipmaps.ts delete mode 100644 examples-testing/examples/webgl_materials_displacementmap.ts delete mode 100644 examples-testing/examples/webgl_materials_envmaps.ts delete mode 100644 examples-testing/examples/webgl_materials_envmaps_exr.ts delete mode 100644 examples-testing/examples/webgl_materials_envmaps_fasthdr.ts delete mode 100644 examples-testing/examples/webgl_materials_envmaps_groundprojected.ts delete mode 100644 examples-testing/examples/webgl_materials_envmaps_hdr.ts delete mode 100644 examples-testing/examples/webgl_materials_modified.ts delete mode 100644 examples-testing/examples/webgl_materials_normalmap_object_space.ts delete mode 100644 examples-testing/examples/webgl_materials_physical_clearcoat.ts delete mode 100644 examples-testing/examples/webgl_materials_physical_transmission.ts delete mode 100644 examples-testing/examples/webgl_materials_physical_transmission_alpha.ts delete mode 100644 examples-testing/examples/webgl_materials_texture_anisotropy.ts delete mode 100644 examples-testing/examples/webgl_materials_texture_canvas.ts delete mode 100644 examples-testing/examples/webgl_materials_texture_filters.ts delete mode 100644 examples-testing/examples/webgl_materials_texture_manualmipmap.ts delete mode 100644 examples-testing/examples/webgl_materials_texture_partialupdate.ts delete mode 100644 examples-testing/examples/webgl_materials_texture_rotation.ts delete mode 100644 examples-testing/examples/webgl_materials_toon.ts delete mode 100644 examples-testing/examples/webgl_materials_video.ts delete mode 100644 examples-testing/examples/webgl_materials_video_webcam.ts delete mode 100644 examples-testing/examples/webgl_materials_wireframe.ts delete mode 100644 examples-testing/examples/webgl_math_obb.ts delete mode 100644 examples-testing/examples/webgl_math_orientation_transform.ts delete mode 100644 examples-testing/examples/webgl_mesh_batch.ts delete mode 100644 examples-testing/examples/webgl_mirror.ts delete mode 100644 examples-testing/examples/webgl_modifier_edgesplit.ts delete mode 100644 examples-testing/examples/webgl_modifier_simplifier.ts delete mode 100644 examples-testing/examples/webgl_modifier_tessellation.ts delete mode 100644 examples-testing/examples/webgl_morphtargets.ts delete mode 100644 examples-testing/examples/webgl_morphtargets_face.ts delete mode 100644 examples-testing/examples/webgl_morphtargets_horse.ts delete mode 100644 examples-testing/examples/webgl_morphtargets_sphere.ts delete mode 100644 examples-testing/examples/webgl_multiple_elements.ts delete mode 100644 examples-testing/examples/webgl_multiple_rendertargets.ts delete mode 100644 examples-testing/examples/webgl_multiple_scenes_comparison.ts delete mode 100644 examples-testing/examples/webgl_multiple_views.ts delete mode 100644 examples-testing/examples/webgl_multisampled_renderbuffers.ts delete mode 100644 examples-testing/examples/webgl_panorama_cube.ts delete mode 100644 examples-testing/examples/webgl_panorama_equirectangular.ts delete mode 100644 examples-testing/examples/webgl_performance.ts delete mode 100644 examples-testing/examples/webgl_pmrem_cubemap.ts delete mode 100644 examples-testing/examples/webgl_pmrem_equirectangular.ts delete mode 100644 examples-testing/examples/webgl_pmrem_test.ts delete mode 100644 examples-testing/examples/webgl_points_billboards.ts delete mode 100644 examples-testing/examples/webgl_points_sprites.ts delete mode 100644 examples-testing/examples/webgl_points_waves.ts delete mode 100644 examples-testing/examples/webgl_portal.ts delete mode 100644 examples-testing/examples/webgl_postprocessing.ts delete mode 100644 examples-testing/examples/webgl_postprocessing_advanced.ts delete mode 100644 examples-testing/examples/webgl_postprocessing_afterimage.ts delete mode 100644 examples-testing/examples/webgl_postprocessing_backgrounds.ts delete mode 100644 examples-testing/examples/webgl_postprocessing_fxaa.ts delete mode 100644 examples-testing/examples/webgl_postprocessing_glitch.ts delete mode 100644 examples-testing/examples/webgl_postprocessing_godrays.ts delete mode 100644 examples-testing/examples/webgl_postprocessing_gtao.ts delete mode 100644 examples-testing/examples/webgl_postprocessing_masking.ts delete mode 100644 examples-testing/examples/webgl_postprocessing_outline.ts delete mode 100644 examples-testing/examples/webgl_postprocessing_pixel.ts delete mode 100644 examples-testing/examples/webgl_postprocessing_procedural.ts delete mode 100644 examples-testing/examples/webgl_postprocessing_rgb_halftone.ts delete mode 100644 examples-testing/examples/webgl_postprocessing_sao.ts delete mode 100644 examples-testing/examples/webgl_postprocessing_smaa.ts delete mode 100644 examples-testing/examples/webgl_postprocessing_sobel.ts delete mode 100644 examples-testing/examples/webgl_postprocessing_ssaa.ts delete mode 100644 examples-testing/examples/webgl_postprocessing_ssao.ts delete mode 100644 examples-testing/examples/webgl_postprocessing_ssr.ts delete mode 100644 examples-testing/examples/webgl_postprocessing_taa.ts delete mode 100644 examples-testing/examples/webgl_postprocessing_transition.ts delete mode 100644 examples-testing/examples/webgl_postprocessing_unreal_bloom.ts delete mode 100644 examples-testing/examples/webgl_postprocessing_unreal_bloom_selective.ts delete mode 100644 examples-testing/examples/webgl_random_uv.ts delete mode 100644 examples-testing/examples/webgl_raycaster_sprite.ts delete mode 100644 examples-testing/examples/webgl_raycaster_texture.ts delete mode 100644 examples-testing/examples/webgl_read_float_buffer.ts delete mode 100644 examples-testing/examples/webgl_refraction.ts delete mode 100644 examples-testing/examples/webgl_rtt.ts delete mode 100644 examples-testing/examples/webgl_shader.ts delete mode 100644 examples-testing/examples/webgl_shader_lava.ts delete mode 100644 examples-testing/examples/webgl_shaders_ocean.ts delete mode 100644 examples-testing/examples/webgl_shaders_sky.ts delete mode 100644 examples-testing/examples/webgl_shadow_contact.ts delete mode 100644 examples-testing/examples/webgl_shadowmap.ts delete mode 100644 examples-testing/examples/webgl_shadowmap_csm.ts delete mode 100644 examples-testing/examples/webgl_shadowmap_pcss.ts delete mode 100644 examples-testing/examples/webgl_shadowmap_performance.ts delete mode 100644 examples-testing/examples/webgl_shadowmap_pointlight.ts delete mode 100644 examples-testing/examples/webgl_shadowmap_progressive.ts delete mode 100644 examples-testing/examples/webgl_shadowmap_viewer.ts delete mode 100644 examples-testing/examples/webgl_shadowmap_vsm.ts delete mode 100644 examples-testing/examples/webgl_shadowmesh.ts delete mode 100644 examples-testing/examples/webgl_simple_gi.ts delete mode 100644 examples-testing/examples/webgl_sprites.ts delete mode 100644 examples-testing/examples/webgl_test_memory.ts delete mode 100644 examples-testing/examples/webgl_test_memory2.ts delete mode 100644 examples-testing/examples/webgl_test_wide_gamut.ts delete mode 100644 examples-testing/examples/webgl_texture2darray_compressed.ts delete mode 100644 examples-testing/examples/webgl_texture2darray_layerupdate.ts delete mode 100644 examples-testing/examples/webgl_texture3d.ts delete mode 100644 examples-testing/examples/webgl_texture3d_partialupdate.ts delete mode 100644 examples-testing/examples/webgl_tonemapping.ts delete mode 100644 examples-testing/examples/webgl_tsl_clearcoat.ts delete mode 100644 examples-testing/examples/webgl_tsl_instancing.ts delete mode 100644 examples-testing/examples/webgl_tsl_shadowmap.ts delete mode 100644 examples-testing/examples/webgl_tsl_skinning.ts delete mode 100644 examples-testing/examples/webgl_ubo.ts delete mode 100644 examples-testing/examples/webgl_ubo_arrays.ts delete mode 100644 examples-testing/examples/webgl_video_kinect.ts delete mode 100644 examples-testing/examples/webgl_video_panorama_equirectangular.ts delete mode 100644 examples-testing/examples/webgl_volume_cloud.ts delete mode 100644 examples-testing/examples/webgl_volume_instancing.ts delete mode 100644 examples-testing/examples/webgl_volume_perlin.ts delete mode 100644 examples-testing/examples/webgl_watch.ts delete mode 100644 examples-testing/examples/webgpu_animation_retargeting.ts delete mode 100644 examples-testing/examples/webgpu_animation_retargeting_readyplayer.ts delete mode 100644 examples-testing/examples/webgpu_backdrop.ts delete mode 100644 examples-testing/examples/webgpu_backdrop_area.ts delete mode 100644 examples-testing/examples/webgpu_backdrop_water.ts delete mode 100644 examples-testing/examples/webgpu_camera.ts delete mode 100644 examples-testing/examples/webgpu_camera_array.ts delete mode 100644 examples-testing/examples/webgpu_camera_logarithmicdepthbuffer.ts delete mode 100644 examples-testing/examples/webgpu_caustics.ts delete mode 100644 examples-testing/examples/webgpu_centroid_sampling.ts delete mode 100644 examples-testing/examples/webgpu_clearcoat.ts delete mode 100644 examples-testing/examples/webgpu_clipping.ts delete mode 100644 examples-testing/examples/webgpu_compile_async.ts delete mode 100644 examples-testing/examples/webgpu_compute_audio.ts delete mode 100644 examples-testing/examples/webgpu_compute_birds.ts delete mode 100644 examples-testing/examples/webgpu_compute_cloth.ts delete mode 100644 examples-testing/examples/webgpu_compute_geometry.ts delete mode 100644 examples-testing/examples/webgpu_compute_particles.ts delete mode 100644 examples-testing/examples/webgpu_compute_particles_rain.ts delete mode 100644 examples-testing/examples/webgpu_compute_particles_snow.ts delete mode 100644 examples-testing/examples/webgpu_compute_points.ts delete mode 100644 examples-testing/examples/webgpu_compute_sort_bitonic.ts delete mode 100644 examples-testing/examples/webgpu_compute_texture.ts delete mode 100644 examples-testing/examples/webgpu_compute_texture_3d.ts delete mode 100644 examples-testing/examples/webgpu_compute_texture_pingpong.ts delete mode 100644 examples-testing/examples/webgpu_cubemap_adjustments.ts delete mode 100644 examples-testing/examples/webgpu_cubemap_dynamic.ts delete mode 100644 examples-testing/examples/webgpu_cubemap_mix.ts delete mode 100644 examples-testing/examples/webgpu_custom_fog.ts delete mode 100644 examples-testing/examples/webgpu_custom_fog_background.ts delete mode 100644 examples-testing/examples/webgpu_custom_fog_scattering.ts delete mode 100644 examples-testing/examples/webgpu_display_stereo.ts delete mode 100644 examples-testing/examples/webgpu_equirectangular.ts delete mode 100644 examples-testing/examples/webgpu_fog_height.ts delete mode 100644 examples-testing/examples/webgpu_hdr.ts delete mode 100644 examples-testing/examples/webgpu_instance_mesh.ts delete mode 100644 examples-testing/examples/webgpu_instance_path.ts delete mode 100644 examples-testing/examples/webgpu_instance_points.ts delete mode 100644 examples-testing/examples/webgpu_instancing_morph.ts delete mode 100644 examples-testing/examples/webgpu_layers.ts delete mode 100644 examples-testing/examples/webgpu_lensflares.ts delete mode 100644 examples-testing/examples/webgpu_lightprobe.ts delete mode 100644 examples-testing/examples/webgpu_lightprobe_cubecamera.ts delete mode 100644 examples-testing/examples/webgpu_lights_dynamic.ts delete mode 100644 examples-testing/examples/webgpu_lights_ies_spotlight.ts delete mode 100644 examples-testing/examples/webgpu_lights_phong.ts delete mode 100644 examples-testing/examples/webgpu_lights_physical.ts delete mode 100644 examples-testing/examples/webgpu_lights_pointlights.ts delete mode 100644 examples-testing/examples/webgpu_lights_rectarealight.ts delete mode 100644 examples-testing/examples/webgpu_lights_selective.ts delete mode 100644 examples-testing/examples/webgpu_lights_spotlight.ts delete mode 100644 examples-testing/examples/webgpu_lights_tiled.ts delete mode 100644 examples-testing/examples/webgpu_lines_fat.ts delete mode 100644 examples-testing/examples/webgpu_lines_fat_raycasting.ts delete mode 100644 examples-testing/examples/webgpu_lines_fat_wireframe.ts delete mode 100644 examples-testing/examples/webgpu_loader_gltf.ts delete mode 100644 examples-testing/examples/webgpu_loader_gltf_anisotropy.ts delete mode 100644 examples-testing/examples/webgpu_loader_gltf_compressed.ts delete mode 100644 examples-testing/examples/webgpu_loader_gltf_dispersion.ts delete mode 100644 examples-testing/examples/webgpu_loader_gltf_iridescence.ts delete mode 100644 examples-testing/examples/webgpu_loader_gltf_sheen.ts delete mode 100644 examples-testing/examples/webgpu_loader_gltf_transmission.ts delete mode 100644 examples-testing/examples/webgpu_loader_materialx.ts delete mode 100644 examples-testing/examples/webgpu_loader_texture_ktx2.ts delete mode 100644 examples-testing/examples/webgpu_materials_alphahash.ts delete mode 100644 examples-testing/examples/webgpu_materials_arrays.ts delete mode 100644 examples-testing/examples/webgpu_materials_basic.ts delete mode 100644 examples-testing/examples/webgpu_materials_cubemap_mipmaps.ts delete mode 100644 examples-testing/examples/webgpu_materials_displacementmap.ts delete mode 100644 examples-testing/examples/webgpu_materials_envmaps.ts delete mode 100644 examples-testing/examples/webgpu_materials_envmaps_bpcem.ts delete mode 100644 examples-testing/examples/webgpu_materials_lightmap.ts delete mode 100644 examples-testing/examples/webgpu_materials_matcap.ts delete mode 100644 examples-testing/examples/webgpu_materials_sss.ts delete mode 100644 examples-testing/examples/webgpu_materials_texture_manualmipmap.ts delete mode 100644 examples-testing/examples/webgpu_materials_toon.ts delete mode 100644 examples-testing/examples/webgpu_materials_transmission.ts delete mode 100644 examples-testing/examples/webgpu_materials_video.ts delete mode 100644 examples-testing/examples/webgpu_materialx_noise.ts delete mode 100644 examples-testing/examples/webgpu_mesh_batch.ts delete mode 100644 examples-testing/examples/webgpu_mirror.ts delete mode 100644 examples-testing/examples/webgpu_modifier_curve.ts delete mode 100644 examples-testing/examples/webgpu_morphtargets.ts delete mode 100644 examples-testing/examples/webgpu_morphtargets_face.ts delete mode 100644 examples-testing/examples/webgpu_mrt.ts delete mode 100644 examples-testing/examples/webgpu_mrt_mask.ts delete mode 100644 examples-testing/examples/webgpu_multiple_canvas.ts delete mode 100644 examples-testing/examples/webgpu_multiple_elements.ts delete mode 100644 examples-testing/examples/webgpu_multiple_rendertargets.ts delete mode 100644 examples-testing/examples/webgpu_multiple_rendertargets_readback.ts delete mode 100644 examples-testing/examples/webgpu_multisampled_renderbuffers.ts delete mode 100644 examples-testing/examples/webgpu_occlusion.ts delete mode 100644 examples-testing/examples/webgpu_ocean.ts delete mode 100644 examples-testing/examples/webgpu_parallax_uv.ts delete mode 100644 examples-testing/examples/webgpu_particles.ts delete mode 100644 examples-testing/examples/webgpu_performance.ts delete mode 100644 examples-testing/examples/webgpu_performance_renderbundle.ts delete mode 100644 examples-testing/examples/webgpu_pmrem_cubemap.ts delete mode 100644 examples-testing/examples/webgpu_pmrem_equirectangular.ts delete mode 100644 examples-testing/examples/webgpu_pmrem_scene.ts delete mode 100644 examples-testing/examples/webgpu_pmrem_test.ts delete mode 100644 examples-testing/examples/webgpu_portal.ts delete mode 100644 examples-testing/examples/webgpu_postprocessing.ts delete mode 100644 examples-testing/examples/webgpu_postprocessing_3dlut.ts delete mode 100644 examples-testing/examples/webgpu_postprocessing_afterimage.ts delete mode 100644 examples-testing/examples/webgpu_postprocessing_anamorphic.ts delete mode 100644 examples-testing/examples/webgpu_postprocessing_ao.ts delete mode 100644 examples-testing/examples/webgpu_postprocessing_bloom.ts delete mode 100644 examples-testing/examples/webgpu_postprocessing_bloom_emissive.ts delete mode 100644 examples-testing/examples/webgpu_postprocessing_bloom_selective.ts delete mode 100644 examples-testing/examples/webgpu_postprocessing_ca.ts delete mode 100644 examples-testing/examples/webgpu_postprocessing_difference.ts delete mode 100644 examples-testing/examples/webgpu_postprocessing_dof.ts delete mode 100644 examples-testing/examples/webgpu_postprocessing_dof_basic.ts delete mode 100644 examples-testing/examples/webgpu_postprocessing_fxaa.ts delete mode 100644 examples-testing/examples/webgpu_postprocessing_godrays.ts delete mode 100644 examples-testing/examples/webgpu_postprocessing_lensflare.ts delete mode 100644 examples-testing/examples/webgpu_postprocessing_masking.ts delete mode 100644 examples-testing/examples/webgpu_postprocessing_motion_blur.ts delete mode 100644 examples-testing/examples/webgpu_postprocessing_outline.ts delete mode 100644 examples-testing/examples/webgpu_postprocessing_pixel.ts delete mode 100644 examples-testing/examples/webgpu_postprocessing_radial_blur.ts delete mode 100644 examples-testing/examples/webgpu_postprocessing_retro.ts delete mode 100644 examples-testing/examples/webgpu_postprocessing_smaa.ts delete mode 100644 examples-testing/examples/webgpu_postprocessing_sobel.ts delete mode 100644 examples-testing/examples/webgpu_postprocessing_ssaa.ts delete mode 100644 examples-testing/examples/webgpu_postprocessing_ssgi.ts delete mode 100644 examples-testing/examples/webgpu_postprocessing_ssgi_ballpool.ts delete mode 100644 examples-testing/examples/webgpu_postprocessing_ssr.ts delete mode 100644 examples-testing/examples/webgpu_postprocessing_sss.ts delete mode 100644 examples-testing/examples/webgpu_postprocessing_traa.ts delete mode 100644 examples-testing/examples/webgpu_postprocessing_transition.ts delete mode 100644 examples-testing/examples/webgpu_procedural_texture.ts delete mode 100644 examples-testing/examples/webgpu_reflection.ts delete mode 100644 examples-testing/examples/webgpu_reflection_roughness.ts delete mode 100644 examples-testing/examples/webgpu_refraction.ts delete mode 100644 examples-testing/examples/webgpu_rendertarget_2d-array_3d.ts delete mode 100644 examples-testing/examples/webgpu_reversed_depth_buffer.ts delete mode 100644 examples-testing/examples/webgpu_rtt.ts delete mode 100644 examples-testing/examples/webgpu_shadow_contact.ts delete mode 100644 examples-testing/examples/webgpu_shadowmap.ts delete mode 100644 examples-testing/examples/webgpu_shadowmap_array.ts delete mode 100644 examples-testing/examples/webgpu_shadowmap_csm.ts delete mode 100644 examples-testing/examples/webgpu_shadowmap_opacity.ts delete mode 100644 examples-testing/examples/webgpu_shadowmap_pointlight.ts delete mode 100644 examples-testing/examples/webgpu_shadowmap_progressive.ts delete mode 100644 examples-testing/examples/webgpu_shadowmap_vsm.ts delete mode 100644 examples-testing/examples/webgpu_skinning.ts delete mode 100644 examples-testing/examples/webgpu_skinning_instancing.ts delete mode 100644 examples-testing/examples/webgpu_sprites.ts delete mode 100644 examples-testing/examples/webgpu_storage_buffer.ts delete mode 100644 examples-testing/examples/webgpu_struct_drawindirect.ts delete mode 100644 examples-testing/examples/webgpu_test_memory.ts delete mode 100644 examples-testing/examples/webgpu_texturegrad.ts delete mode 100644 examples-testing/examples/webgpu_textures_2d-array.ts delete mode 100644 examples-testing/examples/webgpu_textures_2d-array_compressed.ts delete mode 100644 examples-testing/examples/webgpu_textures_anisotropy.ts delete mode 100644 examples-testing/examples/webgpu_textures_partialupdate.ts delete mode 100644 examples-testing/examples/webgpu_tonemapping.ts delete mode 100644 examples-testing/examples/webgpu_tsl_angular_slicing.ts delete mode 100644 examples-testing/examples/webgpu_tsl_earth.ts delete mode 100644 examples-testing/examples/webgpu_tsl_galaxy.ts delete mode 100644 examples-testing/examples/webgpu_tsl_graph.ts delete mode 100644 examples-testing/examples/webgpu_tsl_halftone.ts delete mode 100644 examples-testing/examples/webgpu_tsl_interoperability.ts delete mode 100644 examples-testing/examples/webgpu_tsl_procedural_terrain.ts delete mode 100644 examples-testing/examples/webgpu_tsl_raging_sea.ts delete mode 100644 examples-testing/examples/webgpu_tsl_vfx_flames.ts delete mode 100644 examples-testing/examples/webgpu_tsl_vfx_linkedparticles.ts delete mode 100644 examples-testing/examples/webgpu_tsl_vfx_tornado.ts delete mode 100644 examples-testing/examples/webgpu_tsl_wood.ts delete mode 100644 examples-testing/examples/webgpu_video_panorama.ts delete mode 100644 examples-testing/examples/webgpu_volume_caustics.ts delete mode 100644 examples-testing/examples/webgpu_volume_cloud.ts delete mode 100644 examples-testing/examples/webgpu_volume_lighting.ts delete mode 100644 examples-testing/examples/webgpu_volume_lighting_rectarea.ts delete mode 100644 examples-testing/examples/webgpu_volume_lighting_traa.ts delete mode 100644 examples-testing/examples/webgpu_volume_perlin.ts delete mode 100644 examples-testing/examples/webgpu_water.ts delete mode 100644 examples-testing/examples/webgpu_xr_cubes.ts delete mode 100644 examples-testing/examples/webgpu_xr_native_layers.ts delete mode 100644 examples-testing/examples/webgpu_xr_rollercoaster.ts delete mode 100644 examples-testing/examples/webxr_ar_cones.ts delete mode 100644 examples-testing/examples/webxr_ar_hittest.ts delete mode 100644 examples-testing/examples/webxr_ar_lighting.ts delete mode 100644 examples-testing/examples/webxr_ar_plane_detection.ts delete mode 100644 examples-testing/examples/webxr_vr_handinput.ts delete mode 100644 examples-testing/examples/webxr_vr_panorama.ts delete mode 100644 examples-testing/examples/webxr_vr_panorama_depth.ts delete mode 100644 examples-testing/examples/webxr_vr_rollercoaster.ts delete mode 100644 examples-testing/examples/webxr_vr_sandbox.ts delete mode 100644 examples-testing/examples/webxr_vr_video.ts delete mode 100644 examples-testing/examples/webxr_xr_controls_transform.ts delete mode 100644 examples-testing/examples/webxr_xr_dragging_custom_depth.ts diff --git a/examples-testing/changes.patch b/examples-testing/changes.patch index 38a3d9c81..5953a1005 100644 --- a/examples-testing/changes.patch +++ b/examples-testing/changes.patch @@ -1,5 +1,5 @@ diff --git a/examples-testing/examples/css2d_label.ts b/examples-testing/examples/css2d_label.ts -index 1b288998..4a5af212 100644 +index 1b2889980..4a5af2121 100644 --- a/examples-testing/examples/css2d_label.ts +++ b/examples-testing/examples/css2d_label.ts @@ -7,7 +7,7 @@ import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; @@ -39,7 +39,7 @@ index 1b288998..4a5af212 100644 scene.add(moon); diff --git a/examples-testing/examples/css3d_mixed.ts b/examples-testing/examples/css3d_mixed.ts -index b526e73e..261f0d70 100644 +index b526e73e5..261f0d704 100644 --- a/examples-testing/examples/css3d_mixed.ts +++ b/examples-testing/examples/css3d_mixed.ts @@ -3,8 +3,11 @@ import * as THREE from 'three'; @@ -66,7 +66,7 @@ index b526e73e..261f0d70 100644 const material = new THREE.MeshStandardMaterial({ color: 0x2200ff }); diff --git a/examples-testing/examples/css3d_molecules.ts b/examples-testing/examples/css3d_molecules.ts -index 53847260..f08bb34a 100644 +index 538472607..f08bb34ab 100644 --- a/examples-testing/examples/css3d_molecules.ts +++ b/examples-testing/examples/css3d_molecules.ts @@ -5,11 +5,11 @@ import { PDBLoader } from 'three/addons/loaders/PDBLoader.js'; @@ -182,7 +182,7 @@ index 53847260..f08bb34a 100644 const atom = document.createElement('img'); atom.src = colorSprite; diff --git a/examples-testing/examples/css3d_orthographic.ts b/examples-testing/examples/css3d_orthographic.ts -index 4aabbed0..67e41fde 100644 +index 4aabbed08..67e41fde7 100644 --- a/examples-testing/examples/css3d_orthographic.ts +++ b/examples-testing/examples/css3d_orthographic.ts @@ -2,11 +2,11 @@ import * as THREE from 'three'; @@ -284,7 +284,7 @@ index 4aabbed0..67e41fde 100644 camera.setViewOffset( fullWidth || window.innerWidth, diff --git a/examples-testing/examples/css3d_periodictable.ts b/examples-testing/examples/css3d_periodictable.ts -index e3a33f79..516df464 100644 +index e3a33f796..516df464c 100644 --- a/examples-testing/examples/css3d_periodictable.ts +++ b/examples-testing/examples/css3d_periodictable.ts @@ -597,11 +597,16 @@ const table = [ @@ -389,7 +389,7 @@ index e3a33f79..516df464 100644 .onUpdate(render) .start(); diff --git a/examples-testing/examples/css3d_sandbox.ts b/examples-testing/examples/css3d_sandbox.ts -index 1088b84b..02f1fa75 100644 +index 1088b84b1..02f1fa75a 100644 --- a/examples-testing/examples/css3d_sandbox.ts +++ b/examples-testing/examples/css3d_sandbox.ts @@ -2,13 +2,13 @@ import * as THREE from 'three'; @@ -490,7 +490,7 @@ index 1088b84b..02f1fa75 100644 camera.setViewOffset( fullWidth || window.innerWidth, diff --git a/examples-testing/examples/css3d_sprites.ts b/examples-testing/examples/css3d_sprites.ts -index 39c3455a..b3092cf3 100644 +index 39c3455a5..b3092cf3c 100644 --- a/examples-testing/examples/css3d_sprites.ts +++ b/examples-testing/examples/css3d_sprites.ts @@ -4,12 +4,12 @@ import TWEEN from 'three/addons/libs/tween.module.js'; @@ -538,7 +538,7 @@ index 39c3455a..b3092cf3 100644 .onComplete(transition) .start(); diff --git a/examples-testing/examples/css3d_youtube.ts b/examples-testing/examples/css3d_youtube.ts -index 62652f87..3dcc2f12 100644 +index 62652f87f..3dcc2f12b 100644 --- a/examples-testing/examples/css3d_youtube.ts +++ b/examples-testing/examples/css3d_youtube.ts @@ -3,10 +3,10 @@ import * as THREE from 'three'; @@ -589,7 +589,7 @@ index 62652f87..3dcc2f12 100644 controls.addEventListener('start', function () { diff --git a/examples-testing/examples/games_fps.ts b/examples-testing/examples/games_fps.ts -index ed08fb5f..7b533de6 100644 +index ed08fb5fa..7b533de69 100644 --- a/examples-testing/examples/games_fps.ts +++ b/examples-testing/examples/games_fps.ts @@ -40,7 +40,7 @@ directionalLight.shadow.radius = 4; @@ -691,7 +691,7 @@ index ed08fb5f..7b533de6 100644 } }); diff --git a/examples-testing/examples/misc_animation_groups.ts b/examples-testing/examples/misc_animation_groups.ts -index 9fed234f..638dab1f 100644 +index 9fed234fc..638dab1f7 100644 --- a/examples-testing/examples/misc_animation_groups.ts +++ b/examples-testing/examples/misc_animation_groups.ts @@ -2,8 +2,8 @@ import * as THREE from 'three'; @@ -706,7 +706,7 @@ index 9fed234f..638dab1f 100644 init(); diff --git a/examples-testing/examples/misc_animation_keys.ts b/examples-testing/examples/misc_animation_keys.ts -index 32d497e5..505c3458 100644 +index 32d497e5a..505c34580 100644 --- a/examples-testing/examples/misc_animation_keys.ts +++ b/examples-testing/examples/misc_animation_keys.ts @@ -2,8 +2,8 @@ import * as THREE from 'three'; @@ -733,7 +733,7 @@ index 32d497e5..505c3458 100644 scene.add(mesh); diff --git a/examples-testing/examples/misc_boxselection.ts b/examples-testing/examples/misc_boxselection.ts -index e7079c40..e1d6904d 100644 +index e7079c405..e1d6904dc 100644 --- a/examples-testing/examples/misc_boxselection.ts +++ b/examples-testing/examples/misc_boxselection.ts @@ -5,8 +5,8 @@ import Stats from 'three/addons/libs/stats.module.js'; @@ -794,7 +794,7 @@ index e7079c40..e1d6904d 100644 } }); diff --git a/examples-testing/examples/misc_controls_arcball.ts b/examples-testing/examples/misc_controls_arcball.ts -index f2611be6..5982d695 100644 +index f2611be64..5982d6958 100644 --- a/examples-testing/examples/misc_controls_arcball.ts +++ b/examples-testing/examples/misc_controls_arcball.ts @@ -12,8 +12,12 @@ const cameraType = { type: 'Perspective' }; @@ -860,7 +860,7 @@ index f2611be6..5982d695 100644 camera = makeOrthographicCamera(); camera.position.set(0, 0, orthographicDistance); diff --git a/examples-testing/examples/misc_controls_drag.ts b/examples-testing/examples/misc_controls_drag.ts -index b12b0421..c3b378aa 100644 +index b12b0421e..c3b378aa7 100644 --- a/examples-testing/examples/misc_controls_drag.ts +++ b/examples-testing/examples/misc_controls_drag.ts @@ -2,12 +2,12 @@ import * as THREE from 'three'; @@ -912,7 +912,7 @@ index b12b0421..c3b378aa 100644 } diff --git a/examples-testing/examples/misc_controls_fly.ts b/examples-testing/examples/misc_controls_fly.ts -index 6493ebd8..50e66d62 100644 +index 6493ebd8a..50e66d62e 100644 --- a/examples-testing/examples/misc_controls_fly.ts +++ b/examples-testing/examples/misc_controls_fly.ts @@ -16,11 +16,15 @@ const MARGIN = 0; @@ -963,7 +963,7 @@ index 6493ebd8..50e66d62 100644 meshMoon = new THREE.Mesh(geometry, materialMoon); meshMoon.position.set(radius * 5, 0, 0); diff --git a/examples-testing/examples/misc_controls_map.ts b/examples-testing/examples/misc_controls_map.ts -index 9c7af0cd..2946df42 100644 +index 9c7af0cda..2946df424 100644 --- a/examples-testing/examples/misc_controls_map.ts +++ b/examples-testing/examples/misc_controls_map.ts @@ -4,7 +4,7 @@ import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; @@ -976,7 +976,7 @@ index 9c7af0cd..2946df42 100644 init(); //render(); // remove when using animation loop diff --git a/examples-testing/examples/misc_controls_orbit.ts b/examples-testing/examples/misc_controls_orbit.ts -index 7d2ec262..e8115f05 100644 +index 7d2ec2626..e8115f050 100644 --- a/examples-testing/examples/misc_controls_orbit.ts +++ b/examples-testing/examples/misc_controls_orbit.ts @@ -2,7 +2,7 @@ import * as THREE from 'three'; @@ -989,7 +989,7 @@ index 7d2ec262..e8115f05 100644 init(); //render(); // remove when using animation loop diff --git a/examples-testing/examples/misc_controls_pointerlock.ts b/examples-testing/examples/misc_controls_pointerlock.ts -index 0b6fcc51..d97bb4c3 100644 +index 0b6fcc516..d97bb4c3b 100644 --- a/examples-testing/examples/misc_controls_pointerlock.ts +++ b/examples-testing/examples/misc_controls_pointerlock.ts @@ -2,11 +2,11 @@ import * as THREE from 'three'; @@ -1059,7 +1059,7 @@ index 0b6fcc51..d97bb4c3 100644 const box = new THREE.Mesh(boxGeometry, boxMaterial); diff --git a/examples-testing/examples/misc_controls_trackball.ts b/examples-testing/examples/misc_controls_trackball.ts -index c2512a35..c4719944 100644 +index c2512a352..c4719944f 100644 --- a/examples-testing/examples/misc_controls_trackball.ts +++ b/examples-testing/examples/misc_controls_trackball.ts @@ -5,7 +5,12 @@ import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; @@ -1086,7 +1086,7 @@ index c2512a35..c4719944 100644 controls.rotateSpeed = 1.0; diff --git a/examples-testing/examples/misc_controls_transform.ts b/examples-testing/examples/misc_controls_transform.ts -index 6f7793d3..d7d327e9 100644 +index 6f7793d33..d7d327e96 100644 --- a/examples-testing/examples/misc_controls_transform.ts +++ b/examples-testing/examples/misc_controls_transform.ts @@ -3,8 +3,8 @@ import * as THREE from 'three'; @@ -1112,7 +1112,7 @@ index 6f7793d3..d7d327e9 100644 orbit.object = currentCamera; diff --git a/examples-testing/examples/misc_exporter_draco.ts b/examples-testing/examples/misc_exporter_draco.ts -index 40a62fb1..cb9d3f59 100644 +index 40a62fb18..cb9d3f599 100644 --- a/examples-testing/examples/misc_exporter_draco.ts +++ b/examples-testing/examples/misc_exporter_draco.ts @@ -4,7 +4,11 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -1144,7 +1144,7 @@ index 40a62fb1..cb9d3f59 100644 save(new Blob([buffer], { type: 'application/octet-stream' }), filename); } diff --git a/examples-testing/examples/misc_exporter_exr.ts b/examples-testing/examples/misc_exporter_exr.ts -index 014ea58a..643f6352 100644 +index 014ea58a0..643f63522 100644 --- a/examples-testing/examples/misc_exporter_exr.ts +++ b/examples-testing/examples/misc_exporter_exr.ts @@ -5,7 +5,14 @@ import { EXRExporter, ZIP_COMPRESSION, ZIPS_COMPRESSION, NO_COMPRESSION } from ' @@ -1173,7 +1173,7 @@ index 014ea58a..643f6352 100644 const link = document.createElement('a'); diff --git a/examples-testing/examples/misc_exporter_gltf.ts b/examples-testing/examples/misc_exporter_gltf.ts -index f6fe0345..98c4a481 100644 +index f6fe03450..98c4a4815 100644 --- a/examples-testing/examples/misc_exporter_gltf.ts +++ b/examples-testing/examples/misc_exporter_gltf.ts @@ -7,7 +7,7 @@ import { MeshoptDecoder } from 'three/addons/libs/meshopt_decoder.module.js'; @@ -1246,7 +1246,7 @@ index f6fe0345..98c4a481 100644 ctx.fillRect(0, 0, 64, 64); ctx.fillStyle = '#FFD500'; diff --git a/examples-testing/examples/misc_exporter_ktx2.ts b/examples-testing/examples/misc_exporter_ktx2.ts -index c96889a2..1c8535e0 100644 +index c96889a24..1c8535e0e 100644 --- a/examples-testing/examples/misc_exporter_ktx2.ts +++ b/examples-testing/examples/misc_exporter_ktx2.ts @@ -5,7 +5,14 @@ import { KTX2Exporter } from 'three/addons/exporters/KTX2Exporter.js'; @@ -1275,7 +1275,7 @@ index c96889a2..1c8535e0 100644 const link = document.createElement('a'); diff --git a/examples-testing/examples/misc_exporter_obj.ts b/examples-testing/examples/misc_exporter_obj.ts -index 025034da..73638fff 100644 +index 025034daf..73638fff1 100644 --- a/examples-testing/examples/misc_exporter_obj.ts +++ b/examples-testing/examples/misc_exporter_obj.ts @@ -4,7 +4,7 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -1320,7 +1320,7 @@ index 025034da..73638fff 100644 } diff --git a/examples-testing/examples/misc_exporter_ply.ts b/examples-testing/examples/misc_exporter_ply.ts -index b7e32468..c1682103 100644 +index b7e324688..c1682103c 100644 --- a/examples-testing/examples/misc_exporter_ply.ts +++ b/examples-testing/examples/misc_exporter_ply.ts @@ -4,7 +4,11 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -1357,7 +1357,7 @@ index b7e32468..c1682103 100644 save(new Blob([buffer], { type: 'application/octet-stream' }), filename); } diff --git a/examples-testing/examples/misc_exporter_stl.ts b/examples-testing/examples/misc_exporter_stl.ts -index ff6d6e2b..105aeb07 100644 +index ff6d6e2b5..105aeb07d 100644 --- a/examples-testing/examples/misc_exporter_stl.ts +++ b/examples-testing/examples/misc_exporter_stl.ts @@ -4,7 +4,11 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -1394,7 +1394,7 @@ index ff6d6e2b..105aeb07 100644 save(new Blob([buffer], { type: 'application/octet-stream' }), filename); } diff --git a/examples-testing/examples/misc_exporter_usdz.ts b/examples-testing/examples/misc_exporter_usdz.ts -index f1ce6548..5ec818dd 100644 +index f1ce65485..5ec818dd0 100644 --- a/examples-testing/examples/misc_exporter_usdz.ts +++ b/examples-testing/examples/misc_exporter_usdz.ts @@ -7,7 +7,7 @@ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; @@ -1434,7 +1434,7 @@ index f1ce6548..5ec818dd 100644 } diff --git a/examples-testing/examples/misc_uv_tests.ts b/examples-testing/examples/misc_uv_tests.ts -index 4f782d45..0759cfca 100644 +index 4f782d45f..0759cfcad 100644 --- a/examples-testing/examples/misc_uv_tests.ts +++ b/examples-testing/examples/misc_uv_tests.ts @@ -7,7 +7,7 @@ import { UVsDebug } from 'three/addons/utils/UVsDebug.js'; @@ -1447,7 +1447,7 @@ index 4f782d45..0759cfca 100644 d.innerHTML = '

' + name + '

'; diff --git a/examples-testing/examples/physics_ammo_instancing.ts b/examples-testing/examples/physics_ammo_instancing.ts -index 5cbee326..400763ef 100644 +index 5cbee3260..400763ef9 100644 --- a/examples-testing/examples/physics_ammo_instancing.ts +++ b/examples-testing/examples/physics_ammo_instancing.ts @@ -1,12 +1,12 @@ @@ -1468,7 +1468,7 @@ index 5cbee326..400763ef 100644 init(); diff --git a/examples-testing/examples/physics_jolt_instancing.ts b/examples-testing/examples/physics_jolt_instancing.ts -index 70980b8d..e102f557 100644 +index 70980b8d5..e102f5577 100644 --- a/examples-testing/examples/physics_jolt_instancing.ts +++ b/examples-testing/examples/physics_jolt_instancing.ts @@ -1,12 +1,12 @@ @@ -1489,7 +1489,7 @@ index 70980b8d..e102f557 100644 init(); diff --git a/examples-testing/examples/physics_rapier_basic.ts b/examples-testing/examples/physics_rapier_basic.ts -index 0bacda80..cb3c4e4b 100644 +index 0bacda80c..cb3c4e4b5 100644 --- a/examples-testing/examples/physics_rapier_basic.ts +++ b/examples-testing/examples/physics_rapier_basic.ts @@ -1,12 +1,16 @@ @@ -1527,7 +1527,7 @@ index 0bacda80..cb3c4e4b 100644 } } diff --git a/examples-testing/examples/physics_rapier_character_controller.ts b/examples-testing/examples/physics_rapier_character_controller.ts -index 8e797a98..d9b8caa6 100644 +index 8e797a98c..d9b8caa6f 100644 --- a/examples-testing/examples/physics_rapier_character_controller.ts +++ b/examples-testing/examples/physics_rapier_character_controller.ts @@ -1,12 +1,14 @@ @@ -1559,7 +1559,7 @@ index 8e797a98..d9b8caa6 100644 } diff --git a/examples-testing/examples/physics_rapier_instancing.ts b/examples-testing/examples/physics_rapier_instancing.ts -index 20509f7f..c6e0ca0e 100644 +index 20509f7fd..c6e0ca0e1 100644 --- a/examples-testing/examples/physics_rapier_instancing.ts +++ b/examples-testing/examples/physics_rapier_instancing.ts @@ -1,12 +1,12 @@ @@ -1580,7 +1580,7 @@ index 20509f7f..c6e0ca0e 100644 init(); diff --git a/examples-testing/examples/physics_rapier_joints.ts b/examples-testing/examples/physics_rapier_joints.ts -index 5f8805fe..318ffa8e 100644 +index 5f8805fe1..318ffa8ef 100644 --- a/examples-testing/examples/physics_rapier_joints.ts +++ b/examples-testing/examples/physics_rapier_joints.ts @@ -1,11 +1,11 @@ @@ -1608,7 +1608,7 @@ index 5f8805fe..318ffa8e 100644 const material = new THREE.MeshStandardMaterial({ color: 0xcccc00 }); diff --git a/examples-testing/examples/physics_rapier_terrain.ts b/examples-testing/examples/physics_rapier_terrain.ts -index 7708ceab..96c71642 100644 +index 7708ceab8..96c716420 100644 --- a/examples-testing/examples/physics_rapier_terrain.ts +++ b/examples-testing/examples/physics_rapier_terrain.ts @@ -1,7 +1,7 @@ @@ -1701,7 +1701,7 @@ index 7708ceab..96c71642 100644 const data = new Float32Array(size); const hRange = maxHeight - minHeight; diff --git a/examples-testing/examples/physics_rapier_vehicle_controller.ts b/examples-testing/examples/physics_rapier_vehicle_controller.ts -index fac5ef08..d4399154 100644 +index fac5ef082..d4399154b 100644 --- a/examples-testing/examples/physics_rapier_vehicle_controller.ts +++ b/examples-testing/examples/physics_rapier_vehicle_controller.ts @@ -1,12 +1,23 @@ @@ -1771,7 +1771,7 @@ index fac5ef08..d4399154 100644 const steerAngle = Math.PI / 4; diff --git a/examples-testing/examples/svg_lines.ts b/examples-testing/examples/svg_lines.ts -index 99b74c40..65aaf28d 100644 +index 99b74c405..65aaf28dd 100644 --- a/examples-testing/examples/svg_lines.ts +++ b/examples-testing/examples/svg_lines.ts @@ -4,7 +4,7 @@ import { SVGRenderer } from 'three/addons/renderers/SVGRenderer.js'; @@ -1784,7 +1784,7 @@ index 99b74c40..65aaf28d 100644 init(); animate(); diff --git a/examples-testing/examples/svg_sandbox.ts b/examples-testing/examples/svg_sandbox.ts -index fa66f10e..d9e680e8 100644 +index fa66f10e1..d9e680e89 100644 --- a/examples-testing/examples/svg_sandbox.ts +++ b/examples-testing/examples/svg_sandbox.ts @@ -7,9 +7,9 @@ import { SVGRenderer, SVGObject } from 'three/addons/renderers/SVGRenderer.js'; @@ -1869,7 +1869,7 @@ index fa66f10e..d9e680e8 100644 node.appendChild(doc.documentElement); diff --git a/examples-testing/examples/webaudio_orientation.ts b/examples-testing/examples/webaudio_orientation.ts -index 7baaa88a..e133c2cd 100644 +index 7baaa88a0..e133c2cd2 100644 --- a/examples-testing/examples/webaudio_orientation.ts +++ b/examples-testing/examples/webaudio_orientation.ts @@ -4,16 +4,16 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -1931,7 +1931,7 @@ index 7baaa88a..e133c2cd 100644 const wall = new THREE.Mesh(wallGeometry, wallMaterial); wall.position.set(0, 0.5, -0.5); diff --git a/examples-testing/examples/webaudio_sandbox.ts b/examples-testing/examples/webaudio_sandbox.ts -index 34afca98..93e03302 100644 +index 34afca985..93e033027 100644 --- a/examples-testing/examples/webaudio_sandbox.ts +++ b/examples-testing/examples/webaudio_sandbox.ts @@ -4,20 +4,24 @@ import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; @@ -2036,7 +2036,7 @@ index 34afca98..93e03302 100644 const gui = new GUI(); const soundControls = new SoundControls(); diff --git a/examples-testing/examples/webaudio_timing.ts b/examples-testing/examples/webaudio_timing.ts -index f37b5c5c..9b6c61f9 100644 +index f37b5c5cf..9b6c61f9a 100644 --- a/examples-testing/examples/webaudio_timing.ts +++ b/examples-testing/examples/webaudio_timing.ts @@ -2,22 +2,22 @@ import * as THREE from 'three'; @@ -2077,7 +2077,7 @@ index f37b5c5c..9b6c61f9 100644 ball.userData.down = false; } diff --git a/examples-testing/examples/webaudio_visualizer.ts b/examples-testing/examples/webaudio_visualizer.ts -index a3f58cb3..0bad866f 100644 +index a3f58cb36..0bad866f6 100644 --- a/examples-testing/examples/webaudio_visualizer.ts +++ b/examples-testing/examples/webaudio_visualizer.ts @@ -1,8 +1,13 @@ @@ -2123,7 +2123,7 @@ index a3f58cb3..0bad866f 100644 const geometry = new THREE.PlaneGeometry(1, 1); diff --git a/examples-testing/examples/webgl_animation_keyframes.ts b/examples-testing/examples/webgl_animation_keyframes.ts -index 20a44a9c..0b6f4895 100644 +index 20a44a9ce..0b6f48955 100644 --- a/examples-testing/examples/webgl_animation_keyframes.ts +++ b/examples-testing/examples/webgl_animation_keyframes.ts @@ -8,11 +8,11 @@ import { Sky } from 'three/addons/objects/Sky.js'; @@ -2150,7 +2150,7 @@ index 20a44a9c..0b6f4895 100644 const camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 100); diff --git a/examples-testing/examples/webgl_animation_multiple.ts b/examples-testing/examples/webgl_animation_multiple.ts -index 87355e96..4f93de7c 100644 +index 87355e968..4f93de7cf 100644 --- a/examples-testing/examples/webgl_animation_multiple.ts +++ b/examples-testing/examples/webgl_animation_multiple.ts @@ -4,11 +4,11 @@ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; @@ -2200,7 +2200,7 @@ index 87355e96..4f93de7c 100644 const model1 = shareSkinnedMesh.clone(); diff --git a/examples-testing/examples/webgl_animation_skinning_morph.ts b/examples-testing/examples/webgl_animation_skinning_morph.ts -index 0ae68e7d..d7e40e67 100644 +index 0ae68e7d7..d7e40e67a 100644 --- a/examples-testing/examples/webgl_animation_skinning_morph.ts +++ b/examples-testing/examples/webgl_animation_skinning_morph.ts @@ -5,10 +5,29 @@ import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; @@ -2302,7 +2302,7 @@ index 0ae68e7d..d7e40e67 100644 activeAction = actions[name]; diff --git a/examples-testing/examples/webgl_animation_walk.ts b/examples-testing/examples/webgl_animation_walk.ts -index b4ab9c44..ee10390a 100644 +index b4ab9c444..ee10390a0 100644 --- a/examples-testing/examples/webgl_animation_walk.ts +++ b/examples-testing/examples/webgl_animation_walk.ts @@ -6,10 +6,19 @@ import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; @@ -2462,7 +2462,7 @@ index b4ab9c44..ee10390a 100644 switch (event.code) { case 'ArrowUp': diff --git a/examples-testing/examples/webgl_buffergeometry.ts b/examples-testing/examples/webgl_buffergeometry.ts -index 28b2c96a..6a802ab6 100644 +index 28b2c96a4..6a802ab6c 100644 --- a/examples-testing/examples/webgl_buffergeometry.ts +++ b/examples-testing/examples/webgl_buffergeometry.ts @@ -2,17 +2,17 @@ import * as THREE from 'three'; @@ -2499,7 +2499,7 @@ index 28b2c96a..6a802ab6 100644 geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3).onUpload(disposeArray)); diff --git a/examples-testing/examples/webgl_buffergeometry_attributes_integer.ts b/examples-testing/examples/webgl_buffergeometry_attributes_integer.ts -index 00490b71..c553afe5 100644 +index 00490b716..c553afe5d 100644 --- a/examples-testing/examples/webgl_buffergeometry_attributes_integer.ts +++ b/examples-testing/examples/webgl_buffergeometry_attributes_integer.ts @@ -1,6 +1,6 @@ @@ -2520,7 +2520,7 @@ index 00490b71..c553afe5 100644 geometry.computeBoundingSphere(); diff --git a/examples-testing/examples/webgl_buffergeometry_attributes_none.ts b/examples-testing/examples/webgl_buffergeometry_attributes_none.ts -index a1424e87..f7fcf29f 100644 +index a1424e871..f7fcf29f9 100644 --- a/examples-testing/examples/webgl_buffergeometry_attributes_none.ts +++ b/examples-testing/examples/webgl_buffergeometry_attributes_none.ts @@ -1,6 +1,6 @@ @@ -2552,7 +2552,7 @@ index a1424e87..f7fcf29f 100644 mesh.rotation.y = (time / 1000.0) * 0.5; diff --git a/examples-testing/examples/webgl_buffergeometry_custom_attributes_particles.ts b/examples-testing/examples/webgl_buffergeometry_custom_attributes_particles.ts -index 0dffa65c..96dd8910 100644 +index 0dffa65cc..96dd89106 100644 --- a/examples-testing/examples/webgl_buffergeometry_custom_attributes_particles.ts +++ b/examples-testing/examples/webgl_buffergeometry_custom_attributes_particles.ts @@ -2,9 +2,11 @@ import * as THREE from 'three'; @@ -2590,7 +2590,7 @@ index 0dffa65c..96dd8910 100644 stats = new Stats(); diff --git a/examples-testing/examples/webgl_buffergeometry_drawrange.ts b/examples-testing/examples/webgl_buffergeometry_drawrange.ts -index 142ff43b..4b26ac2a 100644 +index 142ff43bf..4b26ac2ac 100644 --- a/examples-testing/examples/webgl_buffergeometry_drawrange.ts +++ b/examples-testing/examples/webgl_buffergeometry_drawrange.ts @@ -5,15 +5,15 @@ import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; @@ -2628,7 +2628,7 @@ index 142ff43b..4b26ac2a 100644 camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 4000); camera.position.z = 1750; diff --git a/examples-testing/examples/webgl_buffergeometry_glbufferattribute.ts b/examples-testing/examples/webgl_buffergeometry_glbufferattribute.ts -index 80fa828b..77f1bc38 100644 +index 80fa828bb..77f1bc385 100644 --- a/examples-testing/examples/webgl_buffergeometry_glbufferattribute.ts +++ b/examples-testing/examples/webgl_buffergeometry_glbufferattribute.ts @@ -2,11 +2,11 @@ import * as THREE from 'three'; @@ -2684,7 +2684,7 @@ index 80fa828b..77f1bc38 100644 gl.bufferData(gl.ARRAY_BUFFER, new Uint8Array(colors), gl.STATIC_DRAW); diff --git a/examples-testing/examples/webgl_buffergeometry_indexed.ts b/examples-testing/examples/webgl_buffergeometry_indexed.ts -index a2f9f379..4ad49d3c 100644 +index a2f9f3795..4ad49d3cd 100644 --- a/examples-testing/examples/webgl_buffergeometry_indexed.ts +++ b/examples-testing/examples/webgl_buffergeometry_indexed.ts @@ -3,9 +3,9 @@ import * as THREE from 'three'; @@ -2700,7 +2700,7 @@ index a2f9f379..4ad49d3c 100644 init(); diff --git a/examples-testing/examples/webgl_buffergeometry_instancing.ts b/examples-testing/examples/webgl_buffergeometry_instancing.ts -index b27f500f..c9432b18 100644 +index b27f500f0..c9432b181 100644 --- a/examples-testing/examples/webgl_buffergeometry_instancing.ts +++ b/examples-testing/examples/webgl_buffergeometry_instancing.ts @@ -3,14 +3,14 @@ import * as THREE from 'three'; @@ -2742,7 +2742,7 @@ index b27f500f..c9432b18 100644 object.rotation.y = time * 0.0005; object.material.uniforms['time'].value = time * 0.005; diff --git a/examples-testing/examples/webgl_buffergeometry_instancing_billboards.ts b/examples-testing/examples/webgl_buffergeometry_instancing_billboards.ts -index 2158dff3..2c97fbd9 100644 +index 2158dff39..2c97fbd94 100644 --- a/examples-testing/examples/webgl_buffergeometry_instancing_billboards.ts +++ b/examples-testing/examples/webgl_buffergeometry_instancing_billboards.ts @@ -2,10 +2,10 @@ import * as THREE from 'three'; @@ -2771,7 +2771,7 @@ index 2158dff3..2c97fbd9 100644 depthWrite: true, }); diff --git a/examples-testing/examples/webgl_buffergeometry_instancing_interleaved.ts b/examples-testing/examples/webgl_buffergeometry_instancing_interleaved.ts -index bef2c264..c0887a8d 100644 +index bef2c264d..c0887a8d3 100644 --- a/examples-testing/examples/webgl_buffergeometry_instancing_interleaved.ts +++ b/examples-testing/examples/webgl_buffergeometry_instancing_interleaved.ts @@ -2,8 +2,8 @@ import * as THREE from 'three'; @@ -2795,7 +2795,7 @@ index bef2c264..c0887a8d 100644 camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1000); diff --git a/examples-testing/examples/webgl_buffergeometry_lines.ts b/examples-testing/examples/webgl_buffergeometry_lines.ts -index d039e011..0b39e912 100644 +index d039e0112..0b39e912d 100644 --- a/examples-testing/examples/webgl_buffergeometry_lines.ts +++ b/examples-testing/examples/webgl_buffergeometry_lines.ts @@ -2,11 +2,11 @@ import * as THREE from 'three'; @@ -2840,7 +2840,7 @@ index d039e011..0b39e912 100644 for (let i = 0; i < segments; i++) { diff --git a/examples-testing/examples/webgl_buffergeometry_lines_indexed.ts b/examples-testing/examples/webgl_buffergeometry_lines_indexed.ts -index 58296087..8af0d6e9 100644 +index 58296087e..8af0d6e9d 100644 --- a/examples-testing/examples/webgl_buffergeometry_lines_indexed.ts +++ b/examples-testing/examples/webgl_buffergeometry_lines_indexed.ts @@ -2,16 +2,16 @@ import * as THREE from 'three'; @@ -2905,7 +2905,7 @@ index 58296087..8af0d6e9 100644 add_vertex(points[0]); diff --git a/examples-testing/examples/webgl_buffergeometry_points.ts b/examples-testing/examples/webgl_buffergeometry_points.ts -index 4547d9d0..357c3ddd 100644 +index 4547d9d08..357c3ddd6 100644 --- a/examples-testing/examples/webgl_buffergeometry_points.ts +++ b/examples-testing/examples/webgl_buffergeometry_points.ts @@ -2,17 +2,17 @@ import * as THREE from 'three'; @@ -2931,7 +2931,7 @@ index 4547d9d0..357c3ddd 100644 // diff --git a/examples-testing/examples/webgl_buffergeometry_points_interleaved.ts b/examples-testing/examples/webgl_buffergeometry_points_interleaved.ts -index 93eed992..60e05bc0 100644 +index 93eed992e..60e05bc0a 100644 --- a/examples-testing/examples/webgl_buffergeometry_points_interleaved.ts +++ b/examples-testing/examples/webgl_buffergeometry_points_interleaved.ts @@ -2,16 +2,16 @@ import * as THREE from 'three'; @@ -2956,7 +2956,7 @@ index 93eed992..60e05bc0 100644 camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 5, 3500); camera.position.z = 2750; diff --git a/examples-testing/examples/webgl_buffergeometry_rawshader.ts b/examples-testing/examples/webgl_buffergeometry_rawshader.ts -index 5bc113dc..225e220a 100644 +index 5bc113dc3..225e220ac 100644 --- a/examples-testing/examples/webgl_buffergeometry_rawshader.ts +++ b/examples-testing/examples/webgl_buffergeometry_rawshader.ts @@ -2,14 +2,14 @@ import * as THREE from 'three'; @@ -2998,7 +2998,7 @@ index 5bc113dc..225e220a 100644 object.rotation.y = time * 0.0005; object.material.uniforms.time.value = time * 0.005; diff --git a/examples-testing/examples/webgl_buffergeometry_selective_draw.ts b/examples-testing/examples/webgl_buffergeometry_selective_draw.ts -index d07176c5..ce51386e 100644 +index d07176c51..ce51386ee 100644 --- a/examples-testing/examples/webgl_buffergeometry_selective_draw.ts +++ b/examples-testing/examples/webgl_buffergeometry_selective_draw.ts @@ -2,8 +2,8 @@ import * as THREE from 'three'; @@ -3055,7 +3055,7 @@ index d07176c5..ce51386e 100644 function hideLines() { diff --git a/examples-testing/examples/webgl_buffergeometry_uint.ts b/examples-testing/examples/webgl_buffergeometry_uint.ts -index 0b8df6ec..dbb23e05 100644 +index 0b8df6ec7..dbb23e05e 100644 --- a/examples-testing/examples/webgl_buffergeometry_uint.ts +++ b/examples-testing/examples/webgl_buffergeometry_uint.ts @@ -2,16 +2,16 @@ import * as THREE from 'three'; @@ -3080,7 +3080,7 @@ index 0b8df6ec..dbb23e05 100644 // diff --git a/examples-testing/examples/webgl_camera.ts b/examples-testing/examples/webgl_camera.ts -index f3d66360..4235f508 100644 +index f3d663603..4235f508f 100644 --- a/examples-testing/examples/webgl_camera.ts +++ b/examples-testing/examples/webgl_camera.ts @@ -6,11 +6,11 @@ let SCREEN_WIDTH = window.innerWidth; @@ -3110,7 +3110,7 @@ index f3d66360..4235f508 100644 case 79 /*O*/: activeCamera = cameraOrtho; diff --git a/examples-testing/examples/webgl_camera_array.ts b/examples-testing/examples/webgl_camera_array.ts -index 8b10e27c..11295c5f 100644 +index 8b10e27cb..11295c5fd 100644 --- a/examples-testing/examples/webgl_camera_array.ts +++ b/examples-testing/examples/webgl_camera_array.ts @@ -1,7 +1,7 @@ @@ -3133,7 +3133,7 @@ index 8b10e27c..11295c5f 100644 subcamera.aspect = ASPECT_RATIO; subcamera.updateProjectionMatrix(); diff --git a/examples-testing/examples/webgl_camera_logarithmicdepthbuffer.ts b/examples-testing/examples/webgl_camera_logarithmicdepthbuffer.ts -index f1d44000..7f149db1 100644 +index f1d440004..7f149db11 100644 --- a/examples-testing/examples/webgl_camera_logarithmicdepthbuffer.ts +++ b/examples-testing/examples/webgl_camera_logarithmicdepthbuffer.ts @@ -1,6 +1,6 @@ @@ -3310,7 +3310,7 @@ index f1d44000..7f149db1 100644 if (amount === 0) return; const dir = amount / Math.abs(amount); diff --git a/examples-testing/examples/webgl_clipculldistance.ts b/examples-testing/examples/webgl_clipculldistance.ts -index 42c1ff17..e7210e99 100644 +index 42c1ff17f..e7210e997 100644 --- a/examples-testing/examples/webgl_clipculldistance.ts +++ b/examples-testing/examples/webgl_clipculldistance.ts @@ -2,9 +2,14 @@ import * as THREE from 'three'; @@ -3351,7 +3351,7 @@ index 42c1ff17..e7210e99 100644 transparent: true, vertexColors: true, diff --git a/examples-testing/examples/webgl_clipping.ts b/examples-testing/examples/webgl_clipping.ts -index cde10c7d..6ab3ba65 100644 +index cde10c7d1..6ab3ba65c 100644 --- a/examples-testing/examples/webgl_clipping.ts +++ b/examples-testing/examples/webgl_clipping.ts @@ -5,7 +5,12 @@ import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; @@ -3389,7 +3389,7 @@ index cde10c7d..6ab3ba65 100644 get Plane() { diff --git a/examples-testing/examples/webgl_clipping_advanced.ts b/examples-testing/examples/webgl_clipping_advanced.ts -index f65f0004..988b56d8 100644 +index f65f00043..988b56d80 100644 --- a/examples-testing/examples/webgl_clipping_advanced.ts +++ b/examples-testing/examples/webgl_clipping_advanced.ts @@ -5,7 +5,7 @@ import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; @@ -3511,7 +3511,7 @@ index f65f0004..988b56d8 100644 const planeMeshes = volumeVisualization.children; diff --git a/examples-testing/examples/webgl_clipping_intersection.ts b/examples-testing/examples/webgl_clipping_intersection.ts -index 5f45e45d..cb3cbdf2 100644 +index 5f45e45df..cb3cbdf2e 100644 --- a/examples-testing/examples/webgl_clipping_intersection.ts +++ b/examples-testing/examples/webgl_clipping_intersection.ts @@ -4,7 +4,7 @@ import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; @@ -3544,7 +3544,7 @@ index 5f45e45d..cb3cbdf2 100644 render(); diff --git a/examples-testing/examples/webgl_clipping_stencil.ts b/examples-testing/examples/webgl_clipping_stencil.ts -index 7cb1f5c5..859a7551 100644 +index 7cb1f5c52..859a7551f 100644 --- a/examples-testing/examples/webgl_clipping_stencil.ts +++ b/examples-testing/examples/webgl_clipping_stencil.ts @@ -3,9 +3,13 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -3574,7 +3574,7 @@ index 7cb1f5c5..859a7551 100644 const baseMat = new THREE.MeshBasicMaterial(); baseMat.depthWrite = false; diff --git a/examples-testing/examples/webgl_custom_attributes.ts b/examples-testing/examples/webgl_custom_attributes.ts -index 0dc89774..431876d6 100644 +index 0dc897748..431876d68 100644 --- a/examples-testing/examples/webgl_custom_attributes.ts +++ b/examples-testing/examples/webgl_custom_attributes.ts @@ -2,11 +2,16 @@ import * as THREE from 'three'; @@ -3618,7 +3618,7 @@ index 0dc89774..431876d6 100644 stats = new Stats(); diff --git a/examples-testing/examples/webgl_custom_attributes_lines.ts b/examples-testing/examples/webgl_custom_attributes_lines.ts -index 3e2454e9..d89a77d5 100644 +index 3e2454e92..d89a77d53 100644 --- a/examples-testing/examples/webgl_custom_attributes_lines.ts +++ b/examples-testing/examples/webgl_custom_attributes_lines.ts @@ -1,20 +1,25 @@ @@ -3672,7 +3672,7 @@ index 3e2454e9..d89a77d5 100644 stats = new Stats(); diff --git a/examples-testing/examples/webgl_custom_attributes_points.ts b/examples-testing/examples/webgl_custom_attributes_points.ts -index ae112980..9035134c 100644 +index ae112980a..9035134c7 100644 --- a/examples-testing/examples/webgl_custom_attributes_points.ts +++ b/examples-testing/examples/webgl_custom_attributes_points.ts @@ -2,9 +2,9 @@ import * as THREE from 'three'; @@ -3708,7 +3708,7 @@ index ae112980..9035134c 100644 stats = new Stats(); diff --git a/examples-testing/examples/webgl_custom_attributes_points2.ts b/examples-testing/examples/webgl_custom_attributes_points2.ts -index edd158fa..6f58c70a 100644 +index edd158fa1..6f58c70ad 100644 --- a/examples-testing/examples/webgl_custom_attributes_points2.ts +++ b/examples-testing/examples/webgl_custom_attributes_points2.ts @@ -4,8 +4,8 @@ import Stats from 'three/addons/libs/stats.module.js'; @@ -3783,7 +3783,7 @@ index edd158fa..6f58c70a 100644 function animate() { diff --git a/examples-testing/examples/webgl_custom_attributes_points3.ts b/examples-testing/examples/webgl_custom_attributes_points3.ts -index 1bca8ccd..66f12591 100644 +index 1bca8ccd4..66f12591f 100644 --- a/examples-testing/examples/webgl_custom_attributes_points3.ts +++ b/examples-testing/examples/webgl_custom_attributes_points3.ts @@ -4,11 +4,11 @@ import Stats from 'three/addons/libs/stats.module.js'; @@ -3860,7 +3860,7 @@ index 1bca8ccd..66f12591 100644 stats = new Stats(); diff --git a/examples-testing/examples/webgl_decals.ts b/examples-testing/examples/webgl_decals.ts -index 8f77c30f..acc5b219 100644 +index 8f77c30fc..acc5b2193 100644 --- a/examples-testing/examples/webgl_decals.ts +++ b/examples-testing/examples/webgl_decals.ts @@ -7,12 +7,12 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -3942,7 +3942,7 @@ index 8f77c30f..acc5b219 100644 specular: 0x111111, map: map, diff --git a/examples-testing/examples/webgl_effects_anaglyph.ts b/examples-testing/examples/webgl_effects_anaglyph.ts -index 7e67c917..e3d0719b 100644 +index 7e67c9173..e3d0719bf 100644 --- a/examples-testing/examples/webgl_effects_anaglyph.ts +++ b/examples-testing/examples/webgl_effects_anaglyph.ts @@ -2,9 +2,13 @@ import * as THREE from 'three'; @@ -3971,7 +3971,7 @@ index 7e67c917..e3d0719b 100644 mouseY = (event.clientY - windowHalfY) / 100; } diff --git a/examples-testing/examples/webgl_effects_ascii.ts b/examples-testing/examples/webgl_effects_ascii.ts -index a412bb79..751102d7 100644 +index a412bb79e..751102d76 100644 --- a/examples-testing/examples/webgl_effects_ascii.ts +++ b/examples-testing/examples/webgl_effects_ascii.ts @@ -3,9 +3,13 @@ import * as THREE from 'three'; @@ -3991,7 +3991,7 @@ index a412bb79..751102d7 100644 const start = Date.now(); diff --git a/examples-testing/examples/webgl_effects_parallaxbarrier.ts b/examples-testing/examples/webgl_effects_parallaxbarrier.ts -index 90c86797..e03cb5d7 100644 +index 90c867973..e03cb5d7b 100644 --- a/examples-testing/examples/webgl_effects_parallaxbarrier.ts +++ b/examples-testing/examples/webgl_effects_parallaxbarrier.ts @@ -2,9 +2,13 @@ import * as THREE from 'three'; @@ -4020,7 +4020,7 @@ index 90c86797..e03cb5d7 100644 mouseY = (event.clientY - windowHalfY) / 100; } diff --git a/examples-testing/examples/webgl_effects_stereo.ts b/examples-testing/examples/webgl_effects_stereo.ts -index 67385054..98538672 100644 +index 673850541..985386724 100644 --- a/examples-testing/examples/webgl_effects_stereo.ts +++ b/examples-testing/examples/webgl_effects_stereo.ts @@ -2,9 +2,13 @@ import * as THREE from 'three'; @@ -4049,7 +4049,7 @@ index 67385054..98538672 100644 mouseY = (event.clientY - windowHalfY) * 0.01; } diff --git a/examples-testing/examples/webgl_framebuffer_texture.ts b/examples-testing/examples/webgl_framebuffer_texture.ts -index df4acc9d..636f9db9 100644 +index df4acc9d6..636f9db9c 100644 --- a/examples-testing/examples/webgl_framebuffer_texture.ts +++ b/examples-testing/examples/webgl_framebuffer_texture.ts @@ -3,10 +3,10 @@ import * as THREE from 'three'; @@ -4085,7 +4085,7 @@ index df4acc9d..636f9db9 100644 for (let i = 0; i < l; i++) { diff --git a/examples-testing/examples/webgl_furnace_test.ts b/examples-testing/examples/webgl_furnace_test.ts -index a8195417..46230b4a 100644 +index a81954176..46230b4a9 100644 --- a/examples-testing/examples/webgl_furnace_test.ts +++ b/examples-testing/examples/webgl_furnace_test.ts @@ -1,6 +1,6 @@ @@ -4117,7 +4117,7 @@ index a8195417..46230b4a 100644 render(); diff --git a/examples-testing/examples/webgl_geometries.ts b/examples-testing/examples/webgl_geometries.ts -index 0eb50120..a8181348 100644 +index 0eb50120c..a81813486 100644 --- a/examples-testing/examples/webgl_geometries.ts +++ b/examples-testing/examples/webgl_geometries.ts @@ -5,7 +5,7 @@ import Stats from 'three/addons/libs/stats.module.js'; @@ -4139,7 +4139,7 @@ index 0eb50120..a8181348 100644 object.rotation.y = timer * 2.5; } diff --git a/examples-testing/examples/webgl_geometry_colors.ts b/examples-testing/examples/webgl_geometry_colors.ts -index 3166c03b..d2c22418 100644 +index 3166c03bc..d2c22418f 100644 --- a/examples-testing/examples/webgl_geometry_colors.ts +++ b/examples-testing/examples/webgl_geometry_colors.ts @@ -2,9 +2,9 @@ import * as THREE from 'three'; @@ -4192,7 +4192,7 @@ index 3166c03b..d2c22418 100644 mouseY = event.clientY - windowHalfY; } diff --git a/examples-testing/examples/webgl_geometry_colors_lookuptable.ts b/examples-testing/examples/webgl_geometry_colors_lookuptable.ts -index 6b013852..c46e067e 100644 +index 6b0138529..c46e067e6 100644 --- a/examples-testing/examples/webgl_geometry_colors_lookuptable.ts +++ b/examples-testing/examples/webgl_geometry_colors_lookuptable.ts @@ -5,19 +5,19 @@ import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; @@ -4240,7 +4240,7 @@ index 6b013852..c46e067e 100644 map.needsUpdate = true; } diff --git a/examples-testing/examples/webgl_geometry_convex.ts b/examples-testing/examples/webgl_geometry_convex.ts -index 09516c07..fe904221 100644 +index 09516c070..fe9042217 100644 --- a/examples-testing/examples/webgl_geometry_convex.ts +++ b/examples-testing/examples/webgl_geometry_convex.ts @@ -4,7 +4,7 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -4262,7 +4262,7 @@ index 09516c07..fe904221 100644 // if normal and uv attributes are not removed, mergeVertices() can't consolidate identical vertices with different normal/uv data diff --git a/examples-testing/examples/webgl_geometry_cube.ts b/examples-testing/examples/webgl_geometry_cube.ts -index 572601ac..136a3141 100644 +index 572601acb..136a31413 100644 --- a/examples-testing/examples/webgl_geometry_cube.ts +++ b/examples-testing/examples/webgl_geometry_cube.ts @@ -1,7 +1,7 @@ @@ -4276,7 +4276,7 @@ index 572601ac..136a3141 100644 init(); diff --git a/examples-testing/examples/webgl_geometry_extrude_shapes.ts b/examples-testing/examples/webgl_geometry_extrude_shapes.ts -index a905c8ea..00283d47 100644 +index a905c8ea3..00283d47e 100644 --- a/examples-testing/examples/webgl_geometry_extrude_shapes.ts +++ b/examples-testing/examples/webgl_geometry_extrude_shapes.ts @@ -2,7 +2,7 @@ import * as THREE from 'three'; @@ -4289,7 +4289,7 @@ index a905c8ea..00283d47 100644 init(); diff --git a/examples-testing/examples/webgl_geometry_extrude_splines.ts b/examples-testing/examples/webgl_geometry_extrude_splines.ts -index 8636812f..90e2b7b0 100644 +index 8636812f7..90e2b7b06 100644 --- a/examples-testing/examples/webgl_geometry_extrude_splines.ts +++ b/examples-testing/examples/webgl_geometry_extrude_splines.ts @@ -6,9 +6,14 @@ import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; @@ -4359,7 +4359,7 @@ index 8636812f..90e2b7b0 100644 }); folderGeometry diff --git a/examples-testing/examples/webgl_geometry_minecraft.ts b/examples-testing/examples/webgl_geometry_minecraft.ts -index 22015655..32f4cb68 100644 +index 220156552..32f4cb688 100644 --- a/examples-testing/examples/webgl_geometry_minecraft.ts +++ b/examples-testing/examples/webgl_geometry_minecraft.ts @@ -6,9 +6,9 @@ import { FirstPersonControls } from 'three/addons/controls/FirstPersonControls.j @@ -4402,7 +4402,7 @@ index 22015655..32f4cb68 100644 } diff --git a/examples-testing/examples/webgl_geometry_nurbs.ts b/examples-testing/examples/webgl_geometry_nurbs.ts -index ecd79c67..74676e45 100644 +index ecd79c67e..74676e45c 100644 --- a/examples-testing/examples/webgl_geometry_nurbs.ts +++ b/examples-testing/examples/webgl_geometry_nurbs.ts @@ -7,10 +7,10 @@ import { NURBSSurface } from 'three/addons/curves/NURBSSurface.js'; @@ -4494,7 +4494,7 @@ index ecd79c67..74676e45 100644 document.removeEventListener('pointermove', onPointerMove); diff --git a/examples-testing/examples/webgl_geometry_shapes.ts b/examples-testing/examples/webgl_geometry_shapes.ts -index 0c25d885..2042c353 100644 +index 0c25d8855..2042c3530 100644 --- a/examples-testing/examples/webgl_geometry_shapes.ts +++ b/examples-testing/examples/webgl_geometry_shapes.ts @@ -2,11 +2,11 @@ import * as THREE from 'three'; @@ -4584,7 +4584,7 @@ index 0c25d885..2042c353 100644 document.removeEventListener('pointermove', onPointerMove); diff --git a/examples-testing/examples/webgl_geometry_teapot.ts b/examples-testing/examples/webgl_geometry_teapot.ts -index 2516d660..0d7d5628 100644 +index 2516d660c..0d7d5628f 100644 --- a/examples-testing/examples/webgl_geometry_teapot.ts +++ b/examples-testing/examples/webgl_geometry_teapot.ts @@ -5,22 +5,30 @@ import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; @@ -4631,7 +4631,7 @@ index 2516d660..0d7d5628 100644 init(); render(); diff --git a/examples-testing/examples/webgl_geometry_terrain.ts b/examples-testing/examples/webgl_geometry_terrain.ts -index 55b4aa47..2ed4b9f6 100644 +index 55b4aa474..2ed4b9f6c 100644 --- a/examples-testing/examples/webgl_geometry_terrain.ts +++ b/examples-testing/examples/webgl_geometry_terrain.ts @@ -5,9 +5,9 @@ import Stats from 'three/addons/libs/stats.module.js'; @@ -4693,7 +4693,7 @@ index 55b4aa47..2ed4b9f6 100644 context.drawImage(canvas, 0, 0); diff --git a/examples-testing/examples/webgl_geometry_terrain_raycast.ts b/examples-testing/examples/webgl_geometry_terrain_raycast.ts -index f1383c13..6e9cd025 100644 +index f1383c138..6e9cd0256 100644 --- a/examples-testing/examples/webgl_geometry_terrain_raycast.ts +++ b/examples-testing/examples/webgl_geometry_terrain_raycast.ts @@ -5,18 +5,18 @@ import Stats from 'three/addons/libs/stats.module.js'; @@ -4783,7 +4783,7 @@ index f1383c13..6e9cd025 100644 helper.position.copy(intersects[0].point); } diff --git a/examples-testing/examples/webgl_geometry_text.ts b/examples-testing/examples/webgl_geometry_text.ts -index 1a9d00d0..e5947329 100644 +index 1a9d00d06..e59473290 100644 --- a/examples-testing/examples/webgl_geometry_text.ts +++ b/examples-testing/examples/webgl_geometry_text.ts @@ -1,23 +1,23 @@ @@ -4895,7 +4895,7 @@ index 1a9d00d0..e5947329 100644 document.removeEventListener('pointermove', onPointerMove); diff --git a/examples-testing/examples/webgl_geometry_text_shapes.ts b/examples-testing/examples/webgl_geometry_text_shapes.ts -index d9633cd1..c2b07d41 100644 +index d9633cd15..c2b07d41f 100644 --- a/examples-testing/examples/webgl_geometry_text_shapes.ts +++ b/examples-testing/examples/webgl_geometry_text_shapes.ts @@ -3,7 +3,7 @@ import * as THREE from 'three'; @@ -4938,7 +4938,7 @@ index d9633cd1..c2b07d41 100644 if (shape.holes && shape.holes.length > 0) { for (let j = 0; j < shape.holes.length; j++) { diff --git a/examples-testing/examples/webgl_geometry_text_stroke.ts b/examples-testing/examples/webgl_geometry_text_stroke.ts -index 373892c7..9bf58924 100644 +index 373892c74..9bf589244 100644 --- a/examples-testing/examples/webgl_geometry_text_stroke.ts +++ b/examples-testing/examples/webgl_geometry_text_stroke.ts @@ -5,7 +5,7 @@ import { SVGLoader } from 'three/addons/loaders/SVGLoader.js'; @@ -5000,7 +5000,7 @@ index 373892c7..9bf58924 100644 if (shape.holes && shape.holes.length > 0) { for (let j = 0; j < shape.holes.length; j++) { diff --git a/examples-testing/examples/webgl_gpgpu_birds.ts b/examples-testing/examples/webgl_gpgpu_birds.ts -index 20a5e0d9..e9293211 100644 +index 20a5e0d97..e9293211c 100644 --- a/examples-testing/examples/webgl_gpgpu_birds.ts +++ b/examples-testing/examples/webgl_gpgpu_birds.ts @@ -3,7 +3,7 @@ import * as THREE from 'three'; @@ -5113,7 +5113,7 @@ index 20a5e0d9..e9293211 100644 mouseX = event.clientX - windowHalfX; diff --git a/examples-testing/examples/webgl_gpgpu_birds_gltf.ts b/examples-testing/examples/webgl_gpgpu_birds_gltf.ts -index 05f81a86..f86d0d38 100644 +index 05f81a869..f86d0d386 100644 --- a/examples-testing/examples/webgl_gpgpu_birds_gltf.ts +++ b/examples-testing/examples/webgl_gpgpu_birds_gltf.ts @@ -2,7 +2,7 @@ import * as THREE from 'three'; @@ -5262,7 +5262,7 @@ index 05f81a86..f86d0d38 100644 mouseX = event.clientX - windowHalfX; diff --git a/examples-testing/examples/webgl_gpgpu_protoplanet.ts b/examples-testing/examples/webgl_gpgpu_protoplanet.ts -index 30444ddb..e1c40aef 100644 +index 30444ddba..e1c40aefa 100644 --- a/examples-testing/examples/webgl_gpgpu_protoplanet.ts +++ b/examples-testing/examples/webgl_gpgpu_protoplanet.ts @@ -4,22 +4,32 @@ import Stats from 'three/addons/libs/stats.module.js'; @@ -5356,7 +5356,7 @@ index 30444ddb..e1c40aef 100644 } diff --git a/examples-testing/examples/webgl_gpgpu_water.ts b/examples-testing/examples/webgl_gpgpu_water.ts -index 3210a298..183dac1e 100644 +index 3210a298a..183dac1e3 100644 --- a/examples-testing/examples/webgl_gpgpu_water.ts +++ b/examples-testing/examples/webgl_gpgpu_water.ts @@ -4,7 +4,7 @@ import Stats from 'three/addons/libs/stats.module.js'; @@ -5525,7 +5525,7 @@ index 3210a298..183dac1e 100644 shader.uniforms[name] = { value: this.extra[name] }; } diff --git a/examples-testing/examples/webgl_helpers.ts b/examples-testing/examples/webgl_helpers.ts -index a8c3b977..09ad778d 100644 +index a8c3b9773..09ad778dc 100644 --- a/examples-testing/examples/webgl_helpers.ts +++ b/examples-testing/examples/webgl_helpers.ts @@ -5,10 +5,10 @@ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; @@ -5562,7 +5562,7 @@ index a8c3b977..09ad778d 100644 line.material.opacity = 0.25; line.material.transparent = true; diff --git a/examples-testing/examples/webgl_instancing_dynamic.ts b/examples-testing/examples/webgl_instancing_dynamic.ts -index bc4a8866..105d30e1 100644 +index bc4a88662..105d30e11 100644 --- a/examples-testing/examples/webgl_instancing_dynamic.ts +++ b/examples-testing/examples/webgl_instancing_dynamic.ts @@ -3,15 +3,19 @@ import * as THREE from 'three'; @@ -5598,7 +5598,7 @@ index bc4a8866..105d30e1 100644 mesh.computeBoundingSphere(); diff --git a/examples-testing/examples/webgl_instancing_morph.ts b/examples-testing/examples/webgl_instancing_morph.ts -index 70e89199..d6fb00fb 100644 +index 70e89199c..d6fb00fbf 100644 --- a/examples-testing/examples/webgl_instancing_morph.ts +++ b/examples-testing/examples/webgl_instancing_morph.ts @@ -4,7 +4,13 @@ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; @@ -5635,7 +5635,7 @@ index 70e89199..d6fb00fb 100644 renderer.render(scene, camera); diff --git a/examples-testing/examples/webgl_instancing_performance.ts b/examples-testing/examples/webgl_instancing_performance.ts -index bf1deaba..705d2cf3 100644 +index bf1deabad..705d2cf36 100644 --- a/examples-testing/examples/webgl_instancing_performance.ts +++ b/examples-testing/examples/webgl_instancing_performance.ts @@ -6,8 +6,12 @@ import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; @@ -5736,7 +5736,7 @@ index bf1deaba..705d2cf3 100644 const k = 1024; diff --git a/examples-testing/examples/webgl_instancing_raycast.ts b/examples-testing/examples/webgl_instancing_raycast.ts -index 371ea070..861bc9c5 100644 +index 371ea070b..861bc9c51 100644 --- a/examples-testing/examples/webgl_instancing_raycast.ts +++ b/examples-testing/examples/webgl_instancing_raycast.ts @@ -4,9 +4,13 @@ import Stats from 'three/addons/libs/stats.module.js'; @@ -5782,7 +5782,7 @@ index 371ea070..861bc9c5 100644 } diff --git a/examples-testing/examples/webgl_instancing_scatter.ts b/examples-testing/examples/webgl_instancing_scatter.ts -index fc3b9cc9..d0904e8e 100644 +index fc3b9cc9f..d0904e8e4 100644 --- a/examples-testing/examples/webgl_instancing_scatter.ts +++ b/examples-testing/examples/webgl_instancing_scatter.ts @@ -5,7 +5,7 @@ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; @@ -5855,7 +5855,7 @@ index fc3b9cc9..d0904e8e 100644 ages[i] += 0.005; diff --git a/examples-testing/examples/webgl_interactive_buffergeometry.ts b/examples-testing/examples/webgl_interactive_buffergeometry.ts -index 1d6608b1..c6aca942 100644 +index 1d6608b13..c6aca942f 100644 --- a/examples-testing/examples/webgl_interactive_buffergeometry.ts +++ b/examples-testing/examples/webgl_interactive_buffergeometry.ts @@ -2,18 +2,18 @@ import * as THREE from 'three'; @@ -5915,7 +5915,7 @@ index 1d6608b1..c6aca942 100644 linePosition.copyAt(0, meshPosition, face.a); linePosition.copyAt(1, meshPosition, face.b); diff --git a/examples-testing/examples/webgl_interactive_cubes.ts b/examples-testing/examples/webgl_interactive_cubes.ts -index adfcfddf..d26cee37 100644 +index adfcfddf8..d26cee370 100644 --- a/examples-testing/examples/webgl_interactive_cubes.ts +++ b/examples-testing/examples/webgl_interactive_cubes.ts @@ -2,10 +2,10 @@ import * as THREE from 'three'; @@ -5960,7 +5960,7 @@ index adfcfddf..d26cee37 100644 INTERSECTED = null; } diff --git a/examples-testing/examples/webgl_interactive_cubes_gpu.ts b/examples-testing/examples/webgl_interactive_cubes_gpu.ts -index 5b19d208..b8772168 100644 +index 5b19d2085..b8772168d 100644 --- a/examples-testing/examples/webgl_interactive_cubes_gpu.ts +++ b/examples-testing/examples/webgl_interactive_cubes_gpu.ts @@ -5,12 +5,12 @@ import Stats from 'three/addons/libs/stats.module.js'; @@ -6018,7 +6018,7 @@ index 5b19d208..b8772168 100644 pointer.y = e.clientY; } diff --git a/examples-testing/examples/webgl_interactive_cubes_ortho.ts b/examples-testing/examples/webgl_interactive_cubes_ortho.ts -index 520674b5..4b083ffe 100644 +index 520674b5f..4b083ffe9 100644 --- a/examples-testing/examples/webgl_interactive_cubes_ortho.ts +++ b/examples-testing/examples/webgl_interactive_cubes_ortho.ts @@ -2,11 +2,11 @@ import * as THREE from 'three'; @@ -6064,7 +6064,7 @@ index 520674b5..4b083ffe 100644 INTERSECTED = null; } diff --git a/examples-testing/examples/webgl_interactive_lines.ts b/examples-testing/examples/webgl_interactive_lines.ts -index b137c550..891e5541 100644 +index b137c5501..891e5541c 100644 --- a/examples-testing/examples/webgl_interactive_lines.ts +++ b/examples-testing/examples/webgl_interactive_lines.ts @@ -2,8 +2,13 @@ import * as THREE from 'three'; @@ -6093,7 +6093,7 @@ index b137c550..891e5541 100644 pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; } diff --git a/examples-testing/examples/webgl_interactive_points.ts b/examples-testing/examples/webgl_interactive_points.ts -index b6be0df0..2bc27dae 100644 +index b6be0df05..2bc27dae9 100644 --- a/examples-testing/examples/webgl_interactive_points.ts +++ b/examples-testing/examples/webgl_interactive_points.ts @@ -4,19 +4,19 @@ import Stats from 'three/addons/libs/stats.module.js'; @@ -6174,7 +6174,7 @@ index b6be0df0..2bc27dae 100644 attributes.size.array[INTERSECTED] = PARTICLE_SIZE * 1.25; attributes.size.needsUpdate = true; diff --git a/examples-testing/examples/webgl_interactive_raycasting_points.ts b/examples-testing/examples/webgl_interactive_raycasting_points.ts -index 50b8d1e1..8f0a5c28 100644 +index 50b8d1e1d..8f0a5c289 100644 --- a/examples-testing/examples/webgl_interactive_raycasting_points.ts +++ b/examples-testing/examples/webgl_interactive_raycasting_points.ts @@ -2,16 +2,16 @@ import * as THREE from 'three'; @@ -6253,7 +6253,7 @@ index 50b8d1e1..8f0a5c28 100644 pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; } diff --git a/examples-testing/examples/webgl_interactive_voxelpainter.ts b/examples-testing/examples/webgl_interactive_voxelpainter.ts -index 48b16f3b..dae1d758 100644 +index 48b16f3b7..dae1d7589 100644 --- a/examples-testing/examples/webgl_interactive_voxelpainter.ts +++ b/examples-testing/examples/webgl_interactive_voxelpainter.ts @@ -1,15 +1,15 @@ @@ -6333,7 +6333,7 @@ index 48b16f3b..dae1d758 100644 case 16: isShiftDown = false; diff --git a/examples-testing/examples/webgl_lensflares.ts b/examples-testing/examples/webgl_lensflares.ts -index 0a55bec4..b45da6b9 100644 +index 0a55bec42..b45da6b93 100644 --- a/examples-testing/examples/webgl_lensflares.ts +++ b/examples-testing/examples/webgl_lensflares.ts @@ -5,10 +5,10 @@ import Stats from 'three/addons/libs/stats.module.js'; @@ -6360,7 +6360,7 @@ index 0a55bec4..b45da6b9 100644 light.color.setHSL(h, s, l); light.position.set(x, y, z); diff --git a/examples-testing/examples/webgl_lightprobe.ts b/examples-testing/examples/webgl_lightprobe.ts -index 58f021e6..1427cce4 100644 +index 58f021e6d..1427cce47 100644 --- a/examples-testing/examples/webgl_lightprobe.ts +++ b/examples-testing/examples/webgl_lightprobe.ts @@ -8,12 +8,15 @@ import { LightProbeGenerator } from 'three/addons/lights/LightProbeGenerator.js' @@ -6392,7 +6392,7 @@ index 58f021e6..1427cce4 100644 prefix + 'px' + postfix, prefix + 'nx' + postfix, diff --git a/examples-testing/examples/webgl_lightprobe_cubecamera.ts b/examples-testing/examples/webgl_lightprobe_cubecamera.ts -index 65425d4e..a9a0533e 100644 +index 65425d4e7..a9a0533ef 100644 --- a/examples-testing/examples/webgl_lightprobe_cubecamera.ts +++ b/examples-testing/examples/webgl_lightprobe_cubecamera.ts @@ -4,9 +4,9 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -6417,7 +6417,7 @@ index 65425d4e..a9a0533e 100644 prefix + 'px' + postfix, prefix + 'nx' + postfix, diff --git a/examples-testing/examples/webgl_lights_hemisphere.ts b/examples-testing/examples/webgl_lights_hemisphere.ts -index 2910b15d..32daa5e5 100644 +index 2910b15de..32daa5e5d 100644 --- a/examples-testing/examples/webgl_lights_hemisphere.ts +++ b/examples-testing/examples/webgl_lights_hemisphere.ts @@ -5,9 +5,9 @@ import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; @@ -6454,7 +6454,7 @@ index 2910b15d..32daa5e5 100644 topColor: { value: new THREE.Color(0x0077ff) }, bottomColor: { value: new THREE.Color(0xffffff) }, diff --git a/examples-testing/examples/webgl_lights_physical.ts b/examples-testing/examples/webgl_lights_physical.ts -index 707ef200..8e4cf0bb 100644 +index 707ef200e..8e4cf0bbe 100644 --- a/examples-testing/examples/webgl_lights_physical.ts +++ b/examples-testing/examples/webgl_lights_physical.ts @@ -5,13 +5,19 @@ import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; @@ -6499,7 +6499,7 @@ index 707ef200..8e4cf0bb 100644 stats = new Stats(); container.appendChild(stats.dom); diff --git a/examples-testing/examples/webgl_lights_rectarealight.ts b/examples-testing/examples/webgl_lights_rectarealight.ts -index 621d183d..3f4b060e 100644 +index 621d183d8..3f4b060ec 100644 --- a/examples-testing/examples/webgl_lights_rectarealight.ts +++ b/examples-testing/examples/webgl_lights_rectarealight.ts @@ -6,9 +6,9 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -6525,7 +6525,7 @@ index 621d183d..3f4b060e 100644 ctx.fillRect(0, 0, 2, 2); ctx.fillStyle = '#fff'; diff --git a/examples-testing/examples/webgl_lights_spotlight.ts b/examples-testing/examples/webgl_lights_spotlight.ts -index 78fec9d4..c83421bb 100644 +index 78fec9d4e..c83421bb3 100644 --- a/examples-testing/examples/webgl_lights_spotlight.ts +++ b/examples-testing/examples/webgl_lights_spotlight.ts @@ -5,9 +5,9 @@ import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; @@ -6570,7 +6570,7 @@ index 78fec9d4..c83421bb 100644 renderer.render(scene, camera); } diff --git a/examples-testing/examples/webgl_lights_spotlights.ts b/examples-testing/examples/webgl_lights_spotlights.ts -index be8dfd69..c2b109f2 100644 +index be8dfd69d..c2b109f2f 100644 --- a/examples-testing/examples/webgl_lights_spotlights.ts +++ b/examples-testing/examples/webgl_lights_spotlights.ts @@ -29,7 +29,7 @@ const spotLight1 = createSpotlight(0xff7f00); @@ -6601,7 +6601,7 @@ index be8dfd69..c2b109f2 100644 .to( { diff --git a/examples-testing/examples/webgl_lines_colors.ts b/examples-testing/examples/webgl_lines_colors.ts -index 9da19ee2..68bc979b 100644 +index 9da19ee2e..68bc979b0 100644 --- a/examples-testing/examples/webgl_lines_colors.ts +++ b/examples-testing/examples/webgl_lines_colors.ts @@ -8,7 +8,7 @@ let mouseX = 0, @@ -6641,7 +6641,7 @@ index 9da19ee2..68bc979b 100644 } } diff --git a/examples-testing/examples/webgl_lines_dashed.ts b/examples-testing/examples/webgl_lines_dashed.ts -index 3e3ee304..0c098cd5 100644 +index 3e3ee3041..0c098cd5b 100644 --- a/examples-testing/examples/webgl_lines_dashed.ts +++ b/examples-testing/examples/webgl_lines_dashed.ts @@ -4,8 +4,8 @@ import Stats from 'three/addons/libs/stats.module.js'; @@ -6674,7 +6674,7 @@ index 3e3ee304..0c098cd5 100644 object.rotation.y = 0.25 * time; } diff --git a/examples-testing/examples/webgl_lines_fat.ts b/examples-testing/examples/webgl_lines_fat.ts -index b5b66bbb..b4bfb555 100644 +index b5b66bbb1..b4bfb555c 100644 --- a/examples-testing/examples/webgl_lines_fat.ts +++ b/examples-testing/examples/webgl_lines_fat.ts @@ -7,14 +7,19 @@ import { LineMaterial } from 'three/addons/lines/LineMaterial.js'; @@ -6704,7 +6704,7 @@ index b5b66bbb..b4bfb555 100644 init(); diff --git a/examples-testing/examples/webgl_lines_fat_raycasting.ts b/examples-testing/examples/webgl_lines_fat_raycasting.ts -index ac9400da..7041465f 100644 +index ac9400da3..7041465f3 100644 --- a/examples-testing/examples/webgl_lines_fat_raycasting.ts +++ b/examples-testing/examples/webgl_lines_fat_raycasting.ts @@ -8,11 +8,12 @@ import { LineSegmentsGeometry } from 'three/addons/lines/LineSegmentsGeometry.js @@ -6786,7 +6786,7 @@ index ac9400da..7041465f 100644 gui.add(params, 'translation', 0, 10).onChange(function (val) { diff --git a/examples-testing/examples/webgl_lines_fat_wireframe.ts b/examples-testing/examples/webgl_lines_fat_wireframe.ts -index 59660ad7..9f38fcb6 100644 +index 59660ad7e..9f38fcb6d 100644 --- a/examples-testing/examples/webgl_lines_fat_wireframe.ts +++ b/examples-testing/examples/webgl_lines_fat_wireframe.ts @@ -8,15 +8,20 @@ import { LineMaterial } from 'three/addons/lines/LineMaterial.js'; @@ -6827,7 +6827,7 @@ index 59660ad7..9f38fcb6 100644 const geometry = new WireframeGeometry2(geo); diff --git a/examples-testing/examples/webgl_loader_3dm.ts b/examples-testing/examples/webgl_loader_3dm.ts -index 7570306f..a363c213 100644 +index 7570306fd..a363c2132 100644 --- a/examples-testing/examples/webgl_loader_3dm.ts +++ b/examples-testing/examples/webgl_loader_3dm.ts @@ -5,8 +5,8 @@ import { Rhino3dmLoader } from 'three/addons/loaders/3DMLoader.js'; @@ -6860,7 +6860,7 @@ index 7570306f..a363c213 100644 for (let i = 0; i < layers.length; i++) { diff --git a/examples-testing/examples/webgl_loader_3ds.ts b/examples-testing/examples/webgl_loader_3ds.ts -index 10ce3407..ac3a6e23 100644 +index 10ce34076..ac3a6e232 100644 --- a/examples-testing/examples/webgl_loader_3ds.ts +++ b/examples-testing/examples/webgl_loader_3ds.ts @@ -3,8 +3,8 @@ import * as THREE from 'three'; @@ -6888,7 +6888,7 @@ index 10ce3407..ac3a6e23 100644 }); diff --git a/examples-testing/examples/webgl_loader_3dtiles.ts b/examples-testing/examples/webgl_loader_3dtiles.ts -index bae36bc8..70aff037 100644 +index bae36bc84..70aff0378 100644 --- a/examples-testing/examples/webgl_loader_3dtiles.ts +++ b/examples-testing/examples/webgl_loader_3dtiles.ts @@ -13,8 +13,8 @@ import { @@ -6903,7 +6903,7 @@ index bae36bc8..70aff037 100644 init(); diff --git a/examples-testing/examples/webgl_loader_3mf.ts b/examples-testing/examples/webgl_loader_3mf.ts -index c31e3219..eecda516 100644 +index c31e32196..eecda516b 100644 --- a/examples-testing/examples/webgl_loader_3mf.ts +++ b/examples-testing/examples/webgl_loader_3mf.ts @@ -4,7 +4,12 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -6944,7 +6944,7 @@ index c31e3219..eecda516 100644 scene.remove(object); diff --git a/examples-testing/examples/webgl_loader_3mf_materials.ts b/examples-testing/examples/webgl_loader_3mf_materials.ts -index 0dea0139..6e897da1 100644 +index 0dea01391..6e897da19 100644 --- a/examples-testing/examples/webgl_loader_3mf_materials.ts +++ b/examples-testing/examples/webgl_loader_3mf_materials.ts @@ -3,7 +3,7 @@ import * as THREE from 'three'; @@ -6957,7 +6957,7 @@ index 0dea0139..6e897da1 100644 init(); diff --git a/examples-testing/examples/webgl_loader_amf.ts b/examples-testing/examples/webgl_loader_amf.ts -index ee576e04..7569a044 100644 +index ee576e04f..7569a0448 100644 --- a/examples-testing/examples/webgl_loader_amf.ts +++ b/examples-testing/examples/webgl_loader_amf.ts @@ -3,7 +3,7 @@ import * as THREE from 'three'; @@ -6970,7 +6970,7 @@ index ee576e04..7569a044 100644 init(); diff --git a/examples-testing/examples/webgl_loader_bvh.ts b/examples-testing/examples/webgl_loader_bvh.ts -index 70cec7e6..ba36382e 100644 +index 70cec7e6a..ba36382e8 100644 --- a/examples-testing/examples/webgl_loader_bvh.ts +++ b/examples-testing/examples/webgl_loader_bvh.ts @@ -6,8 +6,8 @@ import { BVHLoader } from 'three/addons/loaders/BVHLoader.js'; @@ -6985,7 +6985,7 @@ index 70cec7e6..ba36382e 100644 init(); diff --git a/examples-testing/examples/webgl_loader_collada.ts b/examples-testing/examples/webgl_loader_collada.ts -index 3106369c..d9705f46 100644 +index 3106369c8..d9705f46e 100644 --- a/examples-testing/examples/webgl_loader_collada.ts +++ b/examples-testing/examples/webgl_loader_collada.ts @@ -4,13 +4,13 @@ import Stats from 'three/addons/libs/stats.module.js'; @@ -7015,7 +7015,7 @@ index 3106369c..d9705f46 100644 // diff --git a/examples-testing/examples/webgl_loader_collada_skinning.ts b/examples-testing/examples/webgl_loader_collada_skinning.ts -index bbbd0ac3..206bd4c7 100644 +index bbbd0ac3e..206bd4c76 100644 --- a/examples-testing/examples/webgl_loader_collada_skinning.ts +++ b/examples-testing/examples/webgl_loader_collada_skinning.ts @@ -5,13 +5,13 @@ import Stats from 'three/addons/libs/stats.module.js'; @@ -7045,7 +7045,7 @@ index bbbd0ac3..206bd4c7 100644 mixer = new THREE.AnimationMixer(avatar); diff --git a/examples-testing/examples/webgl_loader_draco.ts b/examples-testing/examples/webgl_loader_draco.ts -index c1df7100..6a8c4844 100644 +index c1df7100d..6a8c48446 100644 --- a/examples-testing/examples/webgl_loader_draco.ts +++ b/examples-testing/examples/webgl_loader_draco.ts @@ -2,9 +2,9 @@ import * as THREE from 'three'; @@ -7061,7 +7061,7 @@ index c1df7100..6a8c4844 100644 // Configure and create Draco decoder. const dracoLoader = new DRACOLoader(); diff --git a/examples-testing/examples/webgl_loader_fbx.ts b/examples-testing/examples/webgl_loader_fbx.ts -index bcf26ae9..7e78cf59 100644 +index bcf26ae95..7e78cf591 100644 --- a/examples-testing/examples/webgl_loader_fbx.ts +++ b/examples-testing/examples/webgl_loader_fbx.ts @@ -8,8 +8,14 @@ import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; @@ -7142,7 +7142,7 @@ index bcf26ae9..7e78cf59 100644 } } diff --git a/examples-testing/examples/webgl_loader_fbx_nurbs.ts b/examples-testing/examples/webgl_loader_fbx_nurbs.ts -index f2e45bcb..c15264d7 100644 +index f2e45bcb5..c15264d71 100644 --- a/examples-testing/examples/webgl_loader_fbx_nurbs.ts +++ b/examples-testing/examples/webgl_loader_fbx_nurbs.ts @@ -5,7 +5,7 @@ import Stats from 'three/addons/libs/stats.module.js'; @@ -7155,7 +7155,7 @@ index f2e45bcb..c15264d7 100644 init(); diff --git a/examples-testing/examples/webgl_loader_gcode.ts b/examples-testing/examples/webgl_loader_gcode.ts -index 16dfb92e..14f1b485 100644 +index 16dfb92e2..14f1b4850 100644 --- a/examples-testing/examples/webgl_loader_gcode.ts +++ b/examples-testing/examples/webgl_loader_gcode.ts @@ -4,9 +4,13 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -7197,7 +7197,7 @@ index 16dfb92e..14f1b485 100644 model = object; model.position.copy(positions[assets.indexOf(asset)]); diff --git a/examples-testing/examples/webgl_loader_gltf.ts b/examples-testing/examples/webgl_loader_gltf.ts -index e0cb6854..68c61500 100644 +index e0cb68546..68c615001 100644 --- a/examples-testing/examples/webgl_loader_gltf.ts +++ b/examples-testing/examples/webgl_loader_gltf.ts @@ -5,8 +5,16 @@ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; @@ -7261,7 +7261,7 @@ index e0cb6854..68c61500 100644 box.setFromObject(selection); diff --git a/examples-testing/examples/webgl_loader_gltf_animation_pointer.ts b/examples-testing/examples/webgl_loader_gltf_animation_pointer.ts -index 443df047..d2cb449e 100644 +index 443df047b..d2cb449eb 100644 --- a/examples-testing/examples/webgl_loader_gltf_animation_pointer.ts +++ b/examples-testing/examples/webgl_loader_gltf_animation_pointer.ts @@ -10,11 +10,11 @@ import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; @@ -7279,7 +7279,7 @@ index 443df047..d2cb449e 100644 const stats = new Stats(); container.appendChild(stats.dom); diff --git a/examples-testing/examples/webgl_loader_gltf_anisotropy.ts b/examples-testing/examples/webgl_loader_gltf_anisotropy.ts -index f0dd05cf..3ce7cc23 100644 +index f0dd05cfe..3ce7cc23a 100644 --- a/examples-testing/examples/webgl_loader_gltf_anisotropy.ts +++ b/examples-testing/examples/webgl_loader_gltf_anisotropy.ts @@ -4,7 +4,7 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -7292,7 +7292,7 @@ index f0dd05cf..3ce7cc23 100644 init(); diff --git a/examples-testing/examples/webgl_loader_gltf_avif.ts b/examples-testing/examples/webgl_loader_gltf_avif.ts -index 37d63859..68dff97f 100644 +index 37d63859e..68dff97f2 100644 --- a/examples-testing/examples/webgl_loader_gltf_avif.ts +++ b/examples-testing/examples/webgl_loader_gltf_avif.ts @@ -4,7 +4,7 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -7305,7 +7305,7 @@ index 37d63859..68dff97f 100644 init(); render(); diff --git a/examples-testing/examples/webgl_loader_gltf_compressed.ts b/examples-testing/examples/webgl_loader_gltf_compressed.ts -index 235d9b3d..06e6d507 100644 +index 235d9b3d0..06e6d5072 100644 --- a/examples-testing/examples/webgl_loader_gltf_compressed.ts +++ b/examples-testing/examples/webgl_loader_gltf_compressed.ts @@ -7,7 +7,7 @@ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; @@ -7318,7 +7318,7 @@ index 235d9b3d..06e6d507 100644 init(); render(); diff --git a/examples-testing/examples/webgl_loader_gltf_dispersion.ts b/examples-testing/examples/webgl_loader_gltf_dispersion.ts -index 0f1d7e57..c4a5d7f1 100644 +index 0f1d7e57a..c4a5d7f1b 100644 --- a/examples-testing/examples/webgl_loader_gltf_dispersion.ts +++ b/examples-testing/examples/webgl_loader_gltf_dispersion.ts @@ -4,7 +4,7 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -7331,7 +7331,7 @@ index 0f1d7e57..c4a5d7f1 100644 init().then(render); diff --git a/examples-testing/examples/webgl_loader_gltf_instancing.ts b/examples-testing/examples/webgl_loader_gltf_instancing.ts -index 6acb140e..205f4816 100644 +index 6acb140ec..205f48162 100644 --- a/examples-testing/examples/webgl_loader_gltf_instancing.ts +++ b/examples-testing/examples/webgl_loader_gltf_instancing.ts @@ -4,7 +4,7 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -7344,7 +7344,7 @@ index 6acb140e..205f4816 100644 init(); render(); diff --git a/examples-testing/examples/webgl_loader_gltf_iridescence.ts b/examples-testing/examples/webgl_loader_gltf_iridescence.ts -index 5519835f..6cfbb56c 100644 +index 5519835fe..6cfbb56c3 100644 --- a/examples-testing/examples/webgl_loader_gltf_iridescence.ts +++ b/examples-testing/examples/webgl_loader_gltf_iridescence.ts @@ -4,7 +4,7 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -7357,7 +7357,7 @@ index 5519835f..6cfbb56c 100644 init().catch(function (err) { console.error(err); diff --git a/examples-testing/examples/webgl_loader_gltf_progressive_lod.ts b/examples-testing/examples/webgl_loader_gltf_progressive_lod.ts -index 8d1c6048..72b6bed3 100644 +index 8d1c60480..72b6bed31 100644 --- a/examples-testing/examples/webgl_loader_gltf_progressive_lod.ts +++ b/examples-testing/examples/webgl_loader_gltf_progressive_lod.ts @@ -5,8 +5,8 @@ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; @@ -7372,7 +7372,7 @@ index 8d1c6048..72b6bed3 100644 init(); diff --git a/examples-testing/examples/webgl_loader_gltf_sheen.ts b/examples-testing/examples/webgl_loader_gltf_sheen.ts -index b058f1e2..3f75e10c 100644 +index b058f1e27..3f75e10cc 100644 --- a/examples-testing/examples/webgl_loader_gltf_sheen.ts +++ b/examples-testing/examples/webgl_loader_gltf_sheen.ts @@ -6,7 +6,7 @@ import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; @@ -7397,7 +7397,7 @@ index b058f1e2..3f75e10c 100644 const gui = new GUI(); diff --git a/examples-testing/examples/webgl_loader_gltf_transmission.ts b/examples-testing/examples/webgl_loader_gltf_transmission.ts -index e8ea0a92..d05568b5 100644 +index e8ea0a927..d05568b57 100644 --- a/examples-testing/examples/webgl_loader_gltf_transmission.ts +++ b/examples-testing/examples/webgl_loader_gltf_transmission.ts @@ -6,7 +6,12 @@ import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; @@ -7415,7 +7415,7 @@ index e8ea0a92..d05568b5 100644 init(); diff --git a/examples-testing/examples/webgl_loader_imagebitmap.ts b/examples-testing/examples/webgl_loader_imagebitmap.ts -index c02a03e1..0dce69c1 100644 +index c02a03e17..0dce69c16 100644 --- a/examples-testing/examples/webgl_loader_imagebitmap.ts +++ b/examples-testing/examples/webgl_loader_imagebitmap.ts @@ -1,7 +1,7 @@ @@ -7456,7 +7456,7 @@ index c02a03e1..0dce69c1 100644 texture.onUpdate = null; // make sure this callback is executed only once per texture } diff --git a/examples-testing/examples/webgl_loader_kmz.ts b/examples-testing/examples/webgl_loader_kmz.ts -index f93555e4..8793a351 100644 +index f93555e41..8793a3511 100644 --- a/examples-testing/examples/webgl_loader_kmz.ts +++ b/examples-testing/examples/webgl_loader_kmz.ts @@ -3,7 +3,7 @@ import * as THREE from 'three'; @@ -7469,7 +7469,7 @@ index f93555e4..8793a351 100644 init(); diff --git a/examples-testing/examples/webgl_loader_lwo.ts b/examples-testing/examples/webgl_loader_lwo.ts -index fb10c834..df003a04 100644 +index fb10c8340..df003a04f 100644 --- a/examples-testing/examples/webgl_loader_lwo.ts +++ b/examples-testing/examples/webgl_loader_lwo.ts @@ -3,7 +3,7 @@ import * as THREE from 'three'; @@ -7482,7 +7482,7 @@ index fb10c834..df003a04 100644 init(); diff --git a/examples-testing/examples/webgl_loader_md2_control.ts b/examples-testing/examples/webgl_loader_md2_control.ts -index 56b7dc3c..fb634369 100644 +index 56b7dc3c4..fb634369d 100644 --- a/examples-testing/examples/webgl_loader_md2_control.ts +++ b/examples-testing/examples/webgl_loader_md2_control.ts @@ -9,10 +9,10 @@ import { Gyroscope } from 'three/addons/misc/Gyroscope.js'; @@ -7533,7 +7533,7 @@ index 56b7dc3c..fb634369 100644 case 'ArrowUp': case 'KeyW': diff --git a/examples-testing/examples/webgl_loader_mdd.ts b/examples-testing/examples/webgl_loader_mdd.ts -index 16e49221..d71600a5 100644 +index 16e49221c..d71600a53 100644 --- a/examples-testing/examples/webgl_loader_mdd.ts +++ b/examples-testing/examples/webgl_loader_mdd.ts @@ -2,7 +2,11 @@ import * as THREE from 'three'; @@ -7550,7 +7550,7 @@ index 16e49221..d71600a5 100644 init(); diff --git a/examples-testing/examples/webgl_loader_obj.ts b/examples-testing/examples/webgl_loader_obj.ts -index 42f1f325..19ce7067 100644 +index 42f1f3257..19ce70671 100644 --- a/examples-testing/examples/webgl_loader_obj.ts +++ b/examples-testing/examples/webgl_loader_obj.ts @@ -4,7 +4,7 @@ import { MTLLoader } from 'three/addons/loaders/MTLLoader.js'; @@ -7563,7 +7563,7 @@ index 42f1f325..19ce7067 100644 init(); diff --git a/examples-testing/examples/webgl_loader_pcd.ts b/examples-testing/examples/webgl_loader_pcd.ts -index dd0f0b0f..e47005a8 100644 +index dd0f0b0f5..e47005a81 100644 --- a/examples-testing/examples/webgl_loader_pcd.ts +++ b/examples-testing/examples/webgl_loader_pcd.ts @@ -4,7 +4,7 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -7585,7 +7585,7 @@ index dd0f0b0f..e47005a8 100644 points.geometry.center(); points.geometry.rotateX(Math.PI); diff --git a/examples-testing/examples/webgl_loader_pdb.ts b/examples-testing/examples/webgl_loader_pdb.ts -index b560efa7..9882dff5 100644 +index b560efa73..9882dff52 100644 --- a/examples-testing/examples/webgl_loader_pdb.ts +++ b/examples-testing/examples/webgl_loader_pdb.ts @@ -5,10 +5,10 @@ import { PDBLoader } from 'three/addons/loaders/PDBLoader.js'; @@ -7644,7 +7644,7 @@ index b560efa7..9882dff5 100644 geometryAtoms.translate(offset.x, offset.y, offset.z); geometryBonds.translate(offset.x, offset.y, offset.z); diff --git a/examples-testing/examples/webgl_loader_ply.ts b/examples-testing/examples/webgl_loader_ply.ts -index 0f4042b7..dff17d16 100644 +index 0f4042b7d..dff17d169 100644 --- a/examples-testing/examples/webgl_loader_ply.ts +++ b/examples-testing/examples/webgl_loader_ply.ts @@ -4,9 +4,9 @@ import Stats from 'three/addons/libs/stats.module.js'; @@ -7669,7 +7669,7 @@ index 0f4042b7..dff17d16 100644 directionalLight.position.set(x, y, z); scene.add(directionalLight); diff --git a/examples-testing/examples/webgl_loader_svg.ts b/examples-testing/examples/webgl_loader_svg.ts -index a04b79de..48a055d4 100644 +index a04b79de2..48a055d41 100644 --- a/examples-testing/examples/webgl_loader_svg.ts +++ b/examples-testing/examples/webgl_loader_svg.ts @@ -3,14 +3,24 @@ import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; @@ -7768,7 +7768,7 @@ index a04b79de..48a055d4 100644 }); } diff --git a/examples-testing/examples/webgl_loader_texture_dds.ts b/examples-testing/examples/webgl_loader_texture_dds.ts -index ba9b18e9..d4161ff3 100644 +index ba9b18e9d..d4161ff31 100644 --- a/examples-testing/examples/webgl_loader_texture_dds.ts +++ b/examples-testing/examples/webgl_loader_texture_dds.ts @@ -2,8 +2,8 @@ import * as THREE from 'three'; @@ -7792,7 +7792,7 @@ index ba9b18e9..d4161ff3 100644 mesh.position.y = -2; scene.add(mesh); diff --git a/examples-testing/examples/webgl_loader_texture_ktx.ts b/examples-testing/examples/webgl_loader_texture_ktx.ts -index a4e74930..9f0ad48f 100644 +index a4e749301..9f0ad48f3 100644 --- a/examples-testing/examples/webgl_loader_texture_ktx.ts +++ b/examples-testing/examples/webgl_loader_texture_ktx.ts @@ -17,8 +17,8 @@ import { KTXLoader } from 'three/addons/loaders/KTXLoader.js'; @@ -7858,7 +7858,7 @@ index a4e74930..9f0ad48f 100644 meshes.push(new THREE.Mesh(geometry, material1)); meshes.push(new THREE.Mesh(geometry, material2)); diff --git a/examples-testing/examples/webgl_loader_texture_tga.ts b/examples-testing/examples/webgl_loader_texture_tga.ts -index c4f65b79..fd6bab07 100644 +index c4f65b79a..fd6bab071 100644 --- a/examples-testing/examples/webgl_loader_texture_tga.ts +++ b/examples-testing/examples/webgl_loader_texture_tga.ts @@ -5,7 +5,7 @@ import Stats from 'three/addons/libs/stats.module.js'; @@ -7871,7 +7871,7 @@ index c4f65b79..fd6bab07 100644 init(); diff --git a/examples-testing/examples/webgl_loader_texture_tiff.ts b/examples-testing/examples/webgl_loader_texture_tiff.ts -index f097774a..bb5b9d9d 100644 +index f097774aa..bb5b9d9d9 100644 --- a/examples-testing/examples/webgl_loader_texture_tiff.ts +++ b/examples-testing/examples/webgl_loader_texture_tiff.ts @@ -2,7 +2,7 @@ import * as THREE from 'three'; @@ -7884,7 +7884,7 @@ index f097774a..bb5b9d9d 100644 init(); diff --git a/examples-testing/examples/webgl_loader_texture_ultrahdr.ts b/examples-testing/examples/webgl_loader_texture_ultrahdr.ts -index c8bce4bf..a28a7f00 100644 +index c8bce4bf9..a28a7f00b 100644 --- a/examples-testing/examples/webgl_loader_texture_ultrahdr.ts +++ b/examples-testing/examples/webgl_loader_texture_ultrahdr.ts @@ -5,7 +5,14 @@ import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; @@ -7930,7 +7930,7 @@ index c8bce4bf..a28a7f00 100644 loader.load(`textures/equirectangular/spruit_sunrise_${resolution}.hdr.jpg`, function (texture) { diff --git a/examples-testing/examples/webgl_loader_ttf.ts b/examples-testing/examples/webgl_loader_ttf.ts -index b54b8c29..40a692eb 100644 +index b54b8c290..40a692ebd 100644 --- a/examples-testing/examples/webgl_loader_ttf.ts +++ b/examples-testing/examples/webgl_loader_ttf.ts @@ -4,9 +4,9 @@ import { TTFLoader } from 'three/addons/loaders/TTFLoader.js'; @@ -8021,7 +8021,7 @@ index b54b8c29..40a692eb 100644 document.removeEventListener('pointermove', onPointerMove); document.removeEventListener('pointerup', onPointerUp); diff --git a/examples-testing/examples/webgl_loader_usdz.ts b/examples-testing/examples/webgl_loader_usdz.ts -index 409c6b59..dbde51b0 100644 +index 409c6b597..dbde51b00 100644 --- a/examples-testing/examples/webgl_loader_usdz.ts +++ b/examples-testing/examples/webgl_loader_usdz.ts @@ -4,7 +4,7 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -8034,7 +8034,7 @@ index 409c6b59..dbde51b0 100644 init(); diff --git a/examples-testing/examples/webgl_loader_vox.ts b/examples-testing/examples/webgl_loader_vox.ts -index 87e0b2f8..5094b88c 100644 +index 87e0b2f81..5094b88cb 100644 --- a/examples-testing/examples/webgl_loader_vox.ts +++ b/examples-testing/examples/webgl_loader_vox.ts @@ -3,7 +3,7 @@ import * as THREE from 'three'; @@ -8047,7 +8047,7 @@ index 87e0b2f8..5094b88c 100644 init(); diff --git a/examples-testing/examples/webgl_loader_vrml.ts b/examples-testing/examples/webgl_loader_vrml.ts -index 1dda79f2..02c9d920 100644 +index 1dda79f2b..02c9d9209 100644 --- a/examples-testing/examples/webgl_loader_vrml.ts +++ b/examples-testing/examples/webgl_loader_vrml.ts @@ -6,7 +6,12 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -8101,7 +8101,7 @@ index 1dda79f2..02c9d920 100644 vrmlScene = object; scene.add(object); diff --git a/examples-testing/examples/webgl_loader_vtk.ts b/examples-testing/examples/webgl_loader_vtk.ts -index dfc79865..de6983fb 100644 +index dfc798657..de6983fb2 100644 --- a/examples-testing/examples/webgl_loader_vtk.ts +++ b/examples-testing/examples/webgl_loader_vtk.ts @@ -5,9 +5,9 @@ import Stats from 'three/addons/libs/stats.module.js'; @@ -8117,7 +8117,7 @@ index dfc79865..de6983fb 100644 init(); diff --git a/examples-testing/examples/webgl_loader_xyz.ts b/examples-testing/examples/webgl_loader_xyz.ts -index 315c6de3..61b37933 100644 +index 315c6de39..61b37933a 100644 --- a/examples-testing/examples/webgl_loader_xyz.ts +++ b/examples-testing/examples/webgl_loader_xyz.ts @@ -2,9 +2,9 @@ import * as THREE from 'three'; @@ -8133,7 +8133,7 @@ index 315c6de3..61b37933 100644 init(); diff --git a/examples-testing/examples/webgl_lod.ts b/examples-testing/examples/webgl_lod.ts -index d957efbb..fd75a6cd 100644 +index d957efbbd..fd75a6cd0 100644 --- a/examples-testing/examples/webgl_lod.ts +++ b/examples-testing/examples/webgl_lod.ts @@ -2,9 +2,9 @@ import * as THREE from 'three'; @@ -8158,7 +8158,7 @@ index d957efbb..fd75a6cd 100644 [new THREE.IcosahedronGeometry(100, 8), 300], [new THREE.IcosahedronGeometry(100, 4), 1000], diff --git a/examples-testing/examples/webgl_marchingcubes.ts b/examples-testing/examples/webgl_marchingcubes.ts -index ea96c0f9..b87765f3 100644 +index ea96c0f9f..b87765f39 100644 --- a/examples-testing/examples/webgl_marchingcubes.ts +++ b/examples-testing/examples/webgl_marchingcubes.ts @@ -7,17 +7,42 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -8274,7 +8274,7 @@ index ea96c0f9..b87765f3 100644 // fill the field with some metaballs diff --git a/examples-testing/examples/webgl_materials_alphahash.ts b/examples-testing/examples/webgl_materials_alphahash.ts -index 790e31be..335db180 100644 +index 790e31be4..335db1806 100644 --- a/examples-testing/examples/webgl_materials_alphahash.ts +++ b/examples-testing/examples/webgl_materials_alphahash.ts @@ -10,9 +10,15 @@ import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; @@ -8297,7 +8297,7 @@ index 790e31be..335db180 100644 let needsUpdate = false; diff --git a/examples-testing/examples/webgl_materials_blending.ts b/examples-testing/examples/webgl_materials_blending.ts -index fb2e6a91..6063485e 100644 +index fb2e6a91e..6063485e3 100644 --- a/examples-testing/examples/webgl_materials_blending.ts +++ b/examples-testing/examples/webgl_materials_blending.ts @@ -1,7 +1,7 @@ @@ -8350,7 +8350,7 @@ index fb2e6a91..6063485e 100644 canvas.height = 32; diff --git a/examples-testing/examples/webgl_materials_blending_custom.ts b/examples-testing/examples/webgl_materials_blending_custom.ts -index 07244742..62111971 100644 +index 072447426..62111971a 100644 --- a/examples-testing/examples/webgl_materials_blending_custom.ts +++ b/examples-testing/examples/webgl_materials_blending_custom.ts @@ -2,12 +2,12 @@ import * as THREE from 'three'; @@ -8401,7 +8401,7 @@ index 07244742..62111971 100644 material.blendEquation = value; } diff --git a/examples-testing/examples/webgl_materials_bumpmap.ts b/examples-testing/examples/webgl_materials_bumpmap.ts -index a09c2157..00fe46b6 100644 +index a09c21573..00fe46b68 100644 --- a/examples-testing/examples/webgl_materials_bumpmap.ts +++ b/examples-testing/examples/webgl_materials_bumpmap.ts @@ -5,13 +5,13 @@ import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; @@ -8441,7 +8441,7 @@ index a09c2157..00fe46b6 100644 mesh.position.y = -0.5; diff --git a/examples-testing/examples/webgl_materials_car.ts b/examples-testing/examples/webgl_materials_car.ts -index 44be6e0e..f3a7b953 100644 +index 44be6e0ee..f3a7b9530 100644 --- a/examples-testing/examples/webgl_materials_car.ts +++ b/examples-testing/examples/webgl_materials_car.ts @@ -8,16 +8,16 @@ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; @@ -8522,7 +8522,7 @@ index 44be6e0e..f3a7b953 100644 // shadow diff --git a/examples-testing/examples/webgl_materials_cubemap.ts b/examples-testing/examples/webgl_materials_cubemap.ts -index 5f269275..87044a87 100644 +index 5f2692751..87044a874 100644 --- a/examples-testing/examples/webgl_materials_cubemap.ts +++ b/examples-testing/examples/webgl_materials_cubemap.ts @@ -5,9 +5,9 @@ import Stats from 'three/addons/libs/stats.module.js'; @@ -8547,7 +8547,7 @@ index 5f269275..87044a87 100644 head.position.y = -3; head.material = cubeMaterial1; diff --git a/examples-testing/examples/webgl_materials_cubemap_dynamic.ts b/examples-testing/examples/webgl_materials_cubemap_dynamic.ts -index 301835dd..352e2bb7 100644 +index 301835dda..352e2bb7f 100644 --- a/examples-testing/examples/webgl_materials_cubemap_dynamic.ts +++ b/examples-testing/examples/webgl_materials_cubemap_dynamic.ts @@ -6,12 +6,12 @@ import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; @@ -8577,7 +8577,7 @@ index 301835dd..352e2bb7 100644 cube.position.x = Math.cos(time) * 30; diff --git a/examples-testing/examples/webgl_materials_cubemap_mipmaps.ts b/examples-testing/examples/webgl_materials_cubemap_mipmaps.ts -index 944f4c18..21010ffa 100644 +index 944f4c18e..21010ffa4 100644 --- a/examples-testing/examples/webgl_materials_cubemap_mipmaps.ts +++ b/examples-testing/examples/webgl_materials_cubemap_mipmaps.ts @@ -2,9 +2,9 @@ import * as THREE from 'three'; @@ -8617,7 +8617,7 @@ index 944f4c18..21010ffa 100644 customizedCubeTexture.colorSpace = THREE.SRGBColorSpace; customizedCubeTexture.minFilter = THREE.LinearMipMapLinearFilter; diff --git a/examples-testing/examples/webgl_materials_cubemap_refraction.ts b/examples-testing/examples/webgl_materials_cubemap_refraction.ts -index 8c025071..a47c4832 100644 +index 8c025071f..a47c4832f 100644 --- a/examples-testing/examples/webgl_materials_cubemap_refraction.ts +++ b/examples-testing/examples/webgl_materials_cubemap_refraction.ts @@ -4,9 +4,9 @@ import Stats from 'three/addons/libs/stats.module.js'; @@ -8656,7 +8656,7 @@ index 8c025071..a47c4832 100644 mouseY = (event.clientY - windowHalfY) * 4; } diff --git a/examples-testing/examples/webgl_materials_cubemap_render_to_mipmaps.ts b/examples-testing/examples/webgl_materials_cubemap_render_to_mipmaps.ts -index 599a1369..a23a0582 100644 +index 599a1369b..a23a0582c 100644 --- a/examples-testing/examples/webgl_materials_cubemap_render_to_mipmaps.ts +++ b/examples-testing/examples/webgl_materials_cubemap_render_to_mipmaps.ts @@ -1,8 +1,8 @@ @@ -8706,7 +8706,7 @@ index 599a1369..a23a0582 100644 const material = new THREE.ShaderMaterial({ diff --git a/examples-testing/examples/webgl_materials_displacementmap.ts b/examples-testing/examples/webgl_materials_displacementmap.ts -index fd0be9a5..9b08fe73 100644 +index fd0be9a5e..9b08fe733 100644 --- a/examples-testing/examples/webgl_materials_displacementmap.ts +++ b/examples-testing/examples/webgl_materials_displacementmap.ts @@ -6,8 +6,8 @@ import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; @@ -8742,7 +8742,7 @@ index fd0be9a5..9b08fe73 100644 mesh = new THREE.Mesh(geometry, material); diff --git a/examples-testing/examples/webgl_materials_envmaps.ts b/examples-testing/examples/webgl_materials_envmaps.ts -index 18a5542e..13fe2efc 100644 +index 18a5542ed..13fe2efc9 100644 --- a/examples-testing/examples/webgl_materials_envmaps.ts +++ b/examples-testing/examples/webgl_materials_envmaps.ts @@ -3,9 +3,19 @@ import * as THREE from 'three'; @@ -8769,7 +8769,7 @@ index 18a5542e..13fe2efc 100644 init(); diff --git a/examples-testing/examples/webgl_materials_envmaps_exr.ts b/examples-testing/examples/webgl_materials_envmaps_exr.ts -index c3f3f4f7..4ae4cb95 100644 +index c3f3f4f7d..4ae4cb959 100644 --- a/examples-testing/examples/webgl_materials_envmaps_exr.ts +++ b/examples-testing/examples/webgl_materials_envmaps_exr.ts @@ -14,11 +14,12 @@ const params = { @@ -8802,7 +8802,7 @@ index c3f3f4f7..4ae4cb95 100644 roughness: params.roughness, envMapIntensity: 1.0, diff --git a/examples-testing/examples/webgl_materials_envmaps_fasthdr.ts b/examples-testing/examples/webgl_materials_envmaps_fasthdr.ts -index de8715eb..18b6ed98 100644 +index de8715eb7..18b6ed982 100644 --- a/examples-testing/examples/webgl_materials_envmaps_fasthdr.ts +++ b/examples-testing/examples/webgl_materials_envmaps_fasthdr.ts @@ -13,8 +13,8 @@ const params = { @@ -8826,7 +8826,7 @@ index de8715eb..18b6ed98 100644 texture.mapping = THREE.CubeUVReflectionMapping; scene.environment = texture; diff --git a/examples-testing/examples/webgl_materials_envmaps_groundprojected.ts b/examples-testing/examples/webgl_materials_envmaps_groundprojected.ts -index c1e0ed83..f7acbbb3 100644 +index c1e0ed83b..f7acbbb3a 100644 --- a/examples-testing/examples/webgl_materials_envmaps_groundprojected.ts +++ b/examples-testing/examples/webgl_materials_envmaps_groundprojected.ts @@ -13,7 +13,7 @@ const params = { @@ -8862,7 +8862,7 @@ index c1e0ed83..f7acbbb3 100644 // shadow const mesh = new THREE.Mesh( diff --git a/examples-testing/examples/webgl_materials_envmaps_hdr.ts b/examples-testing/examples/webgl_materials_envmaps_hdr.ts -index 2dc2b808..31851f3d 100644 +index 2dc2b808a..31851f3d4 100644 --- a/examples-testing/examples/webgl_materials_envmaps_hdr.ts +++ b/examples-testing/examples/webgl_materials_envmaps_hdr.ts @@ -7,7 +7,13 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -8913,7 +8913,7 @@ index 2dc2b808..31851f3d 100644 metalness: params.metalness, roughness: params.roughness, diff --git a/examples-testing/examples/webgl_materials_modified.ts b/examples-testing/examples/webgl_materials_modified.ts -index de36aeb7..778aedd5 100644 +index de36aeb7d..778aedd59 100644 --- a/examples-testing/examples/webgl_materials_modified.ts +++ b/examples-testing/examples/webgl_materials_modified.ts @@ -5,7 +5,7 @@ import Stats from 'three/addons/libs/stats.module.js'; @@ -8955,7 +8955,7 @@ index de36aeb7..778aedd5 100644 if (shader) { shader.uniforms.time.value = performance.now() / 1000; diff --git a/examples-testing/examples/webgl_materials_normalmap_object_space.ts b/examples-testing/examples/webgl_materials_normalmap_object_space.ts -index 1fc6f806..72108134 100644 +index 1fc6f8066..72108134a 100644 --- a/examples-testing/examples/webgl_materials_normalmap_object_space.ts +++ b/examples-testing/examples/webgl_materials_normalmap_object_space.ts @@ -3,7 +3,7 @@ import * as THREE from 'three'; @@ -8996,7 +8996,7 @@ index 1fc6f806..72108134 100644 child.scale.multiplyScalar(0.5); diff --git a/examples-testing/examples/webgl_materials_physical_clearcoat.ts b/examples-testing/examples/webgl_materials_physical_clearcoat.ts -index 408fd992..dc782ea9 100644 +index 408fd9921..dc782ea9e 100644 --- a/examples-testing/examples/webgl_materials_physical_clearcoat.ts +++ b/examples-testing/examples/webgl_materials_physical_clearcoat.ts @@ -7,12 +7,12 @@ import { HDRCubeTextureLoader } from 'three/addons/loaders/HDRCubeTextureLoader. @@ -9017,7 +9017,7 @@ index 408fd992..dc782ea9 100644 init(); diff --git a/examples-testing/examples/webgl_materials_physical_transmission.ts b/examples-testing/examples/webgl_materials_physical_transmission.ts -index 08ee08ca..3fca6cce 100644 +index 08ee08cac..3fca6ccea 100644 --- a/examples-testing/examples/webgl_materials_physical_transmission.ts +++ b/examples-testing/examples/webgl_materials_physical_transmission.ts @@ -20,7 +20,7 @@ const params = { @@ -9039,7 +9039,7 @@ index 08ee08ca..3fca6cce 100644 context.fillRect(0, 1, 2, 1); diff --git a/examples-testing/examples/webgl_materials_physical_transmission_alpha.ts b/examples-testing/examples/webgl_materials_physical_transmission_alpha.ts -index 6318d784..254e1b23 100644 +index 6318d7844..254e1b230 100644 --- a/examples-testing/examples/webgl_materials_physical_transmission_alpha.ts +++ b/examples-testing/examples/webgl_materials_physical_transmission_alpha.ts @@ -22,9 +22,9 @@ const params = { @@ -9079,7 +9079,7 @@ index 6318d784..254e1b23 100644 scene = new THREE.Scene(); diff --git a/examples-testing/examples/webgl_materials_texture_anisotropy.ts b/examples-testing/examples/webgl_materials_texture_anisotropy.ts -index 1e030d64..65577721 100644 +index 1e030d64d..65577721c 100644 --- a/examples-testing/examples/webgl_materials_texture_anisotropy.ts +++ b/examples-testing/examples/webgl_materials_texture_anisotropy.ts @@ -5,9 +5,9 @@ import Stats from 'three/addons/libs/stats.module.js'; @@ -9120,7 +9120,7 @@ index 1e030d64..65577721 100644 mouseY = event.clientY - windowHalfY; } diff --git a/examples-testing/examples/webgl_materials_texture_canvas.ts b/examples-testing/examples/webgl_materials_texture_canvas.ts -index d23c6843..5a190fab 100644 +index d23c68436..5a190fab0 100644 --- a/examples-testing/examples/webgl_materials_texture_canvas.ts +++ b/examples-testing/examples/webgl_materials_texture_canvas.ts @@ -1,6 +1,10 @@ @@ -9165,7 +9165,7 @@ index d23c6843..5a190fab 100644 function onWindowResize() { diff --git a/examples-testing/examples/webgl_materials_texture_filters.ts b/examples-testing/examples/webgl_materials_texture_filters.ts -index 77b25468..b2ccb769 100644 +index 77b254684..b2ccb7690 100644 --- a/examples-testing/examples/webgl_materials_texture_filters.ts +++ b/examples-testing/examples/webgl_materials_texture_filters.ts @@ -3,9 +3,9 @@ import * as THREE from 'three'; @@ -9208,7 +9208,7 @@ index 77b25468..b2ccb769 100644 mouseY = event.clientY - windowHalfY; } diff --git a/examples-testing/examples/webgl_materials_texture_manualmipmap.ts b/examples-testing/examples/webgl_materials_texture_manualmipmap.ts -index 24bd4eb9..2dad75e8 100644 +index 24bd4eb9f..2dad75e8e 100644 --- a/examples-testing/examples/webgl_materials_texture_manualmipmap.ts +++ b/examples-testing/examples/webgl_materials_texture_manualmipmap.ts @@ -3,9 +3,9 @@ import * as THREE from 'three'; @@ -9277,7 +9277,7 @@ index 24bd4eb9..2dad75e8 100644 mouseY = event.clientY - windowHalfY; } diff --git a/examples-testing/examples/webgl_materials_texture_partialupdate.ts b/examples-testing/examples/webgl_materials_texture_partialupdate.ts -index 57fdc1c0..895c3b5a 100644 +index cae161939..0bcdf473d 100644 --- a/examples-testing/examples/webgl_materials_texture_partialupdate.ts +++ b/examples-testing/examples/webgl_materials_texture_partialupdate.ts @@ -1,6 +1,11 @@ @@ -9293,7 +9293,7 @@ index 57fdc1c0..895c3b5a 100644 let last = 0; const position = new THREE.Vector2(); -@@ -80,9 +85,9 @@ function animate() { +@@ -81,9 +86,9 @@ function animate() { renderer.render(scene, camera); } @@ -9306,7 +9306,7 @@ index 57fdc1c0..895c3b5a 100644 // generate a random color and update texture data diff --git a/examples-testing/examples/webgl_materials_texture_rotation.ts b/examples-testing/examples/webgl_materials_texture_rotation.ts -index eedc80c6..e5c7131f 100644 +index eedc80c6f..e5c7131f4 100644 --- a/examples-testing/examples/webgl_materials_texture_rotation.ts +++ b/examples-testing/examples/webgl_materials_texture_rotation.ts @@ -3,7 +3,10 @@ import * as THREE from 'three'; @@ -9331,7 +9331,7 @@ index eedc80c6..e5c7131f 100644 if (texture.matrixAutoUpdate === true) { texture.offset.set(API.offsetX, API.offsetY); diff --git a/examples-testing/examples/webgl_materials_toon.ts b/examples-testing/examples/webgl_materials_toon.ts -index 46c6a7e9..e89fc8cf 100644 +index 46c6a7e93..e89fc8cf3 100644 --- a/examples-testing/examples/webgl_materials_toon.ts +++ b/examples-testing/examples/webgl_materials_toon.ts @@ -4,20 +4,20 @@ import Stats from 'three/addons/libs/stats.module.js'; @@ -9370,7 +9370,7 @@ index 46c6a7e9..e89fc8cf 100644 font: font, diff --git a/examples-testing/examples/webgl_materials_video.ts b/examples-testing/examples/webgl_materials_video.ts -index 4f0d26a1..0bf378c5 100644 +index 4f0d26a18..0bf378c5d 100644 --- a/examples-testing/examples/webgl_materials_video.ts +++ b/examples-testing/examples/webgl_materials_video.ts @@ -5,13 +5,16 @@ import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; @@ -9487,7 +9487,7 @@ index 4f0d26a1..0bf378c5 100644 } diff --git a/examples-testing/examples/webgl_materials_video_webcam.ts b/examples-testing/examples/webgl_materials_video_webcam.ts -index cf6f8d50..00b48c16 100644 +index cf6f8d50c..00b48c163 100644 --- a/examples-testing/examples/webgl_materials_video_webcam.ts +++ b/examples-testing/examples/webgl_materials_video_webcam.ts @@ -2,7 +2,7 @@ import * as THREE from 'three'; @@ -9509,7 +9509,7 @@ index cf6f8d50..00b48c16 100644 const texture = new THREE.VideoTexture(video); texture.colorSpace = THREE.SRGBColorSpace; diff --git a/examples-testing/examples/webgl_materials_wireframe.ts b/examples-testing/examples/webgl_materials_wireframe.ts -index 8adbd71d..6424e8cb 100644 +index 8adbd71d6..6424e8cbd 100644 --- a/examples-testing/examples/webgl_materials_wireframe.ts +++ b/examples-testing/examples/webgl_materials_wireframe.ts @@ -8,7 +8,10 @@ const API = { @@ -9545,7 +9545,7 @@ index 8adbd71d..6424e8cb 100644 const position = geometry.attributes.position; diff --git a/examples-testing/examples/webgl_math_obb.ts b/examples-testing/examples/webgl_math_obb.ts -index 71cb5a91..24b50ba8 100644 +index 71cb5a916..24b50ba81 100644 --- a/examples-testing/examples/webgl_math_obb.ts +++ b/examples-testing/examples/webgl_math_obb.ts @@ -5,9 +5,16 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -9590,7 +9590,7 @@ index 71cb5a91..24b50ba8 100644 } diff --git a/examples-testing/examples/webgl_math_orientation_transform.ts b/examples-testing/examples/webgl_math_orientation_transform.ts -index 1d66997c..a596f014 100644 +index 1d66997c4..a596f014f 100644 --- a/examples-testing/examples/webgl_math_orientation_transform.ts +++ b/examples-testing/examples/webgl_math_orientation_transform.ts @@ -2,7 +2,11 @@ import * as THREE from 'three'; @@ -9607,7 +9607,7 @@ index 1d66997c..a596f014 100644 const spherical = new THREE.Spherical(); const rotationMatrix = new THREE.Matrix4(); diff --git a/examples-testing/examples/webgl_mesh_batch.ts b/examples-testing/examples/webgl_mesh_batch.ts -index e238e50a..618a58c0 100644 +index e238e50a3..618a58c03 100644 --- a/examples-testing/examples/webgl_mesh_batch.ts +++ b/examples-testing/examples/webgl_mesh_batch.ts @@ -4,12 +4,12 @@ import Stats from 'three/addons/libs/stats.module.js'; @@ -9718,7 +9718,7 @@ index e238e50a..618a58c0 100644 renderer.render(scene, camera); diff --git a/examples-testing/examples/webgl_mirror.ts b/examples-testing/examples/webgl_mirror.ts -index 4f1f17f7..6c780eab 100644 +index 4f1f17f7d..6c780eabe 100644 --- a/examples-testing/examples/webgl_mirror.ts +++ b/examples-testing/examples/webgl_mirror.ts @@ -5,13 +5,13 @@ import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; @@ -9749,7 +9749,7 @@ index 4f1f17f7..6c780eab 100644 // renderer renderer = new THREE.WebGLRenderer({ antialias: true }); diff --git a/examples-testing/examples/webgl_modifier_edgesplit.ts b/examples-testing/examples/webgl_modifier_edgesplit.ts -index 4725eff6..96620f50 100644 +index 4725eff62..96620f504 100644 --- a/examples-testing/examples/webgl_modifier_edgesplit.ts +++ b/examples-testing/examples/webgl_modifier_edgesplit.ts @@ -7,9 +7,11 @@ import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js' @@ -9777,7 +9777,7 @@ index 4725eff6..96620f50 100644 modifier = new EdgeSplitModifier(); diff --git a/examples-testing/examples/webgl_modifier_simplifier.ts b/examples-testing/examples/webgl_modifier_simplifier.ts -index e6ea453b..f55a6581 100644 +index e6ea453b3..f55a6581b 100644 --- a/examples-testing/examples/webgl_modifier_simplifier.ts +++ b/examples-testing/examples/webgl_modifier_simplifier.ts @@ -4,7 +4,7 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -9799,7 +9799,7 @@ index e6ea453b..f55a6581 100644 simplified.material.flatShading = true; const count = Math.floor(simplified.geometry.attributes.position.count * 0.875); // number of vertices to remove diff --git a/examples-testing/examples/webgl_modifier_tessellation.ts b/examples-testing/examples/webgl_modifier_tessellation.ts -index 4600fc6c..83af66a7 100644 +index 4600fc6cb..83af66a7c 100644 --- a/examples-testing/examples/webgl_modifier_tessellation.ts +++ b/examples-testing/examples/webgl_modifier_tessellation.ts @@ -4,14 +4,14 @@ import Stats from 'three/addons/libs/stats.module.js'; @@ -9851,7 +9851,7 @@ index 4600fc6c..83af66a7 100644 controls = new TrackballControls(camera, renderer.domElement); diff --git a/examples-testing/examples/webgl_morphtargets.ts b/examples-testing/examples/webgl_morphtargets.ts -index d8a4bbe8..92ef3836 100644 +index d8a4bbe8d..92ef3836a 100644 --- a/examples-testing/examples/webgl_morphtargets.ts +++ b/examples-testing/examples/webgl_morphtargets.ts @@ -3,12 +3,16 @@ import * as THREE from 'three'; @@ -9896,7 +9896,7 @@ index d8a4bbe8..92ef3836 100644 } diff --git a/examples-testing/examples/webgl_morphtargets_face.ts b/examples-testing/examples/webgl_morphtargets_face.ts -index 7f348c8a..1fa93e3b 100644 +index 7f348c8a4..1fa93e3be 100644 --- a/examples-testing/examples/webgl_morphtargets_face.ts +++ b/examples-testing/examples/webgl_morphtargets_face.ts @@ -12,7 +12,13 @@ import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; @@ -9932,7 +9932,7 @@ index 7f348c8a..1fa93e3b 100644 } }); diff --git a/examples-testing/examples/webgl_morphtargets_horse.ts b/examples-testing/examples/webgl_morphtargets_horse.ts -index 2c29e9c0..5e285283 100644 +index 2c29e9c0e..5e285283a 100644 --- a/examples-testing/examples/webgl_morphtargets_horse.ts +++ b/examples-testing/examples/webgl_morphtargets_horse.ts @@ -4,9 +4,9 @@ import Stats from 'three/addons/libs/stats.module.js'; @@ -9949,7 +9949,7 @@ index 2c29e9c0..5e285283 100644 const radius = 600; let theta = 0; diff --git a/examples-testing/examples/webgl_morphtargets_sphere.ts b/examples-testing/examples/webgl_morphtargets_sphere.ts -index 3e36f002..0c45a0cf 100644 +index 3e36f002c..0c45a0cf9 100644 --- a/examples-testing/examples/webgl_morphtargets_sphere.ts +++ b/examples-testing/examples/webgl_morphtargets_sphere.ts @@ -3,9 +3,9 @@ import * as THREE from 'three'; @@ -9995,7 +9995,7 @@ index 3e36f002..0c45a0cf 100644 } } diff --git a/examples-testing/examples/webgl_multiple_elements.ts b/examples-testing/examples/webgl_multiple_elements.ts -index 64f8a9c5..8dafa688 100644 +index 64f8a9c5f..8dafa6881 100644 --- a/examples-testing/examples/webgl_multiple_elements.ts +++ b/examples-testing/examples/webgl_multiple_elements.ts @@ -2,14 +2,14 @@ import * as THREE from 'three'; @@ -10026,7 +10026,7 @@ index 64f8a9c5..8dafa688 100644 for (let i = 0; i < 40; i++) { const scene = new THREE.Scene(); diff --git a/examples-testing/examples/webgl_multiple_rendertargets.ts b/examples-testing/examples/webgl_multiple_rendertargets.ts -index 86708082..f63b7de6 100644 +index 86708082b..f63b7de60 100644 --- a/examples-testing/examples/webgl_multiple_rendertargets.ts +++ b/examples-testing/examples/webgl_multiple_rendertargets.ts @@ -3,9 +3,9 @@ import * as THREE from 'three'; @@ -10076,7 +10076,7 @@ index 86708082..f63b7de6 100644 }); diff --git a/examples-testing/examples/webgl_multiple_scenes_comparison.ts b/examples-testing/examples/webgl_multiple_scenes_comparison.ts -index 41a5130d..66d539a9 100644 +index 41a5130d4..66d539a9b 100644 --- a/examples-testing/examples/webgl_multiple_scenes_comparison.ts +++ b/examples-testing/examples/webgl_multiple_scenes_comparison.ts @@ -2,15 +2,15 @@ import * as THREE from 'three'; @@ -10124,7 +10124,7 @@ index 41a5130d..66d539a9 100644 slider.style.left = sliderPos - slider.offsetWidth / 2 + 'px'; } diff --git a/examples-testing/examples/webgl_multiple_views.ts b/examples-testing/examples/webgl_multiple_views.ts -index 672846f9..5ba46471 100644 +index 672846f9f..5ba46471a 100644 --- a/examples-testing/examples/webgl_multiple_views.ts +++ b/examples-testing/examples/webgl_multiple_views.ts @@ -2,15 +2,26 @@ import * as THREE from 'three'; @@ -10196,7 +10196,7 @@ index 672846f9..5ba46471 100644 view.updateCamera(camera, scene, mouseX); diff --git a/examples-testing/examples/webgl_multisampled_renderbuffers.ts b/examples-testing/examples/webgl_multisampled_renderbuffers.ts -index df84fb14..fc3da7a8 100644 +index df84fb144..fc3da7a87 100644 --- a/examples-testing/examples/webgl_multisampled_renderbuffers.ts +++ b/examples-testing/examples/webgl_multisampled_renderbuffers.ts @@ -5,9 +5,9 @@ import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; @@ -10221,7 +10221,7 @@ index df84fb14..fc3da7a8 100644 camera = new THREE.PerspectiveCamera(45, container.offsetWidth / container.offsetHeight, 10, 2000); camera.position.z = 500; diff --git a/examples-testing/examples/webgl_panorama_cube.ts b/examples-testing/examples/webgl_panorama_cube.ts -index efd09cfc..e4832209 100644 +index efd09cfc5..e4832209e 100644 --- a/examples-testing/examples/webgl_panorama_cube.ts +++ b/examples-testing/examples/webgl_panorama_cube.ts @@ -2,14 +2,14 @@ import * as THREE from 'three'; @@ -10264,7 +10264,7 @@ index efd09cfc..e4832209 100644 canvas.width = tileWidth; context.drawImage(image, tileWidth * i, 0, tileWidth, tileWidth, 0, 0, tileWidth, tileWidth); diff --git a/examples-testing/examples/webgl_panorama_equirectangular.ts b/examples-testing/examples/webgl_panorama_equirectangular.ts -index 35949ee6..6d631dee 100644 +index 35949ee6f..6d631dee3 100644 --- a/examples-testing/examples/webgl_panorama_equirectangular.ts +++ b/examples-testing/examples/webgl_panorama_equirectangular.ts @@ -1,6 +1,6 @@ @@ -10320,7 +10320,7 @@ index 35949ee6..6d631dee 100644 camera.fov = THREE.MathUtils.clamp(fov, 10, 75); diff --git a/examples-testing/examples/webgl_performance.ts b/examples-testing/examples/webgl_performance.ts -index 697ea36f..22368981 100644 +index 697ea36fb..223689815 100644 --- a/examples-testing/examples/webgl_performance.ts +++ b/examples-testing/examples/webgl_performance.ts @@ -6,7 +6,7 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -10333,7 +10333,7 @@ index 697ea36f..22368981 100644 init(); diff --git a/examples-testing/examples/webgl_pmrem_cubemap.ts b/examples-testing/examples/webgl_pmrem_cubemap.ts -index fb5bdafc..c432f8d3 100644 +index fb5bdafc1..c432f8d35 100644 --- a/examples-testing/examples/webgl_pmrem_cubemap.ts +++ b/examples-testing/examples/webgl_pmrem_cubemap.ts @@ -4,7 +4,7 @@ import { HDRCubeTextureLoader } from 'three/addons/loaders/HDRCubeTextureLoader. @@ -10346,7 +10346,7 @@ index fb5bdafc..c432f8d3 100644 init(); diff --git a/examples-testing/examples/webgl_pmrem_equirectangular.ts b/examples-testing/examples/webgl_pmrem_equirectangular.ts -index e7b8e76d..409d5e8e 100644 +index e7b8e76df..409d5e8e3 100644 --- a/examples-testing/examples/webgl_pmrem_equirectangular.ts +++ b/examples-testing/examples/webgl_pmrem_equirectangular.ts @@ -4,7 +4,7 @@ import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; @@ -10359,7 +10359,7 @@ index e7b8e76d..409d5e8e 100644 init(); diff --git a/examples-testing/examples/webgl_pmrem_test.ts b/examples-testing/examples/webgl_pmrem_test.ts -index 3c482338..292f01c3 100644 +index 3c482338f..292f01c3d 100644 --- a/examples-testing/examples/webgl_pmrem_test.ts +++ b/examples-testing/examples/webgl_pmrem_test.ts @@ -5,7 +5,7 @@ import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; @@ -10384,7 +10384,7 @@ index 3c482338..292f01c3 100644 }); diff --git a/examples-testing/examples/webgl_points_billboards.ts b/examples-testing/examples/webgl_points_billboards.ts -index 24d4de1a..f1a089bf 100644 +index 24d4de1a9..f1a089bf9 100644 --- a/examples-testing/examples/webgl_points_billboards.ts +++ b/examples-testing/examples/webgl_points_billboards.ts @@ -4,7 +4,11 @@ import Stats from 'three/addons/libs/stats.module.js'; @@ -10410,7 +10410,7 @@ index 24d4de1a..f1a089bf 100644 mouseX = event.clientX - windowHalfX; diff --git a/examples-testing/examples/webgl_points_sprites.ts b/examples-testing/examples/webgl_points_sprites.ts -index 31b9e2ce..d8f24b29 100644 +index 31b9e2ce1..d8f24b296 100644 --- a/examples-testing/examples/webgl_points_sprites.ts +++ b/examples-testing/examples/webgl_points_sprites.ts @@ -4,14 +4,18 @@ import Stats from 'three/addons/libs/stats.module.js'; @@ -10453,7 +10453,7 @@ index 31b9e2ce..d8f24b29 100644 mouseX = event.clientX - windowHalfX; diff --git a/examples-testing/examples/webgl_points_waves.ts b/examples-testing/examples/webgl_points_waves.ts -index 91986e9e..cb8e7687 100644 +index 91986e9e9..cb8e76873 100644 --- a/examples-testing/examples/webgl_points_waves.ts +++ b/examples-testing/examples/webgl_points_waves.ts @@ -6,10 +6,10 @@ const SEPARATION = 100, @@ -10491,7 +10491,7 @@ index 91986e9e..cb8e7687 100644 mouseX = event.clientX - windowHalfX; diff --git a/examples-testing/examples/webgl_portal.ts b/examples-testing/examples/webgl_portal.ts -index 4bc59593..e61736c3 100644 +index 4bc59593f..e61736c30 100644 --- a/examples-testing/examples/webgl_portal.ts +++ b/examples-testing/examples/webgl_portal.ts @@ -3,26 +3,26 @@ import * as THREE from 'three'; @@ -10548,7 +10548,7 @@ index 4bc59593..e61736c3 100644 thisPortalMesh.worldToLocal(reflectedPosition.copy(camera.position)); reflectedPosition.x *= -1.0; diff --git a/examples-testing/examples/webgl_postprocessing.ts b/examples-testing/examples/webgl_postprocessing.ts -index ecc9b28e..2e894236 100644 +index ecc9b28ee..2e8942363 100644 --- a/examples-testing/examples/webgl_postprocessing.ts +++ b/examples-testing/examples/webgl_postprocessing.ts @@ -8,8 +8,8 @@ import { RGBShiftShader } from 'three/addons/shaders/RGBShiftShader.js'; @@ -10563,7 +10563,7 @@ index ecc9b28e..2e894236 100644 init(); diff --git a/examples-testing/examples/webgl_postprocessing_advanced.ts b/examples-testing/examples/webgl_postprocessing_advanced.ts -index 82fc39be..1a14c2b9 100644 +index 82fc39be3..1a14c2b9b 100644 --- a/examples-testing/examples/webgl_postprocessing_advanced.ts +++ b/examples-testing/examples/webgl_postprocessing_advanced.ts @@ -21,11 +21,21 @@ import { GammaCorrectionShader } from 'three/addons/shaders/GammaCorrectionShade @@ -10629,7 +10629,7 @@ index 82fc39be..1a14c2b9 100644 diffuseMap.colorSpace = THREE.SRGBColorSpace; diff --git a/examples-testing/examples/webgl_postprocessing_afterimage.ts b/examples-testing/examples/webgl_postprocessing_afterimage.ts -index 97353dcd..433322a3 100644 +index 97353dcd2..433322a3e 100644 --- a/examples-testing/examples/webgl_postprocessing_afterimage.ts +++ b/examples-testing/examples/webgl_postprocessing_afterimage.ts @@ -7,10 +7,10 @@ import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; @@ -10647,7 +10647,7 @@ index 97353dcd..433322a3 100644 const params = { enable: true, diff --git a/examples-testing/examples/webgl_postprocessing_backgrounds.ts b/examples-testing/examples/webgl_postprocessing_backgrounds.ts -index 57a6a2db..f6d4c716 100644 +index 57a6a2dbd..f6d4c7167 100644 --- a/examples-testing/examples/webgl_postprocessing_backgrounds.ts +++ b/examples-testing/examples/webgl_postprocessing_backgrounds.ts @@ -11,10 +11,10 @@ import { ClearPass } from 'three/addons/postprocessing/ClearPass.js'; @@ -10684,7 +10684,7 @@ index 57a6a2db..f6d4c716 100644 prefix + 'px' + postfix, prefix + 'nx' + postfix, diff --git a/examples-testing/examples/webgl_postprocessing_fxaa.ts b/examples-testing/examples/webgl_postprocessing_fxaa.ts -index c5e632ad..1774b0c8 100644 +index c5e632ad7..1774b0c84 100644 --- a/examples-testing/examples/webgl_postprocessing_fxaa.ts +++ b/examples-testing/examples/webgl_postprocessing_fxaa.ts @@ -6,14 +6,18 @@ import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; @@ -10710,7 +10710,7 @@ index c5e632ad..1774b0c8 100644 camera = new THREE.PerspectiveCamera(45, container.offsetWidth / container.offsetHeight, 1, 2000); camera.position.z = 500; diff --git a/examples-testing/examples/webgl_postprocessing_glitch.ts b/examples-testing/examples/webgl_postprocessing_glitch.ts -index 02acda57..5c39961f 100644 +index 02acda572..5c39961fa 100644 --- a/examples-testing/examples/webgl_postprocessing_glitch.ts +++ b/examples-testing/examples/webgl_postprocessing_glitch.ts @@ -5,21 +5,21 @@ import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; @@ -10751,7 +10751,7 @@ index 02acda57..5c39961f 100644 updateOptions(); diff --git a/examples-testing/examples/webgl_postprocessing_godrays.ts b/examples-testing/examples/webgl_postprocessing_godrays.ts -index b7c2c666..a8d6f639 100644 +index b7c2c6661..a8d6f639f 100644 --- a/examples-testing/examples/webgl_postprocessing_godrays.ts +++ b/examples-testing/examples/webgl_postprocessing_godrays.ts @@ -6,10 +6,10 @@ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; @@ -10796,7 +10796,7 @@ index b7c2c666..a8d6f639 100644 obj.receiveShadow = true; } diff --git a/examples-testing/examples/webgl_postprocessing_gtao.ts b/examples-testing/examples/webgl_postprocessing_gtao.ts -index a37d3041..9165702d 100644 +index a37d3041b..9165702d8 100644 --- a/examples-testing/examples/webgl_postprocessing_gtao.ts +++ b/examples-testing/examples/webgl_postprocessing_gtao.ts @@ -10,7 +10,14 @@ import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; @@ -10816,7 +10816,7 @@ index a37d3041..9165702d 100644 init(); diff --git a/examples-testing/examples/webgl_postprocessing_masking.ts b/examples-testing/examples/webgl_postprocessing_masking.ts -index a4d09866..238c37da 100644 +index a4d09866d..238c37da7 100644 --- a/examples-testing/examples/webgl_postprocessing_masking.ts +++ b/examples-testing/examples/webgl_postprocessing_masking.ts @@ -6,8 +6,8 @@ import { ClearPass } from 'three/addons/postprocessing/ClearPass.js'; @@ -10831,7 +10831,7 @@ index a4d09866..238c37da 100644 init(); diff --git a/examples-testing/examples/webgl_postprocessing_outline.ts b/examples-testing/examples/webgl_postprocessing_outline.ts -index 31ef6b9b..4b1fa11a 100644 +index 31ef6b9b2..4b1fa11a3 100644 --- a/examples-testing/examples/webgl_postprocessing_outline.ts +++ b/examples-testing/examples/webgl_postprocessing_outline.ts @@ -12,11 +12,11 @@ import { OutlinePass } from 'three/addons/postprocessing/OutlinePass.js'; @@ -10887,7 +10887,7 @@ index 31ef6b9b..4b1fa11a 100644 selectedObjects.push(object); } diff --git a/examples-testing/examples/webgl_postprocessing_pixel.ts b/examples-testing/examples/webgl_postprocessing_pixel.ts -index 04aec481..3dc6f997 100644 +index 04aec4816..3dc6f997d 100644 --- a/examples-testing/examples/webgl_postprocessing_pixel.ts +++ b/examples-testing/examples/webgl_postprocessing_pixel.ts @@ -6,8 +6,14 @@ import { RenderPixelatedPass } from 'three/addons/postprocessing/RenderPixelated @@ -10978,7 +10978,7 @@ index 04aec481..3dc6f997 100644 const worldScreenWidth = (camera.right - camera.left) / camera.zoom; const worldScreenHeight = (camera.top - camera.bottom) / camera.zoom; diff --git a/examples-testing/examples/webgl_postprocessing_procedural.ts b/examples-testing/examples/webgl_postprocessing_procedural.ts -index 86982427..4533b72b 100644 +index 869824270..4533b72bd 100644 --- a/examples-testing/examples/webgl_postprocessing_procedural.ts +++ b/examples-testing/examples/webgl_postprocessing_procedural.ts @@ -3,16 +3,20 @@ import * as THREE from 'three'; @@ -11030,7 +11030,7 @@ index 86982427..4533b72b 100644 postMaterial = noiseRandom3DMaterial; const postPlane = new THREE.PlaneGeometry(2, 2); diff --git a/examples-testing/examples/webgl_postprocessing_rgb_halftone.ts b/examples-testing/examples/webgl_postprocessing_rgb_halftone.ts -index 5a40f979..c1630944 100644 +index 5a40f9793..c16309448 100644 --- a/examples-testing/examples/webgl_postprocessing_rgb_halftone.ts +++ b/examples-testing/examples/webgl_postprocessing_rgb_halftone.ts @@ -8,11 +8,11 @@ import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; @@ -11048,7 +11048,7 @@ index 5a40f979..c1630944 100644 init(); diff --git a/examples-testing/examples/webgl_postprocessing_sao.ts b/examples-testing/examples/webgl_postprocessing_sao.ts -index 0c6298e6..c7ab9a36 100644 +index 0c6298e6b..c7ab9a36e 100644 --- a/examples-testing/examples/webgl_postprocessing_sao.ts +++ b/examples-testing/examples/webgl_postprocessing_sao.ts @@ -8,10 +8,10 @@ import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; @@ -11067,7 +11067,7 @@ index 0c6298e6..c7ab9a36 100644 init(); diff --git a/examples-testing/examples/webgl_postprocessing_smaa.ts b/examples-testing/examples/webgl_postprocessing_smaa.ts -index 9e73d38b..e07817b1 100644 +index 9e73d38b0..e07817b17 100644 --- a/examples-testing/examples/webgl_postprocessing_smaa.ts +++ b/examples-testing/examples/webgl_postprocessing_smaa.ts @@ -8,7 +8,12 @@ import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; @@ -11094,7 +11094,7 @@ index 9e73d38b..e07817b1 100644 renderer = new THREE.WebGLRenderer(); renderer.setPixelRatio(window.devicePixelRatio); diff --git a/examples-testing/examples/webgl_postprocessing_sobel.ts b/examples-testing/examples/webgl_postprocessing_sobel.ts -index 55d88dc0..1512ed8b 100644 +index 55d88dc02..1512ed8bd 100644 --- a/examples-testing/examples/webgl_postprocessing_sobel.ts +++ b/examples-testing/examples/webgl_postprocessing_sobel.ts @@ -11,9 +11,9 @@ import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js'; @@ -11110,7 +11110,7 @@ index 55d88dc0..1512ed8b 100644 const params = { enable: true, diff --git a/examples-testing/examples/webgl_postprocessing_ssaa.ts b/examples-testing/examples/webgl_postprocessing_ssaa.ts -index 45d8767b..84be0a91 100644 +index 45d8767b1..84be0a91f 100644 --- a/examples-testing/examples/webgl_postprocessing_ssaa.ts +++ b/examples-testing/examples/webgl_postprocessing_ssaa.ts @@ -7,10 +7,10 @@ import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; @@ -11147,7 +11147,7 @@ index 45d8767b..84be0a91 100644 composer.render(); diff --git a/examples-testing/examples/webgl_postprocessing_ssao.ts b/examples-testing/examples/webgl_postprocessing_ssao.ts -index fd3739af..5d2865f0 100644 +index fd3739af3..5d2865f06 100644 --- a/examples-testing/examples/webgl_postprocessing_ssao.ts +++ b/examples-testing/examples/webgl_postprocessing_ssao.ts @@ -8,10 +8,10 @@ import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; @@ -11166,7 +11166,7 @@ index fd3739af..5d2865f0 100644 init(); diff --git a/examples-testing/examples/webgl_postprocessing_ssr.ts b/examples-testing/examples/webgl_postprocessing_ssr.ts -index 1f5e48d3..bad3a477 100644 +index 1f5e48d3b..bad3a477f 100644 --- a/examples-testing/examples/webgl_postprocessing_ssr.ts +++ b/examples-testing/examples/webgl_postprocessing_ssr.ts @@ -18,17 +18,17 @@ const params = { @@ -11208,7 +11208,7 @@ index 1f5e48d3..bad3a477 100644 geometry = new THREE.BoxGeometry(0.05, 0.05, 0.05); material = new THREE.MeshStandardMaterial({ color: 'green' }); diff --git a/examples-testing/examples/webgl_postprocessing_taa.ts b/examples-testing/examples/webgl_postprocessing_taa.ts -index 11a98674..ddb2bcc6 100644 +index 11a986741..ddb2bcc6c 100644 --- a/examples-testing/examples/webgl_postprocessing_taa.ts +++ b/examples-testing/examples/webgl_postprocessing_taa.ts @@ -8,8 +8,13 @@ import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; @@ -11237,7 +11237,7 @@ index 11a98674..ddb2bcc6 100644 renderer = new THREE.WebGLRenderer(); renderer.setPixelRatio(window.devicePixelRatio); diff --git a/examples-testing/examples/webgl_postprocessing_transition.ts b/examples-testing/examples/webgl_postprocessing_transition.ts -index 1cf5dd3c..53c3e9b3 100644 +index 1cf5dd3cb..53c3e9b31 100644 --- a/examples-testing/examples/webgl_postprocessing_transition.ts +++ b/examples-testing/examples/webgl_postprocessing_transition.ts @@ -7,10 +7,10 @@ import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; @@ -11359,7 +11359,7 @@ index 1cf5dd3c..53c3e9b3 100644 const dummy = new THREE.Object3D(); diff --git a/examples-testing/examples/webgl_postprocessing_unreal_bloom.ts b/examples-testing/examples/webgl_postprocessing_unreal_bloom.ts -index b5e2ee0f..1de630e0 100644 +index b5e2ee0f9..1de630e05 100644 --- a/examples-testing/examples/webgl_postprocessing_unreal_bloom.ts +++ b/examples-testing/examples/webgl_postprocessing_unreal_bloom.ts @@ -10,8 +10,8 @@ import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; @@ -11383,7 +11383,7 @@ index b5e2ee0f..1de630e0 100644 timer = new THREE.Timer(); timer.connect(document); diff --git a/examples-testing/examples/webgl_postprocessing_unreal_bloom_selective.ts b/examples-testing/examples/webgl_postprocessing_unreal_bloom_selective.ts -index 288b4477..e56fedad 100644 +index 288b4477d..e56fedad4 100644 --- a/examples-testing/examples/webgl_postprocessing_unreal_bloom_selective.ts +++ b/examples-testing/examples/webgl_postprocessing_unreal_bloom_selective.ts @@ -23,7 +23,7 @@ const params = { @@ -11452,7 +11452,7 @@ index 288b4477..e56fedad 100644 } } diff --git a/examples-testing/examples/webgl_random_uv.ts b/examples-testing/examples/webgl_random_uv.ts -index fea6b347..fef39937 100644 +index fea6b3478..fef399378 100644 --- a/examples-testing/examples/webgl_random_uv.ts +++ b/examples-testing/examples/webgl_random_uv.ts @@ -6,14 +6,24 @@ import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; @@ -11501,7 +11501,7 @@ index fea6b347..fef39937 100644 materialIn.transparent = true; diff --git a/examples-testing/examples/webgl_raycaster_sprite.ts b/examples-testing/examples/webgl_raycaster_sprite.ts -index f35d5de1..73dd9349 100644 +index f35d5de17..73dd93494 100644 --- a/examples-testing/examples/webgl_raycaster_sprite.ts +++ b/examples-testing/examples/webgl_raycaster_sprite.ts @@ -2,10 +2,10 @@ import * as THREE from 'three'; @@ -11537,7 +11537,7 @@ index f35d5de1..73dd9349 100644 } } diff --git a/examples-testing/examples/webgl_raycaster_texture.ts b/examples-testing/examples/webgl_raycaster_texture.ts -index 72c7054d..0e584dc0 100644 +index 72c7054dc..0e584dc0b 100644 --- a/examples-testing/examples/webgl_raycaster_texture.ts +++ b/examples-testing/examples/webgl_raycaster_texture.ts @@ -8,7 +8,15 @@ const WRAPPING = { @@ -11750,7 +11750,7 @@ index 72c7054d..0e584dc0 100644 circleTexture.needsUpdate = true; } diff --git a/examples-testing/examples/webgl_read_float_buffer.ts b/examples-testing/examples/webgl_read_float_buffer.ts -index 69f84772..e0e975c4 100644 +index 69f847729..e0e975c4f 100644 --- a/examples-testing/examples/webgl_read_float_buffer.ts +++ b/examples-testing/examples/webgl_read_float_buffer.ts @@ -2,9 +2,14 @@ import * as THREE from 'three'; @@ -11824,7 +11824,7 @@ index 69f84772..e0e975c4 100644 mouseY = event.clientY - windowHalfY; } diff --git a/examples-testing/examples/webgl_refraction.ts b/examples-testing/examples/webgl_refraction.ts -index b8ef7143..1c61619e 100644 +index b8ef7143d..1c61619ec 100644 --- a/examples-testing/examples/webgl_refraction.ts +++ b/examples-testing/examples/webgl_refraction.ts @@ -4,14 +4,14 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -11846,7 +11846,7 @@ index b8ef7143..1c61619e 100644 timer = new THREE.Timer(); timer.connect(document); diff --git a/examples-testing/examples/webgl_rtt.ts b/examples-testing/examples/webgl_rtt.ts -index b80e78ed..47e8e8b3 100644 +index b80e78ed3..47e8e8b3b 100644 --- a/examples-testing/examples/webgl_rtt.ts +++ b/examples-testing/examples/webgl_rtt.ts @@ -2,9 +2,16 @@ import * as THREE from 'three'; @@ -11914,7 +11914,7 @@ index b80e78ed..47e8e8b3 100644 mouseY = event.clientY - windowHalfY; } diff --git a/examples-testing/examples/webgl_shader.ts b/examples-testing/examples/webgl_shader.ts -index 47a6c7ec..7ac1257b 100644 +index 47a6c7ece..7ac1257b6 100644 --- a/examples-testing/examples/webgl_shader.ts +++ b/examples-testing/examples/webgl_shader.ts @@ -1,13 +1,13 @@ @@ -11946,7 +11946,7 @@ index 47a6c7ec..7ac1257b 100644 const mesh = new THREE.Mesh(geometry, material); diff --git a/examples-testing/examples/webgl_shader_lava.ts b/examples-testing/examples/webgl_shader_lava.ts -index 0c974f5b..14a618b4 100644 +index 0c974f5b9..14a618b44 100644 --- a/examples-testing/examples/webgl_shader_lava.ts +++ b/examples-testing/examples/webgl_shader_lava.ts @@ -5,14 +5,22 @@ import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; @@ -11988,7 +11988,7 @@ index 0c974f5b..14a618b4 100644 mesh = new THREE.Mesh(new THREE.TorusGeometry(size, 0.3, 30, 30), material); diff --git a/examples-testing/examples/webgl_shaders_ocean.ts b/examples-testing/examples/webgl_shaders_ocean.ts -index e0e26ed3..6832f4e6 100644 +index e0e26ed33..6832f4e68 100644 --- a/examples-testing/examples/webgl_shaders_ocean.ts +++ b/examples-testing/examples/webgl_shaders_ocean.ts @@ -8,14 +8,14 @@ import { Water } from 'three/addons/objects/Water.js'; @@ -12020,7 +12020,7 @@ index e0e26ed3..6832f4e6 100644 function updateSun() { const phi = THREE.MathUtils.degToRad(90 - parameters.elevation); diff --git a/examples-testing/examples/webgl_shaders_sky.ts b/examples-testing/examples/webgl_shaders_sky.ts -index 01cdddec..0cd9aa7b 100644 +index 01cdddec4..0cd9aa7bb 100644 --- a/examples-testing/examples/webgl_shaders_sky.ts +++ b/examples-testing/examples/webgl_shaders_sky.ts @@ -4,9 +4,9 @@ import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; @@ -12036,7 +12036,7 @@ index 01cdddec..0cd9aa7b 100644 init(); diff --git a/examples-testing/examples/webgl_shadow_contact.ts b/examples-testing/examples/webgl_shadow_contact.ts -index f402fa20..8e71282d 100644 +index f402fa20d..8e71282d7 100644 --- a/examples-testing/examples/webgl_shadow_contact.ts +++ b/examples-testing/examples/webgl_shadow_contact.ts @@ -5,9 +5,9 @@ import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; @@ -12089,7 +12089,7 @@ index f402fa20..8e71282d 100644 // blur horizontally and draw in the renderTargetBlur diff --git a/examples-testing/examples/webgl_shadowmap.ts b/examples-testing/examples/webgl_shadowmap.ts -index 9557bb05..61f97bae 100644 +index 9557bb051..61f97bae9 100644 --- a/examples-testing/examples/webgl_shadowmap.ts +++ b/examples-testing/examples/webgl_shadowmap.ts @@ -15,18 +15,18 @@ const SCREEN_WIDTH = window.innerWidth; @@ -12197,7 +12197,7 @@ index 9557bb05..61f97bae 100644 if (morph.position.x > 2000) { morph.position.x = -1000 - Math.random() * 500; diff --git a/examples-testing/examples/webgl_shadowmap_csm.ts b/examples-testing/examples/webgl_shadowmap_csm.ts -index c8e959a0..6eca1560 100644 +index c8e959a0b..6eca15605 100644 --- a/examples-testing/examples/webgl_shadowmap_csm.ts +++ b/examples-testing/examples/webgl_shadowmap_csm.ts @@ -2,12 +2,32 @@ import * as THREE from 'three'; @@ -12249,7 +12249,7 @@ index c8e959a0..6eca1560 100644 }); }); diff --git a/examples-testing/examples/webgl_shadowmap_pcss.ts b/examples-testing/examples/webgl_shadowmap_pcss.ts -index de542688..6d750ce4 100644 +index de5426883..6d750ce4b 100644 --- a/examples-testing/examples/webgl_shadowmap_pcss.ts +++ b/examples-testing/examples/webgl_shadowmap_pcss.ts @@ -4,10 +4,10 @@ import Stats from 'three/addons/libs/stats.module.js'; @@ -12283,7 +12283,7 @@ index de542688..6d750ce4 100644 '\t\t\t\tfloat depth = texture2D( shadowMap, shadowCoord.xy ).r;', ); diff --git a/examples-testing/examples/webgl_shadowmap_performance.ts b/examples-testing/examples/webgl_shadowmap_performance.ts -index de54f335..279e7f9f 100644 +index de54f335f..279e7f9f1 100644 --- a/examples-testing/examples/webgl_shadowmap_performance.ts +++ b/examples-testing/examples/webgl_shadowmap_performance.ts @@ -16,16 +16,17 @@ const FLOOR = -250; @@ -12356,7 +12356,7 @@ index de54f335..279e7f9f 100644 if (morph.position.x > 2000) { morph.position.x = -1000 - Math.random() * 500; diff --git a/examples-testing/examples/webgl_shadowmap_pointlight.ts b/examples-testing/examples/webgl_shadowmap_pointlight.ts -index 4323655d..5db3f4dd 100644 +index 4323655dc..5db3f4dd8 100644 --- a/examples-testing/examples/webgl_shadowmap_pointlight.ts +++ b/examples-testing/examples/webgl_shadowmap_pointlight.ts @@ -4,8 +4,8 @@ import Stats from 'three/addons/libs/stats.module.js'; @@ -12401,7 +12401,7 @@ index 4323655d..5db3f4dd 100644 context.fillRect(0, 1, 2, 1); diff --git a/examples-testing/examples/webgl_shadowmap_progressive.ts b/examples-testing/examples/webgl_shadowmap_progressive.ts -index 29298630..9d04c42e 100644 +index 29298630f..9d04c42ee 100644 --- a/examples-testing/examples/webgl_shadowmap_progressive.ts +++ b/examples-testing/examples/webgl_shadowmap_progressive.ts @@ -9,17 +9,17 @@ import { ProgressiveLightMap } from 'three/addons/misc/ProgressiveLightMap.js'; @@ -12461,7 +12461,7 @@ index 29298630..9d04c42e 100644 } else { // Uniform Hemispherical Surface Distribution for Ambient Occlusion diff --git a/examples-testing/examples/webgl_shadowmap_viewer.ts b/examples-testing/examples/webgl_shadowmap_viewer.ts -index 2ed16e99..b66a00d4 100644 +index 2ed16e992..b66a00d4e 100644 --- a/examples-testing/examples/webgl_shadowmap_viewer.ts +++ b/examples-testing/examples/webgl_shadowmap_viewer.ts @@ -5,10 +5,14 @@ import Stats from 'three/addons/libs/stats.module.js'; @@ -12493,7 +12493,7 @@ index 2ed16e99..b66a00d4 100644 color: 0xff0000, shininess: 150, diff --git a/examples-testing/examples/webgl_shadowmap_vsm.ts b/examples-testing/examples/webgl_shadowmap_vsm.ts -index d5bf4f7b..6613aea1 100644 +index d5bf4f7bb..6613aea1c 100644 --- a/examples-testing/examples/webgl_shadowmap_vsm.ts +++ b/examples-testing/examples/webgl_shadowmap_vsm.ts @@ -5,9 +5,13 @@ import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; @@ -12532,7 +12532,7 @@ index d5bf4f7b..6613aea1 100644 renderer.render(scene, camera); diff --git a/examples-testing/examples/webgl_shadowmesh.ts b/examples-testing/examples/webgl_shadowmesh.ts -index e4f7ebb8..97eef890 100644 +index e4f7ebb8f..97eef8905 100644 --- a/examples-testing/examples/webgl_shadowmesh.ts +++ b/examples-testing/examples/webgl_shadowmesh.ts @@ -13,18 +13,18 @@ const renderer = new THREE.WebGLRenderer({ stencil: true }); @@ -12610,7 +12610,7 @@ index e4f7ebb8..97eef890 100644 } } diff --git a/examples-testing/examples/webgl_simple_gi.ts b/examples-testing/examples/webgl_simple_gi.ts -index 4ab6dc89..adea084a 100644 +index 4ab6dc895..adea084a0 100644 --- a/examples-testing/examples/webgl_simple_gi.ts +++ b/examples-testing/examples/webgl_simple_gi.ts @@ -3,7 +3,7 @@ import * as THREE from 'three'; @@ -12797,7 +12797,7 @@ index 4ab6dc89..adea084a 100644 init(); diff --git a/examples-testing/examples/webgl_sprites.ts b/examples-testing/examples/webgl_sprites.ts -index 2e418934..3ff04b10 100644 +index 2e4189347..3ff04b108 100644 --- a/examples-testing/examples/webgl_sprites.ts +++ b/examples-testing/examples/webgl_sprites.ts @@ -1,13 +1,17 @@ @@ -12873,7 +12873,7 @@ index 2e418934..3ff04b10 100644 sprite.material.rotation += 0.1 * (i / l); diff --git a/examples-testing/examples/webgl_test_memory.ts b/examples-testing/examples/webgl_test_memory.ts -index f5d0e112..128862a2 100644 +index f5d0e112d..128862a27 100644 --- a/examples-testing/examples/webgl_test_memory.ts +++ b/examples-testing/examples/webgl_test_memory.ts @@ -1,6 +1,6 @@ @@ -12894,7 +12894,7 @@ index f5d0e112..128862a2 100644 'rgb(' + Math.floor(Math.random() * 256) + diff --git a/examples-testing/examples/webgl_test_memory2.ts b/examples-testing/examples/webgl_test_memory2.ts -index 366a2791..74077e99 100644 +index 366a27914..74077e99b 100644 --- a/examples-testing/examples/webgl_test_memory2.ts +++ b/examples-testing/examples/webgl_test_memory2.ts @@ -2,15 +2,15 @@ import * as THREE from 'three'; @@ -12945,7 +12945,7 @@ index 366a2791..74077e99 100644 + console.log('after', renderer.info.programs!.length); } diff --git a/examples-testing/examples/webgl_test_wide_gamut.ts b/examples-testing/examples/webgl_test_wide_gamut.ts -index 5988299e..6da2af6c 100644 +index 5988299e1..6da2af6ca 100644 --- a/examples-testing/examples/webgl_test_wide_gamut.ts +++ b/examples-testing/examples/webgl_test_wide_gamut.ts @@ -9,12 +9,12 @@ import { @@ -13022,7 +13022,7 @@ index 5988299e..6da2af6c 100644 textureL.needsUpdate = true; diff --git a/examples-testing/examples/webgl_texture2darray_compressed.ts b/examples-testing/examples/webgl_texture2darray_compressed.ts -index e074be57..6039c4df 100644 +index e074be576..6039c4df2 100644 --- a/examples-testing/examples/webgl_texture2darray_compressed.ts +++ b/examples-testing/examples/webgl_texture2darray_compressed.ts @@ -3,7 +3,12 @@ import * as THREE from 'three'; @@ -13051,7 +13051,7 @@ index e074be57..6039c4df 100644 }); diff --git a/examples-testing/examples/webgl_texture2darray_layerupdate.ts b/examples-testing/examples/webgl_texture2darray_layerupdate.ts -index 0cc136cb..26237259 100644 +index 0cc136cb7..262372590 100644 --- a/examples-testing/examples/webgl_texture2darray_layerupdate.ts +++ b/examples-testing/examples/webgl_texture2darray_layerupdate.ts @@ -3,7 +3,7 @@ import * as THREE from 'three'; @@ -13112,7 +13112,7 @@ index 0cc136cb..26237259 100644 renderer.render(scene, camera); } diff --git a/examples-testing/examples/webgl_texture3d.ts b/examples-testing/examples/webgl_texture3d.ts -index 977dbadb..fb1460ca 100644 +index 977dbadb7..fb1460cae 100644 --- a/examples-testing/examples/webgl_texture3d.ts +++ b/examples-testing/examples/webgl_texture3d.ts @@ -5,7 +5,15 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -13142,7 +13142,7 @@ index 977dbadb..fb1460ca 100644 material.uniforms['u_renderthreshold'].value = volconfig.isothreshold; // For ISO renderstyle material.uniforms['u_cmdata'].value = cmtextures[volconfig.colormap]; diff --git a/examples-testing/examples/webgl_texture3d_partialupdate.ts b/examples-testing/examples/webgl_texture3d_partialupdate.ts -index 58615db8..1ae4e437 100644 +index 58615db84..1ae4e437f 100644 --- a/examples-testing/examples/webgl_texture3d_partialupdate.ts +++ b/examples-testing/examples/webgl_texture3d_partialupdate.ts @@ -6,14 +6,14 @@ import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; @@ -13195,7 +13195,7 @@ index 58615db8..1ae4e437 100644 renderer.render(scene, camera); } diff --git a/examples-testing/examples/webgl_tonemapping.ts b/examples-testing/examples/webgl_tonemapping.ts -index 2163e1b0..a7cb196e 100644 +index 2163e1b06..a7cb196ef 100644 --- a/examples-testing/examples/webgl_tonemapping.ts +++ b/examples-testing/examples/webgl_tonemapping.ts @@ -1,23 +1,32 @@ @@ -13259,7 +13259,7 @@ index 2163e1b0..a7cb196e 100644 } diff --git a/examples-testing/examples/webgl_tsl_clearcoat.ts b/examples-testing/examples/webgl_tsl_clearcoat.ts -index 6a123032..99fab8d9 100644 +index 6a123032e..99fab8d99 100644 --- a/examples-testing/examples/webgl_tsl_clearcoat.ts +++ b/examples-testing/examples/webgl_tsl_clearcoat.ts @@ -7,10 +7,10 @@ import { HDRCubeTextureLoader } from 'three/addons/loaders/HDRCubeTextureLoader. @@ -13277,7 +13277,7 @@ index 6a123032..99fab8d9 100644 init(); diff --git a/examples-testing/examples/webgl_tsl_instancing.ts b/examples-testing/examples/webgl_tsl_instancing.ts -index c80bebf9..3bdc2e64 100644 +index c80bebf9b..3bdc2e645 100644 --- a/examples-testing/examples/webgl_tsl_instancing.ts +++ b/examples-testing/examples/webgl_tsl_instancing.ts @@ -9,8 +9,12 @@ import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js' @@ -13373,7 +13373,7 @@ index c80bebf9..3bdc2e64 100644 const k = 1024; diff --git a/examples-testing/examples/webgl_tsl_shadowmap.ts b/examples-testing/examples/webgl_tsl_shadowmap.ts -index f006d7c1..29913d9a 100644 +index f006d7c1a..29913d9a4 100644 --- a/examples-testing/examples/webgl_tsl_shadowmap.ts +++ b/examples-testing/examples/webgl_tsl_shadowmap.ts @@ -5,9 +5,9 @@ import { mx_fractal_noise_float, mx_fractal_noise_vec3, positionLocal, positionW @@ -13399,7 +13399,7 @@ index f006d7c1..29913d9a 100644 const delta = timer.getDelta(); diff --git a/examples-testing/examples/webgl_tsl_skinning.ts b/examples-testing/examples/webgl_tsl_skinning.ts -index 6164dbab..27b9b71b 100644 +index 6164dbab6..27b9b71b6 100644 --- a/examples-testing/examples/webgl_tsl_skinning.ts +++ b/examples-testing/examples/webgl_tsl_skinning.ts @@ -4,9 +4,9 @@ import { WebGLRenderer } from 'three'; @@ -13415,7 +13415,7 @@ index 6164dbab..27b9b71b 100644 init(); diff --git a/examples-testing/examples/webgl_ubo.ts b/examples-testing/examples/webgl_ubo.ts -index a34a5b2f..75fc5e0d 100644 +index a34a5b2ff..75fc5e0d8 100644 --- a/examples-testing/examples/webgl_ubo.ts +++ b/examples-testing/examples/webgl_ubo.ts @@ -1,11 +1,11 @@ @@ -13464,7 +13464,7 @@ index a34a5b2f..75fc5e0d 100644 child.rotation.y += delta * 0.3; } diff --git a/examples-testing/examples/webgl_ubo_arrays.ts b/examples-testing/examples/webgl_ubo_arrays.ts -index 1d8f5f76..214deef5 100644 +index 1d8f5f763..214deef55 100644 --- a/examples-testing/examples/webgl_ubo_arrays.ts +++ b/examples-testing/examples/webgl_ubo_arrays.ts @@ -4,11 +4,15 @@ import Stats from 'three/addons/libs/stats.module.js'; @@ -13516,7 +13516,7 @@ index 1d8f5f76..214deef5 100644 // Parameters for circular movement const radius = 5; // Smaller radius for individual circular movements diff --git a/examples-testing/examples/webgl_video_kinect.ts b/examples-testing/examples/webgl_video_kinect.ts -index 8abc9391..dd637649 100644 +index 8abc93917..dd637649d 100644 --- a/examples-testing/examples/webgl_video_kinect.ts +++ b/examples-testing/examples/webgl_video_kinect.ts @@ -2,9 +2,9 @@ import * as THREE from 'three'; @@ -13562,7 +13562,7 @@ index 8abc9391..dd637649 100644 mouse.y = (event.clientY - window.innerHeight / 2) * 8; } diff --git a/examples-testing/examples/webgl_video_panorama_equirectangular.ts b/examples-testing/examples/webgl_video_panorama_equirectangular.ts -index 866eca16..07301138 100644 +index 866eca16a..073011389 100644 --- a/examples-testing/examples/webgl_video_panorama_equirectangular.ts +++ b/examples-testing/examples/webgl_video_panorama_equirectangular.ts @@ -1,6 +1,6 @@ @@ -13610,7 +13610,7 @@ index 866eca16..07301138 100644 lon = (onPointerDownPointerX - event.clientX) * 0.1 + onPointerDownLon; lat = (onPointerDownPointerY - event.clientY) * 0.1 + onPointerDownLat; diff --git a/examples-testing/examples/webgl_volume_cloud.ts b/examples-testing/examples/webgl_volume_cloud.ts -index 9aa07b98..dfe98826 100644 +index 9aa07b98f..dfe98826a 100644 --- a/examples-testing/examples/webgl_volume_cloud.ts +++ b/examples-testing/examples/webgl_volume_cloud.ts @@ -4,8 +4,8 @@ import { ImprovedNoise } from 'three/addons/math/ImprovedNoise.js'; @@ -13647,7 +13647,7 @@ index 9aa07b98..dfe98826 100644 renderer.render(scene, camera); } diff --git a/examples-testing/examples/webgl_volume_instancing.ts b/examples-testing/examples/webgl_volume_instancing.ts -index 7045732d..3a614592 100644 +index 7045732d6..3a6145920 100644 --- a/examples-testing/examples/webgl_volume_instancing.ts +++ b/examples-testing/examples/webgl_volume_instancing.ts @@ -2,7 +2,11 @@ import * as THREE from 'three'; @@ -13686,7 +13686,7 @@ index 7045732d..3a614592 100644 const transform = new THREE.Object3D(); diff --git a/examples-testing/examples/webgl_volume_perlin.ts b/examples-testing/examples/webgl_volume_perlin.ts -index 0f299f66..b02dd139 100644 +index 0f299f66f..b02dd139c 100644 --- a/examples-testing/examples/webgl_volume_perlin.ts +++ b/examples-testing/examples/webgl_volume_perlin.ts @@ -4,8 +4,8 @@ import { ImprovedNoise } from 'three/addons/math/ImprovedNoise.js'; @@ -13710,7 +13710,7 @@ index 0f299f66..b02dd139 100644 renderer.render(scene, camera); } diff --git a/examples-testing/examples/webgl_watch.ts b/examples-testing/examples/webgl_watch.ts -index 8c7b7ecb..83a4fbb6 100644 +index 8c7b7ecb6..83a4fbb6f 100644 --- a/examples-testing/examples/webgl_watch.ts +++ b/examples-testing/examples/webgl_watch.ts @@ -1,5 +1,5 @@ @@ -13816,7 +13816,7 @@ index 8c7b7ecb..83a4fbb6 100644 function upBloom() { diff --git a/examples-testing/examples/webgpu_animation_retargeting.ts b/examples-testing/examples/webgpu_animation_retargeting.ts -index 23ebb2af..8f8292c9 100644 +index 23ebb2af4..8f8292c9f 100644 --- a/examples-testing/examples/webgpu_animation_retargeting.ts +++ b/examples-testing/examples/webgpu_animation_retargeting.ts @@ -21,17 +21,17 @@ import { @@ -13889,7 +13889,7 @@ index 23ebb2af..8f8292c9 100644 const targetSkelHelper = new THREE.SkeletonHelper(targetModel.scene); diff --git a/examples-testing/examples/webgpu_animation_retargeting_readyplayer.ts b/examples-testing/examples/webgpu_animation_retargeting_readyplayer.ts -index de90d890..cd271c05 100644 +index de90d890f..cd271c05d 100644 --- a/examples-testing/examples/webgpu_animation_retargeting_readyplayer.ts +++ b/examples-testing/examples/webgpu_animation_retargeting_readyplayer.ts @@ -2,18 +2,18 @@ import * as THREE from 'three/webgpu'; @@ -13943,7 +13943,7 @@ index de90d890..cd271c05 100644 hip: 'mixamorigHips', diff --git a/examples-testing/examples/webgpu_backdrop.ts b/examples-testing/examples/webgpu_backdrop.ts -index af6dcc39..35eb38d4 100644 +index af6dcc399..35eb38d45 100644 --- a/examples-testing/examples/webgpu_backdrop.ts +++ b/examples-testing/examples/webgpu_backdrop.ts @@ -22,10 +22,10 @@ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; @@ -13981,7 +13981,7 @@ index af6dcc39..35eb38d4 100644 const id = portals.children.length; const rotation = THREE.MathUtils.degToRad(id * 45); diff --git a/examples-testing/examples/webgpu_backdrop_area.ts b/examples-testing/examples/webgpu_backdrop_area.ts -index 384311dd..8d39e8ac 100644 +index 384311dd4..8d39e8acc 100644 --- a/examples-testing/examples/webgpu_backdrop_area.ts +++ b/examples-testing/examples/webgpu_backdrop_area.ts @@ -20,8 +20,8 @@ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; @@ -14013,7 +14013,7 @@ index 384311dd..8d39e8ac 100644 }); } diff --git a/examples-testing/examples/webgpu_backdrop_water.ts b/examples-testing/examples/webgpu_backdrop_water.ts -index a8538cf9..53b97519 100644 +index a8538cf94..53b975193 100644 --- a/examples-testing/examples/webgpu_backdrop_water.ts +++ b/examples-testing/examples/webgpu_backdrop_water.ts @@ -24,11 +24,13 @@ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; @@ -14054,7 +14054,7 @@ index a8538cf9..53b97519 100644 floorPosition = new THREE.Vector3(0, 0.2, 0); diff --git a/examples-testing/examples/webgpu_camera.ts b/examples-testing/examples/webgpu_camera.ts -index bf4724ac..b964cc5f 100644 +index bf4724ac8..b964cc5fd 100644 --- a/examples-testing/examples/webgpu_camera.ts +++ b/examples-testing/examples/webgpu_camera.ts @@ -4,11 +4,11 @@ let SCREEN_WIDTH = window.innerWidth; @@ -14084,7 +14084,7 @@ index bf4724ac..b964cc5f 100644 case 79 /*O*/: activeCamera = cameraOrtho; diff --git a/examples-testing/examples/webgpu_camera_array.ts b/examples-testing/examples/webgpu_camera_array.ts -index a4d82a70..9511c886 100644 +index a4d82a709..9511c886a 100644 --- a/examples-testing/examples/webgpu_camera_array.ts +++ b/examples-testing/examples/webgpu_camera_array.ts @@ -1,7 +1,7 @@ @@ -14107,7 +14107,7 @@ index a4d82a70..9511c886 100644 subcamera.position.x = x / AMOUNT - 0.5; diff --git a/examples-testing/examples/webgpu_camera_logarithmicdepthbuffer.ts b/examples-testing/examples/webgpu_camera_logarithmicdepthbuffer.ts -index a0e748e1..81206987 100644 +index a0e748e1b..812069877 100644 --- a/examples-testing/examples/webgpu_camera_logarithmicdepthbuffer.ts +++ b/examples-testing/examples/webgpu_camera_logarithmicdepthbuffer.ts @@ -2,7 +2,7 @@ import * as THREE from 'three/webgpu'; @@ -14276,7 +14276,7 @@ index a0e748e1..81206987 100644 if (amount === 0) return; const dir = amount / Math.abs(amount); diff --git a/examples-testing/examples/webgpu_caustics.ts b/examples-testing/examples/webgpu_caustics.ts -index 0829fb55..b7692cbb 100644 +index 0829fb558..b7692cbbc 100644 --- a/examples-testing/examples/webgpu_caustics.ts +++ b/examples-testing/examples/webgpu_caustics.ts @@ -20,8 +20,8 @@ import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; @@ -14309,7 +14309,7 @@ index 0829fb55..b7692cbb 100644 gui.addColor(duck.material, 'color').name('material color'); gui.add({ model: 'duck' }, 'model', ['duck', 'glass']).onChange(model => { diff --git a/examples-testing/examples/webgpu_centroid_sampling.ts b/examples-testing/examples/webgpu_centroid_sampling.ts -index ca159d04..24b0ee88 100644 +index ca159d04c..24b0ee885 100644 --- a/examples-testing/examples/webgpu_centroid_sampling.ts +++ b/examples-testing/examples/webgpu_centroid_sampling.ts @@ -2,14 +2,22 @@ import * as THREE from 'three/webgpu'; @@ -14405,7 +14405,7 @@ index ca159d04..24b0ee88 100644 THREE.InterpolationSamplingMode.NORMAL, THREE.InterpolationSamplingMode.CENTROID, diff --git a/examples-testing/examples/webgpu_clearcoat.ts b/examples-testing/examples/webgpu_clearcoat.ts -index 02fbacb9..d3ee1763 100644 +index 02fbacb9d..d3ee1763c 100644 --- a/examples-testing/examples/webgpu_clearcoat.ts +++ b/examples-testing/examples/webgpu_clearcoat.ts @@ -7,10 +7,10 @@ import { HDRCubeTextureLoader } from 'three/addons/loaders/HDRCubeTextureLoader. @@ -14423,7 +14423,7 @@ index 02fbacb9..d3ee1763 100644 init(); diff --git a/examples-testing/examples/webgpu_clipping.ts b/examples-testing/examples/webgpu_clipping.ts -index b7e94da9..b7c85c2e 100644 +index b7e94da98..b7c85c2ee 100644 --- a/examples-testing/examples/webgpu_clipping.ts +++ b/examples-testing/examples/webgpu_clipping.ts @@ -4,7 +4,11 @@ import { Inspector } from 'three/addons/inspector/Inspector.js'; @@ -14458,7 +14458,7 @@ index b7e94da9..b7c85c2e 100644 object.position.y = 0.8; diff --git a/examples-testing/examples/webgpu_compile_async.ts b/examples-testing/examples/webgpu_compile_async.ts -index fbcf0a7b..b80719c4 100644 +index fbcf0a7b0..b80719c41 100644 --- a/examples-testing/examples/webgpu_compile_async.ts +++ b/examples-testing/examples/webgpu_compile_async.ts @@ -16,9 +16,9 @@ let MESH_COUNT = 256; @@ -14537,7 +14537,7 @@ index fbcf0a7b..b80719c4 100644 const startX = -(GRID_SIZE - 1) / 2; const startY = -(GRID_SIZE - 1) / 2; diff --git a/examples-testing/examples/webgpu_compute_audio.ts b/examples-testing/examples/webgpu_compute_audio.ts -index 229033d7..da8f94d5 100644 +index 229033d79..da8f94d50 100644 --- a/examples-testing/examples/webgpu_compute_audio.ts +++ b/examples-testing/examples/webgpu_compute_audio.ts @@ -3,15 +3,15 @@ import { Fn, uniform, instanceIndex, instancedArray, float, texture, screenUV, c @@ -14591,7 +14591,7 @@ index 229033d7..da8f94d5 100644 gui.add(pitch, 'value', 0.5, 2, 0.01).name('pitch'); gui.add(delayVolume, 'value', 0, 1, 0.01).name('delayVolume'); diff --git a/examples-testing/examples/webgpu_compute_birds.ts b/examples-testing/examples/webgpu_compute_birds.ts -index 665a5452..9931b08b 100644 +index 665a54528..9931b08b2 100644 --- a/examples-testing/examples/webgpu_compute_birds.ts +++ b/examples-testing/examples/webgpu_compute_birds.ts @@ -35,13 +35,24 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -14651,10 +14651,10 @@ index 665a5452..9931b08b 100644 pointer.x = (event.clientX / window.innerWidth) * 2.0 - 1.0; diff --git a/examples-testing/examples/webgpu_compute_cloth.ts b/examples-testing/examples/webgpu_compute_cloth.ts -index f4dd0864..b5950ad0 100644 +index c18af53f2..7d92981fd 100644 --- a/examples-testing/examples/webgpu_compute_cloth.ts +++ b/examples-testing/examples/webgpu_compute_cloth.ts -@@ -24,7 +24,7 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +@@ -23,7 +23,7 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; import WebGPU from 'three/addons/capabilities/WebGPU.js'; @@ -14663,7 +14663,7 @@ index f4dd0864..b5950ad0 100644 const clothWidth = 1; const clothHeight = 1; -@@ -32,18 +32,41 @@ const clothNumSegmentsX = 30; +@@ -31,18 +31,41 @@ const clothNumSegmentsX = 30; const clothNumSegmentsY = 30; const sphereRadius = 0.15; @@ -14715,7 +14715,7 @@ index f4dd0864..b5950ad0 100644 const timer = new THREE.Timer(); timer.connect(document); -@@ -99,7 +122,7 @@ async function init() { +@@ -98,7 +121,7 @@ async function init() { setupCloth(); @@ -14724,7 +14724,7 @@ index f4dd0864..b5950ad0 100644 gui.add(stiffnessUniform, 'value', 0.1, 0.5, 0.01).name('stiffness'); gui.add(params, 'wireframe'); gui.add(params, 'sphere'); -@@ -124,7 +147,7 @@ async function init() { +@@ -123,7 +146,7 @@ async function init() { function setupVerletGeometry() { // this function sets up the geometry of the verlet system, a grid of vertices connected by springs @@ -14733,7 +14733,7 @@ index f4dd0864..b5950ad0 100644 const id = verletVertices.length; const vertex = { id, -@@ -136,7 +159,7 @@ function setupVerletGeometry() { +@@ -135,7 +158,7 @@ function setupVerletGeometry() { return vertex; }; @@ -14742,7 +14742,7 @@ index f4dd0864..b5950ad0 100644 const id = verletSprings.length; const spring = { id, -@@ -350,7 +373,7 @@ function setupWireframe() { +@@ -337,7 +360,7 @@ function setupWireframe() { const springWireframeMaterial = new THREE.LineBasicNodeMaterial(); springWireframeMaterial.positionNode = Fn(() => { const vertexIds = springVertexIdBuffer.element(instanceIndex); @@ -14751,7 +14751,7 @@ index f4dd0864..b5950ad0 100644 return vertexPositionBuffer.element(vertexId); })(); -@@ -383,7 +406,7 @@ function setupClothMesh() { +@@ -370,7 +393,7 @@ function setupClothMesh() { const verletVertexIdArray = new Uint32Array(vertexCount * 4); const indices = []; @@ -14760,7 +14760,7 @@ index f4dd0864..b5950ad0 100644 return y * clothNumSegmentsX + x; }; -@@ -420,7 +443,7 @@ function setupClothMesh() { +@@ -407,7 +430,7 @@ function setupClothMesh() { clothMaterial.positionNode = Fn(({ material }) => { // gather the position of the 4 verlet vertices and calculate the center position and normal from that @@ -14769,7 +14769,7 @@ index f4dd0864..b5950ad0 100644 const v0 = vertexPositionBuffer.element(vertexIds.x).toVar(); const v1 = vertexPositionBuffer.element(vertexIds.y).toVar(); const v2 = vertexPositionBuffer.element(vertexIds.z).toVar(); -@@ -437,7 +460,7 @@ function setupClothMesh() { +@@ -424,7 +447,7 @@ function setupClothMesh() { const normal = cross(tangent, bitangent); // send the normalView from the vertex shader to the fragment shader @@ -14779,7 +14779,7 @@ index f4dd0864..b5950ad0 100644 return v0.add(v1).add(v2).add(v3).mul(0.25); })(); diff --git a/examples-testing/examples/webgpu_compute_geometry.ts b/examples-testing/examples/webgpu_compute_geometry.ts -index 9299e215..509cf684 100644 +index 9299e215b..509cf6845 100644 --- a/examples-testing/examples/webgpu_compute_geometry.ts +++ b/examples-testing/examples/webgpu_compute_geometry.ts @@ -18,8 +18,8 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -14839,7 +14839,7 @@ index 9299e215..509cf684 100644 } else { pointerPosition.value.w = 0; // disable diff --git a/examples-testing/examples/webgpu_compute_particles.ts b/examples-testing/examples/webgpu_compute_particles.ts -index 71fbd662..c2fc51fe 100644 +index 71fbd6622..c2fc51fe1 100644 --- a/examples-testing/examples/webgpu_compute_particles.ts +++ b/examples-testing/examples/webgpu_compute_particles.ts @@ -14,11 +14,11 @@ const size = uniform(0.12); @@ -14877,7 +14877,7 @@ index 71fbd662..c2fc51fe 100644 gui.add(gravity, 'value', -0.0098, 0, 0.0001).name('gravity'); gui.add(bounce, 'value', 0.1, 1, 0.01).name('bounce'); diff --git a/examples-testing/examples/webgpu_compute_particles_rain.ts b/examples-testing/examples/webgpu_compute_particles_rain.ts -index eaa4e957..43cca9dd 100644 +index eaa4e957e..43cca9ddd 100644 --- a/examples-testing/examples/webgpu_compute_particles_rain.ts +++ b/examples-testing/examples/webgpu_compute_particles_rain.ts @@ -25,14 +25,17 @@ import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js' @@ -14925,7 +14925,7 @@ index eaa4e957..43cca9dd 100644 // use lerp to smooth the movement collisionBoxPosUI = new THREE.Vector3().copy(collisionBox.position); diff --git a/examples-testing/examples/webgpu_compute_particles_snow.ts b/examples-testing/examples/webgpu_compute_particles_snow.ts -index e00fe068..476468a0 100644 +index e00fe068f..476468a0a 100644 --- a/examples-testing/examples/webgpu_compute_particles_snow.ts +++ b/examples-testing/examples/webgpu_compute_particles_snow.ts @@ -26,12 +26,14 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -14976,7 +14976,7 @@ index e00fe068..476468a0 100644 totalPass = totalPass.mul(vignette); totalPass = totalPass.add(teapotTreePass.mul(10).add(teapotTreePassBlurred).toInspector('Teapot Blur')); diff --git a/examples-testing/examples/webgpu_compute_points.ts b/examples-testing/examples/webgpu_compute_points.ts -index eab1d9f6..65a487a7 100644 +index eab1d9f61..65a487a75 100644 --- a/examples-testing/examples/webgpu_compute_points.ts +++ b/examples-testing/examples/webgpu_compute_points.ts @@ -4,8 +4,8 @@ import { Inspector } from 'three/addons/inspector/Inspector.js'; @@ -15009,7 +15009,7 @@ index eab1d9f6..65a487a7 100644 const y = event.clientY; diff --git a/examples-testing/examples/webgpu_compute_sort_bitonic.ts b/examples-testing/examples/webgpu_compute_sort_bitonic.ts -index 9a37a9ec..2726efaa 100644 +index 9a37a9ece..2726efaa7 100644 --- a/examples-testing/examples/webgpu_compute_sort_bitonic.ts +++ b/examples-testing/examples/webgpu_compute_sort_bitonic.ts @@ -18,8 +18,8 @@ const StepType = { @@ -15116,7 +15116,7 @@ index 9a37a9ec..2726efaa 100644 If(currentElementsStorage.element(idxAfter).lessThan(currentElementsStorage.element(idxBefore)), () => { // Apply the swapped values to temporary storage. diff --git a/examples-testing/examples/webgpu_compute_texture.ts b/examples-testing/examples/webgpu_compute_texture.ts -index 43d162ab..259faad4 100644 +index 43d162ab5..259faad4c 100644 --- a/examples-testing/examples/webgpu_compute_texture.ts +++ b/examples-testing/examples/webgpu_compute_texture.ts @@ -3,7 +3,7 @@ import { texture, textureStore, Fn, instanceIndex, float, uvec2, vec4 } from 'th @@ -15138,7 +15138,7 @@ index 43d162ab..259faad4 100644 const posY = instanceIndex.div(width); const indexUV = uvec2(posX, posY); diff --git a/examples-testing/examples/webgpu_compute_texture_3d.ts b/examples-testing/examples/webgpu_compute_texture_3d.ts -index ccdfba0b..4e0661c5 100644 +index ccdfba0be..4e0661c5d 100644 --- a/examples-testing/examples/webgpu_compute_texture_3d.ts +++ b/examples-testing/examples/webgpu_compute_texture_3d.ts @@ -23,9 +23,9 @@ import { Inspector } from 'three/addons/inspector/Inspector.js'; @@ -15203,7 +15203,7 @@ index ccdfba0b..4e0661c5 100644 gui.add(opacity, 'value', 0, 1, 0.01).name('opacity'); gui.add(range, 'value', 0, 1, 0.01).name('range'); diff --git a/examples-testing/examples/webgpu_compute_texture_pingpong.ts b/examples-testing/examples/webgpu_compute_texture_pingpong.ts -index 5c411fa6..9bc80d89 100644 +index 5c411fa69..9bc80d895 100644 --- a/examples-testing/examples/webgpu_compute_texture_pingpong.ts +++ b/examples-testing/examples/webgpu_compute_texture_pingpong.ts @@ -16,10 +16,10 @@ import { @@ -15249,7 +15249,7 @@ index 5c411fa6..9bc80d89 100644 const posY = instanceIndex.div(width); const indexUV = ivec2(int(posX), int(posY)); diff --git a/examples-testing/examples/webgpu_cubemap_adjustments.ts b/examples-testing/examples/webgpu_cubemap_adjustments.ts -index 2e886d82..1161f02f 100644 +index 2e886d824..1161f02f6 100644 --- a/examples-testing/examples/webgpu_cubemap_adjustments.ts +++ b/examples-testing/examples/webgpu_cubemap_adjustments.ts @@ -20,7 +20,7 @@ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; @@ -15280,7 +15280,7 @@ index 2e886d82..1161f02f 100644 gui.add({ blurBackground: blurNode.value }, 'blurBackground', 0, 1, 0.01).onChange(value => { blurNode.value = value; diff --git a/examples-testing/examples/webgpu_cubemap_dynamic.ts b/examples-testing/examples/webgpu_cubemap_dynamic.ts -index 244b8593..e66a01f9 100644 +index 244b85935..e66a01f9b 100644 --- a/examples-testing/examples/webgpu_cubemap_dynamic.ts +++ b/examples-testing/examples/webgpu_cubemap_dynamic.ts @@ -4,12 +4,12 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -15319,7 +15319,7 @@ index 244b8593..e66a01f9 100644 cube.position.x = Math.cos(time) * 30; diff --git a/examples-testing/examples/webgpu_cubemap_mix.ts b/examples-testing/examples/webgpu_cubemap_mix.ts -index 6fbdb55a..fd5ec570 100644 +index 6fbdb55ae..fd5ec5705 100644 --- a/examples-testing/examples/webgpu_cubemap_mix.ts +++ b/examples-testing/examples/webgpu_cubemap_mix.ts @@ -8,7 +8,7 @@ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; @@ -15332,7 +15332,7 @@ index 6fbdb55a..fd5ec570 100644 init(); diff --git a/examples-testing/examples/webgpu_custom_fog.ts b/examples-testing/examples/webgpu_custom_fog.ts -index bcfcb0fd..be07fc03 100644 +index bcfcb0fd3..be07fc038 100644 --- a/examples-testing/examples/webgpu_custom_fog.ts +++ b/examples-testing/examples/webgpu_custom_fog.ts @@ -4,8 +4,8 @@ import { color, fog, float, positionWorld, triNoise3D, positionView, normalWorld @@ -15347,7 +15347,7 @@ index bcfcb0fd..be07fc03 100644 init(); diff --git a/examples-testing/examples/webgpu_custom_fog_background.ts b/examples-testing/examples/webgpu_custom_fog_background.ts -index a9b61934..e482a1e2 100644 +index a9b619345..e482a1e2e 100644 --- a/examples-testing/examples/webgpu_custom_fog_background.ts +++ b/examples-testing/examples/webgpu_custom_fog_background.ts @@ -6,8 +6,8 @@ import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; @@ -15362,7 +15362,7 @@ index a9b61934..e482a1e2 100644 init(); diff --git a/examples-testing/examples/webgpu_custom_fog_scattering.ts b/examples-testing/examples/webgpu_custom_fog_scattering.ts -index 7f14133f..7be64774 100644 +index 7f14133fd..7be64774d 100644 --- a/examples-testing/examples/webgpu_custom_fog_scattering.ts +++ b/examples-testing/examples/webgpu_custom_fog_scattering.ts @@ -5,7 +5,11 @@ import { gaussianBlur } from 'three/addons/tsl/display/GaussianBlurNode.js'; @@ -15394,7 +15394,7 @@ index 7f14133f..7be64774 100644 gui.add(scattering, 'value', 0, 5).name('scattering factor'); gui.add(params, 'scatteringEnabled') diff --git a/examples-testing/examples/webgpu_display_stereo.ts b/examples-testing/examples/webgpu_display_stereo.ts -index 7f1ade72..c948dd93 100644 +index 7f1ade726..c948dd939 100644 --- a/examples-testing/examples/webgpu_display_stereo.ts +++ b/examples-testing/examples/webgpu_display_stereo.ts @@ -1,22 +1,36 @@ @@ -15479,7 +15479,7 @@ index 7f1ade72..c948dd93 100644 position.y = matrix.elements[13]; position.z = matrix.elements[14]; diff --git a/examples-testing/examples/webgpu_equirectangular.ts b/examples-testing/examples/webgpu_equirectangular.ts -index 9e159e46..128e18b7 100644 +index 9e159e465..128e18b7b 100644 --- a/examples-testing/examples/webgpu_equirectangular.ts +++ b/examples-testing/examples/webgpu_equirectangular.ts @@ -4,8 +4,8 @@ import { texture, equirectUV } from 'three/tsl'; @@ -15503,7 +15503,7 @@ index 9e159e46..128e18b7 100644 gui.add(scene, 'backgroundIntensity', 0, 1).name('background intensity'); diff --git a/examples-testing/examples/webgpu_fog_height.ts b/examples-testing/examples/webgpu_fog_height.ts -index 65bda9cd..5d3957e5 100644 +index 65bda9cd8..5d3957e54 100644 --- a/examples-testing/examples/webgpu_fog_height.ts +++ b/examples-testing/examples/webgpu_fog_height.ts @@ -4,8 +4,8 @@ import { exponentialHeightFogFactor, uniform, fog, color } from 'three/tsl'; @@ -15527,7 +15527,7 @@ index 65bda9cd..5d3957e5 100644 gui.add(density, 'value', 0.001, 0.1).step(0.0001).name('Density'); gui.add(height, 'value', -5, 5).name('Height'); diff --git a/examples-testing/examples/webgpu_hdr.ts b/examples-testing/examples/webgpu_hdr.ts -index b96ba5f4..676326fe 100644 +index b96ba5f42..676326fed 100644 --- a/examples-testing/examples/webgpu_hdr.ts +++ b/examples-testing/examples/webgpu_hdr.ts @@ -17,7 +17,7 @@ const hdrMediaQuery = window.matchMedia('(dynamic-range: high)'); @@ -15558,7 +15558,7 @@ index b96ba5f4..676326fe 100644 const colorFolder = gui.addFolder('HDR'); colorFolder.add(params.intensity, 'value', 0, 10, 0.1).name('Intensity'); diff --git a/examples-testing/examples/webgpu_instance_mesh.ts b/examples-testing/examples/webgpu_instance_mesh.ts -index 0abd2f26..30705fb0 100644 +index 0abd2f26c..30705fb0c 100644 --- a/examples-testing/examples/webgpu_instance_mesh.ts +++ b/examples-testing/examples/webgpu_instance_mesh.ts @@ -3,9 +3,9 @@ import { mix, range, normalWorld, oscSine, time } from 'three/tsl'; @@ -15583,7 +15583,7 @@ index 0abd2f26..30705fb0 100644 }); diff --git a/examples-testing/examples/webgpu_instance_path.ts b/examples-testing/examples/webgpu_instance_path.ts -index a5bce322..8b2fa4ad 100644 +index a5bce3225..8b2fa4ad7 100644 --- a/examples-testing/examples/webgpu_instance_path.ts +++ b/examples-testing/examples/webgpu_instance_path.ts @@ -19,7 +19,7 @@ import { @@ -15631,7 +15631,7 @@ index a5bce322..8b2fa4ad 100644 const localTime = instanceTime.add(time); const modTime = mod(time.mul(0.4), 1); diff --git a/examples-testing/examples/webgpu_instance_points.ts b/examples-testing/examples/webgpu_instance_points.ts -index 40912937..d2ce322f 100644 +index 409129373..d2ce322f4 100644 --- a/examples-testing/examples/webgpu_instance_points.ts +++ b/examples-testing/examples/webgpu_instance_points.ts @@ -19,16 +19,25 @@ import { Inspector } from 'three/addons/inspector/Inspector.js'; @@ -15689,7 +15689,7 @@ index 40912937..d2ce322f 100644 gui.add(material, 'alphaToCoverage'); diff --git a/examples-testing/examples/webgpu_instancing_morph.ts b/examples-testing/examples/webgpu_instancing_morph.ts -index f8f5c5fe..4da82327 100644 +index f8f5c5fe5..4da82327d 100644 --- a/examples-testing/examples/webgpu_instancing_morph.ts +++ b/examples-testing/examples/webgpu_instancing_morph.ts @@ -3,7 +3,12 @@ import * as THREE from 'three/webgpu'; @@ -15725,7 +15725,7 @@ index f8f5c5fe..4da82327 100644 renderer.render(scene, camera); diff --git a/examples-testing/examples/webgpu_layers.ts b/examples-testing/examples/webgpu_layers.ts -index c1db7d60..319d54e8 100644 +index c1db7d60c..319d54e83 100644 --- a/examples-testing/examples/webgpu_layers.ts +++ b/examples-testing/examples/webgpu_layers.ts @@ -4,7 +4,7 @@ import { Inspector } from 'three/addons/inspector/Inspector.js'; @@ -15772,7 +15772,7 @@ index c1db7d60..319d54e8 100644 const rotatedPosition = rotate(positionLocal, instanceRotation.mul(modTime.mul(20))); diff --git a/examples-testing/examples/webgpu_lensflares.ts b/examples-testing/examples/webgpu_lensflares.ts -index ab4e4967..d5662716 100644 +index ab4e49677..d5662716d 100644 --- a/examples-testing/examples/webgpu_lensflares.ts +++ b/examples-testing/examples/webgpu_lensflares.ts @@ -4,10 +4,10 @@ import { FlyControls } from 'three/addons/controls/FlyControls.js'; @@ -15799,7 +15799,7 @@ index ab4e4967..d5662716 100644 light.color.setHSL(h, s, l); light.position.set(x, y, z); diff --git a/examples-testing/examples/webgpu_lightprobe.ts b/examples-testing/examples/webgpu_lightprobe.ts -index 3e90b6f6..34208354 100644 +index 3e90b6f66..342083547 100644 --- a/examples-testing/examples/webgpu_lightprobe.ts +++ b/examples-testing/examples/webgpu_lightprobe.ts @@ -1,6 +1,7 @@ @@ -15849,7 +15849,7 @@ index 3e90b6f6..34208354 100644 gui.add(API, 'lightProbeIntensity', 0, 1, 0.02) .name('light probe') diff --git a/examples-testing/examples/webgpu_lightprobe_cubecamera.ts b/examples-testing/examples/webgpu_lightprobe_cubecamera.ts -index 60fe6ccc..c29cde36 100644 +index 60fe6ccc9..c29cde36b 100644 --- a/examples-testing/examples/webgpu_lightprobe_cubecamera.ts +++ b/examples-testing/examples/webgpu_lightprobe_cubecamera.ts @@ -6,9 +6,9 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -15874,7 +15874,7 @@ index 60fe6ccc..c29cde36 100644 prefix + 'px' + postfix, prefix + 'nx' + postfix, diff --git a/examples-testing/examples/webgpu_lights_dynamic.ts b/examples-testing/examples/webgpu_lights_dynamic.ts -index b08c5902..123a9c1a 100644 +index b08c5902f..123a9c1a9 100644 --- a/examples-testing/examples/webgpu_lights_dynamic.ts +++ b/examples-testing/examples/webgpu_lights_dynamic.ts @@ -5,10 +5,15 @@ import { DynamicLighting } from 'three/addons/lighting/DynamicLighting.js'; @@ -15941,7 +15941,7 @@ index b08c5902..123a9c1a 100644 light.dispose(); } diff --git a/examples-testing/examples/webgpu_lights_ies_spotlight.ts b/examples-testing/examples/webgpu_lights_ies_spotlight.ts -index edfc51ef..d3f6e377 100644 +index edfc51efa..d3f6e3779 100644 --- a/examples-testing/examples/webgpu_lights_ies_spotlight.ts +++ b/examples-testing/examples/webgpu_lights_ies_spotlight.ts @@ -1,13 +1,13 @@ @@ -15989,7 +15989,7 @@ index edfc51ef..d3f6e377 100644 for (let i = 0; i < lights.length; i++) { diff --git a/examples-testing/examples/webgpu_lights_phong.ts b/examples-testing/examples/webgpu_lights_phong.ts -index c2cdf439..64116a21 100644 +index c2cdf4393..64116a21b 100644 --- a/examples-testing/examples/webgpu_lights_phong.ts +++ b/examples-testing/examples/webgpu_lights_phong.ts @@ -6,7 +6,14 @@ import { Inspector } from 'three/addons/inspector/Inspector.js'; @@ -16018,7 +16018,7 @@ index c2cdf439..64116a21 100644 material.colorNode = color(hexColor); material.lights = false; diff --git a/examples-testing/examples/webgpu_lights_physical.ts b/examples-testing/examples/webgpu_lights_physical.ts -index 43d7ce72..616ed8e6 100644 +index 43d7ce723..616ed8e6b 100644 --- a/examples-testing/examples/webgpu_lights_physical.ts +++ b/examples-testing/examples/webgpu_lights_physical.ts @@ -4,8 +4,13 @@ import { Inspector } from 'three/addons/inspector/Inspector.js'; @@ -16078,7 +16078,7 @@ index 43d7ce72..616ed8e6 100644 gui.add(params, 'shadows'); } diff --git a/examples-testing/examples/webgpu_lights_pointlights.ts b/examples-testing/examples/webgpu_lights_pointlights.ts -index e00d75b2..c0d16ef1 100644 +index e00d75b21..c0d16ef13 100644 --- a/examples-testing/examples/webgpu_lights_pointlights.ts +++ b/examples-testing/examples/webgpu_lights_pointlights.ts @@ -16,9 +16,13 @@ import { @@ -16130,7 +16130,7 @@ index e00d75b2..c0d16ef1 100644 const v0 = new THREE.Vector3(); diff --git a/examples-testing/examples/webgpu_lights_rectarealight.ts b/examples-testing/examples/webgpu_lights_rectarealight.ts -index 75e2cc44..66cbd8c5 100644 +index 75e2cc442..66cbd8c58 100644 --- a/examples-testing/examples/webgpu_lights_rectarealight.ts +++ b/examples-testing/examples/webgpu_lights_rectarealight.ts @@ -8,9 +8,9 @@ import { RectAreaLightTexturesLib } from 'three/addons/lights/RectAreaLightTextu @@ -16147,7 +16147,7 @@ index 75e2cc44..66cbd8c5 100644 init(); diff --git a/examples-testing/examples/webgpu_lights_selective.ts b/examples-testing/examples/webgpu_lights_selective.ts -index d0004834..54f94948 100644 +index d00048347..54f94948b 100644 --- a/examples-testing/examples/webgpu_lights_selective.ts +++ b/examples-testing/examples/webgpu_lights_selective.ts @@ -6,7 +6,14 @@ import { Inspector } from 'three/addons/inspector/Inspector.js'; @@ -16185,7 +16185,7 @@ index d0004834..54f94948 100644 gui.add(centerObject.material, 'roughness', 0, 1, 0.01); gui.add(centerObject.material, 'metalness', 0, 1, 0.01); diff --git a/examples-testing/examples/webgpu_lights_spotlight.ts b/examples-testing/examples/webgpu_lights_spotlight.ts -index 85e32080..4b07c276 100644 +index 85e320800..4b07c276f 100644 --- a/examples-testing/examples/webgpu_lights_spotlight.ts +++ b/examples-testing/examples/webgpu_lights_spotlight.ts @@ -5,9 +5,9 @@ import { Inspector } from 'three/addons/inspector/Inspector.js'; @@ -16239,7 +16239,7 @@ index 85e32080..4b07c276 100644 renderer.render(scene, camera); } diff --git a/examples-testing/examples/webgpu_lights_tiled.ts b/examples-testing/examples/webgpu_lights_tiled.ts -index e575ee34..d9cc9fb5 100644 +index e575ee344..d9cc9fb54 100644 --- a/examples-testing/examples/webgpu_lights_tiled.ts +++ b/examples-testing/examples/webgpu_lights_tiled.ts @@ -3,6 +3,7 @@ import { texture, uv, pass, normalMap, uniform } from 'three/tsl'; @@ -16301,7 +16301,7 @@ index e575ee34..d9cc9fb5 100644 renderPipeline.outputNode = compose.add(debugBlockIndexes.mul(tileInfluence)); diff --git a/examples-testing/examples/webgpu_lines_fat.ts b/examples-testing/examples/webgpu_lines_fat.ts -index 96417888..2ec8e8b1 100644 +index 96417888c..2ec8e8b1f 100644 --- a/examples-testing/examples/webgpu_lines_fat.ts +++ b/examples-testing/examples/webgpu_lines_fat.ts @@ -9,15 +9,23 @@ import { Line2 } from 'three/addons/lines/webgpu/Line2.js'; @@ -16336,7 +16336,7 @@ index 96417888..2ec8e8b1 100644 init(); diff --git a/examples-testing/examples/webgpu_lines_fat_raycasting.ts b/examples-testing/examples/webgpu_lines_fat_raycasting.ts -index 4280b580..2b1b6daa 100644 +index 4280b580c..2b1b6daaa 100644 --- a/examples-testing/examples/webgpu_lines_fat_raycasting.ts +++ b/examples-testing/examples/webgpu_lines_fat_raycasting.ts @@ -1,6 +1,7 @@ @@ -16445,7 +16445,7 @@ index 4280b580..2b1b6daa 100644 gui.add(params, 'translation', 0, 10).onChange(function (val) { diff --git a/examples-testing/examples/webgpu_lines_fat_wireframe.ts b/examples-testing/examples/webgpu_lines_fat_wireframe.ts -index 9ab5c44c..dee04a5a 100644 +index 9ab5c44c0..dee04a5ae 100644 --- a/examples-testing/examples/webgpu_lines_fat_wireframe.ts +++ b/examples-testing/examples/webgpu_lines_fat_wireframe.ts @@ -2,19 +2,26 @@ import * as THREE from 'three/webgpu'; @@ -16500,7 +16500,7 @@ index 9ab5c44c..dee04a5a 100644 const param = { 'line type': 0, diff --git a/examples-testing/examples/webgpu_loader_gltf.ts b/examples-testing/examples/webgpu_loader_gltf.ts -index 6380bd90..355b0534 100644 +index 6380bd903..355b05345 100644 --- a/examples-testing/examples/webgpu_loader_gltf.ts +++ b/examples-testing/examples/webgpu_loader_gltf.ts @@ -7,8 +7,16 @@ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; @@ -16588,7 +16588,7 @@ index 6380bd90..355b0534 100644 box.setFromObject(selection); diff --git a/examples-testing/examples/webgpu_loader_gltf_anisotropy.ts b/examples-testing/examples/webgpu_loader_gltf_anisotropy.ts -index a3aa95e1..144199f2 100644 +index a3aa95e15..144199f29 100644 --- a/examples-testing/examples/webgpu_loader_gltf_anisotropy.ts +++ b/examples-testing/examples/webgpu_loader_gltf_anisotropy.ts @@ -4,7 +4,7 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -16601,7 +16601,7 @@ index a3aa95e1..144199f2 100644 init(); diff --git a/examples-testing/examples/webgpu_loader_gltf_compressed.ts b/examples-testing/examples/webgpu_loader_gltf_compressed.ts -index 31d5d0d1..8a2e68da 100644 +index 31d5d0d1a..8a2e68da5 100644 --- a/examples-testing/examples/webgpu_loader_gltf_compressed.ts +++ b/examples-testing/examples/webgpu_loader_gltf_compressed.ts @@ -6,7 +6,7 @@ import { MeshoptDecoder } from 'three/addons/libs/meshopt_decoder.module.js'; @@ -16614,7 +16614,7 @@ index 31d5d0d1..8a2e68da 100644 init(); diff --git a/examples-testing/examples/webgpu_loader_gltf_dispersion.ts b/examples-testing/examples/webgpu_loader_gltf_dispersion.ts -index a2e815dd..25b08c75 100644 +index a2e815ddb..25b08c750 100644 --- a/examples-testing/examples/webgpu_loader_gltf_dispersion.ts +++ b/examples-testing/examples/webgpu_loader_gltf_dispersion.ts @@ -4,7 +4,7 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -16627,7 +16627,7 @@ index a2e815dd..25b08c75 100644 init(); diff --git a/examples-testing/examples/webgpu_loader_gltf_iridescence.ts b/examples-testing/examples/webgpu_loader_gltf_iridescence.ts -index 8007d5e6..dc83ae61 100644 +index 8007d5e65..dc83ae61c 100644 --- a/examples-testing/examples/webgpu_loader_gltf_iridescence.ts +++ b/examples-testing/examples/webgpu_loader_gltf_iridescence.ts @@ -4,7 +4,7 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -16640,7 +16640,7 @@ index 8007d5e6..dc83ae61 100644 init().catch(function (err) { console.error(err); diff --git a/examples-testing/examples/webgpu_loader_gltf_sheen.ts b/examples-testing/examples/webgpu_loader_gltf_sheen.ts -index 442edc7f..46d19c02 100644 +index 442edc7fb..46d19c02b 100644 --- a/examples-testing/examples/webgpu_loader_gltf_sheen.ts +++ b/examples-testing/examples/webgpu_loader_gltf_sheen.ts @@ -6,7 +6,7 @@ import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; @@ -16668,7 +16668,7 @@ index 442edc7f..46d19c02 100644 gui.add(object.material, 'sheen', 0, 1); }); diff --git a/examples-testing/examples/webgpu_loader_gltf_transmission.ts b/examples-testing/examples/webgpu_loader_gltf_transmission.ts -index 45b86e77..2f70dde2 100644 +index 45b86e772..2f70dde29 100644 --- a/examples-testing/examples/webgpu_loader_gltf_transmission.ts +++ b/examples-testing/examples/webgpu_loader_gltf_transmission.ts @@ -6,7 +6,12 @@ import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; @@ -16686,7 +16686,7 @@ index 45b86e77..2f70dde2 100644 init(); diff --git a/examples-testing/examples/webgpu_loader_materialx.ts b/examples-testing/examples/webgpu_loader_materialx.ts -index bb84eb69..55416bc9 100644 +index bb84eb693..55416bc98 100644 --- a/examples-testing/examples/webgpu_loader_materialx.ts +++ b/examples-testing/examples/webgpu_loader_materialx.ts @@ -61,9 +61,9 @@ const localSamples = [ @@ -16797,7 +16797,7 @@ index bb84eb69..55416bc9 100644 } }); diff --git a/examples-testing/examples/webgpu_loader_texture_ktx2.ts b/examples-testing/examples/webgpu_loader_texture_ktx2.ts -index 1daf0f03..ee2cb370 100644 +index 1daf0f032..ee2cb370b 100644 --- a/examples-testing/examples/webgpu_loader_texture_ktx2.ts +++ b/examples-testing/examples/webgpu_loader_texture_ktx2.ts @@ -1,12 +1,18 @@ @@ -16851,7 +16851,7 @@ index 1daf0f03..ee2cb370 100644 for (let i = 0; i < uv.count; i++) { diff --git a/examples-testing/examples/webgpu_materials_alphahash.ts b/examples-testing/examples/webgpu_materials_alphahash.ts -index 047bbe58..5a01713a 100644 +index 047bbe582..5a01713a4 100644 --- a/examples-testing/examples/webgpu_materials_alphahash.ts +++ b/examples-testing/examples/webgpu_materials_alphahash.ts @@ -7,7 +7,13 @@ import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; @@ -16879,7 +16879,7 @@ index 047bbe58..5a01713a 100644 gui.add(params, 'alpha', 0, 1).onChange(onMaterialUpdate); gui.add(params, 'alphaHash').onChange(onMaterialUpdate); diff --git a/examples-testing/examples/webgpu_materials_arrays.ts b/examples-testing/examples/webgpu_materials_arrays.ts -index e6d15853..154e3803 100644 +index e6d15853e..154e3803c 100644 --- a/examples-testing/examples/webgpu_materials_arrays.ts +++ b/examples-testing/examples/webgpu_materials_arrays.ts @@ -4,9 +4,9 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -16905,7 +16905,7 @@ index e6d15853..154e3803 100644 gui.add(api, 'webgpu').onChange(() => { init(!api.webgpu); diff --git a/examples-testing/examples/webgpu_materials_basic.ts b/examples-testing/examples/webgpu_materials_basic.ts -index 6aca76c9..fe08f13b 100644 +index 6aca76c97..fe08f13b4 100644 --- a/examples-testing/examples/webgpu_materials_basic.ts +++ b/examples-testing/examples/webgpu_materials_basic.ts @@ -2,9 +2,9 @@ import * as THREE from 'three/webgpu'; @@ -16954,7 +16954,7 @@ index 6aca76c9..fe08f13b 100644 mouseY = (event.clientY - windowHalfY) / 100; } diff --git a/examples-testing/examples/webgpu_materials_cubemap_mipmaps.ts b/examples-testing/examples/webgpu_materials_cubemap_mipmaps.ts -index 6c66f9d3..ccc9cd5e 100644 +index 6c66f9d35..ccc9cd5ec 100644 --- a/examples-testing/examples/webgpu_materials_cubemap_mipmaps.ts +++ b/examples-testing/examples/webgpu_materials_cubemap_mipmaps.ts @@ -2,9 +2,9 @@ import * as THREE from 'three/webgpu'; @@ -17005,7 +17005,7 @@ index 6c66f9d3..ccc9cd5e 100644 customizedCubeTexture.colorSpace = THREE.SRGBColorSpace; customizedCubeTexture.minFilter = THREE.LinearMipMapLinearFilter; diff --git a/examples-testing/examples/webgpu_materials_displacementmap.ts b/examples-testing/examples/webgpu_materials_displacementmap.ts -index 17a01f32..1ecf1a86 100644 +index 17a01f324..1ecf1a86f 100644 --- a/examples-testing/examples/webgpu_materials_displacementmap.ts +++ b/examples-testing/examples/webgpu_materials_displacementmap.ts @@ -5,7 +5,7 @@ import { Inspector } from 'three/addons/inspector/Inspector.js'; @@ -17048,7 +17048,7 @@ index 17a01f32..1ecf1a86 100644 mesh = new THREE.Mesh(geometry, material); diff --git a/examples-testing/examples/webgpu_materials_envmaps.ts b/examples-testing/examples/webgpu_materials_envmaps.ts -index 6862caee..97cae265 100644 +index 6862caeed..97cae265b 100644 --- a/examples-testing/examples/webgpu_materials_envmaps.ts +++ b/examples-testing/examples/webgpu_materials_envmaps.ts @@ -4,9 +4,18 @@ import { Inspector } from 'three/addons/inspector/Inspector.js'; @@ -17083,7 +17083,7 @@ index 6862caee..97cae265 100644 if (value === 'Cube') { scene.background = textureCube; diff --git a/examples-testing/examples/webgpu_materials_envmaps_bpcem.ts b/examples-testing/examples/webgpu_materials_envmaps_bpcem.ts -index adc28c98..8a0c918d 100644 +index adc28c989..8a0c918d7 100644 --- a/examples-testing/examples/webgpu_materials_envmaps_bpcem.ts +++ b/examples-testing/examples/webgpu_materials_envmaps_bpcem.ts @@ -16,11 +16,11 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -17111,7 +17111,7 @@ index adc28c98..8a0c918d 100644 'box projected': true, }; diff --git a/examples-testing/examples/webgpu_materials_lightmap.ts b/examples-testing/examples/webgpu_materials_lightmap.ts -index 081de95a..78b575f1 100644 +index 081de95ac..78b575f13 100644 --- a/examples-testing/examples/webgpu_materials_lightmap.ts +++ b/examples-testing/examples/webgpu_materials_lightmap.ts @@ -5,8 +5,8 @@ import { Inspector } from 'three/addons/inspector/Inspector.js'; @@ -17141,7 +17141,7 @@ index 081de95a..78b575f1 100644 } }); diff --git a/examples-testing/examples/webgpu_materials_matcap.ts b/examples-testing/examples/webgpu_materials_matcap.ts -index 0a841ae2..847b3df3 100644 +index 0a841ae27..847b3df3e 100644 --- a/examples-testing/examples/webgpu_materials_matcap.ts +++ b/examples-testing/examples/webgpu_materials_matcap.ts @@ -6,7 +6,10 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -17241,7 +17241,7 @@ index 0a841ae2..847b3df3 100644 }); } diff --git a/examples-testing/examples/webgpu_materials_sss.ts b/examples-testing/examples/webgpu_materials_sss.ts -index c76df99d..b94767b7 100644 +index c76df99dc..b94767b77 100644 --- a/examples-testing/examples/webgpu_materials_sss.ts +++ b/examples-testing/examples/webgpu_materials_sss.ts @@ -6,9 +6,9 @@ import { Inspector } from 'three/addons/inspector/Inspector.js'; @@ -17337,7 +17337,7 @@ index c76df99d..b94767b7 100644 } diff --git a/examples-testing/examples/webgpu_materials_texture_manualmipmap.ts b/examples-testing/examples/webgpu_materials_texture_manualmipmap.ts -index 09037272..2456b295 100644 +index 090372729..2456b2959 100644 --- a/examples-testing/examples/webgpu_materials_texture_manualmipmap.ts +++ b/examples-testing/examples/webgpu_materials_texture_manualmipmap.ts @@ -3,9 +3,9 @@ import * as THREE from 'three/webgpu'; @@ -17383,7 +17383,7 @@ index 09037272..2456b295 100644 mouseY = event.clientY - windowHalfY; } diff --git a/examples-testing/examples/webgpu_materials_toon.ts b/examples-testing/examples/webgpu_materials_toon.ts -index 78cb62ed..210b1d6e 100644 +index 78cb62ed6..210b1d6e9 100644 --- a/examples-testing/examples/webgpu_materials_toon.ts +++ b/examples-testing/examples/webgpu_materials_toon.ts @@ -6,18 +6,22 @@ import { Inspector } from 'three/addons/inspector/Inspector.js'; @@ -17423,7 +17423,7 @@ index 78cb62ed..210b1d6e 100644 font: font, diff --git a/examples-testing/examples/webgpu_materials_transmission.ts b/examples-testing/examples/webgpu_materials_transmission.ts -index 310d979d..eeb24a3e 100644 +index 310d979d5..eeb24a3eb 100644 --- a/examples-testing/examples/webgpu_materials_transmission.ts +++ b/examples-testing/examples/webgpu_materials_transmission.ts @@ -20,7 +20,7 @@ const params = { @@ -17454,7 +17454,7 @@ index 310d979d..eeb24a3e 100644 context.fillRect(0, 1, 2, 1); diff --git a/examples-testing/examples/webgpu_materials_video.ts b/examples-testing/examples/webgpu_materials_video.ts -index bc837653..3148b5e1 100644 +index bc837653f..3148b5e16 100644 --- a/examples-testing/examples/webgpu_materials_video.ts +++ b/examples-testing/examples/webgpu_materials_video.ts @@ -1,10 +1,13 @@ @@ -17567,7 +17567,7 @@ index bc837653..3148b5e1 100644 } diff --git a/examples-testing/examples/webgpu_materialx_noise.ts b/examples-testing/examples/webgpu_materialx_noise.ts -index 229c26a8..46060513 100644 +index 229c26a8e..460605135 100644 --- a/examples-testing/examples/webgpu_materialx_noise.ts +++ b/examples-testing/examples/webgpu_materialx_noise.ts @@ -13,12 +13,12 @@ import { Inspector } from 'three/addons/inspector/Inspector.js'; @@ -17588,7 +17588,7 @@ index 229c26a8..46060513 100644 init(); diff --git a/examples-testing/examples/webgpu_mesh_batch.ts b/examples-testing/examples/webgpu_mesh_batch.ts -index c20dc4b5..3c821864 100644 +index c20dc4b57..3c8218646 100644 --- a/examples-testing/examples/webgpu_mesh_batch.ts +++ b/examples-testing/examples/webgpu_mesh_batch.ts @@ -1,17 +1,18 @@ @@ -17684,7 +17684,7 @@ index c20dc4b5..3c821864 100644 list[i].z *= factor; } diff --git a/examples-testing/examples/webgpu_mirror.ts b/examples-testing/examples/webgpu_mirror.ts -index 56b22619..d0a270bf 100644 +index 56b226193..d0a270bfb 100644 --- a/examples-testing/examples/webgpu_mirror.ts +++ b/examples-testing/examples/webgpu_mirror.ts @@ -5,11 +5,11 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -17714,7 +17714,7 @@ index 56b22619..d0a270bf 100644 const groundNode = texture(decalDiffuse).a.mix(color(0xffffff), groundReflector); const verticalNode = color(0x0000ff).mul(0.1).add(verticalReflector); diff --git a/examples-testing/examples/webgpu_modifier_curve.ts b/examples-testing/examples/webgpu_modifier_curve.ts -index c465ddc5..9fa79745 100644 +index c465ddc57..9fa79745b 100644 --- a/examples-testing/examples/webgpu_modifier_curve.ts +++ b/examples-testing/examples/webgpu_modifier_curve.ts @@ -7,16 +7,16 @@ import { TextGeometry } from 'three/addons/geometries/TextGeometry.js'; @@ -17752,7 +17752,7 @@ index c465ddc5..9fa79745 100644 mouse.x = (event.clientX / window.innerWidth) * 2 - 1; mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; diff --git a/examples-testing/examples/webgpu_morphtargets.ts b/examples-testing/examples/webgpu_morphtargets.ts -index cde3ae83..c6e68d05 100644 +index cde3ae83b..c6e68d052 100644 --- a/examples-testing/examples/webgpu_morphtargets.ts +++ b/examples-testing/examples/webgpu_morphtargets.ts @@ -4,12 +4,16 @@ import { Inspector } from 'three/addons/inspector/Inspector.js'; @@ -17801,7 +17801,7 @@ index cde3ae83..c6e68d05 100644 } diff --git a/examples-testing/examples/webgpu_morphtargets_face.ts b/examples-testing/examples/webgpu_morphtargets_face.ts -index 91120dfb..53282897 100644 +index 91120dfb5..53282897e 100644 --- a/examples-testing/examples/webgpu_morphtargets_face.ts +++ b/examples-testing/examples/webgpu_morphtargets_face.ts @@ -12,7 +12,7 @@ import { Inspector } from 'three/addons/inspector/Inspector.js'; @@ -17831,7 +17831,7 @@ index 91120dfb..53282897 100644 } }); diff --git a/examples-testing/examples/webgpu_mrt.ts b/examples-testing/examples/webgpu_mrt.ts -index 2b41adc9..78ef5384 100644 +index 2b41adc9c..78ef5384d 100644 --- a/examples-testing/examples/webgpu_mrt.ts +++ b/examples-testing/examples/webgpu_mrt.ts @@ -20,8 +20,8 @@ import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; @@ -17846,7 +17846,7 @@ index 2b41adc9..78ef5384 100644 init(); diff --git a/examples-testing/examples/webgpu_mrt_mask.ts b/examples-testing/examples/webgpu_mrt_mask.ts -index 2bb80b84..a7f4b833 100644 +index 2bb80b84e..a7f4b8335 100644 --- a/examples-testing/examples/webgpu_mrt_mask.ts +++ b/examples-testing/examples/webgpu_mrt_mask.ts @@ -6,11 +6,11 @@ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; @@ -17886,7 +17886,7 @@ index 2bb80b84..a7f4b833 100644 const id = spheres.children.length; const rotation = THREE.MathUtils.degToRad(id * 90); diff --git a/examples-testing/examples/webgpu_multiple_canvas.ts b/examples-testing/examples/webgpu_multiple_canvas.ts -index db55fe16..caeb5d84 100644 +index db55fe165..caeb5d841 100644 --- a/examples-testing/examples/webgpu_multiple_canvas.ts +++ b/examples-testing/examples/webgpu_multiple_canvas.ts @@ -1,4 +1,4 @@ @@ -17917,7 +17917,7 @@ index db55fe16..caeb5d84 100644 for (let i = 0; i < 40; i++) { const scene = new THREE.Scene(); diff --git a/examples-testing/examples/webgpu_multiple_elements.ts b/examples-testing/examples/webgpu_multiple_elements.ts -index 65bd3e75..3d3f2fd9 100644 +index 65bd3e75d..3d3f2fd92 100644 --- a/examples-testing/examples/webgpu_multiple_elements.ts +++ b/examples-testing/examples/webgpu_multiple_elements.ts @@ -1,16 +1,16 @@ @@ -17951,7 +17951,7 @@ index 65bd3e75..3d3f2fd9 100644 for (let i = 0; i < 40; i++) { const scene = new THREE.Scene(); diff --git a/examples-testing/examples/webgpu_multiple_rendertargets.ts b/examples-testing/examples/webgpu_multiple_rendertargets.ts -index 2085d3c6..f4bc1ae6 100644 +index 2085d3c63..f4bc1ae6e 100644 --- a/examples-testing/examples/webgpu_multiple_rendertargets.ts +++ b/examples-testing/examples/webgpu_multiple_rendertargets.ts @@ -3,8 +3,8 @@ import { mix, vec2, step, texture, uv, screenUV, normalWorld, output, mrt } from @@ -17975,7 +17975,7 @@ index 2085d3c6..f4bc1ae6 100644 // render scene into target diff --git a/examples-testing/examples/webgpu_multiple_rendertargets_readback.ts b/examples-testing/examples/webgpu_multiple_rendertargets_readback.ts -index 2232ab18..cbc9492b 100644 +index 2232ab182..cbc9492b1 100644 --- a/examples-testing/examples/webgpu_multiple_rendertargets_readback.ts +++ b/examples-testing/examples/webgpu_multiple_rendertargets_readback.ts @@ -5,8 +5,15 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -18044,7 +18044,7 @@ index 2232ab18..cbc9492b 100644 pixelBufferTexture.image.data = pixelBuffer; pixelBufferTexture.needsUpdate = true; diff --git a/examples-testing/examples/webgpu_multisampled_renderbuffers.ts b/examples-testing/examples/webgpu_multisampled_renderbuffers.ts -index 05098c8d..9164cb14 100644 +index 05098c8d4..9164cb14b 100644 --- a/examples-testing/examples/webgpu_multisampled_renderbuffers.ts +++ b/examples-testing/examples/webgpu_multisampled_renderbuffers.ts @@ -3,12 +3,12 @@ import { texture } from 'three/tsl'; @@ -18091,7 +18091,7 @@ index 05098c8d..9164cb14 100644 mouse.y = e.offsetY / window.innerHeight; } diff --git a/examples-testing/examples/webgpu_occlusion.ts b/examples-testing/examples/webgpu_occlusion.ts -index 376dc987..d6dd4f61 100644 +index 376dc987d..d6dd4f613 100644 --- a/examples-testing/examples/webgpu_occlusion.ts +++ b/examples-testing/examples/webgpu_occlusion.ts @@ -3,10 +3,16 @@ import { uniform } from 'three/tsl'; @@ -18126,7 +18126,7 @@ index 376dc987..d6dd4f61 100644 this.uniformNode.value.copy(isOccluded ? this.occludedColor : this.normalColor); } diff --git a/examples-testing/examples/webgpu_ocean.ts b/examples-testing/examples/webgpu_ocean.ts -index a7939461..fe749d04 100644 +index a7939461e..fe749d04c 100644 --- a/examples-testing/examples/webgpu_ocean.ts +++ b/examples-testing/examples/webgpu_ocean.ts @@ -1,6 +1,6 @@ @@ -18178,7 +18178,7 @@ index a7939461..fe749d04 100644 const folderSky = gui.addFolder('Sky'); folderSky.add(parameters, 'elevation', 0, 90, 0.1).onChange(updateSun); diff --git a/examples-testing/examples/webgpu_parallax_uv.ts b/examples-testing/examples/webgpu_parallax_uv.ts -index b6282d89..43fa2744 100644 +index b6282d89f..43fa27447 100644 --- a/examples-testing/examples/webgpu_parallax_uv.ts +++ b/examples-testing/examples/webgpu_parallax_uv.ts @@ -6,9 +6,9 @@ import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; @@ -18203,7 +18203,7 @@ index b6282d89..43fa2744 100644 gui.add(parallaxScale, 'value', 0.2, 0.5).name('Parallax Scale'); gui.add(scaleUV, 'value', 1, 5).name('UV Scale'); diff --git a/examples-testing/examples/webgpu_particles.ts b/examples-testing/examples/webgpu_particles.ts -index 9e81f076..da9ee875 100644 +index 9e81f0761..da9ee8757 100644 --- a/examples-testing/examples/webgpu_particles.ts +++ b/examples-testing/examples/webgpu_particles.ts @@ -7,8 +7,8 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -18236,7 +18236,7 @@ index 9e81f076..da9ee875 100644 gui.add(speed, 'value', 0, 1, 0.01).name('speed'); } diff --git a/examples-testing/examples/webgpu_performance.ts b/examples-testing/examples/webgpu_performance.ts -index fb4ed155..8f0c113a 100644 +index fb4ed1556..8f0c113a5 100644 --- a/examples-testing/examples/webgpu_performance.ts +++ b/examples-testing/examples/webgpu_performance.ts @@ -8,17 +8,17 @@ import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; @@ -18272,7 +18272,7 @@ index fb4ed155..8f0c113a 100644 setStatic(model, options.static); }); diff --git a/examples-testing/examples/webgpu_performance_renderbundle.ts b/examples-testing/examples/webgpu_performance_renderbundle.ts -index 3b20847b..bda53998 100644 +index 3b20847b8..bda539989 100644 --- a/examples-testing/examples/webgpu_performance_renderbundle.ts +++ b/examples-testing/examples/webgpu_performance_renderbundle.ts @@ -1,13 +1,14 @@ @@ -18354,7 +18354,7 @@ index 3b20847b..bda53998 100644 function reload() { diff --git a/examples-testing/examples/webgpu_pmrem_cubemap.ts b/examples-testing/examples/webgpu_pmrem_cubemap.ts -index bd751ba3..9f58dc8b 100644 +index bd751ba33..9f58dc8b0 100644 --- a/examples-testing/examples/webgpu_pmrem_cubemap.ts +++ b/examples-testing/examples/webgpu_pmrem_cubemap.ts @@ -5,7 +5,7 @@ import { HDRCubeTextureLoader } from 'three/addons/loaders/HDRCubeTextureLoader. @@ -18367,7 +18367,7 @@ index bd751ba3..9f58dc8b 100644 init(); diff --git a/examples-testing/examples/webgpu_pmrem_equirectangular.ts b/examples-testing/examples/webgpu_pmrem_equirectangular.ts -index 76f8326d..cc85ba86 100644 +index 76f8326d3..cc85ba863 100644 --- a/examples-testing/examples/webgpu_pmrem_equirectangular.ts +++ b/examples-testing/examples/webgpu_pmrem_equirectangular.ts @@ -5,7 +5,7 @@ import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; @@ -18380,7 +18380,7 @@ index 76f8326d..cc85ba86 100644 init(); diff --git a/examples-testing/examples/webgpu_pmrem_scene.ts b/examples-testing/examples/webgpu_pmrem_scene.ts -index deab005f..36ca1c7d 100644 +index deab005fc..36ca1c7d1 100644 --- a/examples-testing/examples/webgpu_pmrem_scene.ts +++ b/examples-testing/examples/webgpu_pmrem_scene.ts @@ -5,7 +5,7 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -18402,7 +18402,7 @@ index deab005f..36ca1c7d 100644 .name('roughness') .onChange(() => render()); diff --git a/examples-testing/examples/webgpu_pmrem_test.ts b/examples-testing/examples/webgpu_pmrem_test.ts -index 3e1ea3ef..e0e11ef8 100644 +index 3e1ea3ef9..e0e11ef81 100644 --- a/examples-testing/examples/webgpu_pmrem_test.ts +++ b/examples-testing/examples/webgpu_pmrem_test.ts @@ -1,11 +1,11 @@ @@ -18441,7 +18441,7 @@ index 3e1ea3ef..e0e11ef8 100644 }); }); diff --git a/examples-testing/examples/webgpu_portal.ts b/examples-testing/examples/webgpu_portal.ts -index df2e400f..a9b88127 100644 +index df2e400f3..a9b881274 100644 --- a/examples-testing/examples/webgpu_portal.ts +++ b/examples-testing/examples/webgpu_portal.ts @@ -17,10 +17,10 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -18490,7 +18490,7 @@ index df2e400f..a9b88127 100644 applyFX(0); diff --git a/examples-testing/examples/webgpu_postprocessing.ts b/examples-testing/examples/webgpu_postprocessing.ts -index d554a883..1b45472b 100644 +index d554a8835..1b45472b6 100644 --- a/examples-testing/examples/webgpu_postprocessing.ts +++ b/examples-testing/examples/webgpu_postprocessing.ts @@ -4,8 +4,8 @@ import { dotScreen } from 'three/addons/tsl/display/DotScreenNode.js'; @@ -18505,7 +18505,7 @@ index d554a883..1b45472b 100644 init(); diff --git a/examples-testing/examples/webgpu_postprocessing_3dlut.ts b/examples-testing/examples/webgpu_postprocessing_3dlut.ts -index 867cc62f..cd14289f 100644 +index 867cc62f7..cd14289f0 100644 --- a/examples-testing/examples/webgpu_postprocessing_3dlut.ts +++ b/examples-testing/examples/webgpu_postprocessing_3dlut.ts @@ -18,21 +18,31 @@ import { @@ -18631,7 +18631,7 @@ index 867cc62f..cd14289f 100644 lutPass.size.value = lut.texture3D.image.width; } diff --git a/examples-testing/examples/webgpu_postprocessing_afterimage.ts b/examples-testing/examples/webgpu_postprocessing_afterimage.ts -index 2df8212f..8d35cb73 100644 +index 2df8212f5..8d35cb734 100644 --- a/examples-testing/examples/webgpu_postprocessing_afterimage.ts +++ b/examples-testing/examples/webgpu_postprocessing_afterimage.ts @@ -13,12 +13,12 @@ import { @@ -18705,7 +18705,7 @@ index 2df8212f..8d35cb73 100644 renderPipeline.render(); diff --git a/examples-testing/examples/webgpu_postprocessing_anamorphic.ts b/examples-testing/examples/webgpu_postprocessing_anamorphic.ts -index 1cdb6893..8286bb59 100644 +index 1cdb6893c..8286bb59b 100644 --- a/examples-testing/examples/webgpu_postprocessing_anamorphic.ts +++ b/examples-testing/examples/webgpu_postprocessing_anamorphic.ts @@ -9,8 +9,8 @@ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; @@ -18729,7 +18729,7 @@ index 1cdb6893..8286bb59 100644 gui.add(threshold, 'value', 0.8, 3, 0.001).name('threshold'); gui.add(scaleNode, 'value', 1, 10, 0.1).name('scale'); diff --git a/examples-testing/examples/webgpu_postprocessing_ao.ts b/examples-testing/examples/webgpu_postprocessing_ao.ts -index 76046bee..460c90fd 100644 +index 76046bee8..460c90fd6 100644 --- a/examples-testing/examples/webgpu_postprocessing_ao.ts +++ b/examples-testing/examples/webgpu_postprocessing_ao.ts @@ -13,8 +13,8 @@ import { @@ -18771,7 +18771,7 @@ index 76046bee..460c90fd 100644 gui.add(params, 'distanceExponent', 1, 2).onChange(updateParameters); gui.add(params, 'distanceFallOff', 0.01, 1).onChange(updateParameters); diff --git a/examples-testing/examples/webgpu_postprocessing_bloom.ts b/examples-testing/examples/webgpu_postprocessing_bloom.ts -index 3197d680..d173ef87 100644 +index 3197d680d..d173ef87b 100644 --- a/examples-testing/examples/webgpu_postprocessing_bloom.ts +++ b/examples-testing/examples/webgpu_postprocessing_bloom.ts @@ -7,8 +7,11 @@ import { Inspector } from 'three/addons/inspector/Inspector.js'; @@ -18798,7 +18798,7 @@ index 3197d680..d173ef87 100644 const bloomFolder = gui.addFolder('bloom'); diff --git a/examples-testing/examples/webgpu_postprocessing_bloom_emissive.ts b/examples-testing/examples/webgpu_postprocessing_bloom_emissive.ts -index faa5b67f..efe98431 100644 +index faa5b67fb..efe984318 100644 --- a/examples-testing/examples/webgpu_postprocessing_bloom_emissive.ts +++ b/examples-testing/examples/webgpu_postprocessing_bloom_emissive.ts @@ -9,8 +9,8 @@ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; @@ -18822,7 +18822,7 @@ index faa5b67f..efe98431 100644 const bloomFolder = gui.addFolder('Bloom'); bloomFolder.add(bloomPass.strength, 'value', 0.0, 5.0).name('strength'); diff --git a/examples-testing/examples/webgpu_postprocessing_bloom_selective.ts b/examples-testing/examples/webgpu_postprocessing_bloom_selective.ts -index b8f2427d..00b649ae 100644 +index b8f2427d4..00b649aed 100644 --- a/examples-testing/examples/webgpu_postprocessing_bloom_selective.ts +++ b/examples-testing/examples/webgpu_postprocessing_bloom_selective.ts @@ -86,16 +86,17 @@ window.addEventListener('pointerdown', event => { @@ -18847,7 +18847,7 @@ index b8f2427d..00b649ae 100644 const bloomFolder = gui.addFolder('Bloom'); bloomFolder.add(bloomPass.threshold, 'value', 0.0, 1.0).name('threshold'); diff --git a/examples-testing/examples/webgpu_postprocessing_ca.ts b/examples-testing/examples/webgpu_postprocessing_ca.ts -index 48b54905..4af52250 100644 +index 48b549050..4af52250f 100644 --- a/examples-testing/examples/webgpu_postprocessing_ca.ts +++ b/examples-testing/examples/webgpu_postprocessing_ca.ts @@ -17,8 +17,12 @@ const params = { @@ -18895,7 +18895,7 @@ index 48b54905..4af52250 100644 subChild.rotation.z = time * (1 - subIndex * 0.1); } diff --git a/examples-testing/examples/webgpu_postprocessing_difference.ts b/examples-testing/examples/webgpu_postprocessing_difference.ts -index 91cc4cbf..7dd420b3 100644 +index 91cc4cbf5..7dd420b37 100644 --- a/examples-testing/examples/webgpu_postprocessing_difference.ts +++ b/examples-testing/examples/webgpu_postprocessing_difference.ts @@ -9,8 +9,8 @@ const params = { @@ -18919,7 +18919,7 @@ index 91cc4cbf..7dd420b3 100644 } diff --git a/examples-testing/examples/webgpu_postprocessing_dof.ts b/examples-testing/examples/webgpu_postprocessing_dof.ts -index 862c2802..72722971 100644 +index 862c2802b..72722971d 100644 --- a/examples-testing/examples/webgpu_postprocessing_dof.ts +++ b/examples-testing/examples/webgpu_postprocessing_dof.ts @@ -7,12 +7,16 @@ import { Inspector } from 'three/addons/inspector/Inspector.js'; @@ -18951,7 +18951,7 @@ index 862c2802..72722971 100644 gui.add(effectController.focalLength, 'value', 50, 750).name('focal length'); gui.add(effectController.bokehScale, 'value', 1, 20).name('bokeh scale'); diff --git a/examples-testing/examples/webgpu_postprocessing_dof_basic.ts b/examples-testing/examples/webgpu_postprocessing_dof_basic.ts -index 7d51910e..9dc7e177 100644 +index 3ad295247..6e0d389bb 100644 --- a/examples-testing/examples/webgpu_postprocessing_dof_basic.ts +++ b/examples-testing/examples/webgpu_postprocessing_dof_basic.ts @@ -11,7 +11,15 @@ import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; @@ -18990,7 +18990,7 @@ index 7d51910e..9dc7e177 100644 raycaster.setFromCamera(pointerCoords, camera); diff --git a/examples-testing/examples/webgpu_postprocessing_fxaa.ts b/examples-testing/examples/webgpu_postprocessing_fxaa.ts -index 24c01a95..b820da43 100644 +index 24c01a950..b820da439 100644 --- a/examples-testing/examples/webgpu_postprocessing_fxaa.ts +++ b/examples-testing/examples/webgpu_postprocessing_fxaa.ts @@ -9,8 +9,12 @@ const params = { @@ -19018,7 +19018,7 @@ index 24c01a95..b820da43 100644 if (value === true) { renderPipeline.outputNode = fxaaPass; diff --git a/examples-testing/examples/webgpu_postprocessing_godrays.ts b/examples-testing/examples/webgpu_postprocessing_godrays.ts -index 32e619a4..fe3c281c 100644 +index 32e619a47..fe3c281c6 100644 --- a/examples-testing/examples/webgpu_postprocessing_godrays.ts +++ b/examples-testing/examples/webgpu_postprocessing_godrays.ts @@ -9,7 +9,11 @@ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; @@ -19071,7 +19071,7 @@ index 32e619a4..fe3c281c 100644 godraysFolder.add(godraysPass.raymarchSteps, 'value', 24, 120).step(1).name('raymarch steps'); godraysFolder.add(godraysPass.density, 'value', 0, 1).name('density'); diff --git a/examples-testing/examples/webgpu_postprocessing_lensflare.ts b/examples-testing/examples/webgpu_postprocessing_lensflare.ts -index 39b1e5f6..07904d37 100644 +index 39b1e5f6e..07904d37c 100644 --- a/examples-testing/examples/webgpu_postprocessing_lensflare.ts +++ b/examples-testing/examples/webgpu_postprocessing_lensflare.ts @@ -11,8 +11,8 @@ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; @@ -19095,7 +19095,7 @@ index 39b1e5f6..07904d37 100644 const bloomFolder = gui.addFolder('bloom'); bloomFolder.add(bloomPass.strength, 'value', 0.0, 2.0).name('strength'); diff --git a/examples-testing/examples/webgpu_postprocessing_masking.ts b/examples-testing/examples/webgpu_postprocessing_masking.ts -index b234abc6..2f52a98c 100644 +index b234abc66..2f52a98c9 100644 --- a/examples-testing/examples/webgpu_postprocessing_masking.ts +++ b/examples-testing/examples/webgpu_postprocessing_masking.ts @@ -2,8 +2,8 @@ import * as THREE from 'three/webgpu'; @@ -19119,7 +19119,7 @@ index b234abc6..2f52a98c 100644 compose = sceneMask2.mix(compose, texture(texture2)); diff --git a/examples-testing/examples/webgpu_postprocessing_motion_blur.ts b/examples-testing/examples/webgpu_postprocessing_motion_blur.ts -index 5ae3d966..64a4411f 100644 +index 5ae3d9661..64a4411fc 100644 --- a/examples-testing/examples/webgpu_postprocessing_motion_blur.ts +++ b/examples-testing/examples/webgpu_postprocessing_motion_blur.ts @@ -8,10 +8,10 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -19156,7 +19156,7 @@ index 5ae3d966..64a4411f 100644 gui.add(blurAmount, 'value', 0, 3).name('blur amount'); gui.add(params, 'speed', 0, 2); diff --git a/examples-testing/examples/webgpu_postprocessing_outline.ts b/examples-testing/examples/webgpu_postprocessing_outline.ts -index f87415b7..beb843ed 100644 +index f87415b74..beb843ed8 100644 --- a/examples-testing/examples/webgpu_postprocessing_outline.ts +++ b/examples-testing/examples/webgpu_postprocessing_outline.ts @@ -1,16 +1,16 @@ @@ -19208,7 +19208,7 @@ index f87415b7..beb843ed 100644 selectedObjects.push(object); } diff --git a/examples-testing/examples/webgpu_postprocessing_pixel.ts b/examples-testing/examples/webgpu_postprocessing_pixel.ts -index a3d58f20..26c4a8ac 100644 +index a3d58f203..26c4a8ac8 100644 --- a/examples-testing/examples/webgpu_postprocessing_pixel.ts +++ b/examples-testing/examples/webgpu_postprocessing_pixel.ts @@ -6,8 +6,18 @@ import { Inspector } from 'three/addons/inspector/Inspector.js'; @@ -19295,7 +19295,7 @@ index a3d58f20..26c4a8ac 100644 const worldScreenWidth = (camera.right - camera.left) / camera.zoom; const worldScreenHeight = (camera.top - camera.bottom) / camera.zoom; diff --git a/examples-testing/examples/webgpu_postprocessing_radial_blur.ts b/examples-testing/examples/webgpu_postprocessing_radial_blur.ts -index f358421e..95f4cba6 100644 +index f358421e3..95f4cba68 100644 --- a/examples-testing/examples/webgpu_postprocessing_radial_blur.ts +++ b/examples-testing/examples/webgpu_postprocessing_radial_blur.ts @@ -9,8 +9,12 @@ const params = { @@ -19323,7 +19323,7 @@ index f358421e..95f4cba6 100644 gui.add(decayUniform, 'value', 0, 1).name('decay'); gui.add(countUniform, 'value', 16, 64, 1).name('sample count'); diff --git a/examples-testing/examples/webgpu_postprocessing_retro.ts b/examples-testing/examples/webgpu_postprocessing_retro.ts -index b9958352..541a349a 100644 +index b9958352f..541a349ac 100644 --- a/examples-testing/examples/webgpu_postprocessing_retro.ts +++ b/examples-testing/examples/webgpu_postprocessing_retro.ts @@ -40,9 +40,13 @@ import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; @@ -19390,7 +19390,7 @@ index b9958352..541a349a 100644 gui.add({ model: 'Coffee Mug' }, 'model', Object.keys(models)).name('Model').onChange(loadModel); diff --git a/examples-testing/examples/webgpu_postprocessing_smaa.ts b/examples-testing/examples/webgpu_postprocessing_smaa.ts -index f0637425..e23e3725 100644 +index f06374258..e23e37256 100644 --- a/examples-testing/examples/webgpu_postprocessing_smaa.ts +++ b/examples-testing/examples/webgpu_postprocessing_smaa.ts @@ -4,7 +4,10 @@ import { smaa } from 'three/addons/tsl/display/SMAANode.js'; @@ -19415,7 +19415,7 @@ index f0637425..e23e3725 100644 const smaaFolder = gui.addFolder('SMAA'); smaaFolder.add(params, 'enabled').onChange(value => { diff --git a/examples-testing/examples/webgpu_postprocessing_sobel.ts b/examples-testing/examples/webgpu_postprocessing_sobel.ts -index bdc37d2f..0888593b 100644 +index bdc37d2fd..0888593b0 100644 --- a/examples-testing/examples/webgpu_postprocessing_sobel.ts +++ b/examples-testing/examples/webgpu_postprocessing_sobel.ts @@ -7,8 +7,8 @@ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; @@ -19448,7 +19448,7 @@ index bdc37d2f..0888593b 100644 // diff --git a/examples-testing/examples/webgpu_postprocessing_ssaa.ts b/examples-testing/examples/webgpu_postprocessing_ssaa.ts -index c38832c7..8630c18d 100644 +index c38832c7a..8630c18d6 100644 --- a/examples-testing/examples/webgpu_postprocessing_ssaa.ts +++ b/examples-testing/examples/webgpu_postprocessing_ssaa.ts @@ -1,11 +1,11 @@ @@ -19495,7 +19495,7 @@ index c38832c7..8630c18d 100644 renderPipeline.render(); } diff --git a/examples-testing/examples/webgpu_postprocessing_ssgi.ts b/examples-testing/examples/webgpu_postprocessing_ssgi.ts -index 8c8e4a91..12595f85 100644 +index 8c8e4a91c..12595f85c 100644 --- a/examples-testing/examples/webgpu_postprocessing_ssgi.ts +++ b/examples-testing/examples/webgpu_postprocessing_ssgi.ts @@ -20,7 +20,11 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -19529,8 +19529,111 @@ index 8c8e4a91..12595f85 100644 if (value === 1) { renderPipeline.outputNode = vec4(vec3(ao), 1); } else if (value === 2) { +diff --git a/examples-testing/examples/webgpu_postprocessing_ssgi_ballpool.ts b/examples-testing/examples/webgpu_postprocessing_ssgi_ballpool.ts +index 4d1e617c1..54d760f2f 100644 +--- a/examples-testing/examples/webgpu_postprocessing_ssgi_ballpool.ts ++++ b/examples-testing/examples/webgpu_postprocessing_ssgi_ballpool.ts +@@ -14,7 +14,8 @@ import { + } from 'three/tsl'; + import { ssgi } from 'three/addons/tsl/display/SSGINode.js'; + import { traa } from 'three/addons/tsl/display/TRAANode.js'; +-import { World } from '@perplexdotgg/bounce'; ++import { Body, Vec3, World } from '@perplexdotgg/bounce'; ++import { WithPool } from 'monomorph'; + + const BALL_RADIUS = 0.4; + const FILL_RATIO = 0.4; +@@ -25,15 +26,18 @@ const WALL_THICKNESS = 0.5; + const CAM_FOV = 45; + const EASE_SPEED = 8; + +-let camera, scene, renderer, renderPipeline; +-let raycaster, pointer; +-let mouseLight; ++let camera: THREE.PerspectiveCamera, ++ scene: THREE.Scene, ++ renderer: THREE.WebGPURenderer, ++ renderPipeline: THREE.RenderPipeline; ++let raycaster: THREE.Raycaster, pointer: THREE.Vector2; ++let mouseLight: THREE.PointLight; + +-let world; ++let world: World; + let ballCount = 0; +-let bodies = []; +-let ballsMesh = null; +-let wallMeshes = []; ++let bodies: WithPool[] = []; ++let ballsMesh: THREE.InstancedMesh | null = null; ++let wallMeshes: THREE.Mesh[] = []; + const boxSize = { w: 8, h: BOX_HEIGHT, d: BOX_DEPTH }; + + let mouseRayOrigin = new THREE.Vector3(); +@@ -207,7 +211,12 @@ function createBox() { + metalness: 0.0, + }); + +- const walls = [ ++ const walls: { ++ size: [number, number, number]; ++ pos: [number, number, number]; ++ mat?: THREE.MeshPhysicalMaterial; ++ noMesh?: boolean; ++ }[] = [ + { size: [boxSize.w, t, boxSize.d], pos: [0, -t / 2, 0], mat: whiteMaterial }, + { size: [boxSize.w, t, boxSize.d], pos: [0, boxSize.h + t / 2, 0], mat: whiteMaterial }, + { size: [boxSize.w, boxSize.h, t], pos: [0, hh, -hd - t / 2], mat: whiteMaterial }, +@@ -271,7 +280,7 @@ function createBalls() { + } + } + +-function onPointerDown(event) { ++function onPointerDown(event: PointerEvent) { + activePointers.add(event.pointerId); + + if (event.pointerType === 'touch') { +@@ -281,7 +290,7 @@ function onPointerDown(event) { + } + } + +-function onPointerUp(event) { ++function onPointerUp(event: PointerEvent) { + activePointers.delete(event.pointerId); + + if (event.pointerType === 'touch') { +@@ -291,7 +300,7 @@ function onPointerUp(event) { + } + } + +-function onPointerMove(event) { ++function onPointerMove(event: PointerEvent) { + pointer.x = (event.clientX / window.innerWidth) * 2 - 1; + pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; + +@@ -386,7 +395,7 @@ function animate() { + x: _pushDir.x * strength, + y: _pushDir.y * strength, + z: _pushDir.z * strength, +- }); ++ } as Vec3); + } + } + } +@@ -404,10 +413,10 @@ function animate() { + _dummy.quaternion.set(q.x, q.y, q.z, q.w); + _dummy.updateMatrix(); + +- ballsMesh.setMatrixAt(i, _dummy.matrix); ++ ballsMesh!.setMatrixAt(i, _dummy.matrix); + } + +- ballsMesh.instanceMatrix.needsUpdate = true; ++ ballsMesh!.instanceMatrix.needsUpdate = true; + + renderPipeline.render(); + } diff --git a/examples-testing/examples/webgpu_postprocessing_ssr.ts b/examples-testing/examples/webgpu_postprocessing_ssr.ts -index 7ce11780..2d8012fa 100644 +index 7ce11780a..2d8012faa 100644 --- a/examples-testing/examples/webgpu_postprocessing_ssr.ts +++ b/examples-testing/examples/webgpu_postprocessing_ssr.ts @@ -14,7 +14,7 @@ import { @@ -19597,7 +19700,7 @@ index 7ce11780..2d8012fa 100644 }); }); diff --git a/examples-testing/examples/webgpu_postprocessing_sss.ts b/examples-testing/examples/webgpu_postprocessing_sss.ts -index 42cc26f3..01ca5fa0 100644 +index 42cc26f39..01ca5fa02 100644 --- a/examples-testing/examples/webgpu_postprocessing_sss.ts +++ b/examples-testing/examples/webgpu_postprocessing_sss.ts @@ -8,7 +8,11 @@ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; @@ -19636,7 +19739,7 @@ index 42cc26f3..01ca5fa0 100644 gui.add(sssPass.shadowIntensity, 'value', 0, 1).name('shadow intensity'); gui.add(sssPass.maxDistance, 'value', 0.01, 1).name('max ray distance'); diff --git a/examples-testing/examples/webgpu_postprocessing_traa.ts b/examples-testing/examples/webgpu_postprocessing_traa.ts -index 01d17ca1..9c7b6cb6 100644 +index 01d17ca17..9c7b6cb64 100644 --- a/examples-testing/examples/webgpu_postprocessing_traa.ts +++ b/examples-testing/examples/webgpu_postprocessing_traa.ts @@ -4,7 +4,10 @@ import { traa } from 'three/addons/tsl/display/TRAANode.js'; @@ -19652,7 +19755,7 @@ index 01d17ca1..9c7b6cb6 100644 init(); diff --git a/examples-testing/examples/webgpu_postprocessing_transition.ts b/examples-testing/examples/webgpu_postprocessing_transition.ts -index 1a5a88fc..74dc8f8e 100644 +index 1a5a88fc1..74dc8f8ec 100644 --- a/examples-testing/examples/webgpu_postprocessing_transition.ts +++ b/examples-testing/examples/webgpu_postprocessing_transition.ts @@ -3,17 +3,33 @@ import * as THREE from 'three/webgpu'; @@ -19796,7 +19899,7 @@ index 1a5a88fc..74dc8f8e 100644 gui.add(effectController, 'animateScene').name('Animate Scene'); gui.add(effectController, 'animateTransition').name('Animate Transition'); diff --git a/examples-testing/examples/webgpu_procedural_texture.ts b/examples-testing/examples/webgpu_procedural_texture.ts -index 21fb4a3c..d3a164be 100644 +index 21fb4a3c2..d3a164bef 100644 --- a/examples-testing/examples/webgpu_procedural_texture.ts +++ b/examples-testing/examples/webgpu_procedural_texture.ts @@ -4,7 +4,7 @@ import { gaussianBlur } from 'three/addons/tsl/display/GaussianBlurNode.js'; @@ -19818,7 +19921,7 @@ index 21fb4a3c..d3a164be 100644 gui.add(blurAmount, 'value', 0, 2).name('blur amount ( after rtt )'); gui.add(proceduralToTexture, 'autoUpdate').name('auto update'); diff --git a/examples-testing/examples/webgpu_reflection.ts b/examples-testing/examples/webgpu_reflection.ts -index d5dd2331..a5a22f6a 100644 +index d5dd23318..a5a22f6a1 100644 --- a/examples-testing/examples/webgpu_reflection.ts +++ b/examples-testing/examples/webgpu_reflection.ts @@ -32,9 +32,9 @@ import { Inspector } from 'three/addons/inspector/Inspector.js'; @@ -19898,7 +20001,7 @@ index d5dd2331..a5a22f6a 100644 animated = animated.add(direction.mul(effect.add(instanceSize))); diff --git a/examples-testing/examples/webgpu_reflection_roughness.ts b/examples-testing/examples/webgpu_reflection_roughness.ts -index 2b6b4a78..edabe7a8 100644 +index 2b6b4a78d..edabe7a83 100644 --- a/examples-testing/examples/webgpu_reflection_roughness.ts +++ b/examples-testing/examples/webgpu_reflection_roughness.ts @@ -6,8 +6,8 @@ import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; @@ -19913,7 +20016,7 @@ index 2b6b4a78..edabe7a8 100644 init(); diff --git a/examples-testing/examples/webgpu_refraction.ts b/examples-testing/examples/webgpu_refraction.ts -index d475ed2d..3d0c4e82 100644 +index d475ed2de..3d0c4e82b 100644 --- a/examples-testing/examples/webgpu_refraction.ts +++ b/examples-testing/examples/webgpu_refraction.ts @@ -5,11 +5,11 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -19932,7 +20035,7 @@ index d475ed2d..3d0c4e82 100644 init(); diff --git a/examples-testing/examples/webgpu_rendertarget_2d-array_3d.ts b/examples-testing/examples/webgpu_rendertarget_2d-array_3d.ts -index 627fd6b1..9dc934c0 100644 +index 627fd6b18..9dc934c0f 100644 --- a/examples-testing/examples/webgpu_rendertarget_2d-array_3d.ts +++ b/examples-testing/examples/webgpu_rendertarget_2d-array_3d.ts @@ -5,11 +5,22 @@ import { TextureHelper } from 'three/addons/helpers/TextureHelperGPU.js'; @@ -20018,7 +20121,7 @@ index 627fd6b1..9dc934c0 100644 const left = Math.floor(view.left * window.innerWidth); const bottom = Math.floor((1 - view.top - view.height) * window.innerHeight); diff --git a/examples-testing/examples/webgpu_reversed_depth_buffer.ts b/examples-testing/examples/webgpu_reversed_depth_buffer.ts -index d159748f..fae26aad 100644 +index d159748f5..fae26aad0 100644 --- a/examples-testing/examples/webgpu_reversed_depth_buffer.ts +++ b/examples-testing/examples/webgpu_reversed_depth_buffer.ts @@ -1,10 +1,14 @@ @@ -20066,7 +20169,7 @@ index d159748f..fae26aad 100644 reverseRenderer.setPixelRatio(window.devicePixelRatio); reverseRenderer.setSize(0.33 * window.innerWidth, window.innerHeight); diff --git a/examples-testing/examples/webgpu_rtt.ts b/examples-testing/examples/webgpu_rtt.ts -index fa7577ea..b4c7a8c3 100644 +index fa7577ea8..b4c7a8c39 100644 --- a/examples-testing/examples/webgpu_rtt.ts +++ b/examples-testing/examples/webgpu_rtt.ts @@ -3,12 +3,12 @@ import { texture, uniform, saturation, hue } from 'three/tsl'; @@ -20095,7 +20198,7 @@ index fa7577ea..b4c7a8c3 100644 mouse.y = e.offsetY / window.innerHeight; } diff --git a/examples-testing/examples/webgpu_shadow_contact.ts b/examples-testing/examples/webgpu_shadow_contact.ts -index ac3e4fbd..64234710 100644 +index ac3e4fbd8..642347107 100644 --- a/examples-testing/examples/webgpu_shadow_contact.ts +++ b/examples-testing/examples/webgpu_shadow_contact.ts @@ -4,13 +4,13 @@ import { gaussianBlur } from 'three/addons/tsl/display/GaussianBlurNode.js'; @@ -20128,7 +20231,7 @@ index ac3e4fbd..64234710 100644 gui.add(params, 'shadowBlur', 0, 15, 0.1).onChange(() => { state.shadow.blur = params.shadowBlur; diff --git a/examples-testing/examples/webgpu_shadowmap.ts b/examples-testing/examples/webgpu_shadowmap.ts -index f6edec75..a0064c0d 100644 +index f6edec750..a0064c0dc 100644 --- a/examples-testing/examples/webgpu_shadowmap.ts +++ b/examples-testing/examples/webgpu_shadowmap.ts @@ -5,9 +5,9 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -20154,7 +20257,7 @@ index f6edec75..a0064c0d 100644 const delta = timer.getDelta(); diff --git a/examples-testing/examples/webgpu_shadowmap_array.ts b/examples-testing/examples/webgpu_shadowmap_array.ts -index 0dfe8084..efdb6b9e 100644 +index 0dfe8084f..efdb6b9eb 100644 --- a/examples-testing/examples/webgpu_shadowmap_array.ts +++ b/examples-testing/examples/webgpu_shadowmap_array.ts @@ -8,10 +8,10 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -20182,7 +20285,7 @@ index 0dfe8084..efdb6b9e 100644 const delta = timer.getDelta(); diff --git a/examples-testing/examples/webgpu_shadowmap_csm.ts b/examples-testing/examples/webgpu_shadowmap_csm.ts -index 5be06145..60811643 100644 +index 5be061456..60811643e 100644 --- a/examples-testing/examples/webgpu_shadowmap_csm.ts +++ b/examples-testing/examples/webgpu_shadowmap_csm.ts @@ -4,12 +4,33 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -20255,7 +20358,7 @@ index 5be06145..60811643 100644 }); diff --git a/examples-testing/examples/webgpu_shadowmap_opacity.ts b/examples-testing/examples/webgpu_shadowmap_opacity.ts -index e508e103..ec2ae150 100644 +index e508e1039..ec2ae1509 100644 --- a/examples-testing/examples/webgpu_shadowmap_opacity.ts +++ b/examples-testing/examples/webgpu_shadowmap_opacity.ts @@ -6,7 +6,7 @@ import { Inspector } from 'three/addons/inspector/Inspector.js'; @@ -20294,7 +20397,7 @@ index e508e103..ec2ae150 100644 // apply shadow diff --git a/examples-testing/examples/webgpu_shadowmap_pointlight.ts b/examples-testing/examples/webgpu_shadowmap_pointlight.ts -index 3cb878e6..ef4acd04 100644 +index 3cb878e6f..ef4acd04a 100644 --- a/examples-testing/examples/webgpu_shadowmap_pointlight.ts +++ b/examples-testing/examples/webgpu_shadowmap_pointlight.ts @@ -1,11 +1,11 @@ @@ -20342,7 +20445,7 @@ index 3cb878e6..ef4acd04 100644 context.fillRect(0, 1, 2, 1); diff --git a/examples-testing/examples/webgpu_shadowmap_progressive.ts b/examples-testing/examples/webgpu_shadowmap_progressive.ts -index ca5ee0f7..26016858 100644 +index ca5ee0f72..260168589 100644 --- a/examples-testing/examples/webgpu_shadowmap_progressive.ts +++ b/examples-testing/examples/webgpu_shadowmap_progressive.ts @@ -11,17 +11,17 @@ import { ProgressiveLightMap } from 'three/addons/misc/ProgressiveLightMapGPU.js @@ -20401,7 +20504,7 @@ index ca5ee0f7..26016858 100644 gui.add(params, 'Blur Edges'); gui.add(params, 'Blend Window', 1, 500, 1); diff --git a/examples-testing/examples/webgpu_shadowmap_vsm.ts b/examples-testing/examples/webgpu_shadowmap_vsm.ts -index edaf4fd9..a40b8b28 100644 +index edaf4fd9e..a40b8b289 100644 --- a/examples-testing/examples/webgpu_shadowmap_vsm.ts +++ b/examples-testing/examples/webgpu_shadowmap_vsm.ts @@ -4,9 +4,9 @@ import { Inspector } from 'three/addons/inspector/Inspector.js'; @@ -20436,7 +20539,7 @@ index edaf4fd9..a40b8b28 100644 const delta = timer.getDelta(); diff --git a/examples-testing/examples/webgpu_skinning.ts b/examples-testing/examples/webgpu_skinning.ts -index 36669a5d..ba34cfcf 100644 +index 36669a5d4..ba34cfcfd 100644 --- a/examples-testing/examples/webgpu_skinning.ts +++ b/examples-testing/examples/webgpu_skinning.ts @@ -3,9 +3,9 @@ import { color, screenUV } from 'three/tsl'; @@ -20452,7 +20555,7 @@ index 36669a5d..ba34cfcf 100644 init(); diff --git a/examples-testing/examples/webgpu_skinning_instancing.ts b/examples-testing/examples/webgpu_skinning_instancing.ts -index 877ee46c..1b8d0b2d 100644 +index 877ee46cb..1b8d0b2dc 100644 --- a/examples-testing/examples/webgpu_skinning_instancing.ts +++ b/examples-testing/examples/webgpu_skinning_instancing.ts @@ -4,10 +4,10 @@ import { gaussianBlur } from 'three/addons/tsl/display/GaussianBlurNode.js'; @@ -20520,7 +20623,7 @@ index 877ee46c..1b8d0b2d 100644 } }); diff --git a/examples-testing/examples/webgpu_sprites.ts b/examples-testing/examples/webgpu_sprites.ts -index de9f219b..68a7b73a 100644 +index de9f219b8..68a7b73a8 100644 --- a/examples-testing/examples/webgpu_sprites.ts +++ b/examples-testing/examples/webgpu_sprites.ts @@ -1,11 +1,11 @@ @@ -20539,7 +20642,7 @@ index de9f219b..68a7b73a 100644 let imageWidth = 1, imageHeight = 1; diff --git a/examples-testing/examples/webgpu_storage_buffer.ts b/examples-testing/examples/webgpu_storage_buffer.ts -index 1b4461d3..5c2d4902 100644 +index 1b4461d33..5c2d49027 100644 --- a/examples-testing/examples/webgpu_storage_buffer.ts +++ b/examples-testing/examples/webgpu_storage_buffer.ts @@ -2,8 +2,8 @@ import * as THREE from 'three/webgpu'; @@ -20616,7 +20719,7 @@ index 1b4461d3..5c2d4902 100644 color.assign(vec3(value, value, value)); diff --git a/examples-testing/examples/webgpu_struct_drawindirect.ts b/examples-testing/examples/webgpu_struct_drawindirect.ts -index b0d1139d..ad573696 100644 +index b0d1139db..ad5736962 100644 --- a/examples-testing/examples/webgpu_struct_drawindirect.ts +++ b/examples-testing/examples/webgpu_struct_drawindirect.ts @@ -45,7 +45,7 @@ scene.background = new THREE.Color(0x00001f); @@ -20646,7 +20749,7 @@ index b0d1139d..ad573696 100644 }; diff --git a/examples-testing/examples/webgpu_test_memory.ts b/examples-testing/examples/webgpu_test_memory.ts -index aadde4dc..c4babf4a 100644 +index aadde4dc6..c4babf4ae 100644 --- a/examples-testing/examples/webgpu_test_memory.ts +++ b/examples-testing/examples/webgpu_test_memory.ts @@ -1,16 +1,22 @@ @@ -20725,7 +20828,7 @@ index aadde4dc..c4babf4a 100644 } diff --git a/examples-testing/examples/webgpu_textures_2d-array.ts b/examples-testing/examples/webgpu_textures_2d-array.ts -index a1a354ff..940a35c8 100644 +index a1a354ff7..940a35c89 100644 --- a/examples-testing/examples/webgpu_textures_2d-array.ts +++ b/examples-testing/examples/webgpu_textures_2d-array.ts @@ -5,7 +5,10 @@ import { unzipSync } from 'three/addons/libs/fflate.module.js'; @@ -20759,7 +20862,7 @@ index a1a354ff..940a35c8 100644 let oscLayers = oscTriangle(time.mul(0.5)); // [ /\/ ] triangle osc animation diff --git a/examples-testing/examples/webgpu_textures_2d-array_compressed.ts b/examples-testing/examples/webgpu_textures_2d-array_compressed.ts -index 62b51645..12beb46d 100644 +index 62b516458..12beb46d5 100644 --- a/examples-testing/examples/webgpu_textures_2d-array_compressed.ts +++ b/examples-testing/examples/webgpu_textures_2d-array_compressed.ts @@ -6,7 +6,11 @@ import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js'; @@ -20776,7 +20879,7 @@ index 62b51645..12beb46d 100644 const depth = uniform(0); diff --git a/examples-testing/examples/webgpu_textures_anisotropy.ts b/examples-testing/examples/webgpu_textures_anisotropy.ts -index 0444823f..fdcb1a01 100644 +index 0444823f5..fdcb1a01a 100644 --- a/examples-testing/examples/webgpu_textures_anisotropy.ts +++ b/examples-testing/examples/webgpu_textures_anisotropy.ts @@ -1,8 +1,8 @@ @@ -20816,7 +20919,7 @@ index 0444823f..fdcb1a01 100644 const windowHalfY = window.innerHeight / 2; diff --git a/examples-testing/examples/webgpu_textures_partialupdate.ts b/examples-testing/examples/webgpu_textures_partialupdate.ts -index d189cec9..da4f7320 100644 +index d893a041d..c14143058 100644 --- a/examples-testing/examples/webgpu_textures_partialupdate.ts +++ b/examples-testing/examples/webgpu_textures_partialupdate.ts @@ -1,6 +1,11 @@ @@ -20832,7 +20935,7 @@ index d189cec9..da4f7320 100644 let last = 0; const position = new THREE.Vector2(); -@@ -79,9 +84,9 @@ function animate() { +@@ -80,9 +85,9 @@ function animate() { } } @@ -20845,7 +20948,7 @@ index d189cec9..da4f7320 100644 // generate a random color and update texture data diff --git a/examples-testing/examples/webgpu_tonemapping.ts b/examples-testing/examples/webgpu_tonemapping.ts -index 4140a49f..e371e494 100644 +index 4140a49fd..e371e494c 100644 --- a/examples-testing/examples/webgpu_tonemapping.ts +++ b/examples-testing/examples/webgpu_tonemapping.ts @@ -1,23 +1,33 @@ @@ -20901,7 +21004,7 @@ index 4140a49f..e371e494 100644 .name('type') .onChange(function () { diff --git a/examples-testing/examples/webgpu_tsl_angular_slicing.ts b/examples-testing/examples/webgpu_tsl_angular_slicing.ts -index d74e1bc0..8149db0a 100644 +index d74e1bc0a..8149db0a4 100644 --- a/examples-testing/examples/webgpu_tsl_angular_slicing.ts +++ b/examples-testing/examples/webgpu_tsl_angular_slicing.ts @@ -8,7 +8,7 @@ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; @@ -20953,7 +21056,7 @@ index d74e1bc0..8149db0a 100644 gui.add(sliceArc, 'value', 0, Math.PI * 2, 0.001).name('sliceArc'); gui.addColor({ color: sliceColor.value.getHexString(THREE.SRGBColorSpace) }, 'color').onChange(value => diff --git a/examples-testing/examples/webgpu_tsl_earth.ts b/examples-testing/examples/webgpu_tsl_earth.ts -index dece09af..512fb5cd 100644 +index dece09afb..512fb5cdf 100644 --- a/examples-testing/examples/webgpu_tsl_earth.ts +++ b/examples-testing/examples/webgpu_tsl_earth.ts @@ -21,7 +21,12 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -20980,7 +21083,7 @@ index dece09af..512fb5cd 100644 gui.addColor({ color: atmosphereDayColor.value.getHex(THREE.SRGBColorSpace) }, 'color') .onChange(value => { diff --git a/examples-testing/examples/webgpu_tsl_galaxy.ts b/examples-testing/examples/webgpu_tsl_galaxy.ts -index 73913850..46c0eead 100644 +index 739138504..46c0eead5 100644 --- a/examples-testing/examples/webgpu_tsl_galaxy.ts +++ b/examples-testing/examples/webgpu_tsl_galaxy.ts @@ -5,7 +5,7 @@ import { Inspector } from 'three/addons/inspector/Inspector.js'; @@ -21002,7 +21105,7 @@ index 73913850..46c0eead 100644 gui.add(size, 'value', 0, 1, 0.001).name('size'); diff --git a/examples-testing/examples/webgpu_tsl_graph.ts b/examples-testing/examples/webgpu_tsl_graph.ts -index cdde58a9..606093a3 100644 +index cdde58a9a..606093a39 100644 --- a/examples-testing/examples/webgpu_tsl_graph.ts +++ b/examples-testing/examples/webgpu_tsl_graph.ts @@ -11,9 +11,9 @@ import { Inspector } from 'three/addons/inspector/Inspector.js'; @@ -21150,7 +21253,7 @@ index cdde58a9..606093a3 100644 }); diff --git a/examples-testing/examples/webgpu_tsl_halftone.ts b/examples-testing/examples/webgpu_tsl_halftone.ts -index 682e1df6..6aef3f0d 100644 +index 682e1df64..6aef3f0dd 100644 --- a/examples-testing/examples/webgpu_tsl_halftone.ts +++ b/examples-testing/examples/webgpu_tsl_halftone.ts @@ -6,7 +6,36 @@ import { Inspector } from 'three/addons/inspector/Inspector.js'; @@ -21307,7 +21410,7 @@ index 682e1df6..6aef3f0d 100644 renderer.render(scene, camera); } diff --git a/examples-testing/examples/webgpu_tsl_interoperability.ts b/examples-testing/examples/webgpu_tsl_interoperability.ts -index e57c66ee..4ca0fc90 100644 +index e57c66eeb..4ca0fc90d 100644 --- a/examples-testing/examples/webgpu_tsl_interoperability.ts +++ b/examples-testing/examples/webgpu_tsl_interoperability.ts @@ -23,13 +23,13 @@ import WebGPU from 'three/addons/capabilities/WebGPU.js'; @@ -21345,7 +21448,7 @@ index e57c66ee..4ca0fc90 100644 gui.add(cellSizeUniform, 'value', 6, 50, 1).name('Cell Size'); gui.add(cellOffsetUniform, 'value', 0, 1, 0.1).name('Cell Offset'); diff --git a/examples-testing/examples/webgpu_tsl_procedural_terrain.ts b/examples-testing/examples/webgpu_tsl_procedural_terrain.ts -index 5630137b..ebffc04e 100644 +index 5630137b8..ebffc04e0 100644 --- a/examples-testing/examples/webgpu_tsl_procedural_terrain.ts +++ b/examples-testing/examples/webgpu_tsl_procedural_terrain.ts @@ -22,7 +22,26 @@ import { Inspector } from 'three/addons/inspector/Inspector.js'; @@ -21431,7 +21534,7 @@ index 5630137b..ebffc04e 100644 drag.worldCoords.copy(intersect.point); } diff --git a/examples-testing/examples/webgpu_tsl_raging_sea.ts b/examples-testing/examples/webgpu_tsl_raging_sea.ts -index cb513d07..b403ba07 100644 +index cb513d075..b403ba076 100644 --- a/examples-testing/examples/webgpu_tsl_raging_sea.ts +++ b/examples-testing/examples/webgpu_tsl_raging_sea.ts @@ -19,7 +19,7 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -21462,7 +21565,7 @@ index cb513d07..b403ba07 100644 gui.addColor({ color: material.color.getHex(THREE.SRGBColorSpace) }, 'color') .name('color') diff --git a/examples-testing/examples/webgpu_tsl_vfx_flames.ts b/examples-testing/examples/webgpu_tsl_vfx_flames.ts -index f1e42344..34df22c9 100644 +index f1e423444..34df22c9f 100644 --- a/examples-testing/examples/webgpu_tsl_vfx_flames.ts +++ b/examples-testing/examples/webgpu_tsl_vfx_flames.ts @@ -20,7 +20,15 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -21497,7 +21600,7 @@ index f1e42344..34df22c9 100644 gradient.colors = ['#090033', '#5f1f93', '#e02e96', '#ffbd80', '#fff0db']; diff --git a/examples-testing/examples/webgpu_tsl_vfx_linkedparticles.ts b/examples-testing/examples/webgpu_tsl_vfx_linkedparticles.ts -index e220c164..f9b0a23f 100644 +index e220c164b..f9b0a23f4 100644 --- a/examples-testing/examples/webgpu_tsl_vfx_linkedparticles.ts +++ b/examples-testing/examples/webgpu_tsl_vfx_linkedparticles.ts @@ -37,10 +37,16 @@ import { Inspector } from 'three/addons/inspector/Inspector.js'; @@ -21548,7 +21651,7 @@ index e220c164..f9b0a23f 100644 screenPointer.y = -(e.clientY / window.innerHeight) * 2 + 1; } diff --git a/examples-testing/examples/webgpu_tsl_vfx_tornado.ts b/examples-testing/examples/webgpu_tsl_vfx_tornado.ts -index 625a9d20..0509e361 100644 +index 625a9d209..0509e361d 100644 --- a/examples-testing/examples/webgpu_tsl_vfx_tornado.ts +++ b/examples-testing/examples/webgpu_tsl_vfx_tornado.ts @@ -25,7 +25,11 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -21648,7 +21751,7 @@ index 625a9d20..0509e361 100644 gui.addColor({ color: emissiveColor.value.getHexString(THREE.SRGBColorSpace) }, 'color') .onChange(value => emissiveColor.value.set(value)) diff --git a/examples-testing/examples/webgpu_tsl_wood.ts b/examples-testing/examples/webgpu_tsl_wood.ts -index 5a4e0650..f42fd5a7 100644 +index 5a4e06502..f42fd5a76 100644 --- a/examples-testing/examples/webgpu_tsl_wood.ts +++ b/examples-testing/examples/webgpu_tsl_wood.ts @@ -1,22 +1,30 @@ @@ -21801,7 +21904,7 @@ index 5a4e0650..f42fd5a7 100644 createLabel('custom', font, text_mat, getGridPosition(Math.round(WoodGenuses.length / 2 - 1), 5)); diff --git a/examples-testing/examples/webgpu_video_panorama.ts b/examples-testing/examples/webgpu_video_panorama.ts -index f52b15ff..78885bec 100644 +index f52b15ffe..78885bec4 100644 --- a/examples-testing/examples/webgpu_video_panorama.ts +++ b/examples-testing/examples/webgpu_video_panorama.ts @@ -1,6 +1,6 @@ @@ -21849,7 +21952,7 @@ index f52b15ff..78885bec 100644 lon = (onPointerDownPointerX - event.clientX) * 0.1 + onPointerDownLon; lat = (onPointerDownPointerY - event.clientY) * 0.1 + onPointerDownLat; diff --git a/examples-testing/examples/webgpu_volume_caustics.ts b/examples-testing/examples/webgpu_volume_caustics.ts -index 0c85390d..23cd2ae0 100644 +index 0c85390dd..23cd2ae09 100644 --- a/examples-testing/examples/webgpu_volume_caustics.ts +++ b/examples-testing/examples/webgpu_volume_caustics.ts @@ -31,9 +31,9 @@ import { Inspector } from 'three/addons/inspector/Inspector.js'; @@ -21899,7 +22002,7 @@ index 0c85390d..23cd2ae0 100644 let density = sampleGrain(1); diff --git a/examples-testing/examples/webgpu_volume_cloud.ts b/examples-testing/examples/webgpu_volume_cloud.ts -index 1d46c354..5f4fdfa3 100644 +index 1d46c3543..5f4fdfa36 100644 --- a/examples-testing/examples/webgpu_volume_cloud.ts +++ b/examples-testing/examples/webgpu_volume_cloud.ts @@ -8,8 +8,8 @@ import { ImprovedNoise } from 'three/addons/math/ImprovedNoise.js'; @@ -21953,7 +22056,7 @@ index 1d46c354..5f4fdfa3 100644 gui.add(opacity, 'value', 0, 1, 0.01).name('opacity'); gui.add(range, 'value', 0, 1, 0.01).name('range'); diff --git a/examples-testing/examples/webgpu_volume_lighting.ts b/examples-testing/examples/webgpu_volume_lighting.ts -index 38099c6a..c9e0380f 100644 +index 38099c6a5..c9e0380fc 100644 --- a/examples-testing/examples/webgpu_volume_lighting.ts +++ b/examples-testing/examples/webgpu_volume_lighting.ts @@ -10,9 +10,9 @@ import { TeapotGeometry } from 'three/addons/geometries/TeapotGeometry.js'; @@ -21994,7 +22097,7 @@ index 38099c6a..c9e0380f 100644 const rayMarching = gui.addFolder('Ray Marching'); rayMarching.add(params, 'resolution', 0.1, 1).onChange(resolution => { diff --git a/examples-testing/examples/webgpu_volume_lighting_rectarea.ts b/examples-testing/examples/webgpu_volume_lighting_rectarea.ts -index 47c82841..73084cc4 100644 +index 47c82841c..73084cc4c 100644 --- a/examples-testing/examples/webgpu_volume_lighting_rectarea.ts +++ b/examples-testing/examples/webgpu_volume_lighting_rectarea.ts @@ -10,11 +10,11 @@ import { Inspector } from 'three/addons/inspector/Inspector.js'; @@ -22048,7 +22151,7 @@ index 47c82841..73084cc4 100644 const rayMarching = gui.addFolder('Ray Marching'); rayMarching.add(params, 'resolution', 0.1, 1).onChange(resolution => { diff --git a/examples-testing/examples/webgpu_volume_lighting_traa.ts b/examples-testing/examples/webgpu_volume_lighting_traa.ts -index c5cc55e7..7ae7f370 100644 +index c5cc55e71..7ae7f3702 100644 --- a/examples-testing/examples/webgpu_volume_lighting_traa.ts +++ b/examples-testing/examples/webgpu_volume_lighting_traa.ts @@ -26,7 +26,7 @@ import { TeapotGeometry } from 'three/addons/geometries/TeapotGeometry.js'; @@ -22105,7 +22208,7 @@ index c5cc55e7..7ae7f370 100644 gui.add(params, 'animated'); gui.add(params, 'traa').name('TRAA').onChange(updatePostProcessing); diff --git a/examples-testing/examples/webgpu_volume_perlin.ts b/examples-testing/examples/webgpu_volume_perlin.ts -index fb6f2bbd..50d3e036 100644 +index fb6f2bbd5..50d3e0361 100644 --- a/examples-testing/examples/webgpu_volume_perlin.ts +++ b/examples-testing/examples/webgpu_volume_perlin.ts @@ -8,8 +8,8 @@ import { ImprovedNoise } from 'three/addons/math/ImprovedNoise.js'; @@ -22178,7 +22281,7 @@ index fb6f2bbd..50d3e036 100644 gui.add(steps, 'value', 0, 300, 1).name('steps'); diff --git a/examples-testing/examples/webgpu_water.ts b/examples-testing/examples/webgpu_water.ts -index 88a9e5f6..7d8c8b73 100644 +index 88a9e5f60..7d8c8b731 100644 --- a/examples-testing/examples/webgpu_water.ts +++ b/examples-testing/examples/webgpu_water.ts @@ -9,11 +9,16 @@ import { Inspector } from 'three/addons/inspector/Inspector.js'; @@ -22212,7 +22315,7 @@ index 88a9e5f6..7d8c8b73 100644 gui.addColor(params, 'color').onChange(function (value) { waterNode.color.value.set(value); diff --git a/examples-testing/examples/webgpu_xr_cubes.ts b/examples-testing/examples/webgpu_xr_cubes.ts -index d164eb84..71b4dcc3 100644 +index d164eb843..71b4dcc37 100644 --- a/examples-testing/examples/webgpu_xr_cubes.ts +++ b/examples-testing/examples/webgpu_xr_cubes.ts @@ -4,16 +4,20 @@ import { BoxLineGeometry } from 'three/addons/geometries/BoxLineGeometry.js'; @@ -22291,7 +22394,7 @@ index d164eb84..71b4dcc3 100644 INTERSECTED.material.emissive.setHex(0xff0000); } diff --git a/examples-testing/examples/webgpu_xr_native_layers.ts b/examples-testing/examples/webgpu_xr_native_layers.ts -index cd7f3fb2..70371da6 100644 +index cd7f3fb2d..70371da60 100644 --- a/examples-testing/examples/webgpu_xr_native_layers.ts +++ b/examples-testing/examples/webgpu_xr_native_layers.ts @@ -15,11 +15,11 @@ import { @@ -22573,7 +22676,7 @@ index cd7f3fb2..70371da6 100644 // const delta = timer.getDelta() * 0.8; diff --git a/examples-testing/examples/webgpu_xr_rollercoaster.ts b/examples-testing/examples/webgpu_xr_rollercoaster.ts -index 6035d068..39b9b8fc 100644 +index 6035d0687..39b9b8fce 100644 --- a/examples-testing/examples/webgpu_xr_rollercoaster.ts +++ b/examples-testing/examples/webgpu_xr_rollercoaster.ts @@ -9,7 +9,7 @@ import { @@ -22613,7 +22716,7 @@ index 6035d068..39b9b8fc 100644 // diff --git a/examples-testing/examples/webxr_ar_cones.ts b/examples-testing/examples/webxr_ar_cones.ts -index 95eb3439..0e641cdb 100644 +index 95eb34393..0e641cdb3 100644 --- a/examples-testing/examples/webxr_ar_cones.ts +++ b/examples-testing/examples/webxr_ar_cones.ts @@ -1,8 +1,8 @@ @@ -22628,7 +22731,7 @@ index 95eb3439..0e641cdb 100644 init(); diff --git a/examples-testing/examples/webxr_ar_hittest.ts b/examples-testing/examples/webxr_ar_hittest.ts -index 009b4b97..ffb71130 100644 +index 009b4b976..ffb711306 100644 --- a/examples-testing/examples/webxr_ar_hittest.ts +++ b/examples-testing/examples/webxr_ar_hittest.ts @@ -1,13 +1,13 @@ @@ -22678,7 +22781,7 @@ index 009b4b97..ffb71130 100644 reticle.visible = false; } diff --git a/examples-testing/examples/webxr_ar_lighting.ts b/examples-testing/examples/webxr_ar_lighting.ts -index 10f49f0b..00e35fbe 100644 +index 10f49f0bf..00e35fbef 100644 --- a/examples-testing/examples/webxr_ar_lighting.ts +++ b/examples-testing/examples/webxr_ar_lighting.ts @@ -3,9 +3,9 @@ import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; @@ -22695,7 +22798,7 @@ index 10f49f0b..00e35fbe 100644 init(); diff --git a/examples-testing/examples/webxr_vr_handinput.ts b/examples-testing/examples/webxr_vr_handinput.ts -index d746e458..af438f0f 100644 +index d746e4582..af438f0f6 100644 --- a/examples-testing/examples/webxr_vr_handinput.ts +++ b/examples-testing/examples/webxr_vr_handinput.ts @@ -4,13 +4,13 @@ import { VRButton } from 'three/addons/webxr/VRButton.js'; @@ -22719,7 +22822,7 @@ index d746e458..af438f0f 100644 init(); diff --git a/examples-testing/examples/webxr_vr_panorama.ts b/examples-testing/examples/webxr_vr_panorama.ts -index 535e1c93..ab2e4ee0 100644 +index 535e1c937..ab2e4ee0d 100644 --- a/examples-testing/examples/webxr_vr_panorama.ts +++ b/examples-testing/examples/webxr_vr_panorama.ts @@ -1,9 +1,9 @@ @@ -22756,7 +22859,7 @@ index 535e1c93..ab2e4ee0 100644 canvas.width = tileWidth; context.drawImage(imageObj, tileWidth * i, 0, tileWidth, tileWidth, 0, 0, tileWidth, tileWidth); diff --git a/examples-testing/examples/webxr_vr_panorama_depth.ts b/examples-testing/examples/webxr_vr_panorama_depth.ts -index ea3d7619..a195db61 100644 +index ea3d76199..a195db610 100644 --- a/examples-testing/examples/webxr_vr_panorama_depth.ts +++ b/examples-testing/examples/webxr_vr_panorama_depth.ts @@ -1,12 +1,16 @@ @@ -22779,7 +22882,7 @@ index ea3d7619..a195db61 100644 timer = new THREE.Timer(); timer.connect(document); diff --git a/examples-testing/examples/webxr_vr_rollercoaster.ts b/examples-testing/examples/webxr_vr_rollercoaster.ts -index b8c35a9e..ee5f02da 100644 +index b8c35a9e3..ee5f02daf 100644 --- a/examples-testing/examples/webxr_vr_rollercoaster.ts +++ b/examples-testing/examples/webxr_vr_rollercoaster.ts @@ -9,7 +9,7 @@ import { @@ -22819,7 +22922,7 @@ index b8c35a9e..ee5f02da 100644 // diff --git a/examples-testing/examples/webxr_vr_sandbox.ts b/examples-testing/examples/webxr_vr_sandbox.ts -index 19108d58..794121e2 100644 +index 19108d589..794121e2b 100644 --- a/examples-testing/examples/webxr_vr_sandbox.ts +++ b/examples-testing/examples/webxr_vr_sandbox.ts @@ -11,9 +11,9 @@ import { XRControllerModelFactory } from 'three/addons/webxr/XRControllerModelFa @@ -22856,7 +22959,7 @@ index 19108d58..794121e2 100644 + update(): void; } diff --git a/examples-testing/examples/webxr_vr_video.ts b/examples-testing/examples/webxr_vr_video.ts -index 50a99041..b5925eb1 100644 +index 50a990412..b5925eb18 100644 --- a/examples-testing/examples/webxr_vr_video.ts +++ b/examples-testing/examples/webxr_vr_video.ts @@ -1,12 +1,12 @@ @@ -22884,7 +22987,7 @@ index 50a99041..b5925eb1 100644 const texture = new THREE.VideoTexture(video); diff --git a/examples-testing/examples/webxr_xr_controls_transform.ts b/examples-testing/examples/webxr_xr_controls_transform.ts -index cd9b1290..3edf26cf 100644 +index cd9b1290d..3edf26cf8 100644 --- a/examples-testing/examples/webxr_xr_controls_transform.ts +++ b/examples-testing/examples/webxr_xr_controls_transform.ts @@ -4,15 +4,15 @@ import { XRButton } from 'three/addons/webxr/XRButton.js'; @@ -22928,7 +23031,7 @@ index cd9b1290..3edf26cf 100644 if (controller.userData.active === false) return; diff --git a/examples-testing/examples/webxr_xr_dragging_custom_depth.ts b/examples-testing/examples/webxr_xr_dragging_custom_depth.ts -index 2cd50ba4..b97f3eee 100644 +index 2cd50ba4c..b97f3eee2 100644 --- a/examples-testing/examples/webxr_xr_dragging_custom_depth.ts +++ b/examples-testing/examples/webxr_xr_dragging_custom_depth.ts @@ -3,18 +3,18 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; diff --git a/examples-testing/examples/css2d_label.ts b/examples-testing/examples/css2d_label.ts deleted file mode 100644 index 1b2889980..000000000 --- a/examples-testing/examples/css2d_label.ts +++ /dev/null @@ -1,189 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { CSS2DRenderer, CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let gui; - -let camera, scene, renderer, labelRenderer; - -const layers = { - 'Toggle Name': function () { - camera.layers.toggle(0); - }, - 'Toggle Mass': function () { - camera.layers.toggle(1); - }, - 'Enable All': function () { - camera.layers.enableAll(); - }, - - 'Disable All': function () { - camera.layers.disableAll(); - }, -}; - -const timer = new THREE.Timer(); -timer.connect(document); -const textureLoader = new THREE.TextureLoader(); - -let moon; - -init(); -animate(); - -function init() { - const EARTH_RADIUS = 1; - const MOON_RADIUS = 0.27; - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 200); - camera.position.set(10, 5, 20); - camera.layers.enableAll(); - - scene = new THREE.Scene(); - - const dirLight = new THREE.DirectionalLight(0xffffff, 3); - dirLight.position.set(0, 0, 1); - dirLight.layers.enableAll(); - scene.add(dirLight); - - const axesHelper = new THREE.AxesHelper(5); - axesHelper.layers.enableAll(); - scene.add(axesHelper); - - // - - const earthGeometry = new THREE.SphereGeometry(EARTH_RADIUS, 16, 16); - const earthMaterial = new THREE.MeshPhongMaterial({ - specular: 0x333333, - shininess: 5, - map: textureLoader.load('textures/planets/earth_atmos_2048.jpg'), - specularMap: textureLoader.load('textures/planets/earth_specular_2048.jpg'), - normalMap: textureLoader.load('textures/planets/earth_normal_2048.jpg'), - normalScale: new THREE.Vector2(0.85, 0.85), - }); - earthMaterial.map.colorSpace = THREE.SRGBColorSpace; - const earth = new THREE.Mesh(earthGeometry, earthMaterial); - scene.add(earth); - - const moonGeometry = new THREE.SphereGeometry(MOON_RADIUS, 16, 16); - const moonMaterial = new THREE.MeshPhongMaterial({ - shininess: 5, - map: textureLoader.load('textures/planets/moon_1024.jpg'), - }); - moonMaterial.map.colorSpace = THREE.SRGBColorSpace; - moon = new THREE.Mesh(moonGeometry, moonMaterial); - scene.add(moon); - - // - - earth.layers.enableAll(); - moon.layers.enableAll(); - - const earthDiv = document.createElement('div'); - earthDiv.className = 'label'; - earthDiv.textContent = 'Earth'; - earthDiv.style.backgroundColor = 'transparent'; - - const earthLabel = new CSS2DObject(earthDiv); - earthLabel.position.set(1.5 * EARTH_RADIUS, 0, 0); - earthLabel.center.set(0, 1); - earth.add(earthLabel); - earthLabel.layers.set(0); - - const earthMassDiv = document.createElement('div'); - earthMassDiv.className = 'label'; - earthMassDiv.textContent = '5.97237e24 kg'; - earthMassDiv.style.backgroundColor = 'transparent'; - - const earthMassLabel = new CSS2DObject(earthMassDiv); - earthMassLabel.position.set(1.5 * EARTH_RADIUS, 0, 0); - earthMassLabel.center.set(0, 0); - earth.add(earthMassLabel); - earthMassLabel.layers.set(1); - - const moonDiv = document.createElement('div'); - moonDiv.className = 'label'; - moonDiv.textContent = 'Moon'; - moonDiv.style.backgroundColor = 'transparent'; - - const moonLabel = new CSS2DObject(moonDiv); - moonLabel.position.set(1.5 * MOON_RADIUS, 0, 0); - moonLabel.center.set(0, 1); - moon.add(moonLabel); - moonLabel.layers.set(0); - - const moonMassDiv = document.createElement('div'); - moonMassDiv.className = 'label'; - moonMassDiv.textContent = '7.342e22 kg'; - moonMassDiv.style.backgroundColor = 'transparent'; - - const moonMassLabel = new CSS2DObject(moonMassDiv); - moonMassLabel.position.set(1.5 * MOON_RADIUS, 0, 0); - moonMassLabel.center.set(0, 0); - moon.add(moonMassLabel); - moonMassLabel.layers.set(1); - - // - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - document.body.appendChild(renderer.domElement); - - labelRenderer = new CSS2DRenderer(); - labelRenderer.setSize(window.innerWidth, window.innerHeight); - labelRenderer.domElement.style.position = 'absolute'; - labelRenderer.domElement.style.top = '0px'; - document.body.appendChild(labelRenderer.domElement); - - const controls = new OrbitControls(camera, labelRenderer.domElement); - controls.minDistance = 5; - controls.maxDistance = 100; - - // - - window.addEventListener('resize', onWindowResize); - - initGui(); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - labelRenderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - requestAnimationFrame(animate); - - const elapsed = timer.getElapsed(); - - moon.position.set(Math.sin(elapsed) * 5, 0, Math.cos(elapsed) * 5); - - renderer.render(scene, camera); - labelRenderer.render(scene, camera); -} - -// - -function initGui() { - gui = new GUI(); - - gui.title('Camera Layers'); - - gui.add(layers, 'Toggle Name'); - gui.add(layers, 'Toggle Mass'); - gui.add(layers, 'Enable All'); - gui.add(layers, 'Disable All'); - - gui.open(); -} diff --git a/examples-testing/examples/css3d_mixed.ts b/examples-testing/examples/css3d_mixed.ts deleted file mode 100644 index b526e73e5..000000000 --- a/examples-testing/examples/css3d_mixed.ts +++ /dev/null @@ -1,138 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { CSS3DRenderer, CSS3DObject } from 'three/addons/renderers/CSS3DRenderer.js'; - -let camera, scene, rendererCSS3D, rendererWebGL; -let controls; - -init(); - -function init() { - const controlsDomElement = document.createElement('div'); - controlsDomElement.style.position = 'absolute'; - controlsDomElement.style.top = '0'; - controlsDomElement.style.width = '100%'; - controlsDomElement.style.height = '100%'; - document.body.appendChild(controlsDomElement); - - rendererCSS3D = new CSS3DRenderer(); - rendererCSS3D.setSize(window.innerWidth, window.innerHeight); - document.body.appendChild(rendererCSS3D.domElement); - - rendererWebGL = new THREE.WebGLRenderer({ antialias: true, alpha: true }); - rendererWebGL.domElement.style.position = 'absolute'; - rendererWebGL.domElement.style.top = '0'; - rendererWebGL.domElement.style.pointerEvents = 'none'; - rendererWebGL.setPixelRatio(window.devicePixelRatio); - rendererWebGL.setSize(window.innerWidth, window.innerHeight); - rendererWebGL.toneMapping = THREE.NeutralToneMapping; - rendererWebGL.setAnimationLoop(animate); - document.body.appendChild(rendererWebGL.domElement); - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000); - camera.position.set(-1000, 500, 1500); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xf0f0f0); - - // Add room - const roomGeometry = new THREE.EdgesGeometry(new THREE.BoxGeometry(4000, 2000, 4000, 10, 5, 10)); - const roomMaterial = new THREE.LineBasicMaterial({ color: 0x000000, opacity: 0.2, transparent: true }); - const room = new THREE.LineSegments(roomGeometry, roomMaterial); - scene.add(room); - - // Add light - const hemisphereLight = new THREE.HemisphereLight(0xffffff, 0x444444, 4); - hemisphereLight.position.set(-25, 100, 50); - scene.add(hemisphereLight); - - // Add cutout mesh - const geometry = new THREE.PlaneGeometry(1024, 768); - const material = new THREE.MeshBasicMaterial({ - color: 0xff0000, - blending: THREE.NoBlending, - opacity: 0, - premultipliedAlpha: true, - }); - const mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - // Add frame - const frame = buildFrame(1024, 768, 50); - scene.add(frame); - - // Add CSS3D element - const iframe = document.createElement('iframe'); - iframe.style.width = '1028px'; - iframe.style.height = '768px'; - iframe.style.border = '0px'; - iframe.style.backfaceVisibility = 'hidden'; - iframe.src = './#webgl_animation_keyframes'; - scene.add(new CSS3DObject(iframe)); - - // Add controls - controls = new OrbitControls(camera); - controls.connect(controlsDomElement); - controls.addEventListener('start', () => (iframe.style.pointerEvents = 'none')); - controls.addEventListener('end', () => (iframe.style.pointerEvents = 'auto')); - controls.enableDamping = true; - - window.addEventListener('resize', onWindowResize); -} - -function buildFrame(width, height, thickness) { - const group = new THREE.Group(); - const material = new THREE.MeshStandardMaterial({ color: 0x2200ff }); - - // Create the frame border - const outerShape = new THREE.Shape(); - outerShape.moveTo(-(width / 2 + thickness), -(height / 2 + thickness)); - outerShape.lineTo(width / 2 + thickness, -(height / 2 + thickness)); - outerShape.lineTo(width / 2 + thickness, height / 2 + thickness); - outerShape.lineTo(-(width / 2 + thickness), height / 2 + thickness); - outerShape.lineTo(-(width / 2 + thickness), -(height / 2 + thickness)); - - // Create inner rectangle (hole) - const innerHole = new THREE.Path(); - innerHole.moveTo(-width / 2, -height / 2); - innerHole.lineTo(width / 2, -height / 2); - innerHole.lineTo(width / 2, height / 2); - innerHole.lineTo(-width / 2, height / 2); - innerHole.lineTo(-width / 2, -height / 2); - - outerShape.holes.push(innerHole); - - const frameGeometry = new THREE.ExtrudeGeometry(outerShape, { - depth: thickness, - bevelEnabled: false, - }); - - const frameMesh = new THREE.Mesh(frameGeometry, material); - frameMesh.position.z = -thickness / 2; - group.add(frameMesh); - - // Add back plane - const backGeometry = new THREE.PlaneGeometry(width + thickness * 2, height + thickness * 2); - const backMesh = new THREE.Mesh(backGeometry, material); - backMesh.position.set(0, 0, -thickness / 2); - backMesh.rotation.y = Math.PI; - group.add(backMesh); - - return group; -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - rendererWebGL.setSize(window.innerWidth, window.innerHeight); - rendererCSS3D.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - controls.update(); - - rendererWebGL.render(scene, camera); - rendererCSS3D.render(scene, camera); -} diff --git a/examples-testing/examples/css3d_molecules.ts b/examples-testing/examples/css3d_molecules.ts deleted file mode 100644 index 538472607..000000000 --- a/examples-testing/examples/css3d_molecules.ts +++ /dev/null @@ -1,353 +0,0 @@ -import * as THREE from 'three'; - -import { TrackballControls } from 'three/addons/controls/TrackballControls.js'; -import { PDBLoader } from 'three/addons/loaders/PDBLoader.js'; -import { CSS3DRenderer, CSS3DObject, CSS3DSprite } from 'three/addons/renderers/CSS3DRenderer.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let camera, scene, renderer; -let controls; -let root; - -const objects = []; -const tmpVec1 = new THREE.Vector3(); -const tmpVec2 = new THREE.Vector3(); -const tmpVec3 = new THREE.Vector3(); -const tmpVec4 = new THREE.Vector3(); -const offset = new THREE.Vector3(); - -const VIZ_TYPE = { - Atoms: 0, - Bonds: 1, - 'Atoms + Bonds': 2, -}; - -const MOLECULES = { - Ethanol: 'ethanol.pdb', - Aspirin: 'aspirin.pdb', - Caffeine: 'caffeine.pdb', - Nicotine: 'nicotine.pdb', - LSD: 'lsd.pdb', - Cocaine: 'cocaine.pdb', - Cholesterol: 'cholesterol.pdb', - Lycopene: 'lycopene.pdb', - Glucose: 'glucose.pdb', - 'Aluminium oxide': 'Al2O3.pdb', - Cubane: 'cubane.pdb', - Copper: 'cu.pdb', - Fluorite: 'caf2.pdb', - Salt: 'nacl.pdb', - 'YBCO superconductor': 'ybco.pdb', - Buckyball: 'buckyball.pdb', - // 'Diamond': 'diamond.pdb', - Graphite: 'graphite.pdb', -}; - -const params = { - vizType: 2, - molecule: 'caffeine.pdb', -}; - -const loader = new PDBLoader(); -const colorSpriteMap = {}; -const baseSprite = document.createElement('img'); - -init(); -animate(); - -function init() { - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 5000); - camera.position.z = 1000; - - scene = new THREE.Scene(); - - root = new THREE.Object3D(); - scene.add(root); - - // - - renderer = new CSS3DRenderer(); - renderer.setSize(window.innerWidth, window.innerHeight); - document.getElementById('container').appendChild(renderer.domElement); - - // - - controls = new TrackballControls(camera, renderer.domElement); - controls.rotateSpeed = 0.5; - - // - - baseSprite.onload = function () { - loadMolecule(params.molecule); - }; - - baseSprite.src = 'textures/sprites/ball.png'; - - // - - window.addEventListener('resize', onWindowResize); - - // - - const gui = new GUI(); - - gui.add(params, 'vizType', VIZ_TYPE).onChange(changeVizType); - gui.add(params, 'molecule', MOLECULES).onChange(loadMolecule); - gui.open(); -} - -function changeVizType(value) { - if (value === 0) showAtoms(); - else if (value === 1) showBonds(); - else showAtomsBonds(); -} - -// - -function showAtoms() { - for (let i = 0; i < objects.length; i++) { - const object = objects[i]; - - if (object instanceof CSS3DSprite) { - object.element.style.display = ''; - object.visible = true; - } else { - object.element.style.display = 'none'; - object.visible = false; - } - } -} - -function showBonds() { - for (let i = 0; i < objects.length; i++) { - const object = objects[i]; - - if (object instanceof CSS3DSprite) { - object.element.style.display = 'none'; - object.visible = false; - } else { - object.element.style.display = ''; - object.element.style.height = object.userData.bondLengthFull; - object.visible = true; - } - } -} - -function showAtomsBonds() { - for (let i = 0; i < objects.length; i++) { - const object = objects[i]; - - object.element.style.display = ''; - object.visible = true; - - if (!(object instanceof CSS3DSprite)) { - object.element.style.height = object.userData.bondLengthShort; - } - } -} - -// - -function colorify(ctx, width, height, color) { - const r = color.r, - g = color.g, - b = color.b; - - const imageData = ctx.getImageData(0, 0, width, height); - const data = imageData.data; - - for (let i = 0, l = data.length; i < l; i += 4) { - data[i + 0] *= r; - data[i + 1] *= g; - data[i + 2] *= b; - } - - ctx.putImageData(imageData, 0, 0); -} - -function imageToCanvas(image) { - const width = image.width; - const height = image.height; - - const canvas = document.createElement('canvas'); - - canvas.width = width; - canvas.height = height; - - const context = canvas.getContext('2d'); - context.drawImage(image, 0, 0, width, height); - - return canvas; -} - -// - -function loadMolecule(model) { - const url = 'models/pdb/' + model; - - for (let i = 0; i < objects.length; i++) { - const object = objects[i]; - object.parent.remove(object); - } - - objects.length = 0; - - loader.load(url, function (pdb) { - const geometryAtoms = pdb.geometryAtoms; - const geometryBonds = pdb.geometryBonds; - const json = pdb.json; - - geometryAtoms.computeBoundingBox(); - geometryAtoms.boundingBox.getCenter(offset).negate(); - - geometryAtoms.translate(offset.x, offset.y, offset.z); - geometryBonds.translate(offset.x, offset.y, offset.z); - - const positionAtoms = geometryAtoms.getAttribute('position'); - const colorAtoms = geometryAtoms.getAttribute('color'); - - const position = new THREE.Vector3(); - const color = new THREE.Color(); - - for (let i = 0; i < positionAtoms.count; i++) { - position.fromBufferAttribute(positionAtoms, i); - color.fromBufferAttribute(colorAtoms, i); - - const atomJSON = json.atoms[i]; - const element = atomJSON[4]; - - if (!colorSpriteMap[element]) { - const canvas = imageToCanvas(baseSprite); - const context = canvas.getContext('2d'); - - colorify(context, canvas.width, canvas.height, color); - - const dataUrl = canvas.toDataURL(); - - colorSpriteMap[element] = dataUrl; - } - - const colorSprite = colorSpriteMap[element]; - - const atom = document.createElement('img'); - atom.src = colorSprite; - - const object = new CSS3DSprite(atom); - object.position.copy(position); - object.position.multiplyScalar(75); - - object.matrixAutoUpdate = false; - object.updateMatrix(); - - root.add(object); - - objects.push(object); - } - - const positionBonds = geometryBonds.getAttribute('position'); - - const start = new THREE.Vector3(); - const end = new THREE.Vector3(); - - for (let i = 0; i < positionBonds.count; i += 2) { - start.fromBufferAttribute(positionBonds, i); - end.fromBufferAttribute(positionBonds, i + 1); - - start.multiplyScalar(75); - end.multiplyScalar(75); - - tmpVec1.subVectors(end, start); - const bondLength = tmpVec1.length() - 50; - - // - - let bond = document.createElement('div'); - bond.className = 'bond'; - bond.style.height = bondLength + 'px'; - - let object = new CSS3DObject(bond); - object.position.copy(start); - object.position.lerp(end, 0.5); - - object.userData.bondLengthShort = bondLength + 'px'; - object.userData.bondLengthFull = bondLength + 55 + 'px'; - - // - - const axis = tmpVec2.set(0, 1, 0).cross(tmpVec1); - const radians = Math.acos(tmpVec3.set(0, 1, 0).dot(tmpVec4.copy(tmpVec1).normalize())); - - const objMatrix = new THREE.Matrix4().makeRotationAxis(axis.normalize(), radians); - object.matrix.copy(objMatrix); - object.quaternion.setFromRotationMatrix(object.matrix); - - object.matrixAutoUpdate = false; - object.updateMatrix(); - - root.add(object); - - objects.push(object); - - // - - const joint = new THREE.Object3D(); - joint.position.copy(start); - joint.position.lerp(end, 0.5); - - joint.matrix.copy(objMatrix); - joint.quaternion.setFromRotationMatrix(joint.matrix); - - joint.matrixAutoUpdate = false; - joint.updateMatrix(); - - bond = document.createElement('div'); - bond.className = 'bond'; - bond.style.height = bondLength + 'px'; - - object = new CSS3DObject(bond); - object.rotation.y = Math.PI / 2; - - object.matrixAutoUpdate = false; - object.updateMatrix(); - - object.userData.bondLengthShort = bondLength + 'px'; - object.userData.bondLengthFull = bondLength + 55 + 'px'; - - object.userData.joint = joint; - - joint.add(object); - root.add(joint); - - objects.push(object); - } - - //console.log( "CSS3DObjects:", objects.length ); - - changeVizType(params.vizType); - }); -} - -// - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - requestAnimationFrame(animate); - controls.update(); - - const time = Date.now() * 0.0004; - - root.rotation.x = time; - root.rotation.y = time * 0.7; - - render(); -} - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/css3d_orthographic.ts b/examples-testing/examples/css3d_orthographic.ts deleted file mode 100644 index 4aabbed08..000000000 --- a/examples-testing/examples/css3d_orthographic.ts +++ /dev/null @@ -1,208 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { CSS3DRenderer, CSS3DObject } from 'three/addons/renderers/CSS3DRenderer.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let camera, scene, renderer; - -let scene2, renderer2; - -const frustumSize = 500; - -init(); -animate(); - -function init() { - const aspect = window.innerWidth / window.innerHeight; - camera = new THREE.OrthographicCamera( - (frustumSize * aspect) / -2, - (frustumSize * aspect) / 2, - frustumSize / 2, - frustumSize / -2, - 1, - 1000, - ); - - camera.position.set(-200, 200, 200); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xf0f0f0); - - scene2 = new THREE.Scene(); - - const material = new THREE.MeshBasicMaterial({ - color: 0x000000, - wireframe: true, - wireframeLinewidth: 1, - side: THREE.DoubleSide, - }); - - // left - createPlane( - 100, - 100, - 'chocolate', - new THREE.Vector3(-50, 0, 0), - new THREE.Euler(0, -90 * THREE.MathUtils.DEG2RAD, 0), - ); - // right - createPlane(100, 100, 'saddlebrown', new THREE.Vector3(0, 0, 50), new THREE.Euler(0, 0, 0)); - // top - createPlane( - 100, - 100, - 'yellowgreen', - new THREE.Vector3(0, 50, 0), - new THREE.Euler(-90 * THREE.MathUtils.DEG2RAD, 0, 0), - ); - // bottom - createPlane( - 300, - 300, - 'seagreen', - new THREE.Vector3(0, -50, 0), - new THREE.Euler(-90 * THREE.MathUtils.DEG2RAD, 0, 0), - ); - - // - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - document.body.appendChild(renderer.domElement); - - renderer2 = new CSS3DRenderer(); - renderer2.setSize(window.innerWidth, window.innerHeight); - renderer2.domElement.style.position = 'absolute'; - renderer2.domElement.style.top = 0; - document.body.appendChild(renderer2.domElement); - - const controls = new OrbitControls(camera, renderer2.domElement); - controls.minZoom = 0.5; - controls.maxZoom = 2; - - function createPlane(width, height, cssColor, pos, rot) { - const element = document.createElement('div'); - element.style.width = width + 'px'; - element.style.height = height + 'px'; - element.style.opacity = 0.75; - element.style.background = cssColor; - - const object = new CSS3DObject(element); - object.position.copy(pos); - object.rotation.copy(rot); - scene2.add(object); - - const geometry = new THREE.PlaneGeometry(width, height); - const mesh = new THREE.Mesh(geometry, material); - mesh.position.copy(object.position); - mesh.rotation.copy(object.rotation); - scene.add(mesh); - } - - window.addEventListener('resize', onWindowResize); - createPanel(); -} - -function onWindowResize() { - const aspect = window.innerWidth / window.innerHeight; - - camera.left = (-frustumSize * aspect) / 2; - camera.right = (frustumSize * aspect) / 2; - camera.top = frustumSize / 2; - camera.bottom = -frustumSize / 2; - - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - renderer2.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - requestAnimationFrame(animate); - - renderer.render(scene, camera); - renderer2.render(scene2, camera); -} - -function createPanel() { - const panel = new GUI(); - const folder1 = panel.addFolder('camera setViewOffset').close(); - - const settings = { - setViewOffset() { - folder1.children[1].enable().setValue(window.innerWidth); - folder1.children[2].enable().setValue(window.innerHeight); - folder1.children[3].enable().setValue(0); - folder1.children[4].enable().setValue(0); - folder1.children[5].enable().setValue(window.innerWidth); - folder1.children[6].enable().setValue(window.innerHeight); - }, - fullWidth: 0, - fullHeight: 0, - offsetX: 0, - offsetY: 0, - width: 0, - height: 0, - clearViewOffset() { - folder1.children[1].setValue(0).disable(); - folder1.children[2].setValue(0).disable(); - folder1.children[3].setValue(0).disable(); - folder1.children[4].setValue(0).disable(); - folder1.children[5].setValue(0).disable(); - folder1.children[6].setValue(0).disable(); - camera.clearViewOffset(); - }, - }; - - folder1.add(settings, 'setViewOffset'); - folder1 - .add(settings, 'fullWidth', window.screen.width / 4, window.screen.width * 2, 1) - .onChange(val => updateCameraViewOffset({ fullWidth: val })) - .disable(); - folder1 - .add(settings, 'fullHeight', window.screen.height / 4, window.screen.height * 2, 1) - .onChange(val => updateCameraViewOffset({ fullHeight: val })) - .disable(); - folder1 - .add(settings, 'offsetX', 0, 256, 1) - .onChange(val => updateCameraViewOffset({ x: val })) - .disable(); - folder1 - .add(settings, 'offsetY', 0, 256, 1) - .onChange(val => updateCameraViewOffset({ y: val })) - .disable(); - folder1 - .add(settings, 'width', window.screen.width / 4, window.screen.width * 2, 1) - .onChange(val => updateCameraViewOffset({ width: val })) - .disable(); - folder1 - .add(settings, 'height', window.screen.height / 4, window.screen.height * 2, 1) - .onChange(val => updateCameraViewOffset({ height: val })) - .disable(); - folder1.add(settings, 'clearViewOffset'); -} - -function updateCameraViewOffset({ fullWidth, fullHeight, x, y, width, height }) { - if (!camera.view) { - camera.setViewOffset( - fullWidth || window.innerWidth, - fullHeight || window.innerHeight, - x || 0, - y || 0, - width || window.innerWidth, - height || window.innerHeight, - ); - } else { - camera.setViewOffset( - fullWidth || camera.view.fullWidth, - fullHeight || camera.view.fullHeight, - x || camera.view.offsetX, - y || camera.view.offsetY, - width || camera.view.width, - height || camera.view.height, - ); - } -} diff --git a/examples-testing/examples/css3d_periodictable.ts b/examples-testing/examples/css3d_periodictable.ts deleted file mode 100644 index e3a33f796..000000000 --- a/examples-testing/examples/css3d_periodictable.ts +++ /dev/null @@ -1,793 +0,0 @@ -import * as THREE from 'three'; - -import TWEEN from 'three/addons/libs/tween.module.js'; -import { TrackballControls } from 'three/addons/controls/TrackballControls.js'; -import { CSS3DRenderer, CSS3DObject } from 'three/addons/renderers/CSS3DRenderer.js'; - -const table = [ - 'H', - 'Hydrogen', - '1.00794', - 1, - 1, - 'He', - 'Helium', - '4.002602', - 18, - 1, - 'Li', - 'Lithium', - '6.941', - 1, - 2, - 'Be', - 'Beryllium', - '9.012182', - 2, - 2, - 'B', - 'Boron', - '10.811', - 13, - 2, - 'C', - 'Carbon', - '12.0107', - 14, - 2, - 'N', - 'Nitrogen', - '14.0067', - 15, - 2, - 'O', - 'Oxygen', - '15.9994', - 16, - 2, - 'F', - 'Fluorine', - '18.9984032', - 17, - 2, - 'Ne', - 'Neon', - '20.1797', - 18, - 2, - 'Na', - 'Sodium', - '22.98976...', - 1, - 3, - 'Mg', - 'Magnesium', - '24.305', - 2, - 3, - 'Al', - 'Aluminium', - '26.9815386', - 13, - 3, - 'Si', - 'Silicon', - '28.0855', - 14, - 3, - 'P', - 'Phosphorus', - '30.973762', - 15, - 3, - 'S', - 'Sulfur', - '32.065', - 16, - 3, - 'Cl', - 'Chlorine', - '35.453', - 17, - 3, - 'Ar', - 'Argon', - '39.948', - 18, - 3, - 'K', - 'Potassium', - '39.948', - 1, - 4, - 'Ca', - 'Calcium', - '40.078', - 2, - 4, - 'Sc', - 'Scandium', - '44.955912', - 3, - 4, - 'Ti', - 'Titanium', - '47.867', - 4, - 4, - 'V', - 'Vanadium', - '50.9415', - 5, - 4, - 'Cr', - 'Chromium', - '51.9961', - 6, - 4, - 'Mn', - 'Manganese', - '54.938045', - 7, - 4, - 'Fe', - 'Iron', - '55.845', - 8, - 4, - 'Co', - 'Cobalt', - '58.933195', - 9, - 4, - 'Ni', - 'Nickel', - '58.6934', - 10, - 4, - 'Cu', - 'Copper', - '63.546', - 11, - 4, - 'Zn', - 'Zinc', - '65.38', - 12, - 4, - 'Ga', - 'Gallium', - '69.723', - 13, - 4, - 'Ge', - 'Germanium', - '72.63', - 14, - 4, - 'As', - 'Arsenic', - '74.9216', - 15, - 4, - 'Se', - 'Selenium', - '78.96', - 16, - 4, - 'Br', - 'Bromine', - '79.904', - 17, - 4, - 'Kr', - 'Krypton', - '83.798', - 18, - 4, - 'Rb', - 'Rubidium', - '85.4678', - 1, - 5, - 'Sr', - 'Strontium', - '87.62', - 2, - 5, - 'Y', - 'Yttrium', - '88.90585', - 3, - 5, - 'Zr', - 'Zirconium', - '91.224', - 4, - 5, - 'Nb', - 'Niobium', - '92.90628', - 5, - 5, - 'Mo', - 'Molybdenum', - '95.96', - 6, - 5, - 'Tc', - 'Technetium', - '(98)', - 7, - 5, - 'Ru', - 'Ruthenium', - '101.07', - 8, - 5, - 'Rh', - 'Rhodium', - '102.9055', - 9, - 5, - 'Pd', - 'Palladium', - '106.42', - 10, - 5, - 'Ag', - 'Silver', - '107.8682', - 11, - 5, - 'Cd', - 'Cadmium', - '112.411', - 12, - 5, - 'In', - 'Indium', - '114.818', - 13, - 5, - 'Sn', - 'Tin', - '118.71', - 14, - 5, - 'Sb', - 'Antimony', - '121.76', - 15, - 5, - 'Te', - 'Tellurium', - '127.6', - 16, - 5, - 'I', - 'Iodine', - '126.90447', - 17, - 5, - 'Xe', - 'Xenon', - '131.293', - 18, - 5, - 'Cs', - 'Caesium', - '132.9054', - 1, - 6, - 'Ba', - 'Barium', - '132.9054', - 2, - 6, - 'La', - 'Lanthanum', - '138.90547', - 4, - 9, - 'Ce', - 'Cerium', - '140.116', - 5, - 9, - 'Pr', - 'Praseodymium', - '140.90765', - 6, - 9, - 'Nd', - 'Neodymium', - '144.242', - 7, - 9, - 'Pm', - 'Promethium', - '(145)', - 8, - 9, - 'Sm', - 'Samarium', - '150.36', - 9, - 9, - 'Eu', - 'Europium', - '151.964', - 10, - 9, - 'Gd', - 'Gadolinium', - '157.25', - 11, - 9, - 'Tb', - 'Terbium', - '158.92535', - 12, - 9, - 'Dy', - 'Dysprosium', - '162.5', - 13, - 9, - 'Ho', - 'Holmium', - '164.93032', - 14, - 9, - 'Er', - 'Erbium', - '167.259', - 15, - 9, - 'Tm', - 'Thulium', - '168.93421', - 16, - 9, - 'Yb', - 'Ytterbium', - '173.054', - 17, - 9, - 'Lu', - 'Lutetium', - '174.9668', - 18, - 9, - 'Hf', - 'Hafnium', - '178.49', - 4, - 6, - 'Ta', - 'Tantalum', - '180.94788', - 5, - 6, - 'W', - 'Tungsten', - '183.84', - 6, - 6, - 'Re', - 'Rhenium', - '186.207', - 7, - 6, - 'Os', - 'Osmium', - '190.23', - 8, - 6, - 'Ir', - 'Iridium', - '192.217', - 9, - 6, - 'Pt', - 'Platinum', - '195.084', - 10, - 6, - 'Au', - 'Gold', - '196.966569', - 11, - 6, - 'Hg', - 'Mercury', - '200.59', - 12, - 6, - 'Tl', - 'Thallium', - '204.3833', - 13, - 6, - 'Pb', - 'Lead', - '207.2', - 14, - 6, - 'Bi', - 'Bismuth', - '208.9804', - 15, - 6, - 'Po', - 'Polonium', - '(209)', - 16, - 6, - 'At', - 'Astatine', - '(210)', - 17, - 6, - 'Rn', - 'Radon', - '(222)', - 18, - 6, - 'Fr', - 'Francium', - '(223)', - 1, - 7, - 'Ra', - 'Radium', - '(226)', - 2, - 7, - 'Ac', - 'Actinium', - '(227)', - 4, - 10, - 'Th', - 'Thorium', - '232.03806', - 5, - 10, - 'Pa', - 'Protactinium', - '231.0588', - 6, - 10, - 'U', - 'Uranium', - '238.02891', - 7, - 10, - 'Np', - 'Neptunium', - '(237)', - 8, - 10, - 'Pu', - 'Plutonium', - '(244)', - 9, - 10, - 'Am', - 'Americium', - '(243)', - 10, - 10, - 'Cm', - 'Curium', - '(247)', - 11, - 10, - 'Bk', - 'Berkelium', - '(247)', - 12, - 10, - 'Cf', - 'Californium', - '(251)', - 13, - 10, - 'Es', - 'Einstenium', - '(252)', - 14, - 10, - 'Fm', - 'Fermium', - '(257)', - 15, - 10, - 'Md', - 'Mendelevium', - '(258)', - 16, - 10, - 'No', - 'Nobelium', - '(259)', - 17, - 10, - 'Lr', - 'Lawrencium', - '(262)', - 18, - 10, - 'Rf', - 'Rutherfordium', - '(267)', - 4, - 7, - 'Db', - 'Dubnium', - '(268)', - 5, - 7, - 'Sg', - 'Seaborgium', - '(271)', - 6, - 7, - 'Bh', - 'Bohrium', - '(272)', - 7, - 7, - 'Hs', - 'Hassium', - '(270)', - 8, - 7, - 'Mt', - 'Meitnerium', - '(276)', - 9, - 7, - 'Ds', - 'Darmstadium', - '(281)', - 10, - 7, - 'Rg', - 'Roentgenium', - '(280)', - 11, - 7, - 'Cn', - 'Copernicium', - '(285)', - 12, - 7, - 'Nh', - 'Nihonium', - '(286)', - 13, - 7, - 'Fl', - 'Flerovium', - '(289)', - 14, - 7, - 'Mc', - 'Moscovium', - '(290)', - 15, - 7, - 'Lv', - 'Livermorium', - '(293)', - 16, - 7, - 'Ts', - 'Tennessine', - '(294)', - 17, - 7, - 'Og', - 'Oganesson', - '(294)', - 18, - 7, -]; - -let camera, scene, renderer; -let controls; - -const objects = []; -const targets = { table: [], sphere: [], helix: [], grid: [] }; - -init(); -animate(); - -function init() { - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 10000); - camera.position.z = 3000; - - scene = new THREE.Scene(); - - // table - - for (let i = 0; i < table.length; i += 5) { - const element = document.createElement('div'); - element.className = 'element'; - element.style.backgroundColor = 'rgba(0,127,127,' + (Math.random() * 0.5 + 0.25) + ')'; - - const number = document.createElement('div'); - number.className = 'number'; - number.textContent = i / 5 + 1; - element.appendChild(number); - - const symbol = document.createElement('div'); - symbol.className = 'symbol'; - symbol.textContent = table[i]; - element.appendChild(symbol); - - const details = document.createElement('div'); - details.className = 'details'; - details.innerHTML = table[i + 1] + '
' + table[i + 2]; - element.appendChild(details); - - const objectCSS = new CSS3DObject(element); - objectCSS.position.x = Math.random() * 4000 - 2000; - objectCSS.position.y = Math.random() * 4000 - 2000; - objectCSS.position.z = Math.random() * 4000 - 2000; - scene.add(objectCSS); - - objects.push(objectCSS); - - // - - const object = new THREE.Object3D(); - object.position.x = table[i + 3] * 140 - 1330; - object.position.y = -(table[i + 4] * 180) + 990; - - targets.table.push(object); - } - - // sphere - - const vector = new THREE.Vector3(); - - for (let i = 0, l = objects.length; i < l; i++) { - const phi = Math.acos(-1 + (2 * i) / l); - const theta = Math.sqrt(l * Math.PI) * phi; - - const object = new THREE.Object3D(); - - object.position.setFromSphericalCoords(800, phi, theta); - - vector.copy(object.position).multiplyScalar(2); - - object.lookAt(vector); - - targets.sphere.push(object); - } - - // helix - - for (let i = 0, l = objects.length; i < l; i++) { - const theta = i * 0.175 + Math.PI; - const y = -(i * 8) + 450; - - const object = new THREE.Object3D(); - - object.position.setFromCylindricalCoords(900, theta, y); - - vector.x = object.position.x * 2; - vector.y = object.position.y; - vector.z = object.position.z * 2; - - object.lookAt(vector); - - targets.helix.push(object); - } - - // grid - - for (let i = 0; i < objects.length; i++) { - const object = new THREE.Object3D(); - - object.position.x = (i % 5) * 400 - 800; - object.position.y = -(Math.floor(i / 5) % 5) * 400 + 800; - object.position.z = Math.floor(i / 25) * 1000 - 2000; - - targets.grid.push(object); - } - - // - - renderer = new CSS3DRenderer(); - renderer.setSize(window.innerWidth, window.innerHeight); - document.getElementById('container').appendChild(renderer.domElement); - - // - - controls = new TrackballControls(camera, renderer.domElement); - controls.minDistance = 500; - controls.maxDistance = 6000; - controls.addEventListener('change', render); - - const buttonTable = document.getElementById('table'); - buttonTable.addEventListener('click', function () { - transform(targets.table, 2000); - }); - - const buttonSphere = document.getElementById('sphere'); - buttonSphere.addEventListener('click', function () { - transform(targets.sphere, 2000); - }); - - const buttonHelix = document.getElementById('helix'); - buttonHelix.addEventListener('click', function () { - transform(targets.helix, 2000); - }); - - const buttonGrid = document.getElementById('grid'); - buttonGrid.addEventListener('click', function () { - transform(targets.grid, 2000); - }); - - transform(targets.table, 2000); - - // - - window.addEventListener('resize', onWindowResize); -} - -function transform(targets, duration) { - TWEEN.removeAll(); - - for (let i = 0; i < objects.length; i++) { - const object = objects[i]; - const target = targets[i]; - - new TWEEN.Tween(object.position) - .to( - { x: target.position.x, y: target.position.y, z: target.position.z }, - Math.random() * duration + duration, - ) - .easing(TWEEN.Easing.Exponential.InOut) - .start(); - - new TWEEN.Tween(object.rotation) - .to( - { x: target.rotation.x, y: target.rotation.y, z: target.rotation.z }, - Math.random() * duration + duration, - ) - .easing(TWEEN.Easing.Exponential.InOut) - .start(); - } - - new TWEEN.Tween(this) - .to({}, duration * 2) - .onUpdate(render) - .start(); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - render(); -} - -function animate() { - requestAnimationFrame(animate); - - TWEEN.update(); - - controls.update(); -} - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/css3d_sandbox.ts b/examples-testing/examples/css3d_sandbox.ts deleted file mode 100644 index 1088b84b1..000000000 --- a/examples-testing/examples/css3d_sandbox.ts +++ /dev/null @@ -1,180 +0,0 @@ -import * as THREE from 'three'; - -import { TrackballControls } from 'three/addons/controls/TrackballControls.js'; -import { CSS3DRenderer, CSS3DObject } from 'three/addons/renderers/CSS3DRenderer.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let camera, scene, renderer; - -let scene2, renderer2; - -let controls; - -init(); -animate(); - -function init() { - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(200, 200, 200); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xf0f0f0); - - scene2 = new THREE.Scene(); - - const material = new THREE.MeshBasicMaterial({ - color: 0x000000, - wireframe: true, - wireframeLinewidth: 1, - side: THREE.DoubleSide, - }); - - // - - for (let i = 0; i < 10; i++) { - const element = document.createElement('div'); - element.style.width = '100px'; - element.style.height = '100px'; - element.style.opacity = i < 5 ? 0.5 : 1; - element.style.background = new THREE.Color(Math.random() * 0xffffff).getStyle(); - - const object = new CSS3DObject(element); - object.position.x = Math.random() * 200 - 100; - object.position.y = Math.random() * 200 - 100; - object.position.z = Math.random() * 200 - 100; - object.rotation.x = Math.random(); - object.rotation.y = Math.random(); - object.rotation.z = Math.random(); - object.scale.x = Math.random() + 0.5; - object.scale.y = Math.random() + 0.5; - scene2.add(object); - - const geometry = new THREE.PlaneGeometry(100, 100); - const mesh = new THREE.Mesh(geometry, material); - mesh.position.copy(object.position); - mesh.rotation.copy(object.rotation); - mesh.scale.copy(object.scale); - scene.add(mesh); - } - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - document.body.appendChild(renderer.domElement); - - renderer2 = new CSS3DRenderer(); - renderer2.setSize(window.innerWidth, window.innerHeight); - renderer2.domElement.style.position = 'absolute'; - renderer2.domElement.style.top = 0; - document.body.appendChild(renderer2.domElement); - - controls = new TrackballControls(camera, renderer2.domElement); - - window.addEventListener('resize', onWindowResize); - createPanel(); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - renderer2.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - requestAnimationFrame(animate); - - controls.update(); - - renderer.render(scene, camera); - renderer2.render(scene2, camera); -} - -function createPanel() { - const panel = new GUI(); - const folder1 = panel.addFolder('camera setViewOffset').close(); - - const settings = { - setViewOffset() { - folder1.children[1].enable().setValue(window.innerWidth); - folder1.children[2].enable().setValue(window.innerHeight); - folder1.children[3].enable().setValue(0); - folder1.children[4].enable().setValue(0); - folder1.children[5].enable().setValue(window.innerWidth); - folder1.children[6].enable().setValue(window.innerHeight); - }, - fullWidth: 0, - fullHeight: 0, - offsetX: 0, - offsetY: 0, - width: 0, - height: 0, - clearViewOffset() { - folder1.children[1].setValue(0).disable(); - folder1.children[2].setValue(0).disable(); - folder1.children[3].setValue(0).disable(); - folder1.children[4].setValue(0).disable(); - folder1.children[5].setValue(0).disable(); - folder1.children[6].setValue(0).disable(); - camera.clearViewOffset(); - }, - }; - - folder1.add(settings, 'setViewOffset'); - folder1 - .add(settings, 'fullWidth', window.screen.width / 4, window.screen.width * 2, 1) - .onChange(val => updateCameraViewOffset({ fullWidth: val })) - .disable(); - folder1 - .add(settings, 'fullHeight', window.screen.height / 4, window.screen.height * 2, 1) - .onChange(val => updateCameraViewOffset({ fullHeight: val })) - .disable(); - folder1 - .add(settings, 'offsetX', 0, 256, 1) - .onChange(val => updateCameraViewOffset({ x: val })) - .disable(); - folder1 - .add(settings, 'offsetY', 0, 256, 1) - .onChange(val => updateCameraViewOffset({ y: val })) - .disable(); - folder1 - .add(settings, 'width', window.screen.width / 4, window.screen.width * 2, 1) - .onChange(val => updateCameraViewOffset({ width: val })) - .disable(); - folder1 - .add(settings, 'height', window.screen.height / 4, window.screen.height * 2, 1) - .onChange(val => updateCameraViewOffset({ height: val })) - .disable(); - folder1.add(settings, 'clearViewOffset'); -} - -function updateCameraViewOffset({ fullWidth, fullHeight, x, y, width, height }) { - if (!camera.view) { - camera.setViewOffset( - fullWidth || window.innerWidth, - fullHeight || window.innerHeight, - x || 0, - y || 0, - width || window.innerWidth, - height || window.innerHeight, - ); - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - } else { - camera.setViewOffset( - fullWidth || camera.view.fullWidth, - fullHeight || camera.view.fullHeight, - x || camera.view.offsetX, - y || camera.view.offsetY, - width || camera.view.width, - height || camera.view.height, - ); - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - } -} diff --git a/examples-testing/examples/css3d_sprites.ts b/examples-testing/examples/css3d_sprites.ts deleted file mode 100644 index 39c3455a5..000000000 --- a/examples-testing/examples/css3d_sprites.ts +++ /dev/null @@ -1,157 +0,0 @@ -import * as THREE from 'three'; - -import TWEEN from 'three/addons/libs/tween.module.js'; -import { TrackballControls } from 'three/addons/controls/TrackballControls.js'; -import { CSS3DRenderer, CSS3DSprite } from 'three/addons/renderers/CSS3DRenderer.js'; - -let camera, scene, renderer; -let controls; - -const particlesTotal = 512; -const positions = []; -const objects = []; -let current = 0; - -init(); -animate(); - -function init() { - camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 5000); - camera.position.set(600, 400, 1500); - camera.lookAt(0, 0, 0); - - scene = new THREE.Scene(); - - const image = document.createElement('img'); - image.addEventListener('load', function () { - for (let i = 0; i < particlesTotal; i++) { - const object = new CSS3DSprite(image.cloneNode()); - ((object.position.x = Math.random() * 4000 - 2000), - (object.position.y = Math.random() * 4000 - 2000), - (object.position.z = Math.random() * 4000 - 2000)); - scene.add(object); - - objects.push(object); - } - - transition(); - }); - image.src = 'textures/sprite.png'; - - // Plane - - const amountX = 16; - const amountZ = 32; - const separationPlane = 150; - const offsetX = ((amountX - 1) * separationPlane) / 2; - const offsetZ = ((amountZ - 1) * separationPlane) / 2; - - for (let i = 0; i < particlesTotal; i++) { - const x = (i % amountX) * separationPlane; - const z = Math.floor(i / amountX) * separationPlane; - const y = (Math.sin(x * 0.5) + Math.sin(z * 0.5)) * 200; - - positions.push(x - offsetX, y, z - offsetZ); - } - - // Cube - - const amount = 8; - const separationCube = 150; - const offset = ((amount - 1) * separationCube) / 2; - - for (let i = 0; i < particlesTotal; i++) { - const x = (i % amount) * separationCube; - const y = Math.floor((i / amount) % amount) * separationCube; - const z = Math.floor(i / (amount * amount)) * separationCube; - - positions.push(x - offset, y - offset, z - offset); - } - - // Random - - for (let i = 0; i < particlesTotal; i++) { - positions.push(Math.random() * 4000 - 2000, Math.random() * 4000 - 2000, Math.random() * 4000 - 2000); - } - - // Sphere - - const radius = 750; - - for (let i = 0; i < particlesTotal; i++) { - const phi = Math.acos(-1 + (2 * i) / particlesTotal); - const theta = Math.sqrt(particlesTotal * Math.PI) * phi; - - positions.push( - radius * Math.cos(theta) * Math.sin(phi), - radius * Math.sin(theta) * Math.sin(phi), - radius * Math.cos(phi), - ); - } - - // - - renderer = new CSS3DRenderer(); - renderer.setSize(window.innerWidth, window.innerHeight); - document.getElementById('container').appendChild(renderer.domElement); - - // - - controls = new TrackballControls(camera, renderer.domElement); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function transition() { - const offset = current * particlesTotal * 3; - const duration = 2000; - - for (let i = 0, j = offset; i < particlesTotal; i++, j += 3) { - const object = objects[i]; - - new TWEEN.Tween(object.position) - .to( - { - x: positions[j], - y: positions[j + 1], - z: positions[j + 2], - }, - Math.random() * duration + duration, - ) - .easing(TWEEN.Easing.Exponential.InOut) - .start(); - } - - new TWEEN.Tween(this) - .to({}, duration * 3) - .onComplete(transition) - .start(); - - current = (current + 1) % 4; -} - -function animate() { - requestAnimationFrame(animate); - - TWEEN.update(); - controls.update(); - - const time = performance.now(); - - for (let i = 0, l = objects.length; i < l; i++) { - const object = objects[i]; - const scale = Math.sin((Math.floor(object.position.x) + time) * 0.002) * 0.3 + 1; - object.scale.set(scale, scale, scale); - } - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/css3d_youtube.ts b/examples-testing/examples/css3d_youtube.ts deleted file mode 100644 index 62652f87f..000000000 --- a/examples-testing/examples/css3d_youtube.ts +++ /dev/null @@ -1,79 +0,0 @@ -import * as THREE from 'three'; - -import { TrackballControls } from 'three/addons/controls/TrackballControls.js'; -import { CSS3DRenderer, CSS3DObject } from 'three/addons/renderers/CSS3DRenderer.js'; - -let camera, scene, renderer; -let controls; - -function Element(id, x, y, z, ry) { - const div = document.createElement('div'); - div.style.width = '480px'; - div.style.height = '360px'; - div.style.backgroundColor = '#000'; - - const iframe = document.createElement('iframe'); - iframe.style.width = '480px'; - iframe.style.height = '360px'; - iframe.style.border = '0px'; - iframe.src = ['https://www.youtube.com/embed/', id, '?rel=0'].join(''); - div.appendChild(iframe); - - const object = new CSS3DObject(div); - object.position.set(x, y, z); - object.rotation.y = ry; - - return object; -} - -init(); -animate(); - -function init() { - const container = document.getElementById('container'); - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 5000); - camera.position.set(500, 350, 750); - - scene = new THREE.Scene(); - - renderer = new CSS3DRenderer(); - renderer.setSize(window.innerWidth, window.innerHeight); - container.appendChild(renderer.domElement); - - const group = new THREE.Group(); - group.add(new Element('SJOz3qjfQXU', 0, 0, 240, 0)); - group.add(new Element('Y2-xZ-1HE-Q', 240, 0, 0, Math.PI / 2)); - group.add(new Element('IrydklNpcFI', 0, 0, -240, Math.PI)); - group.add(new Element('9ubytEsCaS0', -240, 0, 0, -Math.PI / 2)); - scene.add(group); - - controls = new TrackballControls(camera, renderer.domElement); - controls.rotateSpeed = 4; - - window.addEventListener('resize', onWindowResize); - - // Block iframe events when dragging camera - - const blocker = document.getElementById('blocker'); - blocker.style.display = 'none'; - - controls.addEventListener('start', function () { - blocker.style.display = ''; - }); - controls.addEventListener('end', function () { - blocker.style.display = 'none'; - }); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - requestAnimationFrame(animate); - controls.update(); - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/games_fps.ts b/examples-testing/examples/games_fps.ts deleted file mode 100644 index ed08fb5fa..000000000 --- a/examples-testing/examples/games_fps.ts +++ /dev/null @@ -1,377 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -import { Octree } from 'three/addons/math/Octree.js'; -import { OctreeHelper } from 'three/addons/helpers/OctreeHelper.js'; - -import { Capsule } from 'three/addons/math/Capsule.js'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -const timer = new THREE.Timer(); -timer.connect(document); - -const scene = new THREE.Scene(); -scene.background = new THREE.Color(0x88ccee); -scene.fog = new THREE.Fog(0x88ccee, 0, 50); - -const camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 1000); -camera.rotation.order = 'YXZ'; - -const fillLight1 = new THREE.HemisphereLight(0x8dc1de, 0x00668d, 1.5); -fillLight1.position.set(2, 1, 1); -scene.add(fillLight1); - -const directionalLight = new THREE.DirectionalLight(0xffffff, 2.5); -directionalLight.position.set(-5, 25, -1); -directionalLight.castShadow = true; -directionalLight.shadow.camera.near = 0.01; -directionalLight.shadow.camera.far = 500; -directionalLight.shadow.camera.right = 30; -directionalLight.shadow.camera.left = -30; -directionalLight.shadow.camera.top = 30; -directionalLight.shadow.camera.bottom = -30; -directionalLight.shadow.mapSize.width = 1024; -directionalLight.shadow.mapSize.height = 1024; -directionalLight.shadow.radius = 4; -directionalLight.shadow.bias = -0.00006; -scene.add(directionalLight); - -const container = document.getElementById('container'); - -const renderer = new THREE.WebGLRenderer({ antialias: true }); -renderer.setPixelRatio(window.devicePixelRatio); -renderer.setSize(window.innerWidth, window.innerHeight); -renderer.setAnimationLoop(animate); -renderer.shadowMap.enabled = true; -renderer.shadowMap.type = THREE.VSMShadowMap; -renderer.toneMapping = THREE.ACESFilmicToneMapping; -container.appendChild(renderer.domElement); - -const stats = new Stats(); -stats.domElement.style.position = 'absolute'; -stats.domElement.style.top = '0px'; -container.appendChild(stats.domElement); - -const GRAVITY = 30; - -const NUM_SPHERES = 100; -const SPHERE_RADIUS = 0.2; - -const STEPS_PER_FRAME = 5; - -const sphereGeometry = new THREE.IcosahedronGeometry(SPHERE_RADIUS, 5); -const sphereMaterial = new THREE.MeshLambertMaterial({ color: 0xdede8d }); - -const spheres = []; -let sphereIdx = 0; - -for (let i = 0; i < NUM_SPHERES; i++) { - const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial); - sphere.castShadow = true; - sphere.receiveShadow = true; - - scene.add(sphere); - - spheres.push({ - mesh: sphere, - collider: new THREE.Sphere(new THREE.Vector3(0, -100, 0), SPHERE_RADIUS), - velocity: new THREE.Vector3(), - }); -} - -const worldOctree = new Octree(); - -const playerCollider = new Capsule(new THREE.Vector3(0, 0.35, 0), new THREE.Vector3(0, 1, 0), 0.35); - -const playerVelocity = new THREE.Vector3(); -const playerDirection = new THREE.Vector3(); - -let playerOnFloor = false; -let mouseTime = 0; - -const keyStates = {}; - -const vector1 = new THREE.Vector3(); -const vector2 = new THREE.Vector3(); -const vector3 = new THREE.Vector3(); - -document.addEventListener('keydown', event => { - keyStates[event.code] = true; -}); - -document.addEventListener('keyup', event => { - keyStates[event.code] = false; -}); - -container.addEventListener('mousedown', () => { - document.body.requestPointerLock(); - - mouseTime = performance.now(); -}); - -document.addEventListener('mouseup', () => { - if (document.pointerLockElement !== null) throwBall(); -}); - -document.body.addEventListener('mousemove', event => { - if (document.pointerLockElement === document.body) { - camera.rotation.y -= event.movementX / 500; - camera.rotation.x -= event.movementY / 500; - } -}); - -window.addEventListener('resize', onWindowResize); - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function throwBall() { - const sphere = spheres[sphereIdx]; - - camera.getWorldDirection(playerDirection); - - sphere.collider.center.copy(playerCollider.end).addScaledVector(playerDirection, playerCollider.radius * 1.5); - - // throw the ball with more force if we hold the button longer, and if we move forward - - const impulse = 15 + 30 * (1 - Math.exp((mouseTime - performance.now()) * 0.001)); - - sphere.velocity.copy(playerDirection).multiplyScalar(impulse); - sphere.velocity.addScaledVector(playerVelocity, 2); - - sphereIdx = (sphereIdx + 1) % spheres.length; -} - -function playerCollisions() { - const result = worldOctree.capsuleIntersect(playerCollider); - - playerOnFloor = false; - - if (result) { - // determine if the surface we bumped into is something we can stand on - - playerOnFloor = result.normal.y >= 0.15; // allow slopes up to ~81° but ignore sheer vertical walls - - if (!playerOnFloor) { - playerVelocity.addScaledVector(result.normal, -result.normal.dot(playerVelocity)); - } - - if (result.depth >= 1e-10) { - playerCollider.translate(result.normal.multiplyScalar(result.depth)); - } - } -} - -function updatePlayer(deltaTime) { - let damping = Math.exp(-4 * deltaTime) - 1; - - if (!playerOnFloor) { - playerVelocity.y -= GRAVITY * deltaTime; - - // small air resistance - damping *= 0.1; - } - - playerVelocity.addScaledVector(playerVelocity, damping); - - const deltaPosition = playerVelocity.clone().multiplyScalar(deltaTime); - playerCollider.translate(deltaPosition); - - playerCollisions(); - - camera.position.copy(playerCollider.end); -} - -function playerSphereCollision(sphere) { - const center = vector1.addVectors(playerCollider.start, playerCollider.end).multiplyScalar(0.5); - - const sphere_center = sphere.collider.center; - - const r = playerCollider.radius + sphere.collider.radius; - const r2 = r * r; - - // approximation: player = 3 spheres - - for (const point of [playerCollider.start, playerCollider.end, center]) { - const d2 = point.distanceToSquared(sphere_center); - - if (d2 < r2) { - const normal = vector1.subVectors(point, sphere_center).normalize(); - const v1 = vector2.copy(normal).multiplyScalar(normal.dot(playerVelocity)); - const v2 = vector3.copy(normal).multiplyScalar(normal.dot(sphere.velocity)); - - playerVelocity.add(v2).sub(v1); - sphere.velocity.add(v1).sub(v2); - - const d = (r - Math.sqrt(d2)) / 2; - sphere_center.addScaledVector(normal, -d); - } - } -} - -function spheresCollisions() { - for (let i = 0, length = spheres.length; i < length; i++) { - const s1 = spheres[i]; - - for (let j = i + 1; j < length; j++) { - const s2 = spheres[j]; - - const d2 = s1.collider.center.distanceToSquared(s2.collider.center); - const r = s1.collider.radius + s2.collider.radius; - const r2 = r * r; - - if (d2 < r2) { - const normal = vector1.subVectors(s1.collider.center, s2.collider.center).normalize(); - const v1 = vector2.copy(normal).multiplyScalar(normal.dot(s1.velocity)); - const v2 = vector3.copy(normal).multiplyScalar(normal.dot(s2.velocity)); - - s1.velocity.add(v2).sub(v1); - s2.velocity.add(v1).sub(v2); - - const d = (r - Math.sqrt(d2)) / 2; - - s1.collider.center.addScaledVector(normal, d); - s2.collider.center.addScaledVector(normal, -d); - } - } - } -} - -function updateSpheres(deltaTime) { - spheres.forEach(sphere => { - sphere.collider.center.addScaledVector(sphere.velocity, deltaTime); - - const result = worldOctree.sphereIntersect(sphere.collider); - - if (result) { - sphere.velocity.addScaledVector(result.normal, -result.normal.dot(sphere.velocity) * 1.5); - sphere.collider.center.add(result.normal.multiplyScalar(result.depth)); - } else { - sphere.velocity.y -= GRAVITY * deltaTime; - } - - const damping = Math.exp(-1.5 * deltaTime) - 1; - sphere.velocity.addScaledVector(sphere.velocity, damping); - - playerSphereCollision(sphere); - }); - - spheresCollisions(); - - for (const sphere of spheres) { - sphere.mesh.position.copy(sphere.collider.center); - } -} - -function getForwardVector() { - camera.getWorldDirection(playerDirection); - playerDirection.y = 0; - playerDirection.normalize(); - - return playerDirection; -} - -function getSideVector() { - camera.getWorldDirection(playerDirection); - playerDirection.y = 0; - playerDirection.normalize(); - playerDirection.cross(camera.up); - - return playerDirection; -} - -function controls(deltaTime) { - // gives a bit of air control - const speedDelta = deltaTime * (playerOnFloor ? 25 : 8); - - if (keyStates['KeyW']) { - playerVelocity.add(getForwardVector().multiplyScalar(speedDelta)); - } - - if (keyStates['KeyS']) { - playerVelocity.add(getForwardVector().multiplyScalar(-speedDelta)); - } - - if (keyStates['KeyA']) { - playerVelocity.add(getSideVector().multiplyScalar(-speedDelta)); - } - - if (keyStates['KeyD']) { - playerVelocity.add(getSideVector().multiplyScalar(speedDelta)); - } - - if (playerOnFloor) { - if (keyStates['Space']) { - playerVelocity.y = 15; - } - } -} - -const loader = new GLTFLoader().setPath('./models/gltf/'); - -loader.load('collision-world.glb', gltf => { - scene.add(gltf.scene); - - worldOctree.fromGraphNode(gltf.scene); - - gltf.scene.traverse(child => { - if (child.isMesh) { - child.castShadow = true; - child.receiveShadow = true; - - if (child.material.map) { - child.material.map.anisotropy = 4; - } - } - }); - - const helper = new OctreeHelper(worldOctree); - helper.visible = false; - scene.add(helper); - - const gui = new GUI({ width: 200 }); - gui.add({ debug: false }, 'debug').onChange(function (value) { - helper.visible = value; - }); -}); - -function teleportPlayerIfOob() { - if (camera.position.y <= -25) { - playerCollider.start.set(0, 0.35, 0); - playerCollider.end.set(0, 1, 0); - playerCollider.radius = 0.35; - camera.position.copy(playerCollider.end); - camera.rotation.set(0, 0, 0); - } -} - -function animate() { - timer.update(); - - const deltaTime = Math.min(0.05, timer.getDelta()) / STEPS_PER_FRAME; - - // we look for collisions in substeps to mitigate the risk of - // an object traversing another too quickly for detection. - - for (let i = 0; i < STEPS_PER_FRAME; i++) { - controls(deltaTime); - - updatePlayer(deltaTime); - - updateSpheres(deltaTime); - - teleportPlayerIfOob(); - } - - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/misc_animation_groups.ts b/examples-testing/examples/misc_animation_groups.ts deleted file mode 100644 index 9fed234fc..000000000 --- a/examples-testing/examples/misc_animation_groups.ts +++ /dev/null @@ -1,128 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -let stats, timer; -let scene, camera, renderer, mixer; - -init(); - -function init() { - scene = new THREE.Scene(); - - // - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(50, 50, 100); - camera.lookAt(scene.position); - - // all objects of this animation group share a common animation state - - const animationGroup = new THREE.AnimationObjectGroup(); - - // - - const geometry = new THREE.BoxGeometry(5, 5, 5); - const material = new THREE.MeshBasicMaterial({ transparent: true }); - - // - - for (let i = 0; i < 5; i++) { - for (let j = 0; j < 5; j++) { - const mesh = new THREE.Mesh(geometry, material); - - mesh.position.x = 32 - 16 * i; - mesh.position.y = 0; - mesh.position.z = 32 - 16 * j; - - scene.add(mesh); - animationGroup.add(mesh); - } - } - - // create some keyframe tracks - - const xAxis = new THREE.Vector3(1, 0, 0); - const qInitial = new THREE.Quaternion().setFromAxisAngle(xAxis, 0); - const qFinal = new THREE.Quaternion().setFromAxisAngle(xAxis, Math.PI); - const quaternionKF = new THREE.QuaternionKeyframeTrack( - '.quaternion', - [0, 1, 2], - [ - qInitial.x, - qInitial.y, - qInitial.z, - qInitial.w, - qFinal.x, - qFinal.y, - qFinal.z, - qFinal.w, - qInitial.x, - qInitial.y, - qInitial.z, - qInitial.w, - ], - ); - - const colorKF = new THREE.ColorKeyframeTrack( - '.material.color', - [0, 1, 2], - [1, 0, 0, 0, 1, 0, 0, 0, 1], - THREE.InterpolateDiscrete, - ); - const opacityKF = new THREE.NumberKeyframeTrack('.material.opacity', [0, 1, 2], [1, 0, 1]); - - // create clip - - const clip = new THREE.AnimationClip('default', 3, [quaternionKF, colorKF, opacityKF]); - - // apply the animation group to the mixer as the root object - - mixer = new THREE.AnimationMixer(animationGroup); - - const clipAction = mixer.clipAction(clip); - clipAction.play(); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // - - stats = new Stats(); - document.body.appendChild(stats.dom); - - // - - timer = new THREE.Timer(); - timer.connect(document); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - const delta = timer.getDelta(); - - if (mixer) { - mixer.update(delta); - } - - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/misc_animation_keys.ts b/examples-testing/examples/misc_animation_keys.ts deleted file mode 100644 index 32d497e5a..000000000 --- a/examples-testing/examples/misc_animation_keys.ts +++ /dev/null @@ -1,132 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -let stats, timer; -let scene, camera, renderer, mixer; - -init(); - -function init() { - scene = new THREE.Scene(); - - // - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(25, 25, 50); - camera.lookAt(scene.position); - - // - - const axesHelper = new THREE.AxesHelper(10); - scene.add(axesHelper); - - // - - const geometry = new THREE.BoxGeometry(5, 5, 5); - const material = new THREE.MeshBasicMaterial({ color: 0xffffff, transparent: true }); - const mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - // create a keyframe track (i.e. a timed sequence of keyframes) for each animated property - // Note: the keyframe track type should correspond to the type of the property being animated - - // POSITION - const positionKF = new THREE.VectorKeyframeTrack('.position', [0, 1, 2], [0, 0, 0, 30, 0, 0, 0, 0, 0]); - - // SCALE - const scaleKF = new THREE.VectorKeyframeTrack('.scale', [0, 1, 2], [1, 1, 1, 2, 2, 2, 1, 1, 1]); - - // ROTATION - // Rotation should be performed using quaternions, using a THREE.QuaternionKeyframeTrack - // Interpolating Euler angles (.rotation property) can be problematic and is currently not supported - - // set up rotation about x axis - const xAxis = new THREE.Vector3(1, 0, 0); - - const qInitial = new THREE.Quaternion().setFromAxisAngle(xAxis, 0); - const qFinal = new THREE.Quaternion().setFromAxisAngle(xAxis, Math.PI); - const quaternionKF = new THREE.QuaternionKeyframeTrack( - '.quaternion', - [0, 1, 2], - [ - qInitial.x, - qInitial.y, - qInitial.z, - qInitial.w, - qFinal.x, - qFinal.y, - qFinal.z, - qFinal.w, - qInitial.x, - qInitial.y, - qInitial.z, - qInitial.w, - ], - ); - - // COLOR - const colorKF = new THREE.ColorKeyframeTrack( - '.material.color', - [0, 1, 2], - [1, 0, 0, 0, 1, 0, 0, 0, 1], - THREE.InterpolateDiscrete, - ); - - // OPACITY - const opacityKF = new THREE.NumberKeyframeTrack('.material.opacity', [0, 1, 2], [1, 0, 1]); - - // create an animation sequence with the tracks - // If a negative time value is passed, the duration will be calculated from the times of the passed tracks array - const clip = new THREE.AnimationClip('Action', 3, [scaleKF, positionKF, quaternionKF, colorKF, opacityKF]); - - // setup the THREE.AnimationMixer - mixer = new THREE.AnimationMixer(mesh); - - // create a ClipAction and set it to play - const clipAction = mixer.clipAction(clip); - clipAction.play(); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // - - stats = new Stats(); - document.body.appendChild(stats.dom); - - // - - timer = new THREE.Timer(); - timer.connect(document); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - const delta = timer.getDelta(); - - if (mixer) { - mixer.update(delta); - } - - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/misc_boxselection.ts b/examples-testing/examples/misc_boxselection.ts deleted file mode 100644 index e7079c405..000000000 --- a/examples-testing/examples/misc_boxselection.ts +++ /dev/null @@ -1,137 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { SelectionBox } from 'three/addons/interactive/SelectionBox.js'; -import { SelectionHelper } from 'three/addons/interactive/SelectionHelper.js'; - -let container, stats; -let camera, scene, renderer; - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 500); - camera.position.z = 50; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xf0f0f0); - - scene.add(new THREE.AmbientLight(0xaaaaaa)); - - const light = new THREE.SpotLight(0xffffff, 10000); - light.position.set(0, 25, 50); - light.angle = Math.PI / 5; - - light.castShadow = true; - light.shadow.camera.near = 10; - light.shadow.camera.far = 100; - light.shadow.mapSize.width = 1024; - light.shadow.mapSize.height = 1024; - - scene.add(light); - - const geometry = new THREE.BoxGeometry(); - - for (let i = 0; i < 200; i++) { - const object = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({ color: Math.random() * 0xffffff })); - - object.position.x = Math.random() * 80 - 40; - object.position.y = Math.random() * 45 - 25; - object.position.z = Math.random() * 45 - 25; - - object.rotation.x = Math.random() * 2 * Math.PI; - object.rotation.y = Math.random() * 2 * Math.PI; - object.rotation.z = Math.random() * 2 * Math.PI; - - object.scale.x = Math.random() * 2 + 1; - object.scale.y = Math.random() * 2 + 1; - object.scale.z = Math.random() * 2 + 1; - - object.castShadow = true; - object.receiveShadow = true; - - scene.add(object); - } - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.shadowMap.enabled = true; - renderer.shadowMap.type = THREE.PCFShadowMap; - - container.appendChild(renderer.domElement); - - stats = new Stats(); - container.appendChild(stats.dom); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - renderer.render(scene, camera); - - stats.update(); -} - -const selectionBox = new SelectionBox(camera, scene); -const helper = new SelectionHelper(renderer, 'selectBox'); - -document.addEventListener('pointerdown', function (event) { - for (const item of selectionBox.collection) { - item.material.emissive.set(0x000000); - } - - selectionBox.startPoint.set( - (event.clientX / window.innerWidth) * 2 - 1, - -(event.clientY / window.innerHeight) * 2 + 1, - 0.5, - ); -}); - -document.addEventListener('pointermove', function (event) { - if (helper.isDown) { - for (let i = 0; i < selectionBox.collection.length; i++) { - selectionBox.collection[i].material.emissive.set(0x000000); - } - - selectionBox.endPoint.set( - (event.clientX / window.innerWidth) * 2 - 1, - -(event.clientY / window.innerHeight) * 2 + 1, - 0.5, - ); - - const allSelected = selectionBox.select(); - - for (let i = 0; i < allSelected.length; i++) { - allSelected[i].material.emissive.set(0xffffff); - } - } -}); - -document.addEventListener('pointerup', function (event) { - selectionBox.endPoint.set( - (event.clientX / window.innerWidth) * 2 - 1, - -(event.clientY / window.innerHeight) * 2 + 1, - 0.5, - ); - - const allSelected = selectionBox.select(); - - for (let i = 0; i < allSelected.length; i++) { - allSelected[i].material.emissive.set(0xffffff); - } -}); diff --git a/examples-testing/examples/misc_controls_arcball.ts b/examples-testing/examples/misc_controls_arcball.ts deleted file mode 100644 index f2611be64..000000000 --- a/examples-testing/examples/misc_controls_arcball.ts +++ /dev/null @@ -1,211 +0,0 @@ -import * as THREE from 'three'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { ArcballControls } from 'three/addons/controls/ArcballControls.js'; - -import { OBJLoader } from 'three/addons/loaders/OBJLoader.js'; -import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; - -const cameras = ['Orthographic', 'Perspective']; -const cameraType = { type: 'Perspective' }; - -const perspectiveDistance = 2.5; -const orthographicDistance = 120; -let camera, controls, scene, renderer, gui; -let folderOptions, folderAnimations; - -const arcballGui = { - gizmoVisible: true, - - setArcballControls: function () { - controls = new ArcballControls(camera, renderer.domElement, scene); - controls.addEventListener('change', render); - - this.gizmoVisible = true; - - this.populateGui(); - }, - - populateGui: function () { - folderOptions.add(controls, 'enabled').name('Enable controls'); - folderOptions.add(controls, 'enableFocus').name('Enable focus'); - folderOptions.add(controls, 'enableGrid').name('Enable Grid'); - folderOptions.add(controls, 'enableRotate').name('Enable rotate'); - folderOptions.add(controls, 'enablePan').name('Enable pan'); - folderOptions.add(controls, 'enableZoom').name('Enable zoom'); - folderOptions.add(controls, 'cursorZoom').name('Cursor zoom'); - folderOptions.add(controls, 'adjustNearFar').name('adjust near/far'); - folderOptions.add(controls, 'scaleFactor', 1.1, 10, 0.1).name('Scale factor'); - folderOptions.add(controls, 'minDistance', 0, 50, 0.5).name('Min distance'); - folderOptions.add(controls, 'maxDistance', 0, 50, 0.5).name('Max distance'); - folderOptions.add(controls, 'minZoom', 0, 50, 0.5).name('Min zoom'); - folderOptions.add(controls, 'maxZoom', 0, 50, 0.5).name('Max zoom'); - folderOptions - .add(arcballGui, 'gizmoVisible') - .name('Show gizmos') - .onChange(function () { - controls.setGizmosVisible(arcballGui.gizmoVisible); - }); - folderOptions.add(controls, 'copyState').name('Copy state(ctrl+c)'); - folderOptions.add(controls, 'pasteState').name('Paste state(ctrl+v)'); - folderOptions.add(controls, 'reset').name('Reset'); - folderAnimations.add(controls, 'enableAnimations').name('Enable anim.'); - folderAnimations.add(controls, 'dampingFactor', 0, 100, 1).name('Damping'); - folderAnimations.add(controls, 'wMax', 0, 100, 1).name('Angular spd'); - }, -}; - -init(); - -function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.toneMapping = THREE.ReinhardToneMapping; - renderer.toneMappingExposure = 3; - renderer.domElement.style.background = 'linear-gradient( 180deg, rgba( 0,0,0,1 ) 0%, rgba( 128,128,255,1 ) 100% )'; - container.appendChild(renderer.domElement); - - // - - scene = new THREE.Scene(); - - camera = makePerspectiveCamera(); - camera.position.set(0, 0, perspectiveDistance); - - const material = new THREE.MeshStandardMaterial(); - - new OBJLoader().setPath('models/obj/cerberus/').load('Cerberus.obj', function (group) { - const textureLoader = new THREE.TextureLoader().setPath('models/obj/cerberus/'); - - material.roughness = 1; - material.metalness = 1; - - const diffuseMap = textureLoader.load('Cerberus_A.jpg', render); - diffuseMap.colorSpace = THREE.SRGBColorSpace; - material.map = diffuseMap; - - material.metalnessMap = material.roughnessMap = textureLoader.load('Cerberus_RM.jpg', render); - material.normalMap = textureLoader.load('Cerberus_N.jpg', render); - - material.map.wrapS = THREE.RepeatWrapping; - material.roughnessMap.wrapS = THREE.RepeatWrapping; - material.metalnessMap.wrapS = THREE.RepeatWrapping; - material.normalMap.wrapS = THREE.RepeatWrapping; - - group.traverse(function (child) { - if (child.isMesh) { - child.material = material; - } - }); - - group.rotation.y = Math.PI / 2; - group.position.x += 0.25; - scene.add(group); - render(); - - new HDRLoader().setPath('textures/equirectangular/').load('venice_sunset_1k.hdr', function (hdrEquirect) { - hdrEquirect.mapping = THREE.EquirectangularReflectionMapping; - - scene.environment = hdrEquirect; - - render(); - }); - - window.addEventListener('keydown', onKeyDown); - window.addEventListener('resize', onWindowResize); - - // - - gui = new GUI(); - gui.add(cameraType, 'type', cameras) - .name('Choose Camera') - .onChange(function () { - setCamera(cameraType.type); - }); - - folderOptions = gui.addFolder('Arcball parameters'); - folderAnimations = folderOptions.addFolder('Animations'); - - arcballGui.setArcballControls(); - - render(); - }); -} - -function makeOrthographicCamera() { - const halfFovV = THREE.MathUtils.DEG2RAD * 45 * 0.5; - const halfFovH = Math.atan((window.innerWidth / window.innerHeight) * Math.tan(halfFovV)); - - const halfW = perspectiveDistance * Math.tan(halfFovH); - const halfH = perspectiveDistance * Math.tan(halfFovV); - const near = 0.01; - const far = 2000; - const newCamera = new THREE.OrthographicCamera(-halfW, halfW, halfH, -halfH, near, far); - return newCamera; -} - -function makePerspectiveCamera() { - const fov = 45; - const aspect = window.innerWidth / window.innerHeight; - const near = 0.01; - const far = 2000; - const newCamera = new THREE.PerspectiveCamera(fov, aspect, near, far); - return newCamera; -} - -function onWindowResize() { - if (camera.type == 'OrthographicCamera') { - const halfFovV = THREE.MathUtils.DEG2RAD * 45 * 0.5; - const halfFovH = Math.atan((window.innerWidth / window.innerHeight) * Math.tan(halfFovV)); - - const halfW = perspectiveDistance * Math.tan(halfFovH); - const halfH = perspectiveDistance * Math.tan(halfFovV); - camera.left = -halfW; - camera.right = halfW; - camera.top = halfH; - camera.bottom = -halfH; - } else if (camera.type == 'PerspectiveCamera') { - camera.aspect = window.innerWidth / window.innerHeight; - } - - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - render(); -} - -function render() { - renderer.render(scene, camera); -} - -function onKeyDown(event) { - if (event.key === 'c') { - if (event.ctrlKey || event.metaKey) { - controls.copyState(); - } - } else if (event.key === 'v') { - if (event.ctrlKey || event.metaKey) { - controls.pasteState(); - } - } -} - -function setCamera(type) { - if (type == 'Orthographic') { - camera = makeOrthographicCamera(); - camera.position.set(0, 0, orthographicDistance); - } else if (type == 'Perspective') { - camera = makePerspectiveCamera(); - camera.position.set(0, 0, perspectiveDistance); - } - - controls.setCamera(camera); - - render(); -} diff --git a/examples-testing/examples/misc_controls_drag.ts b/examples-testing/examples/misc_controls_drag.ts deleted file mode 100644 index b12b0421e..000000000 --- a/examples-testing/examples/misc_controls_drag.ts +++ /dev/null @@ -1,153 +0,0 @@ -import * as THREE from 'three'; - -import { DragControls } from 'three/addons/controls/DragControls.js'; - -let container; -let camera, scene, renderer; -let controls, group; -let enableSelection = false; - -const objects = []; - -const mouse = new THREE.Vector2(), - raycaster = new THREE.Raycaster(); - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 500); - camera.position.z = 25; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xf0f0f0); - - scene.add(new THREE.AmbientLight(0xaaaaaa)); - - const light = new THREE.SpotLight(0xffffff, 10000); - light.position.set(0, 25, 50); - light.angle = Math.PI / 9; - - light.castShadow = true; - light.shadow.camera.near = 10; - light.shadow.camera.far = 100; - light.shadow.mapSize.width = 1024; - light.shadow.mapSize.height = 1024; - - scene.add(light); - - group = new THREE.Group(); - scene.add(group); - - const geometry = new THREE.BoxGeometry(); - - for (let i = 0; i < 200; i++) { - const object = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({ color: Math.random() * 0xffffff })); - - object.position.x = Math.random() * 30 - 15; - object.position.y = Math.random() * 15 - 7.5; - object.position.z = Math.random() * 20 - 10; - - object.rotation.x = Math.random() * 2 * Math.PI; - object.rotation.y = Math.random() * 2 * Math.PI; - object.rotation.z = Math.random() * 2 * Math.PI; - - object.scale.x = Math.random() * 2 + 1; - object.scale.y = Math.random() * 2 + 1; - object.scale.z = Math.random() * 2 + 1; - - object.castShadow = true; - object.receiveShadow = true; - - scene.add(object); - - objects.push(object); - } - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.shadowMap.enabled = true; - renderer.shadowMap.type = THREE.PCFShadowMap; - - container.appendChild(renderer.domElement); - - controls = new DragControls([...objects], camera, renderer.domElement); - controls.rotateSpeed = 2; - controls.addEventListener('drag', render); - - // - - window.addEventListener('resize', onWindowResize); - - document.addEventListener('click', onClick); - window.addEventListener('keydown', onKeyDown); - window.addEventListener('keyup', onKeyUp); - - render(); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - render(); -} - -function onKeyDown(event) { - enableSelection = event.keyCode === 16 ? true : false; - - if (event.keyCode === 77) { - controls.touches.ONE = controls.touches.ONE === THREE.TOUCH.PAN ? THREE.TOUCH.ROTATE : THREE.TOUCH.PAN; - } -} - -function onKeyUp() { - enableSelection = false; -} - -function onClick(event) { - event.preventDefault(); - - if (enableSelection === true) { - const draggableObjects = controls.objects; - draggableObjects.length = 0; - - mouse.x = (event.clientX / window.innerWidth) * 2 - 1; - mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; - - raycaster.setFromCamera(mouse, camera); - - const intersections = raycaster.intersectObjects(objects, true); - - if (intersections.length > 0) { - const object = intersections[0].object; - - if (group.children.includes(object) === true) { - object.material.emissive.set(0x000000); - scene.attach(object); - } else { - object.material.emissive.set(0xaaaaaa); - group.attach(object); - } - - controls.transformGroup = true; - draggableObjects.push(group); - } - - if (group.children.length === 0) { - controls.transformGroup = false; - draggableObjects.push(...objects); - } - } - - render(); -} - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/misc_controls_fly.ts b/examples-testing/examples/misc_controls_fly.ts deleted file mode 100644 index 6493ebd8a..000000000 --- a/examples-testing/examples/misc_controls_fly.ts +++ /dev/null @@ -1,218 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { pass } from 'three/tsl'; -import { film } from 'three/addons/tsl/display/FilmNode.js'; - -import Stats from 'three/addons/libs/stats.module.js'; -import { FlyControls } from 'three/addons/controls/FlyControls.js'; - -const radius = 6371; -const tilt = 0.41; -const rotationSpeed = 0.02; - -const cloudsScale = 1.005; -const moonScale = 0.23; - -const MARGIN = 0; -let SCREEN_HEIGHT = window.innerHeight - MARGIN * 2; -let SCREEN_WIDTH = window.innerWidth; - -let camera, controls, scene, renderer, stats; -let geometry, meshPlanet, meshClouds, meshMoon; -let dirLight; - -let renderPipeline; - -const textureLoader = new THREE.TextureLoader(); - -let d, dPlanet, dMoon; -const dMoonVec = new THREE.Vector3(); - -const timer = new THREE.Timer(); -timer.connect(document); - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(25, SCREEN_WIDTH / SCREEN_HEIGHT, 50, 1e7); - camera.position.z = radius * 5; - - scene = new THREE.Scene(); - scene.fog = new THREE.FogExp2(0x000000, 0.00000025); - - dirLight = new THREE.DirectionalLight(0xffffff, 3); - dirLight.position.set(-1, 0, 1).normalize(); - scene.add(dirLight); - - const materialNormalMap = new THREE.MeshPhongMaterial({ - specular: 0x7c7c7c, - shininess: 15, - map: textureLoader.load('textures/planets/earth_atmos_2048.jpg'), - specularMap: textureLoader.load('textures/planets/earth_specular_2048.jpg'), - normalMap: textureLoader.load('textures/planets/earth_normal_2048.jpg'), - - // y scale is negated to compensate for normal map handedness. - normalScale: new THREE.Vector2(0.85, -0.85), - }); - materialNormalMap.map.colorSpace = THREE.SRGBColorSpace; - - // planet - - geometry = new THREE.SphereGeometry(radius, 100, 50); - - meshPlanet = new THREE.Mesh(geometry, materialNormalMap); - meshPlanet.rotation.y = 0; - meshPlanet.rotation.z = tilt; - scene.add(meshPlanet); - - // clouds - - const materialClouds = new THREE.MeshLambertMaterial({ - map: textureLoader.load('textures/planets/earth_clouds_1024.png'), - transparent: true, - }); - materialClouds.map.colorSpace = THREE.SRGBColorSpace; - - meshClouds = new THREE.Mesh(geometry, materialClouds); - meshClouds.scale.set(cloudsScale, cloudsScale, cloudsScale); - meshClouds.rotation.z = tilt; - scene.add(meshClouds); - - // moon - - const materialMoon = new THREE.MeshPhongMaterial({ - map: textureLoader.load('textures/planets/moon_1024.jpg'), - }); - materialMoon.map.colorSpace = THREE.SRGBColorSpace; - - meshMoon = new THREE.Mesh(geometry, materialMoon); - meshMoon.position.set(radius * 5, 0, 0); - meshMoon.scale.set(moonScale, moonScale, moonScale); - scene.add(meshMoon); - - // stars - - const r = radius, - starsGeometry = [new THREE.BufferGeometry(), new THREE.BufferGeometry()]; - - const vertices1 = []; - const vertices2 = []; - - const vertex = new THREE.Vector3(); - - for (let i = 0; i < 250; i++) { - vertex.x = Math.random() * 2 - 1; - vertex.y = Math.random() * 2 - 1; - vertex.z = Math.random() * 2 - 1; - vertex.multiplyScalar(r); - - vertices1.push(vertex.x, vertex.y, vertex.z); - } - - for (let i = 0; i < 1500; i++) { - vertex.x = Math.random() * 2 - 1; - vertex.y = Math.random() * 2 - 1; - vertex.z = Math.random() * 2 - 1; - vertex.multiplyScalar(r); - - vertices2.push(vertex.x, vertex.y, vertex.z); - } - - starsGeometry[0].setAttribute('position', new THREE.Float32BufferAttribute(vertices1, 3)); - starsGeometry[1].setAttribute('position', new THREE.Float32BufferAttribute(vertices2, 3)); - - const starsMaterials = [ - new THREE.PointsMaterial({ color: 0x9c9c9c }), - new THREE.PointsMaterial({ color: 0x838383 }), - new THREE.PointsMaterial({ color: 0x5a5a5a }), - ]; - - for (let i = 10; i < 30; i++) { - const stars = new THREE.Points(starsGeometry[i % 2], starsMaterials[i % 3]); - - stars.rotation.x = Math.random() * 6; - stars.rotation.y = Math.random() * 6; - stars.rotation.z = Math.random() * 6; - stars.scale.setScalar(i * 10); - - stars.matrixAutoUpdate = false; - stars.updateMatrix(); - - scene.add(stars); - } - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // - - controls = new FlyControls(camera, renderer.domElement); - - controls.movementSpeed = 1000; - controls.domElement = renderer.domElement; - controls.rollSpeed = Math.PI / 24; - controls.autoForward = false; - controls.dragToLook = false; - - // - - stats = new Stats(); - document.body.appendChild(stats.dom); - - window.addEventListener('resize', onWindowResize); - - // postprocessing - - renderPipeline = new THREE.RenderPipeline(renderer); - - const scenePass = pass(scene, camera); - const scenePassColor = scenePass.getTextureNode(); - - renderPipeline.outputNode = film(scenePassColor); -} - -function onWindowResize() { - SCREEN_HEIGHT = window.innerHeight; - SCREEN_WIDTH = window.innerWidth; - - camera.aspect = SCREEN_WIDTH / SCREEN_HEIGHT; - camera.updateProjectionMatrix(); - - renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); -} - -function animate() { - timer.update(); - - render(); - stats.update(); -} - -function render() { - // rotate the planet and clouds - - const delta = timer.getDelta(); - - meshPlanet.rotation.y += rotationSpeed * delta; - meshClouds.rotation.y += 1.25 * rotationSpeed * delta; - - // slow down as we approach the surface - - dPlanet = camera.position.length(); - - dMoonVec.subVectors(camera.position, meshMoon.position); - dMoon = dMoonVec.length(); - - if (dMoon < dPlanet) { - d = dMoon - radius * moonScale * 1.01; - } else { - d = dPlanet - radius * 1.01; - } - - controls.movementSpeed = 0.33 * d; - controls.update(delta); - - renderPipeline.render(); -} diff --git a/examples-testing/examples/misc_controls_map.ts b/examples-testing/examples/misc_controls_map.ts deleted file mode 100644 index 9c7af0cda..000000000 --- a/examples-testing/examples/misc_controls_map.ts +++ /dev/null @@ -1,102 +0,0 @@ -import * as THREE from 'three'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { MapControls } from 'three/addons/controls/MapControls.js'; - -let camera, controls, scene, renderer; - -init(); -//render(); // remove when using animation loop - -function init() { - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xcccccc); - scene.fog = new THREE.FogExp2(0xcccccc, 0.002); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(0, 200, -200); - - // controls - - controls = new MapControls(camera, renderer.domElement); - - //controls.addEventListener( 'change', render ); // call this only in static scenes (i.e., if there is no animation loop) - - controls.enableDamping = true; // an animation loop is required when either damping or auto-rotation are enabled - controls.dampingFactor = 0.05; - - controls.screenSpacePanning = false; - - controls.minDistance = 100; - controls.maxDistance = 500; - - controls.maxPolarAngle = Math.PI / 2; - - // world - - const geometry = new THREE.BoxGeometry(); - geometry.translate(0, 0.5, 0); - const material = new THREE.MeshPhongMaterial({ color: 0xeeeeee, flatShading: true }); - - const mesh = new THREE.InstancedMesh(geometry, material, 500); - const dummy = new THREE.Object3D(); - - for (let i = 0; i < 500; i++) { - dummy.position.x = Math.random() * 1600 - 800; - dummy.position.y = 0; - dummy.position.z = Math.random() * 1600 - 800; - dummy.scale.x = 20; - dummy.scale.y = Math.random() * 80 + 10; - dummy.scale.z = 20; - - dummy.updateMatrix(); - mesh.setMatrixAt(i, dummy.matrix); - } - - scene.add(mesh); - - // lights - - const dirLight1 = new THREE.DirectionalLight(0xffffff, 3); - dirLight1.position.set(1, 1, 1); - scene.add(dirLight1); - - const dirLight2 = new THREE.DirectionalLight(0x002288, 3); - dirLight2.position.set(-1, -1, -1); - scene.add(dirLight2); - - const ambientLight = new THREE.AmbientLight(0x555555); - scene.add(ambientLight); - - // - - window.addEventListener('resize', onWindowResize); - - const gui = new GUI(); - gui.add(controls, 'zoomToCursor'); - gui.add(controls, 'screenSpacePanning'); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - controls.update(); // only required if controls.enableDamping = true, or if controls.autoRotate = true - - render(); -} - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/misc_controls_orbit.ts b/examples-testing/examples/misc_controls_orbit.ts deleted file mode 100644 index 7d2ec2626..000000000 --- a/examples-testing/examples/misc_controls_orbit.ts +++ /dev/null @@ -1,95 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let camera, controls, scene, renderer; - -init(); -//render(); // remove when using animation loop - -function init() { - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xcccccc); - scene.fog = new THREE.FogExp2(0xcccccc, 0.002); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(400, 200, 0); - - // controls - - controls = new OrbitControls(camera, renderer.domElement); - controls.listenToKeyEvents(window); // optional - - //controls.addEventListener( 'change', render ); // call this only in static scenes (i.e., if there is no animation loop) - - controls.enableDamping = true; // an animation loop is required when either damping or auto-rotation are enabled - controls.dampingFactor = 0.05; - - controls.screenSpacePanning = false; - - controls.minDistance = 100; - controls.maxDistance = 500; - - controls.cursorStyle = 'grab'; - - controls.maxPolarAngle = Math.PI / 2; - - // world - - const geometry = new THREE.ConeGeometry(10, 30, 4, 1); - const material = new THREE.MeshPhongMaterial({ color: 0xffffff, flatShading: true }); - - const mesh = new THREE.InstancedMesh(geometry, material, 500); - const dummy = new THREE.Object3D(); - - for (let i = 0; i < 500; i++) { - dummy.position.x = Math.random() * 1600 - 800; - dummy.position.y = 0; - dummy.position.z = Math.random() * 1600 - 800; - - dummy.updateMatrix(); - mesh.setMatrixAt(i, dummy.matrix); - } - - scene.add(mesh); - - // lights - - const dirLight1 = new THREE.DirectionalLight(0xffffff, 3); - dirLight1.position.set(1, 1, 1); - scene.add(dirLight1); - - const dirLight2 = new THREE.DirectionalLight(0x002288, 3); - dirLight2.position.set(-1, -1, -1); - scene.add(dirLight2); - - const ambientLight = new THREE.AmbientLight(0x555555); - scene.add(ambientLight); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - controls.update(); // only required if controls.enableDamping = true, or if controls.autoRotate = true - - render(); -} - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/misc_controls_pointerlock.ts b/examples-testing/examples/misc_controls_pointerlock.ts deleted file mode 100644 index 0b6fcc516..000000000 --- a/examples-testing/examples/misc_controls_pointerlock.ts +++ /dev/null @@ -1,245 +0,0 @@ -import * as THREE from 'three'; - -import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js'; - -let camera, scene, renderer, controls; - -const objects = []; - -let raycaster; - -let moveForward = false; -let moveBackward = false; -let moveLeft = false; -let moveRight = false; -let canJump = false; - -let prevTime = performance.now(); -const velocity = new THREE.Vector3(); -const direction = new THREE.Vector3(); -const vertex = new THREE.Vector3(); -const color = new THREE.Color(); - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.y = 10; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xffffff); - scene.fog = new THREE.Fog(0xffffff, 0, 750); - - const light = new THREE.HemisphereLight(0xeeeeff, 0x777788, 2.5); - light.position.set(0.5, 1, 0.75); - scene.add(light); - - controls = new PointerLockControls(camera, document.body); - - const blocker = document.getElementById('blocker'); - const instructions = document.getElementById('instructions'); - - instructions.addEventListener('click', function () { - controls.lock(); - }); - - controls.addEventListener('lock', function () { - instructions.style.display = 'none'; - blocker.style.display = 'none'; - }); - - controls.addEventListener('unlock', function () { - blocker.style.display = 'block'; - instructions.style.display = ''; - }); - - scene.add(controls.object); - - const onKeyDown = function (event) { - switch (event.code) { - case 'ArrowUp': - case 'KeyW': - moveForward = true; - break; - - case 'ArrowLeft': - case 'KeyA': - moveLeft = true; - break; - - case 'ArrowDown': - case 'KeyS': - moveBackward = true; - break; - - case 'ArrowRight': - case 'KeyD': - moveRight = true; - break; - - case 'Space': - if (canJump === true) velocity.y += 350; - canJump = false; - break; - } - }; - - const onKeyUp = function (event) { - switch (event.code) { - case 'ArrowUp': - case 'KeyW': - moveForward = false; - break; - - case 'ArrowLeft': - case 'KeyA': - moveLeft = false; - break; - - case 'ArrowDown': - case 'KeyS': - moveBackward = false; - break; - - case 'ArrowRight': - case 'KeyD': - moveRight = false; - break; - } - }; - - document.addEventListener('keydown', onKeyDown); - document.addEventListener('keyup', onKeyUp); - - raycaster = new THREE.Raycaster(new THREE.Vector3(), new THREE.Vector3(0, -1, 0), 0, 10); - - // floor - - let floorGeometry = new THREE.PlaneGeometry(2000, 2000, 100, 100); - floorGeometry.rotateX(-Math.PI / 2); - - // vertex displacement - - let position = floorGeometry.attributes.position; - - for (let i = 0, l = position.count; i < l; i++) { - vertex.fromBufferAttribute(position, i); - - vertex.x += Math.random() * 20 - 10; - vertex.y += Math.random() * 2; - vertex.z += Math.random() * 20 - 10; - - position.setXYZ(i, vertex.x, vertex.y, vertex.z); - } - - floorGeometry = floorGeometry.toNonIndexed(); // ensure each face has unique vertices - - position = floorGeometry.attributes.position; - const colorsFloor = []; - - for (let i = 0, l = position.count; i < l; i++) { - color.setHSL(Math.random() * 0.3 + 0.5, 0.75, Math.random() * 0.25 + 0.75, THREE.SRGBColorSpace); - colorsFloor.push(color.r, color.g, color.b); - } - - floorGeometry.setAttribute('color', new THREE.Float32BufferAttribute(colorsFloor, 3)); - - const floorMaterial = new THREE.MeshBasicMaterial({ vertexColors: true }); - - const floor = new THREE.Mesh(floorGeometry, floorMaterial); - scene.add(floor); - - // objects - - const boxGeometry = new THREE.BoxGeometry(20, 20, 20).toNonIndexed(); - - position = boxGeometry.attributes.position; - const colorsBox = []; - - for (let i = 0, l = position.count; i < l; i++) { - color.setHSL(Math.random() * 0.3 + 0.5, 0.75, Math.random() * 0.25 + 0.75, THREE.SRGBColorSpace); - colorsBox.push(color.r, color.g, color.b); - } - - boxGeometry.setAttribute('color', new THREE.Float32BufferAttribute(colorsBox, 3)); - - for (let i = 0; i < 500; i++) { - const boxMaterial = new THREE.MeshPhongMaterial({ specular: 0xffffff, flatShading: true, vertexColors: true }); - boxMaterial.color.setHSL(Math.random() * 0.2 + 0.5, 0.75, Math.random() * 0.25 + 0.75, THREE.SRGBColorSpace); - - const box = new THREE.Mesh(boxGeometry, boxMaterial); - box.position.x = Math.floor(Math.random() * 20 - 10) * 20; - box.position.y = Math.floor(Math.random() * 20) * 20 + 10; - box.position.z = Math.floor(Math.random() * 20 - 10) * 20; - - scene.add(box); - objects.push(box); - } - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - const time = performance.now(); - - if (controls.isLocked === true) { - raycaster.ray.origin.copy(controls.object.position); - raycaster.ray.origin.y -= 10; - - const intersections = raycaster.intersectObjects(objects, false); - - const onObject = intersections.length > 0; - - const delta = (time - prevTime) / 1000; - - velocity.x -= velocity.x * 10.0 * delta; - velocity.z -= velocity.z * 10.0 * delta; - - velocity.y -= 9.8 * 100.0 * delta; // 100.0 = mass - - direction.z = Number(moveForward) - Number(moveBackward); - direction.x = Number(moveRight) - Number(moveLeft); - direction.normalize(); // this ensures consistent movements in all directions - - if (moveForward || moveBackward) velocity.z -= direction.z * 400.0 * delta; - if (moveLeft || moveRight) velocity.x -= direction.x * 400.0 * delta; - - if (onObject === true) { - velocity.y = Math.max(0, velocity.y); - canJump = true; - } - - controls.moveRight(-velocity.x * delta); - controls.moveForward(-velocity.z * delta); - - controls.object.position.y += velocity.y * delta; // new behavior - - if (controls.object.position.y < 10) { - velocity.y = 0; - controls.object.position.y = 10; - - canJump = true; - } - } - - prevTime = time; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/misc_controls_trackball.ts b/examples-testing/examples/misc_controls_trackball.ts deleted file mode 100644 index c2512a352..000000000 --- a/examples-testing/examples/misc_controls_trackball.ts +++ /dev/null @@ -1,138 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { TrackballControls } from 'three/addons/controls/TrackballControls.js'; - -let perspectiveCamera, orthographicCamera, controls, scene, renderer, stats; - -const params = { - orthographicCamera: false, -}; - -const frustumSize = 400; - -init(); - -function init() { - const aspect = window.innerWidth / window.innerHeight; - - perspectiveCamera = new THREE.PerspectiveCamera(60, aspect, 1, 1000); - perspectiveCamera.position.z = 500; - - orthographicCamera = new THREE.OrthographicCamera( - (frustumSize * aspect) / -2, - (frustumSize * aspect) / 2, - frustumSize / 2, - frustumSize / -2, - 1, - 1000, - ); - orthographicCamera.position.z = 500; - - // world - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xcccccc); - scene.fog = new THREE.FogExp2(0xcccccc, 0.002); - - const geometry = new THREE.ConeGeometry(10, 30, 4, 1); - const material = new THREE.MeshPhongMaterial({ color: 0xffffff, flatShading: true }); - - const mesh = new THREE.InstancedMesh(geometry, material, 500); - const dummy = new THREE.Object3D(); - - for (let i = 0; i < 500; i++) { - dummy.position.x = (Math.random() - 0.5) * 1000; - dummy.position.y = (Math.random() - 0.5) * 1000; - dummy.position.z = (Math.random() - 0.5) * 1000; - - dummy.updateMatrix(); - mesh.setMatrixAt(i, dummy.matrix); - } - - scene.add(mesh); - - // lights - - const dirLight1 = new THREE.DirectionalLight(0xffffff, 3); - dirLight1.position.set(1, 1, 1); - scene.add(dirLight1); - - const dirLight2 = new THREE.DirectionalLight(0x002288, 3); - dirLight2.position.set(-1, -1, -1); - scene.add(dirLight2); - - const ambientLight = new THREE.AmbientLight(0x555555); - scene.add(ambientLight); - - // renderer - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - stats = new Stats(); - document.body.appendChild(stats.dom); - - // - - const gui = new GUI(); - gui.add(params, 'orthographicCamera') - .name('use orthographic') - .onChange(function (value) { - controls.dispose(); - - createControls(value ? orthographicCamera : perspectiveCamera); - }); - - // - - window.addEventListener('resize', onWindowResize); - - createControls(perspectiveCamera); -} - -function createControls(camera) { - controls = new TrackballControls(camera, renderer.domElement); - - controls.rotateSpeed = 1.0; - controls.zoomSpeed = 1.2; - controls.panSpeed = 0.8; - - controls.keys = ['KeyA', 'KeyS', 'KeyD']; -} - -function onWindowResize() { - const aspect = window.innerWidth / window.innerHeight; - - perspectiveCamera.aspect = aspect; - perspectiveCamera.updateProjectionMatrix(); - - orthographicCamera.left = (-frustumSize * aspect) / 2; - orthographicCamera.right = (frustumSize * aspect) / 2; - orthographicCamera.top = frustumSize / 2; - orthographicCamera.bottom = -frustumSize / 2; - orthographicCamera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - controls.handleResize(); -} - -function animate() { - controls.update(); - - render(); - - stats.update(); -} - -function render() { - const camera = params.orthographicCamera ? orthographicCamera : perspectiveCamera; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/misc_controls_transform.ts b/examples-testing/examples/misc_controls_transform.ts deleted file mode 100644 index 6f7793d33..000000000 --- a/examples-testing/examples/misc_controls_transform.ts +++ /dev/null @@ -1,182 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { TransformControls } from 'three/addons/controls/TransformControls.js'; - -let cameraPersp, cameraOrtho, currentCamera; -let scene, renderer, control, orbit; - -init(); -render(); - -function init() { - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - document.body.appendChild(renderer.domElement); - - const aspect = window.innerWidth / window.innerHeight; - - const frustumSize = 5; - - cameraPersp = new THREE.PerspectiveCamera(50, aspect, 0.1, 100); - cameraOrtho = new THREE.OrthographicCamera( - -frustumSize * aspect, - frustumSize * aspect, - frustumSize, - -frustumSize, - 0.1, - 100, - ); - currentCamera = cameraPersp; - - currentCamera.position.set(5, 2.5, 5); - - scene = new THREE.Scene(); - scene.add(new THREE.GridHelper(5, 10, 0x888888, 0x444444)); - - const ambientLight = new THREE.AmbientLight(0xffffff); - scene.add(ambientLight); - - const light = new THREE.DirectionalLight(0xffffff, 4); - light.position.set(1, 1, 1); - scene.add(light); - - const texture = new THREE.TextureLoader().load('textures/crate.gif', render); - texture.colorSpace = THREE.SRGBColorSpace; - texture.anisotropy = renderer.capabilities.getMaxAnisotropy(); - - const geometry = new THREE.BoxGeometry(); - const material = new THREE.MeshLambertMaterial({ map: texture }); - - orbit = new OrbitControls(currentCamera, renderer.domElement); - orbit.update(); - orbit.addEventListener('change', render); - - control = new TransformControls(currentCamera, renderer.domElement); - control.addEventListener('change', render); - control.addEventListener('dragging-changed', function (event) { - orbit.enabled = !event.value; - }); - - const mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - control.attach(mesh); - - const gizmo = control.getHelper(); - scene.add(gizmo); - - window.addEventListener('resize', onWindowResize); - - window.addEventListener('keydown', function (event) { - switch (event.key) { - case 'q': - control.setSpace(control.space === 'local' ? 'world' : 'local'); - break; - - case 'Shift': - control.setTranslationSnap(1); - control.setRotationSnap(THREE.MathUtils.degToRad(15)); - control.setScaleSnap(0.25); - break; - - case 'w': - control.setMode('translate'); - break; - - case 'e': - control.setMode('rotate'); - break; - - case 'r': - control.setMode('scale'); - break; - - case 'c': - const position = currentCamera.position.clone(); - - currentCamera = currentCamera.isPerspectiveCamera ? cameraOrtho : cameraPersp; - currentCamera.position.copy(position); - - orbit.object = currentCamera; - control.camera = currentCamera; - - currentCamera.lookAt(orbit.target.x, orbit.target.y, orbit.target.z); - onWindowResize(); - break; - - case 'v': - const randomFoV = Math.random() + 0.1; - const randomZoom = Math.random() + 0.1; - - cameraPersp.fov = randomFoV * 160; - cameraOrtho.bottom = -randomFoV * 500; - cameraOrtho.top = randomFoV * 500; - - cameraPersp.zoom = randomZoom * 5; - cameraOrtho.zoom = randomZoom * 5; - onWindowResize(); - break; - - case '+': - case '=': - control.setSize(control.size + 0.1); - break; - - case '-': - case '_': - control.setSize(Math.max(control.size - 0.1, 0.1)); - break; - - case 'x': - control.showX = !control.showX; - break; - - case 'y': - control.showY = !control.showY; - break; - - case 'z': - control.showZ = !control.showZ; - break; - - case ' ': - control.enabled = !control.enabled; - break; - - case 'Escape': - control.reset(); - break; - } - }); - - window.addEventListener('keyup', function (event) { - switch (event.key) { - case 'Shift': - control.setTranslationSnap(null); - control.setRotationSnap(null); - control.setScaleSnap(null); - break; - } - }); -} - -function onWindowResize() { - const aspect = window.innerWidth / window.innerHeight; - - cameraPersp.aspect = aspect; - cameraPersp.updateProjectionMatrix(); - - cameraOrtho.left = cameraOrtho.bottom * aspect; - cameraOrtho.right = cameraOrtho.top * aspect; - cameraOrtho.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - render(); -} - -function render() { - renderer.render(scene, currentCamera); -} diff --git a/examples-testing/examples/misc_exporter_draco.ts b/examples-testing/examples/misc_exporter_draco.ts deleted file mode 100644 index 40a62fb18..000000000 --- a/examples-testing/examples/misc_exporter_draco.ts +++ /dev/null @@ -1,117 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { DRACOExporter } from 'three/addons/exporters/DRACOExporter.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let scene, camera, renderer, exporter, mesh; - -const params = { - export: exportFile, -}; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(4, 2, 4); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xa0a0a0); - scene.fog = new THREE.Fog(0xa0a0a0, 4, 20); - - exporter = new DRACOExporter(); - - // - - const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444, 3); - hemiLight.position.set(0, 20, 0); - scene.add(hemiLight); - - const directionalLight = new THREE.DirectionalLight(0xffffff, 3); - directionalLight.position.set(0, 20, 10); - directionalLight.castShadow = true; - directionalLight.shadow.camera.top = 2; - directionalLight.shadow.camera.bottom = -2; - directionalLight.shadow.camera.left = -2; - directionalLight.shadow.camera.right = 2; - scene.add(directionalLight); - - // ground - - const ground = new THREE.Mesh( - new THREE.PlaneGeometry(40, 40), - new THREE.MeshPhongMaterial({ color: 0xbbbbbb, depthWrite: false }), - ); - ground.rotation.x = -Math.PI / 2; - ground.receiveShadow = true; - scene.add(ground); - - const grid = new THREE.GridHelper(40, 20, 0x000000, 0x000000); - grid.material.opacity = 0.2; - grid.material.transparent = true; - scene.add(grid); - - // export mesh - - const geometry = new THREE.TorusKnotGeometry(0.75, 0.2, 200, 30); - const material = new THREE.MeshPhongMaterial({ color: 0x00ff00 }); - mesh = new THREE.Mesh(geometry, material); - mesh.castShadow = true; - mesh.position.y = 1.5; - scene.add(mesh); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.shadowMap.enabled = true; - document.body.appendChild(renderer.domElement); - - // - - const controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, 1.5, 0); - controls.update(); - - // - - window.addEventListener('resize', onWindowResize); - - const gui = new GUI(); - - gui.add(params, 'export').name('Export DRC'); - gui.open(); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - renderer.render(scene, camera); -} - -function exportFile() { - const result = exporter.parse(mesh); - saveArrayBuffer(result, 'file.drc'); -} - -const link = document.createElement('a'); -link.style.display = 'none'; -document.body.appendChild(link); - -function save(blob, filename) { - link.href = URL.createObjectURL(blob); - link.download = filename; - link.click(); -} - -function saveArrayBuffer(buffer, filename) { - save(new Blob([buffer], { type: 'application/octet-stream' }), filename); -} diff --git a/examples-testing/examples/misc_exporter_exr.ts b/examples-testing/examples/misc_exporter_exr.ts deleted file mode 100644 index 014ea58a0..000000000 --- a/examples-testing/examples/misc_exporter_exr.ts +++ /dev/null @@ -1,158 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { EXRExporter, ZIP_COMPRESSION, ZIPS_COMPRESSION, NO_COMPRESSION } from 'three/addons/exporters/EXRExporter.js'; -import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let scene, camera, renderer, exporter, mesh, controls, renderTarget, dataTexture; - -const params = { - target: 'pmrem', - type: 'HalfFloatType', - compression: 'ZIP', - export: exportFile, -}; - -init(); - -function init() { - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(10, 0, 0); - - scene = new THREE.Scene(); - - exporter = new EXRExporter(); - const hdrLoader = new HDRLoader(); - - // - - const pmremGenerator = new THREE.PMREMGenerator(renderer); - pmremGenerator.compileEquirectangularShader(); - - hdrLoader.load('textures/equirectangular/san_giuseppe_bridge_2k.hdr', function (texture) { - texture.mapping = THREE.EquirectangularReflectionMapping; - - renderTarget = pmremGenerator.fromEquirectangular(texture); - scene.background = renderTarget.texture; - }); - - createDataTexture(); - - // - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - controls.rotateSpeed = -0.25; // negative, to track mouse pointer - - // - - window.addEventListener('resize', onWindowResize); - - const gui = new GUI(); - - const input = gui.addFolder('Input'); - input.add(params, 'target').options(['pmrem', 'data-texture']).onChange(swapScene); - - const options = gui.addFolder('Output Options'); - options.add(params, 'type').options(['FloatType', 'HalfFloatType']); - options.add(params, 'compression').options(['ZIP', 'ZIPS', 'NONE']); - - gui.add(params, 'export').name('Export EXR'); - gui.open(); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - controls.update(); - renderer.render(scene, camera); -} - -function createDataTexture() { - const normal = new THREE.Vector3(); - const coord = new THREE.Vector2(); - const size = 800, - radius = 320, - factor = (Math.PI * 0.5) / radius; - const data = new Float32Array(4 * size * size); - - for (let i = 0; i < size; i++) { - for (let j = 0; j < size; j++) { - const idx = i * size * 4 + j * 4; - coord.set(j, i).subScalar(size / 2); - - if (coord.length() < radius) - normal.set(Math.sin(coord.x * factor), Math.sin(coord.y * factor), Math.cos(coord.x * factor)); - else normal.set(0, 0, 1); - - data[idx + 0] = 0.5 + 0.5 * normal.x; - data[idx + 1] = 0.5 + 0.5 * normal.y; - data[idx + 2] = 0.5 + 0.5 * normal.z; - data[idx + 3] = 1; - } - } - - dataTexture = new THREE.DataTexture(data, size, size, THREE.RGBAFormat, THREE.FloatType); - dataTexture.needsUpdate = true; - - const material = new THREE.MeshBasicMaterial({ map: dataTexture }); - const quad = new THREE.PlaneGeometry(50, 50); - mesh = new THREE.Mesh(quad, material); - mesh.visible = false; - - scene.add(mesh); -} - -function swapScene() { - if (params.target == 'pmrem') { - camera.position.set(10, 0, 0); - controls.enabled = true; - scene.background = renderTarget.texture; - mesh.visible = false; - } else { - camera.position.set(0, 0, 70); - controls.enabled = false; - scene.background = new THREE.Color(0, 0, 0); - mesh.visible = true; - } -} - -async function exportFile() { - let result, exportType, exportCompression; - - if (params.type == 'HalfFloatType') exportType = THREE.HalfFloatType; - else exportType = THREE.FloatType; - - if (params.compression == 'ZIP') exportCompression = ZIP_COMPRESSION; - else if (params.compression == 'ZIPS') exportCompression = ZIPS_COMPRESSION; - else exportCompression = NO_COMPRESSION; - - if (params.target == 'pmrem') - result = await exporter.parse(renderer, renderTarget, { type: exportType, compression: exportCompression }); - else result = await exporter.parse(dataTexture, { type: exportType, compression: exportCompression }); - - saveArrayBuffer(result, params.target + '.exr'); -} - -function saveArrayBuffer(buffer, filename) { - const blob = new Blob([buffer], { type: 'image/x-exr' }); - const link = document.createElement('a'); - - link.href = URL.createObjectURL(blob); - link.download = filename; - link.click(); -} diff --git a/examples-testing/examples/misc_exporter_gltf.ts b/examples-testing/examples/misc_exporter_gltf.ts deleted file mode 100644 index f6fe03450..000000000 --- a/examples-testing/examples/misc_exporter_gltf.ts +++ /dev/null @@ -1,534 +0,0 @@ -import * as THREE from 'three'; - -import { GLTFExporter } from 'three/addons/exporters/GLTFExporter.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js'; -import { MeshoptDecoder } from 'three/addons/libs/meshopt_decoder.module.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import * as TextureUtils from 'three/addons/utils/WebGLTextureUtils.js'; - -function exportGLTF(input) { - const gltfExporter = new GLTFExporter().setTextureUtils(TextureUtils); - - const options = { - trs: params.trs, - onlyVisible: params.onlyVisible, - binary: params.binary, - maxTextureSize: params.maxTextureSize, - }; - gltfExporter.parse( - input, - function (result) { - if (result instanceof ArrayBuffer) { - saveArrayBuffer(result, 'scene.glb'); - } else { - const output = JSON.stringify(result, null, 2); - console.log(output); - saveString(output, 'scene.gltf'); - } - }, - function (error) { - console.log('An error happened during parsing', error); - }, - options, - ); -} - -const link = document.createElement('a'); -link.style.display = 'none'; -document.body.appendChild(link); // Firefox workaround, see #6594 - -function save(blob, filename) { - link.href = URL.createObjectURL(blob); - link.download = filename; - link.click(); - - // URL.revokeObjectURL( url ); breaks Firefox... -} - -function saveString(text, filename) { - save(new Blob([text], { type: 'text/plain' }), filename); -} - -function saveArrayBuffer(buffer, filename) { - save(new Blob([buffer], { type: 'application/octet-stream' }), filename); -} - -let container; - -let camera, object, object2, material, geometry, scene1, scene2, renderer; -let gridHelper, sphere, model, coffeemat, webpBox; - -const params = { - trs: false, - onlyVisible: true, - binary: false, - maxTextureSize: 4096, - exportScene1: exportScene1, - exportScenes: exportScenes, - exportSphere: exportSphere, - exportModel: exportModel, - exportObjects: exportObjects, - exportSceneObject: exportSceneObject, - exportCompressedObject: exportCompressedObject, - exportWebPModel: exportWebPModel, -}; - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - // Make linear gradient texture - - const data = new Uint8ClampedArray(100 * 100 * 4); - - for (let y = 0; y < 100; y++) { - for (let x = 0; x < 100; x++) { - const stride = 4 * (100 * y + x); - - data[stride] = Math.round((255 * y) / 99); - data[stride + 1] = Math.round(255 - (255 * y) / 99); - data[stride + 2] = 0; - data[stride + 3] = 255; - } - } - - const gradientTexture = new THREE.DataTexture(data, 100, 100, THREE.RGBAFormat); - gradientTexture.minFilter = THREE.LinearFilter; - gradientTexture.magFilter = THREE.LinearFilter; - gradientTexture.needsUpdate = true; - - scene1 = new THREE.Scene(); - scene1.name = 'Scene1'; - - // --------------------------------------------------------------------- - // Perspective Camera - // --------------------------------------------------------------------- - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000); - camera.position.set(600, 400, 0); - - camera.name = 'PerspectiveCamera'; - scene1.add(camera); - - // --------------------------------------------------------------------- - // Ambient light - // --------------------------------------------------------------------- - const ambientLight = new THREE.AmbientLight(0xcccccc); - ambientLight.name = 'AmbientLight'; - scene1.add(ambientLight); - - // --------------------------------------------------------------------- - // DirectLight - // --------------------------------------------------------------------- - const dirLight = new THREE.DirectionalLight(0xffffff, 3); - dirLight.target.position.set(0, 0, -1); - dirLight.add(dirLight.target); - dirLight.lookAt(-1, -1, 0); - dirLight.name = 'DirectionalLight'; - scene1.add(dirLight); - - // --------------------------------------------------------------------- - // Grid - // --------------------------------------------------------------------- - gridHelper = new THREE.GridHelper(2000, 20, 0xc1c1c1, 0x8d8d8d); - gridHelper.position.y = -50; - gridHelper.name = 'Grid'; - scene1.add(gridHelper); - - // --------------------------------------------------------------------- - // Axes - // --------------------------------------------------------------------- - const axes = new THREE.AxesHelper(500); - axes.name = 'AxesHelper'; - scene1.add(axes); - - // --------------------------------------------------------------------- - // Simple geometry with basic material - // --------------------------------------------------------------------- - // Icosahedron - const mapGrid = new THREE.TextureLoader().load('textures/uv_grid_opengl.jpg'); - mapGrid.wrapS = mapGrid.wrapT = THREE.RepeatWrapping; - mapGrid.colorSpace = THREE.SRGBColorSpace; - material = new THREE.MeshBasicMaterial({ - color: 0xffffff, - map: mapGrid, - }); - - object = new THREE.Mesh(new THREE.IcosahedronGeometry(75, 0), material); - object.position.set(-200, 0, 200); - object.name = 'Icosahedron'; - scene1.add(object); - - // Octahedron - material = new THREE.MeshBasicMaterial({ - color: 0x0000ff, - wireframe: true, - }); - object = new THREE.Mesh(new THREE.OctahedronGeometry(75, 1), material); - object.position.set(0, 0, 200); - object.name = 'Octahedron'; - scene1.add(object); - - // Tetrahedron - material = new THREE.MeshBasicMaterial({ - color: 0xff0000, - transparent: true, - opacity: 0.5, - }); - - object = new THREE.Mesh(new THREE.TetrahedronGeometry(75, 0), material); - object.position.set(200, 0, 200); - object.name = 'Tetrahedron'; - scene1.add(object); - - // --------------------------------------------------------------------- - // Buffered geometry primitives - // --------------------------------------------------------------------- - // Sphere - material = new THREE.MeshStandardMaterial({ - color: 0xffff00, - metalness: 0.5, - roughness: 1.0, - flatShading: true, - }); - material.map = gradientTexture; - material.bumpMap = mapGrid; - sphere = new THREE.Mesh(new THREE.SphereGeometry(70, 10, 10), material); - sphere.position.set(0, 0, 0); - sphere.name = 'Sphere'; - scene1.add(sphere); - - // Cylinder - material = new THREE.MeshStandardMaterial({ - color: 0xff00ff, - flatShading: true, - }); - object = new THREE.Mesh(new THREE.CylinderGeometry(10, 80, 100), material); - object.position.set(200, 0, 0); - object.name = 'Cylinder'; - scene1.add(object); - - // TorusKnot - material = new THREE.MeshStandardMaterial({ - color: 0xff0000, - roughness: 1, - }); - object = new THREE.Mesh(new THREE.TorusKnotGeometry(50, 15, 40, 10), material); - object.position.set(-200, 0, 0); - object.name = 'Cylinder'; - scene1.add(object); - - // --------------------------------------------------------------------- - // Hierarchy - // --------------------------------------------------------------------- - const mapWood = new THREE.TextureLoader().load('textures/hardwood2_diffuse.jpg'); - material = new THREE.MeshStandardMaterial({ map: mapWood, side: THREE.DoubleSide }); - - object = new THREE.Mesh(new THREE.BoxGeometry(40, 100, 100), material); - object.position.set(-200, 0, 400); - object.name = 'Cube'; - scene1.add(object); - - object2 = new THREE.Mesh(new THREE.BoxGeometry(40, 40, 40, 2, 2, 2), material); - object2.position.set(0, 0, 50); - object2.rotation.set(0, 45, 0); - object2.name = 'SubCube'; - object.add(object2); - - // --------------------------------------------------------------------- - // Groups - // --------------------------------------------------------------------- - const group1 = new THREE.Group(); - group1.name = 'Group'; - scene1.add(group1); - - const group2 = new THREE.Group(); - group2.name = 'subGroup'; - group2.position.set(0, 50, 0); - group1.add(group2); - - object2 = new THREE.Mesh(new THREE.BoxGeometry(30, 30, 30), material); - object2.name = 'Cube in group'; - object2.position.set(0, 0, 400); - group2.add(object2); - - // --------------------------------------------------------------------- - // THREE.Line Strip - // --------------------------------------------------------------------- - geometry = new THREE.BufferGeometry(); - let numPoints = 100; - let positions = new Float32Array(numPoints * 3); - - for (let i = 0; i < numPoints; i++) { - positions[i * 3] = i; - positions[i * 3 + 1] = Math.sin(i / 2) * 20; - positions[i * 3 + 2] = 0; - } - - geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); - object = new THREE.Line(geometry, new THREE.LineBasicMaterial({ color: 0xffff00 })); - object.position.set(-50, 0, -200); - scene1.add(object); - - // --------------------------------------------------------------------- - // THREE.Line Loop - // --------------------------------------------------------------------- - geometry = new THREE.BufferGeometry(); - numPoints = 5; - const radius = 70; - positions = new Float32Array(numPoints * 3); - - for (let i = 0; i < numPoints; i++) { - const s = (i * Math.PI * 2) / numPoints; - positions[i * 3] = radius * Math.sin(s); - positions[i * 3 + 1] = radius * Math.cos(s); - positions[i * 3 + 2] = 0; - } - - geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); - object = new THREE.LineLoop(geometry, new THREE.LineBasicMaterial({ color: 0xffff00 })); - object.position.set(0, 0, -200); - - scene1.add(object); - - // --------------------------------------------------------------------- - // THREE.Points - // --------------------------------------------------------------------- - numPoints = 100; - const pointsArray = new Float32Array(numPoints * 3); - for (let i = 0; i < numPoints; i++) { - pointsArray[3 * i] = -50 + Math.random() * 100; - pointsArray[3 * i + 1] = Math.random() * 100; - pointsArray[3 * i + 2] = -50 + Math.random() * 100; - } - - const pointsGeo = new THREE.BufferGeometry(); - pointsGeo.setAttribute('position', new THREE.BufferAttribute(pointsArray, 3)); - - const pointsMaterial = new THREE.PointsMaterial({ color: 0xffff00, size: 5 }); - const pointCloud = new THREE.Points(pointsGeo, pointsMaterial); - pointCloud.name = 'Points'; - pointCloud.position.set(-200, 0, -200); - scene1.add(pointCloud); - - // --------------------------------------------------------------------- - // Ortho camera - // --------------------------------------------------------------------- - - const height = 1000; // frustum height - const aspect = window.innerWidth / window.innerHeight; - - const cameraOrtho = new THREE.OrthographicCamera(-height * aspect, height * aspect, height, -height, 0, 2000); - cameraOrtho.position.set(600, 400, 0); - cameraOrtho.lookAt(0, 0, 0); - scene1.add(cameraOrtho); - cameraOrtho.name = 'OrthographicCamera'; - - material = new THREE.MeshLambertMaterial({ - color: 0xffff00, - side: THREE.DoubleSide, - }); - - object = new THREE.Mesh(new THREE.CircleGeometry(50, 20, 0, Math.PI * 2), material); - object.position.set(200, 0, -400); - scene1.add(object); - - object = new THREE.Mesh(new THREE.RingGeometry(10, 50, 20, 5, 0, Math.PI * 2), material); - object.position.set(0, 0, -400); - scene1.add(object); - - object = new THREE.Mesh(new THREE.CylinderGeometry(25, 75, 100, 40, 5), material); - object.position.set(-200, 0, -400); - scene1.add(object); - - // - const points = []; - - for (let i = 0; i < 50; i++) { - points.push(new THREE.Vector2(Math.sin(i * 0.2) * Math.sin(i * 0.1) * 15 + 50, (i - 5) * 2)); - } - - object = new THREE.Mesh(new THREE.LatheGeometry(points, 20), material); - object.position.set(200, 0, 400); - scene1.add(object); - - // --------------------------------------------------------------------- - // Big red box hidden just for testing `onlyVisible` option - // --------------------------------------------------------------------- - material = new THREE.MeshBasicMaterial({ - color: 0xff0000, - }); - object = new THREE.Mesh(new THREE.BoxGeometry(200, 200, 200), material); - object.position.set(0, 0, 0); - object.name = 'CubeHidden'; - object.visible = false; - scene1.add(object); - - // --------------------------------------------------------------------- - // Model requiring KHR_mesh_quantization - // --------------------------------------------------------------------- - const loader = new GLTFLoader(); - loader.load('models/gltf/ShaderBall.glb', function (gltf) { - model = gltf.scene; - model.scale.setScalar(50); - model.position.set(200, -40, -200); - scene1.add(model); - }); - - // --------------------------------------------------------------------- - // Model requiring KHR_mesh_quantization - // --------------------------------------------------------------------- - - material = new THREE.MeshBasicMaterial({ - color: 0xffffff, - }); - object = new THREE.InstancedMesh(new THREE.BoxGeometry(10, 10, 10, 2, 2, 2), material, 50); - const matrix = new THREE.Matrix4(); - const color = new THREE.Color(); - for (let i = 0; i < 50; i++) { - matrix.setPosition(Math.random() * 100 - 50, Math.random() * 100 - 50, Math.random() * 100 - 50); - object.setMatrixAt(i, matrix); - object.setColorAt(i, color.setHSL(i / 50, 1, 0.5)); - } - - object.position.set(400, 0, 200); - scene1.add(object); - - // --------------------------------------------------------------------- - // 2nd THREE.Scene - // --------------------------------------------------------------------- - scene2 = new THREE.Scene(); - object = new THREE.Mesh(new THREE.BoxGeometry(100, 100, 100), material); - object.position.set(0, 0, 0); - object.name = 'Cube2ndScene'; - scene2.name = 'Scene2'; - scene2.add(object); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = 1; - - container.appendChild(renderer.domElement); - - // - - window.addEventListener('resize', onWindowResize); - - // --------------------------------------------------------------------- - // Exporting compressed textures and meshes (KTX2 / Draco / Meshopt) - // --------------------------------------------------------------------- - const ktx2Loader = new KTX2Loader().setTranscoderPath('jsm/libs/basis/').detectSupport(renderer); - - const gltfLoader = new GLTFLoader().setPath('models/gltf/'); - gltfLoader.setKTX2Loader(ktx2Loader); - gltfLoader.setMeshoptDecoder(MeshoptDecoder); - gltfLoader.load('coffeemat.glb', function (gltf) { - gltf.scene.position.x = 400; - gltf.scene.position.z = -200; - - scene1.add(gltf.scene); - - coffeemat = gltf.scene; - }); - - // --------------------------------------------------------------------- - // Box with WebP texture (EXT_texture_webp) - // --------------------------------------------------------------------- - const canvas = document.createElement('canvas'); - canvas.width = 64; - canvas.height = 64; - const ctx = canvas.getContext('2d'); - ctx.fillStyle = '#005BBB'; - ctx.fillRect(0, 0, 64, 64); - ctx.fillStyle = '#FFD500'; - ctx.fillRect(16, 16, 32, 32); - - const webpTexture = new THREE.CanvasTexture(canvas); - webpTexture.userData.mimeType = 'image/webp'; - webpTexture.colorSpace = THREE.SRGBColorSpace; - - webpBox = new THREE.Mesh(new THREE.BoxGeometry(100, 100, 100), new THREE.MeshBasicMaterial({ map: webpTexture })); - webpBox.position.set(400, 0, 0); - webpBox.name = 'WebPBox'; - scene1.add(webpBox); - - // - - const gui = new GUI(); - - let h = gui.addFolder('Settings'); - h.add(params, 'trs').name('Use TRS'); - h.add(params, 'onlyVisible').name('Only Visible Objects'); - h.add(params, 'binary').name('Binary (GLB)'); - h.add(params, 'maxTextureSize', 2, 8192).name('Max Texture Size').step(1); - - h = gui.addFolder('Export'); - h.add(params, 'exportScene1').name('Export Scene 1'); - h.add(params, 'exportScenes').name('Export Scene 1 and 2'); - h.add(params, 'exportSphere').name('Export Sphere'); - h.add(params, 'exportModel').name('Export Model'); - h.add(params, 'exportObjects').name('Export Sphere With Grid'); - h.add(params, 'exportSceneObject').name('Export Scene 1 and Object'); - h.add(params, 'exportCompressedObject').name('Export Coffeemat (from compressed data)'); - h.add(params, 'exportWebPModel').name('Export WebP Model (EXT_texture_webp)'); - - gui.open(); -} - -function exportScene1() { - exportGLTF(scene1); -} - -function exportScenes() { - exportGLTF([scene1, scene2]); -} - -function exportSphere() { - exportGLTF(sphere); -} - -function exportModel() { - exportGLTF(model); -} - -function exportObjects() { - exportGLTF([sphere, gridHelper]); -} - -function exportSceneObject() { - exportGLTF([scene1, gridHelper]); -} - -function exportCompressedObject() { - exportGLTF([coffeemat]); -} - -function exportWebPModel() { - exportGLTF(webpBox); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - const timer = Date.now() * 0.0001; - - camera.position.x = Math.cos(timer) * 800; - camera.position.z = Math.sin(timer) * 800; - - camera.lookAt(scene1.position); - renderer.render(scene1, camera); -} diff --git a/examples-testing/examples/misc_exporter_ktx2.ts b/examples-testing/examples/misc_exporter_ktx2.ts deleted file mode 100644 index c96889a24..000000000 --- a/examples-testing/examples/misc_exporter_ktx2.ts +++ /dev/null @@ -1,145 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { KTX2Exporter } from 'three/addons/exporters/KTX2Exporter.js'; -import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let scene, camera, renderer, exporter, mesh, controls, renderTarget, dataTexture; - -const params = { - target: 'pmrem', - export: exportFile, -}; - -init(); - -function init() { - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.toneMapping = THREE.AgXToneMapping; - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(10, 0, 0); - - scene = new THREE.Scene(); - - exporter = new KTX2Exporter(); - const hdrLoader = new HDRLoader(); - - // - - const pmremGenerator = new THREE.PMREMGenerator(renderer); - pmremGenerator.compileEquirectangularShader(); - - hdrLoader.load('textures/equirectangular/venice_sunset_1k.hdr', function (texture) { - texture.mapping = THREE.EquirectangularReflectionMapping; - - renderTarget = pmremGenerator.fromEquirectangular(texture); - scene.background = renderTarget.texture; - }); - - createDataTexture(); - - // - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - controls.rotateSpeed = -0.25; // negative, to track mouse pointer - - // - - window.addEventListener('resize', onWindowResize); - - const gui = new GUI(); - - gui.add(params, 'target').options(['pmrem', 'data-texture']).onChange(swapScene); - gui.add(params, 'export').name('Export KTX2'); - gui.open(); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - controls.update(); - renderer.render(scene, camera); -} - -function createDataTexture() { - const normal = new THREE.Vector3(); - const coord = new THREE.Vector2(); - const size = 800, - radius = 320, - factor = (Math.PI * 0.5) / radius; - const data = new Float32Array(4 * size * size); - - for (let i = 0; i < size; i++) { - for (let j = 0; j < size; j++) { - const idx = i * size * 4 + j * 4; - coord.set(j, i).subScalar(size / 2); - - if (coord.length() < radius) - normal.set(Math.sin(coord.x * factor), Math.sin(coord.y * factor), Math.cos(coord.x * factor)); - else normal.set(0, 0, 1); - - data[idx + 0] = 0.5 + 0.5 * normal.x; - data[idx + 1] = 0.5 + 0.5 * normal.y; - data[idx + 2] = 0.5 + 0.5 * normal.z; - data[idx + 3] = 1; - } - } - - dataTexture = new THREE.DataTexture(data, size, size, THREE.RGBAFormat, THREE.FloatType); - dataTexture.needsUpdate = true; - - const material = new THREE.MeshBasicMaterial({ map: dataTexture }); - const quad = new THREE.PlaneGeometry(50, 50); - mesh = new THREE.Mesh(quad, material); - mesh.visible = false; - - scene.add(mesh); -} - -function swapScene() { - if (params.target == 'pmrem') { - camera.position.set(10, 0, 0); - controls.enabled = true; - scene.background = renderTarget.texture; - mesh.visible = false; - renderer.toneMapping = THREE.AgXToneMapping; - } else { - camera.position.set(0, 0, 70); - controls.enabled = false; - scene.background = new THREE.Color(0, 0, 0); - mesh.visible = true; - renderer.toneMapping = THREE.NoToneMapping; - } -} - -async function exportFile() { - let result; - - if (params.target == 'pmrem') result = await exporter.parse(renderer, renderTarget); - else result = await exporter.parse(dataTexture); - - saveArrayBuffer(result, params.target + '.ktx2'); -} - -function saveArrayBuffer(buffer, filename) { - const blob = new Blob([buffer], { type: 'image/ktx2' }); - const link = document.createElement('a'); - - link.href = URL.createObjectURL(blob); - link.download = filename; - link.click(); -} diff --git a/examples-testing/examples/misc_exporter_obj.ts b/examples-testing/examples/misc_exporter_obj.ts deleted file mode 100644 index 025034daf..000000000 --- a/examples-testing/examples/misc_exporter_obj.ts +++ /dev/null @@ -1,192 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { OBJExporter } from 'three/addons/exporters/OBJExporter.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let camera, scene, renderer; - -const params = { - addTriangle: addTriangle, - addCube: addCube, - addCylinder: addCylinder, - addMultiple: addMultiple, - addTransformed: addTransformed, - addPoints: addPoints, - exportToObj: exportToObj, -}; - -init(); - -function init() { - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(0, 0, 400); - - scene = new THREE.Scene(); - - const ambientLight = new THREE.AmbientLight(0xffffff); - scene.add(ambientLight); - - const directionalLight = new THREE.DirectionalLight(0xffffff, 2.5); - directionalLight.position.set(0, 1, 1); - scene.add(directionalLight); - - const gui = new GUI(); - - let h = gui.addFolder('Geometry Selection'); - h.add(params, 'addTriangle').name('Triangle'); - h.add(params, 'addCube').name('Cube'); - h.add(params, 'addCylinder').name('Cylinder'); - h.add(params, 'addMultiple').name('Multiple objects'); - h.add(params, 'addTransformed').name('Transformed objects'); - h.add(params, 'addPoints').name('Point Cloud'); - - h = gui.addFolder('Export'); - h.add(params, 'exportToObj').name('Export OBJ'); - - gui.open(); - - addGeometry(1); - - window.addEventListener('resize', onWindowResize); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.enablePan = false; -} - -function exportToObj() { - const exporter = new OBJExporter(); - const result = exporter.parse(scene); - saveString(result, 'object.obj'); -} - -function addGeometry(type) { - for (let i = 0; i < scene.children.length; i++) { - const child = scene.children[i]; - - if (child.isMesh || child.isPoints) { - child.geometry.dispose(); - scene.remove(child); - i--; - } - } - - if (type === 1) { - const material = new THREE.MeshLambertMaterial({ color: 0x00cc00 }); - const geometry = generateTriangleGeometry(); - - scene.add(new THREE.Mesh(geometry, material)); - } else if (type === 2) { - const material = new THREE.MeshLambertMaterial({ color: 0x00cc00 }); - const geometry = new THREE.BoxGeometry(100, 100, 100); - scene.add(new THREE.Mesh(geometry, material)); - } else if (type === 3) { - const material = new THREE.MeshLambertMaterial({ color: 0x00cc00 }); - const geometry = new THREE.CylinderGeometry(50, 50, 100, 30, 1); - scene.add(new THREE.Mesh(geometry, material)); - } else if (type === 4 || type === 5) { - const material = new THREE.MeshLambertMaterial({ color: 0x00cc00 }); - const geometry = generateTriangleGeometry(); - - const mesh = new THREE.Mesh(geometry, material); - mesh.position.x = -200; - scene.add(mesh); - - const geometry2 = new THREE.BoxGeometry(100, 100, 100); - const mesh2 = new THREE.Mesh(geometry2, material); - scene.add(mesh2); - - const geometry3 = new THREE.CylinderGeometry(50, 50, 100, 30, 1); - const mesh3 = new THREE.Mesh(geometry3, material); - mesh3.position.x = 200; - scene.add(mesh3); - - if (type === 5) { - mesh.rotation.y = Math.PI / 4.0; - mesh2.rotation.y = Math.PI / 4.0; - mesh3.rotation.y = Math.PI / 4.0; - } - } else if (type === 6) { - const points = [0, 0, 0, 100, 0, 0, 100, 100, 0, 0, 100, 0]; - const colors = [0.5, 0, 0, 0.5, 0, 0, 0, 0.5, 0, 0, 0.5, 0]; - - const geometry = new THREE.BufferGeometry(); - geometry.setAttribute('position', new THREE.Float32BufferAttribute(points, 3)); - geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); - - const material = new THREE.PointsMaterial({ size: 10, vertexColors: true }); - - const pointCloud = new THREE.Points(geometry, material); - pointCloud.name = 'point cloud'; - scene.add(pointCloud); - } -} - -function addTriangle() { - addGeometry(1); -} - -function addCube() { - addGeometry(2); -} - -function addCylinder() { - addGeometry(3); -} - -function addMultiple() { - addGeometry(4); -} - -function addTransformed() { - addGeometry(5); -} - -function addPoints() { - addGeometry(6); -} - -const link = document.createElement('a'); -link.style.display = 'none'; -document.body.appendChild(link); - -function save(blob, filename) { - link.href = URL.createObjectURL(blob); - link.download = filename; - link.click(); -} - -function saveString(text, filename) { - save(new Blob([text], { type: 'text/plain' }), filename); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - renderer.render(scene, camera); -} - -function generateTriangleGeometry() { - const geometry = new THREE.BufferGeometry(); - const vertices = []; - - vertices.push(-50, -50, 0); - vertices.push(50, -50, 0); - vertices.push(50, 50, 0); - - geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); - geometry.computeVertexNormals(); - - return geometry; -} diff --git a/examples-testing/examples/misc_exporter_ply.ts b/examples-testing/examples/misc_exporter_ply.ts deleted file mode 100644 index b7e324688..000000000 --- a/examples-testing/examples/misc_exporter_ply.ts +++ /dev/null @@ -1,156 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { PLYExporter } from 'three/addons/exporters/PLYExporter.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let scene, camera, renderer, exporter, mesh; - -const params = { - exportASCII: exportASCII, - exportBinaryBigEndian: exportBinaryBigEndian, - exportBinaryLittleEndian: exportBinaryLittleEndian, -}; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(4, 2, 4); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xa0a0a0); - scene.fog = new THREE.Fog(0xa0a0a0, 4, 20); - - exporter = new PLYExporter(); - - // - - const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444, 3); - hemiLight.position.set(0, 20, 0); - scene.add(hemiLight); - - const directionalLight = new THREE.DirectionalLight(0xffffff, 3); - directionalLight.position.set(0, 20, 10); - directionalLight.castShadow = true; - directionalLight.shadow.camera.top = 2; - directionalLight.shadow.camera.bottom = -2; - directionalLight.shadow.camera.left = -2; - directionalLight.shadow.camera.right = 2; - scene.add(directionalLight); - - // ground - - const ground = new THREE.Mesh( - new THREE.PlaneGeometry(40, 40), - new THREE.MeshPhongMaterial({ color: 0xcbcbcb, depthWrite: false }), - ); - ground.rotation.x = -Math.PI / 2; - ground.receiveShadow = true; - scene.add(ground); - - const grid = new THREE.GridHelper(40, 20, 0x000000, 0x000000); - grid.material.opacity = 0.2; - grid.material.transparent = true; - scene.add(grid); - - // export mesh - - const geometry = new THREE.BoxGeometry(); - const material = new THREE.MeshPhongMaterial({ vertexColors: true }); - - // color vertices based on vertex positions - const colors = geometry.getAttribute('position').array.slice(); - for (let i = 0, l = colors.length; i < l; i++) { - if (colors[i] > 0) colors[i] = 0.5; - else colors[i] = 0; - } - - geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3, false)); - - mesh = new THREE.Mesh(geometry, material); - mesh.castShadow = true; - mesh.position.y = 0.5; - scene.add(mesh); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.shadowMap.enabled = true; - document.body.appendChild(renderer.domElement); - - // - - const controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, 0.5, 0); - controls.update(); - - // - - window.addEventListener('resize', onWindowResize); - - const gui = new GUI(); - - gui.add(params, 'exportASCII').name('Export PLY (ASCII)'); - gui.add(params, 'exportBinaryBigEndian').name('Export PLY (Binary BE)'); - gui.add(params, 'exportBinaryLittleEndian').name('Export PLY (Binary LE)'); - gui.open(); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - renderer.render(scene, camera); -} - -function exportASCII() { - exporter.parse(mesh, function (result) { - saveString(result, 'box.ply'); - }); -} - -function exportBinaryBigEndian() { - exporter.parse( - mesh, - function (result) { - saveArrayBuffer(result, 'box.ply'); - }, - { binary: true }, - ); -} - -function exportBinaryLittleEndian() { - exporter.parse( - mesh, - function (result) { - saveArrayBuffer(result, 'box.ply'); - }, - { binary: true, littleEndian: true }, - ); -} - -const link = document.createElement('a'); -link.style.display = 'none'; -document.body.appendChild(link); - -function save(blob, filename) { - link.href = URL.createObjectURL(blob); - link.download = filename; - link.click(); -} - -function saveString(text, filename) { - save(new Blob([text], { type: 'text/plain' }), filename); -} - -function saveArrayBuffer(buffer, filename) { - save(new Blob([buffer], { type: 'application/octet-stream' }), filename); -} diff --git a/examples-testing/examples/misc_exporter_stl.ts b/examples-testing/examples/misc_exporter_stl.ts deleted file mode 100644 index ff6d6e2b5..000000000 --- a/examples-testing/examples/misc_exporter_stl.ts +++ /dev/null @@ -1,129 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { STLExporter } from 'three/addons/exporters/STLExporter.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let scene, camera, renderer, exporter, mesh; - -const params = { - exportASCII: exportASCII, - exportBinary: exportBinary, -}; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(4, 2, 4); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xa0a0a0); - scene.fog = new THREE.Fog(0xa0a0a0, 4, 20); - - exporter = new STLExporter(); - - // - - const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444, 3); - hemiLight.position.set(0, 20, 0); - scene.add(hemiLight); - - const directionalLight = new THREE.DirectionalLight(0xffffff, 3); - directionalLight.position.set(0, 20, 10); - directionalLight.castShadow = true; - directionalLight.shadow.camera.top = 2; - directionalLight.shadow.camera.bottom = -2; - directionalLight.shadow.camera.left = -2; - directionalLight.shadow.camera.right = 2; - scene.add(directionalLight); - - // ground - - const ground = new THREE.Mesh( - new THREE.PlaneGeometry(40, 40), - new THREE.MeshPhongMaterial({ color: 0xbbbbbb, depthWrite: false }), - ); - ground.rotation.x = -Math.PI / 2; - ground.receiveShadow = true; - scene.add(ground); - - const grid = new THREE.GridHelper(40, 20, 0x000000, 0x000000); - grid.material.opacity = 0.2; - grid.material.transparent = true; - scene.add(grid); - - // export mesh - - const geometry = new THREE.BoxGeometry(); - const material = new THREE.MeshPhongMaterial({ color: 0x00ff00 }); - - mesh = new THREE.Mesh(geometry, material); - mesh.castShadow = true; - mesh.position.y = 0.5; - scene.add(mesh); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.shadowMap.enabled = true; - document.body.appendChild(renderer.domElement); - - // - - const controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, 0.5, 0); - controls.update(); - - // - - window.addEventListener('resize', onWindowResize); - - const gui = new GUI(); - - gui.add(params, 'exportASCII').name('Export STL (ASCII)'); - gui.add(params, 'exportBinary').name('Export STL (Binary)'); - gui.open(); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - renderer.render(scene, camera); -} - -function exportASCII() { - const result = exporter.parse(mesh); - saveString(result, 'box.stl'); -} - -function exportBinary() { - const result = exporter.parse(mesh, { binary: true }); - saveArrayBuffer(result, 'box.stl'); -} - -const link = document.createElement('a'); -link.style.display = 'none'; -document.body.appendChild(link); - -function save(blob, filename) { - link.href = URL.createObjectURL(blob); - link.download = filename; - link.click(); -} - -function saveString(text, filename) { - save(new Blob([text], { type: 'text/plain' }), filename); -} - -function saveArrayBuffer(buffer, filename) { - save(new Blob([buffer], { type: 'application/octet-stream' }), filename); -} diff --git a/examples-testing/examples/misc_exporter_usdz.ts b/examples-testing/examples/misc_exporter_usdz.ts deleted file mode 100644 index f1ce65485..000000000 --- a/examples-testing/examples/misc_exporter_usdz.ts +++ /dev/null @@ -1,130 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; - -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { USDZExporter } from 'three/addons/exporters/USDZExporter.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let camera, scene, renderer; - -const params = { - exportUSDZ: exportUSDZ, -}; - -init(); -render(); - -function init() { - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - document.body.appendChild(renderer.domElement); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); - camera.position.set(-2.5, 0.6, 3.0); - - const pmremGenerator = new THREE.PMREMGenerator(renderer); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xf0f0f0); - scene.environment = pmremGenerator.fromScene(new RoomEnvironment(), 0.04).texture; - - const loader = new GLTFLoader().setPath('models/gltf/DamagedHelmet/glTF/'); - loader.load('DamagedHelmet.gltf', async function (gltf) { - scene.add(gltf.scene); - - const shadowMesh = createSpotShadowMesh(); - shadowMesh.position.y = -1.1; - shadowMesh.position.z = -0.25; - shadowMesh.scale.setScalar(2); - scene.add(shadowMesh); - - render(); - - // USDZ - - const exporter = new USDZExporter(); - const arraybuffer = await exporter.parseAsync(gltf.scene); - const blob = new Blob([arraybuffer], { type: 'application/octet-stream' }); - - const link = document.getElementById('link'); - link.href = URL.createObjectURL(blob); - }); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.addEventListener('change', render); // use if there is no animation loop - controls.minDistance = 2; - controls.maxDistance = 10; - controls.target.set(0, -0.15, -0.2); - controls.update(); - - window.addEventListener('resize', onWindowResize); - - const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent); - - if (isIOS === false) { - const gui = new GUI(); - - gui.add(params, 'exportUSDZ').name('Export USDZ'); - gui.open(); - } -} - -function createSpotShadowMesh() { - const canvas = document.createElement('canvas'); - canvas.width = 128; - canvas.height = 128; - - const context = canvas.getContext('2d'); - const gradient = context.createRadialGradient( - canvas.width / 2, - canvas.height / 2, - 0, - canvas.width / 2, - canvas.height / 2, - canvas.width / 2, - ); - gradient.addColorStop(0.1, 'rgba(130,130,130,1)'); - gradient.addColorStop(1, 'rgba(255,255,255,1)'); - - context.fillStyle = gradient; - context.fillRect(0, 0, canvas.width, canvas.height); - - const shadowTexture = new THREE.CanvasTexture(canvas); - - const geometry = new THREE.PlaneGeometry(); - const material = new THREE.MeshBasicMaterial({ - map: shadowTexture, - blending: THREE.MultiplyBlending, - toneMapped: false, - premultipliedAlpha: true, - }); - - const mesh = new THREE.Mesh(geometry, material); - mesh.rotation.x = -Math.PI / 2; - - return mesh; -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - render(); -} - -function exportUSDZ() { - const link = document.getElementById('link'); - link.click(); -} - -// - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/misc_uv_tests.ts b/examples-testing/examples/misc_uv_tests.ts deleted file mode 100644 index 4f782d45f..000000000 --- a/examples-testing/examples/misc_uv_tests.ts +++ /dev/null @@ -1,44 +0,0 @@ -import * as THREE from 'three'; - -import { UVsDebug } from 'three/addons/utils/UVsDebug.js'; - -/* - * This is to help debug UVs problems in geometry, - * as well as allow a new user to visualize what UVs are about. - */ - -function test(name, geometry) { - const d = document.createElement('div'); - - d.innerHTML = '

' + name + '

'; - - d.appendChild(UVsDebug(geometry)); - - document.body.appendChild(d); -} - -const points = []; - -for (let i = 0; i < 10; i++) { - points.push(new THREE.Vector2(Math.sin(i * 0.2) * 15 + 50, (i - 5) * 2)); -} - -// - -test('new THREE.PlaneGeometry( 100, 100, 4, 4 )', new THREE.PlaneGeometry(100, 100, 4, 4)); - -test('new THREE.SphereGeometry( 75, 12, 6 )', new THREE.SphereGeometry(75, 12, 6)); - -test('new THREE.IcosahedronGeometry( 30, 1 )', new THREE.IcosahedronGeometry(30, 1)); - -test('new THREE.OctahedronGeometry( 30, 2 )', new THREE.OctahedronGeometry(30, 2)); - -test('new THREE.CylinderGeometry( 25, 75, 100, 10, 5 )', new THREE.CylinderGeometry(25, 75, 100, 10, 5)); - -test('new THREE.BoxGeometry( 100, 100, 100, 4, 4, 4 )', new THREE.BoxGeometry(100, 100, 100, 4, 4, 4)); - -test('new THREE.LatheGeometry( points, 8 )', new THREE.LatheGeometry(points, 8)); - -test('new THREE.TorusGeometry( 50, 20, 8, 8 )', new THREE.TorusGeometry(50, 20, 8, 8)); - -test('new THREE.TorusKnotGeometry( 50, 10, 12, 6 )', new THREE.TorusKnotGeometry(50, 10, 12, 6)); diff --git a/examples-testing/examples/physics_ammo_instancing.ts b/examples-testing/examples/physics_ammo_instancing.ts deleted file mode 100644 index 5cbee3260..000000000 --- a/examples-testing/examples/physics_ammo_instancing.ts +++ /dev/null @@ -1,132 +0,0 @@ -import * as THREE from 'three'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { AmmoPhysics } from 'three/addons/physics/AmmoPhysics.js'; -import Stats from 'three/addons/libs/stats.module.js'; - -let camera, scene, renderer, stats; -let physics, position; - -let boxes, spheres; - -init(); - -async function init() { - physics = await AmmoPhysics(); - position = new THREE.Vector3(); - - // - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(-1, 1.5, 2); - camera.lookAt(0, 0.5, 0); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x666666); - - const hemiLight = new THREE.HemisphereLight(); - scene.add(hemiLight); - - const dirLight = new THREE.DirectionalLight(0xffffff, 3); - dirLight.position.set(5, 5, 5); - dirLight.castShadow = true; - dirLight.shadow.camera.zoom = 2; - scene.add(dirLight); - - const shadowPlane = new THREE.Mesh( - new THREE.PlaneGeometry(10, 10), - new THREE.ShadowMaterial({ - color: 0x444444, - }), - ); - shadowPlane.rotation.x = -Math.PI / 2; - shadowPlane.receiveShadow = true; - scene.add(shadowPlane); - - const floorCollider = new THREE.Mesh( - new THREE.BoxGeometry(10, 5, 10), - new THREE.MeshBasicMaterial({ color: 0x666666 }), - ); - floorCollider.position.y = -2.5; - floorCollider.userData.physics = { mass: 0 }; - floorCollider.visible = false; - scene.add(floorCollider); - - // - - const material = new THREE.MeshLambertMaterial(); - - const matrix = new THREE.Matrix4(); - const color = new THREE.Color(); - - // Boxes - - const geometryBox = new THREE.BoxGeometry(0.075, 0.075, 0.075); - boxes = new THREE.InstancedMesh(geometryBox, material, 400); - boxes.instanceMatrix.setUsage(THREE.DynamicDrawUsage); // will be updated every frame - boxes.castShadow = true; - boxes.receiveShadow = true; - boxes.userData.physics = { mass: 1 }; - scene.add(boxes); - - for (let i = 0; i < boxes.count; i++) { - matrix.setPosition(Math.random() - 0.5, Math.random() * 2, Math.random() - 0.5); - boxes.setMatrixAt(i, matrix); - boxes.setColorAt(i, color.setHex(0xffffff * Math.random())); - } - - // Spheres - - const geometrySphere = new THREE.IcosahedronGeometry(0.05, 4); - spheres = new THREE.InstancedMesh(geometrySphere, material, 400); - spheres.instanceMatrix.setUsage(THREE.DynamicDrawUsage); // will be updated every frame - spheres.castShadow = true; - spheres.receiveShadow = true; - spheres.userData.physics = { mass: 1 }; - scene.add(spheres); - - for (let i = 0; i < spheres.count; i++) { - matrix.setPosition(Math.random() - 0.5, Math.random() * 2, Math.random() - 0.5); - spheres.setMatrixAt(i, matrix); - spheres.setColorAt(i, color.setHex(0xffffff * Math.random())); - } - - physics.addScene(scene); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.shadowMap.enabled = true; - document.body.appendChild(renderer.domElement); - - stats = new Stats(); - document.body.appendChild(stats.dom); - - // - - const controls = new OrbitControls(camera, renderer.domElement); - controls.target.y = 0.5; - controls.update(); - - setInterval(() => { - let index = Math.floor(Math.random() * boxes.count); - - position.set(0, Math.random() + 1, 0); - physics.setMeshPosition(boxes, position, index); - - // - - index = Math.floor(Math.random() * spheres.count); - - position.set(0, Math.random() + 1, 0); - physics.setMeshPosition(spheres, position, index); - }, 1000 / 60); -} - -function animate() { - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/physics_jolt_instancing.ts b/examples-testing/examples/physics_jolt_instancing.ts deleted file mode 100644 index 70980b8d5..000000000 --- a/examples-testing/examples/physics_jolt_instancing.ts +++ /dev/null @@ -1,133 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { JoltPhysics } from 'three/addons/physics/JoltPhysics.js'; -import Stats from 'three/addons/libs/stats.module.js'; - -let camera, scene, renderer, stats; -let physics, position; - -let boxes, spheres; - -init(); - -async function init() { - physics = await JoltPhysics(); - position = new THREE.Vector3(); - - // - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(-1, 1.5, 2); - camera.lookAt(0, 0.5, 0); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x666666); - - const hemiLight = new THREE.HemisphereLight(); - scene.add(hemiLight); - - const dirLight = new THREE.DirectionalLight(0xffffff, 3); - dirLight.position.set(5, 5, 5); - dirLight.castShadow = true; - dirLight.shadow.camera.zoom = 2; - dirLight.shadow.bias = -0.001; - scene.add(dirLight); - - const shadowPlane = new THREE.Mesh( - new THREE.PlaneGeometry(10, 10), - new THREE.ShadowMaterial({ - color: 0x444444, - }), - ); - shadowPlane.rotation.x = -Math.PI / 2; - shadowPlane.receiveShadow = true; - scene.add(shadowPlane); - - const floorCollider = new THREE.Mesh( - new THREE.BoxGeometry(10, 5, 10), - new THREE.MeshBasicMaterial({ color: 0x666666 }), - ); - floorCollider.position.y = -2.5; - floorCollider.userData.physics = { mass: 0 }; - floorCollider.visible = false; - scene.add(floorCollider); - - // - - const material = new THREE.MeshLambertMaterial(); - - const matrix = new THREE.Matrix4(); - const color = new THREE.Color(); - - // Boxes - - const geometryBox = new THREE.BoxGeometry(0.075, 0.075, 0.075); - boxes = new THREE.InstancedMesh(geometryBox, material, 400); - boxes.instanceMatrix.setUsage(THREE.DynamicDrawUsage); // will be updated every frame - boxes.castShadow = true; - boxes.receiveShadow = true; - boxes.userData.physics = { mass: 1 }; - scene.add(boxes); - - for (let i = 0; i < boxes.count; i++) { - matrix.setPosition(Math.random() - 0.5, Math.random() * 2, Math.random() - 0.5); - boxes.setMatrixAt(i, matrix); - boxes.setColorAt(i, color.setHex(0xffffff * Math.random())); - } - - // Spheres - - const geometrySphere = new THREE.IcosahedronGeometry(0.05, 4); - spheres = new THREE.InstancedMesh(geometrySphere, material, 400); - spheres.instanceMatrix.setUsage(THREE.DynamicDrawUsage); // will be updated every frame - spheres.castShadow = true; - spheres.receiveShadow = true; - spheres.userData.physics = { mass: 1 }; - scene.add(spheres); - - for (let i = 0; i < spheres.count; i++) { - matrix.setPosition(Math.random() - 0.5, Math.random() * 2, Math.random() - 0.5); - spheres.setMatrixAt(i, matrix); - spheres.setColorAt(i, color.setHex(0xffffff * Math.random())); - } - - physics.addScene(scene); - - // - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.shadowMap.enabled = true; - document.body.appendChild(renderer.domElement); - - stats = new Stats(); - document.body.appendChild(stats.dom); - - // - - const controls = new OrbitControls(camera, renderer.domElement); - controls.target.y = 0.5; - controls.update(); - - setInterval(() => { - let index = Math.floor(Math.random() * boxes.count); - - position.set(0, Math.random() + 1, 0); - physics.setMeshPosition(boxes, position, index); - - // - - index = Math.floor(Math.random() * spheres.count); - - position.set(0, Math.random() + 1, 0); - physics.setMeshPosition(spheres, position, index); - }, 1000 / 60); -} - -function animate() { - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/physics_rapier_basic.ts b/examples-testing/examples/physics_rapier_basic.ts deleted file mode 100644 index 0bacda80c..000000000 --- a/examples-testing/examples/physics_rapier_basic.ts +++ /dev/null @@ -1,144 +0,0 @@ -import * as THREE from 'three'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { RapierPhysics } from 'three/addons/physics/RapierPhysics.js'; -import { RapierHelper } from 'three/addons/helpers/RapierHelper.js'; -import { RoundedBoxGeometry } from 'three/addons/geometries/RoundedBoxGeometry.js'; -import Stats from 'three/addons/libs/stats.module.js'; - -let camera, scene, renderer, stats, controls; -let physics, physicsHelper; - -init(); - -async function init() { - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xbfd1e5); - - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(0, 3, 10); - - const ambient = new THREE.HemisphereLight(0x555555, 0xffffff); - - scene.add(ambient); - - const light = new THREE.DirectionalLight(0xffffff, 4); - - light.position.set(0, 12.5, 12.5); - light.castShadow = true; - light.shadow.radius = 3; - light.shadow.blurSamples = 8; - light.shadow.mapSize.width = 1024; - light.shadow.mapSize.height = 1024; - - const size = 10; - light.shadow.camera.left = -size; - light.shadow.camera.bottom = -size; - light.shadow.camera.right = size; - light.shadow.camera.top = size; - light.shadow.camera.near = 1; - light.shadow.camera.far = 50; - - scene.add(light); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.shadowMap.enabled = true; - document.body.appendChild(renderer.domElement); - renderer.setAnimationLoop(animate); - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - controls.target = new THREE.Vector3(0, 2, 0); - controls.update(); - - const geometry = new THREE.BoxGeometry(10, 0.5, 10); - const material = new THREE.MeshStandardMaterial({ color: 0xffffff }); - - const floor = new THREE.Mesh(geometry, material); - floor.receiveShadow = true; - - floor.position.y = -0.25; - floor.userData.physics = { mass: 0 }; - - scene.add(floor); - - new THREE.TextureLoader().load('textures/grid.png', function (texture) { - texture.wrapS = THREE.RepeatWrapping; - texture.wrapT = THREE.RepeatWrapping; - texture.repeat.set(20, 20); - floor.material.map = texture; - floor.material.needsUpdate = true; - }); - - stats = new Stats(); - document.body.appendChild(stats.dom); - - initPhysics(); - - onWindowResize(); - - window.addEventListener('resize', onWindowResize, false); -} - -async function initPhysics() { - //Initialize physics engine using the script in the jsm/physics folder - physics = await RapierPhysics(); - - physics.addScene(scene); - - addBody(); - - //Optionally display collider outlines - physicsHelper = new RapierHelper(physics.world); - scene.add(physicsHelper); - - setInterval(addBody, 1000); -} - -const geometries = [ - new THREE.BoxGeometry(1, 1, 1), - new THREE.SphereGeometry(0.5), - new RoundedBoxGeometry(1, 1, 1, 2, 0.25), -]; - -function addBody() { - const geometry = geometries[Math.floor(Math.random() * geometries.length)]; - const material = new THREE.MeshStandardMaterial({ color: Math.floor(Math.random() * 0xffffff) }); - - const mesh = new THREE.Mesh(geometry, material); - mesh.castShadow = true; - - mesh.position.set(Math.random() * 2 - 1, Math.random() * 3 + 6, Math.random() * 2 - 1); - - scene.add(mesh); - - //parameter 2 - mass, parameter 3 - restitution ( how bouncy ) - physics.addMesh(mesh, 1, 0.5); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - for (const object of scene.children) { - if (object.isMesh) { - if (object.position.y < -10) { - scene.remove(object); - physics.removeMesh(object); - } - } - } - - if (physicsHelper) physicsHelper.update(); - - controls.update(); - - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/physics_rapier_character_controller.ts b/examples-testing/examples/physics_rapier_character_controller.ts deleted file mode 100644 index 8e797a98c..000000000 --- a/examples-testing/examples/physics_rapier_character_controller.ts +++ /dev/null @@ -1,187 +0,0 @@ -import * as THREE from 'three'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { RapierPhysics } from 'three/addons/physics/RapierPhysics.js'; -import { RapierHelper } from 'three/addons/helpers/RapierHelper.js'; -import Stats from 'three/addons/libs/stats.module.js'; - -let camera, scene, renderer, stats; -let physics, characterController, physicsHelper; -let player, movement; - -init(); - -async function init() { - scene = new THREE.Scene(); - - scene.background = new THREE.Color(0xbfd1e5); - - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(2, 5, 15); - - const ambient = new THREE.HemisphereLight(0x555555, 0xffffff); - - scene.add(ambient); - - const light = new THREE.DirectionalLight(0xffffff, 3); - - light.position.set(0, 12.5, 12.5); - light.castShadow = true; - light.shadow.radius = 3; - light.shadow.blurSamples = 8; - light.shadow.mapSize.width = 1024; - light.shadow.mapSize.height = 1024; - - const size = 10; - light.shadow.camera.left = -size; - light.shadow.camera.bottom = -size; - light.shadow.camera.right = size; - light.shadow.camera.top = size; - light.shadow.camera.near = 1; - light.shadow.camera.far = 50; - - scene.add(light); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.shadowMap.enabled = true; - document.body.appendChild(renderer.domElement); - renderer.setAnimationLoop(animate); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.target = new THREE.Vector3(0, 2, 0); - controls.update(); - - const geometry = new THREE.BoxGeometry(20, 0.5, 20); - const material = new THREE.MeshStandardMaterial({ color: 0xffffff }); - - const ground = new THREE.Mesh(geometry, material); - ground.receiveShadow = true; - - ground.position.y = -0.25; - ground.userData.physics = { mass: 0 }; - - scene.add(ground); - - new THREE.TextureLoader().load('textures/grid.png', function (texture) { - texture.wrapS = THREE.RepeatWrapping; - texture.wrapT = THREE.RepeatWrapping; - texture.repeat.set(20, 20); - ground.material.map = texture; - ground.material.needsUpdate = true; - }); - - for (let i = 0; i < 10; i++) addBody(Math.random() > 0.7); - - stats = new Stats(); - document.body.appendChild(stats.dom); - - initPhysics(); - - onWindowResize(); - - // Movement input - movement = { forward: 0, right: 0 }; - - window.addEventListener('keydown', event => { - if (event.key === 'w' || event.key === 'ArrowUp') movement.forward = 1; - if (event.key === 's' || event.key === 'ArrowDown') movement.forward = -1; - if (event.key === 'a' || event.key === 'ArrowLeft') movement.right = -1; - if (event.key === 'd' || event.key === 'ArrowRight') movement.right = 1; - }); - - window.addEventListener('keyup', event => { - if (event.key === 'w' || event.key === 's' || event.key === 'ArrowUp' || event.key === 'ArrowDown') - movement.forward = 0; - if (event.key === 'a' || event.key === 'd' || event.key === 'ArrowLeft' || event.key === 'ArrowRight') - movement.right = 0; - }); - - window.addEventListener('resize', onWindowResize, false); -} - -function random(min, max) { - return Math.random() * (max - min) + min; -} - -async function initPhysics() { - //Initialize physics engine using the script in the jsm/physics folder - physics = await RapierPhysics(); - - //Optionally display collider outlines - physicsHelper = new RapierHelper(physics.world); - scene.add(physicsHelper); - - physics.addScene(scene); - - addCharacterController(); -} - -function addCharacterController() { - // Character Capsule - const geometry = new THREE.CapsuleGeometry(0.3, 1, 8, 8); - const material = new THREE.MeshStandardMaterial({ color: 0x0000ff }); - player = new THREE.Mesh(geometry, material); - player.castShadow = true; - player.position.set(0, 0.8, 0); - scene.add(player); - - // Rapier Character Controller - characterController = physics.world.createCharacterController(0.01); - characterController.setApplyImpulsesToDynamicBodies(true); - characterController.setCharacterMass(3); - const colliderDesc = physics.RAPIER.ColliderDesc.capsule(0.5, 0.3).setTranslation(0, 0.8, 0); - player.userData.collider = physics.world.createCollider(colliderDesc); -} - -function addBody(fixed = true) { - const geometry = fixed ? new THREE.BoxGeometry(1, 1, 1) : new THREE.SphereGeometry(0.25); - const material = new THREE.MeshStandardMaterial({ color: fixed ? 0xff0000 : 0x00ff00 }); - - const mesh = new THREE.Mesh(geometry, material); - mesh.castShadow = true; - - mesh.position.set(random(-10, 10), 0.5, random(-10, 10)); - - mesh.userData.physics = { mass: fixed ? 0 : 0.5, restitution: fixed ? 0 : 0.3 }; - - scene.add(mesh); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - if (physicsHelper) physicsHelper.update(); - - renderer.render(scene, camera); - - if (physics && characterController) { - const deltaTime = 1 / 60; - - // Character movement - const speed = 2.5 * deltaTime; - const moveVector = new physics.RAPIER.Vector3(movement.right * speed, 0, -movement.forward * speed); - - characterController.computeColliderMovement(player.userData.collider, moveVector); - - // Read the result. - const translation = characterController.computedMovement(); - const position = player.userData.collider.translation(); - - position.x += translation.x; - position.y += translation.y; - position.z += translation.z; - - player.userData.collider.setTranslation(position); - - // Sync Three.js mesh with Rapier collider - player.position.set(position.x, position.y, position.z); - } - - stats.update(); -} diff --git a/examples-testing/examples/physics_rapier_instancing.ts b/examples-testing/examples/physics_rapier_instancing.ts deleted file mode 100644 index 20509f7fd..000000000 --- a/examples-testing/examples/physics_rapier_instancing.ts +++ /dev/null @@ -1,142 +0,0 @@ -import * as THREE from 'three'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { RapierPhysics } from 'three/addons/physics/RapierPhysics.js'; -import Stats from 'three/addons/libs/stats.module.js'; - -let camera, scene, renderer, stats; -let physics, position; - -let boxes, spheres; - -init(); - -async function init() { - physics = await RapierPhysics(); - position = new THREE.Vector3(); - - // - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(-1, 1.5, 2); - camera.lookAt(0, 0.5, 0); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x666666); - - const hemiLight = new THREE.HemisphereLight(); - scene.add(hemiLight); - - const dirLight = new THREE.DirectionalLight(0xffffff, 3); - dirLight.position.set(5, 5, 5); - dirLight.castShadow = true; - dirLight.shadow.camera.zoom = 2; - scene.add(dirLight); - - const shadowPlane = new THREE.Mesh( - new THREE.PlaneGeometry(10, 10), - new THREE.ShadowMaterial({ - color: 0x444444, - }), - ); - shadowPlane.rotation.x = -Math.PI / 2; - shadowPlane.receiveShadow = true; - scene.add(shadowPlane); - - const floorCollider = new THREE.Mesh( - new THREE.BoxGeometry(10, 5, 10), - new THREE.MeshBasicMaterial({ color: 0x666666 }), - ); - floorCollider.position.y = -2.5; - floorCollider.userData.physics = { mass: 0 }; - floorCollider.visible = false; - scene.add(floorCollider); - - // - - const material = new THREE.MeshLambertMaterial(); - - const matrix = new THREE.Matrix4(); - const color = new THREE.Color(); - - // Boxes - - const geometryBox = new THREE.BoxGeometry(0.075, 0.075, 0.075); - boxes = new THREE.InstancedMesh(geometryBox, material, 400); - boxes.instanceMatrix.setUsage(THREE.DynamicDrawUsage); // will be updated every frame - boxes.castShadow = true; - boxes.receiveShadow = true; - boxes.userData.physics = { mass: 1 }; - scene.add(boxes); - - for (let i = 0; i < boxes.count; i++) { - matrix.setPosition(Math.random() - 0.5, Math.random() * 2, Math.random() - 0.5); - boxes.setMatrixAt(i, matrix); - boxes.setColorAt(i, color.setHex(0xffffff * Math.random())); - } - - // Spheres - - const geometrySphere = new THREE.IcosahedronGeometry(0.05, 4); - spheres = new THREE.InstancedMesh(geometrySphere, material, 400); - spheres.instanceMatrix.setUsage(THREE.DynamicDrawUsage); // will be updated every frame - spheres.castShadow = true; - spheres.receiveShadow = true; - spheres.userData.physics = { mass: 1 }; - scene.add(spheres); - - for (let i = 0; i < spheres.count; i++) { - matrix.setPosition(Math.random() - 0.5, Math.random() * 2, Math.random() - 0.5); - spheres.setMatrixAt(i, matrix); - spheres.setColorAt(i, color.setHex(0xffffff * Math.random())); - } - - physics.addScene(scene); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.shadowMap.enabled = true; - document.body.appendChild(renderer.domElement); - - stats = new Stats(); - document.body.appendChild(stats.dom); - - // - - const controls = new OrbitControls(camera, renderer.domElement); - controls.target.y = 0.5; - controls.update(); - - setInterval(() => { - let index = Math.floor(Math.random() * boxes.count); - - position.set(0, Math.random() + 1, 0); - physics.setMeshPosition(boxes, position, index); - - // - - index = Math.floor(Math.random() * spheres.count); - - position.set(0, Math.random() + 1, 0); - physics.setMeshPosition(spheres, position, index); - }, 1000 / 60); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/physics_rapier_joints.ts b/examples-testing/examples/physics_rapier_joints.ts deleted file mode 100644 index 5f8805fe1..000000000 --- a/examples-testing/examples/physics_rapier_joints.ts +++ /dev/null @@ -1,130 +0,0 @@ -import * as THREE from 'three'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { RapierPhysics } from 'three/addons/physics/RapierPhysics.js'; -import { RapierHelper } from 'three/addons/helpers/RapierHelper.js'; -import Stats from 'three/addons/libs/stats.module.js'; - -let camera, scene, renderer, stats; -let physics, pivot, physicsHelper; - -init(); - -async function init() { - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xbfd1e5); - - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(0, 3, 10); - - const ambient = new THREE.HemisphereLight(0x555555, 0xffffff); - - scene.add(ambient); - - const light = new THREE.DirectionalLight(0xffffff, 4); - - light.position.set(0, 12.5, 12.5); - light.castShadow = true; - light.shadow.radius = 3; - light.shadow.blurSamples = 8; - light.shadow.mapSize.width = 1024; - light.shadow.mapSize.height = 1024; - - const size = 10; - light.shadow.camera.left = -size; - light.shadow.camera.bottom = -size; - light.shadow.camera.right = size; - light.shadow.camera.top = size; - light.shadow.camera.near = 1; - light.shadow.camera.far = 50; - - scene.add(light); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.shadowMap.enabled = true; - document.body.appendChild(renderer.domElement); - renderer.setAnimationLoop(animate); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.target = new THREE.Vector3(0, 2, 0); - controls.update(); - - stats = new Stats(); - document.body.appendChild(stats.dom); - - //Create pivot point - const geometry = new THREE.SphereGeometry(0.5); - const material = new THREE.MeshStandardMaterial({ color: 0xff0000 }); - - pivot = new THREE.Mesh(geometry, material); - - pivot.position.y = 6; - pivot.userData.physics = { mass: 0 }; - - scene.add(pivot); - - initPhysics(); - - onWindowResize(); - - window.addEventListener('resize', onWindowResize, false); -} - -async function initPhysics() { - //Initialize physics engine using the script in the jsm/physics folder - physics = await RapierPhysics(); - - physics.addScene(scene); - - //Optionally display collider outlines - physicsHelper = new RapierHelper(physics.world); - scene.add(physicsHelper); - - const link1 = addLink(pivot, 0); - const link2 = addLink(link1, 2); - addLink(link2, 4); -} - -//link - the mesh that the new link will be attached to -//x - used to position the new link -function addLink(link, x) { - const geometry = new THREE.CapsuleGeometry(0.25, 1.8); - const material = new THREE.MeshStandardMaterial({ color: 0xcccc00 }); - - const mesh = new THREE.Mesh(geometry, material); - mesh.rotateZ(Math.PI * 0.5); - - mesh.position.set(x + 0.9, 5.8, 0); - - scene.add(mesh); - - physics.addMesh(mesh, 1, 0.5); - - const jointParams = physics.RAPIER.JointData.spherical( - link == pivot ? new physics.RAPIER.Vector3(0, -0.5, 0) : new physics.RAPIER.Vector3(0, -1.15, 0), // Joint position in world space - new physics.RAPIER.Vector3(0, 1.15, 0), // Corresponding attachment on sphere - ); - const body1 = link.userData.physics.body; - const body2 = mesh.userData.physics.body; - body2.setAngularDamping(10.0); - - physics.world.createImpulseJoint(jointParams, body1, body2, true); - - return mesh; -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - if (physicsHelper) physicsHelper.update(); - - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/physics_rapier_terrain.ts b/examples-testing/examples/physics_rapier_terrain.ts deleted file mode 100644 index 7708ceab8..000000000 --- a/examples-testing/examples/physics_rapier_terrain.ts +++ /dev/null @@ -1,295 +0,0 @@ -import * as THREE from 'three'; -import Stats from 'three/addons/libs/stats.module.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { RapierPhysics } from 'three/addons/physics/RapierPhysics.js'; - -// Heightfield parameters -const terrainWidthExtents = 100; -const terrainDepthExtents = 100; -const terrainWidth = 128; -const terrainDepth = 128; -const terrainHalfWidth = terrainWidth / 2; -const terrainHalfDepth = terrainDepth / 2; -const terrainMaxHeight = 8; -const terrainMinHeight = -2; - -// Graphics variables -let container, stats; -let camera, scene, renderer; -let terrainMesh; -const timer = new THREE.Timer(); -timer.connect(document); - -// Physics variables -let physics; -const dynamicObjects = []; -let heightData = null; - -let time = 0; -const objectTimePeriod = 3; -let timeNextSpawn = time + objectTimePeriod; -const maxNumObjects = 30; - -init(); - -async function init() { - heightData = generateHeight(terrainWidth, terrainDepth, terrainMinHeight, terrainMaxHeight); - initGraphics(); - await initPhysics(); - // Start animation loop only after physics is initialized - renderer.setAnimationLoop(animate); -} - -function initGraphics() { - container = document.getElementById('container'); - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - // Remove setAnimationLoop from here since we'll start it after physics init - renderer.shadowMap.enabled = true; - container.appendChild(renderer.domElement); - - stats = new Stats(); - stats.domElement.style.position = 'absolute'; - stats.domElement.style.top = '0px'; - container.appendChild(stats.domElement); - - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.2, 2000); - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xbfd1e5); - - camera.position.y = - heightData[terrainHalfWidth + terrainHalfDepth * terrainWidth] * (terrainMaxHeight - terrainMinHeight) + 5; - camera.position.z = terrainDepthExtents / 2; - camera.lookAt(0, 0, 0); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.enableZoom = false; - - const geometry = new THREE.PlaneGeometry( - terrainWidthExtents, - terrainDepthExtents, - terrainWidth - 1, - terrainDepth - 1, - ); - geometry.rotateX(-Math.PI / 2); - - const vertices = geometry.attributes.position.array; - - for (let i = 0, j = 0, l = vertices.length; i < l; i++, j += 3) { - vertices[j + 1] = heightData[i]; - } - - geometry.computeVertexNormals(); - - const groundMaterial = new THREE.MeshPhongMaterial({ color: 0xc7c7c7 }); - terrainMesh = new THREE.Mesh(geometry, groundMaterial); - terrainMesh.receiveShadow = true; - terrainMesh.castShadow = true; - scene.add(terrainMesh); - - const textureLoader = new THREE.TextureLoader(); - textureLoader.load('textures/grid.png', function (texture) { - texture.wrapS = THREE.RepeatWrapping; - texture.wrapT = THREE.RepeatWrapping; - texture.repeat.set(terrainWidth - 1, terrainDepth - 1); - groundMaterial.map = texture; - groundMaterial.needsUpdate = true; - }); - - const ambientLight = new THREE.AmbientLight(0xbbbbbb); - scene.add(ambientLight); - - const light = new THREE.DirectionalLight(0xffffff, 3); - light.position.set(100, 100, 50); - light.castShadow = true; - const dLight = 200; - const sLight = dLight * 0.25; - light.shadow.camera.left = -sLight; - light.shadow.camera.right = sLight; - light.shadow.camera.top = sLight; - light.shadow.camera.bottom = -sLight; - light.shadow.camera.near = dLight / 30; - light.shadow.camera.far = dLight; - light.shadow.mapSize.x = 1024 * 2; - light.shadow.mapSize.y = 1024 * 2; - scene.add(light); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - renderer.setSize(window.innerWidth, window.innerHeight); -} - -async function initPhysics() { - physics = await RapierPhysics(); - - // Create the terrain body using RapierPhysics module - physics.addHeightfield(terrainMesh, terrainWidth - 1, terrainDepth - 1, heightData, { - x: terrainWidthExtents, - y: 1.0, - z: terrainDepthExtents, - }); - - // Continue with adding other dynamic objects as needed -} - -function generateHeight(width, depth, minHeight, maxHeight) { - const size = width * depth; - const data = new Float32Array(size); - const hRange = maxHeight - minHeight; - const w2 = width / 2; - const d2 = depth / 2; - const phaseMult = 12; - let p = 0; - - for (let j = 0; j < depth; j++) { - for (let i = 0; i < width; i++) { - const radius = Math.sqrt(Math.pow((i - w2) / w2, 2.0) + Math.pow((j - d2) / d2, 2.0)); - const height = (Math.sin(radius * phaseMult) + 1) * 0.5 * hRange + minHeight; - data[p] = height; - p++; - } - } - - return data; -} - -function generateObject() { - const numTypes = 3; // cones not working - const objectType = Math.ceil(Math.random() * numTypes); - let threeObject = null; - const objectSize = 3; - let radius, height; - - switch (objectType) { - case 1: // Sphere - radius = 1 + Math.random() * objectSize; - threeObject = new THREE.Mesh(new THREE.SphereGeometry(radius, 20, 20), createObjectMaterial()); - break; - case 2: // Box - const sx = 1 + Math.random() * objectSize; - const sy = 1 + Math.random() * objectSize; - const sz = 1 + Math.random() * objectSize; - threeObject = new THREE.Mesh(new THREE.BoxGeometry(sx, sy, sz, 1, 1, 1), createObjectMaterial()); - break; - case 3: // Cylinder - radius = 1 + Math.random() * objectSize; - height = 1 + Math.random() * objectSize; - threeObject = new THREE.Mesh( - new THREE.CylinderGeometry(radius, radius, height, 20, 1), - createObjectMaterial(), - ); - break; - default: // Cone - radius = 1 + Math.random() * objectSize; - height = 2 + Math.random() * objectSize; - threeObject = new THREE.Mesh(new THREE.ConeGeometry(radius, height, 20, 2), createObjectMaterial()); - break; - } - - // Position objects higher and with more randomization to prevent clustering - threeObject.position.set( - (Math.random() - 0.5) * terrainWidth * 0.6, - terrainMaxHeight + objectSize + 15 + Math.random() * 5, // Even higher with randomization - (Math.random() - 0.5) * terrainDepth * 0.6, - ); - - const mass = objectSize * 5; - const restitution = 0.3; // Add some bounciness - - // Add to scene first - scene.add(threeObject); - - // Add physics to the object - physics.addMesh(threeObject, mass, restitution); - - // Store the object for later reference - dynamicObjects.push(threeObject); - - // Force a small delay before adding the next object - timeNextSpawn = time + 0.5; - - threeObject.receiveShadow = true; - threeObject.castShadow = true; -} - -function createObjectMaterial() { - const c = Math.floor(Math.random() * (1 << 24)); - return new THREE.MeshPhongMaterial({ color: c }); -} - -function animate() { - timer.update(); - - render(); - stats.update(); -} - -function render() { - const deltaTime = timer.getDelta(); - - // Generate new objects with a delay between them - if (dynamicObjects.length < maxNumObjects && time > timeNextSpawn) { - // Generate object directly in this frame - generateObject(); - // timeNextSpawn is now set in generateObject() - } - - // Clean up objects that have fallen off the terrain - for (let i = dynamicObjects.length - 1; i >= 0; i--) { - const obj = dynamicObjects[i]; - if (obj.position.y < terrainMinHeight - 10) { - // Remove from scene and physics world - scene.remove(obj); - dynamicObjects.splice(i, 1); - } - } - - updatePhysics(); - renderer.render(scene, camera); - time += deltaTime; -} - -function updatePhysics() { - // Check for objects that might need help with physics - for (let i = 0, il = dynamicObjects.length; i < il; i++) { - const objThree = dynamicObjects[i]; - - // If object is not moving but should be (based on height), try to fix it - if (objThree.position.y > 1.0) { - // Check if physics body exists - if (objThree.userData && objThree.userData.physics && objThree.userData.physics.body) { - const body = objThree.userData.physics.body; - - // Make sure body is awake - if (typeof body.wakeUp === 'function') { - body.wakeUp(); - } - - // Check velocity and apply impulse if needed - if (typeof body.linvel === 'function') { - const velocity = body.linvel(); - const speed = Math.sqrt( - velocity.x * velocity.x + velocity.y * velocity.y + velocity.z * velocity.z, - ); - - // If object is very slow, give it a stronger impulse - if (speed < 0.5) { - body.applyImpulse({ x: 0, y: -2.0, z: 0 }, true); - } - } - } else { - // If the object doesn't have a physics body but should, recreate it - const mass = 5; // Default mass - const restitution = 0.3; // Default restitution - - // Recreate physics for the object - physics.addMesh(objThree, mass, restitution); - } - } - } -} diff --git a/examples-testing/examples/physics_rapier_vehicle_controller.ts b/examples-testing/examples/physics_rapier_vehicle_controller.ts deleted file mode 100644 index fac5ef082..000000000 --- a/examples-testing/examples/physics_rapier_vehicle_controller.ts +++ /dev/null @@ -1,297 +0,0 @@ -import * as THREE from 'three'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { RapierPhysics } from 'three/addons/physics/RapierPhysics.js'; -import { RapierHelper } from 'three/addons/helpers/RapierHelper.js'; -import Stats from 'three/addons/libs/stats.module.js'; - -let camera, scene, renderer, stats; -let physics, physicsHelper, controls; -let car, chassis, wheels, movement, vehicleController; - -init(); - -async function init() { - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xbfd1e5); - - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(0, 4, 10); - - const ambient = new THREE.HemisphereLight(0x555555, 0xffffff); - - scene.add(ambient); - - const light = new THREE.DirectionalLight(0xffffff, 4); - - light.position.set(0, 12.5, 12.5); - light.castShadow = true; - light.shadow.radius = 3; - light.shadow.blurSamples = 8; - light.shadow.mapSize.width = 2048; - light.shadow.mapSize.height = 2048; - - const size = 40; - light.shadow.camera.left = -size; - light.shadow.camera.bottom = -size; - light.shadow.camera.right = size; - light.shadow.camera.top = size; - light.shadow.camera.near = 1; - light.shadow.camera.far = 50; - - scene.add(light); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.shadowMap.enabled = true; - document.body.appendChild(renderer.domElement); - renderer.setAnimationLoop(animate); - - controls = new OrbitControls(camera, renderer.domElement); - controls.target = new THREE.Vector3(0, 2, 0); - controls.update(); - - const geometry = new THREE.BoxGeometry(100, 0.5, 100); - const material = new THREE.MeshStandardMaterial({ color: 0xffffff }); - - const ground = new THREE.Mesh(geometry, material); - ground.receiveShadow = true; - - ground.position.set(0, -0.25, -20); - ground.userData.physics = { mass: 0 }; - - scene.add(ground); - - new THREE.TextureLoader().load('textures/grid.png', function (texture) { - texture.wrapS = THREE.RepeatWrapping; - texture.wrapT = THREE.RepeatWrapping; - texture.repeat.set(80, 80); - ground.material.map = texture; - ground.material.needsUpdate = true; - }); - - stats = new Stats(); - document.body.appendChild(stats.dom); - - initPhysics(); - - onWindowResize(); - - // Movement input - movement = { - forward: 0, - right: 0, - brake: 0, - reset: false, - accelerateForce: { value: 0, min: -30, max: 30, step: 1 }, - brakeForce: { value: 0, min: 0, max: 1, step: 0.05 }, - }; - - window.addEventListener('keydown', event => { - //console.log( event.key ); - if (event.key === 'w' || event.key === 'ArrowUp') movement.forward = -1; - if (event.key === 's' || event.key === 'ArrowDown') movement.forward = 1; - if (event.key === 'a' || event.key === 'ArrowLeft') movement.right = 1; - if (event.key === 'd' || event.key === 'ArrowRight') movement.right = -1; - if (event.key === 'r') movement.reset = true; - if (event.key === ' ') movement.brake = 1; - }); - - window.addEventListener('keyup', event => { - if (event.key === 'w' || event.key === 's' || event.key === 'ArrowUp' || event.key === 'ArrowDown') - movement.forward = 0; - if (event.key === 'a' || event.key === 'd' || event.key === 'ArrowLeft' || event.key === 'ArrowRight') - movement.right = 0; - if (event.key === 'r') movement.reset = false; - if (event.key === ' ') movement.brake = 0; - }); - - window.addEventListener('resize', onWindowResize, false); -} - -async function initPhysics() { - //Initialize physics engine using the script in the jsm/physics folder - physics = await RapierPhysics(); - - //Optionally display collider outlines - physicsHelper = new RapierHelper(physics.world); - scene.add(physicsHelper); - - physics.addScene(scene); - - createCar(); -} - -function createCar() { - const geometry = new THREE.BoxGeometry(2, 1, 4); - const material = new THREE.MeshStandardMaterial({ color: 0xff0000 }); - const mesh = new THREE.Mesh(geometry, material); - mesh.castShadow = true; - scene.add(mesh); - car = mesh; - - mesh.position.y = 1; - - physics.addMesh(mesh, 10, 0.8); // addMesh places the RigidBody in the mesh.userData.physics object - chassis = mesh.userData.physics.body; - - vehicleController = physics.world.createVehicleController(chassis); - - wheels = []; - - addWheel(0, { x: -1, y: 0, z: -1.5 }, mesh); - addWheel(1, { x: 1, y: 0, z: -1.5 }, mesh); - addWheel(2, { x: -1, y: 0, z: 1.5 }, mesh); - addWheel(3, { x: 1, y: 0, z: 1.5 }, mesh); - - vehicleController.setWheelSteering(0, Math.PI / 4); - vehicleController.setWheelSteering(1, Math.PI / 4); -} - -function addWheel(index, pos, carMesh) { - // Define wheel properties - const wheelRadius = 0.3; - const wheelWidth = 0.4; - const suspensionRestLength = 0.8; - const wheelPosition = pos; // Position relative to chassis - const wheelDirection = { x: 0.0, y: -1.0, z: 0.0 }; // Downward direction - const wheelAxle = { x: -1.0, y: 0.0, z: 0.0 }; // Axle direction - - // Add the wheel to the vehicle controller - vehicleController.addWheel(wheelPosition, wheelDirection, wheelAxle, suspensionRestLength, wheelRadius); - - // Set suspension stiffness for wheel - vehicleController.setWheelSuspensionStiffness(index, 24.0); - - // Set wheel friction - vehicleController.setWheelFrictionSlip(index, 1000.0); - - // Enable steering for the wheel - vehicleController.setWheelSteering(index, pos.z < 0); - - // Create a wheel mesh - const geometry = new THREE.CylinderGeometry(wheelRadius, wheelRadius, wheelWidth, 16); - geometry.rotateZ(Math.PI * 0.5); - const material = new THREE.MeshStandardMaterial({ color: 0x000000 }); - const wheel = new THREE.Mesh(geometry, material); - - wheel.castShadow = true; - - wheel.position.copy(pos); - - wheels.push(wheel); - carMesh.add(wheel); -} - -function updateWheels() { - if (vehicleController === undefined) return; - - const wheelSteeringQuat = new THREE.Quaternion(); - const wheelRotationQuat = new THREE.Quaternion(); - const up = new THREE.Vector3(0, 1, 0); - - //const chassisPosition = chassis.translation(); - - wheels.forEach((wheel, index) => { - const wheelAxleCs = vehicleController.wheelAxleCs(index); - const connection = vehicleController.wheelChassisConnectionPointCs(index).y || 0; - const suspension = vehicleController.wheelSuspensionLength(index) || 0; - const steering = vehicleController.wheelSteering(index) || 0; - const rotationRad = vehicleController.wheelRotation(index) || 0; - - wheel.position.y = connection - suspension; - - wheelSteeringQuat.setFromAxisAngle(up, steering); - wheelRotationQuat.setFromAxisAngle(wheelAxleCs, rotationRad); - - wheel.quaternion.multiplyQuaternions(wheelSteeringQuat, wheelRotationQuat); - }); -} - -function updateCarControl() { - if (movement.reset) { - chassis.setTranslation(new physics.RAPIER.Vector3(0, 1, 0), true); - chassis.setRotation(new physics.RAPIER.Quaternion(0, 0, 0, 1), true); - chassis.setLinvel(new physics.RAPIER.Vector3(0, 0, 0), true); - chassis.setAngvel(new physics.RAPIER.Vector3(0, 0, 0), true); - - movement.accelerateForce.value = 0; - movement.brakeForce.value = 0; - - return; - } - - let accelerateForce = 0; - - if (movement.forward < 0) { - //if (movement.accelerateForce.value === 0) chassis.wakeUp(); - accelerateForce = movement.accelerateForce.value - movement.accelerateForce.step; - if (accelerateForce < movement.accelerateForce.min) accelerateForce = movement.accelerateForce.min; - } else if (movement.forward > 0) { - //if (movement.accelerateForce.value === 0) chassis.wakeUp(); - accelerateForce = movement.accelerateForce.value + movement.accelerateForce.step; - - if (accelerateForce > movement.accelerateForce.max) accelerateForce = movement.accelerateForce.max; - } else { - if (chassis.isSleeping()) chassis.wakeUp(); - } - - movement.accelerateForce.value = accelerateForce; - - //console.log(accelerateForce); - - let brakeForce = 0; - - if (movement.brake > 0) { - brakeForce = movement.brakeForce.value + movement.brakeForce.step; - if (brakeForce > movement.brakeForce.max) brakeForce = movement.brakeForce.max; - } - - movement.brakeForce.value = brakeForce; - - const engineForce = accelerateForce; - - vehicleController.setWheelEngineForce(0, engineForce); - vehicleController.setWheelEngineForce(1, engineForce); - - const currentSteering = vehicleController.wheelSteering(0); - const steerDirection = movement.right; - const steerAngle = Math.PI / 4; - - const steering = THREE.MathUtils.lerp(currentSteering, steerAngle * steerDirection, 0.25); - - vehicleController.setWheelSteering(0, steering); - vehicleController.setWheelSteering(1, steering); - - const wheelBrake = movement.brake * brakeForce; - vehicleController.setWheelBrake(0, wheelBrake); - vehicleController.setWheelBrake(1, wheelBrake); - vehicleController.setWheelBrake(2, wheelBrake); - vehicleController.setWheelBrake(3, wheelBrake); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - if (vehicleController) { - updateCarControl(); - vehicleController.updateVehicle(1 / 60); - updateWheels(); - } - - if (controls && car) { - controls.target.copy(car.position); - controls.update(); - } - - if (physicsHelper) physicsHelper.update(); - - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/svg_lines.ts b/examples-testing/examples/svg_lines.ts deleted file mode 100644 index 99b74c405..000000000 --- a/examples-testing/examples/svg_lines.ts +++ /dev/null @@ -1,87 +0,0 @@ -import * as THREE from 'three'; - -import { SVGRenderer } from 'three/addons/renderers/SVGRenderer.js'; - -THREE.ColorManagement.enabled = false; - -let camera, scene, renderer; - -init(); -animate(); - -function init() { - camera = new THREE.PerspectiveCamera(33, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.z = 10; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0, 0, 0); - - renderer = new SVGRenderer(); - renderer.setSize(window.innerWidth, window.innerHeight); - document.body.appendChild(renderer.domElement); - - // - - const vertices = []; - const divisions = 50; - - for (let i = 0; i <= divisions; i++) { - const v = (i / divisions) * (Math.PI * 2); - - const x = Math.sin(v); - const z = Math.cos(v); - - vertices.push(x, 0, z); - } - - const geometry = new THREE.BufferGeometry(); - geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); - - // - - for (let i = 1; i <= 3; i++) { - const material = new THREE.LineBasicMaterial({ - color: Math.random() * 0xffffff, - linewidth: 10, - }); - const line = new THREE.Line(geometry, material); - line.scale.setScalar(i / 3); - scene.add(line); - } - - const material = new THREE.LineDashedMaterial({ - color: 'blue', - linewidth: 1, - dashSize: 10, - gapSize: 10, - }); - const line = new THREE.Line(geometry, material); - line.scale.setScalar(2); - scene.add(line); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - let count = 0; - const time = performance.now() / 1000; - - scene.traverse(function (child) { - child.rotation.x = count + time / 3; - child.rotation.z = count + time / 4; - - count++; - }); - - renderer.render(scene, camera); - requestAnimationFrame(animate); -} diff --git a/examples-testing/examples/svg_sandbox.ts b/examples-testing/examples/svg_sandbox.ts deleted file mode 100644 index fa66f10e1..000000000 --- a/examples-testing/examples/svg_sandbox.ts +++ /dev/null @@ -1,212 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { SVGRenderer, SVGObject } from 'three/addons/renderers/SVGRenderer.js'; - -THREE.ColorManagement.enabled = false; - -let camera, scene, renderer, stats, controls; - -let group; - -init(); -animate(); - -function init() { - camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000); - camera.position.z = 500; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xf0f0f0); - - // QRCODE - - const loader = new THREE.BufferGeometryLoader(); - loader.load('models/json/QRCode_buffergeometry.json', function (geometry) { - mesh = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({ vertexColors: true })); - mesh.scale.x = mesh.scale.y = mesh.scale.z = 2; - scene.add(mesh); - }); - - // CUBES - - const boxGeometry = new THREE.BoxGeometry(100, 100, 100); - - let mesh = new THREE.Mesh( - boxGeometry, - new THREE.MeshBasicMaterial({ color: 0x0000ff, opacity: 0.5, transparent: true }), - ); - mesh.position.x = 500; - mesh.rotation.x = Math.random(); - mesh.rotation.y = Math.random(); - mesh.scale.x = mesh.scale.y = mesh.scale.z = 2; - scene.add(mesh); - - mesh = new THREE.Mesh(boxGeometry, new THREE.MeshBasicMaterial({ color: Math.random() * 0xffffff })); - mesh.position.x = 500; - mesh.position.y = 500; - mesh.rotation.x = Math.random(); - mesh.rotation.y = Math.random(); - mesh.scale.x = mesh.scale.y = mesh.scale.z = 2; - scene.add(mesh); - - // PLANE - - mesh = new THREE.Mesh( - new THREE.PlaneGeometry(100, 100), - new THREE.MeshBasicMaterial({ color: Math.random() * 0xffffff, side: THREE.DoubleSide }), - ); - mesh.position.y = -500; - mesh.scale.x = mesh.scale.y = mesh.scale.z = 2; - scene.add(mesh); - - // CYLINDER - - mesh = new THREE.Mesh( - new THREE.CylinderGeometry(20, 100, 200, 10), - new THREE.MeshBasicMaterial({ color: Math.random() * 0xffffff }), - ); - mesh.position.x = -500; - mesh.rotation.x = -Math.PI / 2; - mesh.scale.x = mesh.scale.y = mesh.scale.z = 2; - scene.add(mesh); - - // POLYFIELD - - const geometry = new THREE.BufferGeometry(); - const material = new THREE.MeshBasicMaterial({ vertexColors: true, side: THREE.DoubleSide }); - - const v = new THREE.Vector3(); - const v0 = new THREE.Vector3(); - const v1 = new THREE.Vector3(); - const v2 = new THREE.Vector3(); - const color = new THREE.Color(); - - const vertices = []; - const colors = []; - - for (let i = 0; i < 100; i++) { - v.set(Math.random() * 1000 - 500, Math.random() * 1000 - 500, Math.random() * 1000 - 500); - - v0.set(Math.random() * 100 - 50, Math.random() * 100 - 50, Math.random() * 100 - 50); - - v1.set(Math.random() * 100 - 50, Math.random() * 100 - 50, Math.random() * 100 - 50); - - v2.set(Math.random() * 100 - 50, Math.random() * 100 - 50, Math.random() * 100 - 50); - - v0.add(v); - v1.add(v); - v2.add(v); - - color.setHex(Math.random() * 0xffffff); - - // create a single triangle - - vertices.push(v0.x, v0.y, v0.z); - vertices.push(v1.x, v1.y, v1.z); - vertices.push(v2.x, v2.y, v2.z); - - colors.push(color.r, color.g, color.b); - colors.push(color.r, color.g, color.b); - colors.push(color.r, color.g, color.b); - } - - geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); - geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); - - group = new THREE.Mesh(geometry, material); - group.scale.set(2, 2, 2); - scene.add(group); - - // SPRITES - - for (let i = 0; i < 50; i++) { - const material = new THREE.SpriteMaterial({ color: Math.random() * 0xffffff }); - const sprite = new THREE.Sprite(material); - sprite.position.x = Math.random() * 1000 - 500; - sprite.position.y = Math.random() * 1000 - 500; - sprite.position.z = Math.random() * 1000 - 500; - sprite.scale.set(64, 64, 1); - scene.add(sprite); - } - - // CUSTOM - - const node = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); - node.setAttribute('stroke', 'black'); - node.setAttribute('fill', 'red'); - node.setAttribute('r', '40'); - - for (let i = 0; i < 50; i++) { - const object = new SVGObject(node.cloneNode()); - object.position.x = Math.random() * 1000 - 500; - object.position.y = Math.random() * 1000 - 500; - object.position.z = Math.random() * 1000 - 500; - scene.add(object); - } - - // CUSTOM FROM FILE - - const fileLoader = new THREE.FileLoader(); - fileLoader.load('models/svg/hexagon.svg', function (svg) { - const node = document.createElementNS('http://www.w3.org/2000/svg', 'g'); - const parser = new DOMParser(); - const doc = parser.parseFromString(svg, 'image/svg+xml'); - - node.appendChild(doc.documentElement); - - const object = new SVGObject(node); - object.position.x = 500; - scene.add(object); - }); - - // LIGHTS - - const ambient = new THREE.AmbientLight(0x80ffff); - scene.add(ambient); - - const directional = new THREE.DirectionalLight(0xffff00); - directional.position.set(-1, 0.5, 0); - scene.add(directional); - - renderer = new SVGRenderer(); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setQuality('low'); - document.body.appendChild(renderer.domElement); - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - - stats = new Stats(); - document.body.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - requestAnimationFrame(animate); - - render(); - stats.update(); -} - -function render() { - group.rotation.x += 0.01; - - controls.update(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webaudio_orientation.ts b/examples-testing/examples/webaudio_orientation.ts deleted file mode 100644 index 7baaa88a0..000000000 --- a/examples-testing/examples/webaudio_orientation.ts +++ /dev/null @@ -1,141 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { PositionalAudioHelper } from 'three/addons/helpers/PositionalAudioHelper.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -let scene, camera, renderer; - -const startButton = document.getElementById('startButton'); -startButton.addEventListener('click', init); - -function init() { - const overlay = document.getElementById('overlay'); - overlay.remove(); - - const container = document.getElementById('container'); - - // - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(3, 2, 3); - - const reflectionCube = new THREE.CubeTextureLoader() - .setPath('textures/cube/SwedishRoyalCastle/') - .load(['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg']); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xa0a0a0); - scene.fog = new THREE.Fog(0xa0a0a0, 2, 20); - - // - - const hemiLight = new THREE.HemisphereLight(0xffffff, 0x8d8d8d, 3); - hemiLight.position.set(0, 20, 0); - scene.add(hemiLight); - - const dirLight = new THREE.DirectionalLight(0xffffff, 3); - dirLight.position.set(5, 5, 0); - dirLight.castShadow = true; - dirLight.shadow.camera.top = 1; - dirLight.shadow.camera.bottom = -1; - dirLight.shadow.camera.left = -1; - dirLight.shadow.camera.right = 1; - dirLight.shadow.camera.near = 0.1; - dirLight.shadow.camera.far = 20; - scene.add(dirLight); - - // scene.add( new THREE.CameraHelper( dirLight.shadow.camera ) ); - - // - - const mesh = new THREE.Mesh( - new THREE.PlaneGeometry(50, 50), - new THREE.MeshPhongMaterial({ color: 0xcbcbcb, depthWrite: false }), - ); - mesh.rotation.x = -Math.PI / 2; - mesh.receiveShadow = true; - scene.add(mesh); - - const grid = new THREE.GridHelper(50, 50, 0xc1c1c1, 0xc1c1c1); - scene.add(grid); - - // - - const listener = new THREE.AudioListener(); - camera.add(listener); - - const audioElement = document.getElementById('music'); - audioElement.play(); - - const positionalAudio = new THREE.PositionalAudio(listener); - positionalAudio.setMediaElementSource(audioElement); - positionalAudio.setRefDistance(1); - positionalAudio.setDirectionalCone(180, 230, 0.1); - - const helper = new PositionalAudioHelper(positionalAudio, 0.1); - positionalAudio.add(helper); - - // - - const gltfLoader = new GLTFLoader(); - gltfLoader.load('models/gltf/BoomBox.glb', function (gltf) { - const boomBox = gltf.scene; - boomBox.position.set(0, 0.2, 0); - boomBox.scale.set(20, 20, 20); - - boomBox.traverse(function (object) { - if (object.isMesh) { - object.material.envMap = reflectionCube; - object.geometry.rotateY(-Math.PI); - object.castShadow = true; - } - }); - - boomBox.add(positionalAudio); - scene.add(boomBox); - - renderer.setAnimationLoop(animate); - }); - - // sound is damped behind this wall - - const wallGeometry = new THREE.BoxGeometry(2, 1, 0.1); - const wallMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000, transparent: true, opacity: 0.5 }); - - const wall = new THREE.Mesh(wallGeometry, wallMaterial); - wall.position.set(0, 0.5, -0.5); - scene.add(wall); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.shadowMap.enabled = true; - container.appendChild(renderer.domElement); - - // - - const controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, 0.1, 0); - controls.update(); - controls.minDistance = 0.5; - controls.maxDistance = 10; - controls.maxPolarAngle = 0.5 * Math.PI; - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webaudio_sandbox.ts b/examples-testing/examples/webaudio_sandbox.ts deleted file mode 100644 index 34afca985..000000000 --- a/examples-testing/examples/webaudio_sandbox.ts +++ /dev/null @@ -1,223 +0,0 @@ -import * as THREE from 'three'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { FirstPersonControls } from 'three/addons/controls/FirstPersonControls.js'; - -let camera, controls, scene, renderer, light; - -let material1, material2, material3; - -let analyser1, analyser2, analyser3; - -const timer = new THREE.Timer(); -timer.connect(document); - -const startButton = document.getElementById('startButton'); -startButton.addEventListener('click', init); - -function init() { - const overlay = document.getElementById('overlay'); - overlay.remove(); - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000); - camera.position.set(0, 25, 0); - - const listener = new THREE.AudioListener(); - camera.add(listener); - - scene = new THREE.Scene(); - scene.fog = new THREE.FogExp2(0x000000, 0.0025); - - light = new THREE.DirectionalLight(0xffffff, 3); - light.position.set(0, 0.5, 1).normalize(); - scene.add(light); - - const sphere = new THREE.SphereGeometry(20, 32, 16); - - material1 = new THREE.MeshPhongMaterial({ color: 0xffaa00, flatShading: true, shininess: 0 }); - material2 = new THREE.MeshPhongMaterial({ color: 0xff2200, flatShading: true, shininess: 0 }); - material3 = new THREE.MeshPhongMaterial({ color: 0x6622aa, flatShading: true, shininess: 0 }); - - // sound spheres - - const mesh1 = new THREE.Mesh(sphere, material1); - mesh1.position.set(-250, 30, 0); - scene.add(mesh1); - - const sound1 = new THREE.PositionalAudio(listener); - const songElement = document.getElementById('song'); - sound1.setMediaElementSource(songElement); - sound1.setRefDistance(20); - songElement.play(); - mesh1.add(sound1); - - // - - const mesh2 = new THREE.Mesh(sphere, material2); - mesh2.position.set(250, 30, 0); - scene.add(mesh2); - - const sound2 = new THREE.PositionalAudio(listener); - const skullbeatzElement = document.getElementById('skullbeatz'); - sound2.setMediaElementSource(skullbeatzElement); - sound2.setRefDistance(20); - skullbeatzElement.play(); - mesh2.add(sound2); - - // - - const mesh3 = new THREE.Mesh(sphere, material3); - mesh3.position.set(0, 30, -250); - scene.add(mesh3); - - const sound3 = new THREE.PositionalAudio(listener); - const oscillator = listener.context.createOscillator(); - oscillator.type = 'sine'; - oscillator.frequency.setValueAtTime(144, sound3.context.currentTime); - oscillator.start(0); - sound3.setNodeSource(oscillator); - sound3.setRefDistance(20); - sound3.setVolume(0.5); - mesh3.add(sound3); - - // analysers - - analyser1 = new THREE.AudioAnalyser(sound1, 32); - analyser2 = new THREE.AudioAnalyser(sound2, 32); - analyser3 = new THREE.AudioAnalyser(sound3, 32); - - // global ambient audio - - const sound4 = new THREE.Audio(listener); - const utopiaElement = document.getElementById('utopia'); - sound4.setMediaElementSource(utopiaElement); - sound4.setVolume(0.5); - utopiaElement.play(); - - // ground - - const helper = new THREE.GridHelper(1000, 10, 0x444444, 0x444444); - helper.position.y = 0.1; - scene.add(helper); - - // - - const SoundControls = function () { - this.master = listener.getMasterVolume(); - this.firstSphere = sound1.getVolume(); - this.secondSphere = sound2.getVolume(); - this.thirdSphere = sound3.getVolume(); - this.Ambient = sound4.getVolume(); - }; - - const GeneratorControls = function () { - this.frequency = oscillator.frequency.value; - this.wavetype = oscillator.type; - }; - - const gui = new GUI(); - const soundControls = new SoundControls(); - const generatorControls = new GeneratorControls(); - const volumeFolder = gui.addFolder('sound volume'); - const generatorFolder = gui.addFolder('sound generator'); - - volumeFolder - .add(soundControls, 'master') - .min(0.0) - .max(1.0) - .step(0.01) - .onChange(function () { - listener.setMasterVolume(soundControls.master); - }); - volumeFolder - .add(soundControls, 'firstSphere') - .min(0.0) - .max(1.0) - .step(0.01) - .onChange(function () { - sound1.setVolume(soundControls.firstSphere); - }); - volumeFolder - .add(soundControls, 'secondSphere') - .min(0.0) - .max(1.0) - .step(0.01) - .onChange(function () { - sound2.setVolume(soundControls.secondSphere); - }); - - volumeFolder - .add(soundControls, 'thirdSphere') - .min(0.0) - .max(1.0) - .step(0.01) - .onChange(function () { - sound3.setVolume(soundControls.thirdSphere); - }); - volumeFolder - .add(soundControls, 'Ambient') - .min(0.0) - .max(1.0) - .step(0.01) - .onChange(function () { - sound4.setVolume(soundControls.Ambient); - }); - volumeFolder.open(); - generatorFolder - .add(generatorControls, 'frequency') - .min(50.0) - .max(5000.0) - .step(1.0) - .onChange(function () { - oscillator.frequency.setValueAtTime(generatorControls.frequency, listener.context.currentTime); - }); - generatorFolder - .add(generatorControls, 'wavetype', ['sine', 'square', 'sawtooth', 'triangle']) - .onChange(function () { - oscillator.type = generatorControls.wavetype; - }); - - generatorFolder.open(); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // - - controls = new FirstPersonControls(camera, renderer.domElement); - - controls.movementSpeed = 70; - controls.lookSpeed = 0.05; - controls.lookVertical = false; - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - const delta = timer.getDelta(); - - controls.update(delta); - - material1.emissive.b = analyser1.getAverageFrequency() / 256; - material2.emissive.b = analyser2.getAverageFrequency() / 256; - material3.emissive.b = analyser3.getAverageFrequency() / 256; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webaudio_timing.ts b/examples-testing/examples/webaudio_timing.ts deleted file mode 100644 index f37b5c5cf..000000000 --- a/examples-testing/examples/webaudio_timing.ts +++ /dev/null @@ -1,155 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let scene, camera, renderer, timer; - -const objects = []; - -const speed = 2.5; -const height = 3; -const offset = 0.5; - -const startButton = document.getElementById('startButton'); -startButton.addEventListener('click', init); - -function init() { - const overlay = document.getElementById('overlay'); - overlay.remove(); - - const container = document.getElementById('container'); - - scene = new THREE.Scene(); - - timer = new THREE.Timer(); - timer.connect(document); - - // - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(7, 3, 7); - - // lights - - const ambientLight = new THREE.AmbientLight(0xcccccc); - scene.add(ambientLight); - - const directionalLight = new THREE.DirectionalLight(0xffffff, 2.5); - directionalLight.position.set(0, 5, 5); - scene.add(directionalLight); - - const d = 5; - directionalLight.castShadow = true; - directionalLight.shadow.camera.left = -d; - directionalLight.shadow.camera.right = d; - directionalLight.shadow.camera.top = d; - directionalLight.shadow.camera.bottom = -d; - - directionalLight.shadow.camera.near = 1; - directionalLight.shadow.camera.far = 20; - - directionalLight.shadow.mapSize.x = 1024; - directionalLight.shadow.mapSize.y = 1024; - - // audio - - const audioLoader = new THREE.AudioLoader(); - - const listener = new THREE.AudioListener(); - camera.add(listener); - - // floor - - const floorGeometry = new THREE.PlaneGeometry(10, 10); - const floorMaterial = new THREE.MeshLambertMaterial({ color: 0x4676b6 }); - - const floor = new THREE.Mesh(floorGeometry, floorMaterial); - floor.rotation.x = Math.PI * -0.5; - floor.receiveShadow = true; - scene.add(floor); - - // objects - - const count = 5; - const radius = 3; - - const ballGeometry = new THREE.SphereGeometry(0.3, 32, 16); - ballGeometry.translate(0, 0.3, 0); - const ballMaterial = new THREE.MeshLambertMaterial({ color: 0xcccccc }); - - // create objects when audio buffer is loaded - - audioLoader.load('sounds/ping_pong.mp3', function (buffer) { - for (let i = 0; i < count; i++) { - const s = (i / count) * Math.PI * 2; - - const ball = new THREE.Mesh(ballGeometry, ballMaterial); - ball.castShadow = true; - ball.userData.down = false; - - ball.position.x = radius * Math.cos(s); - ball.position.z = radius * Math.sin(s); - - const audio = new THREE.PositionalAudio(listener); - audio.setBuffer(buffer); - ball.add(audio); - - scene.add(ball); - objects.push(ball); - } - - renderer.setAnimationLoop(animate); - }); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.shadowMap.enabled = true; - container.appendChild(renderer.domElement); - - // - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 1; - controls.maxDistance = 25; - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - const time = timer.getElapsed(); - - for (let i = 0; i < objects.length; i++) { - const ball = objects[i]; - - const previousHeight = ball.position.y; - ball.position.y = Math.abs(Math.sin(i * offset + time * speed) * height); - - if (ball.position.y < previousHeight) { - ball.userData.down = true; - } else { - if (ball.userData.down === true) { - // ball changed direction from down to up - - const audio = ball.children[0]; - audio.play(); // play audio with perfect timing when ball hits the surface - ball.userData.down = false; - } - } - } - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webaudio_visualizer.ts b/examples-testing/examples/webaudio_visualizer.ts deleted file mode 100644 index a3f58cb36..000000000 --- a/examples-testing/examples/webaudio_visualizer.ts +++ /dev/null @@ -1,86 +0,0 @@ -import * as THREE from 'three'; - -let scene, camera, renderer, analyser, uniforms; - -const startButton = document.getElementById('startButton'); -startButton.addEventListener('click', init); - -function init() { - const fftSize = 128; - - // - - const overlay = document.getElementById('overlay'); - overlay.remove(); - - // - - const container = document.getElementById('container'); - - scene = new THREE.Scene(); - - camera = new THREE.Camera(); - - // - - const listener = new THREE.AudioListener(); - - const audio = new THREE.Audio(listener); - const file = './sounds/376737_Skullbeatz___Bad_Cat_Maste.mp3'; - - if (/(iPad|iPhone|iPod)/g.test(navigator.userAgent)) { - const loader = new THREE.AudioLoader(); - loader.load(file, function (buffer) { - audio.setBuffer(buffer); - audio.play(); - }); - } else { - const mediaElement = new Audio(file); - mediaElement.play(); - - audio.setMediaElementSource(mediaElement); - } - - analyser = new THREE.AudioAnalyser(audio, fftSize); - - // - - uniforms = { - tAudioData: { value: new THREE.DataTexture(analyser.data, fftSize / 2, 1, THREE.RedFormat) }, - }; - - const material = new THREE.ShaderMaterial({ - uniforms: uniforms, - vertexShader: document.getElementById('vertexShader').textContent, - fragmentShader: document.getElementById('fragmentShader').textContent, - }); - - const geometry = new THREE.PlaneGeometry(1, 1); - - const mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - analyser.getFrequencyData(); - - uniforms.tAudioData.value.needsUpdate = true; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_animation_keyframes.ts b/examples-testing/examples/webgl_animation_keyframes.ts deleted file mode 100644 index 20a44a9ce..000000000 --- a/examples-testing/examples/webgl_animation_keyframes.ts +++ /dev/null @@ -1,96 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { Sky } from 'three/addons/objects/Sky.js'; - -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; - -let mixer; - -const timer = new THREE.Timer(); -timer.connect(document); -const container = document.getElementById('container'); - -const stats = new Stats(); -container.appendChild(stats.dom); - -const renderer = new THREE.WebGLRenderer({ antialias: true }); -renderer.setPixelRatio(window.devicePixelRatio); -renderer.setSize(window.innerWidth, window.innerHeight); -renderer.toneMapping = THREE.ACESFilmicToneMapping; -container.appendChild(renderer.domElement); - -const scene = new THREE.Scene(); - -// Sky - -const sky = new Sky(); -sky.scale.setScalar(10000); -scene.add(sky); - -const uniforms = sky.material.uniforms; -uniforms['turbidity'].value = 0; -uniforms['rayleigh'].value = 3; -uniforms['mieDirectionalG'].value = 0.7; -uniforms['cloudElevation'].value = 1; -uniforms['sunPosition'].value.set(-0.8, 0.19, 0.56); // elevation: 11, azimuth: -55 - -const pmremGenerator = new THREE.PMREMGenerator(renderer); -const environment = pmremGenerator.fromScene(sky).texture; -scene.environment = environment; - -const camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 100); -camera.position.set(5, 2, 8); - -const controls = new OrbitControls(camera, renderer.domElement); -controls.enableDamping = true; -controls.target.set(0, 0.7, 0); -controls.update(); - -const dracoLoader = new DRACOLoader(); -dracoLoader.setDecoderPath('jsm/libs/draco/gltf/'); - -const loader = new GLTFLoader(); -loader.setDRACOLoader(dracoLoader); -loader.load( - 'models/gltf/LittlestTokyo.glb', - function (gltf) { - const model = gltf.scene; - model.position.set(1, 1, 0); - model.scale.set(0.01, 0.01, 0.01); - scene.add(model); - - mixer = new THREE.AnimationMixer(model); - mixer.clipAction(gltf.animations[0]).play(); - - renderer.setAnimationLoop(animate); - }, - undefined, - function (e) { - console.error(e); - }, -); - -window.onresize = function () { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -}; - -function animate() { - timer.update(); - - const delta = timer.getDelta(); - - mixer.update(delta); - - controls.update(); - - stats.update(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_animation_multiple.ts b/examples-testing/examples/webgl_animation_multiple.ts deleted file mode 100644 index 87355e968..000000000 --- a/examples-testing/examples/webgl_animation_multiple.ts +++ /dev/null @@ -1,200 +0,0 @@ -import * as THREE from 'three'; - -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import * as SkeletonUtils from 'three/addons/utils/SkeletonUtils.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let camera, scene, renderer, timer; -let model, animations; - -const mixers = [], - objects = []; - -const params = { - sharedSkeleton: false, -}; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(2, 3, -6); - camera.lookAt(0, 1, 0); - - timer = new THREE.Timer(); - timer.connect(document); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xa0a0a0); - scene.fog = new THREE.Fog(0xa0a0a0, 10, 50); - - const hemiLight = new THREE.HemisphereLight(0xffffff, 0x8d8d8d, 3); - hemiLight.position.set(0, 20, 0); - scene.add(hemiLight); - - const dirLight = new THREE.DirectionalLight(0xffffff, 3); - dirLight.position.set(-3, 10, -10); - dirLight.castShadow = true; - dirLight.shadow.camera.top = 4; - dirLight.shadow.camera.bottom = -4; - dirLight.shadow.camera.left = -4; - dirLight.shadow.camera.right = 4; - dirLight.shadow.camera.near = 0.1; - dirLight.shadow.camera.far = 40; - scene.add(dirLight); - - // scene.add( new THREE.CameraHelper( dirLight.shadow.camera ) ); - - // ground - - const mesh = new THREE.Mesh( - new THREE.PlaneGeometry(200, 200), - new THREE.MeshPhongMaterial({ color: 0xcbcbcb, depthWrite: false }), - ); - mesh.rotation.x = -Math.PI / 2; - mesh.receiveShadow = true; - scene.add(mesh); - - const loader = new GLTFLoader(); - loader.load('models/gltf/Soldier.glb', function (gltf) { - model = gltf.scene; - animations = gltf.animations; - - model.traverse(function (object) { - if (object.isMesh) object.castShadow = true; - }); - - setupDefaultScene(); - }); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.shadowMap.enabled = true; - document.body.appendChild(renderer.domElement); - - window.addEventListener('resize', onWindowResize); - - const gui = new GUI(); - - gui.add(params, 'sharedSkeleton').onChange(function () { - clearScene(); - - if (params.sharedSkeleton === true) { - setupSharedSkeletonScene(); - } else { - setupDefaultScene(); - } - }); - gui.open(); -} - -function clearScene() { - for (const mixer of mixers) { - mixer.stopAllAction(); - } - - mixers.length = 0; - - // - - for (const object of objects) { - scene.remove(object); - - scene.traverse(function (child) { - if (child.isSkinnedMesh) child.skeleton.dispose(); - }); - } -} - -function setupDefaultScene() { - // three cloned models with independent skeletons. - // each model can have its own animation state - - const model1 = SkeletonUtils.clone(model); - const model2 = SkeletonUtils.clone(model); - const model3 = SkeletonUtils.clone(model); - - model1.position.x = -2; - model2.position.x = 0; - model3.position.x = 2; - - const mixer1 = new THREE.AnimationMixer(model1); - const mixer2 = new THREE.AnimationMixer(model2); - const mixer3 = new THREE.AnimationMixer(model3); - - mixer1.clipAction(animations[0]).play(); // idle - mixer2.clipAction(animations[1]).play(); // run - mixer3.clipAction(animations[3]).play(); // walk - - scene.add(model1, model2, model3); - - objects.push(model1, model2, model3); - mixers.push(mixer1, mixer2, mixer3); -} - -function setupSharedSkeletonScene() { - // three cloned models with a single shared skeleton. - // all models share the same animation state - - const sharedModel = SkeletonUtils.clone(model); - const shareSkinnedMesh = sharedModel.getObjectByName('vanguard_Mesh'); - const sharedSkeleton = shareSkinnedMesh.skeleton; - const sharedParentBone = sharedModel.getObjectByName('mixamorigHips'); - scene.add(sharedParentBone); // the bones need to be in the scene for the animation to work - - const model1 = shareSkinnedMesh.clone(); - const model2 = shareSkinnedMesh.clone(); - const model3 = shareSkinnedMesh.clone(); - - model1.bindMode = THREE.DetachedBindMode; - model2.bindMode = THREE.DetachedBindMode; - model3.bindMode = THREE.DetachedBindMode; - - const identity = new THREE.Matrix4(); - - model1.bind(sharedSkeleton, identity); - model2.bind(sharedSkeleton, identity); - model3.bind(sharedSkeleton, identity); - - model1.position.x = -2; - model2.position.x = 0; - model3.position.x = 2; - - // apply transformation from the glTF asset - - model1.scale.setScalar(0.01); - model1.rotation.x = -Math.PI * 0.5; - model2.scale.setScalar(0.01); - model2.rotation.x = -Math.PI * 0.5; - model3.scale.setScalar(0.01); - model3.rotation.x = -Math.PI * 0.5; - - // - - const mixer = new THREE.AnimationMixer(sharedParentBone); - mixer.clipAction(animations[1]).play(); - - scene.add(sharedParentBone, model1, model2, model3); - - objects.push(sharedParentBone, model1, model2, model3); - mixers.push(mixer); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - const delta = timer.getDelta(); - - for (const mixer of mixers) mixer.update(delta); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_animation_skinning_morph.ts b/examples-testing/examples/webgl_animation_skinning_morph.ts deleted file mode 100644 index 0ae68e7d7..000000000 --- a/examples-testing/examples/webgl_animation_skinning_morph.ts +++ /dev/null @@ -1,190 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -let container, stats, timer, gui, mixer, actions, activeAction, previousAction; -let camera, scene, renderer, model, face; - -const api = { state: 'Walking' }; - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 100); - camera.position.set(-5, 3, 10); - camera.lookAt(0, 2, 0); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xe0e0e0); - scene.fog = new THREE.Fog(0xe0e0e0, 20, 100); - - timer = new THREE.Timer(); - timer.connect(document); - - // lights - - const hemiLight = new THREE.HemisphereLight(0xffffff, 0x8d8d8d, 3); - hemiLight.position.set(0, 20, 0); - scene.add(hemiLight); - - const dirLight = new THREE.DirectionalLight(0xffffff, 3); - dirLight.position.set(0, 20, 10); - scene.add(dirLight); - - // ground - - const mesh = new THREE.Mesh( - new THREE.PlaneGeometry(2000, 2000), - new THREE.MeshPhongMaterial({ color: 0xcbcbcb, depthWrite: false }), - ); - mesh.rotation.x = -Math.PI / 2; - scene.add(mesh); - - const grid = new THREE.GridHelper(200, 40, 0x000000, 0x000000); - grid.material.opacity = 0.2; - grid.material.transparent = true; - scene.add(grid); - - // model - - const loader = new GLTFLoader(); - loader.load( - 'models/gltf/RobotExpressive/RobotExpressive.glb', - function (gltf) { - model = gltf.scene; - scene.add(model); - - createGUI(model, gltf.animations); - }, - undefined, - function (e) { - console.error(e); - }, - ); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - window.addEventListener('resize', onWindowResize); - - // stats - stats = new Stats(); - container.appendChild(stats.dom); -} - -function createGUI(model, animations) { - const states = ['Idle', 'Walking', 'Running', 'Dance', 'Death', 'Sitting', 'Standing']; - const emotes = ['Jump', 'Yes', 'No', 'Wave', 'Punch', 'ThumbsUp']; - - gui = new GUI(); - - mixer = new THREE.AnimationMixer(model); - - actions = {}; - - for (let i = 0; i < animations.length; i++) { - const clip = animations[i]; - const action = mixer.clipAction(clip); - actions[clip.name] = action; - - if (emotes.indexOf(clip.name) >= 0 || states.indexOf(clip.name) >= 4) { - action.clampWhenFinished = true; - action.loop = THREE.LoopOnce; - } - } - - // states - - const statesFolder = gui.addFolder('States'); - - const clipCtrl = statesFolder.add(api, 'state').options(states); - - clipCtrl.onChange(function () { - fadeToAction(api.state, 0.5); - }); - - statesFolder.open(); - - // emotes - - const emoteFolder = gui.addFolder('Emotes'); - - function createEmoteCallback(name) { - api[name] = function () { - fadeToAction(name, 0.2); - - mixer.addEventListener('finished', restoreState); - }; - - emoteFolder.add(api, name); - } - - function restoreState() { - mixer.removeEventListener('finished', restoreState); - - fadeToAction(api.state, 0.2); - } - - for (let i = 0; i < emotes.length; i++) { - createEmoteCallback(emotes[i]); - } - - emoteFolder.open(); - - // expressions - - face = model.getObjectByName('Head_4'); - - const expressions = Object.keys(face.morphTargetDictionary); - const expressionFolder = gui.addFolder('Expressions'); - - for (let i = 0; i < expressions.length; i++) { - expressionFolder.add(face.morphTargetInfluences, i, 0, 1, 0.01).name(expressions[i]); - } - - activeAction = actions['Walking']; - activeAction.play(); - - expressionFolder.open(); -} - -function fadeToAction(name, duration) { - previousAction = activeAction; - activeAction = actions[name]; - - if (previousAction !== activeAction) { - previousAction.fadeOut(duration); - } - - activeAction.reset().setEffectiveTimeScale(1).setEffectiveWeight(1).fadeIn(duration).play(); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - timer.update(); - - const dt = timer.getDelta(); - - if (mixer) mixer.update(dt); - - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/webgl_animation_walk.ts b/examples-testing/examples/webgl_animation_walk.ts deleted file mode 100644 index b4ab9c444..000000000 --- a/examples-testing/examples/webgl_animation_walk.ts +++ /dev/null @@ -1,379 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; - -let scene, renderer, camera, floor, orbitControls; -let group, followGroup, model, skeleton, mixer, timer; - -let actions; - -const settings = { - show_skeleton: false, - fixe_transition: true, -}; - -const PI = Math.PI; -const PI90 = Math.PI / 2; - -const controls = { - key: [0, 0], - ease: new THREE.Vector3(), - position: new THREE.Vector3(), - up: new THREE.Vector3(0, 1, 0), - rotate: new THREE.Quaternion(), - current: 'Idle', - fadeDuration: 0.5, - runVelocity: 5, - walkVelocity: 1.8, - rotateSpeed: 0.05, - floorDecale: 0, -}; - -init(); - -function init() { - const container = document.getElementById('container'); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(0, 2, -5); - - timer = new THREE.Timer(); - timer.connect(document); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x5e5d5d); - scene.fog = new THREE.Fog(0x5e5d5d, 2, 20); - - group = new THREE.Group(); - scene.add(group); - - followGroup = new THREE.Group(); - scene.add(followGroup); - - const dirLight = new THREE.DirectionalLight(0xffffff, 5); - dirLight.position.set(-2, 5, -3); - dirLight.castShadow = true; - const cam = dirLight.shadow.camera; - cam.top = cam.right = 2; - cam.bottom = cam.left = -2; - cam.near = 3; - cam.far = 8; - dirLight.shadow.mapSize.set(1024, 1024); - followGroup.add(dirLight); - followGroup.add(dirLight.target); - - //scene.add( new THREE.CameraHelper( cam ) ); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = 0.5; - renderer.shadowMap.enabled = true; - renderer.shadowMap.type = THREE.PCFShadowMap; - container.appendChild(renderer.domElement); - - orbitControls = new OrbitControls(camera, renderer.domElement); - orbitControls.target.set(0, 1, 0); - orbitControls.enableDamping = true; - orbitControls.enablePan = false; - orbitControls.maxPolarAngle = PI90 - 0.05; - orbitControls.update(); - - // EVENTS - - window.addEventListener('resize', onWindowResize); - document.addEventListener('keydown', onKeyDown); - document.addEventListener('keyup', onKeyUp); - - // DEMO - - new HDRLoader().setPath('textures/equirectangular/').load('lobe.hdr', function (texture) { - texture.mapping = THREE.EquirectangularReflectionMapping; - scene.environment = texture; - scene.environmentIntensity = 1.5; - - loadModel(); - addFloor(); - }); -} - -function addFloor() { - const size = 50; - const repeat = 16; - - const maxAnisotropy = renderer.capabilities.getMaxAnisotropy(); - - const floorT = new THREE.TextureLoader().load('textures/floors/FloorsCheckerboard_S_Diffuse.jpg'); - floorT.colorSpace = THREE.SRGBColorSpace; - floorT.repeat.set(repeat, repeat); - floorT.wrapS = floorT.wrapT = THREE.RepeatWrapping; - floorT.anisotropy = maxAnisotropy; - - const floorN = new THREE.TextureLoader().load('textures/floors/FloorsCheckerboard_S_Normal.jpg'); - floorN.repeat.set(repeat, repeat); - floorN.wrapS = floorN.wrapT = THREE.RepeatWrapping; - floorN.anisotropy = maxAnisotropy; - - const mat = new THREE.MeshStandardMaterial({ - map: floorT, - normalMap: floorN, - normalScale: new THREE.Vector2(0.5, 0.5), - color: 0x404040, - depthWrite: false, - roughness: 0.85, - }); - - const g = new THREE.PlaneGeometry(size, size, 50, 50); - g.rotateX(-PI90); - - floor = new THREE.Mesh(g, mat); - floor.receiveShadow = true; - scene.add(floor); - - controls.floorDecale = (size / repeat) * 4; - - const bulbGeometry = new THREE.SphereGeometry(0.05, 16, 8); - const bulbLight = new THREE.PointLight(0xffee88, 2, 500, 2); - - const bulbMat = new THREE.MeshStandardMaterial({ emissive: 0xffffee, emissiveIntensity: 1, color: 0x000000 }); - bulbLight.add(new THREE.Mesh(bulbGeometry, bulbMat)); - bulbLight.position.set(1, 0.1, -3); - bulbLight.castShadow = true; - floor.add(bulbLight); -} - -function loadModel() { - const loader = new GLTFLoader(); - loader.load('models/gltf/Soldier.glb', function (gltf) { - model = gltf.scene; - group.add(model); - model.rotation.y = PI; - group.rotation.y = PI; - - model.traverse(function (object) { - if (object.isMesh) { - if (object.name == 'vanguard_Mesh') { - object.castShadow = true; - object.receiveShadow = true; - //object.material.envMapIntensity = 0.5; - object.material.metalness = 1.0; - object.material.roughness = 0.2; - object.material.color.set(1, 1, 1); - object.material.metalnessMap = object.material.map; - } else { - object.material.metalness = 1; - object.material.roughness = 0; - object.material.transparent = true; - object.material.opacity = 0.8; - object.material.color.set(1, 1, 1); - } - } - }); - - // - - skeleton = new THREE.SkeletonHelper(model); - skeleton.setColors(new THREE.Color(0xe000ff), new THREE.Color(0x00e0ff)); - skeleton.visible = false; - scene.add(skeleton); - - // - - createPanel(); - - // - - const animations = gltf.animations; - - mixer = new THREE.AnimationMixer(model); - - actions = { - Idle: mixer.clipAction(animations[0]), - Walk: mixer.clipAction(animations[3]), - Run: mixer.clipAction(animations[1]), - }; - - for (const m in actions) { - actions[m].enabled = true; - actions[m].setEffectiveTimeScale(1); - if (m !== 'Idle') actions[m].setEffectiveWeight(0); - } - - actions.Idle.play(); - - animate(); - }); -} - -function updateCharacter(delta) { - const fade = controls.fadeDuration; - const key = controls.key; - const up = controls.up; - const ease = controls.ease; - const rotate = controls.rotate; - const position = controls.position; - const azimuth = orbitControls.getAzimuthalAngle(); - - const active = key[0] === 0 && key[1] === 0 ? false : true; - const play = active ? (key[2] ? 'Run' : 'Walk') : 'Idle'; - - // change animation - - if (controls.current != play) { - const current = actions[play]; - const old = actions[controls.current]; - controls.current = play; - - if (settings.fixe_transition) { - current.reset(); - current.weight = 1.0; - current.stopFading(); - old.stopFading(); - // synchro if not idle - if (play !== 'Idle') current.time = old.time * (current.getClip().duration / old.getClip().duration); - old._scheduleFading(fade, old.getEffectiveWeight(), 0); - current._scheduleFading(fade, current.getEffectiveWeight(), 1); - current.play(); - } else { - setWeight(current, 1.0); - old.fadeOut(fade); - current.reset().fadeIn(fade).play(); - } - } - - // move object - - if (controls.current !== 'Idle') { - // run/walk velocity - const velocity = controls.current == 'Run' ? controls.runVelocity : controls.walkVelocity; - - // direction with key - ease.set(key[1], 0, key[0]).multiplyScalar(velocity * delta); - - // calculate camera direction - const angle = unwrapRad(Math.atan2(ease.x, ease.z) + azimuth); - rotate.setFromAxisAngle(up, angle); - - // apply camera angle on ease - controls.ease.applyAxisAngle(up, azimuth); - - position.add(ease); - camera.position.add(ease); - - group.position.copy(position); - group.quaternion.rotateTowards(rotate, controls.rotateSpeed); - - orbitControls.target.copy(position).add({ x: 0, y: 1, z: 0 }); - followGroup.position.copy(position); - - // Move the floor without any limit - const dx = position.x - floor.position.x; - const dz = position.z - floor.position.z; - if (Math.abs(dx) > controls.floorDecale) floor.position.x += dx; - if (Math.abs(dz) > controls.floorDecale) floor.position.z += dz; - } - - if (mixer) mixer.update(delta); - - orbitControls.update(); -} - -function unwrapRad(r) { - return Math.atan2(Math.sin(r), Math.cos(r)); -} - -function createPanel() { - const panel = new GUI({ width: 310 }); - - panel.add(settings, 'show_skeleton').onChange(b => { - skeleton.visible = b; - }); - - panel.add(settings, 'fixe_transition'); -} - -function setWeight(action, weight) { - action.enabled = true; - action.setEffectiveTimeScale(1); - action.setEffectiveWeight(weight); -} - -function onKeyDown(event) { - const key = controls.key; - switch (event.code) { - case 'ArrowUp': - case 'KeyW': - case 'KeyZ': - key[0] = -1; - break; - case 'ArrowDown': - case 'KeyS': - key[0] = 1; - break; - case 'ArrowLeft': - case 'KeyA': - case 'KeyQ': - key[1] = -1; - break; - case 'ArrowRight': - case 'KeyD': - key[1] = 1; - break; - case 'ShiftLeft': - case 'ShiftRight': - key[2] = 1; - break; - } -} - -function onKeyUp(event) { - const key = controls.key; - switch (event.code) { - case 'ArrowUp': - case 'KeyW': - case 'KeyZ': - key[0] = key[0] < 0 ? 0 : key[0]; - break; - case 'ArrowDown': - case 'KeyS': - key[0] = key[0] > 0 ? 0 : key[0]; - break; - case 'ArrowLeft': - case 'KeyA': - case 'KeyQ': - key[1] = key[1] < 0 ? 0 : key[1]; - break; - case 'ArrowRight': - case 'KeyD': - key[1] = key[1] > 0 ? 0 : key[1]; - break; - case 'ShiftLeft': - case 'ShiftRight': - key[2] = 0; - break; - } -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - // Render loop - - const delta = timer.getDelta(); - - updateCharacter(delta); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_buffergeometry.ts b/examples-testing/examples/webgl_buffergeometry.ts deleted file mode 100644 index 28b2c96a4..000000000 --- a/examples-testing/examples/webgl_buffergeometry.ts +++ /dev/null @@ -1,178 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -let container, stats; - -let camera, scene, renderer; - -let mesh; - -init(); -animate(); - -function init() { - container = document.getElementById('container'); - - // - - camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 1, 3500); - camera.position.z = 2750; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x050505); - scene.fog = new THREE.Fog(0x050505, 2000, 3500); - - // - - scene.add(new THREE.AmbientLight(0xcccccc)); - - const light1 = new THREE.DirectionalLight(0xffffff, 1.5); - light1.position.set(1, 1, 1); - scene.add(light1); - - const light2 = new THREE.DirectionalLight(0xffffff, 4.5); - light2.position.set(0, -1, 0); - scene.add(light2); - - // - - const triangles = 160000; - - const geometry = new THREE.BufferGeometry(); - - const positions = []; - const normals = []; - const colors = []; - - const color = new THREE.Color(); - - const n = 800, - n2 = n / 2; // triangles spread in the cube - const d = 12, - d2 = d / 2; // individual triangle size - - const pA = new THREE.Vector3(); - const pB = new THREE.Vector3(); - const pC = new THREE.Vector3(); - - const cb = new THREE.Vector3(); - const ab = new THREE.Vector3(); - - for (let i = 0; i < triangles; i++) { - // positions - - const x = Math.random() * n - n2; - const y = Math.random() * n - n2; - const z = Math.random() * n - n2; - - const ax = x + Math.random() * d - d2; - const ay = y + Math.random() * d - d2; - const az = z + Math.random() * d - d2; - - const bx = x + Math.random() * d - d2; - const by = y + Math.random() * d - d2; - const bz = z + Math.random() * d - d2; - - const cx = x + Math.random() * d - d2; - const cy = y + Math.random() * d - d2; - const cz = z + Math.random() * d - d2; - - positions.push(ax, ay, az); - positions.push(bx, by, bz); - positions.push(cx, cy, cz); - - // flat face normals - - pA.set(ax, ay, az); - pB.set(bx, by, bz); - pC.set(cx, cy, cz); - - cb.subVectors(pC, pB); - ab.subVectors(pA, pB); - cb.cross(ab); - - cb.normalize(); - - const nx = cb.x; - const ny = cb.y; - const nz = cb.z; - - normals.push(nx, ny, nz); - normals.push(nx, ny, nz); - normals.push(nx, ny, nz); - - // colors - - const vx = x / n + 0.5; - const vy = y / n + 0.5; - const vz = z / n + 0.5; - - color.setRGB(vx, vy, vz); - - const alpha = Math.random(); - - colors.push(color.r, color.g, color.b, alpha); - colors.push(color.r, color.g, color.b, alpha); - colors.push(color.r, color.g, color.b, alpha); - } - - function disposeArray() { - this.array = null; - } - - geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3).onUpload(disposeArray)); - geometry.setAttribute('normal', new THREE.Float32BufferAttribute(normals, 3).onUpload(disposeArray)); - geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 4).onUpload(disposeArray)); - - geometry.computeBoundingSphere(); - - const material = new THREE.MeshPhongMaterial({ - color: 0xd5d5d5, - specular: 0xffffff, - shininess: 250, - side: THREE.DoubleSide, - vertexColors: true, - transparent: true, - }); - - mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - // - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - const time = Date.now() * 0.001; - - mesh.rotation.x = time * 0.25; - mesh.rotation.y = time * 0.5; - - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/webgl_buffergeometry_attributes_integer.ts b/examples-testing/examples/webgl_buffergeometry_attributes_integer.ts deleted file mode 100644 index 00490b716..000000000 --- a/examples-testing/examples/webgl_buffergeometry_attributes_integer.ts +++ /dev/null @@ -1,142 +0,0 @@ -import * as THREE from 'three'; - -let camera, scene, renderer, mesh; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 1, 3500); - camera.position.z = 2500; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x050505); - scene.fog = new THREE.Fog(0x050505, 2000, 3500); - - // geometry - - const triangles = 10000; - - const geometry = new THREE.BufferGeometry(); - - const positions = []; - const uvs = []; - const textureIndices = []; - - const n = 800, - n2 = n / 2; // triangles spread in the cube - const d = 50, - d2 = d / 2; // individual triangle size - - for (let i = 0; i < triangles; i++) { - // positions - - const x = Math.random() * n - n2; - const y = Math.random() * n - n2; - const z = Math.random() * n - n2; - - const ax = x + Math.random() * d - d2; - const ay = y + Math.random() * d - d2; - const az = z + Math.random() * d - d2; - - const bx = x + Math.random() * d - d2; - const by = y + Math.random() * d - d2; - const bz = z + Math.random() * d - d2; - - const cx = x + Math.random() * d - d2; - const cy = y + Math.random() * d - d2; - const cz = z + Math.random() * d - d2; - - positions.push(ax, ay, az); - positions.push(bx, by, bz); - positions.push(cx, cy, cz); - - // uvs - - uvs.push(0, 0); - uvs.push(0.5, 1); - uvs.push(1, 0); - - // texture indices - - const t = i % 3; - textureIndices.push(t, t, t); - } - - geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)); - geometry.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2)); - geometry.setAttribute('textureIndex', new THREE.Int16BufferAttribute(textureIndices, 1)); - geometry.attributes.textureIndex.gpuType = THREE.IntType; - - geometry.computeBoundingSphere(); - - // material - - const loader = new THREE.TextureLoader(); - - const map1 = loader.load('textures/crate.gif'); - const map2 = loader.load('textures/floors/FloorsCheckerboard_S_Diffuse.jpg'); - const map3 = loader.load('textures/terrain/grasslight-big.jpg'); - - const material = new THREE.ShaderMaterial({ - uniforms: { - uTextures: { - value: [map1, map2, map3], - }, - }, - vertexShader: /* glsl */ ` - in int textureIndex; - - flat out int vIndex; // "flat" indicates that the value will not be interpolated (required for integer attributes) - out vec2 vUv; - - void main() { - - vIndex = textureIndex; - vUv = uv; - - gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); - - } - `, - fragmentShader: /* glsl */ ` - flat in int vIndex; - in vec2 vUv; - - uniform sampler2D uTextures[ 3 ]; - - out vec4 outColor; - - void main() { - - if ( vIndex == 0 ) outColor = texture( uTextures[ 0 ], vUv ); - else if ( vIndex == 1 ) outColor = texture( uTextures[ 1 ], vUv ); - else if ( vIndex == 2 ) outColor = texture( uTextures[ 2 ], vUv ); - - } - `, - side: THREE.DoubleSide, - glslVersion: THREE.GLSL3, - }); - - // mesh - - mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - // renderer - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); -} - -function animate() { - const time = Date.now() * 0.001; - - mesh.rotation.x = time * 0.25; - mesh.rotation.y = time * 0.5; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_buffergeometry_attributes_none.ts b/examples-testing/examples/webgl_buffergeometry_attributes_none.ts deleted file mode 100644 index a1424e871..000000000 --- a/examples-testing/examples/webgl_buffergeometry_attributes_none.ts +++ /dev/null @@ -1,56 +0,0 @@ -import * as THREE from 'three'; - -let camera, scene, renderer, mesh; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 1, 3500); - camera.position.z = 4; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x050505); - scene.fog = new THREE.Fog(0x050505, 2000, 3500); - - // geometry - - const triangleCount = 10000; - const vertexCountPerTriangle = 3; - const vertexCount = triangleCount * vertexCountPerTriangle; - - const geometry = new THREE.BufferGeometry(); - geometry.setDrawRange(0, vertexCount); - - // material - - const material = new THREE.RawShaderMaterial({ - uniforms: { - seed: { value: 42 }, - }, - vertexShader: document.getElementById('vertexShader').textContent, - fragmentShader: document.getElementById('fragmentShader').textContent, - side: THREE.DoubleSide, - glslVersion: THREE.GLSL3, - }); - - // mesh - - mesh = new THREE.Mesh(geometry, material); - mesh.frustumCulled = false; - scene.add(mesh); - - // renderer - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); -} - -function animate(time) { - mesh.rotation.x = (time / 1000.0) * 0.25; - mesh.rotation.y = (time / 1000.0) * 0.5; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_buffergeometry_custom_attributes_particles.ts b/examples-testing/examples/webgl_buffergeometry_custom_attributes_particles.ts deleted file mode 100644 index 0dffa65cc..000000000 --- a/examples-testing/examples/webgl_buffergeometry_custom_attributes_particles.ts +++ /dev/null @@ -1,103 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -let renderer, scene, camera, stats; - -let particleSystem, uniforms, geometry; - -const particles = 100000; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 10000); - camera.position.z = 300; - - scene = new THREE.Scene(); - - uniforms = { - pointTexture: { value: new THREE.TextureLoader().load('textures/sprites/spark1.png') }, - }; - - const shaderMaterial = new THREE.ShaderMaterial({ - uniforms: uniforms, - vertexShader: document.getElementById('vertexshader').textContent, - fragmentShader: document.getElementById('fragmentshader').textContent, - - blending: THREE.AdditiveBlending, - depthTest: false, - transparent: true, - vertexColors: true, - }); - - const radius = 200; - - geometry = new THREE.BufferGeometry(); - - const positions = []; - const colors = []; - const sizes = []; - - const color = new THREE.Color(); - - for (let i = 0; i < particles; i++) { - positions.push((Math.random() * 2 - 1) * radius); - positions.push((Math.random() * 2 - 1) * radius); - positions.push((Math.random() * 2 - 1) * radius); - - color.setHSL(i / particles, 1.0, 0.5); - - colors.push(color.r, color.g, color.b); - - sizes.push(20); - } - - geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)); - geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); - geometry.setAttribute('size', new THREE.Float32BufferAttribute(sizes, 1).setUsage(THREE.DynamicDrawUsage)); - - particleSystem = new THREE.Points(geometry, shaderMaterial); - - scene.add(particleSystem); - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - - const container = document.getElementById('container'); - container.appendChild(renderer.domElement); - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - const time = Date.now() * 0.005; - - particleSystem.rotation.z = 0.01 * time; - - const sizes = geometry.attributes.size.array; - - for (let i = 0; i < particles; i++) { - sizes[i] = 10 * (1 + Math.sin(0.1 * i + time)); - } - - geometry.attributes.size.needsUpdate = true; - - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/webgl_buffergeometry_drawrange.ts b/examples-testing/examples/webgl_buffergeometry_drawrange.ts deleted file mode 100644 index 142ff43bf..000000000 --- a/examples-testing/examples/webgl_buffergeometry_drawrange.ts +++ /dev/null @@ -1,239 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let group; -let container, stats; -const particlesData = []; -let camera, scene, renderer; -let positions, colors; -let particles; -let pointCloud; -let particlePositions; -let linesMesh; - -const maxParticleCount = 1000; -let particleCount = 500; -const r = 800; -const rHalf = r / 2; - -const effectController = { - showDots: true, - showLines: true, - minDistance: 150, - limitConnections: false, - maxConnections: 20, - particleCount: 500, -}; - -init(); - -function initGUI() { - const gui = new GUI(); - - gui.add(effectController, 'showDots').onChange(function (value) { - pointCloud.visible = value; - }); - gui.add(effectController, 'showLines').onChange(function (value) { - linesMesh.visible = value; - }); - gui.add(effectController, 'minDistance', 10, 300); - gui.add(effectController, 'limitConnections'); - gui.add(effectController, 'maxConnections', 0, 30, 1); - gui.add(effectController, 'particleCount', 0, maxParticleCount, 1).onChange(function (value) { - particleCount = value; - particles.setDrawRange(0, particleCount); - }); -} - -function init() { - initGUI(); - - container = document.getElementById('container'); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 4000); - camera.position.z = 1750; - - const controls = new OrbitControls(camera, container); - controls.minDistance = 1000; - controls.maxDistance = 3000; - - scene = new THREE.Scene(); - - group = new THREE.Group(); - scene.add(group); - - const helper = new THREE.BoxHelper(new THREE.Mesh(new THREE.BoxGeometry(r, r, r))); - helper.material.color.setHex(0x474747); - helper.material.blending = THREE.AdditiveBlending; - helper.material.transparent = true; - group.add(helper); - - const segments = maxParticleCount * maxParticleCount; - - positions = new Float32Array(segments * 3); - colors = new Float32Array(segments * 3); - - const pMaterial = new THREE.PointsMaterial({ - color: 0xffffff, - size: 3, - blending: THREE.AdditiveBlending, - transparent: true, - sizeAttenuation: false, - }); - - particles = new THREE.BufferGeometry(); - particlePositions = new Float32Array(maxParticleCount * 3); - - for (let i = 0; i < maxParticleCount; i++) { - const x = Math.random() * r - r / 2; - const y = Math.random() * r - r / 2; - const z = Math.random() * r - r / 2; - - particlePositions[i * 3] = x; - particlePositions[i * 3 + 1] = y; - particlePositions[i * 3 + 2] = z; - - // add it to the geometry - particlesData.push({ - velocity: new THREE.Vector3(-1 + Math.random() * 2, -1 + Math.random() * 2, -1 + Math.random() * 2), - numConnections: 0, - }); - } - - particles.setDrawRange(0, particleCount); - particles.setAttribute( - 'position', - new THREE.BufferAttribute(particlePositions, 3).setUsage(THREE.DynamicDrawUsage), - ); - - // create the particle system - pointCloud = new THREE.Points(particles, pMaterial); - group.add(pointCloud); - - const geometry = new THREE.BufferGeometry(); - - geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3).setUsage(THREE.DynamicDrawUsage)); - geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3).setUsage(THREE.DynamicDrawUsage)); - - geometry.computeBoundingSphere(); - - geometry.setDrawRange(0, 0); - - const material = new THREE.LineBasicMaterial({ - vertexColors: true, - blending: THREE.AdditiveBlending, - transparent: true, - }); - - linesMesh = new THREE.LineSegments(geometry, material); - group.add(linesMesh); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - // - - stats = new Stats(); - container.appendChild(stats.dom); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - let vertexpos = 0; - let colorpos = 0; - let numConnected = 0; - - for (let i = 0; i < particleCount; i++) particlesData[i].numConnections = 0; - - for (let i = 0; i < particleCount; i++) { - // get the particle - const particleData = particlesData[i]; - - particlePositions[i * 3] += particleData.velocity.x; - particlePositions[i * 3 + 1] += particleData.velocity.y; - particlePositions[i * 3 + 2] += particleData.velocity.z; - - if (particlePositions[i * 3 + 1] < -rHalf || particlePositions[i * 3 + 1] > rHalf) - particleData.velocity.y = -particleData.velocity.y; - - if (particlePositions[i * 3] < -rHalf || particlePositions[i * 3] > rHalf) - particleData.velocity.x = -particleData.velocity.x; - - if (particlePositions[i * 3 + 2] < -rHalf || particlePositions[i * 3 + 2] > rHalf) - particleData.velocity.z = -particleData.velocity.z; - - if (effectController.limitConnections && particleData.numConnections >= effectController.maxConnections) - continue; - - // Check collision - for (let j = i + 1; j < particleCount; j++) { - const particleDataB = particlesData[j]; - if (effectController.limitConnections && particleDataB.numConnections >= effectController.maxConnections) - continue; - - const dx = particlePositions[i * 3] - particlePositions[j * 3]; - const dy = particlePositions[i * 3 + 1] - particlePositions[j * 3 + 1]; - const dz = particlePositions[i * 3 + 2] - particlePositions[j * 3 + 2]; - const dist = Math.sqrt(dx * dx + dy * dy + dz * dz); - - if (dist < effectController.minDistance) { - particleData.numConnections++; - particleDataB.numConnections++; - - const alpha = 1.0 - dist / effectController.minDistance; - - positions[vertexpos++] = particlePositions[i * 3]; - positions[vertexpos++] = particlePositions[i * 3 + 1]; - positions[vertexpos++] = particlePositions[i * 3 + 2]; - - positions[vertexpos++] = particlePositions[j * 3]; - positions[vertexpos++] = particlePositions[j * 3 + 1]; - positions[vertexpos++] = particlePositions[j * 3 + 2]; - - colors[colorpos++] = alpha; - colors[colorpos++] = alpha; - colors[colorpos++] = alpha; - - colors[colorpos++] = alpha; - colors[colorpos++] = alpha; - colors[colorpos++] = alpha; - - numConnected++; - } - } - } - - linesMesh.geometry.setDrawRange(0, numConnected * 2); - linesMesh.geometry.attributes.position.needsUpdate = true; - linesMesh.geometry.attributes.color.needsUpdate = true; - - pointCloud.geometry.attributes.position.needsUpdate = true; - - render(); - - stats.update(); -} - -function render() { - const time = Date.now() * 0.001; - - group.rotation.y = time * 0.1; - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_buffergeometry_glbufferattribute.ts b/examples-testing/examples/webgl_buffergeometry_glbufferattribute.ts deleted file mode 100644 index 80fa828bb..000000000 --- a/examples-testing/examples/webgl_buffergeometry_glbufferattribute.ts +++ /dev/null @@ -1,140 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -let container, stats; - -let camera, scene, renderer; - -let points; - -const particles = 300000; -let drawCount = 10000; - -init(); -animate(); - -function init() { - container = document.getElementById('container'); - - // - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - - container.appendChild(renderer.domElement); - - // - - camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 5, 3500); - camera.position.z = 2750; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x050505); - scene.fog = new THREE.Fog(0x050505, 2000, 3500); - - // - - const geometry = new THREE.BufferGeometry(); - - const positions = []; - const positions2 = []; - const colors = []; - - const color = new THREE.Color(); - - const n = 1000, - n2 = n / 2; // particles spread in the cube - - for (let i = 0; i < particles; i++) { - // positions - - const x = Math.random() * n - n2; - const y = Math.random() * n - n2; - const z = Math.random() * n - n2; - - positions.push(x, y, z); - positions2.push(z * 0.3, x * 0.3, y * 0.3); - - // colors - - const vx = x / n + 0.5; - const vy = y / n + 0.5; - const vz = z / n + 0.5; - - color.setRGB(vx, vy, vz, THREE.SRGBColorSpace); - - const hex = color.getHex(THREE.LinearSRGBColorSpace); - colors.push((hex >> 16) & 255, (hex >> 8) & 255, hex & 255); - } - - const gl = renderer.getContext(); - - const pos = gl.createBuffer(); - gl.bindBuffer(gl.ARRAY_BUFFER, pos); - gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW); - - const pos2 = gl.createBuffer(); - gl.bindBuffer(gl.ARRAY_BUFFER, pos2); - gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions2), gl.STATIC_DRAW); - - const rgb = gl.createBuffer(); - gl.bindBuffer(gl.ARRAY_BUFFER, rgb); - gl.bufferData(gl.ARRAY_BUFFER, new Uint8Array(colors), gl.STATIC_DRAW); - - const posAttr1 = new THREE.GLBufferAttribute(pos, gl.FLOAT, 3, 4, particles); - const posAttr2 = new THREE.GLBufferAttribute(pos2, gl.FLOAT, 3, 4, particles); - geometry.setAttribute('position', posAttr1); - - setInterval(function () { - const attr = geometry.getAttribute('position'); - - geometry.setAttribute('position', attr === posAttr1 ? posAttr2 : posAttr1); - }, 2000); - - geometry.setAttribute('color', new THREE.GLBufferAttribute(rgb, gl.UNSIGNED_BYTE, 3, 1, particles, true)); - - // - - const material = new THREE.PointsMaterial({ size: 15, vertexColors: true }); - - points = new THREE.Points(geometry, material); - - geometry.boundingSphere = new THREE.Sphere().set(new THREE.Vector3(), 500); - - scene.add(points); - - // - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - drawCount = (Math.max(5000, drawCount) + Math.floor(500 * Math.random())) % particles; - points.geometry.setDrawRange(0, drawCount); - - const time = Date.now() * 0.001; - - points.rotation.x = time * 0.1; - points.rotation.y = time * 0.2; - - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/webgl_buffergeometry_indexed.ts b/examples-testing/examples/webgl_buffergeometry_indexed.ts deleted file mode 100644 index a2f9f3795..000000000 --- a/examples-testing/examples/webgl_buffergeometry_indexed.ts +++ /dev/null @@ -1,137 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let camera, scene, renderer, stats; - -let mesh; - -init(); - -function init() { - // - - camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 1, 3500); - camera.position.z = 64; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x050505); - - // - - const light = new THREE.HemisphereLight(); - light.intensity = 3; - scene.add(light); - - // - - const geometry = new THREE.BufferGeometry(); - - const indices = []; - - const vertices = []; - const normals = []; - const colors = []; - - const size = 20; - const segments = 10; - - const halfSize = size / 2; - const segmentSize = size / segments; - - const _color = new THREE.Color(); - - // generate vertices, normals and color data for a simple grid geometry - - for (let i = 0; i <= segments; i++) { - const y = i * segmentSize - halfSize; - - for (let j = 0; j <= segments; j++) { - const x = j * segmentSize - halfSize; - - vertices.push(x, -y, 0); - normals.push(0, 0, 1); - - const r = x / size + 0.5; - const g = y / size + 0.5; - - _color.setRGB(r, g, 1, THREE.SRGBColorSpace); - - colors.push(_color.r, _color.g, _color.b); - } - } - - // generate indices (data for element array buffer) - - for (let i = 0; i < segments; i++) { - for (let j = 0; j < segments; j++) { - const a = i * (segments + 1) + (j + 1); - const b = i * (segments + 1) + j; - const c = (i + 1) * (segments + 1) + j; - const d = (i + 1) * (segments + 1) + (j + 1); - - // generate two faces (triangles) per iteration - - indices.push(a, b, d); // face one - indices.push(b, c, d); // face two - } - } - - // - - geometry.setIndex(indices); - geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); - geometry.setAttribute('normal', new THREE.Float32BufferAttribute(normals, 3)); - geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); - - const material = new THREE.MeshPhongMaterial({ - side: THREE.DoubleSide, - vertexColors: true, - }); - - mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // - - stats = new Stats(); - document.body.appendChild(stats.dom); - - // - - const gui = new GUI(); - gui.add(material, 'wireframe'); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - const time = Date.now() * 0.001; - - mesh.rotation.x = time * 0.25; - mesh.rotation.y = time * 0.5; - - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/webgl_buffergeometry_instancing.ts b/examples-testing/examples/webgl_buffergeometry_instancing.ts deleted file mode 100644 index b27f500f0..000000000 --- a/examples-testing/examples/webgl_buffergeometry_instancing.ts +++ /dev/null @@ -1,138 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let container, stats; - -let camera, scene, renderer; - -init(); - -function init() { - container = document.getElementById('container'); - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10); - camera.position.z = 2; - - scene = new THREE.Scene(); - - // geometry - - const vector = new THREE.Vector4(); - - const instances = 50000; - - const positions = []; - const offsets = []; - const colors = []; - const orientationsStart = []; - const orientationsEnd = []; - - positions.push(0.025, -0.025, 0); - positions.push(-0.025, 0.025, 0); - positions.push(0, 0, 0.025); - - // instanced attributes - - for (let i = 0; i < instances; i++) { - // offsets - - offsets.push(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5); - - // colors - - colors.push(Math.random(), Math.random(), Math.random(), Math.random()); - - // orientation start - - vector.set(Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1); - vector.normalize(); - - orientationsStart.push(vector.x, vector.y, vector.z, vector.w); - - // orientation end - - vector.set(Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1); - vector.normalize(); - - orientationsEnd.push(vector.x, vector.y, vector.z, vector.w); - } - - const geometry = new THREE.InstancedBufferGeometry(); - geometry.instanceCount = instances; // set so its initialized for dat.GUI, will be set in first draw otherwise - - geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)); - - geometry.setAttribute('offset', new THREE.InstancedBufferAttribute(new Float32Array(offsets), 3)); - geometry.setAttribute('color', new THREE.InstancedBufferAttribute(new Float32Array(colors), 4)); - geometry.setAttribute( - 'orientationStart', - new THREE.InstancedBufferAttribute(new Float32Array(orientationsStart), 4), - ); - geometry.setAttribute('orientationEnd', new THREE.InstancedBufferAttribute(new Float32Array(orientationsEnd), 4)); - - // material - - const material = new THREE.RawShaderMaterial({ - uniforms: { - time: { value: 1.0 }, - sineTime: { value: 1.0 }, - }, - vertexShader: document.getElementById('vertexShader').textContent, - fragmentShader: document.getElementById('fragmentShader').textContent, - side: THREE.DoubleSide, - forceSinglePass: true, - transparent: true, - }); - - // - - const mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - // - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - // - - const gui = new GUI({ width: 350 }); - gui.add(geometry, 'instanceCount', 0, instances); - - // - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - const time = performance.now(); - - const object = scene.children[0]; - - object.rotation.y = time * 0.0005; - object.material.uniforms['time'].value = time * 0.005; - object.material.uniforms['sineTime'].value = Math.sin(object.material.uniforms['time'].value * 0.05); - - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/webgl_buffergeometry_instancing_billboards.ts b/examples-testing/examples/webgl_buffergeometry_instancing_billboards.ts deleted file mode 100644 index 2158dff39..000000000 --- a/examples-testing/examples/webgl_buffergeometry_instancing_billboards.ts +++ /dev/null @@ -1,86 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -let container, stats; - -let camera, scene, renderer; -let geometry, material, mesh; - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 5000); - camera.position.z = 1400; - - scene = new THREE.Scene(); - - const circleGeometry = new THREE.CircleGeometry(1, 6); - - geometry = new THREE.InstancedBufferGeometry(); - geometry.index = circleGeometry.index; - geometry.attributes = circleGeometry.attributes; - - const particleCount = 75000; - - const translateArray = new Float32Array(particleCount * 3); - - for (let i = 0, i3 = 0, l = particleCount; i < l; i++, i3 += 3) { - translateArray[i3 + 0] = Math.random() * 2 - 1; - translateArray[i3 + 1] = Math.random() * 2 - 1; - translateArray[i3 + 2] = Math.random() * 2 - 1; - } - - geometry.setAttribute('translate', new THREE.InstancedBufferAttribute(translateArray, 3)); - - material = new THREE.RawShaderMaterial({ - uniforms: { - map: { value: new THREE.TextureLoader().load('textures/sprites/circle.png') }, - time: { value: 0.0 }, - }, - vertexShader: document.getElementById('vshader').textContent, - fragmentShader: document.getElementById('fshader').textContent, - depthTest: true, - depthWrite: true, - }); - - mesh = new THREE.Mesh(geometry, material); - mesh.scale.set(500, 500, 500); - scene.add(mesh); - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - stats = new Stats(); - container.appendChild(stats.dom); - - window.addEventListener('resize', onWindowResize); - - return true; -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - const time = performance.now() * 0.0005; - - material.uniforms['time'].value = time; - - mesh.rotation.x = time * 0.2; - mesh.rotation.y = time * 0.4; - - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/webgl_buffergeometry_instancing_interleaved.ts b/examples-testing/examples/webgl_buffergeometry_instancing_interleaved.ts deleted file mode 100644 index bef2c264d..000000000 --- a/examples-testing/examples/webgl_buffergeometry_instancing_interleaved.ts +++ /dev/null @@ -1,152 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -let container, stats; -let camera, scene, renderer, mesh; - -const instances = 5000; -let lastTime = 0; - -const moveQ = new THREE.Quaternion(0.5, 0.5, 0.5, 0.0).normalize(); -const tmpQ = new THREE.Quaternion(); -const tmpM = new THREE.Matrix4(); -const currentM = new THREE.Matrix4(); - -init(); - -function init() { - container = document.getElementById('container'); - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1000); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x101010); - - // geometry - - const geometry = new THREE.InstancedBufferGeometry(); - - // per mesh data x,y,z,w,u,v,s,t for 4-element alignment - // only use x,y,z and u,v; but x, y, z, nx, ny, nz, u, v would be a good layout - const vertexBuffer = new THREE.InterleavedBuffer( - new Float32Array([ - // Front - -1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, -1, -1, 1, 0, 0, 1, 0, 0, 1, -1, 1, 0, 1, 1, 0, 0, - // Back - 1, 1, -1, 0, 1, 0, 0, 0, -1, 1, -1, 0, 0, 0, 0, 0, 1, -1, -1, 0, 1, 1, 0, 0, -1, -1, -1, 0, 0, 1, 0, 0, - // Left - -1, 1, -1, 0, 1, 1, 0, 0, -1, 1, 1, 0, 1, 0, 0, 0, -1, -1, -1, 0, 0, 1, 0, 0, -1, -1, 1, 0, 0, 0, 0, 0, - // Right - 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, -1, 0, 1, 1, 0, 0, 1, -1, 1, 0, 0, 0, 0, 0, 1, -1, -1, 0, 0, 1, 0, 0, - // Top - -1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, -1, 1, -1, 0, 0, 1, 0, 0, 1, 1, -1, 0, 1, 1, 0, 0, - // Bottom - 1, -1, 1, 0, 1, 0, 0, 0, -1, -1, 1, 0, 0, 0, 0, 0, 1, -1, -1, 0, 1, 1, 0, 0, -1, -1, -1, 0, 0, 1, 0, 0, - ]), - 8, - ); - - // Use vertexBuffer, starting at offset 0, 3 items in position attribute - const positions = new THREE.InterleavedBufferAttribute(vertexBuffer, 3, 0); - geometry.setAttribute('position', positions); - // Use vertexBuffer, starting at offset 4, 2 items in uv attribute - const uvs = new THREE.InterleavedBufferAttribute(vertexBuffer, 2, 4); - geometry.setAttribute('uv', uvs); - - const indices = new Uint16Array([ - 0, 2, 1, 2, 3, 1, 4, 6, 5, 6, 7, 5, 8, 10, 9, 10, 11, 9, 12, 14, 13, 14, 15, 13, 16, 17, 18, 18, 17, 19, 20, 21, - 22, 22, 21, 23, - ]); - - geometry.setIndex(new THREE.BufferAttribute(indices, 1)); - - // material - - const material = new THREE.MeshBasicMaterial(); - material.map = new THREE.TextureLoader().load('textures/crate.gif'); - material.map.colorSpace = THREE.SRGBColorSpace; - material.map.flipY = false; - - // per instance data - - const matrix = new THREE.Matrix4(); - const offset = new THREE.Vector3(); - const orientation = new THREE.Quaternion(); - const scale = new THREE.Vector3(1, 1, 1); - let x, y, z, w; - - mesh = new THREE.InstancedMesh(geometry, material, instances); - - for (let i = 0; i < instances; i++) { - // offsets - - x = Math.random() * 100 - 50; - y = Math.random() * 100 - 50; - z = Math.random() * 100 - 50; - - offset.set(x, y, z).normalize(); - offset.multiplyScalar(5); // move out at least 5 units from center in current direction - offset.set(x + offset.x, y + offset.y, z + offset.z); - - // orientations - - x = Math.random() * 2 - 1; - y = Math.random() * 2 - 1; - z = Math.random() * 2 - 1; - w = Math.random() * 2 - 1; - - orientation.set(x, y, z, w).normalize(); - - matrix.compose(offset, orientation, scale); - - mesh.setMatrixAt(i, matrix); - } - - scene.add(mesh); - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - stats = new Stats(); - container.appendChild(stats.dom); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - const time = performance.now(); - - mesh.rotation.y = time * 0.00005; - - const delta = (time - lastTime) / 5000; - tmpQ.set(moveQ.x * delta, moveQ.y * delta, moveQ.z * delta, 1).normalize(); - tmpM.makeRotationFromQuaternion(tmpQ); - - for (let i = 0, il = instances; i < il; i++) { - mesh.getMatrixAt(i, currentM); - currentM.multiply(tmpM); - mesh.setMatrixAt(i, currentM); - } - - mesh.instanceMatrix.needsUpdate = true; - mesh.computeBoundingSphere(); - - lastTime = time; - - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/webgl_buffergeometry_lines.ts b/examples-testing/examples/webgl_buffergeometry_lines.ts deleted file mode 100644 index d039e0112..000000000 --- a/examples-testing/examples/webgl_buffergeometry_lines.ts +++ /dev/null @@ -1,121 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -let container, stats, timer; - -let camera, scene, renderer; - -let line; - -const segments = 10000; -const r = 800; -let t = 0; - -init(); - -function init() { - container = document.getElementById('container'); - - // - - camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 1, 4000); - camera.position.z = 2750; - - scene = new THREE.Scene(); - - timer = new THREE.Timer(); - timer.connect(document); - - const geometry = new THREE.BufferGeometry(); - const material = new THREE.LineBasicMaterial({ vertexColors: true }); - - const positions = []; - const colors = []; - - for (let i = 0; i < segments; i++) { - const x = Math.random() * r - r / 2; - const y = Math.random() * r - r / 2; - const z = Math.random() * r - r / 2; - - // positions - - positions.push(x, y, z); - - // colors - - colors.push(x / r + 0.5); - colors.push(y / r + 0.5); - colors.push(z / r + 0.5); - } - - geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)); - geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); - generateMorphTargets(geometry); - - geometry.computeBoundingSphere(); - - line = new THREE.Line(geometry, material); - scene.add(line); - - // - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - - container.appendChild(renderer.domElement); - - // - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - timer.update(); - - const delta = timer.getDelta(); - const time = timer.getElapsed(); - - line.rotation.x = time * 0.25; - line.rotation.y = time * 0.5; - - t += delta * 0.5; - line.morphTargetInfluences[0] = Math.abs(Math.sin(t)); - - renderer.render(scene, camera); - - stats.update(); -} - -function generateMorphTargets(geometry) { - const data = []; - - for (let i = 0; i < segments; i++) { - const x = Math.random() * r - r / 2; - const y = Math.random() * r - r / 2; - const z = Math.random() * r - r / 2; - - data.push(x, y, z); - } - - const morphTarget = new THREE.Float32BufferAttribute(data, 3); - morphTarget.name = 'target1'; - - geometry.morphAttributes.position = [morphTarget]; -} diff --git a/examples-testing/examples/webgl_buffergeometry_lines_indexed.ts b/examples-testing/examples/webgl_buffergeometry_lines_indexed.ts deleted file mode 100644 index 58296087e..000000000 --- a/examples-testing/examples/webgl_buffergeometry_lines_indexed.ts +++ /dev/null @@ -1,179 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -let container, stats; - -let camera, scene, renderer; - -let parent_node; - -init(); - -function init() { - container = document.getElementById('container'); - - camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 1, 10000); - camera.position.z = 9000; - - scene = new THREE.Scene(); - - const geometry = new THREE.BufferGeometry(); - const material = new THREE.LineBasicMaterial({ vertexColors: true }); - - const indices = []; - const positions = []; - const colors = []; - - let next_positions_index = 0; - - // - - const iteration_count = 4; - const rangle = (60 * Math.PI) / 180.0; - - function add_vertex(v) { - positions.push(v.x, v.y, v.z); - colors.push(Math.random() * 0.5 + 0.5, Math.random() * 0.5 + 0.5, 1); - - return next_positions_index++; - } - - // simple Koch curve - - function snowflake_iteration(p0, p4, depth) { - if (--depth < 0) { - const i = next_positions_index - 1; // p0 already there - add_vertex(p4); - indices.push(i, i + 1); - - return; - } - - const v = p4.clone().sub(p0); - const v_tier = v.clone().multiplyScalar(1 / 3); - const p1 = p0.clone().add(v_tier); - - const angle = Math.atan2(v.y, v.x) + rangle; - const length = v_tier.length(); - const p2 = p1.clone(); - p2.x += Math.cos(angle) * length; - p2.y += Math.sin(angle) * length; - - const p3 = p0.clone().add(v_tier).add(v_tier); - - snowflake_iteration(p0, p1, depth); - snowflake_iteration(p1, p2, depth); - snowflake_iteration(p2, p3, depth); - snowflake_iteration(p3, p4, depth); - } - - function snowflake(points, loop, x_offset) { - for (let iteration = 0; iteration != iteration_count; iteration++) { - add_vertex(points[0]); - - for (let p_index = 0, p_count = points.length - 1; p_index != p_count; p_index++) { - snowflake_iteration(points[p_index], points[p_index + 1], iteration); - } - - if (loop) snowflake_iteration(points[points.length - 1], points[0], iteration); - - // translate input curve for next iteration - - for (let p_index = 0, p_count = points.length; p_index != p_count; p_index++) { - points[p_index].x += x_offset; - } - } - } - - let y = 0; - - snowflake([new THREE.Vector3(0, y, 0), new THREE.Vector3(500, y, 0)], false, 600); - - y += 600; - snowflake( - [new THREE.Vector3(0, y, 0), new THREE.Vector3(250, y + 400, 0), new THREE.Vector3(500, y, 0)], - true, - 600, - ); - - y += 600; - snowflake( - [ - new THREE.Vector3(0, y, 0), - new THREE.Vector3(500, y, 0), - new THREE.Vector3(500, y + 500, 0), - new THREE.Vector3(0, y + 500, 0), - ], - true, - 600, - ); - - y += 1000; - snowflake( - [ - new THREE.Vector3(250, y, 0), - new THREE.Vector3(500, y, 0), - new THREE.Vector3(250, y, 0), - new THREE.Vector3(250, y + 250, 0), - new THREE.Vector3(250, y, 0), - new THREE.Vector3(0, y, 0), - new THREE.Vector3(250, y, 0), - new THREE.Vector3(250, y - 250, 0), - new THREE.Vector3(250, y, 0), - ], - false, - 600, - ); - - // - - geometry.setIndex(indices); - geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)); - geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); - geometry.computeBoundingSphere(); - - const lineSegments = new THREE.LineSegments(geometry, material); - lineSegments.position.x -= 1200; - lineSegments.position.y -= 1200; - - parent_node = new THREE.Object3D(); - parent_node.add(lineSegments); - - scene.add(parent_node); - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - - container.appendChild(renderer.domElement); - - // - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - const time = Date.now() * 0.001; - - parent_node.rotation.z = time * 0.5; - - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/webgl_buffergeometry_points.ts b/examples-testing/examples/webgl_buffergeometry_points.ts deleted file mode 100644 index 4547d9d08..000000000 --- a/examples-testing/examples/webgl_buffergeometry_points.ts +++ /dev/null @@ -1,109 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -let container, stats; - -let camera, scene, renderer; - -let points; - -init(); -animate(); - -function init() { - container = document.getElementById('container'); - - // - - camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 5, 3500); - camera.position.z = 2750; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x050505); - scene.fog = new THREE.Fog(0x050505, 2000, 3500); - - // - - const particles = 500000; - - const geometry = new THREE.BufferGeometry(); - - const positions = []; - const colors = []; - - const color = new THREE.Color(); - - const n = 1000, - n2 = n / 2; // particles spread in the cube - - for (let i = 0; i < particles; i++) { - // positions - - const x = Math.random() * n - n2; - const y = Math.random() * n - n2; - const z = Math.random() * n - n2; - - positions.push(x, y, z); - - // colors - - const vx = x / n + 0.5; - const vy = y / n + 0.5; - const vz = z / n + 0.5; - - color.setRGB(vx, vy, vz, THREE.SRGBColorSpace); - - colors.push(color.r, color.g, color.b); - } - - geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)); - geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); - - geometry.computeBoundingSphere(); - - // - - const material = new THREE.PointsMaterial({ size: 15, vertexColors: true }); - - points = new THREE.Points(geometry, material); - scene.add(points); - - // - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - - container.appendChild(renderer.domElement); - - // - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - const time = Date.now() * 0.001; - - points.rotation.x = time * 0.25; - points.rotation.y = time * 0.5; - - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/webgl_buffergeometry_points_interleaved.ts b/examples-testing/examples/webgl_buffergeometry_points_interleaved.ts deleted file mode 100644 index 93eed992e..000000000 --- a/examples-testing/examples/webgl_buffergeometry_points_interleaved.ts +++ /dev/null @@ -1,122 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -let container, stats; - -let camera, scene, renderer; - -let points; - -init(); - -function init() { - container = document.getElementById('container'); - - camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 5, 3500); - camera.position.z = 2750; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x050505); - scene.fog = new THREE.Fog(0x050505, 2000, 3500); - - // - - const particles = 500000; - - const geometry = new THREE.BufferGeometry(); - - // create a generic buffer of binary data (a single particle has 16 bytes of data) - - const arrayBuffer = new ArrayBuffer(particles * 16); - - // the following typed arrays share the same buffer - - const interleavedFloat32Buffer = new Float32Array(arrayBuffer); - const interleavedUint8Buffer = new Uint8Array(arrayBuffer); - - // - - const color = new THREE.Color(); - - const n = 1000, - n2 = n / 2; // particles spread in the cube - - for (let i = 0; i < interleavedFloat32Buffer.length; i += 4) { - // position (first 12 bytes) - - const x = Math.random() * n - n2; - const y = Math.random() * n - n2; - const z = Math.random() * n - n2; - - interleavedFloat32Buffer[i + 0] = x; - interleavedFloat32Buffer[i + 1] = y; - interleavedFloat32Buffer[i + 2] = z; - - // color (last 4 bytes) - - const vx = x / n + 0.5; - const vy = y / n + 0.5; - const vz = z / n + 0.5; - - color.setRGB(vx, vy, vz, THREE.SRGBColorSpace); - - const j = (i + 3) * 4; - - interleavedUint8Buffer[j + 0] = color.r * 255; - interleavedUint8Buffer[j + 1] = color.g * 255; - interleavedUint8Buffer[j + 2] = color.b * 255; - interleavedUint8Buffer[j + 3] = 0; // not needed - } - - const interleavedBuffer32 = new THREE.InterleavedBuffer(interleavedFloat32Buffer, 4); - const interleavedBuffer8 = new THREE.InterleavedBuffer(interleavedUint8Buffer, 16); - - geometry.setAttribute('position', new THREE.InterleavedBufferAttribute(interleavedBuffer32, 3, 0, false)); - geometry.setAttribute('color', new THREE.InterleavedBufferAttribute(interleavedBuffer8, 3, 12, true)); - - // - - const material = new THREE.PointsMaterial({ size: 15, vertexColors: true }); - - points = new THREE.Points(geometry, material); - scene.add(points); - - // - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - - container.appendChild(renderer.domElement); - - // - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - const time = Date.now() * 0.001; - - points.rotation.x = time * 0.25; - points.rotation.y = time * 0.5; - - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/webgl_buffergeometry_rawshader.ts b/examples-testing/examples/webgl_buffergeometry_rawshader.ts deleted file mode 100644 index 5bc113dc3..000000000 --- a/examples-testing/examples/webgl_buffergeometry_rawshader.ts +++ /dev/null @@ -1,97 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -let container, stats; - -let camera, scene, renderer; - -init(); - -function init() { - container = document.getElementById('container'); - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10); - camera.position.z = 2; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x101010); - - // geometry - // nr of triangles with 3 vertices per triangle - const vertexCount = 200 * 3; - - const geometry = new THREE.BufferGeometry(); - - const positions = []; - const colors = []; - - for (let i = 0; i < vertexCount; i++) { - // adding x,y,z - positions.push(Math.random() - 0.5); - positions.push(Math.random() - 0.5); - positions.push(Math.random() - 0.5); - - // adding r,g,b,a - colors.push(Math.random() * 255); - colors.push(Math.random() * 255); - colors.push(Math.random() * 255); - colors.push(Math.random() * 255); - } - - const positionAttribute = new THREE.Float32BufferAttribute(positions, 3); - const colorAttribute = new THREE.Uint8BufferAttribute(colors, 4); - - colorAttribute.normalized = true; // this will map the buffer values to 0.0f - +1.0f in the shader - - geometry.setAttribute('position', positionAttribute); - geometry.setAttribute('color', colorAttribute); - - // material - - const material = new THREE.RawShaderMaterial({ - uniforms: { - time: { value: 1.0 }, - }, - vertexShader: document.getElementById('vertexShader').textContent, - fragmentShader: document.getElementById('fragmentShader').textContent, - side: THREE.DoubleSide, - transparent: true, - }); - - const mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - stats = new Stats(); - container.appendChild(stats.dom); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - const time = performance.now(); - - const object = scene.children[0]; - - object.rotation.y = time * 0.0005; - object.material.uniforms.time.value = time * 0.005; - - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/webgl_buffergeometry_selective_draw.ts b/examples-testing/examples/webgl_buffergeometry_selective_draw.ts deleted file mode 100644 index d07176c51..000000000 --- a/examples-testing/examples/webgl_buffergeometry_selective_draw.ts +++ /dev/null @@ -1,150 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -let camera, scene, renderer, stats; -let geometry, mesh; -const numLat = 100; -const numLng = 200; -let numLinesCulled = 0; - -init(); - -function init() { - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.01, 10); - camera.position.z = 3.5; - - stats = new Stats(); - document.body.appendChild(stats.dom); - - window.addEventListener('resize', onWindowResize); - - addLines(1.0); - - const hideLinesButton = document.getElementById('hideLines'); - hideLinesButton.addEventListener('click', hideLines); - - const showAllLinesButton = document.getElementById('showAllLines'); - showAllLinesButton.addEventListener('click', showAllLines); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); -} - -function addLines(radius) { - geometry = new THREE.BufferGeometry(); - const linePositions = new Float32Array(numLat * numLng * 3 * 2); - const lineColors = new Float32Array(numLat * numLng * 3 * 2); - const visible = new Float32Array(numLat * numLng * 2); - - for (let i = 0; i < numLat; ++i) { - for (let j = 0; j < numLng; ++j) { - const lat = (Math.random() * Math.PI) / 50.0 + (i / numLat) * Math.PI; - const lng = (Math.random() * Math.PI) / 50.0 + (j / numLng) * 2 * Math.PI; - - const index = i * numLng + j; - - linePositions[index * 6 + 0] = 0; - linePositions[index * 6 + 1] = 0; - linePositions[index * 6 + 2] = 0; - linePositions[index * 6 + 3] = radius * Math.sin(lat) * Math.cos(lng); - linePositions[index * 6 + 4] = radius * Math.cos(lat); - linePositions[index * 6 + 5] = radius * Math.sin(lat) * Math.sin(lng); - - const color = new THREE.Color(0xffffff); - - color.setHSL(lat / Math.PI, 1.0, 0.2); - lineColors[index * 6 + 0] = color.r; - lineColors[index * 6 + 1] = color.g; - lineColors[index * 6 + 2] = color.b; - - color.setHSL(lat / Math.PI, 1.0, 0.7); - lineColors[index * 6 + 3] = color.r; - lineColors[index * 6 + 4] = color.g; - lineColors[index * 6 + 5] = color.b; - - // non-0 is visible - visible[index * 2 + 0] = 1.0; - visible[index * 2 + 1] = 1.0; - } - } - - geometry.setAttribute('position', new THREE.BufferAttribute(linePositions, 3)); - geometry.setAttribute('vertColor', new THREE.BufferAttribute(lineColors, 3)); - geometry.setAttribute('visible', new THREE.BufferAttribute(visible, 1)); - - geometry.computeBoundingSphere(); - - const shaderMaterial = new THREE.ShaderMaterial({ - vertexShader: document.getElementById('vertexshader').textContent, - fragmentShader: document.getElementById('fragmentshader').textContent, - }); - - mesh = new THREE.LineSegments(geometry, shaderMaterial); - scene.add(mesh); - - updateCount(); -} - -function updateCount() { - const str = - '1 draw call, ' + - numLat * numLng + - ' lines, ' + - numLinesCulled + - ' culled (author)'; - document.getElementById('title').innerHTML = str.replace(/\B(?=(\d{3})+(?!\d))/g, ','); -} - -function hideLines() { - for (let i = 0; i < geometry.attributes.visible.array.length; i += 2) { - if (Math.random() > 0.75) { - if (geometry.attributes.visible.array[i + 0]) { - ++numLinesCulled; - } - - geometry.attributes.visible.array[i + 0] = 0; - geometry.attributes.visible.array[i + 1] = 0; - } - } - - geometry.attributes.visible.needsUpdate = true; - - updateCount(); -} - -function showAllLines() { - numLinesCulled = 0; - - for (let i = 0; i < geometry.attributes.visible.array.length; i += 2) { - geometry.attributes.visible.array[i + 0] = 1; - geometry.attributes.visible.array[i + 1] = 1; - } - - geometry.attributes.visible.needsUpdate = true; - - updateCount(); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - const time = Date.now() * 0.001; - - mesh.rotation.x = time * 0.25; - mesh.rotation.y = time * 0.5; - - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/webgl_buffergeometry_uint.ts b/examples-testing/examples/webgl_buffergeometry_uint.ts deleted file mode 100644 index 0b8df6ec7..000000000 --- a/examples-testing/examples/webgl_buffergeometry_uint.ts +++ /dev/null @@ -1,177 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -let container, stats; - -let camera, scene, renderer; - -let mesh; - -init(); - -function init() { - container = document.getElementById('container'); - - // - - camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 1, 3500); - camera.position.z = 2750; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x050505); - scene.fog = new THREE.Fog(0x050505, 2000, 3500); - - // - - scene.add(new THREE.AmbientLight(0xcccccc)); - - const light1 = new THREE.DirectionalLight(0xffffff, 1.5); - light1.position.set(1, 1, 1); - scene.add(light1); - - const light2 = new THREE.DirectionalLight(0xffffff, 4.5); - light2.position.set(0, -1, 0); - scene.add(light2); - - // - - const triangles = 500000; - - const geometry = new THREE.BufferGeometry(); - - const positions = []; - const normals = []; - const colors = []; - - const color = new THREE.Color(); - - const n = 800, - n2 = n / 2; // triangles spread in the cube - const d = 12, - d2 = d / 2; // individual triangle size - - const pA = new THREE.Vector3(); - const pB = new THREE.Vector3(); - const pC = new THREE.Vector3(); - - const cb = new THREE.Vector3(); - const ab = new THREE.Vector3(); - - for (let i = 0; i < triangles; i++) { - // positions - - const x = Math.random() * n - n2; - const y = Math.random() * n - n2; - const z = Math.random() * n - n2; - - const ax = x + Math.random() * d - d2; - const ay = y + Math.random() * d - d2; - const az = z + Math.random() * d - d2; - - const bx = x + Math.random() * d - d2; - const by = y + Math.random() * d - d2; - const bz = z + Math.random() * d - d2; - - const cx = x + Math.random() * d - d2; - const cy = y + Math.random() * d - d2; - const cz = z + Math.random() * d - d2; - - positions.push(ax, ay, az); - positions.push(bx, by, bz); - positions.push(cx, cy, cz); - - // flat face normals - - pA.set(ax, ay, az); - pB.set(bx, by, bz); - pC.set(cx, cy, cz); - - cb.subVectors(pC, pB); - ab.subVectors(pA, pB); - cb.cross(ab); - - cb.normalize(); - - const nx = cb.x; - const ny = cb.y; - const nz = cb.z; - - normals.push(nx * 32767, ny * 32767, nz * 32767); - normals.push(nx * 32767, ny * 32767, nz * 32767); - normals.push(nx * 32767, ny * 32767, nz * 32767); - - // colors - - const vx = x / n + 0.5; - const vy = y / n + 0.5; - const vz = z / n + 0.5; - - color.setRGB(vx, vy, vz); - - colors.push(color.r * 255, color.g * 255, color.b * 255); - colors.push(color.r * 255, color.g * 255, color.b * 255); - colors.push(color.r * 255, color.g * 255, color.b * 255); - } - - const positionAttribute = new THREE.Float32BufferAttribute(positions, 3); - const normalAttribute = new THREE.Int16BufferAttribute(normals, 3); - const colorAttribute = new THREE.Uint8BufferAttribute(colors, 3); - - normalAttribute.normalized = true; // this will map the buffer values to 0.0f - +1.0f in the shader - colorAttribute.normalized = true; - - geometry.setAttribute('position', positionAttribute); - geometry.setAttribute('normal', normalAttribute); - geometry.setAttribute('color', colorAttribute); - - geometry.computeBoundingSphere(); - - const material = new THREE.MeshPhongMaterial({ - color: 0xd5d5d5, - specular: 0xffffff, - shininess: 250, - side: THREE.DoubleSide, - vertexColors: true, - }); - - mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - // - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - const time = Date.now() * 0.001; - - mesh.rotation.x = time * 0.25; - mesh.rotation.y = time * 0.5; - - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/webgl_camera.ts b/examples-testing/examples/webgl_camera.ts deleted file mode 100644 index f3d663603..000000000 --- a/examples-testing/examples/webgl_camera.ts +++ /dev/null @@ -1,218 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -let SCREEN_WIDTH = window.innerWidth; -let SCREEN_HEIGHT = window.innerHeight; -let aspect = SCREEN_WIDTH / SCREEN_HEIGHT; - -let container, stats; -let camera, scene, renderer, mesh; -let cameraRig, activeCamera, activeHelper; -let cameraPerspective, cameraOrtho; -let cameraPerspectiveHelper, cameraOrthoHelper; -const frustumSize = 600; - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - scene = new THREE.Scene(); - - // - - camera = new THREE.PerspectiveCamera(50, 0.5 * aspect, 1, 10000); - camera.position.z = 2500; - - cameraPerspective = new THREE.PerspectiveCamera(50, 0.5 * aspect, 150, 1000); - - cameraPerspectiveHelper = new THREE.CameraHelper(cameraPerspective); - scene.add(cameraPerspectiveHelper); - - // - cameraOrtho = new THREE.OrthographicCamera( - (0.5 * frustumSize * aspect) / -2, - (0.5 * frustumSize * aspect) / 2, - frustumSize / 2, - frustumSize / -2, - 150, - 1000, - ); - - cameraOrthoHelper = new THREE.CameraHelper(cameraOrtho); - scene.add(cameraOrthoHelper); - - // - - activeCamera = cameraPerspective; - activeHelper = cameraPerspectiveHelper; - - // counteract different front orientation of cameras vs rig - - cameraOrtho.rotation.y = Math.PI; - cameraPerspective.rotation.y = Math.PI; - - cameraRig = new THREE.Group(); - - cameraRig.add(cameraPerspective); - cameraRig.add(cameraOrtho); - - scene.add(cameraRig); - - // - - mesh = new THREE.Mesh( - new THREE.SphereGeometry(100, 16, 8), - new THREE.MeshBasicMaterial({ color: 0xffffff, wireframe: true }), - ); - scene.add(mesh); - - const mesh2 = new THREE.Mesh( - new THREE.SphereGeometry(50, 16, 8), - new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true }), - ); - mesh2.position.y = 150; - mesh.add(mesh2); - - const mesh3 = new THREE.Mesh( - new THREE.SphereGeometry(5, 16, 8), - new THREE.MeshBasicMaterial({ color: 0x0000ff, wireframe: true }), - ); - mesh3.position.z = 150; - cameraRig.add(mesh3); - - // - - const geometry = new THREE.BufferGeometry(); - const vertices = []; - - for (let i = 0; i < 10000; i++) { - vertices.push(THREE.MathUtils.randFloatSpread(2000)); // x - vertices.push(THREE.MathUtils.randFloatSpread(2000)); // y - vertices.push(THREE.MathUtils.randFloatSpread(2000)); // z - } - - geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); - - const particles = new THREE.Points(geometry, new THREE.PointsMaterial({ color: 0x888888 })); - scene.add(particles); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - renderer.setScissorTest(true); - - // - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); - document.addEventListener('keydown', onKeyDown); -} - -// - -function onKeyDown(event) { - switch (event.keyCode) { - case 79 /*O*/: - activeCamera = cameraOrtho; - activeHelper = cameraOrthoHelper; - - break; - - case 80 /*P*/: - activeCamera = cameraPerspective; - activeHelper = cameraPerspectiveHelper; - - break; - } -} - -// - -function onWindowResize() { - SCREEN_WIDTH = window.innerWidth; - SCREEN_HEIGHT = window.innerHeight; - aspect = SCREEN_WIDTH / SCREEN_HEIGHT; - - renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); - - camera.aspect = 0.5 * aspect; - camera.updateProjectionMatrix(); - - cameraPerspective.aspect = 0.5 * aspect; - cameraPerspective.updateProjectionMatrix(); - - cameraOrtho.left = (-0.5 * frustumSize * aspect) / 2; - cameraOrtho.right = (0.5 * frustumSize * aspect) / 2; - cameraOrtho.top = frustumSize / 2; - cameraOrtho.bottom = -frustumSize / 2; - cameraOrtho.updateProjectionMatrix(); -} - -// - -function animate() { - render(); - stats.update(); -} - -function render() { - const r = Date.now() * 0.0005; - - mesh.position.x = 700 * Math.cos(r); - mesh.position.z = 700 * Math.sin(r); - mesh.position.y = 700 * Math.sin(r); - - mesh.children[0].position.x = 70 * Math.cos(2 * r); - mesh.children[0].position.z = 70 * Math.sin(r); - - if (activeCamera === cameraPerspective) { - cameraPerspective.fov = 35 + 30 * Math.sin(0.5 * r); - cameraPerspective.far = mesh.position.length(); - cameraPerspective.updateProjectionMatrix(); - - cameraPerspectiveHelper.update(); - cameraPerspectiveHelper.visible = true; - - cameraOrthoHelper.visible = false; - } else { - cameraOrtho.far = mesh.position.length(); - cameraOrtho.updateProjectionMatrix(); - - cameraOrthoHelper.update(); - cameraOrthoHelper.visible = true; - - cameraPerspectiveHelper.visible = false; - } - - cameraRig.lookAt(mesh.position); - - // - - activeHelper.visible = false; - - renderer.setClearColor(0x000000, 1); - renderer.setScissor(0, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT); - renderer.setViewport(0, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT); - renderer.render(scene, activeCamera); - - // - - activeHelper.visible = true; - - renderer.setClearColor(0x111111, 1); - renderer.setScissor(SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT); - renderer.setViewport(SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT); - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_camera_array.ts b/examples-testing/examples/webgl_camera_array.ts deleted file mode 100644 index 8b10e27cb..000000000 --- a/examples-testing/examples/webgl_camera_array.ts +++ /dev/null @@ -1,104 +0,0 @@ -import * as THREE from 'three'; - -let camera, scene, renderer; -let mesh; -const AMOUNT = 6; - -init(); - -function init() { - const ASPECT_RATIO = window.innerWidth / window.innerHeight; - - const WIDTH = (window.innerWidth / AMOUNT) * window.devicePixelRatio; - const HEIGHT = (window.innerHeight / AMOUNT) * window.devicePixelRatio; - - const cameras = []; - - for (let y = 0; y < AMOUNT; y++) { - for (let x = 0; x < AMOUNT; x++) { - const subcamera = new THREE.PerspectiveCamera(40, ASPECT_RATIO, 0.1, 10); - subcamera.viewport = new THREE.Vector4( - Math.floor(x * WIDTH), - Math.floor(y * HEIGHT), - Math.ceil(WIDTH), - Math.ceil(HEIGHT), - ); - subcamera.position.x = x / AMOUNT - 0.5; - subcamera.position.y = 0.5 - y / AMOUNT; - subcamera.position.z = 1.5; - subcamera.position.multiplyScalar(2); - subcamera.lookAt(0, 0, 0); - subcamera.updateMatrixWorld(); - cameras.push(subcamera); - } - } - - camera = new THREE.ArrayCamera(cameras); - camera.position.z = 3; - - scene = new THREE.Scene(); - - scene.add(new THREE.AmbientLight(0x999999)); - - const light = new THREE.DirectionalLight(0xffffff, 3); - light.position.set(0.5, 0.5, 1); - light.castShadow = true; - light.shadow.camera.zoom = 4; // tighter shadow map - scene.add(light); - - const geometryBackground = new THREE.PlaneGeometry(100, 100); - const materialBackground = new THREE.MeshPhongMaterial({ color: 0x000066 }); - - const background = new THREE.Mesh(geometryBackground, materialBackground); - background.receiveShadow = true; - background.position.set(0, 0, -1); - scene.add(background); - - const geometryCylinder = new THREE.CylinderGeometry(0.5, 0.5, 1, 32); - const materialCylinder = new THREE.MeshPhongMaterial({ color: 0xff0000 }); - - mesh = new THREE.Mesh(geometryCylinder, materialCylinder); - mesh.castShadow = true; - mesh.receiveShadow = true; - scene.add(mesh); - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.shadowMap.enabled = true; - document.body.appendChild(renderer.domElement); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - const ASPECT_RATIO = window.innerWidth / window.innerHeight; - const WIDTH = (window.innerWidth / AMOUNT) * window.devicePixelRatio; - const HEIGHT = (window.innerHeight / AMOUNT) * window.devicePixelRatio; - - camera.aspect = ASPECT_RATIO; - camera.updateProjectionMatrix(); - - for (let y = 0; y < AMOUNT; y++) { - for (let x = 0; x < AMOUNT; x++) { - const subcamera = camera.cameras[AMOUNT * y + x]; - - subcamera.viewport.set(Math.floor(x * WIDTH), Math.floor(y * HEIGHT), Math.ceil(WIDTH), Math.ceil(HEIGHT)); - - subcamera.aspect = ASPECT_RATIO; - subcamera.updateProjectionMatrix(); - } - } - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - mesh.rotation.x += 0.005; - mesh.rotation.z += 0.01; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_camera_logarithmicdepthbuffer.ts b/examples-testing/examples/webgl_camera_logarithmicdepthbuffer.ts deleted file mode 100644 index f1d440004..000000000 --- a/examples-testing/examples/webgl_camera_logarithmicdepthbuffer.ts +++ /dev/null @@ -1,248 +0,0 @@ -import * as THREE from 'three'; - -import { FontLoader } from 'three/addons/loaders/FontLoader.js'; -import { TextGeometry } from 'three/addons/geometries/TextGeometry.js'; - -import Stats from 'three/addons/libs/stats.module.js'; - -// 1 micrometer to 100 billion light years in one scene, with 1 unit = 1 meter? preposterous! and yet... -const NEAR = 1e-6, - FAR = 1e27; -let SCREEN_WIDTH = window.innerWidth; -let SCREEN_HEIGHT = window.innerHeight; -let screensplit = 0.25, - screensplit_right = 0; -const mouse = [0.5, 0.5]; -let zoompos = -100, - minzoomspeed = 0.015; -let zoomspeed = minzoomspeed; - -let container, border, stats; -const objects = {}; - -// Generate a number of text labels, from 1µm in size up to 100,000,000 light years -// Try to use some descriptive real-world examples of objects at each scale - -const labeldata = [ - { size: 0.01, scale: 0.0001, label: 'microscopic (1µm)' }, // FIXME - triangulating text fails at this size, so we scale instead - { size: 0.01, scale: 0.1, label: 'minuscule (1mm)' }, - { size: 0.01, scale: 1.0, label: 'tiny (1cm)' }, - { size: 1, scale: 1.0, label: 'child-sized (1m)' }, - { size: 10, scale: 1.0, label: 'tree-sized (10m)' }, - { size: 100, scale: 1.0, label: 'building-sized (100m)' }, - { size: 1000, scale: 1.0, label: 'medium (1km)' }, - { size: 10000, scale: 1.0, label: 'city-sized (10km)' }, - { size: 3400000, scale: 1.0, label: 'moon-sized (3,400 Km)' }, - { size: 12000000, scale: 1.0, label: 'planet-sized (12,000 km)' }, - { size: 1400000000, scale: 1.0, label: 'sun-sized (1,400,000 km)' }, - { size: 7.47e12, scale: 1.0, label: 'solar system-sized (50Au)' }, - { size: 9.4605284e15, scale: 1.0, label: 'gargantuan (1 light year)' }, - { size: 3.08567758e16, scale: 1.0, label: 'ludicrous (1 parsec)' }, - { size: 1e19, scale: 1.0, label: 'mind boggling (1000 light years)' }, -]; - -init(); - -function init() { - container = document.getElementById('container'); - - const loader = new FontLoader(); - loader.load('fonts/helvetiker_regular.typeface.json', function (font) { - const scene = initScene(font); - - // Initialize two copies of the same scene, one with normal z-buffer and one with logarithmic z-buffer - objects.normal = initView(scene, 'normal', false); - objects.logzbuf = initView(scene, 'logzbuf', true); - - animate(); - }); - - stats = new Stats(); - container.appendChild(stats.dom); - - // Resize border allows the user to easily compare effects of logarithmic depth buffer over the whole scene - border = document.getElementById('renderer_border'); - border.addEventListener('pointerdown', onBorderPointerDown); - - window.addEventListener('mousemove', onMouseMove); - window.addEventListener('resize', onWindowResize); - window.addEventListener('wheel', onMouseWheel); -} - -function initView(scene, name, logDepthBuf) { - const framecontainer = document.getElementById('container_' + name); - - const camera = new THREE.PerspectiveCamera(50, (screensplit * SCREEN_WIDTH) / SCREEN_HEIGHT, NEAR, FAR); - scene.add(camera); - - const renderer = new THREE.WebGLRenderer({ antialias: true, logarithmicDepthBuffer: logDepthBuf }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(SCREEN_WIDTH / 2, SCREEN_HEIGHT); - renderer.domElement.style.position = 'relative'; - renderer.domElement.id = 'renderer_' + name; - framecontainer.appendChild(renderer.domElement); - - return { container: framecontainer, renderer: renderer, scene: scene, camera: camera }; -} - -function initScene(font) { - const scene = new THREE.Scene(); - - scene.add(new THREE.AmbientLight(0x777777)); - - const light = new THREE.DirectionalLight(0xffffff, 3); - light.position.set(100, 100, 100); - scene.add(light); - - const materialargs = { - color: 0xffffff, - specular: 0x050505, - shininess: 50, - emissive: 0x000000, - }; - - const geometry = new THREE.SphereGeometry(0.5, 24, 12); - - for (let i = 0; i < labeldata.length; i++) { - const scale = labeldata[i].scale || 1; - - const labelgeo = new TextGeometry(labeldata[i].label, { - font: font, - size: labeldata[i].size, - depth: labeldata[i].size / 2, - }); - - labelgeo.computeBoundingSphere(); - - // center text - labelgeo.translate(-labelgeo.boundingSphere.radius, 0, 0); - - materialargs.color = new THREE.Color().setHSL(Math.random(), 0.5, 0.5); - - const material = new THREE.MeshPhongMaterial(materialargs); - - const group = new THREE.Group(); - group.position.z = -labeldata[i].size * scale; - scene.add(group); - - const textmesh = new THREE.Mesh(labelgeo, material); - textmesh.scale.set(scale, scale, scale); - textmesh.position.z = -labeldata[i].size * scale; - textmesh.position.y = (labeldata[i].size / 4) * scale; - group.add(textmesh); - - const dotmesh = new THREE.Mesh(geometry, material); - dotmesh.position.y = (-labeldata[i].size / 4) * scale; - dotmesh.scale.multiplyScalar(labeldata[i].size * scale); - group.add(dotmesh); - } - - return scene; -} - -function updateRendererSizes() { - // Recalculate size for both renderers when screen size or split location changes - - SCREEN_WIDTH = window.innerWidth; - SCREEN_HEIGHT = window.innerHeight; - - screensplit_right = 1 - screensplit; - - objects.normal.renderer.setSize(screensplit * SCREEN_WIDTH, SCREEN_HEIGHT); - objects.normal.camera.aspect = (screensplit * SCREEN_WIDTH) / SCREEN_HEIGHT; - objects.normal.camera.updateProjectionMatrix(); - objects.normal.camera.setViewOffset(SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, SCREEN_WIDTH * screensplit, SCREEN_HEIGHT); - objects.normal.container.style.width = screensplit * 100 + '%'; - - objects.logzbuf.renderer.setSize(screensplit_right * SCREEN_WIDTH, SCREEN_HEIGHT); - objects.logzbuf.camera.aspect = (screensplit_right * SCREEN_WIDTH) / SCREEN_HEIGHT; - objects.logzbuf.camera.updateProjectionMatrix(); - objects.logzbuf.camera.setViewOffset( - SCREEN_WIDTH, - SCREEN_HEIGHT, - SCREEN_WIDTH * screensplit, - 0, - SCREEN_WIDTH * screensplit_right, - SCREEN_HEIGHT, - ); - objects.logzbuf.container.style.width = screensplit_right * 100 + '%'; - - border.style.left = screensplit * 100 + '%'; -} - -function animate() { - requestAnimationFrame(animate); - render(); -} - -function render() { - // Put some limits on zooming - const minzoom = labeldata[0].size * labeldata[0].scale * 1; - const maxzoom = labeldata[labeldata.length - 1].size * labeldata[labeldata.length - 1].scale * 100; - let damping = Math.abs(zoomspeed) > minzoomspeed ? 0.95 : 1.0; - - // Zoom out faster the further out you go - const zoom = THREE.MathUtils.clamp(Math.pow(Math.E, zoompos), minzoom, maxzoom); - zoompos = Math.log(zoom); - - // Slow down quickly at the zoom limits - if ((zoom == minzoom && zoomspeed < 0) || (zoom == maxzoom && zoomspeed > 0)) { - damping = 0.85; - } - - zoompos += zoomspeed; - zoomspeed *= damping; - - objects.normal.camera.position.x = Math.sin(0.5 * Math.PI * (mouse[0] - 0.5)) * zoom; - objects.normal.camera.position.y = Math.sin(0.25 * Math.PI * (mouse[1] - 0.5)) * zoom; - objects.normal.camera.position.z = Math.cos(0.5 * Math.PI * (mouse[0] - 0.5)) * zoom; - objects.normal.camera.lookAt(objects.normal.scene.position); - - // Clone camera settings across both scenes - objects.logzbuf.camera.position.copy(objects.normal.camera.position); - objects.logzbuf.camera.quaternion.copy(objects.normal.camera.quaternion); - - // Update renderer sizes if the split has changed - if (screensplit_right != 1 - screensplit) { - updateRendererSizes(); - } - - objects.normal.renderer.render(objects.normal.scene, objects.normal.camera); - objects.logzbuf.renderer.render(objects.logzbuf.scene, objects.logzbuf.camera); - - stats.update(); -} - -function onWindowResize() { - updateRendererSizes(); -} - -function onBorderPointerDown() { - // activate draggable window resizing bar - window.addEventListener('pointermove', onBorderPointerMove); - window.addEventListener('pointerup', onBorderPointerUp); -} - -function onBorderPointerMove(ev) { - screensplit = Math.max(0, Math.min(1, ev.clientX / window.innerWidth)); -} - -function onBorderPointerUp() { - window.removeEventListener('pointermove', onBorderPointerMove); - window.removeEventListener('pointerup', onBorderPointerUp); -} - -function onMouseMove(ev) { - mouse[0] = ev.clientX / window.innerWidth; - mouse[1] = ev.clientY / window.innerHeight; -} - -function onMouseWheel(ev) { - const amount = ev.deltaY; - if (amount === 0) return; - const dir = amount / Math.abs(amount); - zoomspeed = dir / 10; - - // Slow down default zoom speed after user starts zooming, to give them more control - minzoomspeed = 0.001; -} diff --git a/examples-testing/examples/webgl_clipculldistance.ts b/examples-testing/examples/webgl_clipculldistance.ts deleted file mode 100644 index 42c1ff17f..000000000 --- a/examples-testing/examples/webgl_clipculldistance.ts +++ /dev/null @@ -1,113 +0,0 @@ -import * as THREE from 'three'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import Stats from 'three/addons/libs/stats.module.js'; - -let camera, controls, timer, scene, renderer, stats; - -let material; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10); - camera.position.z = 2; - - scene = new THREE.Scene(); - - timer = new THREE.Timer(); - timer.connect(document); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - if (renderer.extensions.has('WEBGL_clip_cull_distance') === false) { - document.getElementById('notSupported').style.display = ''; - return; - } - - const ext = renderer.getContext().getExtension('WEBGL_clip_cull_distance'); - const gl = renderer.getContext(); - - gl.enable(ext.CLIP_DISTANCE0_WEBGL); - - // geometry - - const vertexCount = 200 * 3; - - const geometry = new THREE.BufferGeometry(); - - const positions = []; - const colors = []; - - for (let i = 0; i < vertexCount; i++) { - // adding x,y,z - positions.push(Math.random() - 0.5); - positions.push(Math.random() - 0.5); - positions.push(Math.random() - 0.5); - - // adding r,g,b,a - colors.push(Math.random() * 255); - colors.push(Math.random() * 255); - colors.push(Math.random() * 255); - colors.push(Math.random() * 255); - } - - const positionAttribute = new THREE.Float32BufferAttribute(positions, 3); - const colorAttribute = new THREE.Uint8BufferAttribute(colors, 4); - colorAttribute.normalized = true; - - geometry.setAttribute('position', positionAttribute); - geometry.setAttribute('color', colorAttribute); - - // material - - material = new THREE.ShaderMaterial({ - uniforms: { - time: { value: 1.0 }, - }, - vertexShader: document.getElementById('vertexShader').textContent, - fragmentShader: document.getElementById('fragmentShader').textContent, - side: THREE.DoubleSide, - transparent: true, - vertexColors: true, - }); - - material.extensions.clipCullDistance = true; - - const mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - // - - controls = new OrbitControls(camera, renderer.domElement); - - // - - stats = new Stats(); - document.body.appendChild(stats.dom); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - controls.update(); - stats.update(); - - material.uniforms.time.value = timer.getElapsed(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_clipping.ts b/examples-testing/examples/webgl_clipping.ts deleted file mode 100644 index cde10c7d1..000000000 --- a/examples-testing/examples/webgl_clipping.ts +++ /dev/null @@ -1,195 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let camera, scene, renderer, startTime, object, stats; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(36, window.innerWidth / window.innerHeight, 0.25, 16); - - camera.position.set(0, 1.3, 3); - - scene = new THREE.Scene(); - - // Lights - - scene.add(new THREE.AmbientLight(0xcccccc)); - - const spotLight = new THREE.SpotLight(0xffffff, 60); - spotLight.angle = Math.PI / 5; - spotLight.penumbra = 0.2; - spotLight.position.set(2, 3, 3); - spotLight.castShadow = true; - spotLight.shadow.camera.near = 3; - spotLight.shadow.camera.far = 10; - spotLight.shadow.mapSize.width = 1024; - spotLight.shadow.mapSize.height = 1024; - scene.add(spotLight); - - const dirLight = new THREE.DirectionalLight(0x55505a, 3); - dirLight.position.set(0, 3, 0); - dirLight.castShadow = true; - dirLight.shadow.camera.near = 1; - dirLight.shadow.camera.far = 10; - - dirLight.shadow.camera.right = 1; - dirLight.shadow.camera.left = -1; - dirLight.shadow.camera.top = 1; - dirLight.shadow.camera.bottom = -1; - - dirLight.shadow.mapSize.width = 1024; - dirLight.shadow.mapSize.height = 1024; - scene.add(dirLight); - - // ***** Clipping planes: ***** - - const localPlane = new THREE.Plane(new THREE.Vector3(0, -1, 0), 0.8); - const globalPlane = new THREE.Plane(new THREE.Vector3(-1, 0, 0), 0.1); - - // Geometry - - const material = new THREE.MeshPhongMaterial({ - color: 0x80ee10, - shininess: 100, - side: THREE.DoubleSide, - - // ***** Clipping setup (material): ***** - clippingPlanes: [localPlane], - clipShadows: true, - - alphaToCoverage: true, - }); - - const geometry = new THREE.TorusKnotGeometry(0.4, 0.08, 95, 20); - - object = new THREE.Mesh(geometry, material); - object.castShadow = true; - scene.add(object); - - const ground = new THREE.Mesh( - new THREE.PlaneGeometry(9, 9, 1, 1), - new THREE.MeshPhongMaterial({ color: 0xa0adaf, shininess: 150 }), - ); - - ground.rotation.x = -Math.PI / 2; // rotates X/Y to X/Z - ground.receiveShadow = true; - scene.add(ground); - - // Renderer - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.shadowMap.enabled = true; - document.body.appendChild(renderer.domElement); - - window.addEventListener('resize', onWindowResize); - - // ***** Clipping setup (renderer): ***** - const globalPlanes = [globalPlane], - Empty = Object.freeze([]); - renderer.clippingPlanes = Empty; // GUI sets it to globalPlanes - renderer.localClippingEnabled = true; - - // Stats - - stats = new Stats(); - document.body.appendChild(stats.dom); - - // Controls - - const controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, 1, 0); - controls.update(); - - // GUI - - const gui = new GUI(), - props = { - alphaToCoverage: true, - }, - folderLocal = gui.addFolder('Local Clipping'), - propsLocal = { - get Enabled() { - return renderer.localClippingEnabled; - }, - set Enabled(v) { - renderer.localClippingEnabled = v; - }, - - get Shadows() { - return material.clipShadows; - }, - set Shadows(v) { - material.clipShadows = v; - }, - - get Plane() { - return localPlane.constant; - }, - set Plane(v) { - localPlane.constant = v; - }, - }, - folderGlobal = gui.addFolder('Global Clipping'), - propsGlobal = { - get Enabled() { - return renderer.clippingPlanes !== Empty; - }, - set Enabled(v) { - renderer.clippingPlanes = v ? globalPlanes : Empty; - }, - - get Plane() { - return globalPlane.constant; - }, - set Plane(v) { - globalPlane.constant = v; - }, - }; - - gui.add(props, 'alphaToCoverage').onChange(function (value) { - ground.material.alphaToCoverage = value; - ground.material.needsUpdate = true; - - material.alphaToCoverage = value; - material.needsUpdate = true; - }); - folderLocal.add(propsLocal, 'Enabled'); - folderLocal.add(propsLocal, 'Shadows'); - folderLocal.add(propsLocal, 'Plane', 0.3, 1.25); - - folderGlobal.add(propsGlobal, 'Enabled'); - folderGlobal.add(propsGlobal, 'Plane', -0.4, 3); - - // Start - - startTime = Date.now(); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - const currentTime = Date.now(); - const time = (currentTime - startTime) / 1000; - - object.position.y = 0.8; - object.rotation.x = time * 0.5; - object.rotation.y = time * 0.2; - object.scale.setScalar(Math.cos(time) * 0.125 + 0.875); - - stats.begin(); - renderer.render(scene, camera); - stats.end(); -} diff --git a/examples-testing/examples/webgl_clipping_advanced.ts b/examples-testing/examples/webgl_clipping_advanced.ts deleted file mode 100644 index f65f00043..000000000 --- a/examples-testing/examples/webgl_clipping_advanced.ts +++ /dev/null @@ -1,357 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -function planesFromMesh(vertices, indices) { - // creates a clipping volume from a convex triangular mesh - // specified by the arrays 'vertices' and 'indices' - - const n = indices.length / 3, - result = new Array(n); - - for (let i = 0, j = 0; i < n; ++i, j += 3) { - const a = vertices[indices[j]], - b = vertices[indices[j + 1]], - c = vertices[indices[j + 2]]; - - result[i] = new THREE.Plane().setFromCoplanarPoints(a, b, c); - } - - return result; -} - -function createPlanes(n) { - // creates an array of n uninitialized plane objects - - const result = new Array(n); - - for (let i = 0; i !== n; ++i) result[i] = new THREE.Plane(); - - return result; -} - -function assignTransformedPlanes(planesOut, planesIn, matrix) { - // sets an array of existing planes to transformed 'planesIn' - - for (let i = 0, n = planesIn.length; i !== n; ++i) planesOut[i].copy(planesIn[i]).applyMatrix4(matrix); -} - -function cylindricalPlanes(n, innerRadius) { - const result = createPlanes(n); - - for (let i = 0; i !== n; ++i) { - const plane = result[i], - angle = (i * Math.PI * 2) / n; - - plane.normal.set(Math.cos(angle), 0, Math.sin(angle)); - - plane.constant = innerRadius; - } - - return result; -} - -const planeToMatrix = (function () { - // creates a matrix that aligns X/Y to a given plane - - // temporaries: - const xAxis = new THREE.Vector3(), - yAxis = new THREE.Vector3(), - trans = new THREE.Vector3(); - - return function planeToMatrix(plane) { - const zAxis = plane.normal, - matrix = new THREE.Matrix4(); - - // Hughes & Moeller '99 - // "Building an Orthonormal Basis from a Unit Vector." - - if (Math.abs(zAxis.x) > Math.abs(zAxis.z)) { - yAxis.set(-zAxis.y, zAxis.x, 0); - } else { - yAxis.set(0, -zAxis.z, zAxis.y); - } - - xAxis.crossVectors(yAxis.normalize(), zAxis); - - plane.coplanarPoint(trans); - return matrix.set( - xAxis.x, - yAxis.x, - zAxis.x, - trans.x, - xAxis.y, - yAxis.y, - zAxis.y, - trans.y, - xAxis.z, - yAxis.z, - zAxis.z, - trans.z, - 0, - 0, - 0, - 1, - ); - }; -})(); - -// A regular tetrahedron for the clipping volume: - -const Vertices = [ - new THREE.Vector3(+1, 0, +Math.SQRT1_2), - new THREE.Vector3(-1, 0, +Math.SQRT1_2), - new THREE.Vector3(0, +1, -Math.SQRT1_2), - new THREE.Vector3(0, -1, -Math.SQRT1_2), - ], - Indices = [0, 1, 2, 0, 2, 3, 0, 3, 1, 1, 3, 2], - Planes = planesFromMesh(Vertices, Indices), - PlaneMatrices = Planes.map(planeToMatrix), - GlobalClippingPlanes = cylindricalPlanes(5, 2.5), - Empty = Object.freeze([]); - -let camera, scene, renderer, startTime, stats, object, clipMaterial, volumeVisualization, globalClippingPlanes; - -function init() { - camera = new THREE.PerspectiveCamera(36, window.innerWidth / window.innerHeight, 0.25, 16); - - camera.position.set(0, 1.5, 3); - - scene = new THREE.Scene(); - - // Lights - - scene.add(new THREE.AmbientLight(0xffffff)); - - const spotLight = new THREE.SpotLight(0xffffff, 60); - spotLight.angle = Math.PI / 5; - spotLight.penumbra = 0.2; - spotLight.position.set(2, 3, 3); - spotLight.castShadow = true; - spotLight.shadow.camera.near = 3; - spotLight.shadow.camera.far = 10; - spotLight.shadow.mapSize.width = 1024; - spotLight.shadow.mapSize.height = 1024; - scene.add(spotLight); - - const dirLight = new THREE.DirectionalLight(0xffffff, 1.5); - dirLight.position.set(0, 2, 0); - dirLight.castShadow = true; - dirLight.shadow.camera.near = 1; - dirLight.shadow.camera.far = 10; - - dirLight.shadow.camera.right = 1; - dirLight.shadow.camera.left = -1; - dirLight.shadow.camera.top = 1; - dirLight.shadow.camera.bottom = -1; - - dirLight.shadow.mapSize.width = 1024; - dirLight.shadow.mapSize.height = 1024; - scene.add(dirLight); - - // Geometry - - clipMaterial = new THREE.MeshPhongMaterial({ - color: 0xee0a10, - shininess: 100, - side: THREE.DoubleSide, - // Clipping setup: - clippingPlanes: createPlanes(Planes.length), - clipShadows: true, - }); - - const count = 5 * 5 * 5; - const geometry = new THREE.BoxGeometry(0.18, 0.18, 0.18); - object = new THREE.InstancedMesh(geometry, clipMaterial, count); - object.castShadow = true; - - let i = 0; - const matrix = new THREE.Matrix4(); - - for (let z = -2; z <= 2; ++z) - for (let y = -2; y <= 2; ++y) - for (let x = -2; x <= 2; ++x) { - matrix.setPosition(x / 5, y / 5, z / 5); - object.setMatrixAt(i++, matrix); - } - - scene.add(object); - - const planeGeometry = new THREE.PlaneGeometry(3, 3, 1, 1), - color = new THREE.Color(); - - volumeVisualization = new THREE.Group(); - volumeVisualization.visible = false; - - for (let i = 0, n = Planes.length; i !== n; ++i) { - const material = new THREE.MeshBasicMaterial({ - color: color.setHSL(i / n, 0.5, 0.5).getHex(), - side: THREE.DoubleSide, - - opacity: 0.2, - transparent: true, - - // clip to the others to show the volume (wildly - // intersecting transparent planes look bad) - clippingPlanes: clipMaterial.clippingPlanes.filter(function (_, j) { - return j !== i; - }), - - // no need to enable shadow clipping - the plane - // visualization does not cast shadows - }); - - const mesh = new THREE.Mesh(planeGeometry, material); - mesh.matrixAutoUpdate = false; - - volumeVisualization.add(mesh); - } - - scene.add(volumeVisualization); - - const ground = new THREE.Mesh( - planeGeometry, - new THREE.MeshPhongMaterial({ - color: 0xa0adaf, - shininess: 10, - }), - ); - ground.rotation.x = -Math.PI / 2; - ground.scale.multiplyScalar(3); - ground.receiveShadow = true; - scene.add(ground); - - // Renderer - - const container = document.body; - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.shadowMap.enabled = true; - container.appendChild(renderer.domElement); - // Clipping setup: - globalClippingPlanes = createPlanes(GlobalClippingPlanes.length); - renderer.clippingPlanes = Empty; - renderer.localClippingEnabled = true; - - window.addEventListener('resize', onWindowResize); - - // Stats - - stats = new Stats(); - container.appendChild(stats.dom); - - // Controls - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 1; - controls.maxDistance = 8; - controls.target.set(0, 1, 0); - controls.update(); - - // GUI - - const gui = new GUI(), - folder = gui.addFolder('Local Clipping'), - props = { - get Enabled() { - return renderer.localClippingEnabled; - }, - set Enabled(v) { - renderer.localClippingEnabled = v; - if (!v) volumeVisualization.visible = false; - }, - - get Shadows() { - return clipMaterial.clipShadows; - }, - set Shadows(v) { - clipMaterial.clipShadows = v; - }, - - get Visualize() { - return volumeVisualization.visible; - }, - set Visualize(v) { - if (renderer.localClippingEnabled) volumeVisualization.visible = v; - }, - }; - - folder.add(props, 'Enabled'); - folder.add(props, 'Shadows'); - folder.add(props, 'Visualize').listen(); - - gui.addFolder('Global Clipping').add( - { - get Enabled() { - return renderer.clippingPlanes !== Empty; - }, - set Enabled(v) { - renderer.clippingPlanes = v ? globalClippingPlanes : Empty; - }, - }, - 'Enabled', - ); - - // Start - - startTime = Date.now(); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function setObjectWorldMatrix(object, matrix) { - // set the orientation of an object based on a world matrix - - const parent = object.parent; - scene.updateMatrixWorld(); - object.matrix.copy(parent.matrixWorld).invert(); - object.applyMatrix4(matrix); -} - -const transform = new THREE.Matrix4(), - tmpMatrix = new THREE.Matrix4(); - -function animate() { - const currentTime = Date.now(), - time = (currentTime - startTime) / 1000; - - object.position.y = 1; - object.rotation.x = time * 0.5; - object.rotation.y = time * 0.2; - - object.updateMatrix(); - transform.copy(object.matrix); - - const bouncy = Math.cos(time * 0.5) * 0.5 + 0.7; - transform.multiply(tmpMatrix.makeScale(bouncy, bouncy, bouncy)); - - assignTransformedPlanes(clipMaterial.clippingPlanes, Planes, transform); - - const planeMeshes = volumeVisualization.children; - - for (let i = 0, n = planeMeshes.length; i !== n; ++i) { - tmpMatrix.multiplyMatrices(transform, PlaneMatrices[i]); - setObjectWorldMatrix(planeMeshes[i], tmpMatrix); - } - - transform.makeRotationY(time * 0.1); - - assignTransformedPlanes(globalClippingPlanes, GlobalClippingPlanes, transform); - - stats.begin(); - renderer.render(scene, camera); - stats.end(); -} - -init(); diff --git a/examples-testing/examples/webgl_clipping_intersection.ts b/examples-testing/examples/webgl_clipping_intersection.ts deleted file mode 100644 index 5f45e45df..000000000 --- a/examples-testing/examples/webgl_clipping_intersection.ts +++ /dev/null @@ -1,137 +0,0 @@ -import * as THREE from 'three'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let camera, scene, renderer; - -const params = { - clipIntersection: true, - planeConstant: 0, - showHelpers: false, - alphaToCoverage: true, -}; - -const clipPlanes = [ - new THREE.Plane(new THREE.Vector3(1, 0, 0), 0), - new THREE.Plane(new THREE.Vector3(0, -1, 0), 0), - new THREE.Plane(new THREE.Vector3(0, 0, -1), 0), -]; - -init(); -render(); - -function init() { - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.localClippingEnabled = true; - document.body.appendChild(renderer.domElement); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 200); - - camera.position.set(-1.5, 2.5, 3.0); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.addEventListener('change', render); // use only if there is no animation loop - controls.minDistance = 1; - controls.maxDistance = 10; - controls.enablePan = false; - - const light = new THREE.HemisphereLight(0xffffff, 0x080808, 4.5); - light.position.set(-1.25, 1, 1.25); - scene.add(light); - - // - - const group = new THREE.Group(); - - for (let i = 1; i <= 30; i += 2) { - const geometry = new THREE.SphereGeometry(i / 30, 48, 24); - - const material = new THREE.MeshPhongMaterial({ - color: new THREE.Color().setHSL(Math.random(), 0.5, 0.5, THREE.SRGBColorSpace), - side: THREE.DoubleSide, - clippingPlanes: clipPlanes, - clipIntersection: params.clipIntersection, - alphaToCoverage: true, - }); - - group.add(new THREE.Mesh(geometry, material)); - } - - scene.add(group); - - // helpers - - const helpers = new THREE.Group(); - helpers.add(new THREE.PlaneHelper(clipPlanes[0], 2, 0xff0000)); - helpers.add(new THREE.PlaneHelper(clipPlanes[1], 2, 0x00ff00)); - helpers.add(new THREE.PlaneHelper(clipPlanes[2], 2, 0x0000ff)); - helpers.visible = false; - scene.add(helpers); - - // gui - - const gui = new GUI(); - - gui.add(params, 'alphaToCoverage').onChange(function (value) { - group.children.forEach(c => { - c.material.alphaToCoverage = Boolean(value); - c.material.needsUpdate = true; - }); - - render(); - }); - - gui.add(params, 'clipIntersection') - .name('clip intersection') - .onChange(function (value) { - const children = group.children; - - for (let i = 0; i < children.length; i++) { - children[i].material.clipIntersection = value; - } - - render(); - }); - - gui.add(params, 'planeConstant', -1, 1) - .step(0.01) - .name('plane constant') - .onChange(function (value) { - for (let j = 0; j < clipPlanes.length; j++) { - clipPlanes[j].constant = value; - } - - render(); - }); - - gui.add(params, 'showHelpers') - .name('show helpers') - .onChange(function (value) { - helpers.visible = value; - - render(); - }); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - render(); -} - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_clipping_stencil.ts b/examples-testing/examples/webgl_clipping_stencil.ts deleted file mode 100644 index 7cb1f5c52..000000000 --- a/examples-testing/examples/webgl_clipping_stencil.ts +++ /dev/null @@ -1,263 +0,0 @@ -import * as THREE from 'three'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import Stats from 'three/addons/libs/stats.module.js'; - -let camera, scene, renderer, object, stats; -let planes, planeObjects, planeHelpers; -let timer; - -const params = { - animate: true, - planeX: { - constant: 0, - negated: false, - displayHelper: false, - }, - planeY: { - constant: 0, - negated: false, - displayHelper: false, - }, - planeZ: { - constant: 0, - negated: false, - displayHelper: false, - }, -}; - -init(); - -function createPlaneStencilGroup(geometry, plane, renderOrder) { - const group = new THREE.Group(); - const baseMat = new THREE.MeshBasicMaterial(); - baseMat.depthWrite = false; - baseMat.depthTest = false; - baseMat.colorWrite = false; - baseMat.stencilWrite = true; - baseMat.stencilFunc = THREE.AlwaysStencilFunc; - - // back faces - const mat0 = baseMat.clone(); - mat0.side = THREE.BackSide; - mat0.clippingPlanes = [plane]; - mat0.stencilFail = THREE.IncrementWrapStencilOp; - mat0.stencilZFail = THREE.IncrementWrapStencilOp; - mat0.stencilZPass = THREE.IncrementWrapStencilOp; - - const mesh0 = new THREE.Mesh(geometry, mat0); - mesh0.renderOrder = renderOrder; - group.add(mesh0); - - // front faces - const mat1 = baseMat.clone(); - mat1.side = THREE.FrontSide; - mat1.clippingPlanes = [plane]; - mat1.stencilFail = THREE.DecrementWrapStencilOp; - mat1.stencilZFail = THREE.DecrementWrapStencilOp; - mat1.stencilZPass = THREE.DecrementWrapStencilOp; - - const mesh1 = new THREE.Mesh(geometry, mat1); - mesh1.renderOrder = renderOrder; - - group.add(mesh1); - - return group; -} - -function init() { - timer = new THREE.Timer(); - timer.connect(document); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(36, window.innerWidth / window.innerHeight, 1, 100); - camera.position.set(2, 2, 2); - - scene.add(new THREE.AmbientLight(0xffffff, 1.5)); - - const dirLight = new THREE.DirectionalLight(0xffffff, 3); - dirLight.position.set(5, 10, 7.5); - dirLight.castShadow = true; - dirLight.shadow.camera.right = 2; - dirLight.shadow.camera.left = -2; - dirLight.shadow.camera.top = 2; - dirLight.shadow.camera.bottom = -2; - - dirLight.shadow.mapSize.width = 1024; - dirLight.shadow.mapSize.height = 1024; - scene.add(dirLight); - - planes = [ - new THREE.Plane(new THREE.Vector3(-1, 0, 0), 0), - new THREE.Plane(new THREE.Vector3(0, -1, 0), 0), - new THREE.Plane(new THREE.Vector3(0, 0, -1), 0), - ]; - - planeHelpers = planes.map(p => new THREE.PlaneHelper(p, 2, 0xffffff)); - planeHelpers.forEach(ph => { - ph.visible = false; - scene.add(ph); - }); - - const geometry = new THREE.TorusKnotGeometry(0.4, 0.15, 220, 60); - object = new THREE.Group(); - scene.add(object); - - // Set up clip plane rendering - planeObjects = []; - const planeGeom = new THREE.PlaneGeometry(4, 4); - - for (let i = 0; i < 3; i++) { - const poGroup = new THREE.Group(); - const plane = planes[i]; - const stencilGroup = createPlaneStencilGroup(geometry, plane, i + 1); - - // plane is clipped by the other clipping planes - const planeMat = new THREE.MeshStandardMaterial({ - color: 0xe91e63, - metalness: 0.1, - roughness: 0.75, - clippingPlanes: planes.filter(p => p !== plane), - - stencilWrite: true, - stencilRef: 0, - stencilFunc: THREE.NotEqualStencilFunc, - stencilFail: THREE.ReplaceStencilOp, - stencilZFail: THREE.ReplaceStencilOp, - stencilZPass: THREE.ReplaceStencilOp, - }); - const po = new THREE.Mesh(planeGeom, planeMat); - po.onAfterRender = function (renderer) { - renderer.clearStencil(); - }; - - po.renderOrder = i + 1.1; - - object.add(stencilGroup); - poGroup.add(po); - planeObjects.push(po); - scene.add(poGroup); - } - - const material = new THREE.MeshStandardMaterial({ - color: 0xffc107, - metalness: 0.1, - roughness: 0.75, - clippingPlanes: planes, - clipShadows: true, - shadowSide: THREE.DoubleSide, - }); - - // add the color - const clippedColorFront = new THREE.Mesh(geometry, material); - clippedColorFront.castShadow = true; - clippedColorFront.renderOrder = 6; - object.add(clippedColorFront); - - const ground = new THREE.Mesh( - new THREE.PlaneGeometry(9, 9, 1, 1), - new THREE.ShadowMaterial({ color: 0x000000, opacity: 0.25, side: THREE.DoubleSide }), - ); - - ground.rotation.x = -Math.PI / 2; // rotates X/Y to X/Z - ground.position.y = -1; - ground.receiveShadow = true; - scene.add(ground); - - // Renderer - renderer = new THREE.WebGLRenderer({ antialias: true, stencil: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setClearColor(0x263238); - renderer.setAnimationLoop(animate); - renderer.shadowMap.enabled = true; - renderer.localClippingEnabled = true; - document.body.appendChild(renderer.domElement); - - // Stats - stats = new Stats(); - document.body.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); - - // Controls - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 2; - controls.maxDistance = 20; - controls.update(); - - // GUI - const gui = new GUI(); - gui.add(params, 'animate'); - - const planeX = gui.addFolder('planeX'); - planeX.add(params.planeX, 'displayHelper').onChange(v => (planeHelpers[0].visible = v)); - planeX - .add(params.planeX, 'constant') - .min(-1) - .max(1) - .onChange(d => (planes[0].constant = d)); - planeX.add(params.planeX, 'negated').onChange(() => { - planes[0].negate(); - params.planeX.constant = planes[0].constant; - }); - planeX.open(); - - const planeY = gui.addFolder('planeY'); - planeY.add(params.planeY, 'displayHelper').onChange(v => (planeHelpers[1].visible = v)); - planeY - .add(params.planeY, 'constant') - .min(-1) - .max(1) - .onChange(d => (planes[1].constant = d)); - planeY.add(params.planeY, 'negated').onChange(() => { - planes[1].negate(); - params.planeY.constant = planes[1].constant; - }); - planeY.open(); - - const planeZ = gui.addFolder('planeZ'); - planeZ.add(params.planeZ, 'displayHelper').onChange(v => (planeHelpers[2].visible = v)); - planeZ - .add(params.planeZ, 'constant') - .min(-1) - .max(1) - .onChange(d => (planes[2].constant = d)); - planeZ.add(params.planeZ, 'negated').onChange(() => { - planes[2].negate(); - params.planeZ.constant = planes[2].constant; - }); - planeZ.open(); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - const delta = timer.getDelta(); - - if (params.animate) { - object.rotation.x += delta * 0.5; - object.rotation.y += delta * 0.2; - } - - for (let i = 0; i < planeObjects.length; i++) { - const plane = planes[i]; - const po = planeObjects[i]; - plane.coplanarPoint(po.position); - po.lookAt(po.position.x - plane.normal.x, po.position.y - plane.normal.y, po.position.z - plane.normal.z); - } - - stats.begin(); - renderer.render(scene, camera); - stats.end(); -} diff --git a/examples-testing/examples/webgl_custom_attributes.ts b/examples-testing/examples/webgl_custom_attributes.ts deleted file mode 100644 index 0dc897748..000000000 --- a/examples-testing/examples/webgl_custom_attributes.ts +++ /dev/null @@ -1,100 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -let renderer, scene, camera, stats; - -let sphere, uniforms; - -let displacement, noise; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 10000); - camera.position.z = 300; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x050505); - - uniforms = { - amplitude: { value: 1.0 }, - color: { value: new THREE.Color(0xff2200) }, - colorTexture: { value: new THREE.TextureLoader().load('textures/water.jpg') }, - }; - - uniforms['colorTexture'].value.wrapS = uniforms['colorTexture'].value.wrapT = THREE.RepeatWrapping; - - const shaderMaterial = new THREE.ShaderMaterial({ - uniforms: uniforms, - vertexShader: document.getElementById('vertexshader').textContent, - fragmentShader: document.getElementById('fragmentshader').textContent, - }); - - const radius = 50, - segments = 128, - rings = 64; - - const geometry = new THREE.SphereGeometry(radius, segments, rings); - - displacement = new Float32Array(geometry.attributes.position.count); - noise = new Float32Array(geometry.attributes.position.count); - - for (let i = 0; i < displacement.length; i++) { - noise[i] = Math.random() * 5; - } - - geometry.setAttribute('displacement', new THREE.BufferAttribute(displacement, 1)); - - sphere = new THREE.Mesh(geometry, shaderMaterial); - scene.add(sphere); - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - - const container = document.getElementById('container'); - container.appendChild(renderer.domElement); - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - render(); - stats.update(); -} - -function render() { - const time = Date.now() * 0.01; - - sphere.rotation.y = sphere.rotation.z = 0.01 * time; - - uniforms['amplitude'].value = 2.5 * Math.sin(sphere.rotation.y * 0.125); - uniforms['color'].value.offsetHSL(0.0005, 0, 0); - - for (let i = 0; i < displacement.length; i++) { - displacement[i] = Math.sin(0.1 * i + time); - - noise[i] += 0.5 * (0.5 - Math.random()); - noise[i] = THREE.MathUtils.clamp(noise[i], -5, 5); - - displacement[i] += noise[i]; - } - - sphere.geometry.attributes.displacement.needsUpdate = true; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_custom_attributes_lines.ts b/examples-testing/examples/webgl_custom_attributes_lines.ts deleted file mode 100644 index 3e2454e92..000000000 --- a/examples-testing/examples/webgl_custom_attributes_lines.ts +++ /dev/null @@ -1,121 +0,0 @@ -import * as THREE from 'three'; - -import { FontLoader } from 'three/addons/loaders/FontLoader.js'; -import { TextGeometry } from 'three/addons/geometries/TextGeometry.js'; - -import Stats from 'three/addons/libs/stats.module.js'; - -let renderer, scene, camera, stats; - -let line, uniforms; - -const loader = new FontLoader(); -loader.load('fonts/helvetiker_bold.typeface.json', function (font) { - init(font); -}); - -function init(font) { - camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 10000); - camera.position.z = 400; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x050505); - - uniforms = { - amplitude: { value: 5.0 }, - opacity: { value: 0.3 }, - color: { value: new THREE.Color(0xffffff) }, - }; - - const shaderMaterial = new THREE.ShaderMaterial({ - uniforms: uniforms, - vertexShader: document.getElementById('vertexshader').textContent, - fragmentShader: document.getElementById('fragmentshader').textContent, - blending: THREE.AdditiveBlending, - depthTest: false, - transparent: true, - }); - - const geometry = new TextGeometry('three.js', { - font: font, - - size: 50, - depth: 15, - curveSegments: 10, - - bevelThickness: 5, - bevelSize: 1.5, - bevelEnabled: true, - bevelSegments: 10, - }); - - geometry.center(); - - const count = geometry.attributes.position.count; - - const displacement = new THREE.Float32BufferAttribute(count * 3, 3); - geometry.setAttribute('displacement', displacement); - - const customColor = new THREE.Float32BufferAttribute(count * 3, 3); - geometry.setAttribute('customColor', customColor); - - const color = new THREE.Color(0xffffff); - - for (let i = 0, l = customColor.count; i < l; i++) { - color.setHSL(i / l, 0.5, 0.5); - color.toArray(customColor.array, i * customColor.itemSize); - } - - line = new THREE.Line(geometry, shaderMaterial); - line.rotation.x = 0.2; - scene.add(line); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - - const container = document.getElementById('container'); - container.appendChild(renderer.domElement); - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - render(); - stats.update(); -} - -function render() { - const time = Date.now() * 0.001; - - line.rotation.y = 0.25 * time; - - uniforms.amplitude.value = Math.sin(0.5 * time); - uniforms.color.value.offsetHSL(0.0005, 0, 0); - - const attributes = line.geometry.attributes; - const array = attributes.displacement.array; - - for (let i = 0, l = array.length; i < l; i += 3) { - array[i] += 0.3 * (0.5 - Math.random()); - array[i + 1] += 0.3 * (0.5 - Math.random()); - array[i + 2] += 0.3 * (0.5 - Math.random()); - } - - attributes.displacement.needsUpdate = true; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_custom_attributes_points.ts b/examples-testing/examples/webgl_custom_attributes_points.ts deleted file mode 100644 index ae112980a..000000000 --- a/examples-testing/examples/webgl_custom_attributes_points.ts +++ /dev/null @@ -1,117 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -let renderer, scene, camera, stats; - -let sphere; - -const WIDTH = window.innerWidth; -const HEIGHT = window.innerHeight; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(40, WIDTH / HEIGHT, 1, 10000); - camera.position.z = 300; - - scene = new THREE.Scene(); - - const amount = 100000; - const radius = 200; - - const positions = new Float32Array(amount * 3); - const colors = new Float32Array(amount * 3); - const sizes = new Float32Array(amount); - - const vertex = new THREE.Vector3(); - const color = new THREE.Color(0xffffff); - - for (let i = 0; i < amount; i++) { - vertex.x = (Math.random() * 2 - 1) * radius; - vertex.y = (Math.random() * 2 - 1) * radius; - vertex.z = (Math.random() * 2 - 1) * radius; - vertex.toArray(positions, i * 3); - - if (vertex.x < 0) { - color.setHSL(0.5 + 0.1 * (i / amount), 0.7, 0.5); - } else { - color.setHSL(0.0 + 0.1 * (i / amount), 0.9, 0.5); - } - - color.toArray(colors, i * 3); - - sizes[i] = 10; - } - - const geometry = new THREE.BufferGeometry(); - geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); - geometry.setAttribute('customColor', new THREE.BufferAttribute(colors, 3)); - geometry.setAttribute('size', new THREE.BufferAttribute(sizes, 1)); - - // - - const material = new THREE.ShaderMaterial({ - uniforms: { - color: { value: new THREE.Color(0xffffff) }, - pointTexture: { value: new THREE.TextureLoader().load('textures/sprites/spark1.png') }, - }, - vertexShader: document.getElementById('vertexshader').textContent, - fragmentShader: document.getElementById('fragmentshader').textContent, - - blending: THREE.AdditiveBlending, - depthTest: false, - transparent: true, - }); - - // - - sphere = new THREE.Points(geometry, material); - scene.add(sphere); - - // - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(WIDTH, HEIGHT); - renderer.setAnimationLoop(animate); - - const container = document.getElementById('container'); - container.appendChild(renderer.domElement); - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - render(); - stats.update(); -} - -function render() { - const time = Date.now() * 0.005; - - sphere.rotation.z = 0.01 * time; - - const geometry = sphere.geometry; - const attributes = geometry.attributes; - - for (let i = 0; i < attributes.size.array.length; i++) { - attributes.size.array[i] = 14 + 13 * Math.sin(0.1 * i + time); - } - - attributes.size.needsUpdate = true; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_custom_attributes_points2.ts b/examples-testing/examples/webgl_custom_attributes_points2.ts deleted file mode 100644 index edd158fa1..000000000 --- a/examples-testing/examples/webgl_custom_attributes_points2.ts +++ /dev/null @@ -1,193 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js'; - -let renderer, scene, camera, stats; -let sphere, length1; - -const WIDTH = window.innerWidth; -const HEIGHT = window.innerHeight; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(45, WIDTH / HEIGHT, 1, 10000); - camera.position.z = 300; - - scene = new THREE.Scene(); - - const radius = 100, - segments = 68, - rings = 38; - - let sphereGeometry = new THREE.SphereGeometry(radius, segments, rings); - let boxGeometry = new THREE.BoxGeometry(0.8 * radius, 0.8 * radius, 0.8 * radius, 10, 10, 10); - - // if normal and uv attributes are not removed, mergeVertices() can't consolidate identical vertices with different normal/uv data - - sphereGeometry.deleteAttribute('normal'); - sphereGeometry.deleteAttribute('uv'); - - boxGeometry.deleteAttribute('normal'); - boxGeometry.deleteAttribute('uv'); - - sphereGeometry = BufferGeometryUtils.mergeVertices(sphereGeometry); - boxGeometry = BufferGeometryUtils.mergeVertices(boxGeometry); - - const combinedGeometry = BufferGeometryUtils.mergeGeometries([sphereGeometry, boxGeometry]); - const positionAttribute = combinedGeometry.getAttribute('position'); - - const colors = []; - const sizes = []; - - const color = new THREE.Color(); - const vertex = new THREE.Vector3(); - - length1 = sphereGeometry.getAttribute('position').count; - - for (let i = 0, l = positionAttribute.count; i < l; i++) { - vertex.fromBufferAttribute(positionAttribute, i); - - if (i < length1) { - color.setHSL(0.01 + 0.1 * (i / length1), 0.99, (vertex.y + radius) / (4 * radius)); - } else { - color.setHSL(0.6, 0.75, 0.25 + vertex.y / (2 * radius)); - } - - color.toArray(colors, i * 3); - - sizes[i] = i < length1 ? 10 : 40; - } - - const geometry = new THREE.BufferGeometry(); - geometry.setAttribute('position', positionAttribute); - geometry.setAttribute('size', new THREE.Float32BufferAttribute(sizes, 1)); - geometry.setAttribute('ca', new THREE.Float32BufferAttribute(colors, 3)); - - // - - const texture = new THREE.TextureLoader().load('textures/sprites/disc.png'); - texture.wrapS = THREE.RepeatWrapping; - texture.wrapT = THREE.RepeatWrapping; - - const material = new THREE.ShaderMaterial({ - uniforms: { - color: { value: new THREE.Color(0xffffff) }, - pointTexture: { value: texture }, - }, - vertexShader: document.getElementById('vertexshader').textContent, - fragmentShader: document.getElementById('fragmentshader').textContent, - transparent: true, - }); - - // - - sphere = new THREE.Points(geometry, material); - scene.add(sphere); - - // - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(WIDTH, HEIGHT); - renderer.setAnimationLoop(animate); - - const container = document.getElementById('container'); - container.appendChild(renderer.domElement); - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function sortPoints() { - const vector = new THREE.Vector3(); - - // Model View Projection matrix - - const matrix = new THREE.Matrix4(); - matrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse); - matrix.multiply(sphere.matrixWorld); - - // - - const geometry = sphere.geometry; - - let index = geometry.getIndex(); - const positions = geometry.getAttribute('position').array; - const length = positions.length / 3; - - if (index === null) { - const array = new Uint16Array(length); - - for (let i = 0; i < length; i++) { - array[i] = i; - } - - index = new THREE.BufferAttribute(array, 1); - - geometry.setIndex(index); - } - - const sortArray = []; - - for (let i = 0; i < length; i++) { - vector.fromArray(positions, i * 3); - vector.applyMatrix4(matrix); - - sortArray.push([vector.z, i]); - } - - function numericalSort(a, b) { - return b[0] - a[0]; - } - - sortArray.sort(numericalSort); - - const indices = index.array; - - for (let i = 0; i < length; i++) { - indices[i] = sortArray[i][1]; - } - - geometry.index.needsUpdate = true; -} - -function animate() { - render(); - stats.update(); -} - -function render() { - const time = Date.now() * 0.005; - - sphere.rotation.y = 0.02 * time; - sphere.rotation.z = 0.02 * time; - - const geometry = sphere.geometry; - const attributes = geometry.attributes; - - for (let i = 0; i < attributes.size.array.length; i++) { - if (i < length1) { - attributes.size.array[i] = 16 + 12 * Math.sin(0.1 * i + time); - } - } - - attributes.size.needsUpdate = true; - - sortPoints(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_custom_attributes_points3.ts b/examples-testing/examples/webgl_custom_attributes_points3.ts deleted file mode 100644 index 1bca8ccd4..000000000 --- a/examples-testing/examples/webgl_custom_attributes_points3.ts +++ /dev/null @@ -1,200 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js'; - -let renderer, scene, camera, stats; - -let object; - -let vertices1; - -const WIDTH = window.innerWidth; -const HEIGHT = window.innerHeight; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(40, WIDTH / HEIGHT, 1, 1000); - camera.position.z = 500; - - scene = new THREE.Scene(); - - let radius = 100; - const inner = 0.6 * radius; - const vertex = new THREE.Vector3(); - const vertices = []; - - for (let i = 0; i < 100000; i++) { - vertex.x = Math.random() * 2 - 1; - vertex.y = Math.random() * 2 - 1; - vertex.z = Math.random() * 2 - 1; - vertex.multiplyScalar(radius); - - if ( - vertex.x > inner || - vertex.x < -inner || - vertex.y > inner || - vertex.y < -inner || - vertex.z > inner || - vertex.z < -inner - ) - vertices.push(vertex.x, vertex.y, vertex.z); - } - - vertices1 = vertices.length / 3; - - radius = 200; - - let boxGeometry1 = new THREE.BoxGeometry(radius, 0.1 * radius, 0.1 * radius, 50, 5, 5); - - // if normal and uv attributes are not removed, mergeVertices() can't consolidate identical vertices with different normal/uv data - - boxGeometry1.deleteAttribute('normal'); - boxGeometry1.deleteAttribute('uv'); - - boxGeometry1 = BufferGeometryUtils.mergeVertices(boxGeometry1); - - const matrix = new THREE.Matrix4(); - const position = new THREE.Vector3(); - const rotation = new THREE.Euler(); - const quaternion = new THREE.Quaternion(); - const scale = new THREE.Vector3(1, 1, 1); - - function addGeo(geo, x, y, z, ry) { - position.set(x, y, z); - rotation.set(0, ry, 0); - - matrix.compose(position, quaternion.setFromEuler(rotation), scale); - - const positionAttribute = geo.getAttribute('position'); - - for (let i = 0, l = positionAttribute.count; i < l; i++) { - vertex.fromBufferAttribute(positionAttribute, i); - vertex.applyMatrix4(matrix); - vertices.push(vertex.x, vertex.y, vertex.z); - } - } - - // side 1 - - addGeo(boxGeometry1, 0, 110, 110, 0); - addGeo(boxGeometry1, 0, 110, -110, 0); - addGeo(boxGeometry1, 0, -110, 110, 0); - addGeo(boxGeometry1, 0, -110, -110, 0); - - // side 2 - - addGeo(boxGeometry1, 110, 110, 0, Math.PI / 2); - addGeo(boxGeometry1, 110, -110, 0, Math.PI / 2); - addGeo(boxGeometry1, -110, 110, 0, Math.PI / 2); - addGeo(boxGeometry1, -110, -110, 0, Math.PI / 2); - - // corner edges - - let boxGeometry2 = new THREE.BoxGeometry(0.1 * radius, radius * 1.2, 0.1 * radius, 5, 60, 5); - - boxGeometry2.deleteAttribute('normal'); - boxGeometry2.deleteAttribute('uv'); - - boxGeometry2 = BufferGeometryUtils.mergeVertices(boxGeometry2); - - addGeo(boxGeometry2, 110, 0, 110, 0); - addGeo(boxGeometry2, 110, 0, -110, 0); - addGeo(boxGeometry2, -110, 0, 110, 0); - addGeo(boxGeometry2, -110, 0, -110, 0); - - const positionAttribute = new THREE.Float32BufferAttribute(vertices, 3); - - const colors = []; - const sizes = []; - - const color = new THREE.Color(); - - for (let i = 0; i < positionAttribute.count; i++) { - if (i < vertices1) { - color.setHSL(0.5 + 0.2 * (i / vertices1), 1, 0.5); - } else { - color.setHSL(0.1, 1, 0.5); - } - - color.toArray(colors, i * 3); - - sizes[i] = i < vertices1 ? 10 : 40; - } - - const geometry = new THREE.BufferGeometry(); - geometry.setAttribute('position', positionAttribute); - geometry.setAttribute('ca', new THREE.Float32BufferAttribute(colors, 3)); - geometry.setAttribute('size', new THREE.Float32BufferAttribute(sizes, 1)); - - // - - const texture = new THREE.TextureLoader().load('textures/sprites/ball.png'); - texture.wrapS = THREE.RepeatWrapping; - texture.wrapT = THREE.RepeatWrapping; - - const material = new THREE.ShaderMaterial({ - uniforms: { - amplitude: { value: 1.0 }, - color: { value: new THREE.Color(0xffffff) }, - pointTexture: { value: texture }, - }, - vertexShader: document.getElementById('vertexshader').textContent, - fragmentShader: document.getElementById('fragmentshader').textContent, - }); - - // - - object = new THREE.Points(geometry, material); - scene.add(object); - - // - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(WIDTH, HEIGHT); - renderer.setAnimationLoop(animate); - - const container = document.getElementById('container'); - container.appendChild(renderer.domElement); - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - render(); - stats.update(); -} - -function render() { - const time = Date.now() * 0.01; - - object.rotation.y = object.rotation.z = 0.02 * time; - - const geometry = object.geometry; - const attributes = geometry.attributes; - - for (let i = 0; i < attributes.size.array.length; i++) { - if (i < vertices1) { - attributes.size.array[i] = Math.max(0, 26 + 32 * Math.sin(0.1 * i + 0.6 * time)); - } - } - - attributes.size.needsUpdate = true; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_decals.ts b/examples-testing/examples/webgl_decals.ts deleted file mode 100644 index 8f77c30fc..000000000 --- a/examples-testing/examples/webgl_decals.ts +++ /dev/null @@ -1,240 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { DecalGeometry } from 'three/addons/geometries/DecalGeometry.js'; - -const container = document.getElementById('container'); - -let renderer, scene, camera, stats; -let mesh; -let raycaster; -let line; - -const intersection = { - intersects: false, - point: new THREE.Vector3(), - normal: new THREE.Vector3(), -}; -const mouse = new THREE.Vector2(); -const intersects = []; - -const textureLoader = new THREE.TextureLoader(); -const decalDiffuse = textureLoader.load('textures/decal/decal-diffuse.png'); -decalDiffuse.colorSpace = THREE.SRGBColorSpace; -const decalNormal = textureLoader.load('textures/decal/decal-normal.jpg'); - -const decalMaterial = new THREE.MeshPhongMaterial({ - specular: 0x444444, - map: decalDiffuse, - normalMap: decalNormal, - normalScale: new THREE.Vector2(1, 1), - shininess: 30, - transparent: true, - depthTest: true, - depthWrite: false, - polygonOffset: true, - polygonOffsetFactor: -4, - wireframe: false, -}); - -const decals = []; -let mouseHelper; -const position = new THREE.Vector3(); -const orientation = new THREE.Euler(); -const size = new THREE.Vector3(10, 10, 10); - -const params = { - minScale: 10, - maxScale: 20, - rotate: true, - clear: function () { - removeDecals(); - }, -}; - -init(); - -function init() { - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - stats = new Stats(); - container.appendChild(stats.dom); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.z = 120; - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 50; - controls.maxDistance = 200; - - scene.add(new THREE.AmbientLight(0x666666)); - - const dirLight1 = new THREE.DirectionalLight(0xffddcc, 3); - dirLight1.position.set(1, 0.75, 0.5); - scene.add(dirLight1); - - const dirLight2 = new THREE.DirectionalLight(0xccccff, 3); - dirLight2.position.set(-1, 0.75, -0.5); - scene.add(dirLight2); - - const geometry = new THREE.BufferGeometry(); - geometry.setFromPoints([new THREE.Vector3(), new THREE.Vector3()]); - - line = new THREE.Line(geometry, new THREE.LineBasicMaterial()); - scene.add(line); - - loadLeePerrySmith(); - - raycaster = new THREE.Raycaster(); - - mouseHelper = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 10), new THREE.MeshNormalMaterial()); - mouseHelper.visible = false; - scene.add(mouseHelper); - - window.addEventListener('resize', onWindowResize); - - let moved = false; - - controls.addEventListener('change', function () { - moved = true; - }); - - window.addEventListener('pointerdown', function () { - moved = false; - }); - - window.addEventListener('pointerup', function (event) { - if (moved === false) { - checkIntersection(event.clientX, event.clientY); - - if (intersection.intersects) shoot(); - } - }); - - window.addEventListener('pointermove', onPointerMove); - - function onPointerMove(event) { - if (event.isPrimary) { - checkIntersection(event.clientX, event.clientY); - } - } - - function checkIntersection(x, y) { - if (mesh === undefined) return; - - mouse.x = (x / window.innerWidth) * 2 - 1; - mouse.y = -(y / window.innerHeight) * 2 + 1; - - raycaster.setFromCamera(mouse, camera); - raycaster.intersectObject(mesh, false, intersects); - - if (intersects.length > 0) { - const p = intersects[0].point; - mouseHelper.position.copy(p); - intersection.point.copy(p); - - const normalMatrix = new THREE.Matrix3().getNormalMatrix(mesh.matrixWorld); - - const n = intersects[0].face.normal.clone(); - n.applyNormalMatrix(normalMatrix); - n.multiplyScalar(10); - n.add(intersects[0].point); - - intersection.normal.copy(intersects[0].face.normal); - mouseHelper.lookAt(n); - - const positions = line.geometry.attributes.position; - positions.setXYZ(0, p.x, p.y, p.z); - positions.setXYZ(1, n.x, n.y, n.z); - positions.needsUpdate = true; - - intersection.intersects = true; - - intersects.length = 0; - } else { - intersection.intersects = false; - } - } - - const gui = new GUI(); - - gui.add(params, 'minScale', 1, 30); - gui.add(params, 'maxScale', 1, 30); - gui.add(params, 'rotate'); - gui.add(params, 'clear'); - gui.open(); -} - -function loadLeePerrySmith() { - const map = textureLoader.load('models/gltf/LeePerrySmith/Map-COL.jpg'); - map.colorSpace = THREE.SRGBColorSpace; - const specularMap = textureLoader.load('models/gltf/LeePerrySmith/Map-SPEC.jpg'); - const normalMap = textureLoader.load('models/gltf/LeePerrySmith/Infinite-Level_02_Tangent_SmoothUV.jpg'); - - const loader = new GLTFLoader(); - - loader.load('models/gltf/LeePerrySmith/LeePerrySmith.glb', function (gltf) { - mesh = gltf.scene.children[0]; - mesh.material = new THREE.MeshPhongMaterial({ - specular: 0x111111, - map: map, - specularMap: specularMap, - normalMap: normalMap, - shininess: 25, - }); - - scene.add(mesh); - mesh.scale.multiplyScalar(10); - }); -} - -function shoot() { - position.copy(intersection.point); - orientation.copy(mouseHelper.rotation); - - if (params.rotate) orientation.z = Math.random() * 2 * Math.PI; - - const scale = params.minScale + Math.random() * (params.maxScale - params.minScale); - size.set(scale, scale, scale); - - const material = decalMaterial.clone(); - material.color.setHex(Math.random() * 0xffffff); - - const m = new THREE.Mesh(new DecalGeometry(mesh, position, orientation, size), material); - m.renderOrder = decals.length; // give decals a fixed render order - - decals.push(m); - - mesh.attach(m); -} - -function removeDecals() { - decals.forEach(function (d) { - mesh.remove(d); - }); - - decals.length = 0; -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/webgl_effects_anaglyph.ts b/examples-testing/examples/webgl_effects_anaglyph.ts deleted file mode 100644 index 7e67c9173..000000000 --- a/examples-testing/examples/webgl_effects_anaglyph.ts +++ /dev/null @@ -1,120 +0,0 @@ -import * as THREE from 'three'; - -import { AnaglyphEffect } from 'three/addons/effects/AnaglyphEffect.js'; - -let container, camera, scene, renderer, effect; - -const spheres = []; - -let mouseX = 0; -let mouseY = 0; - -let windowHalfX = window.innerWidth / 2; -let windowHalfY = window.innerHeight / 2; - -document.addEventListener('mousemove', onDocumentMouseMove); - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.01, 100); - camera.position.z = 3; - - const path = 'textures/cube/pisa/'; - const format = '.png'; - const urls = [ - path + 'px' + format, - path + 'nx' + format, - path + 'py' + format, - path + 'ny' + format, - path + 'pz' + format, - path + 'nz' + format, - ]; - - const textureCube = new THREE.CubeTextureLoader().load(urls); - - scene = new THREE.Scene(); - scene.background = textureCube; - - const geometry = new THREE.SphereGeometry(0.1, 32, 16); - const material = new THREE.MeshBasicMaterial({ color: 0xffffff, envMap: textureCube }); - - for (let i = 0; i < 500; i++) { - const mesh = new THREE.Mesh(geometry, material); - - mesh.position.x = Math.random() * 10 - 5; - mesh.position.y = Math.random() * 10 - 5; - mesh.position.z = Math.random() * 10 - 5; - - mesh.scale.x = mesh.scale.y = mesh.scale.z = Math.random() * 3 + 1; - - scene.add(mesh); - - spheres.push(mesh); - } - - // - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - const width = window.innerWidth || 2; - const height = window.innerHeight || 2; - - effect = new AnaglyphEffect(renderer); - effect.setSize(width, height); - - // Configure stereo parameters for physically-correct rendering - // eyeSep: interpupillary distance (default 0.064m / 64mm for humans) - // planeDistance: distance to the zero-parallax plane (objects here appear at screen depth) - effect.eyeSep = 0.064; - effect.planeDistance = 3; // Match camera distance to origin for zero parallax at scene center - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - windowHalfX = window.innerWidth / 2; - windowHalfY = window.innerHeight / 2; - - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - effect.setSize(window.innerWidth, window.innerHeight); -} - -function onDocumentMouseMove(event) { - mouseX = (event.clientX - windowHalfX) / 100; - mouseY = (event.clientY - windowHalfY) / 100; -} - -// - -function animate() { - render(); -} - -function render() { - const timer = 0.0001 * Date.now(); - - camera.position.x += (mouseX - camera.position.x) * 0.05; - camera.position.y += (-mouseY - camera.position.y) * 0.05; - - camera.lookAt(scene.position); - - for (let i = 0, il = spheres.length; i < il; i++) { - const sphere = spheres[i]; - - sphere.position.x = 5 * Math.cos(timer + i); - sphere.position.y = 5 * Math.sin(timer + i * 1.1); - } - - effect.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_effects_ascii.ts b/examples-testing/examples/webgl_effects_ascii.ts deleted file mode 100644 index a412bb79e..000000000 --- a/examples-testing/examples/webgl_effects_ascii.ts +++ /dev/null @@ -1,81 +0,0 @@ -import * as THREE from 'three'; - -import { AsciiEffect } from 'three/addons/effects/AsciiEffect.js'; -import { TrackballControls } from 'three/addons/controls/TrackballControls.js'; - -let camera, controls, scene, renderer, effect; - -let sphere, plane; - -const start = Date.now(); - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.y = 150; - camera.position.z = 500; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0, 0, 0); - - const pointLight1 = new THREE.PointLight(0xffffff, 3, 0, 0); - pointLight1.position.set(500, 500, 500); - scene.add(pointLight1); - - const pointLight2 = new THREE.PointLight(0xffffff, 1, 0, 0); - pointLight2.position.set(-500, -500, -500); - scene.add(pointLight2); - - sphere = new THREE.Mesh(new THREE.SphereGeometry(200, 20, 10), new THREE.MeshPhongMaterial({ flatShading: true })); - scene.add(sphere); - - // Plane - - plane = new THREE.Mesh(new THREE.PlaneGeometry(400, 400), new THREE.MeshBasicMaterial({ color: 0xe0e0e0 })); - plane.position.y = -200; - plane.rotation.x = -Math.PI / 2; - scene.add(plane); - - renderer = new THREE.WebGLRenderer(); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - - effect = new AsciiEffect(renderer, ' .:-+*=%@#', { invert: true }); - effect.setSize(window.innerWidth, window.innerHeight); - effect.domElement.style.color = 'white'; - effect.domElement.style.backgroundColor = 'black'; - - // Special case: append effect.domElement, instead of renderer.domElement. - // AsciiEffect creates a custom domElement (a div container) where the ASCII elements are placed. - - document.body.appendChild(effect.domElement); - - controls = new TrackballControls(camera, effect.domElement); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - effect.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - const timer = Date.now() - start; - - sphere.position.y = Math.abs(Math.sin(timer * 0.002)) * 150; - sphere.rotation.x = timer * 0.0003; - sphere.rotation.z = timer * 0.0002; - - controls.update(); - - effect.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_effects_parallaxbarrier.ts b/examples-testing/examples/webgl_effects_parallaxbarrier.ts deleted file mode 100644 index 90c867973..000000000 --- a/examples-testing/examples/webgl_effects_parallaxbarrier.ts +++ /dev/null @@ -1,110 +0,0 @@ -import * as THREE from 'three'; - -import { ParallaxBarrierEffect } from 'three/addons/effects/ParallaxBarrierEffect.js'; - -let container, camera, scene, renderer, effect; - -const spheres = []; - -let mouseX = 0; -let mouseY = 0; - -let windowHalfX = window.innerWidth / 2; -let windowHalfY = window.innerHeight / 2; - -document.addEventListener('mousemove', onDocumentMouseMove); - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.01, 100); - camera.position.z = 3; - - const path = 'textures/cube/pisa/'; - const format = '.png'; - const urls = [ - path + 'px' + format, - path + 'nx' + format, - path + 'py' + format, - path + 'ny' + format, - path + 'pz' + format, - path + 'nz' + format, - ]; - - const textureCube = new THREE.CubeTextureLoader().load(urls); - - scene = new THREE.Scene(); - scene.background = textureCube; - - const geometry = new THREE.SphereGeometry(0.1, 32, 16); - const material = new THREE.MeshBasicMaterial({ color: 0xffffff, envMap: textureCube }); - - for (let i = 0; i < 500; i++) { - const mesh = new THREE.Mesh(geometry, material); - - mesh.position.x = Math.random() * 10 - 5; - mesh.position.y = Math.random() * 10 - 5; - mesh.position.z = Math.random() * 10 - 5; - - mesh.scale.x = mesh.scale.y = mesh.scale.z = Math.random() * 3 + 1; - - scene.add(mesh); - - spheres.push(mesh); - } - - // - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - const width = window.innerWidth || 2; - const height = window.innerHeight || 2; - - effect = new ParallaxBarrierEffect(renderer); - effect.setSize(width, height); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - windowHalfX = window.innerWidth / 2; - windowHalfY = window.innerHeight / 2; - - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - effect.setSize(window.innerWidth, window.innerHeight); -} - -function onDocumentMouseMove(event) { - mouseX = (event.clientX - windowHalfX) / 100; - mouseY = (event.clientY - windowHalfY) / 100; -} - -// - -function animate() { - const timer = 0.0001 * Date.now(); - - camera.position.x += (mouseX - camera.position.x) * 0.05; - camera.position.y += (-mouseY - camera.position.y) * 0.05; - - camera.lookAt(scene.position); - - for (let i = 0, il = spheres.length; i < il; i++) { - const sphere = spheres[i]; - - sphere.position.x = 5 * Math.cos(timer + i); - sphere.position.y = 5 * Math.sin(timer + i * 1.1); - } - - effect.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_effects_stereo.ts b/examples-testing/examples/webgl_effects_stereo.ts deleted file mode 100644 index 673850541..000000000 --- a/examples-testing/examples/webgl_effects_stereo.ts +++ /dev/null @@ -1,98 +0,0 @@ -import * as THREE from 'three'; - -import { StereoEffect } from 'three/addons/effects/StereoEffect.js'; - -let container, camera, scene, renderer, effect; - -const spheres = []; - -let mouseX = 0, - mouseY = 0; - -let windowHalfX = window.innerWidth / 2; -let windowHalfY = window.innerHeight / 2; - -document.addEventListener('mousemove', onDocumentMouseMove); - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.z = 3; - - scene = new THREE.Scene(); - scene.background = new THREE.CubeTextureLoader() - .setPath('textures/cube/Park3Med/') - .load(['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg']); - - const geometry = new THREE.SphereGeometry(0.1, 32, 16); - - const textureCube = new THREE.CubeTextureLoader() - .setPath('textures/cube/Park3Med/') - .load(['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg']); - textureCube.mapping = THREE.CubeRefractionMapping; - - const material = new THREE.MeshBasicMaterial({ color: 0xffffff, envMap: textureCube, refractionRatio: 0.95 }); - - for (let i = 0; i < 500; i++) { - const mesh = new THREE.Mesh(geometry, material); - mesh.position.x = Math.random() * 10 - 5; - mesh.position.y = Math.random() * 10 - 5; - mesh.position.z = Math.random() * 10 - 5; - mesh.scale.x = mesh.scale.y = mesh.scale.z = Math.random() * 3 + 1; - scene.add(mesh); - - spheres.push(mesh); - } - - // - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - effect = new StereoEffect(renderer); - effect.setSize(window.innerWidth, window.innerHeight); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - windowHalfX = window.innerWidth / 2; - windowHalfY = window.innerHeight / 2; - - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - effect.setSize(window.innerWidth, window.innerHeight); -} - -function onDocumentMouseMove(event) { - mouseX = (event.clientX - windowHalfX) * 0.01; - mouseY = (event.clientY - windowHalfY) * 0.01; -} - -// - -function animate() { - const timer = 0.0001 * Date.now(); - - camera.position.x += (mouseX - camera.position.x) * 0.05; - camera.position.y += (-mouseY - camera.position.y) * 0.05; - camera.lookAt(scene.position); - - for (let i = 0, il = spheres.length; i < il; i++) { - const sphere = spheres[i]; - - sphere.position.x = 5 * Math.cos(timer + i); - sphere.position.y = 5 * Math.sin(timer + i * 1.1); - } - - effect.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_framebuffer_texture.ts b/examples-testing/examples/webgl_framebuffer_texture.ts deleted file mode 100644 index df4acc9d6..000000000 --- a/examples-testing/examples/webgl_framebuffer_texture.ts +++ /dev/null @@ -1,151 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import * as GeometryUtils from 'three/addons/utils/GeometryUtils.js'; - -let camera, scene, renderer; -let line, sprite, texture; - -let cameraOrtho, sceneOrtho; - -let offset = 0; - -const dpr = window.devicePixelRatio; - -const textureSize = 128 * dpr; -const vector = new THREE.Vector2(); -const color = new THREE.Color(); - -init(); - -function init() { - // - - const width = window.innerWidth; - const height = window.innerHeight; - - camera = new THREE.PerspectiveCamera(70, width / height, 1, 1000); - camera.position.z = 20; - - cameraOrtho = new THREE.OrthographicCamera(-width / 2, width / 2, height / 2, -height / 2, 1, 10); - cameraOrtho.position.z = 10; - - scene = new THREE.Scene(); - sceneOrtho = new THREE.Scene(); - - // - - const points = GeometryUtils.gosper(8); - - const geometry = new THREE.BufferGeometry(); - const positionAttribute = new THREE.Float32BufferAttribute(points, 3); - geometry.setAttribute('position', positionAttribute); - geometry.center(); - - const colorAttribute = new THREE.BufferAttribute(new Float32Array(positionAttribute.array.length), 3); - colorAttribute.setUsage(THREE.DynamicDrawUsage); - geometry.setAttribute('color', colorAttribute); - - const material = new THREE.LineBasicMaterial({ vertexColors: true }); - - line = new THREE.Line(geometry, material); - line.scale.setScalar(0.05); - scene.add(line); - - // - - texture = new THREE.FramebufferTexture(textureSize, textureSize); - - // - - const spriteMaterial = new THREE.SpriteMaterial({ map: texture }); - sprite = new THREE.Sprite(spriteMaterial); - sprite.scale.set(textureSize, textureSize, 1); - sceneOrtho.add(sprite); - - updateSpritePosition(); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.autoClear = false; - document.body.appendChild(renderer.domElement); - - // - - const selection = document.getElementById('selection'); - const controls = new OrbitControls(camera, selection); - controls.enablePan = false; - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - cameraOrtho.left = -width / 2; - cameraOrtho.right = width / 2; - cameraOrtho.top = height / 2; - cameraOrtho.bottom = -height / 2; - cameraOrtho.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - updateSpritePosition(); -} - -function updateSpritePosition() { - const halfWidth = window.innerWidth / 2; - const halfHeight = window.innerHeight / 2; - - const halfImageWidth = textureSize / 2; - const halfImageHeight = textureSize / 2; - - sprite.position.set(-halfWidth + halfImageWidth, halfHeight - halfImageHeight, 1); -} - -function animate() { - const colorAttribute = line.geometry.getAttribute('color'); - updateColors(colorAttribute); - - // scene rendering - - renderer.clear(); - renderer.render(scene, camera); - - // calculate start position for copying data - - vector.x = (window.innerWidth * dpr) / 2 - textureSize / 2; - vector.y = (window.innerHeight * dpr) / 2 - textureSize / 2; - - renderer.copyFramebufferToTexture(texture, vector); - - renderer.clearDepth(); - renderer.render(sceneOrtho, cameraOrtho); -} - -function updateColors(colorAttribute) { - const l = colorAttribute.count; - - for (let i = 0; i < l; i++) { - const h = ((offset + i) % l) / l; - - color.setHSL(h, 1, 0.5); - colorAttribute.setX(i, color.r); - colorAttribute.setY(i, color.g); - colorAttribute.setZ(i, color.b); - } - - colorAttribute.needsUpdate = true; - - offset -= 25; -} diff --git a/examples-testing/examples/webgl_furnace_test.ts b/examples-testing/examples/webgl_furnace_test.ts deleted file mode 100644 index a81954176..000000000 --- a/examples-testing/examples/webgl_furnace_test.ts +++ /dev/null @@ -1,96 +0,0 @@ -import * as THREE from 'three'; - -let scene, camera, renderer, radianceMap; - -const COLOR = 0xcccccc; - -function init() { - const width = window.innerWidth; - const height = window.innerHeight; - const aspect = width / height; - - // renderer - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setSize(width, height); - renderer.setPixelRatio(window.devicePixelRatio); - document.body.appendChild(renderer.domElement); - - window.addEventListener('resize', onWindowResize); - - document.body.addEventListener('mouseover', function () { - scene.traverse(function (child) { - if (child.isMesh) child.material.color.setHex(0xffffff); - }); - - render(); - }); - - document.body.addEventListener('mouseout', function () { - scene.traverse(function (child) { - if (child.isMesh) child.material.color.setHex(0xccccff); // tinted for visibility - }); - - render(); - }); - - // scene - - scene = new THREE.Scene(); - - // camera - camera = new THREE.PerspectiveCamera(40, aspect, 1, 30); - camera.position.set(0, 0, 18); -} - -function createObjects() { - const geometry = new THREE.SphereGeometry(0.4, 32, 16); - - for (let x = 0; x <= 10; x++) { - for (let y = 0; y <= 10; y++) { - const material = new THREE.MeshPhysicalMaterial({ - roughness: x / 10, - metalness: y / 10, - color: 0xffffff, - envMap: radianceMap, - envMapIntensity: 1, - transmission: 0, - ior: 1.5, - }); - - const mesh = new THREE.Mesh(geometry, material); - mesh.position.x = x - 5; - mesh.position.y = 5 - y; - scene.add(mesh); - } - } -} - -function createEnvironment() { - const envScene = new THREE.Scene(); - envScene.background = new THREE.Color(COLOR); - - const pmremGenerator = new THREE.PMREMGenerator(renderer); - radianceMap = pmremGenerator.fromScene(envScene).texture; - pmremGenerator.dispose(); - - scene.background = envScene.background; -} - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); - - render(); -} - -function render() { - renderer.render(scene, camera); -} - -Promise.resolve().then(init).then(createEnvironment).then(createObjects).then(render); diff --git a/examples-testing/examples/webgl_geometries.ts b/examples-testing/examples/webgl_geometries.ts deleted file mode 100644 index 0eb50120c..000000000 --- a/examples-testing/examples/webgl_geometries.ts +++ /dev/null @@ -1,165 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { ParametricGeometry } from 'three/addons/geometries/ParametricGeometry.js'; -import { plane, klein, mobius } from 'three/addons/geometries/ParametricFunctions.js'; - -let camera, scene, renderer, stats; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000); - camera.position.y = 500; - - scene = new THREE.Scene(); - - let object, geometry; - - const ambientLight = new THREE.AmbientLight(0xcccccc, 1.5); - scene.add(ambientLight); - - const pointLight = new THREE.PointLight(0xffffff, 2.5, 0, 0); - camera.add(pointLight); - scene.add(camera); - - const map = new THREE.TextureLoader().load('textures/uv_grid_opengl.jpg'); - map.wrapS = map.wrapT = THREE.RepeatWrapping; - map.anisotropy = 16; - map.colorSpace = THREE.SRGBColorSpace; - - const material = new THREE.MeshPhongMaterial({ map: map, side: THREE.DoubleSide }); - - // - - object = new THREE.Mesh(new THREE.SphereGeometry(75, 20, 10), material); - object.position.set(-300, 0, 300); - scene.add(object); - - object = new THREE.Mesh(new THREE.IcosahedronGeometry(75), material); - object.position.set(-100, 0, 300); - scene.add(object); - - object = new THREE.Mesh(new THREE.OctahedronGeometry(75), material); - object.position.set(100, 0, 300); - scene.add(object); - - object = new THREE.Mesh(new THREE.TetrahedronGeometry(75), material); - object.position.set(300, 0, 300); - scene.add(object); - - // - - object = new THREE.Mesh(new THREE.PlaneGeometry(100, 100, 4, 4), material); - object.position.set(-300, 0, 100); - scene.add(object); - - object = new THREE.Mesh(new THREE.BoxGeometry(100, 100, 100, 4, 4, 4), material); - object.position.set(-100, 0, 100); - scene.add(object); - - object = new THREE.Mesh(new THREE.CircleGeometry(50, 20, 0, Math.PI * 2), material); - object.position.set(100, 0, 100); - scene.add(object); - - object = new THREE.Mesh(new THREE.RingGeometry(10, 50, 20, 5, 0, Math.PI * 2), material); - object.position.set(300, 0, 100); - scene.add(object); - - // - - object = new THREE.Mesh(new THREE.CylinderGeometry(25, 75, 100, 40, 5), material); - object.position.set(-300, 0, -100); - scene.add(object); - - const points = []; - - for (let i = 0; i < 50; i++) { - points.push(new THREE.Vector2(Math.sin(i * 0.2) * Math.sin(i * 0.1) * 15 + 50, (i - 5) * 2)); - } - - object = new THREE.Mesh(new THREE.LatheGeometry(points, 20), material); - object.position.set(-100, 0, -100); - scene.add(object); - - object = new THREE.Mesh(new THREE.TorusGeometry(50, 20, 20, 20), material); - object.position.set(100, 0, -100); - scene.add(object); - - object = new THREE.Mesh(new THREE.TorusKnotGeometry(50, 10, 50, 20), material); - object.position.set(300, 0, -100); - scene.add(object); - - // - - object = new THREE.Mesh(new THREE.CapsuleGeometry(20, 50), material); - object.position.set(-300, 0, -300); - scene.add(object); - - geometry = new ParametricGeometry(plane, 10, 10); - geometry.scale(100, 100, 100); - geometry.center(); - object = new THREE.Mesh(geometry, material); - object.position.set(-100, 0, -300); - scene.add(object); - - geometry = new ParametricGeometry(klein, 20, 20); - object = new THREE.Mesh(geometry, material); - object.position.set(100, 0, -300); - object.scale.multiplyScalar(5); - scene.add(object); - - geometry = new ParametricGeometry(mobius, 20, 20); - object = new THREE.Mesh(geometry, material); - object.position.set(300, 0, -300); - object.scale.multiplyScalar(30); - scene.add(object); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - stats = new Stats(); - document.body.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - render(); - stats.update(); -} - -function render() { - const timer = Date.now() * 0.0001; - - camera.position.x = Math.cos(timer) * 800; - camera.position.z = Math.sin(timer) * 800; - - camera.lookAt(scene.position); - - scene.traverse(function (object) { - if (object.isMesh === true) { - object.rotation.x = timer * 5; - object.rotation.y = timer * 2.5; - } - }); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_geometry_colors.ts b/examples-testing/examples/webgl_geometry_colors.ts deleted file mode 100644 index 3166c03bc..000000000 --- a/examples-testing/examples/webgl_geometry_colors.ts +++ /dev/null @@ -1,177 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -let container, stats; - -let camera, scene, renderer; - -let mouseX = 0, - mouseY = 0; - -let windowHalfX = window.innerWidth / 2; -let windowHalfY = window.innerHeight / 2; - -init(); - -function init() { - container = document.getElementById('container'); - - camera = new THREE.PerspectiveCamera(20, window.innerWidth / window.innerHeight, 1, 10000); - camera.position.z = 1800; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xffffff); - - const light = new THREE.DirectionalLight(0xffffff, 3); - light.position.set(0, 0, 1); - scene.add(light); - - // shadow - - const canvas = document.createElement('canvas'); - canvas.width = 128; - canvas.height = 128; - - const context = canvas.getContext('2d'); - const gradient = context.createRadialGradient( - canvas.width / 2, - canvas.height / 2, - 0, - canvas.width / 2, - canvas.height / 2, - canvas.width / 2, - ); - gradient.addColorStop(0.1, 'rgba(210,210,210,1)'); - gradient.addColorStop(1, 'rgba(255,255,255,1)'); - - context.fillStyle = gradient; - context.fillRect(0, 0, canvas.width, canvas.height); - - const shadowTexture = new THREE.CanvasTexture(canvas); - - const shadowMaterial = new THREE.MeshBasicMaterial({ map: shadowTexture }); - const shadowGeo = new THREE.PlaneGeometry(300, 300, 1, 1); - - let shadowMesh; - - shadowMesh = new THREE.Mesh(shadowGeo, shadowMaterial); - shadowMesh.position.y = -250; - shadowMesh.rotation.x = -Math.PI / 2; - scene.add(shadowMesh); - - shadowMesh = new THREE.Mesh(shadowGeo, shadowMaterial); - shadowMesh.position.y = -250; - shadowMesh.position.x = -400; - shadowMesh.rotation.x = -Math.PI / 2; - scene.add(shadowMesh); - - shadowMesh = new THREE.Mesh(shadowGeo, shadowMaterial); - shadowMesh.position.y = -250; - shadowMesh.position.x = 400; - shadowMesh.rotation.x = -Math.PI / 2; - scene.add(shadowMesh); - - const radius = 200; - - const geometry1 = new THREE.IcosahedronGeometry(radius, 1); - - const count = geometry1.attributes.position.count; - const arrayType = typeof Float16Array !== 'undefined' ? Float16Array : Float32Array; - geometry1.setAttribute('color', new THREE.BufferAttribute(new arrayType(count * 3), 3)); - - const geometry2 = geometry1.clone(); - const geometry3 = geometry1.clone(); - - const color = new THREE.Color(); - const positions1 = geometry1.attributes.position; - const positions2 = geometry2.attributes.position; - const positions3 = geometry3.attributes.position; - const colors1 = geometry1.attributes.color; - const colors2 = geometry2.attributes.color; - const colors3 = geometry3.attributes.color; - - for (let i = 0; i < count; i++) { - color.setHSL((positions1.getY(i) / radius + 1) / 2, 1.0, 0.5, THREE.SRGBColorSpace); - colors1.setXYZ(i, color.r, color.g, color.b); - - color.setHSL(0, (positions2.getY(i) / radius + 1) / 2, 0.5, THREE.SRGBColorSpace); - colors2.setXYZ(i, color.r, color.g, color.b); - - color.setRGB(1, 0.8 - (positions3.getY(i) / radius + 1) / 2, 0, THREE.SRGBColorSpace); - colors3.setXYZ(i, color.r, color.g, color.b); - } - - const material = new THREE.MeshPhongMaterial({ - color: 0xffffff, - flatShading: true, - vertexColors: true, - shininess: 0, - }); - - const wireframeMaterial = new THREE.MeshBasicMaterial({ color: 0x000000, wireframe: true, transparent: true }); - - let mesh = new THREE.Mesh(geometry1, material); - let wireframe = new THREE.Mesh(geometry1, wireframeMaterial); - mesh.add(wireframe); - mesh.position.x = -400; - mesh.rotation.x = -1.87; - scene.add(mesh); - - mesh = new THREE.Mesh(geometry2, material); - wireframe = new THREE.Mesh(geometry2, wireframeMaterial); - mesh.add(wireframe); - mesh.position.x = 400; - scene.add(mesh); - - mesh = new THREE.Mesh(geometry3, material); - wireframe = new THREE.Mesh(geometry3, wireframeMaterial); - mesh.add(wireframe); - scene.add(mesh); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - stats = new Stats(); - container.appendChild(stats.dom); - - document.addEventListener('mousemove', onDocumentMouseMove); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - windowHalfX = window.innerWidth / 2; - windowHalfY = window.innerHeight / 2; - - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function onDocumentMouseMove(event) { - mouseX = event.clientX - windowHalfX; - mouseY = event.clientY - windowHalfY; -} - -// - -function animate() { - render(); - stats.update(); -} - -function render() { - camera.position.x += (mouseX - camera.position.x) * 0.05; - camera.position.y += (-mouseY - camera.position.y) * 0.05; - - camera.lookAt(scene.position); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_geometry_colors_lookuptable.ts b/examples-testing/examples/webgl_geometry_colors_lookuptable.ts deleted file mode 100644 index 6b0138529..000000000 --- a/examples-testing/examples/webgl_geometry_colors_lookuptable.ts +++ /dev/null @@ -1,148 +0,0 @@ -import * as THREE from 'three'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { Lut } from 'three/addons/math/Lut.js'; - -let container; - -let perpCamera, orthoCamera, renderer, lut; - -let mesh, sprite; -let scene, uiScene; - -let params; - -init(); - -function init() { - container = document.getElementById('container'); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xffffff); - - uiScene = new THREE.Scene(); - - lut = new Lut(); - - const width = window.innerWidth; - const height = window.innerHeight; - - perpCamera = new THREE.PerspectiveCamera(60, width / height, 1, 100); - perpCamera.position.set(0, 0, 10); - scene.add(perpCamera); - - orthoCamera = new THREE.OrthographicCamera(-1, 1, 1, -1, 1, 2); - orthoCamera.position.set(0.5, 0, 1); - - sprite = new THREE.Sprite( - new THREE.SpriteMaterial({ - map: new THREE.CanvasTexture(lut.createCanvas()), - }), - ); - sprite.material.map.colorSpace = THREE.SRGBColorSpace; - sprite.scale.x = 0.125; - uiScene.add(sprite); - - mesh = new THREE.Mesh( - undefined, - new THREE.MeshLambertMaterial({ - side: THREE.DoubleSide, - color: 0xf5f5f5, - vertexColors: true, - }), - ); - scene.add(mesh); - - params = { - colorMap: 'rainbow', - }; - loadModel(); - - const pointLight = new THREE.PointLight(0xffffff, 3, 0, 0); - perpCamera.add(pointLight); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.autoClear = false; - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(width, height); - container.appendChild(renderer.domElement); - - window.addEventListener('resize', onWindowResize); - - const controls = new OrbitControls(perpCamera, renderer.domElement); - controls.addEventListener('change', render); - - const gui = new GUI(); - - gui.add(params, 'colorMap', ['rainbow', 'cooltowarm', 'blackbody', 'grayscale']).onChange(function () { - updateColors(); - render(); - }); -} - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - perpCamera.aspect = width / height; - perpCamera.updateProjectionMatrix(); - - renderer.setSize(width, height); - render(); -} - -function render() { - renderer.clear(); - renderer.render(scene, perpCamera); - renderer.render(uiScene, orthoCamera); -} - -function loadModel() { - const loader = new THREE.BufferGeometryLoader(); - loader.load('models/json/pressure.json', function (geometry) { - geometry.center(); - geometry.computeVertexNormals(); - - // default color attribute - const colors = []; - - for (let i = 0, n = geometry.attributes.position.count; i < n; ++i) { - colors.push(1, 1, 1); - } - - geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); - - mesh.geometry = geometry; - updateColors(); - - render(); - }); -} - -function updateColors() { - lut.setColorMap(params.colorMap); - - lut.setMax(2000); - lut.setMin(0); - - const geometry = mesh.geometry; - const pressures = geometry.attributes.pressure; - const colors = geometry.attributes.color; - const color = new THREE.Color(); - - for (let i = 0; i < pressures.array.length; i++) { - const colorValue = pressures.array[i]; - - color.copy(lut.getColor(colorValue)).convertSRGBToLinear(); - - colors.setXYZ(i, color.r, color.g, color.b); - } - - colors.needsUpdate = true; - - const map = sprite.material.map; - lut.updateCanvas(map.image); - map.needsUpdate = true; -} diff --git a/examples-testing/examples/webgl_geometry_convex.ts b/examples-testing/examples/webgl_geometry_convex.ts deleted file mode 100644 index 09516c070..000000000 --- a/examples-testing/examples/webgl_geometry_convex.ts +++ /dev/null @@ -1,117 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { ConvexGeometry } from 'three/addons/geometries/ConvexGeometry.js'; -import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js'; - -let group, camera, scene, renderer; - -init(); - -function init() { - scene = new THREE.Scene(); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // camera - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(15, 20, 30); - scene.add(camera); - - // controls - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 20; - controls.maxDistance = 50; - controls.maxPolarAngle = Math.PI / 2; - - // ambient light - - scene.add(new THREE.AmbientLight(0x666666)); - - // point light - - const light = new THREE.PointLight(0xffffff, 3, 0, 0); - camera.add(light); - - // helper - - scene.add(new THREE.AxesHelper(20)); - - // textures - - const loader = new THREE.TextureLoader(); - const texture = loader.load('textures/sprites/disc.png'); - texture.colorSpace = THREE.SRGBColorSpace; - - group = new THREE.Group(); - scene.add(group); - - // points - - let dodecahedronGeometry = new THREE.DodecahedronGeometry(10); - - // if normal and uv attributes are not removed, mergeVertices() can't consolidate identical vertices with different normal/uv data - - dodecahedronGeometry.deleteAttribute('normal'); - dodecahedronGeometry.deleteAttribute('uv'); - - dodecahedronGeometry = BufferGeometryUtils.mergeVertices(dodecahedronGeometry); - - const vertices = []; - const positionAttribute = dodecahedronGeometry.getAttribute('position'); - - for (let i = 0; i < positionAttribute.count; i++) { - const vertex = new THREE.Vector3(); - vertex.fromBufferAttribute(positionAttribute, i); - vertices.push(vertex); - } - - const pointsMaterial = new THREE.PointsMaterial({ - color: 0x0080ff, - map: texture, - size: 1, - alphaTest: 0.5, - }); - - const pointsGeometry = new THREE.BufferGeometry().setFromPoints(vertices); - - const points = new THREE.Points(pointsGeometry, pointsMaterial); - group.add(points); - - // convex hull - - const meshMaterial = new THREE.MeshLambertMaterial({ - color: 0xffffff, - opacity: 0.5, - side: THREE.DoubleSide, - transparent: true, - }); - - const meshGeometry = new ConvexGeometry(vertices); - - const mesh = new THREE.Mesh(meshGeometry, meshMaterial); - group.add(mesh); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - group.rotation.y += 0.005; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_geometry_cube.ts b/examples-testing/examples/webgl_geometry_cube.ts deleted file mode 100644 index 572601acb..000000000 --- a/examples-testing/examples/webgl_geometry_cube.ts +++ /dev/null @@ -1,46 +0,0 @@ -import * as THREE from 'three'; - -let camera, scene, renderer; -let mesh; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.z = 2; - - scene = new THREE.Scene(); - - const texture = new THREE.TextureLoader().load('textures/crate.gif'); - texture.colorSpace = THREE.SRGBColorSpace; - - const geometry = new THREE.BoxGeometry(); - const material = new THREE.MeshBasicMaterial({ map: texture }); - - mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - mesh.rotation.x += 0.005; - mesh.rotation.y += 0.01; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_geometry_extrude_shapes.ts b/examples-testing/examples/webgl_geometry_extrude_shapes.ts deleted file mode 100644 index a905c8ea3..000000000 --- a/examples-testing/examples/webgl_geometry_extrude_shapes.ts +++ /dev/null @@ -1,159 +0,0 @@ -import * as THREE from 'three'; - -import { TrackballControls } from 'three/addons/controls/TrackballControls.js'; - -let camera, scene, renderer, controls; - -init(); - -function init() { - const info = document.createElement('div'); - info.style.position = 'absolute'; - info.style.top = '10px'; - info.style.width = '100%'; - info.style.textAlign = 'center'; - info.style.color = '#fff'; - info.innerHTML = - 'three.js webgl - geometry extrude shapes'; - document.body.appendChild(info); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x222222); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(0, 0, 500); - - controls = new TrackballControls(camera, renderer.domElement); - controls.minDistance = 200; - controls.maxDistance = 500; - - scene.add(new THREE.AmbientLight(0x666666)); - - const light = new THREE.PointLight(0xffffff, 3, 0, 0); - light.position.copy(camera.position); - scene.add(light); - - // - - const closedSpline = new THREE.CatmullRomCurve3([ - new THREE.Vector3(-60, -100, 60), - new THREE.Vector3(-60, 20, 60), - new THREE.Vector3(-60, 120, 60), - new THREE.Vector3(60, 20, -60), - new THREE.Vector3(60, -100, -60), - ]); - - closedSpline.curveType = 'catmullrom'; - closedSpline.closed = true; - - const extrudeSettings1 = { - steps: 100, - bevelEnabled: false, - extrudePath: closedSpline, - }; - - const pts1 = [], - count = 3; - - for (let i = 0; i < count; i++) { - const l = 20; - - const a = ((2 * i) / count) * Math.PI; - - pts1.push(new THREE.Vector2(Math.cos(a) * l, Math.sin(a) * l)); - } - - const shape1 = new THREE.Shape(pts1); - - const geometry1 = new THREE.ExtrudeGeometry(shape1, extrudeSettings1); - - const material1 = new THREE.MeshLambertMaterial({ color: 0xb00000, wireframe: false }); - - const mesh1 = new THREE.Mesh(geometry1, material1); - - scene.add(mesh1); - - // - - const randomPoints = []; - - for (let i = 0; i < 10; i++) { - randomPoints.push( - new THREE.Vector3((i - 4.5) * 50, THREE.MathUtils.randFloat(-50, 50), THREE.MathUtils.randFloat(-50, 50)), - ); - } - - const randomSpline = new THREE.CatmullRomCurve3(randomPoints); - - // - - const extrudeSettings2 = { - steps: 200, - bevelEnabled: false, - extrudePath: randomSpline, - }; - - const pts2 = [], - numPts = 5; - - for (let i = 0; i < numPts * 2; i++) { - const l = i % 2 == 1 ? 10 : 20; - - const a = (i / numPts) * Math.PI; - - pts2.push(new THREE.Vector2(Math.cos(a) * l, Math.sin(a) * l)); - } - - const shape2 = new THREE.Shape(pts2); - - const geometry2 = new THREE.ExtrudeGeometry(shape2, extrudeSettings2); - - const material2 = new THREE.MeshLambertMaterial({ color: 0xff8000, wireframe: false }); - - const mesh2 = new THREE.Mesh(geometry2, material2); - - scene.add(mesh2); - - // - - const materials = [material1, material2]; - - const extrudeSettings3 = { - depth: 20, - steps: 1, - bevelEnabled: true, - bevelThickness: 2, - bevelSize: 4, - bevelSegments: 1, - }; - - const geometry3 = new THREE.ExtrudeGeometry(shape2, extrudeSettings3); - - const mesh3 = new THREE.Mesh(geometry3, materials); - - mesh3.position.set(50, 100, 50); - - scene.add(mesh3); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - controls.update(); - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_geometry_extrude_splines.ts b/examples-testing/examples/webgl_geometry_extrude_splines.ts deleted file mode 100644 index 8636812f7..000000000 --- a/examples-testing/examples/webgl_geometry_extrude_splines.ts +++ /dev/null @@ -1,310 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import * as Curves from 'three/addons/curves/CurveExtras.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let container, stats; - -let camera, scene, renderer, splineCamera, cameraHelper, cameraEye; - -const direction = new THREE.Vector3(); -const binormal = new THREE.Vector3(); -const normal = new THREE.Vector3(); -const position = new THREE.Vector3(); -const lookAt = new THREE.Vector3(); - -const pipeSpline = new THREE.CatmullRomCurve3([ - new THREE.Vector3(0, 10, -10), - new THREE.Vector3(10, 0, -10), - new THREE.Vector3(20, 0, 0), - new THREE.Vector3(30, 0, 10), - new THREE.Vector3(30, 0, 20), - new THREE.Vector3(20, 0, 30), - new THREE.Vector3(10, 0, 30), - new THREE.Vector3(0, 0, 30), - new THREE.Vector3(-10, 10, 30), - new THREE.Vector3(-10, 20, 30), - new THREE.Vector3(0, 30, 30), - new THREE.Vector3(10, 30, 30), - new THREE.Vector3(20, 30, 15), - new THREE.Vector3(10, 30, 10), - new THREE.Vector3(0, 30, 10), - new THREE.Vector3(-10, 20, 10), - new THREE.Vector3(-10, 10, 10), - new THREE.Vector3(0, 0, 10), - new THREE.Vector3(10, -10, 10), - new THREE.Vector3(20, -15, 10), - new THREE.Vector3(30, -15, 10), - new THREE.Vector3(40, -15, 10), - new THREE.Vector3(50, -15, 10), - new THREE.Vector3(60, 0, 10), - new THREE.Vector3(70, 0, 0), - new THREE.Vector3(80, 0, 0), - new THREE.Vector3(90, 0, 0), - new THREE.Vector3(100, 0, 0), -]); - -const sampleClosedSpline = new THREE.CatmullRomCurve3([ - new THREE.Vector3(0, -40, -40), - new THREE.Vector3(0, 40, -40), - new THREE.Vector3(0, 140, -40), - new THREE.Vector3(0, 40, 40), - new THREE.Vector3(0, -40, 40), -]); - -sampleClosedSpline.curveType = 'catmullrom'; -sampleClosedSpline.closed = true; - -// Keep a dictionary of Curve instances -const splines = { - GrannyKnot: new Curves.GrannyKnot(), - HeartCurve: new Curves.HeartCurve(3.5), - VivianiCurve: new Curves.VivianiCurve(70), - KnotCurve: new Curves.KnotCurve(), - HelixCurve: new Curves.HelixCurve(), - TrefoilKnot: new Curves.TrefoilKnot(), - TorusKnot: new Curves.TorusKnot(20), - CinquefoilKnot: new Curves.CinquefoilKnot(20), - TrefoilPolynomialKnot: new Curves.TrefoilPolynomialKnot(14), - FigureEightPolynomialKnot: new Curves.FigureEightPolynomialKnot(), - DecoratedTorusKnot4a: new Curves.DecoratedTorusKnot4a(), - DecoratedTorusKnot4b: new Curves.DecoratedTorusKnot4b(), - DecoratedTorusKnot5a: new Curves.DecoratedTorusKnot5a(), - DecoratedTorusKnot5c: new Curves.DecoratedTorusKnot5c(), - PipeSpline: pipeSpline, - SampleClosedSpline: sampleClosedSpline, -}; - -let parent, tubeGeometry, mesh; - -const params = { - spline: 'GrannyKnot', - scale: 4, - extrusionSegments: 100, - radiusSegments: 3, - closed: true, - animationView: false, - lookAhead: false, - cameraHelper: false, -}; - -const material = new THREE.MeshLambertMaterial({ color: 0xff00ff }); - -const wireframeMaterial = new THREE.MeshBasicMaterial({ - color: 0x000000, - opacity: 0.3, - wireframe: true, - transparent: true, -}); - -function addTube() { - if (mesh !== undefined) { - parent.remove(mesh); - mesh.geometry.dispose(); - } - - const extrudePath = splines[params.spline]; - - tubeGeometry = new THREE.TubeGeometry( - extrudePath, - params.extrusionSegments, - 2, - params.radiusSegments, - params.closed, - ); - - addGeometry(tubeGeometry); - - setScale(); -} - -function setScale() { - mesh.scale.set(params.scale, params.scale, params.scale); -} - -function addGeometry(geometry) { - // 3D shape - - mesh = new THREE.Mesh(geometry, material); - const wireframe = new THREE.Mesh(geometry, wireframeMaterial); - mesh.add(wireframe); - - parent.add(mesh); -} - -function animateCamera() { - cameraHelper.visible = params.cameraHelper; - cameraEye.visible = params.cameraHelper; -} - -init(); - -function init() { - container = document.getElementById('container'); - - // camera - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.01, 10000); - camera.position.set(0, 50, 500); - - // scene - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xf0f0f0); - - // light - - scene.add(new THREE.AmbientLight(0xffffff)); - - const light = new THREE.DirectionalLight(0xffffff, 1.5); - light.position.set(0, 0, 1); - scene.add(light); - - // tube - - parent = new THREE.Object3D(); - scene.add(parent); - - splineCamera = new THREE.PerspectiveCamera(84, window.innerWidth / window.innerHeight, 0.01, 1000); - parent.add(splineCamera); - - cameraHelper = new THREE.CameraHelper(splineCamera); - scene.add(cameraHelper); - - addTube(); - - // debug camera - - cameraEye = new THREE.Mesh(new THREE.SphereGeometry(5), new THREE.MeshBasicMaterial({ color: 0xdddddd })); - parent.add(cameraEye); - - cameraHelper.visible = params.cameraHelper; - cameraEye.visible = params.cameraHelper; - - // renderer - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - // stats - - stats = new Stats(); - container.appendChild(stats.dom); - - // dat.GUI - - const gui = new GUI({ width: 285 }); - - const folderGeometry = gui.addFolder('Geometry'); - folderGeometry.add(params, 'spline', Object.keys(splines)).onChange(function () { - addTube(); - }); - folderGeometry - .add(params, 'scale', 2, 10) - .step(2) - .onChange(function () { - setScale(); - }); - folderGeometry - .add(params, 'extrusionSegments', 50, 500) - .step(50) - .onChange(function () { - addTube(); - }); - folderGeometry - .add(params, 'radiusSegments', 2, 12) - .step(1) - .onChange(function () { - addTube(); - }); - folderGeometry.add(params, 'closed').onChange(function () { - addTube(); - }); - folderGeometry.open(); - - const folderCamera = gui.addFolder('Camera'); - folderCamera.add(params, 'animationView').onChange(function () { - animateCamera(); - }); - folderCamera.add(params, 'lookAhead').onChange(function () { - animateCamera(); - }); - folderCamera.add(params, 'cameraHelper').onChange(function () { - animateCamera(); - }); - folderCamera.open(); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 100; - controls.maxDistance = 2000; - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - render(); - stats.update(); -} - -function render() { - // animate camera along spline - - const time = Date.now(); - const looptime = 20 * 1000; - const t = (time % looptime) / looptime; - - tubeGeometry.parameters.path.getPointAt(t, position); - position.multiplyScalar(params.scale); - - // interpolation - - const segments = tubeGeometry.tangents.length; - const pickt = t * segments; - const pick = Math.floor(pickt); - const pickNext = (pick + 1) % segments; - - binormal.subVectors(tubeGeometry.binormals[pickNext], tubeGeometry.binormals[pick]); - binormal.multiplyScalar(pickt - pick).add(tubeGeometry.binormals[pick]); - - tubeGeometry.parameters.path.getTangentAt(t, direction); - const offset = 15; - - normal.copy(binormal).cross(direction); - - // we move on a offset on its binormal - - position.add(normal.clone().multiplyScalar(offset)); - - splineCamera.position.copy(position); - cameraEye.position.copy(position); - - // using arclength for stabilization in look ahead - - tubeGeometry.parameters.path.getPointAt((t + 30 / tubeGeometry.parameters.path.getLength()) % 1, lookAt); - lookAt.multiplyScalar(params.scale); - - // camera orientation 2 - up orientation via normal - - if (!params.lookAhead) lookAt.copy(position).add(direction); - splineCamera.matrix.lookAt(splineCamera.position, lookAt, normal); - splineCamera.quaternion.setFromRotationMatrix(splineCamera.matrix); - - cameraHelper.update(); - - renderer.render(scene, params.animationView === true ? splineCamera : camera); -} diff --git a/examples-testing/examples/webgl_geometry_minecraft.ts b/examples-testing/examples/webgl_geometry_minecraft.ts deleted file mode 100644 index 220156552..000000000 --- a/examples-testing/examples/webgl_geometry_minecraft.ts +++ /dev/null @@ -1,184 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { FirstPersonControls } from 'three/addons/controls/FirstPersonControls.js'; -import { ImprovedNoise } from 'three/addons/math/ImprovedNoise.js'; -import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js'; - -let container, stats; - -let camera, controls, scene, renderer; - -const worldWidth = 128, - worldDepth = 128; -const worldHalfWidth = worldWidth / 2; -const worldHalfDepth = worldDepth / 2; -const data = generateHeight(worldWidth, worldDepth); - -const timer = new THREE.Timer(); -timer.connect(document); - -init(); - -function init() { - container = document.getElementById('container'); - - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 20000); - camera.position.y = getY(worldHalfWidth, worldHalfDepth) * 100 + 100; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xbfd1e5); - - // sides - - const matrix = new THREE.Matrix4(); - - const pxGeometry = new THREE.PlaneGeometry(100, 100); - pxGeometry.attributes.uv.array[1] = 0.5; - pxGeometry.attributes.uv.array[3] = 0.5; - pxGeometry.rotateY(Math.PI / 2); - pxGeometry.translate(50, 0, 0); - - const nxGeometry = new THREE.PlaneGeometry(100, 100); - nxGeometry.attributes.uv.array[1] = 0.5; - nxGeometry.attributes.uv.array[3] = 0.5; - nxGeometry.rotateY(-Math.PI / 2); - nxGeometry.translate(-50, 0, 0); - - const pyGeometry = new THREE.PlaneGeometry(100, 100); - pyGeometry.attributes.uv.array[5] = 0.5; - pyGeometry.attributes.uv.array[7] = 0.5; - pyGeometry.rotateX(-Math.PI / 2); - pyGeometry.translate(0, 50, 0); - - const pzGeometry = new THREE.PlaneGeometry(100, 100); - pzGeometry.attributes.uv.array[1] = 0.5; - pzGeometry.attributes.uv.array[3] = 0.5; - pzGeometry.translate(0, 0, 50); - - const nzGeometry = new THREE.PlaneGeometry(100, 100); - nzGeometry.attributes.uv.array[1] = 0.5; - nzGeometry.attributes.uv.array[3] = 0.5; - nzGeometry.rotateY(Math.PI); - nzGeometry.translate(0, 0, -50); - - // - - const geometries = []; - - for (let z = 0; z < worldDepth; z++) { - for (let x = 0; x < worldWidth; x++) { - const h = getY(x, z); - - matrix.makeTranslation(x * 100 - worldHalfWidth * 100, h * 100, z * 100 - worldHalfDepth * 100); - - const px = getY(x + 1, z); - const nx = getY(x - 1, z); - const pz = getY(x, z + 1); - const nz = getY(x, z - 1); - - geometries.push(pyGeometry.clone().applyMatrix4(matrix)); - - if ((px !== h && px !== h + 1) || x === 0) { - geometries.push(pxGeometry.clone().applyMatrix4(matrix)); - } - - if ((nx !== h && nx !== h + 1) || x === worldWidth - 1) { - geometries.push(nxGeometry.clone().applyMatrix4(matrix)); - } - - if ((pz !== h && pz !== h + 1) || z === worldDepth - 1) { - geometries.push(pzGeometry.clone().applyMatrix4(matrix)); - } - - if ((nz !== h && nz !== h + 1) || z === 0) { - geometries.push(nzGeometry.clone().applyMatrix4(matrix)); - } - } - } - - const geometry = BufferGeometryUtils.mergeGeometries(geometries); - geometry.computeBoundingSphere(); - - const texture = new THREE.TextureLoader().load('textures/minecraft/atlas.png'); - texture.colorSpace = THREE.SRGBColorSpace; - texture.magFilter = THREE.NearestFilter; - - const mesh = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({ map: texture, side: THREE.DoubleSide })); - scene.add(mesh); - - const ambientLight = new THREE.AmbientLight(0xeeeeee, 3); - scene.add(ambientLight); - - const directionalLight = new THREE.DirectionalLight(0xffffff, 12); - directionalLight.position.set(1, 1, 0.5).normalize(); - scene.add(directionalLight); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - controls = new FirstPersonControls(camera, renderer.domElement); - - controls.movementSpeed = 1000; - controls.lookSpeed = 0.125; - controls.lookVertical = true; - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function generateHeight(width, height) { - const data = [], - perlin = new ImprovedNoise(), - size = width * height, - z = Math.random() * 100; - - let quality = 2; - - for (let j = 0; j < 4; j++) { - if (j === 0) for (let i = 0; i < size; i++) data[i] = 0; - - for (let i = 0; i < size; i++) { - const x = i % width, - y = (i / width) | 0; - data[i] += perlin.noise(x / quality, y / quality, z) * quality; - } - - quality *= 4; - } - - return data; -} - -function getY(x, z) { - return (data[x + z * worldWidth] * 0.15) | 0; -} - -// - -function animate() { - timer.update(); - - render(); - stats.update(); -} - -function render() { - controls.update(timer.getDelta()); - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_geometry_nurbs.ts b/examples-testing/examples/webgl_geometry_nurbs.ts deleted file mode 100644 index ecd79c67e..000000000 --- a/examples-testing/examples/webgl_geometry_nurbs.ts +++ /dev/null @@ -1,298 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { NURBSCurve } from 'three/addons/curves/NURBSCurve.js'; -import { NURBSSurface } from 'three/addons/curves/NURBSSurface.js'; -import { NURBSVolume } from 'three/addons/curves/NURBSVolume.js'; -import { ParametricGeometry } from 'three/addons/geometries/ParametricGeometry.js'; - -let container, stats; - -let camera, scene, renderer; -let group; - -let targetRotation = 0; -let targetRotationOnPointerDown = 0; - -let pointerX = 0; -let pointerXOnPointerDown = 0; - -let windowHalfX = window.innerWidth / 2; - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 2000); - camera.position.set(0, 150, 750); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xf0f0f0); - - scene.add(new THREE.AmbientLight(0xffffff)); - - const light = new THREE.DirectionalLight(0xffffff, 3); - light.position.set(1, 1, 1); - scene.add(light); - - group = new THREE.Group(); - group.position.y = 50; - scene.add(group); - - // NURBS curve - - const nurbsControlPoints = []; - const nurbsKnots = []; - const nurbsDegree = 3; - - for (let i = 0; i <= nurbsDegree; i++) { - nurbsKnots.push(0); - } - - for (let i = 0, j = 20; i < j; i++) { - nurbsControlPoints.push( - new THREE.Vector4( - Math.random() * 400 - 200, - Math.random() * 400, - Math.random() * 400 - 200, - 1, // weight of control point: higher means stronger attraction - ), - ); - - const knot = (i + 1) / (j - nurbsDegree); - nurbsKnots.push(THREE.MathUtils.clamp(knot, 0, 1)); - } - - const nurbsCurve = new NURBSCurve(nurbsDegree, nurbsKnots, nurbsControlPoints); - - const nurbsGeometry = new THREE.BufferGeometry(); - nurbsGeometry.setFromPoints(nurbsCurve.getPoints(200)); - - const nurbsMaterial = new THREE.LineBasicMaterial({ color: 0x333333 }); - - const nurbsLine = new THREE.Line(nurbsGeometry, nurbsMaterial); - nurbsLine.position.set(0, -100, 0); - group.add(nurbsLine); - - const nurbsControlPointsGeometry = new THREE.BufferGeometry(); - nurbsControlPointsGeometry.setFromPoints(nurbsCurve.controlPoints); - - const nurbsControlPointsMaterial = new THREE.LineBasicMaterial({ - color: 0x333333, - opacity: 0.25, - transparent: true, - }); - - const nurbsControlPointsLine = new THREE.Line(nurbsControlPointsGeometry, nurbsControlPointsMaterial); - nurbsControlPointsLine.position.copy(nurbsLine.position); - group.add(nurbsControlPointsLine); - - // NURBS surface - { - const nsControlPoints = [ - [ - new THREE.Vector4(-200, -200, 100, 1), - new THREE.Vector4(-200, -100, -200, 1), - new THREE.Vector4(-200, 100, 250, 1), - new THREE.Vector4(-200, 200, -100, 1), - ], - [ - new THREE.Vector4(0, -200, 0, 1), - new THREE.Vector4(0, -100, -100, 5), - new THREE.Vector4(0, 100, 150, 5), - new THREE.Vector4(0, 200, 0, 1), - ], - [ - new THREE.Vector4(200, -200, -100, 1), - new THREE.Vector4(200, -100, 200, 1), - new THREE.Vector4(200, 100, -250, 1), - new THREE.Vector4(200, 200, 100, 1), - ], - ]; - const degree1 = 2; - const degree2 = 3; - const knots1 = [0, 0, 0, 1, 1, 1]; - const knots2 = [0, 0, 0, 0, 1, 1, 1, 1]; - const nurbsSurface = new NURBSSurface(degree1, degree2, knots1, knots2, nsControlPoints); - - const map = new THREE.TextureLoader().load('textures/uv_grid_opengl.jpg'); - map.wrapS = map.wrapT = THREE.RepeatWrapping; - map.anisotropy = 16; - map.colorSpace = THREE.SRGBColorSpace; - - function getSurfacePoint(u, v, target) { - return nurbsSurface.getPoint(u, v, target); - } - - const geometry = new ParametricGeometry(getSurfacePoint, 20, 20); - const material = new THREE.MeshLambertMaterial({ map: map, side: THREE.DoubleSide }); - const object = new THREE.Mesh(geometry, material); - object.position.set(-400, 100, 0); - object.scale.multiplyScalar(1); - group.add(object); - } - - // NURBS volume - { - const nsControlPoints = [ - [ - [new THREE.Vector4(-200, -200, -200, 1), new THREE.Vector4(-200, -200, 200, 1)], - [new THREE.Vector4(-200, -100, -200, 1), new THREE.Vector4(-200, -100, 200, 1)], - [new THREE.Vector4(-200, 100, -200, 1), new THREE.Vector4(-200, 100, 200, 1)], - [new THREE.Vector4(-200, 200, -200, 1), new THREE.Vector4(-200, 200, 200, 1)], - ], - [ - [new THREE.Vector4(0, -200, -200, 1), new THREE.Vector4(0, -200, 200, 1)], - [new THREE.Vector4(0, -100, -200, 1), new THREE.Vector4(0, -100, 200, 1)], - [new THREE.Vector4(0, 100, -200, 1), new THREE.Vector4(0, 100, 200, 1)], - [new THREE.Vector4(0, 200, -200, 1), new THREE.Vector4(0, 200, 200, 1)], - ], - [ - [new THREE.Vector4(200, -200, -200, 1), new THREE.Vector4(200, -200, 200, 1)], - [new THREE.Vector4(200, -100, 0, 1), new THREE.Vector4(200, -100, 100, 1)], - [new THREE.Vector4(200, 100, 0, 1), new THREE.Vector4(200, 100, 100, 1)], - [new THREE.Vector4(200, 200, 0, 1), new THREE.Vector4(200, 200, 100, 1)], - ], - ]; - const degree1 = 2; - const degree2 = 3; - const degree3 = 1; - const knots1 = [0, 0, 0, 1, 1, 1]; - const knots2 = [0, 0, 0, 0, 1, 1, 1, 1]; - const knots3 = [0, 0, 1, 1]; - const nurbsVolume = new NURBSVolume(degree1, degree2, degree3, knots1, knots2, knots3, nsControlPoints); - - const map = new THREE.TextureLoader().load('textures/uv_grid_opengl.jpg'); - map.wrapS = map.wrapT = THREE.RepeatWrapping; - map.anisotropy = 16; - map.colorSpace = THREE.SRGBColorSpace; - - // Since ParametricGeometry() only support bi-variate parametric geometries - // we create evaluation functions for different surfaces with one of the three - // parameter values (u, v, w) kept constant and create multiple THREE.Mesh - // objects one for each surface - function getSurfacePointFront(u, v, target) { - return nurbsVolume.getPoint(u, v, 0, target); - } - - function getSurfacePointMiddle(u, v, target) { - return nurbsVolume.getPoint(u, v, 0.5, target); - } - - function getSurfacePointBack(u, v, target) { - return nurbsVolume.getPoint(u, v, 1, target); - } - - function getSurfacePointTop(u, w, target) { - return nurbsVolume.getPoint(u, 1, w, target); - } - - function getSurfacePointSide(v, w, target) { - return nurbsVolume.getPoint(0, v, w, target); - } - - const geometryFront = new ParametricGeometry(getSurfacePointFront, 20, 20); - const materialFront = new THREE.MeshLambertMaterial({ map: map, side: THREE.DoubleSide }); - const objectFront = new THREE.Mesh(geometryFront, materialFront); - objectFront.position.set(400, 100, 0); - objectFront.scale.multiplyScalar(0.5); - group.add(objectFront); - - const geometryMiddle = new ParametricGeometry(getSurfacePointMiddle, 20, 20); - const materialMiddle = new THREE.MeshLambertMaterial({ map: map, side: THREE.DoubleSide }); - const objectMiddle = new THREE.Mesh(geometryMiddle, materialMiddle); - objectMiddle.position.set(400, 100, 0); - objectMiddle.scale.multiplyScalar(0.5); - group.add(objectMiddle); - - const geometryBack = new ParametricGeometry(getSurfacePointBack, 20, 20); - const materialBack = new THREE.MeshLambertMaterial({ map: map, side: THREE.DoubleSide }); - const objectBack = new THREE.Mesh(geometryBack, materialBack); - objectBack.position.set(400, 100, 0); - objectBack.scale.multiplyScalar(0.5); - group.add(objectBack); - - const geometryTop = new ParametricGeometry(getSurfacePointTop, 20, 20); - const materialTop = new THREE.MeshLambertMaterial({ map: map, side: THREE.DoubleSide }); - const objectTop = new THREE.Mesh(geometryTop, materialTop); - objectTop.position.set(400, 100, 0); - objectTop.scale.multiplyScalar(0.5); - group.add(objectTop); - - const geometrySide = new ParametricGeometry(getSurfacePointSide, 20, 20); - const materialSide = new THREE.MeshLambertMaterial({ map: map, side: THREE.DoubleSide }); - const objectSide = new THREE.Mesh(geometrySide, materialSide); - objectSide.position.set(400, 100, 0); - objectSide.scale.multiplyScalar(0.5); - group.add(objectSide); - } - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - stats = new Stats(); - container.appendChild(stats.dom); - - container.style.touchAction = 'none'; - container.addEventListener('pointerdown', onPointerDown); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - windowHalfX = window.innerWidth / 2; - - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function onPointerDown(event) { - if (event.isPrimary === false) return; - - pointerXOnPointerDown = event.clientX - windowHalfX; - targetRotationOnPointerDown = targetRotation; - - document.addEventListener('pointermove', onPointerMove); - document.addEventListener('pointerup', onPointerUp); -} - -function onPointerMove(event) { - if (event.isPrimary === false) return; - - pointerX = event.clientX - windowHalfX; - - targetRotation = targetRotationOnPointerDown + (pointerX - pointerXOnPointerDown) * 0.02; -} - -function onPointerUp(event) { - if (event.isPrimary === false) return; - - document.removeEventListener('pointermove', onPointerMove); - document.removeEventListener('pointerup', onPointerUp); -} - -// - -function animate() { - render(); - stats.update(); -} - -function render() { - group.rotation.y += (targetRotation - group.rotation.y) * 0.05; - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_geometry_shapes.ts b/examples-testing/examples/webgl_geometry_shapes.ts deleted file mode 100644 index 0c25d8855..000000000 --- a/examples-testing/examples/webgl_geometry_shapes.ts +++ /dev/null @@ -1,363 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -let container, stats; - -let camera, scene, renderer; - -let group; - -let targetRotation = 0; -let targetRotationOnPointerDown = 0; - -let pointerX = 0; -let pointerXOnPointerDown = 0; - -let windowHalfX = window.innerWidth / 2; - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xf0f0f0); - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(0, 150, 500); - scene.add(camera); - - const light = new THREE.PointLight(0xffffff, 2.5, 0, 0); - camera.add(light); - - group = new THREE.Group(); - group.position.y = 50; - scene.add(group); - - const loader = new THREE.TextureLoader(); - const texture = loader.load('textures/uv_grid_opengl.jpg'); - texture.colorSpace = THREE.SRGBColorSpace; - - // it's necessary to apply these settings in order to correctly display the texture on a shape geometry - - texture.wrapS = texture.wrapT = THREE.RepeatWrapping; - texture.repeat.set(0.008, 0.008); - - function addShape(shape, extrudeSettings, color, x, y, z, rx, ry, rz, s) { - // flat shape with texture - // note: default UVs generated by THREE.ShapeGeometry are simply the x- and y-coordinates of the vertices - - let geometry = new THREE.ShapeGeometry(shape); - - let mesh = new THREE.Mesh(geometry, new THREE.MeshPhongMaterial({ side: THREE.DoubleSide, map: texture })); - mesh.position.set(x, y, z - 175); - mesh.rotation.set(rx, ry, rz); - mesh.scale.set(s, s, s); - group.add(mesh); - - // flat shape - - geometry = new THREE.ShapeGeometry(shape); - - mesh = new THREE.Mesh(geometry, new THREE.MeshPhongMaterial({ color: color, side: THREE.DoubleSide })); - mesh.position.set(x, y, z - 125); - mesh.rotation.set(rx, ry, rz); - mesh.scale.set(s, s, s); - group.add(mesh); - - // extruded shape - - geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings); - - mesh = new THREE.Mesh(geometry, new THREE.MeshPhongMaterial({ color: color })); - mesh.position.set(x, y, z - 75); - mesh.rotation.set(rx, ry, rz); - mesh.scale.set(s, s, s); - group.add(mesh); - - addLineShape(shape, color, x, y, z, rx, ry, rz, s); - } - - function addLineShape(shape, color, x, y, z, rx, ry, rz, s) { - // lines - - shape.autoClose = true; - - const points = shape.getPoints(); - const spacedPoints = shape.getSpacedPoints(50); - - const geometryPoints = new THREE.BufferGeometry().setFromPoints(points); - const geometrySpacedPoints = new THREE.BufferGeometry().setFromPoints(spacedPoints); - - // solid line - - let line = new THREE.Line(geometryPoints, new THREE.LineBasicMaterial({ color: color })); - line.position.set(x, y, z - 25); - line.rotation.set(rx, ry, rz); - line.scale.set(s, s, s); - group.add(line); - - // line from equidistance sampled points - - line = new THREE.Line(geometrySpacedPoints, new THREE.LineBasicMaterial({ color: color })); - line.position.set(x, y, z + 25); - line.rotation.set(rx, ry, rz); - line.scale.set(s, s, s); - group.add(line); - - // vertices from real points - - let particles = new THREE.Points(geometryPoints, new THREE.PointsMaterial({ color: color, size: 4 })); - particles.position.set(x, y, z + 75); - particles.rotation.set(rx, ry, rz); - particles.scale.set(s, s, s); - group.add(particles); - - // equidistance sampled points - - particles = new THREE.Points(geometrySpacedPoints, new THREE.PointsMaterial({ color: color, size: 4 })); - particles.position.set(x, y, z + 125); - particles.rotation.set(rx, ry, rz); - particles.scale.set(s, s, s); - group.add(particles); - } - - // California - - const californiaPts = []; - - californiaPts.push(new THREE.Vector2(610, 320)); - californiaPts.push(new THREE.Vector2(450, 300)); - californiaPts.push(new THREE.Vector2(392, 392)); - californiaPts.push(new THREE.Vector2(266, 438)); - californiaPts.push(new THREE.Vector2(190, 570)); - californiaPts.push(new THREE.Vector2(190, 600)); - californiaPts.push(new THREE.Vector2(160, 620)); - californiaPts.push(new THREE.Vector2(160, 650)); - californiaPts.push(new THREE.Vector2(180, 640)); - californiaPts.push(new THREE.Vector2(165, 680)); - californiaPts.push(new THREE.Vector2(150, 670)); - californiaPts.push(new THREE.Vector2(90, 737)); - californiaPts.push(new THREE.Vector2(80, 795)); - californiaPts.push(new THREE.Vector2(50, 835)); - californiaPts.push(new THREE.Vector2(64, 870)); - californiaPts.push(new THREE.Vector2(60, 945)); - californiaPts.push(new THREE.Vector2(300, 945)); - californiaPts.push(new THREE.Vector2(300, 743)); - californiaPts.push(new THREE.Vector2(600, 473)); - californiaPts.push(new THREE.Vector2(626, 425)); - californiaPts.push(new THREE.Vector2(600, 370)); - californiaPts.push(new THREE.Vector2(610, 320)); - - for (let i = 0; i < californiaPts.length; i++) californiaPts[i].multiplyScalar(0.25); - - const californiaShape = new THREE.Shape(californiaPts); - - // Triangle - - const triangleShape = new THREE.Shape().moveTo(80, 20).lineTo(40, 80).lineTo(120, 80).lineTo(80, 20); // close path - - // Heart - - const x = 0, - y = 0; - - const heartShape = new THREE.Shape() - .moveTo(x + 25, y + 25) - .bezierCurveTo(x + 25, y + 25, x + 20, y, x, y) - .bezierCurveTo(x - 30, y, x - 30, y + 35, x - 30, y + 35) - .bezierCurveTo(x - 30, y + 55, x - 10, y + 77, x + 25, y + 95) - .bezierCurveTo(x + 60, y + 77, x + 80, y + 55, x + 80, y + 35) - .bezierCurveTo(x + 80, y + 35, x + 80, y, x + 50, y) - .bezierCurveTo(x + 35, y, x + 25, y + 25, x + 25, y + 25); - - // Square - - const sqLength = 80; - - const squareShape = new THREE.Shape() - .moveTo(0, 0) - .lineTo(0, sqLength) - .lineTo(sqLength, sqLength) - .lineTo(sqLength, 0) - .lineTo(0, 0); - - // Rounded rectangle - - const roundedRectShape = new THREE.Shape(); - - (function roundedRect(ctx, x, y, width, height, radius) { - ctx.moveTo(x, y + radius); - ctx.lineTo(x, y + height - radius); - ctx.quadraticCurveTo(x, y + height, x + radius, y + height); - ctx.lineTo(x + width - radius, y + height); - ctx.quadraticCurveTo(x + width, y + height, x + width, y + height - radius); - ctx.lineTo(x + width, y + radius); - ctx.quadraticCurveTo(x + width, y, x + width - radius, y); - ctx.lineTo(x + radius, y); - ctx.quadraticCurveTo(x, y, x, y + radius); - })(roundedRectShape, 0, 0, 50, 50, 20); - - // Track - - const trackShape = new THREE.Shape() - .moveTo(40, 40) - .lineTo(40, 160) - .absarc(60, 160, 20, Math.PI, 0, true) - .lineTo(80, 40) - .absarc(60, 40, 20, 2 * Math.PI, Math.PI, true); - - // Circle - - const circleRadius = 40; - const circleShape = new THREE.Shape() - .moveTo(0, circleRadius) - .quadraticCurveTo(circleRadius, circleRadius, circleRadius, 0) - .quadraticCurveTo(circleRadius, -circleRadius, 0, -circleRadius) - .quadraticCurveTo(-circleRadius, -circleRadius, -circleRadius, 0) - .quadraticCurveTo(-circleRadius, circleRadius, 0, circleRadius); - - // Fish - - const fishShape = new THREE.Shape() - .moveTo(x, y) - .quadraticCurveTo(x + 50, y - 80, x + 90, y - 10) - .quadraticCurveTo(x + 100, y - 10, x + 115, y - 40) - .quadraticCurveTo(x + 115, y, x + 115, y + 40) - .quadraticCurveTo(x + 100, y + 10, x + 90, y + 10) - .quadraticCurveTo(x + 50, y + 80, x, y); - - // Arc circle - - const arcShape = new THREE.Shape().moveTo(50, 10).absarc(10, 10, 40, 0, Math.PI * 2, false); - - const holePath = new THREE.Path().moveTo(20, 10).absarc(10, 10, 10, 0, Math.PI * 2, true); - - arcShape.holes.push(holePath); - - // Smiley - - const smileyShape = new THREE.Shape().moveTo(80, 40).absarc(40, 40, 40, 0, Math.PI * 2, false); - - const smileyEye1Path = new THREE.Path().moveTo(35, 20).absellipse(25, 20, 10, 10, 0, Math.PI * 2, true); - - const smileyEye2Path = new THREE.Path().moveTo(65, 20).absarc(55, 20, 10, 0, Math.PI * 2, true); - - const smileyMouthPath = new THREE.Path() - .moveTo(20, 40) - .quadraticCurveTo(40, 60, 60, 40) - .bezierCurveTo(70, 45, 70, 50, 60, 60) - .quadraticCurveTo(40, 80, 20, 60) - .quadraticCurveTo(5, 50, 20, 40); - - smileyShape.holes.push(smileyEye1Path); - smileyShape.holes.push(smileyEye2Path); - smileyShape.holes.push(smileyMouthPath); - - // Spline shape - - const splinepts = []; - splinepts.push(new THREE.Vector2(70, 20)); - splinepts.push(new THREE.Vector2(80, 90)); - splinepts.push(new THREE.Vector2(-30, 70)); - splinepts.push(new THREE.Vector2(0, 0)); - - const splineShape = new THREE.Shape().moveTo(0, 0).splineThru(splinepts); - - const extrudeSettings = { - depth: 8, - bevelEnabled: true, - bevelSegments: 2, - steps: 2, - bevelSize: 1, - bevelThickness: 1, - }; - - // addShape( shape, color, x, y, z, rx, ry,rz, s ); - - addShape(californiaShape, extrudeSettings, 0xf08000, -300, -100, 0, 0, 0, 0, 1); - addShape(triangleShape, extrudeSettings, 0x8080f0, -180, 0, 0, 0, 0, 0, 1); - addShape(roundedRectShape, extrudeSettings, 0x008000, -150, 150, 0, 0, 0, 0, 1); - addShape(trackShape, extrudeSettings, 0x008080, 200, -100, 0, 0, 0, 0, 1); - addShape(squareShape, extrudeSettings, 0x0040f0, 150, 100, 0, 0, 0, 0, 1); - addShape(heartShape, extrudeSettings, 0xf00000, 60, 100, 0, 0, 0, Math.PI, 1); - addShape(circleShape, extrudeSettings, 0x00f000, 120, 250, 0, 0, 0, 0, 1); - addShape(fishShape, extrudeSettings, 0x404040, -60, 200, 0, 0, 0, 0, 1); - addShape(smileyShape, extrudeSettings, 0xf000f0, -200, 250, 0, 0, 0, Math.PI, 1); - addShape(arcShape, extrudeSettings, 0x804000, 150, 0, 0, 0, 0, 0, 1); - addShape(splineShape, extrudeSettings, 0x808080, -50, -100, 0, 0, 0, 0, 1); - - addLineShape(arcShape.holes[0], 0x804000, 150, 0, 0, 0, 0, 0, 1); - - for (let i = 0; i < smileyShape.holes.length; i += 1) { - addLineShape(smileyShape.holes[i], 0xf000f0, -200, 250, 0, 0, 0, Math.PI, 1); - } - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - stats = new Stats(); - container.appendChild(stats.dom); - - container.style.touchAction = 'none'; - container.addEventListener('pointerdown', onPointerDown); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - windowHalfX = window.innerWidth / 2; - - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function onPointerDown(event) { - if (event.isPrimary === false) return; - - pointerXOnPointerDown = event.clientX - windowHalfX; - targetRotationOnPointerDown = targetRotation; - - document.addEventListener('pointermove', onPointerMove); - document.addEventListener('pointerup', onPointerUp); -} - -function onPointerMove(event) { - if (event.isPrimary === false) return; - - pointerX = event.clientX - windowHalfX; - - targetRotation = targetRotationOnPointerDown + (pointerX - pointerXOnPointerDown) * 0.02; -} - -function onPointerUp(event) { - if (event.isPrimary === false) return; - - document.removeEventListener('pointermove', onPointerMove); - document.removeEventListener('pointerup', onPointerUp); -} - -// - -function animate() { - render(); - stats.update(); -} - -function render() { - group.rotation.y += (targetRotation - group.rotation.y) * 0.05; - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_geometry_teapot.ts b/examples-testing/examples/webgl_geometry_teapot.ts deleted file mode 100644 index 2516d660c..000000000 --- a/examples-testing/examples/webgl_geometry_teapot.ts +++ /dev/null @@ -1,185 +0,0 @@ -import * as THREE from 'three'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { TeapotGeometry } from 'three/addons/geometries/TeapotGeometry.js'; - -let camera, scene, renderer; -let cameraControls; -let effectController; -const teapotSize = 300; -let ambientLight, light; - -let tess = -1; // force initialization -let bBottom; -let bLid; -let bBody; -let bFitLid; -let bNonBlinn; -let shading; - -let teapot, textureCube; -const materials = {}; - -init(); -render(); - -function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - const canvasWidth = window.innerWidth; - const canvasHeight = window.innerHeight; - - // CAMERA - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 80000); - camera.position.set(-600, 550, 1300); - - // LIGHTS - ambientLight = new THREE.AmbientLight(0x7c7c7c, 2.0); - - light = new THREE.DirectionalLight(0xffffff, 2.0); - light.position.set(0.32, 0.39, 0.7); - - // RENDERER - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(canvasWidth, canvasHeight); - container.appendChild(renderer.domElement); - - // EVENTS - window.addEventListener('resize', onWindowResize); - - // CONTROLS - cameraControls = new OrbitControls(camera, renderer.domElement); - cameraControls.addEventListener('change', render); - - // TEXTURE MAP - const textureMap = new THREE.TextureLoader().load('textures/uv_grid_opengl.jpg'); - textureMap.wrapS = textureMap.wrapT = THREE.RepeatWrapping; - textureMap.anisotropy = 16; - textureMap.colorSpace = THREE.SRGBColorSpace; - - // REFLECTION MAP - const path = 'textures/cube/pisa/'; - const urls = ['px.png', 'nx.png', 'py.png', 'ny.png', 'pz.png', 'nz.png']; - - textureCube = new THREE.CubeTextureLoader().setPath(path).load(urls); - - materials['wireframe'] = new THREE.MeshBasicMaterial({ wireframe: true }); - materials['flat'] = new THREE.MeshPhongMaterial({ specular: 0x000000, flatShading: true, side: THREE.DoubleSide }); - materials['smooth'] = new THREE.MeshLambertMaterial({ side: THREE.DoubleSide }); - materials['glossy'] = new THREE.MeshPhongMaterial({ - color: 0xc0c0c0, - specular: 0x404040, - shininess: 300, - side: THREE.DoubleSide, - }); - materials['textured'] = new THREE.MeshPhongMaterial({ map: textureMap, side: THREE.DoubleSide }); - materials['reflective'] = new THREE.MeshPhongMaterial({ envMap: textureCube, side: THREE.DoubleSide }); - - // scene itself - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xaaaaaa); - - scene.add(ambientLight); - scene.add(light); - - // GUI - setupGui(); -} - -// EVENT HANDLERS - -function onWindowResize() { - const canvasWidth = window.innerWidth; - const canvasHeight = window.innerHeight; - - renderer.setSize(canvasWidth, canvasHeight); - - camera.aspect = canvasWidth / canvasHeight; - camera.updateProjectionMatrix(); - - render(); -} - -function setupGui() { - effectController = { - newTess: 15, - bottom: true, - lid: true, - body: true, - fitLid: false, - nonblinn: false, - newShading: 'glossy', - }; - - const gui = new GUI(); - gui.add(effectController, 'newTess', [2, 3, 4, 5, 6, 8, 10, 15, 20, 30, 40, 50]) - .name('Tessellation Level') - .onChange(render); - gui.add(effectController, 'lid').name('display lid').onChange(render); - gui.add(effectController, 'body').name('display body').onChange(render); - gui.add(effectController, 'bottom').name('display bottom').onChange(render); - gui.add(effectController, 'fitLid').name('snug lid').onChange(render); - gui.add(effectController, 'nonblinn').name('original scale').onChange(render); - gui.add(effectController, 'newShading', ['wireframe', 'flat', 'smooth', 'glossy', 'textured', 'reflective']) - .name('Shading') - .onChange(render); -} - -// - -function render() { - if ( - effectController.newTess !== tess || - effectController.bottom !== bBottom || - effectController.lid !== bLid || - effectController.body !== bBody || - effectController.fitLid !== bFitLid || - effectController.nonblinn !== bNonBlinn || - effectController.newShading !== shading - ) { - tess = effectController.newTess; - bBottom = effectController.bottom; - bLid = effectController.lid; - bBody = effectController.body; - bFitLid = effectController.fitLid; - bNonBlinn = effectController.nonblinn; - shading = effectController.newShading; - - createNewTeapot(); - } - - // skybox is rendered separately, so that it is always behind the teapot. - if (shading === 'reflective') { - scene.background = textureCube; - } else { - scene.background = null; - } - - renderer.render(scene, camera); -} - -// Whenever the teapot changes, the scene is rebuilt from scratch (not much to it). -function createNewTeapot() { - if (teapot !== undefined) { - teapot.geometry.dispose(); - scene.remove(teapot); - } - - const geometry = new TeapotGeometry( - teapotSize, - tess, - effectController.bottom, - effectController.lid, - effectController.body, - effectController.fitLid, - !effectController.nonblinn, - ); - - teapot = new THREE.Mesh(geometry, materials[shading]); - - scene.add(teapot); -} diff --git a/examples-testing/examples/webgl_geometry_terrain.ts b/examples-testing/examples/webgl_geometry_terrain.ts deleted file mode 100644 index 55b4aa474..000000000 --- a/examples-testing/examples/webgl_geometry_terrain.ts +++ /dev/null @@ -1,174 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { FirstPersonControls } from 'three/addons/controls/FirstPersonControls.js'; -import { ImprovedNoise } from 'three/addons/math/ImprovedNoise.js'; - -let container, stats; -let camera, controls, scene, renderer; -let mesh, texture; - -const worldWidth = 256, - worldDepth = 256; -const timer = new THREE.Timer(); -timer.connect(document); - -init(); - -function init() { - container = document.getElementById('container'); - - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 10000); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xefd1b5); - scene.fog = new THREE.FogExp2(0xefd1b5, 0.0025); - - const data = generateHeight(worldWidth, worldDepth); - - camera.position.set(100, 800, -800); - camera.lookAt(-100, 810, -800); - - const geometry = new THREE.PlaneGeometry(7500, 7500, worldWidth - 1, worldDepth - 1); - geometry.rotateX(-Math.PI / 2); - - const vertices = geometry.attributes.position.array; - - for (let i = 0, j = 0, l = vertices.length; i < l; i++, j += 3) { - vertices[j + 1] = data[i] * 10; - } - - texture = new THREE.CanvasTexture(generateTexture(data, worldWidth, worldDepth)); - texture.wrapS = THREE.ClampToEdgeWrapping; - texture.wrapT = THREE.ClampToEdgeWrapping; - texture.colorSpace = THREE.SRGBColorSpace; - - mesh = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ map: texture })); - scene.add(mesh); - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - controls = new FirstPersonControls(camera, renderer.domElement); - controls.movementSpeed = 150; - controls.lookSpeed = 0.1; - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function generateHeight(width, height) { - let seed = Math.PI / 4; - window.Math.random = function () { - const x = Math.sin(seed++) * 10000; - return x - Math.floor(x); - }; - - const size = width * height, - data = new Uint8Array(size); - const perlin = new ImprovedNoise(), - z = Math.random() * 100; - - let quality = 1; - - for (let j = 0; j < 4; j++) { - for (let i = 0; i < size; i++) { - const x = i % width, - y = ~~(i / width); - data[i] += Math.abs(perlin.noise(x / quality, y / quality, z) * quality * 1.75); - } - - quality *= 5; - } - - return data; -} - -function generateTexture(data, width, height) { - let context, image, imageData, shade; - - const vector3 = new THREE.Vector3(0, 0, 0); - - const sun = new THREE.Vector3(1, 1, 1); - sun.normalize(); - - const canvas = document.createElement('canvas'); - canvas.width = width; - canvas.height = height; - - context = canvas.getContext('2d'); - context.fillStyle = '#000'; - context.fillRect(0, 0, width, height); - - image = context.getImageData(0, 0, canvas.width, canvas.height); - imageData = image.data; - - for (let i = 0, j = 0, l = imageData.length; i < l; i += 4, j++) { - vector3.x = data[j - 2] - data[j + 2]; - vector3.y = 2; - vector3.z = data[j - width * 2] - data[j + width * 2]; - vector3.normalize(); - - shade = vector3.dot(sun); - - imageData[i] = (96 + shade * 128) * (0.5 + data[j] * 0.007); - imageData[i + 1] = (32 + shade * 96) * (0.5 + data[j] * 0.007); - imageData[i + 2] = shade * 96 * (0.5 + data[j] * 0.007); - } - - context.putImageData(image, 0, 0); - - // Scaled 4x - - const canvasScaled = document.createElement('canvas'); - canvasScaled.width = width * 4; - canvasScaled.height = height * 4; - - context = canvasScaled.getContext('2d'); - context.scale(4, 4); - context.drawImage(canvas, 0, 0); - - image = context.getImageData(0, 0, canvasScaled.width, canvasScaled.height); - imageData = image.data; - - for (let i = 0, l = imageData.length; i < l; i += 4) { - const v = ~~(Math.random() * 5); - - imageData[i] += v; - imageData[i + 1] += v; - imageData[i + 2] += v; - } - - context.putImageData(image, 0, 0); - - return canvasScaled; -} - -// - -function animate() { - timer.update(); - - render(); - stats.update(); -} - -function render() { - controls.update(timer.getDelta()); - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_geometry_terrain_raycast.ts b/examples-testing/examples/webgl_geometry_terrain_raycast.ts deleted file mode 100644 index f1383c138..000000000 --- a/examples-testing/examples/webgl_geometry_terrain_raycast.ts +++ /dev/null @@ -1,206 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { ImprovedNoise } from 'three/addons/math/ImprovedNoise.js'; - -let container, stats; - -let camera, controls, scene, renderer; - -let mesh, texture; - -const worldWidth = 256, - worldDepth = 256, - worldHalfWidth = worldWidth / 2, - worldHalfDepth = worldDepth / 2; - -let helper; - -const raycaster = new THREE.Raycaster(); -const pointer = new THREE.Vector2(); - -init(); - -function init() { - container = document.getElementById('container'); - container.innerHTML = ''; - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xbfd1e5); - - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 10, 20000); - - controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 1000; - controls.maxDistance = 10000; - controls.maxPolarAngle = Math.PI / 2; - - // - - const data = generateHeight(worldWidth, worldDepth); - - controls.target.y = data[worldHalfWidth + worldHalfDepth * worldWidth] + 500; - camera.position.y = controls.target.y + 2000; - camera.position.x = 2000; - controls.update(); - - const geometry = new THREE.PlaneGeometry(7500, 7500, worldWidth - 1, worldDepth - 1); - geometry.rotateX(-Math.PI / 2); - - const vertices = geometry.attributes.position.array; - - for (let i = 0, j = 0, l = vertices.length; i < l; i++, j += 3) { - vertices[j + 1] = data[i] * 10; - } - - // - - texture = new THREE.CanvasTexture(generateTexture(data, worldWidth, worldDepth)); - texture.wrapS = THREE.ClampToEdgeWrapping; - texture.wrapT = THREE.ClampToEdgeWrapping; - texture.colorSpace = THREE.SRGBColorSpace; - - mesh = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ map: texture })); - scene.add(mesh); - - const geometryHelper = new THREE.ConeGeometry(20, 100, 3); - geometryHelper.translate(0, 50, 0); - geometryHelper.rotateX(Math.PI / 2); - helper = new THREE.Mesh(geometryHelper, new THREE.MeshNormalMaterial()); - scene.add(helper); - - container.addEventListener('pointermove', onPointerMove); - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function generateHeight(width, height) { - const size = width * height, - data = new Uint8Array(size), - perlin = new ImprovedNoise(), - z = Math.random() * 100; - - let quality = 1; - - for (let j = 0; j < 4; j++) { - for (let i = 0; i < size; i++) { - const x = i % width, - y = ~~(i / width); - data[i] += Math.abs(perlin.noise(x / quality, y / quality, z) * quality * 1.75); - } - - quality *= 5; - } - - return data; -} - -function generateTexture(data, width, height) { - // bake lighting into texture - - let context, image, imageData, shade; - - const vector3 = new THREE.Vector3(0, 0, 0); - - const sun = new THREE.Vector3(1, 1, 1); - sun.normalize(); - - const canvas = document.createElement('canvas'); - canvas.width = width; - canvas.height = height; - - context = canvas.getContext('2d'); - context.fillStyle = '#000'; - context.fillRect(0, 0, width, height); - - image = context.getImageData(0, 0, canvas.width, canvas.height); - imageData = image.data; - - for (let i = 0, j = 0, l = imageData.length; i < l; i += 4, j++) { - vector3.x = data[j - 2] - data[j + 2]; - vector3.y = 2; - vector3.z = data[j - width * 2] - data[j + width * 2]; - vector3.normalize(); - - shade = vector3.dot(sun); - - imageData[i] = (96 + shade * 128) * (0.5 + data[j] * 0.007); - imageData[i + 1] = (32 + shade * 96) * (0.5 + data[j] * 0.007); - imageData[i + 2] = shade * 96 * (0.5 + data[j] * 0.007); - } - - context.putImageData(image, 0, 0); - - // Scaled 4x - - const canvasScaled = document.createElement('canvas'); - canvasScaled.width = width * 4; - canvasScaled.height = height * 4; - - context = canvasScaled.getContext('2d'); - context.scale(4, 4); - context.drawImage(canvas, 0, 0); - - image = context.getImageData(0, 0, canvasScaled.width, canvasScaled.height); - imageData = image.data; - - for (let i = 0, l = imageData.length; i < l; i += 4) { - const v = ~~(Math.random() * 5); - - imageData[i] += v; - imageData[i + 1] += v; - imageData[i + 2] += v; - } - - context.putImageData(image, 0, 0); - - return canvasScaled; -} - -// - -function animate() { - render(); - stats.update(); -} - -function render() { - renderer.render(scene, camera); -} - -function onPointerMove(event) { - pointer.x = (event.clientX / renderer.domElement.clientWidth) * 2 - 1; - pointer.y = -(event.clientY / renderer.domElement.clientHeight) * 2 + 1; - raycaster.setFromCamera(pointer, camera); - - // See if the ray from the camera into the world hits one of our meshes - const intersects = raycaster.intersectObject(mesh); - - // Toggle rotation bool for meshes that we clicked - if (intersects.length > 0) { - helper.position.set(0, 0, 0); - helper.lookAt(intersects[0].face.normal); - - helper.position.copy(intersects[0].point); - } -} diff --git a/examples-testing/examples/webgl_geometry_text.ts b/examples-testing/examples/webgl_geometry_text.ts deleted file mode 100644 index 1a9d00d06..000000000 --- a/examples-testing/examples/webgl_geometry_text.ts +++ /dev/null @@ -1,312 +0,0 @@ -import * as THREE from 'three'; - -import { FontLoader } from 'three/addons/loaders/FontLoader.js'; -import { TextGeometry } from 'three/addons/geometries/TextGeometry.js'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -THREE.Cache.enabled = true; - -let container; - -let camera, cameraTarget, scene, renderer; - -let group, textMesh1, textMesh2, textGeo, materials; - -let firstLetter = true; - -let text = 'three.js', - bevelEnabled = true, - font = undefined, - fontName = 'optimer', // helvetiker, optimer, gentilis, droid sans, droid serif - fontWeight = 'bold'; // normal bold - -const depth = 20, - size = 70, - hover = 30, - curveSegments = 4, - bevelThickness = 2, - bevelSize = 1.5; - -const mirror = true; - -const fontMap = { - helvetiker: 0, - optimer: 1, - gentilis: 2, - 'droid/droid_sans': 3, - 'droid/droid_serif': 4, -}; - -const weightMap = { - regular: 0, - bold: 1, -}; - -const reverseFontMap = []; -const reverseWeightMap = []; - -for (const i in fontMap) reverseFontMap[fontMap[i]] = i; -for (const i in weightMap) reverseWeightMap[weightMap[i]] = i; - -let targetRotation = 0; -let targetRotationOnPointerDown = 0; - -let pointerX = 0; -let pointerXOnPointerDown = 0; - -let windowHalfX = window.innerWidth / 2; - -let fontIndex = 1; - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - // CAMERA - - camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 1500); - camera.position.set(0, 400, 700); - - cameraTarget = new THREE.Vector3(0, 150, 0); - - // SCENE - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x000000); - scene.fog = new THREE.Fog(0x000000, 250, 1400); - - // LIGHTS - - const dirLight = new THREE.DirectionalLight(0xffffff, 0.4); - dirLight.position.set(0, 0, 1).normalize(); - scene.add(dirLight); - - const pointLight = new THREE.PointLight(0xffffff, 4.5, 0, 0); - pointLight.color.setHSL(Math.random(), 1, 0.5); - pointLight.position.set(0, 100, 90); - scene.add(pointLight); - - materials = [ - new THREE.MeshPhongMaterial({ color: 0xffffff, flatShading: true }), // front - new THREE.MeshPhongMaterial({ color: 0xffffff }), // side - ]; - - group = new THREE.Group(); - group.position.y = 100; - - scene.add(group); - - loadFont(); - - const plane = new THREE.Mesh( - new THREE.PlaneGeometry(10000, 10000), - new THREE.MeshBasicMaterial({ color: 0xffffff, opacity: 0.5, transparent: true }), - ); - plane.position.y = 100; - plane.rotation.x = -Math.PI / 2; - scene.add(plane); - - // RENDERER - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - // EVENTS - - container.style.touchAction = 'none'; - container.addEventListener('pointerdown', onPointerDown); - - document.addEventListener('keypress', onDocumentKeyPress); - document.addEventListener('keydown', onDocumentKeyDown); - - // - - const params = { - changeColor: function () { - pointLight.color.setHSL(Math.random(), 1, 0.5); - }, - changeFont: function () { - fontIndex++; - - fontName = reverseFontMap[fontIndex % reverseFontMap.length]; - - loadFont(); - }, - changeWeight: function () { - if (fontWeight === 'bold') { - fontWeight = 'regular'; - } else { - fontWeight = 'bold'; - } - - loadFont(); - }, - changeBevel: function () { - bevelEnabled = !bevelEnabled; - - refreshText(); - }, - }; - - // - - const gui = new GUI(); - - gui.add(params, 'changeColor').name('change color'); - gui.add(params, 'changeFont').name('change font'); - gui.add(params, 'changeWeight').name('change weight'); - gui.add(params, 'changeBevel').name('change bevel'); - gui.open(); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - windowHalfX = window.innerWidth / 2; - - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function onDocumentKeyDown(event) { - if (firstLetter) { - firstLetter = false; - text = ''; - } - - const keyCode = event.keyCode; - - // backspace - - if (keyCode == 8) { - event.preventDefault(); - - text = text.substring(0, text.length - 1); - refreshText(); - - return false; - } -} - -function onDocumentKeyPress(event) { - const keyCode = event.which; - - // backspace - - if (keyCode == 8) { - event.preventDefault(); - } else { - const ch = String.fromCharCode(keyCode); - text += ch; - - refreshText(); - } -} - -function loadFont() { - const loader = new FontLoader(); - loader.load('fonts/' + fontName + '_' + fontWeight + '.typeface.json', function (response) { - font = response; - - refreshText(); - }); -} - -function createText() { - textGeo = new TextGeometry(text, { - font: font, - - size: size, - depth: depth, - curveSegments: curveSegments, - - bevelThickness: bevelThickness, - bevelSize: bevelSize, - bevelEnabled: bevelEnabled, - }); - - textGeo.computeBoundingBox(); - - const centerOffset = -0.5 * (textGeo.boundingBox.max.x - textGeo.boundingBox.min.x); - - textMesh1 = new THREE.Mesh(textGeo, materials); - - textMesh1.position.x = centerOffset; - textMesh1.position.y = hover; - textMesh1.position.z = 0; - - textMesh1.rotation.x = 0; - textMesh1.rotation.y = Math.PI * 2; - - group.add(textMesh1); - - if (mirror) { - textMesh2 = new THREE.Mesh(textGeo, materials); - - textMesh2.position.x = centerOffset; - textMesh2.position.y = -hover; - textMesh2.position.z = depth; - - textMesh2.rotation.x = Math.PI; - textMesh2.rotation.y = Math.PI * 2; - - group.add(textMesh2); - } -} - -function refreshText() { - group.remove(textMesh1); - if (mirror) group.remove(textMesh2); - - if (!text) return; - - createText(); -} - -function onPointerDown(event) { - if (event.isPrimary === false) return; - - pointerXOnPointerDown = event.clientX - windowHalfX; - targetRotationOnPointerDown = targetRotation; - - document.addEventListener('pointermove', onPointerMove); - document.addEventListener('pointerup', onPointerUp); -} - -function onPointerMove(event) { - if (event.isPrimary === false) return; - - pointerX = event.clientX - windowHalfX; - - targetRotation = targetRotationOnPointerDown + (pointerX - pointerXOnPointerDown) * 0.02; -} - -function onPointerUp(event) { - if (event.isPrimary === false) return; - - document.removeEventListener('pointermove', onPointerMove); - document.removeEventListener('pointerup', onPointerUp); -} - -// - -function animate() { - group.rotation.y += (targetRotation - group.rotation.y) * 0.05; - - camera.lookAt(cameraTarget); - - renderer.clear(); - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_geometry_text_shapes.ts b/examples-testing/examples/webgl_geometry_text_shapes.ts deleted file mode 100644 index d9633cd15..000000000 --- a/examples-testing/examples/webgl_geometry_text_shapes.ts +++ /dev/null @@ -1,112 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { FontLoader } from 'three/addons/loaders/FontLoader.js'; - -let camera, scene, renderer; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000); - camera.position.set(0, -400, 600); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xf0f0f0); - - const loader = new FontLoader(); - loader.load('fonts/helvetiker_regular.typeface.json', function (font) { - const color = 0x006699; - - const matDark = new THREE.LineBasicMaterial({ - color: color, - side: THREE.DoubleSide, - }); - - const matLite = new THREE.MeshBasicMaterial({ - color: color, - transparent: true, - opacity: 0.4, - side: THREE.DoubleSide, - }); - - const message = ' Three.js\nSimple text.'; - - const shapes = font.generateShapes(message, 100); - - const geometry = new THREE.ShapeGeometry(shapes); - - geometry.computeBoundingBox(); - - const xMid = -0.5 * (geometry.boundingBox.max.x - geometry.boundingBox.min.x); - - geometry.translate(xMid, 0, 0); - - // make shape ( N.B. edge view not visible ) - - const text = new THREE.Mesh(geometry, matLite); - text.position.z = -150; - scene.add(text); - - // make line shape ( N.B. edge view remains visible ) - - const holeShapes = []; - - for (let i = 0; i < shapes.length; i++) { - const shape = shapes[i]; - - if (shape.holes && shape.holes.length > 0) { - for (let j = 0; j < shape.holes.length; j++) { - const hole = shape.holes[j]; - holeShapes.push(hole); - } - } - } - - shapes.push(...holeShapes); - - const lineText = new THREE.Object3D(); - - for (let i = 0; i < shapes.length; i++) { - const shape = shapes[i]; - - const points = shape.getPoints(); - const geometry = new THREE.BufferGeometry().setFromPoints(points); - - geometry.translate(xMid, 0, 0); - - const lineMesh = new THREE.Line(geometry, matDark); - lineText.add(lineMesh); - } - - scene.add(lineText); - - render(); - }); //end load function - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - document.body.appendChild(renderer.domElement); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, 0, 0); - controls.update(); - - controls.addEventListener('change', render); - - window.addEventListener('resize', onWindowResize); -} // end init - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - render(); -} - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_geometry_text_stroke.ts b/examples-testing/examples/webgl_geometry_text_stroke.ts deleted file mode 100644 index 373892c74..000000000 --- a/examples-testing/examples/webgl_geometry_text_stroke.ts +++ /dev/null @@ -1,152 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { SVGLoader } from 'three/addons/loaders/SVGLoader.js'; -import { Font } from 'three/addons/loaders/FontLoader.js'; -import { unzipSync, strFromU8 } from 'three/addons/libs/fflate.module.js'; - -let camera, scene, renderer; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000); - camera.position.set(0, -400, 1000); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xf0f0f0); - - new THREE.FileLoader() - .setResponseType('arraybuffer') - .load('fonts/MPLUSRounded1c/MPLUSRounded1c-Regular.typeface.json.zip', function (data) { - const zip = unzipSync(new Uint8Array(data)); - const strArray = strFromU8(new Uint8Array(zip['MPLUSRounded1c-Regular.typeface.json'].buffer)); - - const font = new Font(JSON.parse(strArray)); - const color = new THREE.Color(0x006699); - - const matDark = new THREE.MeshBasicMaterial({ - color: color, - side: THREE.DoubleSide, - }); - - const matLite = new THREE.MeshBasicMaterial({ - color: color, - transparent: true, - opacity: 0.4, - side: THREE.DoubleSide, - }); - - const material = { - dark: matDark, - lite: matLite, - color: color, - }; - - const english = ' Three.js\nStroke text.'; // Left to right - - const hebrew = 'טקסט קו'; // Right to left - - const chinese = '文字描邊'; // Top to bottom - - const message1 = generateStrokeText(font, material, english, 80, 'ltr'); - - const message2 = generateStrokeText(font, material, hebrew, 80, 'rtl'); - - const message3 = generateStrokeText(font, material, chinese, 80, 'tb'); - - message1.position.x = -100; - - message2.position.x = -100; - message2.position.y = -300; - - message3.position.x = 300; - message3.position.y = -300; - - scene.add(message1, message2, message3); - - render(); - }); //end load function - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - document.body.appendChild(renderer.domElement); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, 0, 0); - controls.update(); - - controls.addEventListener('change', render); - - window.addEventListener('resize', onWindowResize); -} // end init - -function generateStrokeText(font, material, message, size, direction = 'ltr') { - const shapes = font.generateShapes(message, size, direction); - - const geometry = new THREE.ShapeGeometry(shapes); - - const strokeText = new THREE.Group(); - - geometry.computeBoundingBox(); - - const xMid = -0.5 * (geometry.boundingBox.max.x - geometry.boundingBox.min.x); - - geometry.translate(xMid, 0, 0); - - // make shape ( N.B. edge view not visible ) - - const text = new THREE.Mesh(geometry, material.lite); - - text.position.z = -150; - - strokeText.add(text); - - // make line shape ( N.B. edge view remains visible ) - - const holeShapes = []; - - for (let i = 0; i < shapes.length; i++) { - const shape = shapes[i]; - - if (shape.holes && shape.holes.length > 0) { - for (let j = 0; j < shape.holes.length; j++) { - const hole = shape.holes[j]; - holeShapes.push(hole); - } - } - } - - shapes.push(...holeShapes); - - const style = SVGLoader.getStrokeStyle(5, material.color.getStyle()); - - for (let i = 0; i < shapes.length; i++) { - const shape = shapes[i]; - - const points = shape.getPoints(); - - const geometry = SVGLoader.pointsToStroke(points, style); - - geometry.translate(xMid, 0, 0); - - const strokeMesh = new THREE.Mesh(geometry, material.dark); - strokeText.add(strokeMesh); - } - - return strokeText; -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - render(); -} - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_gpgpu_birds.ts b/examples-testing/examples/webgl_gpgpu_birds.ts deleted file mode 100644 index 20a5e0d97..000000000 --- a/examples-testing/examples/webgl_gpgpu_birds.ts +++ /dev/null @@ -1,313 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { GPUComputationRenderer } from 'three/addons/misc/GPUComputationRenderer.js'; - -/* TEXTURE WIDTH FOR SIMULATION */ -const WIDTH = 32; - -const BIRDS = WIDTH * WIDTH; - -// Custom Geometry - using 3 triangles each. No UVs, no normals currently. -class BirdGeometry extends THREE.BufferGeometry { - constructor() { - super(); - - const trianglesPerBird = 3; - const triangles = BIRDS * trianglesPerBird; - const points = triangles * 3; - - const vertices = new THREE.BufferAttribute(new Float32Array(points * 3), 3); - const birdColors = new THREE.BufferAttribute(new Float32Array(points * 3), 3); - const references = new THREE.BufferAttribute(new Float32Array(points * 2), 2); - const birdVertex = new THREE.BufferAttribute(new Float32Array(points), 1); - - this.setAttribute('position', vertices); - this.setAttribute('birdColor', birdColors); - this.setAttribute('reference', references); - this.setAttribute('birdVertex', birdVertex); - - // this.setAttribute( 'normal', new Float32Array( points * 3 ), 3 ); - - let v = 0; - - function verts_push() { - for (let i = 0; i < arguments.length; i++) { - vertices.array[v++] = arguments[i]; - } - } - - const wingsSpan = 20; - - for (let f = 0; f < BIRDS; f++) { - // Body - - verts_push(0, -0, -20, 0, 4, -20, 0, 0, 30); - - // Wings - - verts_push(0, 0, -15, -wingsSpan, 0, 0, 0, 0, 15); - - verts_push(0, 0, 15, wingsSpan, 0, 0, 0, 0, -15); - } - - for (let v = 0; v < triangles * 3; v++) { - const triangleIndex = ~~(v / 3); - const birdIndex = ~~(triangleIndex / trianglesPerBird); - const x = (birdIndex % WIDTH) / WIDTH; - const y = ~~(birdIndex / WIDTH) / WIDTH; - - const c = new THREE.Color(0x666666 + (~~(v / 9) / BIRDS) * 0x666666); - - birdColors.array[v * 3 + 0] = c.r; - birdColors.array[v * 3 + 1] = c.g; - birdColors.array[v * 3 + 2] = c.b; - - references.array[v * 2] = x; - references.array[v * 2 + 1] = y; - - birdVertex.array[v] = v % 9; - } - - this.scale(0.2, 0.2, 0.2); - } -} - -// - -let container, stats; -let camera, scene, renderer; -let mouseX = 0, - mouseY = 0; - -let windowHalfX = window.innerWidth / 2; -let windowHalfY = window.innerHeight / 2; - -const BOUNDS = 800, - BOUNDS_HALF = BOUNDS / 2; - -let last = performance.now(); - -let gpuCompute; -let velocityVariable; -let positionVariable; -let positionUniforms; -let velocityUniforms; -let birdUniforms; - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 3000); - camera.position.z = 350; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xffffff); - scene.fog = new THREE.Fog(0xffffff, 100, 1000); - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - initComputeRenderer(); - - stats = new Stats(); - container.appendChild(stats.dom); - - container.style.touchAction = 'none'; - container.addEventListener('pointermove', onPointerMove); - - // - - window.addEventListener('resize', onWindowResize); - - const gui = new GUI(); - - const effectController = { - separation: 20.0, - alignment: 20.0, - cohesion: 20.0, - freedom: 0.75, - }; - - const valuesChanger = function () { - velocityUniforms['separationDistance'].value = effectController.separation; - velocityUniforms['alignmentDistance'].value = effectController.alignment; - velocityUniforms['cohesionDistance'].value = effectController.cohesion; - velocityUniforms['freedomFactor'].value = effectController.freedom; - }; - - valuesChanger(); - - gui.add(effectController, 'separation', 0.0, 100.0, 1.0).onChange(valuesChanger); - gui.add(effectController, 'alignment', 0.0, 100, 0.001).onChange(valuesChanger); - gui.add(effectController, 'cohesion', 0.0, 100, 0.025).onChange(valuesChanger); - gui.close(); - - initBirds(); -} - -function initComputeRenderer() { - gpuCompute = new GPUComputationRenderer(WIDTH, WIDTH, renderer); - - const dtPosition = gpuCompute.createTexture(); - const dtVelocity = gpuCompute.createTexture(); - fillPositionTexture(dtPosition); - fillVelocityTexture(dtVelocity); - - velocityVariable = gpuCompute.addVariable( - 'textureVelocity', - document.getElementById('fragmentShaderVelocity').textContent, - dtVelocity, - ); - positionVariable = gpuCompute.addVariable( - 'texturePosition', - document.getElementById('fragmentShaderPosition').textContent, - dtPosition, - ); - - gpuCompute.setVariableDependencies(velocityVariable, [positionVariable, velocityVariable]); - gpuCompute.setVariableDependencies(positionVariable, [positionVariable, velocityVariable]); - - positionUniforms = positionVariable.material.uniforms; - velocityUniforms = velocityVariable.material.uniforms; - - positionUniforms['time'] = { value: 0.0 }; - positionUniforms['delta'] = { value: 0.0 }; - velocityUniforms['time'] = { value: 1.0 }; - velocityUniforms['delta'] = { value: 0.0 }; - velocityUniforms['testing'] = { value: 1.0 }; - velocityUniforms['separationDistance'] = { value: 1.0 }; - velocityUniforms['alignmentDistance'] = { value: 1.0 }; - velocityUniforms['cohesionDistance'] = { value: 1.0 }; - velocityUniforms['freedomFactor'] = { value: 1.0 }; - velocityUniforms['predator'] = { value: new THREE.Vector3() }; - velocityVariable.material.defines.BOUNDS = BOUNDS.toFixed(2); - - velocityVariable.wrapS = THREE.RepeatWrapping; - velocityVariable.wrapT = THREE.RepeatWrapping; - positionVariable.wrapS = THREE.RepeatWrapping; - positionVariable.wrapT = THREE.RepeatWrapping; - - const error = gpuCompute.init(); - - if (error !== null) { - console.error(error); - } -} - -function initBirds() { - const geometry = new BirdGeometry(); - - // For Vertex and Fragment - birdUniforms = { - color: { value: new THREE.Color(0xff2200) }, - texturePosition: { value: null }, - textureVelocity: { value: null }, - time: { value: 1.0 }, - delta: { value: 0.0 }, - }; - - // THREE.ShaderMaterial - const material = new THREE.ShaderMaterial({ - uniforms: birdUniforms, - vertexShader: document.getElementById('birdVS').textContent, - fragmentShader: document.getElementById('birdFS').textContent, - side: THREE.DoubleSide, - }); - - const birdMesh = new THREE.Mesh(geometry, material); - birdMesh.rotation.y = Math.PI / 2; - birdMesh.matrixAutoUpdate = false; - birdMesh.updateMatrix(); - - scene.add(birdMesh); -} - -function fillPositionTexture(texture) { - const theArray = texture.image.data; - - for (let k = 0, kl = theArray.length; k < kl; k += 4) { - const x = Math.random() * BOUNDS - BOUNDS_HALF; - const y = Math.random() * BOUNDS - BOUNDS_HALF; - const z = Math.random() * BOUNDS - BOUNDS_HALF; - - theArray[k + 0] = x; - theArray[k + 1] = y; - theArray[k + 2] = z; - theArray[k + 3] = 1; - } -} - -function fillVelocityTexture(texture) { - const theArray = texture.image.data; - - for (let k = 0, kl = theArray.length; k < kl; k += 4) { - const x = Math.random() - 0.5; - const y = Math.random() - 0.5; - const z = Math.random() - 0.5; - - theArray[k + 0] = x * 10; - theArray[k + 1] = y * 10; - theArray[k + 2] = z * 10; - theArray[k + 3] = 1; - } -} - -function onWindowResize() { - windowHalfX = window.innerWidth / 2; - windowHalfY = window.innerHeight / 2; - - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function onPointerMove(event) { - if (event.isPrimary === false) return; - - mouseX = event.clientX - windowHalfX; - mouseY = event.clientY - windowHalfY; -} - -// - -function animate() { - render(); - stats.update(); -} - -function render() { - const now = performance.now(); - let delta = (now - last) / 1000; - - if (delta > 1) delta = 1; // safety cap on large deltas - last = now; - - positionUniforms['time'].value = now; - positionUniforms['delta'].value = delta; - velocityUniforms['time'].value = now; - velocityUniforms['delta'].value = delta; - birdUniforms['time'].value = now; - birdUniforms['delta'].value = delta; - - velocityUniforms['predator'].value.set((0.5 * mouseX) / windowHalfX, (-0.5 * mouseY) / windowHalfY, 0); - - mouseX = 10000; - mouseY = 10000; - - gpuCompute.compute(); - - birdUniforms['texturePosition'].value = gpuCompute.getCurrentRenderTarget(positionVariable).texture; - birdUniforms['textureVelocity'].value = gpuCompute.getCurrentRenderTarget(velocityVariable).texture; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_gpgpu_birds_gltf.ts b/examples-testing/examples/webgl_gpgpu_birds_gltf.ts deleted file mode 100644 index 05f81a869..000000000 --- a/examples-testing/examples/webgl_gpgpu_birds_gltf.ts +++ /dev/null @@ -1,413 +0,0 @@ -import * as THREE from 'three'; -import Stats from 'three/addons/libs/stats.module.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { GPUComputationRenderer } from 'three/addons/misc/GPUComputationRenderer.js'; - -/* TEXTURE WIDTH FOR SIMULATION */ -const WIDTH = 64; -const BIRDS = WIDTH * WIDTH; - -/* BAKE ANIMATION INTO TEXTURE and CREATE GEOMETRY FROM BASE MODEL */ -const BirdGeometry = new THREE.BufferGeometry(); -let textureAnimation, durationAnimation, birdMesh, materialShader, indicesPerBird; - -function nextPowerOf2(n) { - return Math.pow(2, Math.ceil(Math.log(n) / Math.log(2))); -} - -const gltfs = ['models/gltf/Parrot.glb', 'models/gltf/Flamingo.glb']; -const colors = [0xccffff, 0xffdeff]; -const sizes = [0.2, 0.1]; -const selectModel = Math.floor(Math.random() * gltfs.length); -new GLTFLoader().load(gltfs[selectModel], function (gltf) { - const animations = gltf.animations; - durationAnimation = Math.round(animations[0].duration * 60); - const birdGeo = gltf.scene.children[0].geometry; - const morphAttributes = birdGeo.morphAttributes.position; - const tHeight = nextPowerOf2(durationAnimation); - const tWidth = nextPowerOf2(birdGeo.getAttribute('position').count); - indicesPerBird = birdGeo.index.count; - const tData = new Float32Array(4 * tWidth * tHeight); - - for (let i = 0; i < tWidth; i++) { - for (let j = 0; j < tHeight; j++) { - const offset = j * tWidth * 4; - - const curMorph = Math.floor((j / durationAnimation) * morphAttributes.length); - const nextMorph = - (Math.floor((j / durationAnimation) * morphAttributes.length) + 1) % morphAttributes.length; - const lerpAmount = ((j / durationAnimation) * morphAttributes.length) % 1; - - if (j < durationAnimation) { - let d0, d1; - - d0 = morphAttributes[curMorph].array[i * 3]; - d1 = morphAttributes[nextMorph].array[i * 3]; - - if (d0 !== undefined && d1 !== undefined) - tData[offset + i * 4] = THREE.MathUtils.lerp(d0, d1, lerpAmount); - - d0 = morphAttributes[curMorph].array[i * 3 + 1]; - d1 = morphAttributes[nextMorph].array[i * 3 + 1]; - - if (d0 !== undefined && d1 !== undefined) - tData[offset + i * 4 + 1] = THREE.MathUtils.lerp(d0, d1, lerpAmount); - - d0 = morphAttributes[curMorph].array[i * 3 + 2]; - d1 = morphAttributes[nextMorph].array[i * 3 + 2]; - - if (d0 !== undefined && d1 !== undefined) - tData[offset + i * 4 + 2] = THREE.MathUtils.lerp(d0, d1, lerpAmount); - - tData[offset + i * 4 + 3] = 1; - } - } - } - - textureAnimation = new THREE.DataTexture(tData, tWidth, tHeight, THREE.RGBAFormat, THREE.FloatType); - textureAnimation.needsUpdate = true; - - const vertices = [], - color = [], - reference = [], - seeds = [], - indices = []; - const totalVertices = birdGeo.getAttribute('position').count * 3 * BIRDS; - for (let i = 0; i < totalVertices; i++) { - const bIndex = i % (birdGeo.getAttribute('position').count * 3); - vertices.push(birdGeo.getAttribute('position').array[bIndex]); - color.push(birdGeo.getAttribute('color').array[bIndex]); - } - - let r = Math.random(); - for (let i = 0; i < birdGeo.getAttribute('position').count * BIRDS; i++) { - const bIndex = i % birdGeo.getAttribute('position').count; - const bird = Math.floor(i / birdGeo.getAttribute('position').count); - if (bIndex == 0) r = Math.random(); - const j = ~~bird; - const x = (j % WIDTH) / WIDTH; - const y = ~~(j / WIDTH) / WIDTH; - reference.push(x, y, bIndex / tWidth, durationAnimation / tHeight); - seeds.push(bird, r, Math.random(), Math.random()); - } - - for (let i = 0; i < birdGeo.index.array.length * BIRDS; i++) { - const offset = Math.floor(i / birdGeo.index.array.length) * birdGeo.getAttribute('position').count; - indices.push(birdGeo.index.array[i % birdGeo.index.array.length] + offset); - } - - BirdGeometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(vertices), 3)); - BirdGeometry.setAttribute('birdColor', new THREE.BufferAttribute(new Float32Array(color), 3)); - BirdGeometry.setAttribute('color', new THREE.BufferAttribute(new Float32Array(color), 3)); - BirdGeometry.setAttribute('reference', new THREE.BufferAttribute(new Float32Array(reference), 4)); - BirdGeometry.setAttribute('seeds', new THREE.BufferAttribute(new Float32Array(seeds), 4)); - - BirdGeometry.setIndex(indices); - - init(); -}); - -let container, stats; -let camera, scene, renderer; -let mouseX = 0, - mouseY = 0; - -let windowHalfX = window.innerWidth / 2; -let windowHalfY = window.innerHeight / 2; - -const BOUNDS = 800, - BOUNDS_HALF = BOUNDS / 2; - -let last = performance.now(); - -let gpuCompute; -let velocityVariable; -let positionVariable; -let positionUniforms; -let velocityUniforms; - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 3000); - camera.position.z = 350; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(colors[selectModel]); - scene.fog = new THREE.Fog(colors[selectModel], 100, 1000); - - // LIGHTS - - const hemiLight = new THREE.HemisphereLight(colors[selectModel], 0xffffff, 4.5); - hemiLight.color.setHSL(0.6, 1, 0.6, THREE.SRGBColorSpace); - hemiLight.groundColor.setHSL(0.095, 1, 0.75, THREE.SRGBColorSpace); - hemiLight.position.set(0, 50, 0); - scene.add(hemiLight); - - const dirLight = new THREE.DirectionalLight(0x00ced1, 2.0); - dirLight.color.setHSL(0.1, 1, 0.95, THREE.SRGBColorSpace); - dirLight.position.set(-1, 1.75, 1); - dirLight.position.multiplyScalar(30); - scene.add(dirLight); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - initComputeRenderer(); - - stats = new Stats(); - container.appendChild(stats.dom); - - container.style.touchAction = 'none'; - container.addEventListener('pointermove', onPointerMove); - - window.addEventListener('resize', onWindowResize); - - const gui = new GUI(); - - const effectController = { - separation: 20.0, - alignment: 20.0, - cohesion: 20.0, - freedom: 0.75, - size: sizes[selectModel], - count: Math.floor(BIRDS / 4), - }; - - const valuesChanger = function () { - velocityUniforms['separationDistance'].value = effectController.separation; - velocityUniforms['alignmentDistance'].value = effectController.alignment; - velocityUniforms['cohesionDistance'].value = effectController.cohesion; - velocityUniforms['freedomFactor'].value = effectController.freedom; - if (materialShader) materialShader.uniforms['size'].value = effectController.size; - BirdGeometry.setDrawRange(0, indicesPerBird * effectController.count); - }; - - valuesChanger(); - - gui.add(effectController, 'separation', 0.0, 100.0, 1.0).onChange(valuesChanger); - gui.add(effectController, 'alignment', 0.0, 100, 0.001).onChange(valuesChanger); - gui.add(effectController, 'cohesion', 0.0, 100, 0.025).onChange(valuesChanger); - gui.add(effectController, 'size', 0, 1, 0.01).onChange(valuesChanger); - gui.add(effectController, 'count', 0, BIRDS, 1).onChange(valuesChanger); - gui.close(); - - initBirds(effectController); -} - -function initComputeRenderer() { - gpuCompute = new GPUComputationRenderer(WIDTH, WIDTH, renderer); - - const dtPosition = gpuCompute.createTexture(); - const dtVelocity = gpuCompute.createTexture(); - fillPositionTexture(dtPosition); - fillVelocityTexture(dtVelocity); - - velocityVariable = gpuCompute.addVariable( - 'textureVelocity', - document.getElementById('fragmentShaderVelocity').textContent, - dtVelocity, - ); - positionVariable = gpuCompute.addVariable( - 'texturePosition', - document.getElementById('fragmentShaderPosition').textContent, - dtPosition, - ); - - gpuCompute.setVariableDependencies(velocityVariable, [positionVariable, velocityVariable]); - gpuCompute.setVariableDependencies(positionVariable, [positionVariable, velocityVariable]); - - positionUniforms = positionVariable.material.uniforms; - velocityUniforms = velocityVariable.material.uniforms; - - positionUniforms['time'] = { value: 0.0 }; - positionUniforms['delta'] = { value: 0.0 }; - velocityUniforms['time'] = { value: 1.0 }; - velocityUniforms['delta'] = { value: 0.0 }; - velocityUniforms['testing'] = { value: 1.0 }; - velocityUniforms['separationDistance'] = { value: 1.0 }; - velocityUniforms['alignmentDistance'] = { value: 1.0 }; - velocityUniforms['cohesionDistance'] = { value: 1.0 }; - velocityUniforms['freedomFactor'] = { value: 1.0 }; - velocityUniforms['predator'] = { value: new THREE.Vector3() }; - velocityVariable.material.defines.BOUNDS = BOUNDS.toFixed(2); - - velocityVariable.wrapS = THREE.RepeatWrapping; - velocityVariable.wrapT = THREE.RepeatWrapping; - positionVariable.wrapS = THREE.RepeatWrapping; - positionVariable.wrapT = THREE.RepeatWrapping; - - const error = gpuCompute.init(); - - if (error !== null) { - console.error(error); - } -} - -function initBirds(effectController) { - const geometry = BirdGeometry; - - const m = new THREE.MeshStandardMaterial({ - vertexColors: true, - flatShading: true, - roughness: 1, - metalness: 0, - }); - - m.onBeforeCompile = shader => { - shader.uniforms.texturePosition = { value: null }; - shader.uniforms.textureVelocity = { value: null }; - shader.uniforms.textureAnimation = { value: textureAnimation }; - shader.uniforms.time = { value: 1.0 }; - shader.uniforms.size = { value: effectController.size }; - shader.uniforms.delta = { value: 0.0 }; - - let token = '#define STANDARD'; - - let insert = /* glsl */ ` - attribute vec4 reference; - attribute vec4 seeds; - attribute vec3 birdColor; - uniform sampler2D texturePosition; - uniform sampler2D textureVelocity; - uniform sampler2D textureAnimation; - uniform float size; - uniform float time; - `; - - shader.vertexShader = shader.vertexShader.replace(token, token + insert); - - token = '#include '; - - insert = /* glsl */ ` - vec4 tmpPos = texture2D( texturePosition, reference.xy ); - - vec3 pos = tmpPos.xyz; - vec3 velocity = normalize(texture2D( textureVelocity, reference.xy ).xyz); - vec3 aniPos = texture2D( textureAnimation, vec2( reference.z, mod( time + ( seeds.x ) * ( ( 0.0004 + seeds.y / 10000.0) + normalize( velocity ) / 20000.0 ), reference.w ) ) ).xyz; - vec3 newPosition = position; - - newPosition = mat3( modelMatrix ) * ( newPosition + aniPos ); - newPosition *= size + seeds.y * size * 0.2; - - velocity.z *= -1.; - float xz = length( velocity.xz ); - float xyz = 1.; - float x = sqrt( 1. - velocity.y * velocity.y ); - - float cosry = velocity.x / xz; - float sinry = velocity.z / xz; - - float cosrz = x / xyz; - float sinrz = velocity.y / xyz; - - mat3 maty = mat3( cosry, 0, -sinry, 0 , 1, 0 , sinry, 0, cosry ); - mat3 matz = mat3( cosrz , sinrz, 0, -sinrz, cosrz, 0, 0 , 0 , 1 ); - - newPosition = maty * matz * newPosition; - newPosition += pos; - - vec3 transformed = vec3( newPosition ); - `; - - shader.vertexShader = shader.vertexShader.replace(token, insert); - - materialShader = shader; - }; - - birdMesh = new THREE.Mesh(geometry, m); - birdMesh.rotation.y = Math.PI / 2; - - birdMesh.castShadow = true; - birdMesh.receiveShadow = true; - - scene.add(birdMesh); -} - -function fillPositionTexture(texture) { - const theArray = texture.image.data; - - for (let k = 0, kl = theArray.length; k < kl; k += 4) { - const x = Math.random() * BOUNDS - BOUNDS_HALF; - const y = Math.random() * BOUNDS - BOUNDS_HALF; - const z = Math.random() * BOUNDS - BOUNDS_HALF; - - theArray[k + 0] = x; - theArray[k + 1] = y; - theArray[k + 2] = z; - theArray[k + 3] = 1; - } -} - -function fillVelocityTexture(texture) { - const theArray = texture.image.data; - - for (let k = 0, kl = theArray.length; k < kl; k += 4) { - const x = Math.random() - 0.5; - const y = Math.random() - 0.5; - const z = Math.random() - 0.5; - - theArray[k + 0] = x * 10; - theArray[k + 1] = y * 10; - theArray[k + 2] = z * 10; - theArray[k + 3] = 1; - } -} - -function onWindowResize() { - windowHalfX = window.innerWidth / 2; - windowHalfY = window.innerHeight / 2; - - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function onPointerMove(event) { - if (event.isPrimary === false) return; - - mouseX = event.clientX - windowHalfX; - mouseY = event.clientY - windowHalfY; -} - -// - -function animate() { - render(); - stats.update(); -} - -function render() { - const now = performance.now(); - let delta = (now - last) / 1000; - - if (delta > 1) delta = 1; // safety cap on large deltas - last = now; - - positionUniforms['time'].value = now; - positionUniforms['delta'].value = delta; - velocityUniforms['time'].value = now; - velocityUniforms['delta'].value = delta; - if (materialShader) materialShader.uniforms['time'].value = now / 1000; - if (materialShader) materialShader.uniforms['delta'].value = delta; - - velocityUniforms['predator'].value.set((0.5 * mouseX) / windowHalfX, (-0.5 * mouseY) / windowHalfY, 0); - - mouseX = 10000; - mouseY = 10000; - - gpuCompute.compute(); - - if (materialShader) - materialShader.uniforms['texturePosition'].value = gpuCompute.getCurrentRenderTarget(positionVariable).texture; - if (materialShader) - materialShader.uniforms['textureVelocity'].value = gpuCompute.getCurrentRenderTarget(velocityVariable).texture; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_gpgpu_protoplanet.ts b/examples-testing/examples/webgl_gpgpu_protoplanet.ts deleted file mode 100644 index 30444ddba..000000000 --- a/examples-testing/examples/webgl_gpgpu_protoplanet.ts +++ /dev/null @@ -1,280 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GPUComputationRenderer } from 'three/addons/misc/GPUComputationRenderer.js'; - -// Texture width for simulation (each texel is a debris particle) -const WIDTH = 64; - -let container, stats; -let camera, scene, renderer, geometry; - -const PARTICLES = WIDTH * WIDTH; - -let gpuCompute; -let velocityVariable; -let positionVariable; -let velocityUniforms; -let particleUniforms; -let effectController; - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 5, 15000); - camera.position.y = 120; - camera.position.z = 400; - - scene = new THREE.Scene(); - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 100; - controls.maxDistance = 1000; - - effectController = { - // Can be changed dynamically - gravityConstant: 100.0, - density: 0.45, - - // Must restart simulation - radius: 300, - height: 8, - exponent: 0.4, - maxMass: 15.0, - velocity: 70, - velocityExponent: 0.2, - randVelocity: 0.001, - }; - - initComputeRenderer(); - - stats = new Stats(); - container.appendChild(stats.dom); - - window.addEventListener('resize', onWindowResize); - - initGUI(); - - initProtoplanets(); - - dynamicValuesChanger(); -} - -function initComputeRenderer() { - gpuCompute = new GPUComputationRenderer(WIDTH, WIDTH, renderer); - - const dtPosition = gpuCompute.createTexture(); - const dtVelocity = gpuCompute.createTexture(); - - fillTextures(dtPosition, dtVelocity); - - velocityVariable = gpuCompute.addVariable( - 'textureVelocity', - document.getElementById('computeShaderVelocity').textContent, - dtVelocity, - ); - positionVariable = gpuCompute.addVariable( - 'texturePosition', - document.getElementById('computeShaderPosition').textContent, - dtPosition, - ); - - gpuCompute.setVariableDependencies(velocityVariable, [positionVariable, velocityVariable]); - gpuCompute.setVariableDependencies(positionVariable, [positionVariable, velocityVariable]); - - velocityUniforms = velocityVariable.material.uniforms; - - velocityUniforms['gravityConstant'] = { value: 0.0 }; - velocityUniforms['density'] = { value: 0.0 }; - - const error = gpuCompute.init(); - - if (error !== null) { - console.error(error); - } -} - -function restartSimulation() { - const dtPosition = gpuCompute.createTexture(); - const dtVelocity = gpuCompute.createTexture(); - - fillTextures(dtPosition, dtVelocity); - - gpuCompute.renderTexture(dtPosition, positionVariable.renderTargets[0]); - gpuCompute.renderTexture(dtPosition, positionVariable.renderTargets[1]); - gpuCompute.renderTexture(dtVelocity, velocityVariable.renderTargets[0]); - gpuCompute.renderTexture(dtVelocity, velocityVariable.renderTargets[1]); -} - -function initProtoplanets() { - geometry = new THREE.BufferGeometry(); - - const positions = new Float32Array(PARTICLES * 3); - let p = 0; - - for (let i = 0; i < PARTICLES; i++) { - positions[p++] = (Math.random() * 2 - 1) * effectController.radius; - positions[p++] = 0; //( Math.random() * 2 - 1 ) * effectController.radius; - positions[p++] = (Math.random() * 2 - 1) * effectController.radius; - } - - const uvs = new Float32Array(PARTICLES * 2); - p = 0; - - for (let j = 0; j < WIDTH; j++) { - for (let i = 0; i < WIDTH; i++) { - uvs[p++] = i / (WIDTH - 1); - uvs[p++] = j / (WIDTH - 1); - } - } - - geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); - geometry.setAttribute('uv', new THREE.BufferAttribute(uvs, 2)); - - particleUniforms = { - texturePosition: { value: null }, - textureVelocity: { value: null }, - cameraConstant: { value: getCameraConstant(camera) }, - density: { value: 0.0 }, - }; - - // THREE.ShaderMaterial - const material = new THREE.ShaderMaterial({ - uniforms: particleUniforms, - vertexShader: document.getElementById('particleVertexShader').textContent, - fragmentShader: document.getElementById('particleFragmentShader').textContent, - }); - - const particles = new THREE.Points(geometry, material); - particles.matrixAutoUpdate = false; - particles.updateMatrix(); - - scene.add(particles); -} - -function fillTextures(texturePosition, textureVelocity) { - const posArray = texturePosition.image.data; - const velArray = textureVelocity.image.data; - - const radius = effectController.radius; - const height = effectController.height; - const exponent = effectController.exponent; - const maxMass = (effectController.maxMass * 1024) / PARTICLES; - const maxVel = effectController.velocity; - const velExponent = effectController.velocityExponent; - const randVel = effectController.randVelocity; - - for (let k = 0, kl = posArray.length; k < kl; k += 4) { - // Position - let x, z, rr; - - do { - x = Math.random() * 2 - 1; - z = Math.random() * 2 - 1; - rr = x * x + z * z; - } while (rr > 1); - - rr = Math.sqrt(rr); - - const rExp = radius * Math.pow(rr, exponent); - - // Velocity - const vel = maxVel * Math.pow(rr, velExponent); - - const vx = vel * z + (Math.random() * 2 - 1) * randVel; - const vy = (Math.random() * 2 - 1) * randVel * 0.05; - const vz = -vel * x + (Math.random() * 2 - 1) * randVel; - - x *= rExp; - z *= rExp; - const y = (Math.random() * 2 - 1) * height; - - const mass = Math.random() * maxMass + 1; - - // Fill in texture values - posArray[k + 0] = x; - posArray[k + 1] = y; - posArray[k + 2] = z; - posArray[k + 3] = 1; - - velArray[k + 0] = vx; - velArray[k + 1] = vy; - velArray[k + 2] = vz; - velArray[k + 3] = mass; - } -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - particleUniforms['cameraConstant'].value = getCameraConstant(camera); -} - -function dynamicValuesChanger() { - velocityUniforms['gravityConstant'].value = effectController.gravityConstant; - velocityUniforms['density'].value = effectController.density; - particleUniforms['density'].value = effectController.density; -} - -function initGUI() { - const gui = new GUI({ width: 280 }); - - const folder1 = gui.addFolder('Dynamic parameters'); - - folder1.add(effectController, 'gravityConstant', 0.0, 1000.0, 0.05).onChange(dynamicValuesChanger); - folder1.add(effectController, 'density', 0.0, 10.0, 0.001).onChange(dynamicValuesChanger); - - const folder2 = gui.addFolder('Static parameters'); - - folder2.add(effectController, 'radius', 10.0, 1000.0, 1.0); - folder2.add(effectController, 'height', 0.0, 50.0, 0.01); - folder2.add(effectController, 'exponent', 0.0, 2.0, 0.001); - folder2.add(effectController, 'maxMass', 1.0, 50.0, 0.1); - folder2.add(effectController, 'velocity', 0.0, 150.0, 0.1); - folder2.add(effectController, 'velocityExponent', 0.0, 1.0, 0.01); - folder2.add(effectController, 'randVelocity', 0.0, 50.0, 0.1); - - const buttonRestart = { - restartSimulation: function () { - restartSimulation(); - }, - }; - - folder2.add(buttonRestart, 'restartSimulation'); - - folder1.open(); - folder2.open(); -} - -function getCameraConstant(camera) { - return window.innerHeight / (Math.tan(THREE.MathUtils.DEG2RAD * 0.5 * camera.fov) / camera.zoom); -} - -function animate() { - render(); - stats.update(); -} - -function render() { - gpuCompute.compute(); - - particleUniforms['texturePosition'].value = gpuCompute.getCurrentRenderTarget(positionVariable).texture; - particleUniforms['textureVelocity'].value = gpuCompute.getCurrentRenderTarget(velocityVariable).texture; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_gpgpu_water.ts b/examples-testing/examples/webgl_gpgpu_water.ts deleted file mode 100644 index 3210a298a..000000000 --- a/examples-testing/examples/webgl_gpgpu_water.ts +++ /dev/null @@ -1,583 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { GPUComputationRenderer } from 'three/addons/misc/GPUComputationRenderer.js'; -import { SimplexNoise } from 'three/addons/math/SimplexNoise.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; -import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; - -// Texture width for simulation -const WIDTH = 128; - -// Water size in system units -const BOUNDS = 6; -const BOUNDS_HALF = BOUNDS * 0.5; - -let tmpHeightmap = null; -const tmpQuat = new THREE.Quaternion(); -const tmpQuatX = new THREE.Quaternion(); -const tmpQuatZ = new THREE.Quaternion(); -let duckModel = null; - -let container, stats; -let camera, scene, renderer, controls; -let mousedown = false; -const mouseCoords = new THREE.Vector2(); -const raycaster = new THREE.Raycaster(); - -let sun; -let waterMesh; -let poolBorder; -let meshRay; -let gpuCompute; -let heightmapVariable; -// let smoothShader; -let readWaterLevelShader; -let readWaterLevelRenderTarget; -let readWaterLevelImage; -const waterNormal = new THREE.Vector3(); - -const NUM_DUCK = 12; -const ducks = []; -let ducksEnabled = true; - -const simplex = new SimplexNoise(); - -let frame = 0; - -const effectController = { - mouseSize: 0.2, - mouseDeep: 0.01, - viscosity: 0.93, - speed: 5, - ducksEnabled: ducksEnabled, - wireframe: false, - shadow: false, -}; - -init(); - -async function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); - camera.position.set(0, 2.0, 4); - camera.lookAt(0, 0, 0); - - scene = new THREE.Scene(); - - sun = new THREE.DirectionalLight(0xffffff, 4.0); - sun.position.set(-1, 2.6, 1.4); - scene.add(sun); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = 0.5; - container.appendChild(renderer.domElement); - - controls = new OrbitControls(camera, container); - - stats = new Stats(); - container.appendChild(stats.dom); - - container.style.touchAction = 'none'; - container.addEventListener('pointermove', onPointerMove); - container.addEventListener('pointerdown', onPointerDown); - container.addEventListener('pointerup', onPointerUp); - - window.addEventListener('resize', onWindowResize); - - const hdrLoader = new HDRLoader().setPath('./textures/equirectangular/'); - const glbloader = new GLTFLoader().setPath('models/gltf/'); - glbloader.setDRACOLoader(new DRACOLoader().setDecoderPath('jsm/libs/draco/gltf/')); - - const [env, model] = await Promise.all([ - hdrLoader.loadAsync('blouberg_sunrise_2_1k.hdr'), - glbloader.loadAsync('duck.glb'), - ]); - env.mapping = THREE.EquirectangularReflectionMapping; - scene.environment = env; - scene.background = env; - scene.backgroundBlurriness = 0.3; - scene.environmentIntensity = 1.25; - - duckModel = model.scene.children[0]; - duckModel.receiveShadow = true; - duckModel.castShadow = true; - - const gui = new GUI(); - gui.domElement.style.right = '0px'; - - const valuesChanger = function () { - heightmapVariable.material.uniforms['mouseSize'].value = effectController.mouseSize; - heightmapVariable.material.uniforms['deep'].value = effectController.mouseDeep; - heightmapVariable.material.uniforms['viscosity'].value = effectController.viscosity; - ducksEnabled = effectController.ducksEnabled; - - let i = NUM_DUCK; - while (i--) { - if (ducks[i]) ducks[i].visible = ducksEnabled; - } - }; - - gui.add(effectController, 'mouseSize', 0.1, 1.0, 0.1).onChange(valuesChanger); - gui.add(effectController, 'mouseDeep', 0.01, 1.0, 0.01).onChange(valuesChanger); - gui.add(effectController, 'viscosity', 0.9, 0.999, 0.001).onChange(valuesChanger); - gui.add(effectController, 'speed', 1, 6, 1); - gui.add(effectController, 'ducksEnabled').onChange(valuesChanger); - gui.add(effectController, 'wireframe').onChange(v => { - waterMesh.material.wireframe = v; - poolBorder.material.wireframe = v; - }); - gui.add(effectController, 'shadow').onChange(addShadow); - - //const buttonSmooth = { smoothWater: function () {smoothWater();} }; - //gui.add( buttonSmooth, 'smoothWater' ); - - initWater(); - - createducks(); - - valuesChanger(); - - renderer.setAnimationLoop(animate); -} - -function initWater() { - const geometry = new THREE.PlaneGeometry(BOUNDS, BOUNDS, WIDTH - 1, WIDTH - 1); - - const material = new WaterMaterial({ - color: 0x9bd2ec, - metalness: 0.9, - roughness: 0, - transparent: true, - opacity: 0.8, - side: THREE.DoubleSide, - }); - - waterMesh = new THREE.Mesh(geometry, material); - waterMesh.rotation.x = -Math.PI * 0.5; - waterMesh.matrixAutoUpdate = false; - waterMesh.updateMatrix(); - - waterMesh.receiveShadow = true; - waterMesh.castShadow = true; - - scene.add(waterMesh); - - // pool border - const borderGeom = new THREE.TorusGeometry(4.2, 0.1, 12, 4); - borderGeom.rotateX(Math.PI * 0.5); - borderGeom.rotateY(Math.PI * 0.25); - poolBorder = new THREE.Mesh(borderGeom, new THREE.MeshStandardMaterial({ color: 0x908877, roughness: 0.2 })); - scene.add(poolBorder); - poolBorder.receiveShadow = true; - poolBorder.castShadow = true; - - // THREE.Mesh just for mouse raycasting - const geometryRay = new THREE.PlaneGeometry(BOUNDS, BOUNDS, 1, 1); - meshRay = new THREE.Mesh(geometryRay, new THREE.MeshBasicMaterial({ color: 0xffffff, visible: false })); - meshRay.rotation.x = -Math.PI / 2; - meshRay.matrixAutoUpdate = false; - meshRay.updateMatrix(); - scene.add(meshRay); - - // Creates the gpu computation class and sets it up - - gpuCompute = new GPUComputationRenderer(WIDTH, WIDTH, renderer); - - const heightmap0 = gpuCompute.createTexture(); - - fillTexture(heightmap0); - - heightmapVariable = gpuCompute.addVariable('heightmap', shaderChange.heightmap_frag, heightmap0); - - gpuCompute.setVariableDependencies(heightmapVariable, [heightmapVariable]); - - heightmapVariable.material.uniforms['mousePos'] = { value: new THREE.Vector2(10000, 10000) }; - heightmapVariable.material.uniforms['mouseSize'] = { value: 0.2 }; - heightmapVariable.material.uniforms['viscosity'] = { value: 0.93 }; - heightmapVariable.material.uniforms['deep'] = { value: 0.01 }; - heightmapVariable.material.defines.BOUNDS = BOUNDS.toFixed(1); - - const error = gpuCompute.init(); - if (error !== null) console.error(error); - - // Create compute shader to smooth the water surface and velocity - //smoothShader = gpuCompute.createShaderMaterial( document.getElementById( 'smoothFragmentShader' ).textContent, { smoothTexture: { value: null } } ); - - // Create compute shader to read water level - readWaterLevelShader = gpuCompute.createShaderMaterial( - document.getElementById('readWaterLevelFragmentShader').textContent, - { - point1: { value: new THREE.Vector2() }, - levelTexture: { value: null }, - }, - ); - readWaterLevelShader.defines.WIDTH = WIDTH.toFixed(1); - readWaterLevelShader.defines.BOUNDS = BOUNDS.toFixed(1); - - // Create a 4x1 pixel image and a render target (Uint8, 4 channels, 1 byte per channel) to read water height and orientation - readWaterLevelImage = new Uint8Array(4 * 1 * 4); - - readWaterLevelRenderTarget = new THREE.WebGLRenderTarget(4, 1, { - wrapS: THREE.ClampToEdgeWrapping, - wrapT: THREE.ClampToEdgeWrapping, - minFilter: THREE.NearestFilter, - magFilter: THREE.NearestFilter, - format: THREE.RGBAFormat, - type: THREE.UnsignedByteType, - depthBuffer: false, - }); -} - -function fillTexture(texture) { - const waterMaxHeight = 0.1; - - function noise(x, y) { - let multR = waterMaxHeight; - let mult = 0.025; - let r = 0; - for (let i = 0; i < 15; i++) { - r += multR * simplex.noise(x * mult, y * mult); - multR *= 0.53 + 0.025 * i; - mult *= 1.25; - } - - return r; - } - - const pixels = texture.image.data; - - let p = 0; - for (let j = 0; j < WIDTH; j++) { - for (let i = 0; i < WIDTH; i++) { - const x = (i * 128) / WIDTH; - const y = (j * 128) / WIDTH; - - pixels[p + 0] = noise(x, y); - pixels[p + 1] = pixels[p + 0]; - pixels[p + 2] = 0; - pixels[p + 3] = 1; - - p += 4; - } - } -} - -function addShadow(v) { - renderer.shadowMap.enabled = v; - sun.castShadow = v; - - if (v) { - renderer.shadowMap.type = THREE.VSMShadowMap; - const shadow = sun.shadow; - shadow.mapSize.width = shadow.mapSize.height = 2048; - shadow.radius = 2; - shadow.bias = -0.0005; - const shadowCam = shadow.camera, - s = 5; - shadowCam.near = 0.1; - shadowCam.far = 6; - shadowCam.right = shadowCam.top = s; - shadowCam.left = shadowCam.bottom = -s; - } else { - if (sun.shadow) sun.shadow.dispose(); - } - - // debug shadow - //scene.add( new THREE.CameraHelper(shadowCam) ); -} - -// function smoothWater() { - -// const currentRenderTarget = gpuCompute.getCurrentRenderTarget( heightmapVariable ); -// const alternateRenderTarget = gpuCompute.getAlternateRenderTarget( heightmapVariable ); - -// for ( let i = 0; i < 10; i ++ ) { - -// smoothShader.uniforms[ 'smoothTexture' ].value = currentRenderTarget.texture; -// gpuCompute.doRenderTarget( smoothShader, alternateRenderTarget ); - -// smoothShader.uniforms[ 'smoothTexture' ].value = alternateRenderTarget.texture; -// gpuCompute.doRenderTarget( smoothShader, currentRenderTarget ); - -// } - -// } - -function createducks() { - for (let i = 0; i < NUM_DUCK; i++) { - let sphere = duckModel; - if (i < NUM_DUCK - 1) { - sphere = duckModel.clone(); - } - - sphere.position.x = (Math.random() - 0.5) * BOUNDS * 0.7; - sphere.position.z = (Math.random() - 0.5) * BOUNDS * 0.7; - - sphere.userData.velocity = new THREE.Vector3(); - - scene.add(sphere); - - ducks[i] = sphere; - } -} - -function duckDynamics() { - readWaterLevelShader.uniforms['levelTexture'].value = tmpHeightmap; - - for (let i = 0; i < NUM_DUCK; i++) { - const sphere = ducks[i]; - - if (sphere) { - // Read water level and orientation - const u = (0.5 * sphere.position.x) / BOUNDS_HALF + 0.5; - const v = 1 - ((0.5 * sphere.position.z) / BOUNDS_HALF + 0.5); - readWaterLevelShader.uniforms['point1'].value.set(u, v); - gpuCompute.doRenderTarget(readWaterLevelShader, readWaterLevelRenderTarget); - - renderer.readRenderTargetPixels(readWaterLevelRenderTarget, 0, 0, 4, 1, readWaterLevelImage); - const pixels = new Float32Array(readWaterLevelImage.buffer); - - // Get orientation - waterNormal.set(pixels[1], 0, -pixels[2]); - - const pos = sphere.position; - - const startPos = pos.clone(); - - // Set height - pos.y = pixels[0]; - - // Move sphere - waterNormal.multiplyScalar(0.01); - sphere.userData.velocity.add(waterNormal); - sphere.userData.velocity.multiplyScalar(0.998); - pos.add(sphere.userData.velocity); - - const decal = 0.001; - const limit = BOUNDS_HALF - 0.2; - - if (pos.x < -limit) { - pos.x = -limit + decal; - sphere.userData.velocity.x *= -0.3; - } else if (pos.x > limit) { - pos.x = limit - decal; - sphere.userData.velocity.x *= -0.3; - } - - if (pos.z < -limit) { - pos.z = -limit + decal; - sphere.userData.velocity.z *= -0.3; - } else if (pos.z > limit) { - pos.z = limit - decal; - sphere.userData.velocity.z *= -0.3; - } - - // duck orientation test - - const startNormal = new THREE.Vector3(pixels[1], 1, -pixels[2]).normalize(); - - const dir = startPos.sub(pos); - dir.y = 0; - dir.normalize(); - - const yAxis = new THREE.Vector3(0, 1, 0); - const zAxis = new THREE.Vector3(0, 0, -1); - tmpQuatX.setFromUnitVectors(zAxis, dir); - tmpQuatZ.setFromUnitVectors(yAxis, startNormal); - tmpQuat.multiplyQuaternions(tmpQuatZ, tmpQuatX); - sphere.quaternion.slerp(tmpQuat, 0.017); - } - } -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function onPointerDown() { - mousedown = true; -} - -function onPointerUp() { - mousedown = false; - controls.enabled = true; -} - -function onPointerMove(event) { - const dom = renderer.domElement; - mouseCoords.set((event.clientX / dom.clientWidth) * 2 - 1, -(event.clientY / dom.clientHeight) * 2 + 1); -} - -function raycast() { - // Set uniforms: mouse interaction - const uniforms = heightmapVariable.material.uniforms; - if (mousedown) { - raycaster.setFromCamera(mouseCoords, camera); - - const intersects = raycaster.intersectObject(meshRay); - - if (intersects.length > 0) { - const point = intersects[0].point; - uniforms['mousePos'].value.set(point.x, point.z); - if (controls.enabled) controls.enabled = false; - } else { - uniforms['mousePos'].value.set(10000, 10000); - } - } else { - uniforms['mousePos'].value.set(10000, 10000); - } -} - -function animate() { - render(); - stats.update(); -} - -function render() { - raycast(); - - frame++; - - if (frame >= 7 - effectController.speed) { - // Do the gpu computation - gpuCompute.compute(); - tmpHeightmap = gpuCompute.getCurrentRenderTarget(heightmapVariable).texture; - - if (ducksEnabled) duckDynamics(); - - // Get compute output in custom uniform - if (waterMesh) waterMesh.material.heightmap = tmpHeightmap; - - frame = 0; - } - - // Render - renderer.render(scene, camera); -} - -//---------------------- - -class WaterMaterial extends THREE.MeshStandardMaterial { - constructor(parameters) { - super(); - - this.defines = { - STANDARD: '', - USE_UV: '', - WIDTH: WIDTH.toFixed(1), - BOUNDS: BOUNDS.toFixed(1), - }; - - this.extra = {}; - - this.addParameter('heightmap', null); - - this.setValues(parameters); - } - - addParameter(name, value) { - this.extra[name] = value; - Object.defineProperty(this, name, { - get: () => this.extra[name], - set: v => { - this.extra[name] = v; - if (this.userData.shader) this.userData.shader.uniforms[name].value = this.extra[name]; - }, - }); - } - - onBeforeCompile(shader) { - for (const name in this.extra) { - shader.uniforms[name] = { value: this.extra[name] }; - } - - shader.vertexShader = shader.vertexShader.replace('#include ', shaderChange.common); - //shader.vertexShader = 'uniform sampler2D heightmap;\n' + shader.vertexShader; - shader.vertexShader = shader.vertexShader.replace( - '#include ', - shaderChange.beginnormal_vertex, - ); - shader.vertexShader = shader.vertexShader.replace('#include ', shaderChange.begin_vertex); - - this.userData.shader = shader; - } -} - -const shaderChange = { - heightmap_frag: /* glsl */ ` - #include - - uniform vec2 mousePos; - uniform float mouseSize; - uniform float viscosity; - uniform float deep; - - void main() { - - vec2 cellSize = 1.0 / resolution.xy; - - vec2 uv = gl_FragCoord.xy * cellSize; - - // heightmapValue.x == height from previous frame - // heightmapValue.y == height from penultimate frame - // heightmapValue.z, heightmapValue.w not used - vec4 heightmapValue = texture2D( heightmap, uv ); - - // Get neighbours - vec4 north = texture2D( heightmap, uv + vec2( 0.0, cellSize.y ) ); - vec4 south = texture2D( heightmap, uv + vec2( 0.0, - cellSize.y ) ); - vec4 east = texture2D( heightmap, uv + vec2( cellSize.x, 0.0 ) ); - vec4 west = texture2D( heightmap, uv + vec2( - cellSize.x, 0.0 ) ); - - //float newHeight = ( ( north.x + south.x + east.x + west.x ) * 0.5 - heightmapValue.y ) * viscosity; - float newHeight = ( ( north.x + south.x + east.x + west.x ) * 0.5 - (heightmapValue.y) ) * viscosity; - - - // Mouse influence - float mousePhase = clamp( length( ( uv - vec2( 0.5 ) ) * BOUNDS - vec2( mousePos.x, - mousePos.y ) ) * PI / mouseSize, 0.0, PI ); - //newHeight += ( cos( mousePhase ) + 1.0 ) * 0.28 * 10.0; - newHeight -= ( cos( mousePhase ) + 1.0 ) * deep; - - heightmapValue.y = heightmapValue.x; - heightmapValue.x = newHeight; - - gl_FragColor = heightmapValue; - - } - `, - // FOR MATERIAL - common: /* glsl */ ` - #include - uniform sampler2D heightmap; - `, - beginnormal_vertex: /* glsl */ ` - vec2 cellSize = vec2( 1.0 / WIDTH, 1.0 / WIDTH ); - vec3 objectNormal = vec3( - ( texture2D( heightmap, uv + vec2( - cellSize.x, 0 ) ).x - texture2D( heightmap, uv + vec2( cellSize.x, 0 ) ).x ) * WIDTH / BOUNDS, - ( texture2D( heightmap, uv + vec2( 0, - cellSize.y ) ).x - texture2D( heightmap, uv + vec2( 0, cellSize.y ) ).x ) * WIDTH / BOUNDS, - 1.0 ); - #ifdef USE_TANGENT - vec3 objectTangent = vec3( tangent.xyz ); - #endif - `, - begin_vertex: /* glsl */ ` - float heightValue = texture2D( heightmap, uv ).x; - vec3 transformed = vec3( position.x, position.y, heightValue ); - #ifdef USE_ALPHAHASH - vPosition = vec3( position ); - #endif - `, -}; diff --git a/examples-testing/examples/webgl_helpers.ts b/examples-testing/examples/webgl_helpers.ts deleted file mode 100644 index a8c3b9773..000000000 --- a/examples-testing/examples/webgl_helpers.ts +++ /dev/null @@ -1,117 +0,0 @@ -import * as THREE from 'three'; - -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -import { VertexNormalsHelper } from 'three/addons/helpers/VertexNormalsHelper.js'; -import { VertexTangentsHelper } from 'three/addons/helpers/VertexTangentsHelper.js'; - -let scene, renderer; -let camera, light; -let vnh; -let vth; - -init(); - -function init() { - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // - - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.z = 400; - - scene = new THREE.Scene(); - - light = new THREE.PointLight(); - light.position.set(200, 100, 150); - scene.add(light); - - scene.add(new THREE.PointLightHelper(light, 15)); - - const gridHelper = new THREE.GridHelper(400, 40, 0x0000ff, 0x808080); - gridHelper.position.y = -150; - gridHelper.position.x = -150; - scene.add(gridHelper); - - const polarGridHelper = new THREE.PolarGridHelper(200, 16, 8, 64, 0x0000ff, 0x808080); - polarGridHelper.position.y = -150; - polarGridHelper.position.x = 200; - scene.add(polarGridHelper); - - const loader = new GLTFLoader(); - loader.load('models/gltf/LeePerrySmith/LeePerrySmith.glb', function (gltf) { - const mesh = gltf.scene.children[0]; - - mesh.geometry.computeTangents(); // generates bad data due to degenerate UVs - - const group = new THREE.Group(); - group.scale.multiplyScalar(50); - scene.add(group); - - // To make sure that the matrixWorld is up to date for the boxhelpers - group.updateMatrixWorld(true); - - group.add(mesh); - - vnh = new VertexNormalsHelper(mesh, 5); - scene.add(vnh); - - vth = new VertexTangentsHelper(mesh, 5); - scene.add(vth); - - scene.add(new THREE.BoxHelper(mesh)); - - const wireframe = new THREE.WireframeGeometry(mesh.geometry); - let line = new THREE.LineSegments(wireframe); - line.material.depthTest = false; - line.material.opacity = 0.25; - line.material.transparent = true; - line.position.x = 4; - group.add(line); - scene.add(new THREE.BoxHelper(line)); - - const edges = new THREE.EdgesGeometry(mesh.geometry); - line = new THREE.LineSegments(edges); - line.material.depthTest = false; - line.material.opacity = 0.25; - line.material.transparent = true; - line.position.x = -4; - group.add(line); - scene.add(new THREE.BoxHelper(line)); - - scene.add(new THREE.BoxHelper(group)); - scene.add(new THREE.BoxHelper(scene)); - }); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - const time = -performance.now() * 0.0003; - - camera.position.x = 400 * Math.cos(time); - camera.position.z = 400 * Math.sin(time); - camera.lookAt(scene.position); - - light.position.x = Math.sin(time * 1.7) * 300; - light.position.y = Math.cos(time * 1.5) * 400; - light.position.z = Math.cos(time * 1.3) * 300; - - if (vnh) vnh.update(); - if (vth) vth.update(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_instancing_dynamic.ts b/examples-testing/examples/webgl_instancing_dynamic.ts deleted file mode 100644 index bc4a88662..000000000 --- a/examples-testing/examples/webgl_instancing_dynamic.ts +++ /dev/null @@ -1,175 +0,0 @@ -import * as THREE from 'three'; - -import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; -import TWEEN from 'three/addons/libs/tween.module.js'; - -let camera, scene, renderer, timer, mesh; - -const amount = 100; - -const count = Math.pow(amount, 2); -const dummy = new THREE.Object3D(); - -const seeds = []; -const baseColors = []; - -const color = new THREE.Color(); -const colors = [new THREE.Color(0x00ffff), new THREE.Color(0xffff00), new THREE.Color(0xff00ff)]; -const animation = { t: 0 }; -let currentColorIndex = 0; -let nextColorIndex = 1; - -const maxDistance = 75; -const cameraTarget = new THREE.Vector3(); - -init(); - -function init() { - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.toneMapping = THREE.NeutralToneMapping; - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(10, 10, 10); - camera.lookAt(0, 0, 0); - - const pmremGenerator = new THREE.PMREMGenerator(renderer); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xadd8e6); - scene.environment = pmremGenerator.fromScene(new RoomEnvironment(), 0.04).texture; - - timer = new THREE.Timer(); - timer.connect(document); - - const loader = new THREE.TextureLoader(); - const texture = loader.load('textures/edge3.jpg'); - texture.colorSpace = THREE.SRGBColorSpace; - - const geometry = new THREE.BoxGeometry(); - const material = new THREE.MeshStandardMaterial({ map: texture }); - - mesh = new THREE.InstancedMesh(geometry, material, count); - mesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage); // will be updated every frame - scene.add(mesh); - - let i = 0; - const offset = (amount - 1) / 2; - - for (let x = 0; x < amount; x++) { - for (let z = 0; z < amount; z++) { - dummy.position.set(offset - x, 0, offset - z); - dummy.scale.set(1, 2, 1); - - dummy.updateMatrix(); - - color.setHSL(1, 0.5 + Math.random() * 0.5, 0.5 + Math.random() * 0.5); - baseColors.push(color.getHex()); - - mesh.setMatrixAt(i, dummy.matrix); - mesh.setColorAt(i, color.multiply(colors[0])); - - i++; - - seeds.push(Math.random()); - } - } - - // - - window.addEventListener('resize', onWindowResize); - - setInterval(startTween, 3000); -} - -function startTween() { - // tween for animating color transition - - new TWEEN.Tween(animation) - .to( - { - t: 1, - }, - 2000, - ) - .easing(TWEEN.Easing.Sinusoidal.In) - .onComplete(() => { - animation.t = 0; - - currentColorIndex = nextColorIndex; - nextColorIndex++; - - if (nextColorIndex >= colors.length) nextColorIndex = 0; - }) - .start(); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - timer.update(); - - const time = timer.getElapsed(); - - TWEEN.update(); - - // animate camera - - camera.position.x = Math.sin(time / 4) * 10; - camera.position.z = Math.cos(time / 4) * 10; - camera.position.y = 8 + Math.cos(time / 2) * 2; - - cameraTarget.x = Math.sin(time / 4) * -8; - cameraTarget.z = Math.cos(time / 2) * -8; - - camera.lookAt(cameraTarget); - - camera.up.x = Math.sin(time / 400); - - // animate instance positions and colors - - for (let i = 0; i < mesh.count; i++) { - mesh.getMatrixAt(i, dummy.matrix); - dummy.matrix.decompose(dummy.position, dummy.quaternion, dummy.scale); - - dummy.position.y = Math.abs(Math.sin((time + seeds[i]) * 2 + seeds[i])); - - dummy.updateMatrix(); - - mesh.setMatrixAt(i, dummy.matrix); - - // colors - - if (animation.t > 0) { - const currentColor = colors[currentColorIndex]; - const nextColor = colors[nextColorIndex]; - - const f = dummy.position.length() / maxDistance; - - if (f <= animation.t) { - color.set(baseColors[i]).multiply(nextColor); - } else { - color.set(baseColors[i]).multiply(currentColor); - } - - mesh.setColorAt(i, color); - } - } - - mesh.instanceMatrix.needsUpdate = true; - if (animation.t > 0) mesh.instanceColor.needsUpdate = true; - - mesh.computeBoundingSphere(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_instancing_morph.ts b/examples-testing/examples/webgl_instancing_morph.ts deleted file mode 100644 index 70e89199c..000000000 --- a/examples-testing/examples/webgl_instancing_morph.ts +++ /dev/null @@ -1,150 +0,0 @@ -import * as THREE from 'three'; - -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -import Stats from 'three/addons/libs/stats.module.js'; - -let camera, scene, renderer, stats, mesh, mixer, dummy; - -const offset = 5000; - -const timeOffsets = new Float32Array(1024); - -for (let i = 0; i < 1024; i++) { - timeOffsets[i] = Math.random() * 3; -} - -const timer = new THREE.Timer(); -timer.connect(document); - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 100, 10000); - - scene = new THREE.Scene(); - - scene.background = new THREE.Color(0x99ddff); - - scene.fog = new THREE.Fog(0x99ddff, 5000, 10000); - - const light = new THREE.DirectionalLight(0xffffff, 1); - - light.position.set(200, 1000, 50); - - light.castShadow = true; - - light.shadow.camera.left = -5000; - light.shadow.camera.right = 5000; - light.shadow.camera.top = 5000; - light.shadow.camera.bottom = -5000; - light.shadow.camera.far = 2000; - - light.shadow.bias = -0.01; - - light.shadow.camera.updateProjectionMatrix(); - - scene.add(light); - - const hemi = new THREE.HemisphereLight(0x99ddff, 0x669933, 1 / 3); - - scene.add(hemi); - - const ground = new THREE.Mesh( - new THREE.PlaneGeometry(1000000, 1000000), - new THREE.MeshStandardMaterial({ color: 0x669933, depthWrite: true }), - ); - - ground.rotation.x = -Math.PI / 2; - - ground.receiveShadow = true; - - scene.add(ground); - - const loader = new GLTFLoader(); - - loader.load('models/gltf/Horse.glb', function (glb) { - dummy = glb.scene.children[0]; - - mesh = new THREE.InstancedMesh(dummy.geometry, dummy.material, 1024); - - mesh.castShadow = true; - - for (let x = 0, i = 0; x < 32; x++) { - for (let y = 0; y < 32; y++) { - dummy.position.set(offset - 300 * x + 200 * Math.random(), 0, offset - 300 * y); - - dummy.updateMatrix(); - - mesh.setMatrixAt(i, dummy.matrix); - - mesh.setColorAt(i, new THREE.Color(`hsl(${Math.random() * 360}, 50%, 66%)`)); - - i++; - } - } - - scene.add(mesh); - - mixer = new THREE.AnimationMixer(glb.scene); - - const action = mixer.clipAction(glb.animations[0]); - - action.play(); - }); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - renderer.shadowMap.enabled = true; - renderer.shadowMap.type = THREE.VSMShadowMap; - // - - stats = new Stats(); - document.body.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - timer.update(); - - render(); - - stats.update(); -} - -function render() { - const time = timer.getElapsed(); - - const r = 3000; - camera.position.set(Math.sin(time / 10) * r, 1500 + 1000 * Math.cos(time / 5), Math.cos(time / 10) * r); - camera.lookAt(0, 0, 0); - - if (mesh) { - for (let i = 0; i < 1024; i++) { - mixer.setTime(time + timeOffsets[i]); - - mesh.setMorphAt(i, dummy); - } - - mesh.morphTexture.needsUpdate = true; - } - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_instancing_performance.ts b/examples-testing/examples/webgl_instancing_performance.ts deleted file mode 100644 index bf1deabad..000000000 --- a/examples-testing/examples/webgl_instancing_performance.ts +++ /dev/null @@ -1,262 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js'; - -let container, stats, gui, guiStatsEl; -let camera, controls, scene, renderer, material; - -// gui - -const Method = { - INSTANCED: 'INSTANCED', - MERGED: 'MERGED', - NAIVE: 'NAIVE', -}; - -const api = { - method: Method.INSTANCED, - count: 1000, -}; - -// - -init(); -initMesh(); - -// - -function clean() { - const meshes = []; - - scene.traverse(function (object) { - if (object.isMesh) meshes.push(object); - }); - - for (let i = 0; i < meshes.length; i++) { - const mesh = meshes[i]; - mesh.material.dispose(); - mesh.geometry.dispose(); - - scene.remove(mesh); - } -} - -const randomizeMatrix = (function () { - const position = new THREE.Vector3(); - const quaternion = new THREE.Quaternion(); - const scale = new THREE.Vector3(); - - return function (matrix) { - position.x = Math.random() * 40 - 20; - position.y = Math.random() * 40 - 20; - position.z = Math.random() * 40 - 20; - - quaternion.random(); - - scale.x = scale.y = scale.z = Math.random() * 1; - - matrix.compose(position, quaternion, scale); - }; -})(); - -function initMesh() { - clean(); - - // make instances - new THREE.BufferGeometryLoader().setPath('models/json/').load('suzanne_buffergeometry.json', function (geometry) { - material = new THREE.MeshNormalMaterial(); - - geometry.computeVertexNormals(); - - console.time(api.method + ' (build)'); - - switch (api.method) { - case Method.INSTANCED: - makeInstanced(geometry); - break; - - case Method.MERGED: - makeMerged(geometry); - break; - - case Method.NAIVE: - makeNaive(geometry); - break; - } - - console.timeEnd(api.method + ' (build)'); - }); -} - -function makeInstanced(geometry) { - const matrix = new THREE.Matrix4(); - const mesh = new THREE.InstancedMesh(geometry, material, api.count); - - for (let i = 0; i < api.count; i++) { - randomizeMatrix(matrix); - mesh.setMatrixAt(i, matrix); - } - - scene.add(mesh); - - // - - const geometryByteLength = getGeometryByteLength(geometry); - - guiStatsEl.innerHTML = [ - 'GPU draw calls: 1', - 'GPU memory: ' + formatBytes(api.count * 16 + geometryByteLength, 2), - ].join('
'); -} - -function makeMerged(geometry) { - const geometries = []; - const matrix = new THREE.Matrix4(); - - for (let i = 0; i < api.count; i++) { - randomizeMatrix(matrix); - - const instanceGeometry = geometry.clone(); - instanceGeometry.applyMatrix4(matrix); - - geometries.push(instanceGeometry); - } - - const mergedGeometry = BufferGeometryUtils.mergeGeometries(geometries); - - scene.add(new THREE.Mesh(mergedGeometry, material)); - - // - - guiStatsEl.innerHTML = [ - 'GPU draw calls: 1', - 'GPU memory: ' + formatBytes(getGeometryByteLength(mergedGeometry), 2), - ].join('
'); -} - -function makeNaive(geometry) { - const matrix = new THREE.Matrix4(); - - for (let i = 0; i < api.count; i++) { - randomizeMatrix(matrix); - - const mesh = new THREE.Mesh(geometry, material); - mesh.applyMatrix4(matrix); - - scene.add(mesh); - } - - // - - const geometryByteLength = getGeometryByteLength(geometry); - - guiStatsEl.innerHTML = [ - 'GPU draw calls: ' + api.count, - 'GPU memory: ' + formatBytes(api.count * 16 + geometryByteLength, 2), - ].join('
'); -} - -function init() { - const width = window.innerWidth; - const height = window.innerHeight; - - // camera - - camera = new THREE.PerspectiveCamera(70, width / height, 1, 100); - camera.position.z = 30; - - // renderer - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(width, height); - renderer.setAnimationLoop(animate); - container = document.getElementById('container'); - container.appendChild(renderer.domElement); - - // scene - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xffffff); - - // controls - - controls = new OrbitControls(camera, renderer.domElement); - controls.autoRotate = true; - - // stats - - stats = new Stats(); - container.appendChild(stats.dom); - - // gui - - gui = new GUI(); - gui.add(api, 'method', Method).onChange(initMesh); - gui.add(api, 'count', 1, 10000).step(1).onChange(initMesh); - - const perfFolder = gui.addFolder('Performance'); - - guiStatsEl = document.createElement('div'); - guiStatsEl.classList.add('gui-stats'); - - perfFolder.$children.appendChild(guiStatsEl); - perfFolder.open(); - - // listeners - - window.addEventListener('resize', onWindowResize); - - Object.assign(window, { scene }); -} - -// - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); -} - -function animate() { - controls.update(); - - renderer.render(scene, camera); - - stats.update(); -} - -// - -function getGeometryByteLength(geometry) { - let total = 0; - - if (geometry.index) total += geometry.index.array.byteLength; - - for (const name in geometry.attributes) { - total += geometry.attributes[name].array.byteLength; - } - - return total; -} - -// Source: https://stackoverflow.com/a/18650828/1314762 -function formatBytes(bytes, decimals) { - if (bytes === 0) return '0 bytes'; - - const k = 1024; - const dm = decimals < 0 ? 0 : decimals; - const sizes = ['bytes', 'KB', 'MB']; - - const i = Math.floor(Math.log(bytes) / Math.log(k)); - - return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; -} diff --git a/examples-testing/examples/webgl_instancing_raycast.ts b/examples-testing/examples/webgl_instancing_raycast.ts deleted file mode 100644 index 371ea070b..000000000 --- a/examples-testing/examples/webgl_instancing_raycast.ts +++ /dev/null @@ -1,116 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let camera, scene, renderer, controls, stats; - -let mesh; -const amount = parseInt(window.location.search.slice(1)) || 10; -const count = Math.pow(amount, 3); - -const raycaster = new THREE.Raycaster(); -const mouse = new THREE.Vector2(1, 1); - -const color = new THREE.Color(); -const white = new THREE.Color().setHex(0xffffff); - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(amount, amount, amount); - camera.lookAt(0, 0, 0); - - scene = new THREE.Scene(); - - const light = new THREE.HemisphereLight(0xffffff, 0x888888, 3); - light.position.set(0, 1, 0); - scene.add(light); - - const geometry = new THREE.IcosahedronGeometry(0.5, 3); - const material = new THREE.MeshPhongMaterial({ color: 0xffffff }); - - mesh = new THREE.InstancedMesh(geometry, material, count); - - let i = 0; - const offset = (amount - 1) / 2; - - const matrix = new THREE.Matrix4(); - - for (let x = 0; x < amount; x++) { - for (let y = 0; y < amount; y++) { - for (let z = 0; z < amount; z++) { - matrix.setPosition(offset - x, offset - y, offset - z); - - mesh.setMatrixAt(i, matrix); - mesh.setColorAt(i, color); - - i++; - } - } - } - - scene.add(mesh); - - // - - const gui = new GUI(); - gui.add(mesh, 'count', 0, count); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - controls.enableZoom = false; - controls.enablePan = false; - - stats = new Stats(); - document.body.appendChild(stats.dom); - - window.addEventListener('resize', onWindowResize); - document.addEventListener('mousemove', onMouseMove); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function onMouseMove(event) { - event.preventDefault(); - - mouse.x = (event.clientX / window.innerWidth) * 2 - 1; - mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; -} - -function animate() { - controls.update(); - - raycaster.setFromCamera(mouse, camera); - - const intersection = raycaster.intersectObject(mesh); - - if (intersection.length > 0) { - const instanceId = intersection[0].instanceId; - - mesh.getColorAt(instanceId, color); - - if (color.equals(white)) { - mesh.setColorAt(instanceId, color.setHex(Math.random() * 0xffffff)); - - mesh.instanceColor.needsUpdate = true; - } - } - - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/webgl_instancing_scatter.ts b/examples-testing/examples/webgl_instancing_scatter.ts deleted file mode 100644 index fc3b9cc9f..000000000 --- a/examples-testing/examples/webgl_instancing_scatter.ts +++ /dev/null @@ -1,257 +0,0 @@ -import * as THREE from 'three'; - -import { MeshSurfaceSampler } from 'three/addons/math/MeshSurfaceSampler.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import Stats from 'three/addons/libs/stats.module.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let camera, scene, renderer, stats; - -const api = { - count: 2000, - distribution: 'random', - resample: resample, - surfaceColor: 0xfff784, - backgroundColor: 0xe39469, -}; - -let stemMesh, blossomMesh; -let stemGeometry, blossomGeometry; -let stemMaterial, blossomMaterial; - -let sampler; -const count = api.count; -const ages = new Float32Array(count); -const scales = new Float32Array(count); -const dummy = new THREE.Object3D(); - -const _position = new THREE.Vector3(); -const _normal = new THREE.Vector3(); -const _scale = new THREE.Vector3(); - -// let surfaceGeometry = new THREE.BoxGeometry( 10, 10, 10 ).toNonIndexed(); -const surfaceGeometry = new THREE.TorusKnotGeometry(10, 3, 100, 16).toNonIndexed(); -const surfaceMaterial = new THREE.MeshLambertMaterial({ color: api.surfaceColor, wireframe: false }); -const surface = new THREE.Mesh(surfaceGeometry, surfaceMaterial); - -// Source: https://gist.github.com/gre/1650294 -const easeOutCubic = function (t) { - return --t * t * t + 1; -}; - -// Scaling curve causes particles to grow quickly, ease gradually into full scale, then -// disappear quickly. More of the particle's lifetime is spent around full scale. -const scaleCurve = function (t) { - return Math.abs(easeOutCubic((t > 0.5 ? 1 - t : t) * 2)); -}; - -const loader = new GLTFLoader(); - -loader.load('./models/gltf/Flower/Flower.glb', function (gltf) { - const _stemMesh = gltf.scene.getObjectByName('Stem'); - const _blossomMesh = gltf.scene.getObjectByName('Blossom'); - - stemGeometry = _stemMesh.geometry.clone(); - blossomGeometry = _blossomMesh.geometry.clone(); - - const defaultTransform = new THREE.Matrix4() - .makeRotationX(Math.PI) - .multiply(new THREE.Matrix4().makeScale(7, 7, 7)); - - stemGeometry.applyMatrix4(defaultTransform); - blossomGeometry.applyMatrix4(defaultTransform); - - stemMaterial = _stemMesh.material; - blossomMaterial = _blossomMesh.material; - - stemMesh = new THREE.InstancedMesh(stemGeometry, stemMaterial, count); - blossomMesh = new THREE.InstancedMesh(blossomGeometry, blossomMaterial, count); - - // Assign random colors to the blossoms. - const color = new THREE.Color(); - const blossomPalette = [0xf20587, 0xf2d479, 0xf2c879, 0xf2b077, 0xf24405]; - - for (let i = 0; i < count; i++) { - color.setHex(blossomPalette[Math.floor(Math.random() * blossomPalette.length)]); - blossomMesh.setColorAt(i, color); - } - - // Instance matrices will be updated every frame. - stemMesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage); - blossomMesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage); - - resample(); - - init(); -}); - -function init() { - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(25, 25, 25); - camera.lookAt(0, 0, 0); - - // - - scene = new THREE.Scene(); - scene.background = new THREE.Color(api.backgroundColor); - - const pointLight = new THREE.PointLight(0xaa8899, 2.5, 0, 0); - pointLight.position.set(50, -25, 75); - scene.add(pointLight); - - scene.add(new THREE.AmbientLight(0xffffff, 3)); - - // - - scene.add(stemMesh); - scene.add(blossomMesh); - - scene.add(surface); - - // - - const gui = new GUI(); - gui.add(api, 'count', 0, count).onChange(function () { - stemMesh.count = api.count; - blossomMesh.count = api.count; - }); - - // gui.addColor( api, 'backgroundColor' ).onChange( function () { - - // scene.background.setHex( api.backgroundColor ); - - // } ); - - // gui.addColor( api, 'surfaceColor' ).onChange( function () { - - // surfaceMaterial.color.setHex( api.surfaceColor ); - - // } ); - - gui.add(api, 'distribution').options(['random', 'weighted']).onChange(resample); - gui.add(api, 'resample'); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // - - stats = new Stats(); - document.body.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); -} - -function resample() { - const vertexCount = surface.geometry.getAttribute('position').count; - - console.info('Sampling ' + count + ' points from a surface with ' + vertexCount + ' vertices...'); - - // - - console.time('.build()'); - - sampler = new MeshSurfaceSampler(surface).setWeightAttribute(api.distribution === 'weighted' ? 'uv' : null).build(); - - console.timeEnd('.build()'); - - // - - console.time('.sample()'); - - for (let i = 0; i < count; i++) { - ages[i] = Math.random(); - scales[i] = scaleCurve(ages[i]); - - resampleParticle(i); - } - - console.timeEnd('.sample()'); - - stemMesh.instanceMatrix.needsUpdate = true; - blossomMesh.instanceMatrix.needsUpdate = true; -} - -function resampleParticle(i) { - sampler.sample(_position, _normal); - _normal.add(_position); - - dummy.position.copy(_position); - dummy.scale.set(scales[i], scales[i], scales[i]); - dummy.lookAt(_normal); - dummy.updateMatrix(); - - stemMesh.setMatrixAt(i, dummy.matrix); - blossomMesh.setMatrixAt(i, dummy.matrix); -} - -function updateParticle(i) { - // Update lifecycle. - - ages[i] += 0.005; - - if (ages[i] >= 1) { - ages[i] = 0.001; - scales[i] = scaleCurve(ages[i]); - - resampleParticle(i); - - return; - } - - // Update scale. - - const prevScale = scales[i]; - scales[i] = scaleCurve(ages[i]); - _scale.set(scales[i] / prevScale, scales[i] / prevScale, scales[i] / prevScale); - - // Update transform. - - stemMesh.getMatrixAt(i, dummy.matrix); - dummy.matrix.scale(_scale); - stemMesh.setMatrixAt(i, dummy.matrix); - blossomMesh.setMatrixAt(i, dummy.matrix); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - render(); - - stats.update(); -} - -function render() { - if (stemMesh && blossomMesh) { - const time = Date.now() * 0.001; - - scene.rotation.x = Math.sin(time / 4); - scene.rotation.y = Math.sin(time / 2); - - for (let i = 0; i < api.count; i++) { - updateParticle(i); - } - - stemMesh.instanceMatrix.needsUpdate = true; - blossomMesh.instanceMatrix.needsUpdate = true; - - stemMesh.computeBoundingSphere(); - blossomMesh.computeBoundingSphere(); - } - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_interactive_buffergeometry.ts b/examples-testing/examples/webgl_interactive_buffergeometry.ts deleted file mode 100644 index 1d6608b13..000000000 --- a/examples-testing/examples/webgl_interactive_buffergeometry.ts +++ /dev/null @@ -1,244 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -let container, stats; - -let camera, scene, renderer; - -let raycaster, pointer; - -let mesh, line; - -init(); - -function init() { - container = document.getElementById('container'); - - // - - camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 1, 3500); - camera.position.z = 2750; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x050505); - scene.fog = new THREE.Fog(0x050505, 2000, 3500); - - // - - scene.add(new THREE.AmbientLight(0x444444, 3)); - - const light1 = new THREE.DirectionalLight(0xffffff, 1.5); - light1.position.set(1, 1, 1); - scene.add(light1); - - const light2 = new THREE.DirectionalLight(0xffffff, 4.5); - light2.position.set(0, -1, 0); - scene.add(light2); - - // - - const triangles = 5000; - - let geometry = new THREE.BufferGeometry(); - - const positions = new Float32Array(triangles * 3 * 3); - const normals = new Float32Array(triangles * 3 * 3); - const colors = new Float32Array(triangles * 3 * 3); - - const color = new THREE.Color(); - - const n = 800, - n2 = n / 2; // triangles spread in the cube - const d = 120, - d2 = d / 2; // individual triangle size - - const pA = new THREE.Vector3(); - const pB = new THREE.Vector3(); - const pC = new THREE.Vector3(); - - const cb = new THREE.Vector3(); - const ab = new THREE.Vector3(); - - for (let i = 0; i < positions.length; i += 9) { - // positions - - const x = Math.random() * n - n2; - const y = Math.random() * n - n2; - const z = Math.random() * n - n2; - - const ax = x + Math.random() * d - d2; - const ay = y + Math.random() * d - d2; - const az = z + Math.random() * d - d2; - - const bx = x + Math.random() * d - d2; - const by = y + Math.random() * d - d2; - const bz = z + Math.random() * d - d2; - - const cx = x + Math.random() * d - d2; - const cy = y + Math.random() * d - d2; - const cz = z + Math.random() * d - d2; - - positions[i] = ax; - positions[i + 1] = ay; - positions[i + 2] = az; - - positions[i + 3] = bx; - positions[i + 4] = by; - positions[i + 5] = bz; - - positions[i + 6] = cx; - positions[i + 7] = cy; - positions[i + 8] = cz; - - // flat face normals - - pA.set(ax, ay, az); - pB.set(bx, by, bz); - pC.set(cx, cy, cz); - - cb.subVectors(pC, pB); - ab.subVectors(pA, pB); - cb.cross(ab); - - cb.normalize(); - - const nx = cb.x; - const ny = cb.y; - const nz = cb.z; - - normals[i] = nx; - normals[i + 1] = ny; - normals[i + 2] = nz; - - normals[i + 3] = nx; - normals[i + 4] = ny; - normals[i + 5] = nz; - - normals[i + 6] = nx; - normals[i + 7] = ny; - normals[i + 8] = nz; - - // colors - - const vx = x / n + 0.5; - const vy = y / n + 0.5; - const vz = z / n + 0.5; - - color.setRGB(vx, vy, vz); - - colors[i] = color.r; - colors[i + 1] = color.g; - colors[i + 2] = color.b; - - colors[i + 3] = color.r; - colors[i + 4] = color.g; - colors[i + 5] = color.b; - - colors[i + 6] = color.r; - colors[i + 7] = color.g; - colors[i + 8] = color.b; - } - - geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); - geometry.setAttribute('normal', new THREE.BufferAttribute(normals, 3)); - geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3)); - - geometry.computeBoundingSphere(); - - let material = new THREE.MeshPhongMaterial({ - color: 0xaaaaaa, - specular: 0xffffff, - shininess: 250, - side: THREE.DoubleSide, - vertexColors: true, - }); - - mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - // - - raycaster = new THREE.Raycaster(); - - pointer = new THREE.Vector2(); - - geometry = new THREE.BufferGeometry(); - geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(4 * 3), 3)); - - material = new THREE.LineBasicMaterial({ color: 0xffffff, transparent: true }); - - line = new THREE.Line(geometry, material); - scene.add(line); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - // - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); - document.addEventListener('pointermove', onPointerMove); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function onPointerMove(event) { - pointer.x = (event.clientX / window.innerWidth) * 2 - 1; - pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; -} - -// - -function animate() { - render(); - stats.update(); -} - -function render() { - const time = Date.now() * 0.001; - - mesh.rotation.x = time * 0.15; - mesh.rotation.y = time * 0.25; - - raycaster.setFromCamera(pointer, camera); - - const intersects = raycaster.intersectObject(mesh); - - if (intersects.length > 0) { - const intersect = intersects[0]; - const face = intersect.face; - - const linePosition = line.geometry.attributes.position; - const meshPosition = mesh.geometry.attributes.position; - - linePosition.copyAt(0, meshPosition, face.a); - linePosition.copyAt(1, meshPosition, face.b); - linePosition.copyAt(2, meshPosition, face.c); - linePosition.copyAt(3, meshPosition, face.a); - - mesh.updateMatrix(); - - line.geometry.applyMatrix4(mesh.matrix); - - line.visible = true; - } else { - line.visible = false; - } - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_interactive_cubes.ts b/examples-testing/examples/webgl_interactive_cubes.ts deleted file mode 100644 index adfcfddf8..000000000 --- a/examples-testing/examples/webgl_interactive_cubes.ts +++ /dev/null @@ -1,114 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -let stats; -let camera, scene, raycaster, renderer; - -let INTERSECTED; -let theta = 0; - -const pointer = new THREE.Vector2(); -const radius = 5; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 100); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xf0f0f0); - - const light = new THREE.DirectionalLight(0xffffff, 3); - light.position.set(1, 1, 1).normalize(); - scene.add(light); - - const geometry = new THREE.BoxGeometry(); - - for (let i = 0; i < 2000; i++) { - const object = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({ color: Math.random() * 0xffffff })); - - object.position.x = Math.random() * 40 - 20; - object.position.y = Math.random() * 40 - 20; - object.position.z = Math.random() * 40 - 20; - - object.rotation.x = Math.random() * 2 * Math.PI; - object.rotation.y = Math.random() * 2 * Math.PI; - object.rotation.z = Math.random() * 2 * Math.PI; - - object.scale.x = Math.random() + 0.5; - object.scale.y = Math.random() + 0.5; - object.scale.z = Math.random() + 0.5; - - scene.add(object); - } - - raycaster = new THREE.Raycaster(); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - stats = new Stats(); - document.body.appendChild(stats.dom); - - document.addEventListener('mousemove', onPointerMove); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function onPointerMove(event) { - pointer.x = (event.clientX / window.innerWidth) * 2 - 1; - pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; -} - -// - -function animate() { - render(); - stats.update(); -} - -function render() { - theta += 0.1; - - camera.position.x = radius * Math.sin(THREE.MathUtils.degToRad(theta)); - camera.position.y = radius * Math.sin(THREE.MathUtils.degToRad(theta)); - camera.position.z = radius * Math.cos(THREE.MathUtils.degToRad(theta)); - camera.lookAt(scene.position); - - camera.updateMatrixWorld(); - - // find intersections - - raycaster.setFromCamera(pointer, camera); - - const intersects = raycaster.intersectObjects(scene.children, false); - - if (intersects.length > 0) { - if (INTERSECTED != intersects[0].object) { - if (INTERSECTED) INTERSECTED.material.emissive.setHex(INTERSECTED.currentHex); - - INTERSECTED = intersects[0].object; - INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex(); - INTERSECTED.material.emissive.setHex(0xff0000); - } - } else { - if (INTERSECTED) INTERSECTED.material.emissive.setHex(INTERSECTED.currentHex); - - INTERSECTED = null; - } - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_interactive_cubes_gpu.ts b/examples-testing/examples/webgl_interactive_cubes_gpu.ts deleted file mode 100644 index 5b19d2085..000000000 --- a/examples-testing/examples/webgl_interactive_cubes_gpu.ts +++ /dev/null @@ -1,231 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { TrackballControls } from 'three/addons/controls/TrackballControls.js'; -import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js'; - -let container, stats; -let camera, controls, scene, renderer; -let pickingTexture, pickingScene; -let highlightBox; - -const pickingData = []; - -const pointer = new THREE.Vector2(); -const offset = new THREE.Vector3(10, 10, 10); -const clearColor = new THREE.Color(); - -init(); - -function init() { - container = document.getElementById('container'); - - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 10000); - camera.position.z = 1000; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xffffff); - - scene.add(new THREE.AmbientLight(0xcccccc)); - - const light = new THREE.DirectionalLight(0xffffff, 3); - light.position.set(0, 500, 2000); - scene.add(light); - - const defaultMaterial = new THREE.MeshPhongMaterial({ - color: 0xffffff, - flatShading: true, - vertexColors: true, - shininess: 0, - }); - - // set up the picking texture to use a 32 bit integer so we can write and read integer ids from it - pickingScene = new THREE.Scene(); - pickingTexture = new THREE.WebGLRenderTarget(1, 1, { - type: THREE.IntType, - format: THREE.RGBAIntegerFormat, - internalFormat: 'RGBA32I', - }); - const pickingMaterial = new THREE.ShaderMaterial({ - glslVersion: THREE.GLSL3, - - vertexShader: /* glsl */ ` - attribute int id; - flat varying int vid; - void main() { - - vid = id; - gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); - - } - `, - - fragmentShader: /* glsl */ ` - layout(location = 0) out int out_id; - flat varying int vid; - - void main() { - - out_id = vid; - - } - `, - }); - - function applyId(geometry, id) { - const position = geometry.attributes.position; - const array = new Int16Array(position.count); - array.fill(id); - - const bufferAttribute = new THREE.Int16BufferAttribute(array, 1, false); - bufferAttribute.gpuType = THREE.IntType; - geometry.setAttribute('id', bufferAttribute); - } - - function applyVertexColors(geometry, color) { - const position = geometry.attributes.position; - const colors = []; - - for (let i = 0; i < position.count; i++) { - colors.push(color.r, color.g, color.b); - } - - geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); - } - - const geometries = []; - const matrix = new THREE.Matrix4(); - const quaternion = new THREE.Quaternion(); - const color = new THREE.Color(); - - for (let i = 0; i < 5000; i++) { - const geometry = new THREE.BoxGeometry(); - - const position = new THREE.Vector3(); - position.x = Math.random() * 10000 - 5000; - position.y = Math.random() * 6000 - 3000; - position.z = Math.random() * 8000 - 4000; - - const rotation = new THREE.Euler(); - rotation.x = Math.random() * 2 * Math.PI; - rotation.y = Math.random() * 2 * Math.PI; - rotation.z = Math.random() * 2 * Math.PI; - - const scale = new THREE.Vector3(); - scale.x = Math.random() * 200 + 100; - scale.y = Math.random() * 200 + 100; - scale.z = Math.random() * 200 + 100; - - quaternion.setFromEuler(rotation); - matrix.compose(position, quaternion, scale); - - geometry.applyMatrix4(matrix); - - // give the geometry's vertices a random color to be displayed and an integer - // identifier as a vertex attribute so boxes can be identified after being merged. - applyVertexColors(geometry, color.setHex(Math.random() * 0xffffff)); - applyId(geometry, i); - - geometries.push(geometry); - - pickingData[i] = { - position: position, - rotation: rotation, - scale: scale, - }; - } - - const mergedGeometry = BufferGeometryUtils.mergeGeometries(geometries); - scene.add(new THREE.Mesh(mergedGeometry, defaultMaterial)); - pickingScene.add(new THREE.Mesh(mergedGeometry, pickingMaterial)); - - highlightBox = new THREE.Mesh(new THREE.BoxGeometry(), new THREE.MeshLambertMaterial({ color: 0xffff00 })); - scene.add(highlightBox); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - controls = new TrackballControls(camera, renderer.domElement); - controls.rotateSpeed = 1.0; - controls.zoomSpeed = 1.2; - controls.panSpeed = 0.8; - controls.noZoom = false; - controls.noPan = false; - controls.staticMoving = true; - controls.dynamicDampingFactor = 0.3; - - stats = new Stats(); - container.appendChild(stats.dom); - - renderer.domElement.addEventListener('pointermove', onPointerMove); -} - -// - -function onPointerMove(e) { - pointer.x = e.clientX; - pointer.y = e.clientY; -} - -function animate() { - render(); - stats.update(); -} - -function pick() { - // render the picking scene off-screen - // set the view offset to represent just a single pixel under the mouse - const dpr = window.devicePixelRatio; - camera.setViewOffset( - renderer.domElement.width, - renderer.domElement.height, - Math.floor(pointer.x * dpr), - Math.floor(pointer.y * dpr), - 1, - 1, - ); - - // render the scene - renderer.setRenderTarget(pickingTexture); - - // clear the background to - 1 meaning no item was hit - clearColor.setRGB(-1, -1, -1); - renderer.setClearColor(clearColor); - renderer.render(pickingScene, camera); - - // Restore active render target to canvas - renderer.setRenderTarget(null); - - // clear the view offset so rendering returns to normal - camera.clearViewOffset(); - - // create buffer for reading single pixel - const pixelBuffer = new Int32Array(4); - - // read the pixel - renderer.readRenderTargetPixelsAsync(pickingTexture, 0, 0, 1, 1, pixelBuffer).then(() => { - const id = pixelBuffer[0]; - if (id !== -1) { - // move our highlightBox so that it surrounds the picked object - const data = pickingData[id]; - highlightBox.position.copy(data.position); - highlightBox.rotation.copy(data.rotation); - highlightBox.scale.copy(data.scale).add(offset); - highlightBox.visible = true; - } else { - highlightBox.visible = false; - } - }); -} - -function render() { - controls.update(); - - pick(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_interactive_cubes_ortho.ts b/examples-testing/examples/webgl_interactive_cubes_ortho.ts deleted file mode 100644 index 520674b5f..000000000 --- a/examples-testing/examples/webgl_interactive_cubes_ortho.ts +++ /dev/null @@ -1,129 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -let stats; -let camera, scene, raycaster, renderer; - -let theta = 0; -let INTERSECTED; - -const pointer = new THREE.Vector2(); -const radius = 25; -const frustumSize = 50; - -init(); - -function init() { - const aspect = window.innerWidth / window.innerHeight; - camera = new THREE.OrthographicCamera( - (frustumSize * aspect) / -2, - (frustumSize * aspect) / 2, - frustumSize / 2, - frustumSize / -2, - 0.1, - 100, - ); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xf0f0f0); - - const light = new THREE.DirectionalLight(0xffffff, 3); - light.position.set(1, 1, 1).normalize(); - scene.add(light); - - const geometry = new THREE.BoxGeometry(); - - for (let i = 0; i < 2000; i++) { - const object = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({ color: Math.random() * 0xffffff })); - - object.position.x = Math.random() * 40 - 20; - object.position.y = Math.random() * 40 - 20; - object.position.z = Math.random() * 40 - 20; - - object.rotation.x = Math.random() * 2 * Math.PI; - object.rotation.y = Math.random() * 2 * Math.PI; - object.rotation.z = Math.random() * 2 * Math.PI; - - object.scale.x = Math.random() + 0.5; - object.scale.y = Math.random() + 0.5; - object.scale.z = Math.random() + 0.5; - - scene.add(object); - } - - raycaster = new THREE.Raycaster(); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - stats = new Stats(); - document.body.appendChild(stats.dom); - - document.addEventListener('pointermove', onPointerMove); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - const aspect = window.innerWidth / window.innerHeight; - - camera.left = (-frustumSize * aspect) / 2; - camera.right = (frustumSize * aspect) / 2; - camera.top = frustumSize / 2; - camera.bottom = -frustumSize / 2; - - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function onPointerMove(event) { - pointer.x = (event.clientX / window.innerWidth) * 2 - 1; - pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; -} - -// - -function animate() { - render(); - stats.update(); -} - -function render() { - theta += 0.1; - - camera.position.x = radius * Math.sin(THREE.MathUtils.degToRad(theta)); - camera.position.y = radius * Math.sin(THREE.MathUtils.degToRad(theta)); - camera.position.z = radius * Math.cos(THREE.MathUtils.degToRad(theta)); - camera.lookAt(scene.position); - - camera.updateMatrixWorld(); - - // find intersections - - raycaster.setFromCamera(pointer, camera); - - const intersects = raycaster.intersectObjects(scene.children, false); - - if (intersects.length > 0) { - if (INTERSECTED != intersects[0].object) { - if (INTERSECTED) INTERSECTED.material.emissive.setHex(INTERSECTED.currentHex); - - INTERSECTED = intersects[0].object; - INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex(); - INTERSECTED.material.emissive.setHex(0xff0000); - } - } else { - if (INTERSECTED) INTERSECTED.material.emissive.setHex(INTERSECTED.currentHex); - - INTERSECTED = null; - } - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_interactive_lines.ts b/examples-testing/examples/webgl_interactive_lines.ts deleted file mode 100644 index b137c5501..000000000 --- a/examples-testing/examples/webgl_interactive_lines.ts +++ /dev/null @@ -1,160 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -let container, stats; -let camera, scene, raycaster, renderer, parentTransform, sphereInter; - -const pointer = new THREE.Vector2(); -const radius = 100; -let theta = 0; - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - const info = document.createElement('div'); - info.style.position = 'absolute'; - info.style.top = '10px'; - info.style.width = '100%'; - info.style.textAlign = 'center'; - info.innerHTML = - 'three.js webgl - interactive lines'; - container.appendChild(info); - - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 10000); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xf0f0f0); - - const geometry = new THREE.SphereGeometry(5); - const material = new THREE.MeshBasicMaterial({ color: 0xff0000 }); - - sphereInter = new THREE.Mesh(geometry, material); - sphereInter.visible = false; - scene.add(sphereInter); - - const lineGeometry = new THREE.BufferGeometry(); - const points = []; - - const point = new THREE.Vector3(); - const direction = new THREE.Vector3(); - - for (let i = 0; i < 50; i++) { - direction.x += Math.random() - 0.5; - direction.y += Math.random() - 0.5; - direction.z += Math.random() - 0.5; - direction.normalize().multiplyScalar(10); - - point.add(direction); - points.push(point.x, point.y, point.z); - } - - lineGeometry.setAttribute('position', new THREE.Float32BufferAttribute(points, 3)); - - parentTransform = new THREE.Object3D(); - parentTransform.position.x = Math.random() * 40 - 20; - parentTransform.position.y = Math.random() * 40 - 20; - parentTransform.position.z = Math.random() * 40 - 20; - - parentTransform.rotation.x = Math.random() * 2 * Math.PI; - parentTransform.rotation.y = Math.random() * 2 * Math.PI; - parentTransform.rotation.z = Math.random() * 2 * Math.PI; - - parentTransform.scale.x = Math.random() + 0.5; - parentTransform.scale.y = Math.random() + 0.5; - parentTransform.scale.z = Math.random() + 0.5; - - for (let i = 0; i < 50; i++) { - let object; - - const lineMaterial = new THREE.LineBasicMaterial({ color: Math.random() * 0xffffff }); - - if (Math.random() > 0.5) { - object = new THREE.Line(lineGeometry, lineMaterial); - } else { - object = new THREE.LineSegments(lineGeometry, lineMaterial); - } - - object.position.x = Math.random() * 400 - 200; - object.position.y = Math.random() * 400 - 200; - object.position.z = Math.random() * 400 - 200; - - object.rotation.x = Math.random() * 2 * Math.PI; - object.rotation.y = Math.random() * 2 * Math.PI; - object.rotation.z = Math.random() * 2 * Math.PI; - - object.scale.x = Math.random() + 0.5; - object.scale.y = Math.random() + 0.5; - object.scale.z = Math.random() + 0.5; - - parentTransform.add(object); - } - - scene.add(parentTransform); - - raycaster = new THREE.Raycaster(); - raycaster.params.Line.threshold = 3; - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - stats = new Stats(); - container.appendChild(stats.dom); - - document.addEventListener('pointermove', onPointerMove); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function onPointerMove(event) { - pointer.x = (event.clientX / window.innerWidth) * 2 - 1; - pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; -} - -// - -function animate() { - render(); - stats.update(); -} - -function render() { - theta += 0.1; - - camera.position.x = radius * Math.sin(THREE.MathUtils.degToRad(theta)); - camera.position.y = radius * Math.sin(THREE.MathUtils.degToRad(theta)); - camera.position.z = radius * Math.cos(THREE.MathUtils.degToRad(theta)); - camera.lookAt(scene.position); - - camera.updateMatrixWorld(); - - // find intersections - - raycaster.setFromCamera(pointer, camera); - - const intersects = raycaster.intersectObjects(parentTransform.children, true); - - if (intersects.length > 0) { - sphereInter.visible = true; - sphereInter.position.copy(intersects[0].point); - } else { - sphereInter.visible = false; - } - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_interactive_points.ts b/examples-testing/examples/webgl_interactive_points.ts deleted file mode 100644 index b6be0df05..000000000 --- a/examples-testing/examples/webgl_interactive_points.ts +++ /dev/null @@ -1,143 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js'; - -let renderer, scene, camera, stats; - -let particles; - -const PARTICLE_SIZE = 20; - -let raycaster, intersects; -let pointer, INTERSECTED; - -init(); - -function init() { - const container = document.getElementById('container'); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000); - camera.position.z = 250; - - // - - let boxGeometry = new THREE.BoxGeometry(200, 200, 200, 16, 16, 16); - - // if normal and uv attributes are not removed, mergeVertices() can't consolidate identical vertices with different normal/uv data - - boxGeometry.deleteAttribute('normal'); - boxGeometry.deleteAttribute('uv'); - - boxGeometry = BufferGeometryUtils.mergeVertices(boxGeometry); - - // - - const positionAttribute = boxGeometry.getAttribute('position'); - - const colors = []; - const sizes = []; - - const color = new THREE.Color(); - - for (let i = 0, l = positionAttribute.count; i < l; i++) { - color.setHSL(0.01 + 0.1 * (i / l), 1.0, 0.5); - color.toArray(colors, i * 3); - - sizes[i] = PARTICLE_SIZE * 0.5; - } - - const geometry = new THREE.BufferGeometry(); - geometry.setAttribute('position', positionAttribute); - geometry.setAttribute('customColor', new THREE.Float32BufferAttribute(colors, 3)); - geometry.setAttribute('size', new THREE.Float32BufferAttribute(sizes, 1)); - - // - - const material = new THREE.ShaderMaterial({ - uniforms: { - color: { value: new THREE.Color(0xffffff) }, - pointTexture: { value: new THREE.TextureLoader().load('textures/sprites/disc.png') }, - alphaTest: { value: 0.9 }, - }, - vertexShader: document.getElementById('vertexshader').textContent, - fragmentShader: document.getElementById('fragmentshader').textContent, - }); - - // - - particles = new THREE.Points(geometry, material); - scene.add(particles); - - // - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - // - - raycaster = new THREE.Raycaster(); - pointer = new THREE.Vector2(); - - // - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); - document.addEventListener('pointermove', onPointerMove); -} - -function onPointerMove(event) { - pointer.x = (event.clientX / window.innerWidth) * 2 - 1; - pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - render(); - stats.update(); -} - -function render() { - particles.rotation.x += 0.0005; - particles.rotation.y += 0.001; - - const geometry = particles.geometry; - const attributes = geometry.attributes; - - raycaster.setFromCamera(pointer, camera); - - intersects = raycaster.intersectObject(particles); - - if (intersects.length > 0) { - if (INTERSECTED != intersects[0].index) { - attributes.size.array[INTERSECTED] = PARTICLE_SIZE; - - INTERSECTED = intersects[0].index; - - attributes.size.array[INTERSECTED] = PARTICLE_SIZE * 1.25; - attributes.size.needsUpdate = true; - } - } else if (INTERSECTED !== null) { - attributes.size.array[INTERSECTED] = PARTICLE_SIZE; - attributes.size.needsUpdate = true; - INTERSECTED = null; - } - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_interactive_raycasting_points.ts b/examples-testing/examples/webgl_interactive_raycasting_points.ts deleted file mode 100644 index 50b8d1e1d..000000000 --- a/examples-testing/examples/webgl_interactive_raycasting_points.ts +++ /dev/null @@ -1,223 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -let renderer, scene, camera, stats; -let pointclouds; -let raycaster; -let intersection = null; -let spheresIndex = 0; -let timer; -let toggle = 0; - -const pointer = new THREE.Vector2(); -const spheres = []; - -const threshold = 0.1; -const pointSize = 0.05; -const width = 80; -const length = 160; -const rotateY = new THREE.Matrix4().makeRotationY(0.005); - -init(); - -function generatePointCloudGeometry(color, width, length) { - const geometry = new THREE.BufferGeometry(); - const numPoints = width * length; - - const positions = new Float32Array(numPoints * 3); - const colors = new Float32Array(numPoints * 3); - - let k = 0; - - for (let i = 0; i < width; i++) { - for (let j = 0; j < length; j++) { - const u = i / width; - const v = j / length; - const x = u - 0.5; - const y = (Math.cos(u * Math.PI * 4) + Math.sin(v * Math.PI * 8)) / 20; - const z = v - 0.5; - - positions[3 * k] = x; - positions[3 * k + 1] = y; - positions[3 * k + 2] = z; - - const intensity = (y + 0.1) * 5; - colors[3 * k] = color.r * intensity; - colors[3 * k + 1] = color.g * intensity; - colors[3 * k + 2] = color.b * intensity; - - k++; - } - } - - geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); - geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3)); - geometry.computeBoundingBox(); - - return geometry; -} - -function generatePointcloud(color, width, length) { - const geometry = generatePointCloudGeometry(color, width, length); - const material = new THREE.PointsMaterial({ size: pointSize, vertexColors: true }); - - return new THREE.Points(geometry, material); -} - -function generateIndexedPointcloud(color, width, length) { - const geometry = generatePointCloudGeometry(color, width, length); - const numPoints = width * length; - const indices = new Uint16Array(numPoints); - - let k = 0; - - for (let i = 0; i < width; i++) { - for (let j = 0; j < length; j++) { - indices[k] = k; - k++; - } - } - - geometry.setIndex(new THREE.BufferAttribute(indices, 1)); - - const material = new THREE.PointsMaterial({ size: pointSize, vertexColors: true }); - - return new THREE.Points(geometry, material); -} - -function generateIndexedWithOffsetPointcloud(color, width, length) { - const geometry = generatePointCloudGeometry(color, width, length); - const numPoints = width * length; - const indices = new Uint16Array(numPoints); - - let k = 0; - - for (let i = 0; i < width; i++) { - for (let j = 0; j < length; j++) { - indices[k] = k; - k++; - } - } - - geometry.setIndex(new THREE.BufferAttribute(indices, 1)); - geometry.addGroup(0, indices.length); - - const material = new THREE.PointsMaterial({ size: pointSize, vertexColors: true }); - - return new THREE.Points(geometry, material); -} - -function init() { - const container = document.getElementById('container'); - - scene = new THREE.Scene(); - - timer = new THREE.Timer(); - timer.connect(document); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000); - camera.position.set(10, 10, 10); - camera.lookAt(scene.position); - camera.updateMatrix(); - - // - - const pcBuffer = generatePointcloud(new THREE.Color(1, 0, 0), width, length); - pcBuffer.scale.set(5, 10, 10); - pcBuffer.position.set(-5, 0, 0); - scene.add(pcBuffer); - - const pcIndexed = generateIndexedPointcloud(new THREE.Color(0, 1, 0), width, length); - pcIndexed.scale.set(5, 10, 10); - pcIndexed.position.set(0, 0, 0); - scene.add(pcIndexed); - - const pcIndexedOffset = generateIndexedWithOffsetPointcloud(new THREE.Color(0, 1, 1), width, length); - pcIndexedOffset.scale.set(5, 10, 10); - pcIndexedOffset.position.set(5, 0, 0); - scene.add(pcIndexedOffset); - - pointclouds = [pcBuffer, pcIndexed, pcIndexedOffset]; - - // - - const sphereGeometry = new THREE.SphereGeometry(0.1, 32, 32); - const sphereMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 }); - - for (let i = 0; i < 40; i++) { - const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial); - scene.add(sphere); - spheres.push(sphere); - } - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - // - - raycaster = new THREE.Raycaster(); - raycaster.params.Points.threshold = threshold; - - // - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); - document.addEventListener('pointermove', onPointerMove); -} - -function onPointerMove(event) { - pointer.x = (event.clientX / window.innerWidth) * 2 - 1; - pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - render(); - stats.update(); -} - -function render() { - camera.applyMatrix4(rotateY); - camera.updateMatrixWorld(); - - raycaster.setFromCamera(pointer, camera); - - const intersections = raycaster.intersectObjects(pointclouds, false); - intersection = intersections.length > 0 ? intersections[0] : null; - - if (toggle > 0.02 && intersection !== null) { - spheres[spheresIndex].position.copy(intersection.point); - spheres[spheresIndex].scale.set(1, 1, 1); - spheresIndex = (spheresIndex + 1) % spheres.length; - - toggle = 0; - } - - for (let i = 0; i < spheres.length; i++) { - const sphere = spheres[i]; - sphere.scale.multiplyScalar(0.98); - sphere.scale.clampScalar(0.01, 1); - } - - toggle += timer.getDelta(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_interactive_voxelpainter.ts b/examples-testing/examples/webgl_interactive_voxelpainter.ts deleted file mode 100644 index 48b16f3b7..000000000 --- a/examples-testing/examples/webgl_interactive_voxelpainter.ts +++ /dev/null @@ -1,158 +0,0 @@ -import * as THREE from 'three'; - -let camera, scene, renderer; -let plane; -let pointer, - raycaster, - isShiftDown = false; - -let rollOverMesh, rollOverMaterial; -let cubeGeo, cubeMaterial; - -const objects = []; - -init(); -render(); - -function init() { - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000); - camera.position.set(500, 800, 1300); - camera.lookAt(0, 0, 0); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xf0f0f0); - - // roll-over helpers - - const rollOverGeo = new THREE.BoxGeometry(50, 50, 50); - rollOverMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000, opacity: 0.5, transparent: true }); - rollOverMesh = new THREE.Mesh(rollOverGeo, rollOverMaterial); - scene.add(rollOverMesh); - - // cubes - - const map = new THREE.TextureLoader().load('textures/square-outline-textured.png'); - map.colorSpace = THREE.SRGBColorSpace; - cubeGeo = new THREE.BoxGeometry(50, 50, 50); - cubeMaterial = new THREE.MeshLambertMaterial({ color: 0xfeb74c, map: map }); - - // grid - - const gridHelper = new THREE.GridHelper(1000, 20); - scene.add(gridHelper); - - // - - raycaster = new THREE.Raycaster(); - pointer = new THREE.Vector2(); - - const geometry = new THREE.PlaneGeometry(1000, 1000); - geometry.rotateX(-Math.PI / 2); - - plane = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ visible: false })); - scene.add(plane); - - objects.push(plane); - - // lights - - const ambientLight = new THREE.AmbientLight(0x606060, 3); - scene.add(ambientLight); - - const directionalLight = new THREE.DirectionalLight(0xffffff, 3); - directionalLight.position.set(1, 0.75, 0.5).normalize(); - scene.add(directionalLight); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - document.body.appendChild(renderer.domElement); - - document.addEventListener('pointermove', onPointerMove); - document.addEventListener('pointerdown', onPointerDown); - document.addEventListener('keydown', onDocumentKeyDown); - document.addEventListener('keyup', onDocumentKeyUp); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - render(); -} - -function onPointerMove(event) { - pointer.set((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1); - - raycaster.setFromCamera(pointer, camera); - - const intersects = raycaster.intersectObjects(objects, false); - - if (intersects.length > 0) { - const intersect = intersects[0]; - - rollOverMesh.position.copy(intersect.point).add(intersect.face.normal); - rollOverMesh.position.divideScalar(50).floor().multiplyScalar(50).addScalar(25); - - render(); - } -} - -function onPointerDown(event) { - pointer.set((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1); - - raycaster.setFromCamera(pointer, camera); - - const intersects = raycaster.intersectObjects(objects, false); - - if (intersects.length > 0) { - const intersect = intersects[0]; - - // delete cube - - if (isShiftDown) { - if (intersect.object !== plane) { - scene.remove(intersect.object); - - objects.splice(objects.indexOf(intersect.object), 1); - } - - // create cube - } else { - const voxel = new THREE.Mesh(cubeGeo, cubeMaterial); - voxel.position.copy(intersect.point).add(intersect.face.normal); - voxel.position.divideScalar(50).floor().multiplyScalar(50).addScalar(25); - scene.add(voxel); - - objects.push(voxel); - } - - render(); - } -} - -function onDocumentKeyDown(event) { - switch (event.keyCode) { - case 16: - isShiftDown = true; - break; - } -} - -function onDocumentKeyUp(event) { - switch (event.keyCode) { - case 16: - isShiftDown = false; - break; - } -} - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_lensflares.ts b/examples-testing/examples/webgl_lensflares.ts deleted file mode 100644 index 0a55bec42..000000000 --- a/examples-testing/examples/webgl_lensflares.ts +++ /dev/null @@ -1,140 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { FlyControls } from 'three/addons/controls/FlyControls.js'; -import { Lensflare, LensflareElement } from 'three/addons/objects/Lensflare.js'; - -let container, stats; - -let camera, scene, renderer; -let controls; - -const timer = new THREE.Timer(); -timer.connect(document); - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - // camera - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 15000); - camera.position.z = 250; - - // scene - - scene = new THREE.Scene(); - scene.background = new THREE.Color().setHSL(0.51, 0.4, 0.01, THREE.SRGBColorSpace); - scene.fog = new THREE.Fog(scene.background, 3500, 15000); - - // world - - const s = 250; - - const geometry = new THREE.BoxGeometry(s, s, s); - const material = new THREE.MeshPhongMaterial({ color: 0xffffff, specular: 0xffffff, shininess: 50 }); - - for (let i = 0; i < 3000; i++) { - const mesh = new THREE.Mesh(geometry, material); - - mesh.position.x = 8000 * (2.0 * Math.random() - 1.0); - mesh.position.y = 8000 * (2.0 * Math.random() - 1.0); - mesh.position.z = 8000 * (2.0 * Math.random() - 1.0); - - mesh.rotation.x = Math.random() * Math.PI; - mesh.rotation.y = Math.random() * Math.PI; - mesh.rotation.z = Math.random() * Math.PI; - - mesh.matrixAutoUpdate = false; - mesh.updateMatrix(); - - scene.add(mesh); - } - - // lights - - const dirLight = new THREE.DirectionalLight(0xffffff, 0.15); - dirLight.position.set(0, -1, 0).normalize(); - dirLight.color.setHSL(0.1, 0.7, 0.5); - scene.add(dirLight); - - // lensflares - const textureLoader = new THREE.TextureLoader(); - - const textureFlare0 = textureLoader.load('textures/lensflare/lensflare0.png'); - const textureFlare3 = textureLoader.load('textures/lensflare/lensflare3.png'); - - addLight(0.55, 0.9, 0.5, 5000, 0, -1000); - addLight(0.08, 0.8, 0.5, 0, 0, -1000); - addLight(0.995, 0.5, 0.9, 5000, 5000, -1000); - - function addLight(h, s, l, x, y, z) { - const light = new THREE.PointLight(0xffffff, 1.5, 2000, 0); - light.color.setHSL(h, s, l); - light.position.set(x, y, z); - scene.add(light); - - const lensflare = new Lensflare(); - lensflare.addElement(new LensflareElement(textureFlare0, 700, 0, light.color)); - lensflare.addElement(new LensflareElement(textureFlare3, 60, 0.6)); - lensflare.addElement(new LensflareElement(textureFlare3, 70, 0.7)); - lensflare.addElement(new LensflareElement(textureFlare3, 120, 0.9)); - lensflare.addElement(new LensflareElement(textureFlare3, 70, 1)); - light.add(lensflare); - } - - // renderer - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - // - - controls = new FlyControls(camera, renderer.domElement); - - controls.movementSpeed = 2500; - controls.domElement = container; - controls.rollSpeed = Math.PI / 6; - controls.autoForward = false; - controls.dragToLook = false; - - // stats - - stats = new Stats(); - container.appendChild(stats.dom); - - // events - - window.addEventListener('resize', onWindowResize); -} - -// - -function onWindowResize() { - renderer.setSize(window.innerWidth, window.innerHeight); - - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); -} - -// - -function animate() { - timer.update(); - - render(); - stats.update(); -} - -function render() { - const delta = timer.getDelta(); - - controls.update(delta); - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_lightprobe.ts b/examples-testing/examples/webgl_lightprobe.ts deleted file mode 100644 index 58f021e6d..000000000 --- a/examples-testing/examples/webgl_lightprobe.ts +++ /dev/null @@ -1,142 +0,0 @@ -import * as THREE from 'three'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { LightProbeGenerator } from 'three/addons/lights/LightProbeGenerator.js'; - -import { LightProbeHelper } from 'three/addons/helpers/LightProbeHelper.js'; - -let mesh, renderer, scene, camera; - -let gui; - -let lightProbe; -let directionalLight; - -// linear color space -const API = { - lightProbeIntensity: 1.0, - directionalLightIntensity: 0.6, - envMapIntensity: 1, -}; - -init(); - -function init() { - // renderer - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - document.body.appendChild(renderer.domElement); - - // tone mapping - renderer.toneMapping = THREE.NoToneMapping; - - // scene - scene = new THREE.Scene(); - - // camera - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(0, 0, 30); - - // controls - const controls = new OrbitControls(camera, renderer.domElement); - controls.addEventListener('change', render); - controls.minDistance = 10; - controls.maxDistance = 50; - controls.enablePan = false; - - // probe - lightProbe = new THREE.LightProbe(); - scene.add(lightProbe); - - // light - directionalLight = new THREE.DirectionalLight(0xffffff, API.directionalLightIntensity); - directionalLight.position.set(10, 10, 10); - scene.add(directionalLight); - - // envmap - const genCubeUrls = function (prefix, postfix) { - return [ - prefix + 'px' + postfix, - prefix + 'nx' + postfix, - prefix + 'py' + postfix, - prefix + 'ny' + postfix, - prefix + 'pz' + postfix, - prefix + 'nz' + postfix, - ]; - }; - - const urls = genCubeUrls('textures/cube/pisa/', '.png'); - - new THREE.CubeTextureLoader().load(urls, function (cubeTexture) { - scene.background = cubeTexture; - - lightProbe.copy(LightProbeGenerator.fromCubeTexture(cubeTexture)); - lightProbe.intensity = API.lightProbeIntensity; - lightProbe.position.set(-10, 0, 0); // position not used in scene lighting calculations (helper honors the position, however) - - const geometry = new THREE.SphereGeometry(5, 64, 32); - //const geometry = new THREE.TorusKnotGeometry( 4, 1.5, 256, 32, 2, 3 ); - - const material = new THREE.MeshStandardMaterial({ - color: 0xffffff, - metalness: 0, - roughness: 0, - envMap: cubeTexture, - envMapIntensity: API.envMapIntensity, - }); - - // mesh - mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - // helper - const helper = new LightProbeHelper(lightProbe, 1); - scene.add(helper); - - render(); - }); - - // gui - gui = new GUI({ title: 'Intensity' }); - - gui.add(API, 'lightProbeIntensity', 0, 1, 0.02) - .name('light probe') - .onChange(function () { - lightProbe.intensity = API.lightProbeIntensity; - render(); - }); - - gui.add(API, 'directionalLightIntensity', 0, 1, 0.02) - .name('directional light') - .onChange(function () { - directionalLight.intensity = API.directionalLightIntensity; - render(); - }); - - gui.add(API, 'envMapIntensity', 0, 1, 0.02) - .name('envMap') - .onChange(function () { - mesh.material.envMapIntensity = API.envMapIntensity; - render(); - }); - - // listener - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - renderer.setSize(window.innerWidth, window.innerHeight); - - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - render(); -} - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_lightprobe_cubecamera.ts b/examples-testing/examples/webgl_lightprobe_cubecamera.ts deleted file mode 100644 index 65425d4e7..000000000 --- a/examples-testing/examples/webgl_lightprobe_cubecamera.ts +++ /dev/null @@ -1,85 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { LightProbeHelper } from 'three/addons/helpers/LightProbeHelper.js'; -import { LightProbeGenerator } from 'three/addons/lights/LightProbeGenerator.js'; - -let renderer, scene, camera, cubeCamera; - -let lightProbe; - -init(); - -function init() { - // renderer - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - document.body.appendChild(renderer.domElement); - - // scene - scene = new THREE.Scene(); - - // camera - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(0, 0, 30); - - const cubeRenderTarget = new THREE.WebGLCubeRenderTarget(256); - - cubeCamera = new THREE.CubeCamera(1, 1000, cubeRenderTarget); - - // controls - const controls = new OrbitControls(camera, renderer.domElement); - controls.addEventListener('change', render); - controls.minDistance = 10; - controls.maxDistance = 50; - controls.enablePan = false; - - // probe - lightProbe = new THREE.LightProbe(); - scene.add(lightProbe); - - // envmap - const genCubeUrls = function (prefix, postfix) { - return [ - prefix + 'px' + postfix, - prefix + 'nx' + postfix, - prefix + 'py' + postfix, - prefix + 'ny' + postfix, - prefix + 'pz' + postfix, - prefix + 'nz' + postfix, - ]; - }; - - const urls = genCubeUrls('textures/cube/pisa/', '.png'); - - new THREE.CubeTextureLoader().load(urls, async function (cubeTexture) { - scene.background = cubeTexture; - - cubeCamera.update(renderer, scene); - - const probe = await LightProbeGenerator.fromCubeRenderTarget(renderer, cubeRenderTarget); - - lightProbe.copy(probe); - - scene.add(new LightProbeHelper(lightProbe, 5)); - - render(); - }); - - // listener - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - renderer.setSize(window.innerWidth, window.innerHeight); - - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - render(); -} - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_lights_hemisphere.ts b/examples-testing/examples/webgl_lights_hemisphere.ts deleted file mode 100644 index 2910b15de..000000000 --- a/examples-testing/examples/webgl_lights_hemisphere.ts +++ /dev/null @@ -1,191 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -let camera, scene, renderer; -const mixers = []; -let stats; - -const timer = new THREE.Timer(); -timer.connect(document); - -init(); - -function init() { - const container = document.getElementById('container'); - - camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 5000); - camera.position.set(0, 0, 250); - - scene = new THREE.Scene(); - scene.background = new THREE.Color().setHSL(0.6, 0, 1); - scene.fog = new THREE.Fog(scene.background, 1, 5000); - - // LIGHTS - - const hemiLight = new THREE.HemisphereLight(0xffffff, 0xffffff, 2); - hemiLight.color.setHSL(0.6, 1, 0.6); - hemiLight.groundColor.setHSL(0.095, 1, 0.75); - hemiLight.position.set(0, 50, 0); - scene.add(hemiLight); - - const hemiLightHelper = new THREE.HemisphereLightHelper(hemiLight, 10); - scene.add(hemiLightHelper); - - // - - const dirLight = new THREE.DirectionalLight(0xffffff, 3); - dirLight.color.setHSL(0.1, 1, 0.95); - dirLight.position.set(-1, 1.75, 1); - dirLight.position.multiplyScalar(30); - scene.add(dirLight); - - dirLight.castShadow = true; - - dirLight.shadow.mapSize.width = 2048; - dirLight.shadow.mapSize.height = 2048; - - const d = 50; - - dirLight.shadow.camera.left = -d; - dirLight.shadow.camera.right = d; - dirLight.shadow.camera.top = d; - dirLight.shadow.camera.bottom = -d; - - dirLight.shadow.camera.far = 3500; - dirLight.shadow.bias = -0.0001; - - const dirLightHelper = new THREE.DirectionalLightHelper(dirLight, 10); - scene.add(dirLightHelper); - - // GROUND - - const groundGeo = new THREE.PlaneGeometry(10000, 10000); - const groundMat = new THREE.MeshLambertMaterial({ color: 0xffffff }); - groundMat.color.setHSL(0.095, 1, 0.75); - - const ground = new THREE.Mesh(groundGeo, groundMat); - ground.position.y = -33; - ground.rotation.x = -Math.PI / 2; - ground.receiveShadow = true; - scene.add(ground); - - // SKYDOME - - const vertexShader = document.getElementById('vertexShader').textContent; - const fragmentShader = document.getElementById('fragmentShader').textContent; - const uniforms = { - topColor: { value: new THREE.Color(0x0077ff) }, - bottomColor: { value: new THREE.Color(0xffffff) }, - offset: { value: 33 }, - exponent: { value: 0.6 }, - }; - uniforms['topColor'].value.copy(hemiLight.color); - - scene.fog.color.copy(uniforms['bottomColor'].value); - - const skyGeo = new THREE.SphereGeometry(4000, 32, 15); - const skyMat = new THREE.ShaderMaterial({ - uniforms: uniforms, - vertexShader: vertexShader, - fragmentShader: fragmentShader, - side: THREE.BackSide, - }); - - const sky = new THREE.Mesh(skyGeo, skyMat); - scene.add(sky); - - // MODEL - - const loader = new GLTFLoader(); - - loader.load('models/gltf/Flamingo.glb', function (gltf) { - const mesh = gltf.scene.children[0]; - - const s = 0.35; - mesh.scale.set(s, s, s); - mesh.position.y = 15; - mesh.rotation.y = -1; - - mesh.castShadow = true; - mesh.receiveShadow = true; - - scene.add(mesh); - - const mixer = new THREE.AnimationMixer(mesh); - mixer.clipAction(gltf.animations[0]).setDuration(1).play(); - mixers.push(mixer); - }); - - // RENDERER - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - renderer.shadowMap.enabled = true; - - // STATS - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - const params = { - toggleHemisphereLight: function () { - hemiLight.visible = !hemiLight.visible; - hemiLightHelper.visible = !hemiLightHelper.visible; - }, - toggleDirectionalLight: function () { - dirLight.visible = !dirLight.visible; - dirLightHelper.visible = !dirLightHelper.visible; - }, - shadowIntensity: 1, - }; - - const gui = new GUI(); - - gui.add(params, 'toggleHemisphereLight').name('toggle hemisphere light'); - gui.add(params, 'toggleDirectionalLight').name('toggle directional light'); - gui.add(params, 'shadowIntensity', 0, 1) - .name('shadow intensity') - .onChange(value => { - dirLight.shadow.intensity = value; - }); - gui.open(); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - timer.update(); - - render(); - stats.update(); -} - -function render() { - const delta = timer.getDelta(); - - for (let i = 0; i < mixers.length; i++) { - mixers[i].update(delta); - } - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_lights_physical.ts b/examples-testing/examples/webgl_lights_physical.ts deleted file mode 100644 index 707ef200e..000000000 --- a/examples-testing/examples/webgl_lights_physical.ts +++ /dev/null @@ -1,237 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let camera, scene, renderer, bulbLight, bulbMat, hemiLight, stats; -let ballMat, cubeMat, floorMat; - -let previousShadowMap = false; - -// ref for lumens: http://www.power-sure.com/lumens.htm -const bulbLuminousPowers = { - '110000 lm (1000W)': 110000, - '3500 lm (300W)': 3500, - '1700 lm (100W)': 1700, - '800 lm (60W)': 800, - '400 lm (40W)': 400, - '180 lm (25W)': 180, - '20 lm (4W)': 20, - Off: 0, -}; - -// ref for solar irradiances: https://en.wikipedia.org/wiki/Lux -const hemiLuminousIrradiances = { - '0.0001 lx (Moonless Night)': 0.0001, - '0.002 lx (Night Airglow)': 0.002, - '0.5 lx (Full Moon)': 0.5, - '3.4 lx (City Twilight)': 3.4, - '50 lx (Living Room)': 50, - '100 lx (Very Overcast)': 100, - '350 lx (Office Room)': 350, - '400 lx (Sunrise/Sunset)': 400, - '1000 lx (Overcast)': 1000, - '18000 lx (Daylight)': 18000, - '50000 lx (Direct Sun)': 50000, -}; - -const params = { - shadows: true, - exposure: 0.68, - bulbPower: Object.keys(bulbLuminousPowers)[4], - hemiIrradiance: Object.keys(hemiLuminousIrradiances)[0], -}; - -init(); - -function init() { - const container = document.getElementById('container'); - - stats = new Stats(); - container.appendChild(stats.dom); - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.x = -4; - camera.position.z = 4; - camera.position.y = 2; - - scene = new THREE.Scene(); - - const bulbGeometry = new THREE.SphereGeometry(0.02, 16, 8); - bulbLight = new THREE.PointLight(0xffee88, 1, 100, 2); - - bulbMat = new THREE.MeshStandardMaterial({ - emissive: 0xffffee, - emissiveIntensity: 1, - color: 0x000000, - }); - bulbLight.add(new THREE.Mesh(bulbGeometry, bulbMat)); - bulbLight.position.set(0, 2, 0); - bulbLight.castShadow = true; - scene.add(bulbLight); - - hemiLight = new THREE.HemisphereLight(0xddeeff, 0x0f0e0d, 0.02); - scene.add(hemiLight); - - floorMat = new THREE.MeshStandardMaterial({ - roughness: 0.8, - color: 0xffffff, - metalness: 0.2, - bumpScale: 1, - }); - const textureLoader = new THREE.TextureLoader(); - textureLoader.load('textures/hardwood2_diffuse.jpg', function (map) { - map.wrapS = THREE.RepeatWrapping; - map.wrapT = THREE.RepeatWrapping; - map.anisotropy = 4; - map.repeat.set(10, 24); - map.colorSpace = THREE.SRGBColorSpace; - floorMat.map = map; - floorMat.needsUpdate = true; - }); - textureLoader.load('textures/hardwood2_bump.jpg', function (map) { - map.wrapS = THREE.RepeatWrapping; - map.wrapT = THREE.RepeatWrapping; - map.anisotropy = 4; - map.repeat.set(10, 24); - floorMat.bumpMap = map; - floorMat.needsUpdate = true; - }); - textureLoader.load('textures/hardwood2_roughness.jpg', function (map) { - map.wrapS = THREE.RepeatWrapping; - map.wrapT = THREE.RepeatWrapping; - map.anisotropy = 4; - map.repeat.set(10, 24); - floorMat.roughnessMap = map; - floorMat.needsUpdate = true; - }); - - cubeMat = new THREE.MeshStandardMaterial({ - roughness: 0.7, - color: 0xffffff, - bumpScale: 1, - metalness: 0.2, - }); - textureLoader.load('textures/brick_diffuse.jpg', function (map) { - map.wrapS = THREE.RepeatWrapping; - map.wrapT = THREE.RepeatWrapping; - map.anisotropy = 4; - map.repeat.set(1, 1); - map.colorSpace = THREE.SRGBColorSpace; - cubeMat.map = map; - cubeMat.needsUpdate = true; - }); - textureLoader.load('textures/brick_bump.jpg', function (map) { - map.wrapS = THREE.RepeatWrapping; - map.wrapT = THREE.RepeatWrapping; - map.anisotropy = 4; - map.repeat.set(1, 1); - cubeMat.bumpMap = map; - cubeMat.needsUpdate = true; - }); - - ballMat = new THREE.MeshStandardMaterial({ - color: 0xffffff, - roughness: 0.5, - metalness: 1.0, - }); - textureLoader.load('textures/planets/earth_atmos_2048.jpg', function (map) { - map.anisotropy = 4; - map.colorSpace = THREE.SRGBColorSpace; - ballMat.map = map; - ballMat.needsUpdate = true; - }); - textureLoader.load('textures/planets/earth_specular_2048.jpg', function (map) { - map.anisotropy = 4; - map.colorSpace = THREE.SRGBColorSpace; - ballMat.metalnessMap = map; - ballMat.needsUpdate = true; - }); - - const floorGeometry = new THREE.PlaneGeometry(20, 20); - const floorMesh = new THREE.Mesh(floorGeometry, floorMat); - floorMesh.receiveShadow = true; - floorMesh.rotation.x = -Math.PI / 2.0; - scene.add(floorMesh); - - const ballGeometry = new THREE.SphereGeometry(0.25, 32, 32); - const ballMesh = new THREE.Mesh(ballGeometry, ballMat); - ballMesh.position.set(1, 0.25, 1); - ballMesh.rotation.y = Math.PI; - ballMesh.castShadow = true; - scene.add(ballMesh); - - const boxGeometry = new THREE.BoxGeometry(0.5, 0.5, 0.5); - const boxMesh = new THREE.Mesh(boxGeometry, cubeMat); - boxMesh.position.set(-0.5, 0.25, -1); - boxMesh.castShadow = true; - scene.add(boxMesh); - - const boxMesh2 = new THREE.Mesh(boxGeometry, cubeMat); - boxMesh2.position.set(0, 0.25, -5); - boxMesh2.castShadow = true; - scene.add(boxMesh2); - - const boxMesh3 = new THREE.Mesh(boxGeometry, cubeMat); - boxMesh3.position.set(7, 0.25, 0); - boxMesh3.castShadow = true; - scene.add(boxMesh3); - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.shadowMap.enabled = true; - renderer.toneMapping = THREE.ReinhardToneMapping; - container.appendChild(renderer.domElement); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 1; - controls.maxDistance = 20; - - window.addEventListener('resize', onWindowResize); - - const gui = new GUI(); - - gui.add(params, 'hemiIrradiance', Object.keys(hemiLuminousIrradiances)); - gui.add(params, 'bulbPower', Object.keys(bulbLuminousPowers)); - gui.add(params, 'exposure', 0, 1); - gui.add(params, 'shadows'); - gui.open(); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - renderer.toneMappingExposure = Math.pow(params.exposure, 5.0); // to allow for very bright scenes. - renderer.shadowMap.enabled = params.shadows; - bulbLight.castShadow = params.shadows; - - if (params.shadows !== previousShadowMap) { - ballMat.needsUpdate = true; - cubeMat.needsUpdate = true; - floorMat.needsUpdate = true; - previousShadowMap = params.shadows; - } - - bulbLight.power = bulbLuminousPowers[params.bulbPower]; - bulbMat.emissiveIntensity = bulbLight.intensity / Math.pow(0.02, 2.0); // convert from intensity to irradiance at bulb surface - - hemiLight.intensity = hemiLuminousIrradiances[params.hemiIrradiance]; - const time = Date.now() * 0.0005; - - bulbLight.position.y = Math.cos(time) * 0.75 + 1.25; - - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/webgl_lights_rectarealight.ts b/examples-testing/examples/webgl_lights_rectarealight.ts deleted file mode 100644 index 621d183d8..000000000 --- a/examples-testing/examples/webgl_lights_rectarealight.ts +++ /dev/null @@ -1,110 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { RectAreaLightHelper } from 'three/addons/helpers/RectAreaLightHelper.js'; -import { RectAreaLightUniformsLib } from 'three/addons/lights/RectAreaLightUniformsLib.js'; - -let renderer, scene, camera; -let rectLight1, rectLight2, rectLight3; -let timer, stats; - -init(); - -function init() { - timer = new THREE.Timer(); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animation); - document.body.appendChild(renderer.domElement); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(0, 5, -15); - - scene = new THREE.Scene(); - - RectAreaLightUniformsLib.init(); - - rectLight1 = new THREE.RectAreaLight(0xff0000, 5, 4, 10); - rectLight1.position.set(-5, 6, 5); - scene.add(rectLight1); - - rectLight2 = new THREE.RectAreaLight(0x00ff00, 5, 4, 10); - rectLight2.position.set(0, 6, 5); - scene.add(rectLight2); - - rectLight3 = new THREE.RectAreaLight(0x0000ff, 5, 4, 10); - rectLight3.position.set(5, 6, 5); - scene.add(rectLight3); - - scene.add(new RectAreaLightHelper(rectLight1)); - scene.add(new RectAreaLightHelper(rectLight2)); - scene.add(new RectAreaLightHelper(rectLight3)); - - const geoFloor = new THREE.BoxGeometry(2000, 0.1, 2000); - const matStdFloor = new THREE.MeshStandardMaterial({ color: 0x444444 }); - matStdFloor.roughnessMap = createCheckerTexture(400); - const mshStdFloor = new THREE.Mesh(geoFloor, matStdFloor); - scene.add(mshStdFloor); - - const geoKnot = new THREE.TorusKnotGeometry(1.5, 0.5, 200, 16); - const matKnot = new THREE.MeshStandardMaterial({ color: 0xffffff, roughness: 0, metalness: 0 }); - const meshKnot = new THREE.Mesh(geoKnot, matKnot); - meshKnot.position.set(0, 5.5, 0); - scene.add(meshKnot); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.target.copy(meshKnot.position); - controls.update(); - - // - - window.addEventListener('resize', onWindowResize); - - stats = new Stats(); - document.body.appendChild(stats.dom); -} - -function createCheckerTexture(repeat = 1) { - const canvas = document.createElement('canvas'); - canvas.width = 2; - canvas.height = 2; - - const ctx = canvas.getContext('2d'); - ctx.fillStyle = '#000'; - ctx.fillRect(0, 0, 2, 2); - ctx.fillStyle = '#fff'; - ctx.fillRect(0, 0, 1, 1); - ctx.fillRect(1, 1, 1, 1); - - const texture = new THREE.CanvasTexture(canvas); - texture.repeat.set(repeat, repeat); - texture.magFilter = THREE.NearestFilter; - texture.wrapS = THREE.RepeatWrapping; - texture.wrapT = THREE.RepeatWrapping; - - return texture; -} - -function onWindowResize() { - renderer.setSize(window.innerWidth, window.innerHeight); - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); -} - -function animation() { - timer.update(); - - const delta = timer.getDelta(); - - rectLight1.rotation.y += -delta; - rectLight2.rotation.y += delta * 0.5; - rectLight3.rotation.y += delta; - - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/webgl_lights_spotlight.ts b/examples-testing/examples/webgl_lights_spotlight.ts deleted file mode 100644 index 78fec9d4e..000000000 --- a/examples-testing/examples/webgl_lights_spotlight.ts +++ /dev/null @@ -1,202 +0,0 @@ -import * as THREE from 'three'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { PLYLoader } from 'three/addons/loaders/PLYLoader.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let renderer, scene, camera; - -let spotLight; - -init(); - -function init() { - // Renderer - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - renderer.toneMapping = THREE.NeutralToneMapping; - renderer.toneMappingExposure = 1; - - renderer.shadowMap.enabled = true; - renderer.shadowMap.type = THREE.PCFShadowMap; - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(7, 4, 1); - - // Controls - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 2; - controls.maxDistance = 10; - controls.maxPolarAngle = Math.PI / 2; - controls.target.set(0, 1, 0); - controls.update(); - - // Textures - - const loader = new THREE.TextureLoader().setPath('textures/'); - const filenames = ['disturb.jpg', 'colors.png', 'uv_grid_opengl.jpg']; - - const textures = { none: null }; - - for (let i = 0; i < filenames.length; i++) { - const filename = filenames[i]; - - const texture = loader.load(filename); - texture.minFilter = THREE.LinearFilter; - texture.magFilter = THREE.LinearFilter; - texture.generateMipmaps = false; - texture.colorSpace = THREE.SRGBColorSpace; - - textures[filename] = texture; - } - - // Lights - - const ambient = new THREE.HemisphereLight(0xffffff, 0x8d8d8d, 0.25); - scene.add(ambient); - - spotLight = new THREE.SpotLight(0xffffff, 100); - spotLight.name = 'spotLight'; - spotLight.map = textures['disturb.jpg']; - spotLight.position.set(2.5, 5, 2.5); - spotLight.angle = Math.PI / 6; - spotLight.penumbra = 1; - spotLight.decay = 2; - spotLight.distance = 0; - - spotLight.castShadow = true; - spotLight.shadow.mapSize.width = 1024; - spotLight.shadow.mapSize.height = 1024; - spotLight.shadow.camera.near = 2; - spotLight.shadow.camera.far = 10; - spotLight.shadow.focus = 1; - spotLight.shadow.bias = -0.003; - spotLight.shadow.intensity = 1; - scene.add(spotLight); - - spotLight.lightHelper = new THREE.SpotLightHelper(spotLight); - spotLight.lightHelper.visible = false; - scene.add(spotLight.lightHelper); - - spotLight.shadowCameraHelper = new THREE.CameraHelper(spotLight.shadow.camera); // colored lines - spotLight.shadowCameraHelper.visible = false; - scene.add(spotLight.shadowCameraHelper); - - // - - const geometry = new THREE.PlaneGeometry(10, 10); - const material = new THREE.MeshLambertMaterial({ color: 0xbcbcbc }); - - const mesh = new THREE.Mesh(geometry, material); - mesh.position.set(0, -1, 0); - mesh.rotation.x = -Math.PI / 2; - mesh.receiveShadow = true; - scene.add(mesh); - - // Model - - new PLYLoader().load('models/ply/binary/Lucy100k.ply', function (geometry) { - geometry.scale(0.0024, 0.0024, 0.0024); - geometry.computeVertexNormals(); - - const material = new THREE.MeshLambertMaterial(); - - const mesh = new THREE.Mesh(geometry, material); - mesh.rotation.y = -Math.PI / 2; - mesh.position.y = 0.8; - mesh.castShadow = true; - mesh.receiveShadow = true; - scene.add(mesh); - }); - - // - - window.addEventListener('resize', onWindowResize); - - // GUI - - const gui = new GUI(); - - const params = { - map: textures['disturb.jpg'], - color: spotLight.color.getHex(), - intensity: spotLight.intensity, - distance: spotLight.distance, - angle: spotLight.angle, - penumbra: spotLight.penumbra, - decay: spotLight.decay, - focus: spotLight.shadow.focus, - shadowIntensity: spotLight.shadow.intensity, - helpers: false, - }; - - gui.add(params, 'map', textures).onChange(function (val) { - spotLight.map = val; - }); - - gui.addColor(params, 'color').onChange(function (val) { - spotLight.color.setHex(val); - }); - - gui.add(params, 'intensity', 0, 500).onChange(function (val) { - spotLight.intensity = val; - }); - - gui.add(params, 'distance', 0, 20).onChange(function (val) { - spotLight.distance = val; - }); - - gui.add(params, 'angle', 0, Math.PI / 3).onChange(function (val) { - spotLight.angle = val; - }); - - gui.add(params, 'penumbra', 0, 1).onChange(function (val) { - spotLight.penumbra = val; - }); - - gui.add(params, 'decay', 1, 2).onChange(function (val) { - spotLight.decay = val; - }); - - gui.add(params, 'focus', 0, 1).onChange(function (val) { - spotLight.shadow.focus = val; - }); - - gui.add(params, 'shadowIntensity', 0, 1).onChange(function (val) { - spotLight.shadow.intensity = val; - }); - - gui.add(params, 'helpers').onChange(function (val) { - spotLight.lightHelper.visible = val; - spotLight.shadowCameraHelper.visible = val; - }); - - gui.open(); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - const time = performance.now() / 3000; - - spotLight.position.x = Math.cos(time) * 2.5; - spotLight.position.z = Math.sin(time) * 2.5; - - spotLight.lightHelper.update(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_lights_spotlights.ts b/examples-testing/examples/webgl_lights_spotlights.ts deleted file mode 100644 index be8dfd69d..000000000 --- a/examples-testing/examples/webgl_lights_spotlights.ts +++ /dev/null @@ -1,133 +0,0 @@ -import * as THREE from 'three'; -import TWEEN from 'three/addons/libs/tween.module.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -const renderer = new THREE.WebGLRenderer({ antialias: true }); -renderer.setPixelRatio(window.devicePixelRatio); -renderer.setSize(window.innerWidth, window.innerHeight); -renderer.setAnimationLoop(animate); - -const camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 0.1, 100); - -const controls = new OrbitControls(camera, renderer.domElement); - -const scene = new THREE.Scene(); - -const matFloor = new THREE.MeshPhongMaterial({ color: 0x808080 }); -const matBox = new THREE.MeshPhongMaterial({ color: 0xaaaaaa }); - -const geoFloor = new THREE.PlaneGeometry(100, 100); -const geoBox = new THREE.BoxGeometry(0.3, 0.1, 0.2); - -const mshFloor = new THREE.Mesh(geoFloor, matFloor); -mshFloor.rotation.x = -Math.PI * 0.5; -const mshBox = new THREE.Mesh(geoBox, matBox); - -const ambient = new THREE.AmbientLight(0x444444); - -const spotLight1 = createSpotlight(0xff7f00); -const spotLight2 = createSpotlight(0x00ff7f); -const spotLight3 = createSpotlight(0x7f00ff); - -let lightHelper1, lightHelper2, lightHelper3; - -function init() { - renderer.shadowMap.enabled = true; - renderer.shadowMap.type = THREE.PCFShadowMap; - - camera.position.set(4.6, 2.2, -2.1); - - spotLight1.position.set(1.5, 4, 4.5); - spotLight2.position.set(0, 4, 3.5); - spotLight3.position.set(-1.5, 4, 4.5); - - lightHelper1 = new THREE.SpotLightHelper(spotLight1); - lightHelper2 = new THREE.SpotLightHelper(spotLight2); - lightHelper3 = new THREE.SpotLightHelper(spotLight3); - - mshFloor.receiveShadow = true; - mshFloor.position.set(0, -0.05, 0); - - mshBox.castShadow = true; - mshBox.receiveShadow = true; - mshBox.position.set(0, 0.5, 0); - - scene.add(mshFloor); - scene.add(mshBox); - scene.add(ambient); - scene.add(spotLight1, spotLight2, spotLight3); - scene.add(lightHelper1, lightHelper2, lightHelper3); - - document.body.appendChild(renderer.domElement); - window.addEventListener('resize', onWindowResize); - - controls.target.set(0, 0.5, 0); - controls.maxPolarAngle = Math.PI / 2; - controls.minDistance = 1; - controls.maxDistance = 10; - controls.update(); -} - -function createSpotlight(color) { - const newObj = new THREE.SpotLight(color, 10); - - newObj.castShadow = true; - newObj.angle = 0.3; - newObj.penumbra = 0.2; - newObj.decay = 2; - newObj.distance = 50; - - return newObj; -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function tween(light) { - new TWEEN.Tween(light) - .to( - { - angle: Math.random() * 0.7 + 0.1, - penumbra: Math.random() + 1, - }, - Math.random() * 3000 + 2000, - ) - .easing(TWEEN.Easing.Quadratic.Out) - .start(); - - new TWEEN.Tween(light.position) - .to( - { - x: Math.random() * 3 - 1.5, - y: Math.random() * 1 + 1.5, - z: Math.random() * 3 - 1.5, - }, - Math.random() * 3000 + 2000, - ) - .easing(TWEEN.Easing.Quadratic.Out) - .start(); -} - -function updateTweens() { - tween(spotLight1); - tween(spotLight2); - tween(spotLight3); - - setTimeout(updateTweens, 5000); -} - -function animate() { - TWEEN.update(); - - if (lightHelper1) lightHelper1.update(); - if (lightHelper2) lightHelper2.update(); - if (lightHelper3) lightHelper3.update(); - - renderer.render(scene, camera); -} - -init(); -updateTweens(); diff --git a/examples-testing/examples/webgl_lines_colors.ts b/examples-testing/examples/webgl_lines_colors.ts deleted file mode 100644 index 9da19ee2e..000000000 --- a/examples-testing/examples/webgl_lines_colors.ts +++ /dev/null @@ -1,181 +0,0 @@ -import * as THREE from 'three'; - -import * as GeometryUtils from 'three/addons/utils/GeometryUtils.js'; - -let mouseX = 0, - mouseY = 0; - -let windowHalfX = window.innerWidth / 2; -let windowHalfY = window.innerHeight / 2; - -let camera, scene, renderer; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(33, window.innerWidth / window.innerHeight, 1, 10000); - camera.position.z = 1000; - - scene = new THREE.Scene(); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // - - const hilbertPoints = GeometryUtils.hilbert3D(new THREE.Vector3(0, 0, 0), 200.0, 1, 0, 1, 2, 3, 4, 5, 6, 7); - - const geometry1 = new THREE.BufferGeometry(); - const geometry2 = new THREE.BufferGeometry(); - const geometry3 = new THREE.BufferGeometry(); - - const subdivisions = 6; - - let vertices = []; - let colors1 = []; - let colors2 = []; - let colors3 = []; - - const point = new THREE.Vector3(); - const color = new THREE.Color(); - - const spline = new THREE.CatmullRomCurve3(hilbertPoints); - - for (let i = 0; i < hilbertPoints.length * subdivisions; i++) { - const t = i / (hilbertPoints.length * subdivisions); - spline.getPoint(t, point); - - vertices.push(point.x, point.y, point.z); - - color.setHSL(0.6, 1.0, Math.max(0, -point.x / 200) + 0.5, THREE.SRGBColorSpace); - colors1.push(color.r, color.g, color.b); - - color.setHSL(0.9, 1.0, Math.max(0, -point.y / 200) + 0.5, THREE.SRGBColorSpace); - colors2.push(color.r, color.g, color.b); - - color.setHSL(i / (hilbertPoints.length * subdivisions), 1.0, 0.5, THREE.SRGBColorSpace); - colors3.push(color.r, color.g, color.b); - } - - geometry1.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); - geometry2.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); - geometry3.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); - - geometry1.setAttribute('color', new THREE.Float32BufferAttribute(colors1, 3)); - geometry2.setAttribute('color', new THREE.Float32BufferAttribute(colors2, 3)); - geometry3.setAttribute('color', new THREE.Float32BufferAttribute(colors3, 3)); - - // - - const geometry4 = new THREE.BufferGeometry(); - const geometry5 = new THREE.BufferGeometry(); - const geometry6 = new THREE.BufferGeometry(); - - vertices = []; - colors1 = []; - colors2 = []; - colors3 = []; - - for (let i = 0; i < hilbertPoints.length; i++) { - const point = hilbertPoints[i]; - - vertices.push(point.x, point.y, point.z); - - color.setHSL(0.6, 1.0, Math.max(0, (200 - hilbertPoints[i].x) / 400) * 0.5 + 0.5, THREE.SRGBColorSpace); - colors1.push(color.r, color.g, color.b); - - color.setHSL(0.3, 1.0, Math.max(0, (200 + hilbertPoints[i].x) / 400) * 0.5, THREE.SRGBColorSpace); - colors2.push(color.r, color.g, color.b); - - color.setHSL(i / hilbertPoints.length, 1.0, 0.5, THREE.SRGBColorSpace); - colors3.push(color.r, color.g, color.b); - } - - geometry4.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); - geometry5.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); - geometry6.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); - - geometry4.setAttribute('color', new THREE.Float32BufferAttribute(colors1, 3)); - geometry5.setAttribute('color', new THREE.Float32BufferAttribute(colors2, 3)); - geometry6.setAttribute('color', new THREE.Float32BufferAttribute(colors3, 3)); - - // Create lines and add to scene - - const material = new THREE.LineBasicMaterial({ color: 0xffffff, vertexColors: true }); - - let line, p; - const scale = 0.3, - d = 225; - - const parameters = [ - [material, scale * 1.5, [-d, -d / 2, 0], geometry1], - [material, scale * 1.5, [0, -d / 2, 0], geometry2], - [material, scale * 1.5, [d, -d / 2, 0], geometry3], - - [material, scale * 1.5, [-d, d / 2, 0], geometry4], - [material, scale * 1.5, [0, d / 2, 0], geometry5], - [material, scale * 1.5, [d, d / 2, 0], geometry6], - ]; - - for (let i = 0; i < parameters.length; i++) { - p = parameters[i]; - line = new THREE.Line(p[3], p[0]); - line.scale.x = line.scale.y = line.scale.z = p[1]; - line.position.x = p[2][0]; - line.position.y = p[2][1]; - line.position.z = p[2][2]; - scene.add(line); - } - - // - - document.body.style.touchAction = 'none'; - document.body.addEventListener('pointermove', onPointerMove); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - windowHalfX = window.innerWidth / 2; - windowHalfY = window.innerHeight / 2; - - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function onPointerMove(event) { - if (event.isPrimary === false) return; - - mouseX = event.clientX - windowHalfX; - mouseY = event.clientY - windowHalfY; -} - -// - -function animate() { - camera.position.x += (mouseX - camera.position.x) * 0.05; - camera.position.y += (-mouseY + 200 - camera.position.y) * 0.05; - - camera.lookAt(scene.position); - - const time = Date.now() * 0.0005; - - for (let i = 0; i < scene.children.length; i++) { - const object = scene.children[i]; - - if (object.isLine) { - object.rotation.y = time * (i % 2 ? 1 : -1); - } - } - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_lines_dashed.ts b/examples-testing/examples/webgl_lines_dashed.ts deleted file mode 100644 index 3e3ee3041..000000000 --- a/examples-testing/examples/webgl_lines_dashed.ts +++ /dev/null @@ -1,186 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import * as GeometryUtils from 'three/addons/utils/GeometryUtils.js'; - -let renderer, scene, camera, stats; -const objects = []; - -const WIDTH = window.innerWidth, - HEIGHT = window.innerHeight; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(60, WIDTH / HEIGHT, 1, 200); - camera.position.z = 150; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x111111); - scene.fog = new THREE.Fog(0x111111, 150, 200); - - const subdivisions = 6; - const recursion = 1; - - const points = GeometryUtils.hilbert3D(new THREE.Vector3(0, 0, 0), 25.0, recursion, 0, 1, 2, 3, 4, 5, 6, 7); - const spline = new THREE.CatmullRomCurve3(points); - - const samples = spline.getPoints(points.length * subdivisions); - const geometrySpline = new THREE.BufferGeometry().setFromPoints(samples); - - const line = new THREE.Line( - geometrySpline, - new THREE.LineDashedMaterial({ color: 0xffffff, dashSize: 1, gapSize: 0.5 }), - ); - line.computeLineDistances(); - - objects.push(line); - scene.add(line); - - const geometryBox = box(50, 50, 50); - - const lineSegments = new THREE.LineSegments( - geometryBox, - new THREE.LineDashedMaterial({ color: 0xffaa00, dashSize: 3, gapSize: 1 }), - ); - lineSegments.computeLineDistances(); - - objects.push(lineSegments); - scene.add(lineSegments); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(WIDTH, HEIGHT); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - stats = new Stats(); - document.body.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); -} - -function box(width, height, depth) { - ((width = width * 0.5), (height = height * 0.5), (depth = depth * 0.5)); - - const geometry = new THREE.BufferGeometry(); - const position = []; - - position.push( - -width, - -height, - -depth, - -width, - height, - -depth, - - -width, - height, - -depth, - width, - height, - -depth, - - width, - height, - -depth, - width, - -height, - -depth, - - width, - -height, - -depth, - -width, - -height, - -depth, - - -width, - -height, - depth, - -width, - height, - depth, - - -width, - height, - depth, - width, - height, - depth, - - width, - height, - depth, - width, - -height, - depth, - - width, - -height, - depth, - -width, - -height, - depth, - - -width, - -height, - -depth, - -width, - -height, - depth, - - -width, - height, - -depth, - -width, - height, - depth, - - width, - height, - -depth, - width, - height, - depth, - - width, - -height, - -depth, - width, - -height, - depth, - ); - - geometry.setAttribute('position', new THREE.Float32BufferAttribute(position, 3)); - - return geometry; -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - render(); - stats.update(); -} - -function render() { - const time = Date.now() * 0.001; - - scene.traverse(function (object) { - if (object.isLine) { - object.rotation.x = 0.25 * time; - object.rotation.y = 0.25 * time; - } - }); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_lines_fat.ts b/examples-testing/examples/webgl_lines_fat.ts deleted file mode 100644 index b5b66bbb1..000000000 --- a/examples-testing/examples/webgl_lines_fat.ts +++ /dev/null @@ -1,235 +0,0 @@ -import * as THREE from 'three'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { Line2 } from 'three/addons/lines/Line2.js'; -import { LineMaterial } from 'three/addons/lines/LineMaterial.js'; -import { LineGeometry } from 'three/addons/lines/LineGeometry.js'; -import * as GeometryUtils from 'three/addons/utils/GeometryUtils.js'; - -let line, renderer, scene, camera, camera2, controls; -let line1; -let matLine, matLineBasic, matLineDashed; -let gui; - -// viewport -let insetWidth; -let insetHeight; - -init(); - -function init() { - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setClearColor(0x000000, 0.0); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(-40, 0, 60); - - camera2 = new THREE.PerspectiveCamera(40, 1, 1, 1000); - camera2.position.copy(camera.position); - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - controls.minDistance = 10; - controls.maxDistance = 500; - - // Position and THREE.Color Data - - const positions = []; - const colors = []; - - const points = GeometryUtils.hilbert3D(new THREE.Vector3(0, 0, 0), 20.0, 1, 0, 1, 2, 3, 4, 5, 6, 7); - - const spline = new THREE.CatmullRomCurve3(points); - const divisions = Math.round(12 * points.length); - const point = new THREE.Vector3(); - const color = new THREE.Color(); - - for (let i = 0, l = divisions; i < l; i++) { - const t = i / l; - - spline.getPoint(t, point); - positions.push(point.x, point.y, point.z); - - color.setHSL(t, 1.0, 0.5, THREE.SRGBColorSpace); - colors.push(color.r, color.g, color.b); - } - - // Line2 ( LineGeometry, LineMaterial ) - - const geometry = new LineGeometry(); - geometry.setPositions(positions); - geometry.setColors(colors); - - matLine = new LineMaterial({ - color: 0xffffff, - linewidth: 5, // in world units with size attenuation, pixels otherwise - vertexColors: true, - - dashed: false, - alphaToCoverage: true, - }); - - line = new Line2(geometry, matLine); - line.computeLineDistances(); - line.scale.set(1, 1, 1); - scene.add(line); - - // THREE.Line ( THREE.BufferGeometry, THREE.LineBasicMaterial ) - rendered with gl.LINE_STRIP - - const geo = new THREE.BufferGeometry(); - geo.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)); - geo.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); - - matLineBasic = new THREE.LineBasicMaterial({ vertexColors: true }); - matLineDashed = new THREE.LineDashedMaterial({ vertexColors: true, scale: 2, dashSize: 1, gapSize: 1 }); - - line1 = new THREE.Line(geo, matLineBasic); - line1.computeLineDistances(); - line1.visible = false; - scene.add(line1); - - // - - window.addEventListener('resize', onWindowResize); - onWindowResize(); - - initGui(); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - insetWidth = window.innerHeight / 4; // square - insetHeight = window.innerHeight / 4; - - camera2.aspect = insetWidth / insetHeight; - camera2.updateProjectionMatrix(); -} - -function animate() { - // main scene - - renderer.setClearColor(0x000000, 0); - - renderer.setViewport(0, 0, window.innerWidth, window.innerHeight); - - controls.update(); - - renderer.render(scene, camera); - - // inset scene - - renderer.setClearColor(0x222222, 1); - - renderer.clearDepth(); // important! - - renderer.setScissorTest(true); - - renderer.setScissor(20, 20, insetWidth, insetHeight); - - renderer.setViewport(20, 20, insetWidth, insetHeight); - - camera2.position.copy(camera.position); - camera2.quaternion.copy(camera.quaternion); - - renderer.render(scene, camera2); - - renderer.setScissorTest(false); -} - -// - -function initGui() { - gui = new GUI(); - - const param = { - 'line type': 0, - 'world units': false, - width: 5, - alphaToCoverage: true, - dashed: false, - 'dash scale': 1, - 'dash / gap': 1, - }; - - gui.add(param, 'line type', { LineGeometry: 0, 'gl.LINE': 1 }).onChange(function (val) { - switch (val) { - case 0: - line.visible = true; - - line1.visible = false; - - break; - - case 1: - line.visible = false; - - line1.visible = true; - - break; - } - }); - - gui.add(param, 'world units').onChange(function (val) { - matLine.worldUnits = val; - }); - - gui.add(param, 'width', 1, 10).onChange(function (val) { - matLine.linewidth = val; - }); - - gui.add(param, 'alphaToCoverage').onChange(function (val) { - matLine.alphaToCoverage = val; - }); - - gui.add(param, 'dashed').onChange(function (val) { - matLine.dashed = val; - line1.material = val ? matLineDashed : matLineBasic; - }); - - gui.add(param, 'dash scale', 0.5, 2, 0.1).onChange(function (val) { - matLine.dashScale = val; - matLineDashed.scale = val; - }); - - gui.add(param, 'dash / gap', { '2 : 1': 0, '1 : 1': 1, '1 : 2': 2 }).onChange(function (val) { - switch (val) { - case 0: - matLine.dashSize = 2; - matLine.gapSize = 1; - - matLineDashed.dashSize = 2; - matLineDashed.gapSize = 1; - - break; - - case 1: - matLine.dashSize = 1; - matLine.gapSize = 1; - - matLineDashed.dashSize = 1; - matLineDashed.gapSize = 1; - - break; - - case 2: - matLine.dashSize = 1; - matLine.gapSize = 2; - - matLineDashed.dashSize = 1; - matLineDashed.gapSize = 2; - - break; - } - }); -} diff --git a/examples-testing/examples/webgl_lines_fat_raycasting.ts b/examples-testing/examples/webgl_lines_fat_raycasting.ts deleted file mode 100644 index ac9400da3..000000000 --- a/examples-testing/examples/webgl_lines_fat_raycasting.ts +++ /dev/null @@ -1,280 +0,0 @@ -import * as THREE from 'three'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { LineMaterial } from 'three/addons/lines/LineMaterial.js'; -import { LineSegments2 } from 'three/addons/lines/LineSegments2.js'; -import { LineSegmentsGeometry } from 'three/addons/lines/LineSegmentsGeometry.js'; -import { Line2 } from 'three/addons/lines/Line2.js'; -import { LineGeometry } from 'three/addons/lines/LineGeometry.js'; - -let line, thresholdLine, segments, thresholdSegments; -let renderer, scene, camera, controls; -let sphereInter, sphereOnLine; -let gui; -let timer; - -const color = new THREE.Color(); - -const pointer = new THREE.Vector2(Infinity, Infinity); - -const raycaster = new THREE.Raycaster(); - -raycaster.params.Line2 = {}; -raycaster.params.Line2.threshold = 0; - -const matLine = new LineMaterial({ - color: 0xffffff, - linewidth: 1, // in world units with size attenuation, pixels otherwise - worldUnits: true, - vertexColors: true, - - alphaToCoverage: true, -}); - -const matThresholdLine = new LineMaterial({ - color: 0xffffff, - linewidth: matLine.linewidth, // in world units with size attenuation, pixels otherwise - worldUnits: true, - // vertexColors: true, - transparent: true, - opacity: 0.2, - depthTest: false, - visible: false, -}); - -const params = { - 'line type': 0, - 'world units': matLine.worldUnits, - 'visualize threshold': matThresholdLine.visible, - width: matLine.linewidth, - alphaToCoverage: matLine.alphaToCoverage, - threshold: raycaster.params.Line2.threshold, - translation: 0, - animate: true, -}; - -init(); - -function init() { - timer = new THREE.Timer(); - timer.connect(document); - - renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setClearColor(0x000000, 0.0); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(-40, 0, 60); - - controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 10; - controls.maxDistance = 500; - - const sphereGeometry = new THREE.SphereGeometry(0.25, 8, 4); - const sphereInterMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000, depthTest: false }); - const sphereOnLineMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00, depthTest: false }); - - sphereInter = new THREE.Mesh(sphereGeometry, sphereInterMaterial); - sphereOnLine = new THREE.Mesh(sphereGeometry, sphereOnLineMaterial); - sphereInter.visible = false; - sphereOnLine.visible = false; - sphereInter.renderOrder = 10; - sphereOnLine.renderOrder = 10; - scene.add(sphereInter); - scene.add(sphereOnLine); - - // Position and THREE.Color Data - - const positions = []; - const colors = []; - const points = []; - for (let i = -50; i < 50; i++) { - const t = i / 3; - points.push(new THREE.Vector3(t * Math.sin(2 * t), t, t * Math.cos(2 * t))); - } - - const spline = new THREE.CatmullRomCurve3(points); - const divisions = Math.round(3 * points.length); - const point = new THREE.Vector3(); - const color = new THREE.Color(); - - for (let i = 0, l = divisions; i < l; i++) { - const t = i / l; - - spline.getPoint(t, point); - positions.push(point.x, point.y, point.z); - - color.setHSL(t, 1.0, 0.5, THREE.SRGBColorSpace); - colors.push(color.r, color.g, color.b); - } - - const lineGeometry = new LineGeometry(); - lineGeometry.setPositions(positions); - lineGeometry.setColors(colors); - - const segmentsGeometry = new LineSegmentsGeometry(); - segmentsGeometry.setPositions(positions); - segmentsGeometry.setColors(colors); - - segments = new LineSegments2(segmentsGeometry, matLine); - segments.computeLineDistances(); - segments.scale.set(1, 1, 1); - scene.add(segments); - segments.visible = false; - - thresholdSegments = new LineSegments2(segmentsGeometry, matThresholdLine); - thresholdSegments.computeLineDistances(); - thresholdSegments.scale.set(1, 1, 1); - scene.add(thresholdSegments); - thresholdSegments.visible = false; - - line = new Line2(lineGeometry, matLine); - line.computeLineDistances(); - line.scale.set(1, 1, 1); - scene.add(line); - - thresholdLine = new Line2(lineGeometry, matThresholdLine); - thresholdLine.computeLineDistances(); - thresholdLine.scale.set(1, 1, 1); - scene.add(thresholdLine); - - const geo = new THREE.BufferGeometry(); - geo.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)); - geo.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); - - // - - document.addEventListener('pointermove', onPointerMove); - window.addEventListener('resize', onWindowResize); - onWindowResize(); - - initGui(); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function onPointerMove(event) { - pointer.x = (event.clientX / window.innerWidth) * 2 - 1; - pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; -} - -function animate() { - timer.update(); - - const delta = timer.getDelta(); - - const obj = line.visible ? line : segments; - thresholdLine.position.copy(line.position); - thresholdLine.quaternion.copy(line.quaternion); - thresholdSegments.position.copy(segments.position); - thresholdSegments.quaternion.copy(segments.quaternion); - - if (params.animate) { - line.rotation.y += delta * 0.1; - - segments.rotation.y = line.rotation.y; - } - - raycaster.setFromCamera(pointer, camera); - - const intersects = raycaster.intersectObject(obj); - - if (intersects.length > 0) { - sphereInter.visible = true; - sphereOnLine.visible = true; - - sphereInter.position.copy(intersects[0].point); - sphereOnLine.position.copy(intersects[0].pointOnLine); - - const index = intersects[0].faceIndex; - const colors = obj.geometry.getAttribute('instanceColorStart'); - - color.fromBufferAttribute(colors, index); - - sphereInter.material.color.copy(color).offsetHSL(0.3, 0, 0); - sphereOnLine.material.color.copy(color).offsetHSL(0.7, 0, 0); - - renderer.domElement.style.cursor = 'crosshair'; - } else { - sphereInter.visible = false; - sphereOnLine.visible = false; - renderer.domElement.style.cursor = ''; - } - - renderer.render(scene, camera); -} - -// - -function switchLine(val) { - switch (val) { - case 0: - line.visible = true; - thresholdLine.visible = true; - - segments.visible = false; - thresholdSegments.visible = false; - - break; - - case 1: - line.visible = false; - thresholdLine.visible = false; - - segments.visible = true; - thresholdSegments.visible = true; - - break; - } -} - -function initGui() { - gui = new GUI(); - - gui.add(params, 'line type', { LineGeometry: 0, LineSegmentsGeometry: 1 }) - .onChange(function (val) { - switchLine(val); - }) - .setValue(1); - - gui.add(params, 'world units').onChange(function (val) { - matLine.worldUnits = val; - matThresholdLine.worldUnits = val; - }); - - gui.add(params, 'visualize threshold').onChange(function (val) { - matThresholdLine.visible = val; - }); - - gui.add(params, 'width', 1, 10).onChange(function (val) { - matLine.linewidth = val; - matThresholdLine.linewidth = matLine.linewidth + raycaster.params.Line2.threshold; - }); - - gui.add(params, 'alphaToCoverage').onChange(function (val) { - matLine.alphaToCoverage = val; - }); - - gui.add(params, 'threshold', 0, 10).onChange(function (val) { - raycaster.params.Line2.threshold = val; - matThresholdLine.linewidth = matLine.linewidth + raycaster.params.Line2.threshold; - }); - - gui.add(params, 'translation', 0, 10).onChange(function (val) { - line.position.x = val; - segments.position.x = val; - }); - - gui.add(params, 'animate'); -} diff --git a/examples-testing/examples/webgl_lines_fat_wireframe.ts b/examples-testing/examples/webgl_lines_fat_wireframe.ts deleted file mode 100644 index 59660ad7e..000000000 --- a/examples-testing/examples/webgl_lines_fat_wireframe.ts +++ /dev/null @@ -1,210 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { LineMaterial } from 'three/addons/lines/LineMaterial.js'; -import { Wireframe } from 'three/addons/lines/Wireframe.js'; -import { WireframeGeometry2 } from 'three/addons/lines/WireframeGeometry2.js'; - -let wireframe, renderer, scene, camera, camera2, controls; -let wireframe1; -let matLine, matLineBasic, matLineDashed; -let stats; -let gui; - -// viewport -let insetWidth; -let insetHeight; - -init(); - -function init() { - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setClearColor(0x000000, 0.0); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(-50, 0, 50); - - camera2 = new THREE.PerspectiveCamera(40, 1, 1, 1000); - camera2.position.copy(camera.position); - - controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 10; - controls.maxDistance = 500; - - // Wireframe ( WireframeGeometry2, LineMaterial ) - - let geo = new THREE.IcosahedronGeometry(20, 1); - - const geometry = new WireframeGeometry2(geo); - - matLine = new LineMaterial({ - color: 0x4080ff, - linewidth: 5, // in pixels - dashed: false, - }); - - wireframe = new Wireframe(geometry, matLine); - wireframe.computeLineDistances(); - wireframe.scale.set(1, 1, 1); - scene.add(wireframe); - - // Line ( THREE.WireframeGeometry, THREE.LineBasicMaterial ) - rendered with gl.LINE - - geo = new THREE.WireframeGeometry(geo); - - matLineBasic = new THREE.LineBasicMaterial({ color: 0x4080ff }); - matLineDashed = new THREE.LineDashedMaterial({ scale: 2, dashSize: 1, gapSize: 1 }); - - wireframe1 = new THREE.LineSegments(geo, matLineBasic); - wireframe1.computeLineDistances(); - wireframe1.visible = false; - scene.add(wireframe1); - - // - - window.addEventListener('resize', onWindowResize); - onWindowResize(); - - stats = new Stats(); - document.body.appendChild(stats.dom); - - initGui(); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - insetWidth = window.innerHeight / 4; // square - insetHeight = window.innerHeight / 4; - - camera2.aspect = insetWidth / insetHeight; - camera2.updateProjectionMatrix(); -} - -function animate() { - // main scene - - renderer.setClearColor(0x000000, 0); - - renderer.setViewport(0, 0, window.innerWidth, window.innerHeight); - - renderer.render(scene, camera); - - // inset scene - - renderer.setClearColor(0x222222, 1); - - renderer.clearDepth(); // important! - - renderer.setScissorTest(true); - - renderer.setScissor(20, 20, insetWidth, insetHeight); - - renderer.setViewport(20, 20, insetWidth, insetHeight); - - camera2.position.copy(camera.position); - camera2.quaternion.copy(camera.quaternion); - - renderer.render(scene, camera2); - - renderer.setScissorTest(false); - - stats.update(); -} - -// - -function initGui() { - gui = new GUI(); - - const param = { - 'line type': 0, - 'width (px)': 5, - dashed: false, - 'dash scale': 1, - 'dash / gap': 1, - }; - - gui.add(param, 'line type', { LineGeometry: 0, 'gl.LINE': 1 }).onChange(function (val) { - switch (val) { - case 0: - wireframe.visible = true; - - wireframe1.visible = false; - - break; - - case 1: - wireframe.visible = false; - - wireframe1.visible = true; - - break; - } - }); - - gui.add(param, 'width (px)', 1, 10).onChange(function (val) { - matLine.linewidth = val; - }); - - gui.add(param, 'dashed').onChange(function (val) { - matLine.dashed = val; - - // dashed is implemented as a defines -- not as a uniform. this could be changed. - // ... or THREE.LineDashedMaterial could be implemented as a separate material - // temporary hack - renderer should do this eventually - if (val) matLine.defines.USE_DASH = ''; - else delete matLine.defines.USE_DASH; - matLine.needsUpdate = true; - - wireframe1.material = val ? matLineDashed : matLineBasic; - }); - - gui.add(param, 'dash scale', 0.5, 1, 0.1).onChange(function (val) { - matLine.dashScale = val; - matLineDashed.scale = val; - }); - - gui.add(param, 'dash / gap', { '2 : 1': 0, '1 : 1': 1, '1 : 2': 2 }).onChange(function (val) { - switch (val) { - case 0: - matLine.dashSize = 2; - matLine.gapSize = 1; - - matLineDashed.dashSize = 2; - matLineDashed.gapSize = 1; - - break; - - case 1: - matLine.dashSize = 1; - matLine.gapSize = 1; - - matLineDashed.dashSize = 1; - matLineDashed.gapSize = 1; - - break; - - case 2: - matLine.dashSize = 1; - matLine.gapSize = 2; - - matLineDashed.dashSize = 1; - matLineDashed.gapSize = 2; - - break; - } - }); -} diff --git a/examples-testing/examples/webgl_loader_3dm.ts b/examples-testing/examples/webgl_loader_3dm.ts deleted file mode 100644 index 7570306fd..000000000 --- a/examples-testing/examples/webgl_loader_3dm.ts +++ /dev/null @@ -1,95 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { Rhino3dmLoader } from 'three/addons/loaders/3DMLoader.js'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let camera, scene, renderer; -let controls, gui; - -init(); - -function init() { - THREE.Object3D.DEFAULT_UP.set(0, 0, 1); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(26, -40, 5); - - scene = new THREE.Scene(); - - const directionalLight = new THREE.DirectionalLight(0xffffff, 6); - directionalLight.position.set(0, 0, 2); - scene.add(directionalLight); - - const loader = new Rhino3dmLoader(); - //generally, use this for the Library Path: https://cdn.jsdelivr.net/npm/rhino3dm@8.0.1 - loader.setLibraryPath('jsm/libs/rhino3dm/'); - loader.load( - 'models/3dm/Rhino_Logo.3dm', - function (object) { - scene.add(object); - initGUI(object.userData.layers); - - // hide spinner - document.getElementById('loader').style.display = 'none'; - }, - function (progress) { - console.log((progress.loaded / progress.total) * 100 + '%'); - }, - function (error) { - console.log(error); - }, - ); - - controls = new OrbitControls(camera, renderer.domElement); - - window.addEventListener('resize', resize); -} - -function resize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); -} - -function animate() { - controls.update(); - renderer.render(scene, camera); -} - -function initGUI(layers) { - gui = new GUI({ title: 'layers' }); - - for (let i = 0; i < layers.length; i++) { - const layer = layers[i]; - gui.add(layer, 'visible') - .name(layer.name) - .onChange(function (val) { - const name = this.object.name; - - scene.traverse(function (child) { - if (child.userData.hasOwnProperty('attributes')) { - if ('layerIndex' in child.userData.attributes) { - const layerName = layers[child.userData.attributes.layerIndex].name; - - if (layerName === name) { - child.visible = val; - layer.visible = val; - } - } - } - }); - }); - } -} diff --git a/examples-testing/examples/webgl_loader_3ds.ts b/examples-testing/examples/webgl_loader_3ds.ts deleted file mode 100644 index 10ce34076..000000000 --- a/examples-testing/examples/webgl_loader_3ds.ts +++ /dev/null @@ -1,62 +0,0 @@ -import * as THREE from 'three'; - -import { TrackballControls } from 'three/addons/controls/TrackballControls.js'; -import { TDSLoader } from 'three/addons/loaders/TDSLoader.js'; - -let container, controls; -let camera, scene, renderer; - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 10); - camera.position.z = 2; - - scene = new THREE.Scene(); - scene.add(new THREE.AmbientLight(0xffffff, 3)); - - const directionalLight = new THREE.DirectionalLight(0xffeedd, 3); - directionalLight.position.set(0, 0, 2); - scene.add(directionalLight); - - //3ds files dont store normal maps - const normal = new THREE.TextureLoader().load('models/3ds/portalgun/textures/normal.jpg'); - - const loader = new TDSLoader(); - loader.setResourcePath('models/3ds/portalgun/textures/'); - loader.load('models/3ds/portalgun/portalgun.3ds', function (object) { - object.traverse(function (child) { - if (child.isMesh) { - child.material.specular.setScalar(0.1); - child.material.normalMap = normal; - } - }); - - scene.add(object); - }); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - controls = new TrackballControls(camera, renderer.domElement); - - window.addEventListener('resize', resize); -} - -function resize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - controls.update(); - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_loader_3dtiles.ts b/examples-testing/examples/webgl_loader_3dtiles.ts deleted file mode 100644 index bae36bc84..000000000 --- a/examples-testing/examples/webgl_loader_3dtiles.ts +++ /dev/null @@ -1,78 +0,0 @@ -import * as THREE from 'three'; -import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js'; -import { TilesRenderer, GlobeControls } from '3d-tiles-renderer'; -import { - CesiumIonAuthPlugin, - GLTFExtensionsPlugin, - TilesFadePlugin, - UpdateOnChangePlugin, -} from '3d-tiles-renderer/plugins'; - -// Ion key provided by Cesium for use on threejs.org -// A personal Cesium Ion key can be used for development. -const ION_KEY = - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJiMTFiZTRmZS1mMWIxLTQ5YzYtYjA4Zi0xYTE0MjFmYzQ5OGYiLCJpZCI6MjY3NzgzLCJpYXQiOjE3MzY0NzQxMDh9.ppGPgpse1lq7QeNyljX7THUyK5w1x_4HksSHSlhe5sY'; - -let camera, scene, renderer; -let tiles, controls; - -init(); - -function init() { - // camera - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 100); - camera.position.set(-1, 1, 1).normalize().multiplyScalar(10); - camera.position.set(-8000000, 10000000, -14720000); - camera.lookAt(0, 0, 0); - - // scene - scene = new THREE.Scene(); - - // renderer - renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // loader - const dracoLoader = new DRACOLoader(); - dracoLoader.setDecoderPath('jsm/libs/draco/'); - dracoLoader.setDecoderConfig({ type: 'js' }); - - // tiles - tiles = new TilesRenderer(); - tiles.registerPlugin(new CesiumIonAuthPlugin({ apiToken: ION_KEY, assetId: '2275207', autoRefreshToken: true })); - tiles.registerPlugin(new GLTFExtensionsPlugin({ dracoLoader })); - tiles.registerPlugin(new TilesFadePlugin()); - tiles.registerPlugin(new UpdateOnChangePlugin()); - tiles.setCamera(camera); - tiles.setResolutionFromRenderer(camera, renderer); - scene.add(tiles.group); - - // rotate the globe so the north pole is up - tiles.group.rotation.x = -Math.PI / 2; - - // controls - controls = new GlobeControls(scene, camera, renderer.domElement, tiles); - controls.enableDamping = true; - - window.addEventListener('resize', onWindowResize); - onWindowResize(); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - tiles.setResolutionFromRenderer(camera, renderer); -} - -function animate() { - controls.update(); - tiles.update(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_loader_3mf.ts b/examples-testing/examples/webgl_loader_3mf.ts deleted file mode 100644 index c31e32196..000000000 --- a/examples-testing/examples/webgl_loader_3mf.ts +++ /dev/null @@ -1,105 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { ThreeMFLoader } from 'three/addons/loaders/3MFLoader.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let camera, scene, renderer, object, loader, controls; - -const params = { - asset: 'cube_gears', -}; - -const assets = ['cube_gears', 'facecolors', 'multipletextures', 'vertexcolors']; - -init(); - -function init() { - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - document.body.appendChild(renderer.domElement); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x333333); - - camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 500); - - // Z is up for objects intended to be 3D printed. - - camera.up.set(0, 0, 1); - camera.position.set(-100, -250, 100); - scene.add(camera); - - controls = new OrbitControls(camera, renderer.domElement); - controls.addEventListener('change', render); - controls.minDistance = 50; - controls.maxDistance = 400; - controls.enablePan = false; - controls.update(); - - scene.add(new THREE.AmbientLight(0xffffff, 0.6)); - - const light = new THREE.DirectionalLight(0xffffff, 2); - light.position.set(-1, -2.5, 1); - scene.add(light); - - const manager = new THREE.LoadingManager(); - - manager.onLoad = function () { - const aabb = new THREE.Box3().setFromObject(object); - const center = aabb.getCenter(new THREE.Vector3()); - - object.position.x += object.position.x - center.x; - object.position.y += object.position.y - center.y; - object.position.z += object.position.z - center.z; - - controls.reset(); - - scene.add(object); - render(); - }; - - loader = new ThreeMFLoader(manager); - loadAsset(params.asset); - - window.addEventListener('resize', onWindowResize); - - // - - const gui = new GUI(); - gui.add(params, 'asset', assets).onChange(function (value) { - loadAsset(value); - }); -} - -function loadAsset(asset) { - loader.load('models/3mf/' + asset + '.3mf', function (group) { - if (object) { - object.traverse(function (child) { - if (child.material) child.material.dispose(); - if (child.material && child.material.map) child.material.map.dispose(); - if (child.geometry) child.geometry.dispose(); - }); - - scene.remove(object); - } - - // - - object = group; - }); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - render(); -} - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_loader_3mf_materials.ts b/examples-testing/examples/webgl_loader_3mf_materials.ts deleted file mode 100644 index 0dea01391..000000000 --- a/examples-testing/examples/webgl_loader_3mf_materials.ts +++ /dev/null @@ -1,106 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { ThreeMFLoader } from 'three/addons/loaders/3MFLoader.js'; - -let camera, scene, renderer; - -init(); - -function init() { - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xa0a0a0); - scene.fog = new THREE.Fog(0xa0a0a0, 10, 500); - - camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 500); - camera.position.set(-50, 40, 50); - scene.add(camera); - - // - - const hemiLight = new THREE.HemisphereLight(0xffffff, 0x8d8d8d, 3); - hemiLight.position.set(0, 100, 0); - scene.add(hemiLight); - - const dirLight = new THREE.DirectionalLight(0xffffff, 3); - dirLight.position.set(-0, 40, 50); - dirLight.castShadow = true; - dirLight.shadow.camera.top = 50; - dirLight.shadow.camera.bottom = -25; - dirLight.shadow.camera.left = -25; - dirLight.shadow.camera.right = 25; - dirLight.shadow.camera.near = 0.1; - dirLight.shadow.camera.far = 200; - dirLight.shadow.mapSize.set(1024, 1024); - scene.add(dirLight); - - // scene.add( new THREE.CameraHelper( dirLight.shadow.camera ) ); - - // - - const manager = new THREE.LoadingManager(); - - const loader = new ThreeMFLoader(manager); - loader.load('./models/3mf/truck.3mf', function (object) { - object.rotation.set(-Math.PI / 2, 0, 0); // z-up conversion - - object.traverse(function (child) { - child.castShadow = true; - }); - - scene.add(object); - }); - - // - - manager.onLoad = function () { - render(); - }; - - // - - const ground = new THREE.Mesh( - new THREE.PlaneGeometry(1000, 1000), - new THREE.MeshPhongMaterial({ color: 0xcbcbcb, depthWrite: false }), - ); - ground.rotation.x = -Math.PI / 2; - ground.position.y = 11; - ground.receiveShadow = true; - scene.add(ground); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.shadowMap.enabled = true; - renderer.shadowMap.type = THREE.PCFShadowMap; - document.body.appendChild(renderer.domElement); - - // - - const controls = new OrbitControls(camera, renderer.domElement); - controls.addEventListener('change', render); - controls.minDistance = 50; - controls.maxDistance = 200; - controls.enablePan = false; - controls.target.set(0, 20, 0); - controls.update(); - - window.addEventListener('resize', onWindowResize); - - render(); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - render(); -} - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_loader_amf.ts b/examples-testing/examples/webgl_loader_amf.ts deleted file mode 100644 index ee576e04f..000000000 --- a/examples-testing/examples/webgl_loader_amf.ts +++ /dev/null @@ -1,62 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { AMFLoader } from 'three/addons/loaders/AMFLoader.js'; - -let camera, scene, renderer; - -init(); - -function init() { - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x999999); - - scene.add(new THREE.AmbientLight(0x999999)); - - camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 500); - - // Z is up for objects intended to be 3D printed. - - camera.up.set(0, 0, 1); - camera.position.set(0, -9, 6); - - camera.add(new THREE.PointLight(0xffffff, 250)); - - scene.add(camera); - - const grid = new THREE.GridHelper(50, 50, 0xffffff, 0x555555); - grid.rotateOnAxis(new THREE.Vector3(1, 0, 0), 90 * (Math.PI / 180)); - scene.add(grid); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - document.body.appendChild(renderer.domElement); - - const loader = new AMFLoader(); - loader.load('./models/amf/rook.amf', function (amfobject) { - scene.add(amfobject); - render(); - }); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.addEventListener('change', render); - controls.target.set(0, 0, 2); - controls.enableZoom = false; - controls.update(); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - render(); -} - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_loader_bvh.ts b/examples-testing/examples/webgl_loader_bvh.ts deleted file mode 100644 index 70cec7e6a..000000000 --- a/examples-testing/examples/webgl_loader_bvh.ts +++ /dev/null @@ -1,64 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { BVHLoader } from 'three/addons/loaders/BVHLoader.js'; - -const timer = new THREE.Timer(); -timer.connect(document); - -let camera, controls, scene, renderer; -let mixer; - -init(); - -const loader = new BVHLoader(); -loader.load('models/bvh/pirouette.bvh', function (result) { - const skeletonHelper = new THREE.SkeletonHelper(result.skeleton.bones[0]); - - scene.add(result.skeleton.bones[0]); - scene.add(skeletonHelper); - - // play animation - mixer = new THREE.AnimationMixer(result.skeleton.bones[0]); - mixer.clipAction(result.clip).play(); -}); - -function init() { - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(0, 200, 300); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xeeeeee); - - scene.add(new THREE.GridHelper(400, 10)); - - // renderer - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 300; - controls.maxDistance = 700; - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - const delta = timer.getDelta(); - - if (mixer) mixer.update(delta); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_loader_collada.ts b/examples-testing/examples/webgl_loader_collada.ts deleted file mode 100644 index 3106369c8..000000000 --- a/examples-testing/examples/webgl_loader_collada.ts +++ /dev/null @@ -1,86 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { ColladaLoader } from 'three/addons/loaders/ColladaLoader.js'; - -let container, stats, timer; -let camera, scene, renderer, elf; - -init(); - -function init() { - container = document.getElementById('container'); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 2000); - camera.position.set(8, 10, 8); - camera.lookAt(0, 3, 0); - - scene = new THREE.Scene(); - - timer = new THREE.Timer(); - timer.connect(document); - - // loading manager - - const loadingManager = new THREE.LoadingManager(function () { - scene.add(elf); - }); - - // collada - - const loader = new ColladaLoader(loadingManager); - loader.load('./models/collada/elf/elf.dae', function (collada) { - elf = collada.scene; - }); - - // - - const ambientLight = new THREE.AmbientLight(0xffffff); - scene.add(ambientLight); - - const directionalLight = new THREE.DirectionalLight(0xffffff, 2.5); - directionalLight.position.set(1, 1, 0).normalize(); - scene.add(directionalLight); - - // - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - // - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - render(); - stats.update(); -} - -function render() { - const delta = timer.getDelta(); - - if (elf !== undefined) { - elf.rotation.z += delta * 0.5; - } - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_loader_collada_skinning.ts b/examples-testing/examples/webgl_loader_collada_skinning.ts deleted file mode 100644 index bbbd0ac3e..000000000 --- a/examples-testing/examples/webgl_loader_collada_skinning.ts +++ /dev/null @@ -1,100 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { ColladaLoader } from 'three/addons/loaders/ColladaLoader.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let container, stats, timer, controls; -let camera, scene, renderer, mixer; - -init(); - -function init() { - container = document.getElementById('container'); - - camera = new THREE.PerspectiveCamera(25, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(15, 10, -15); - - scene = new THREE.Scene(); - - timer = new THREE.Timer(); - timer.connect(document); - - // collada - - const loader = new ColladaLoader(); - loader.load('./models/collada/stormtrooper/stormtrooper.dae', function (collada) { - const avatar = collada.scene; - const animations = avatar.animations; - - mixer = new THREE.AnimationMixer(avatar); - mixer.clipAction(animations[0]).play(); - - scene.add(avatar); - }); - - // - - const gridHelper = new THREE.GridHelper(10, 20, 0xc1c1c1, 0x8d8d8d); - scene.add(gridHelper); - - // - - const ambientLight = new THREE.AmbientLight(0xffffff, 0.6); - scene.add(ambientLight); - - const directionalLight = new THREE.DirectionalLight(0xffffff, 3); - directionalLight.position.set(1.5, 1, -1.5); - scene.add(directionalLight); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - // - - controls = new OrbitControls(camera, renderer.domElement); - controls.screenSpacePanning = true; - controls.minDistance = 5; - controls.maxDistance = 40; - controls.target.set(0, 2, 0); - controls.update(); - - // - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - render(); - stats.update(); -} - -function render() { - const delta = timer.getDelta(); - - if (mixer !== undefined) { - mixer.update(delta); - } - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_loader_draco.ts b/examples-testing/examples/webgl_loader_draco.ts deleted file mode 100644 index c1df7100d..000000000 --- a/examples-testing/examples/webgl_loader_draco.ts +++ /dev/null @@ -1,83 +0,0 @@ -import * as THREE from 'three'; - -import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; - -let camera, scene, renderer; - -const container = document.querySelector('#container'); - -// Configure and create Draco decoder. -const dracoLoader = new DRACOLoader(); -dracoLoader.setDecoderPath('jsm/libs/draco/'); -dracoLoader.setDecoderConfig({ type: 'js' }); - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 0.1, 15); - camera.position.set(3, 0.25, 3); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x443333); - scene.fog = new THREE.Fog(0x443333, 1, 4); - - // Ground - const plane = new THREE.Mesh(new THREE.PlaneGeometry(8, 8), new THREE.MeshLambertMaterial({ color: 0xcbcbcb })); - plane.rotation.x = -Math.PI / 2; - plane.position.y = 0.03; - plane.receiveShadow = true; - scene.add(plane); - - // Lights - const hemiLight = new THREE.HemisphereLight(0x8d7c7c, 0x494966, 3); - scene.add(hemiLight); - - const spotLight = new THREE.SpotLight(); - spotLight.intensity = 7; - spotLight.angle = Math.PI / 16; - spotLight.penumbra = 0.5; - spotLight.castShadow = true; - spotLight.shadow.radius = 8; - spotLight.position.set(-1, 1, 1); - scene.add(spotLight); - - dracoLoader.load('models/draco/bunny.drc', function (geometry) { - geometry.computeVertexNormals(); - - const material = new THREE.MeshStandardMaterial({ color: 0xa5a5a5 }); - const mesh = new THREE.Mesh(geometry, material); - mesh.castShadow = true; - mesh.receiveShadow = true; - scene.add(mesh); - - // Release decoder resources. - dracoLoader.dispose(); - }); - - // renderer - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.shadowMap.enabled = true; - container.appendChild(renderer.domElement); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - const timer = Date.now() * 0.0003; - - camera.position.x = Math.sin(timer) * 0.5; - camera.position.z = Math.cos(timer) * 0.5; - camera.lookAt(0, 0.1, 0); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_loader_fbx.ts b/examples-testing/examples/webgl_loader_fbx.ts deleted file mode 100644 index bcf26ae95..000000000 --- a/examples-testing/examples/webgl_loader_fbx.ts +++ /dev/null @@ -1,169 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { FBXLoader } from 'three/addons/loaders/FBXLoader.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -const manager = new THREE.LoadingManager(); - -let camera, scene, renderer, stats, object, loader, guiMorphsFolder; -let mixer; - -const timer = new THREE.Timer(); -timer.connect(document); - -const params = { - asset: 'Samba Dancing', -}; - -const assets = ['Samba Dancing', 'morph_test', 'monkey', 'monkey_embedded_texture', 'vCube']; - -init(); - -function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000); - camera.position.set(100, 200, 300); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xa0a0a0); - scene.fog = new THREE.Fog(0xa0a0a0, 200, 1000); - - const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444, 5); - hemiLight.position.set(0, 200, 0); - scene.add(hemiLight); - - const dirLight = new THREE.DirectionalLight(0xffffff, 5); - dirLight.position.set(0, 200, 100); - dirLight.castShadow = true; - dirLight.shadow.camera.top = 180; - dirLight.shadow.camera.bottom = -100; - dirLight.shadow.camera.left = -120; - dirLight.shadow.camera.right = 120; - scene.add(dirLight); - - // scene.add( new THREE.CameraHelper( dirLight.shadow.camera ) ); - - // ground - const mesh = new THREE.Mesh( - new THREE.PlaneGeometry(2000, 2000), - new THREE.MeshPhongMaterial({ color: 0x999999, depthWrite: false }), - ); - mesh.rotation.x = -Math.PI / 2; - mesh.receiveShadow = true; - scene.add(mesh); - - const grid = new THREE.GridHelper(2000, 20, 0x000000, 0x000000); - grid.material.opacity = 0.2; - grid.material.transparent = true; - scene.add(grid); - - loader = new FBXLoader(manager); - loadAsset(params.asset); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.shadowMap.enabled = true; - container.appendChild(renderer.domElement); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, 100, 0); - controls.update(); - - window.addEventListener('resize', onWindowResize); - - // stats - stats = new Stats(); - container.appendChild(stats.dom); - - const gui = new GUI(); - gui.add(params, 'asset', assets).onChange(function (value) { - loadAsset(value); - }); - - guiMorphsFolder = gui.addFolder('Morphs').hide(); -} - -function loadAsset(asset) { - loader.load('models/fbx/' + asset + '.fbx', function (group) { - if (object) { - object.traverse(function (child) { - if (child.isSkinnedMesh) { - child.skeleton.dispose(); - } - - if (child.material) { - const materials = Array.isArray(child.material) ? child.material : [child.material]; - materials.forEach(material => { - if (material.map) material.map.dispose(); - material.dispose(); - }); - } - - if (child.geometry) child.geometry.dispose(); - }); - - scene.remove(object); - } - - // - - object = group; - - if (object.animations && object.animations.length) { - mixer = new THREE.AnimationMixer(object); - - const action = mixer.clipAction(object.animations[0]); - action.play(); - } else { - mixer = null; - } - - guiMorphsFolder.children.forEach(child => child.destroy()); - guiMorphsFolder.hide(); - - object.traverse(function (child) { - if (child.isMesh) { - child.castShadow = true; - child.receiveShadow = true; - - if (child.morphTargetDictionary) { - guiMorphsFolder.show(); - const meshFolder = guiMorphsFolder.addFolder(child.name || child.uuid); - Object.keys(child.morphTargetDictionary).forEach(key => { - meshFolder.add(child.morphTargetInfluences, child.morphTargetDictionary[key], 0, 1, 0.01); - }); - } - } - }); - - scene.add(object); - }); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - timer.update(); - - const delta = timer.getDelta(); - - if (mixer) mixer.update(delta); - - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/webgl_loader_fbx_nurbs.ts b/examples-testing/examples/webgl_loader_fbx_nurbs.ts deleted file mode 100644 index f2e45bcb5..000000000 --- a/examples-testing/examples/webgl_loader_fbx_nurbs.ts +++ /dev/null @@ -1,61 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { FBXLoader } from 'three/addons/loaders/FBXLoader.js'; - -let camera, scene, renderer, stats; - -init(); - -function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000); - camera.position.set(2, 18, 28); - - scene = new THREE.Scene(); - - // grid - const gridHelper = new THREE.GridHelper(28, 28, 0x303030, 0x303030); - scene.add(gridHelper); - - // stats - stats = new Stats(); - container.appendChild(stats.dom); - - // model - const loader = new FBXLoader(); - loader.load('models/fbx/nurbs.fbx', function (object) { - scene.add(object); - }); - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, 12, 0); - controls.update(); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/webgl_loader_gcode.ts b/examples-testing/examples/webgl_loader_gcode.ts deleted file mode 100644 index 16dfb92e2..000000000 --- a/examples-testing/examples/webgl_loader_gcode.ts +++ /dev/null @@ -1,81 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GCodeLoader } from 'three/addons/loaders/GCodeLoader.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let camera, scene, renderer, controls, loader; - -let model; - -const params = { - asset: 'benchy', -}; - -const assets = ['benchy', 'test_m82', 'test_m83']; - -const positions = [new THREE.Vector3(-100, -20, 100), new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 0, 0)]; - -init(); -render(); - -function init() { - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(0, 0, 70); - - scene = new THREE.Scene(); - - loader = new GCodeLoader(); - loadAsset(params.asset); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - document.body.appendChild(renderer.domElement); - - controls = new OrbitControls(camera, renderer.domElement); - controls.addEventListener('change', render); // use if there is no animation loop - controls.minDistance = 10; - controls.maxDistance = 100; - - window.addEventListener('resize', resize); - - const gui = new GUI(); - - gui.add(params, 'asset', assets).onChange(function (value) { - if (model) { - model.traverse(function (object) { - if (object.material) object.material.dispose(); - if (object.geometry) object.geometry.dispose(); - }); - - scene.remove(model); - } - - loadAsset(value); - }); - - gui.open(); -} - -function loadAsset(asset) { - loader.load('models/gcode/' + asset + '.gcode', function (object) { - model = object; - model.position.copy(positions[assets.indexOf(asset)]); - scene.add(model); - controls.reset(); - }); -} - -function resize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - render(); -} - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_loader_gltf.ts b/examples-testing/examples/webgl_loader_gltf.ts deleted file mode 100644 index e0cb68546..000000000 --- a/examples-testing/examples/webgl_loader_gltf.ts +++ /dev/null @@ -1,167 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let camera, scene, renderer, controls; -let currentModel, mixer; -let currentLoadId = 0; - -const timer = new THREE.Timer(); - -init(); - -function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); - camera.position.set(-1.8, 0.6, 2.7); - - scene = new THREE.Scene(); - - new UltraHDRLoader().setPath('textures/equirectangular/').load('royal_esplanade_2k.hdr.jpg', function (texture) { - texture.mapping = THREE.EquirectangularReflectionMapping; - - scene.background = texture; - scene.environment = texture; - - render(); - - // model - - fetch('https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Assets/main/Models/model-index.json') - .then(response => response.json()) - .then(models => { - const gui = new GUI(); - const modelNames = models.map(m => m.name); - const params = { model: 'DamagedHelmet' }; - - if (!modelNames.includes(params.model) && modelNames.length > 0) { - params.model = modelNames[0]; - } - - gui.add(params, 'model', modelNames).onChange(name => { - const modelInfo = models.find(m => m.name === name); - loadModel(modelInfo); - }); - - gui.add(scene, 'backgroundBlurriness', 0, 1); - - const initialModel = models.find(m => m.name === params.model); - if (initialModel) loadModel(initialModel); - }); - }); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(render); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - container.appendChild(renderer.domElement); - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - controls.minDistance = 2; - controls.maxDistance = 10; - controls.target.set(0, 0, -0.2); - controls.update(); - - window.addEventListener('resize', onWindowResize); -} - -function loadModel(modelInfo) { - const variants = modelInfo.variants; - const variant = variants['glTF-Binary'] || variants['glTF']; - const url = `https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Assets/main/Models/${modelInfo.name}/${variant.endsWith('.glb') ? 'glTF-Binary' : 'glTF'}/${variant}`; - - if (currentModel) { - scene.remove(currentModel); - currentModel = null; - } - - if (mixer) { - mixer.stopAllAction(); - mixer = null; - } - - const loadId = ++currentLoadId; - - const loader = new GLTFLoader(); - loader.load(url, async function (gltf) { - if (loadId !== currentLoadId) return; - - currentModel = gltf.scene; - - // wait until the model can be added to the scene without blocking due to shader compilation - - await renderer.compileAsync(currentModel, camera, scene); - - if (loadId !== currentLoadId) return; - - scene.add(currentModel); - - fitCameraToSelection(camera, controls, currentModel); - - // animations - - if (gltf.animations.length > 0) { - mixer = new THREE.AnimationMixer(currentModel); - - for (const animation of gltf.animations) { - mixer.clipAction(animation).play(); - } - } - }); -} - -function fitCameraToSelection(camera, controls, selection, fitOffset = 1.3) { - const box = new THREE.Box3(); - box.setFromObject(selection); - - const size = box.getSize(new THREE.Vector3()); - const center = box.getCenter(new THREE.Vector3()); - - const maxSize = Math.max(size.x, size.y, size.z); - const fitHeightDistance = maxSize / (2 * Math.atan((Math.PI * camera.fov) / 360)); - // const fitWidthDistance = fitHeightDistance / camera.aspect; - // const distance = fitOffset * Math.max( fitHeightDistance, fitWidthDistance ); - const distance = fitOffset * fitHeightDistance; - - const direction = controls.target.clone().sub(camera.position).normalize().multiplyScalar(distance); - - controls.maxDistance = distance * 10; - controls.minDistance = distance / 10; - controls.target.copy(center); - - camera.near = distance / 100; - camera.far = distance * 100; - camera.updateProjectionMatrix(); - - camera.position.copy(controls.target).sub(direction); - - controls.update(); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - render(); -} - -// - -function render() { - timer.update(); - - controls.update(); - - if (mixer) mixer.update(timer.getDelta()); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_loader_gltf_animation_pointer.ts b/examples-testing/examples/webgl_loader_gltf_animation_pointer.ts deleted file mode 100644 index 443df047b..000000000 --- a/examples-testing/examples/webgl_loader_gltf_animation_pointer.ts +++ /dev/null @@ -1,91 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; - -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; -import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js'; -import { GLTFAnimationPointerExtension } from '@needle-tools/three-animation-pointer'; - -let mixer; - -const timer = new THREE.Timer(); -timer.connect(document); -const container = document.getElementById('container'); - -const stats = new Stats(); -container.appendChild(stats.dom); - -const renderer = new THREE.WebGLRenderer({ antialias: true }); -renderer.setPixelRatio(window.devicePixelRatio); -renderer.setSize(window.innerWidth, window.innerHeight); -container.appendChild(renderer.domElement); - -const pmremGenerator = new THREE.PMREMGenerator(renderer); - -const scene = new THREE.Scene(); -scene.background = new THREE.Color(0xbfe3dd); -scene.environment = pmremGenerator.fromScene(new RoomEnvironment(), 0.04).texture; - -const camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.2, 100); -camera.position.set(-3, 2, 6); - -const controls = new OrbitControls(camera, renderer.domElement); -controls.target.set(0, 0.5, 0); -controls.update(); -controls.enablePan = false; -controls.enableDamping = true; - -const dracoLoader = new DRACOLoader(); -dracoLoader.setDecoderPath('jsm/libs/draco/gltf/'); - -const ktx2Loader = new KTX2Loader().setTranscoderPath('jsm/libs/basis/').detectSupport(renderer); - -const loader = new GLTFLoader(); -loader.setDRACOLoader(dracoLoader); -loader.setKTX2Loader(ktx2Loader); - -loader.register(p => { - return new GLTFAnimationPointerExtension(p); -}); - -loader.load( - 'https://cloud.needle.tools/-/assets/Z23hmXB27L6Db-optimized/file', - function (gltf) { - const model = gltf.scene; - scene.add(model); - - mixer = new THREE.AnimationMixer(model); - mixer.clipAction(gltf.animations[0]).play(); - - renderer.setAnimationLoop(animate); - }, - undefined, - function (e) { - console.error(e); - }, -); - -window.onresize = function () { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -}; - -function animate() { - timer.update(); - - const delta = timer.getDelta(); - - mixer.update(delta); - - controls.update(); - - stats.update(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_loader_gltf_anisotropy.ts b/examples-testing/examples/webgl_loader_gltf_anisotropy.ts deleted file mode 100644 index f0dd05cfe..000000000 --- a/examples-testing/examples/webgl_loader_gltf_anisotropy.ts +++ /dev/null @@ -1,68 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; - -let renderer, scene, camera, controls; - -init(); - -async function init() { - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = 1.35; - document.body.appendChild(renderer.domElement); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.01, 10); - camera.position.set(-0.35, -0.2, 0.35); - - controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, -0.08, 0.11); - controls.minDistance = 0.1; - controls.maxDistance = 2; - controls.addEventListener('change', render); - controls.update(); - - const hdrLoader = new UltraHDRLoader().setPath('textures/equirectangular/'); - const gltfLoader = new GLTFLoader().setPath('models/gltf/'); - - const [texture, gltf] = await Promise.all([ - hdrLoader.loadAsync('royal_esplanade_2k.hdr.jpg'), - gltfLoader.loadAsync('AnisotropyBarnLamp.glb'), - ]); - - // environment - - texture.mapping = THREE.EquirectangularReflectionMapping; - - scene.background = texture; - scene.backgroundBlurriness = 0.5; - scene.environment = texture; - - // model - - scene.add(gltf.scene); - - render(); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - render(); -} - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_loader_gltf_avif.ts b/examples-testing/examples/webgl_loader_gltf_avif.ts deleted file mode 100644 index 37d63859e..000000000 --- a/examples-testing/examples/webgl_loader_gltf_avif.ts +++ /dev/null @@ -1,61 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; - -let camera, scene, renderer; - -init(); -render(); - -function init() { - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(1.5, 4, 9); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xf6eedc); - - // - - const dracoLoader = new DRACOLoader(); - dracoLoader.setDecoderPath('jsm/libs/draco/gltf/'); - - const loader = new GLTFLoader(); - loader.setDRACOLoader(dracoLoader); - loader.setPath('models/gltf/AVIFTest/'); - loader.load('forest_house.glb', function (gltf) { - scene.add(gltf.scene); - - render(); - }); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - document.body.appendChild(renderer.domElement); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.addEventListener('change', render); - controls.target.set(0, 2, 0); - controls.update(); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - render(); -} - -// - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_loader_gltf_compressed.ts b/examples-testing/examples/webgl_loader_gltf_compressed.ts deleted file mode 100644 index 235d9b3d0..000000000 --- a/examples-testing/examples/webgl_loader_gltf_compressed.ts +++ /dev/null @@ -1,83 +0,0 @@ -import * as THREE from 'three'; - -import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js'; -import { MeshoptDecoder } from 'three/addons/libs/meshopt_decoder.module.js'; - -let camera, scene, renderer; - -init(); -render(); - -function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = 1; - container.appendChild(renderer.domElement); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000); - camera.position.set(0, 100, 0); - - const environment = new RoomEnvironment(); - const pmremGenerator = new THREE.PMREMGenerator(renderer); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xbbbbbb); - scene.environment = pmremGenerator.fromScene(environment, 0.04).texture; - environment.dispose(); - - const grid = new THREE.GridHelper(500, 10, 0xffffff, 0xffffff); - grid.material.opacity = 0.5; - grid.material.depthWrite = false; - grid.material.transparent = true; - scene.add(grid); - - const ktx2Loader = new KTX2Loader().setTranscoderPath('jsm/libs/basis/').detectSupport(renderer); - - const loader = new GLTFLoader().setPath('models/gltf/'); - loader.setKTX2Loader(ktx2Loader); - loader.setMeshoptDecoder(MeshoptDecoder); - loader.load('coffeemat.glb', function (gltf) { - // coffeemat.glb was produced from the source scene using gltfpack: - // gltfpack -i coffeemat/scene.gltf -o coffeemat.glb -cc -tc - // The resulting model uses EXT_meshopt_compression (for geometry) and KHR_texture_basisu (for texture compression using ETC1S/BasisLZ) - - gltf.scene.position.y = 8; - - scene.add(gltf.scene); - - render(); - }); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.addEventListener('change', render); // use if there is no animation loop - controls.minDistance = 400; - controls.maxDistance = 1000; - controls.target.set(10, 90, -16); - controls.update(); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - render(); -} - -// - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_loader_gltf_dispersion.ts b/examples-testing/examples/webgl_loader_gltf_dispersion.ts deleted file mode 100644 index 0f1d7e57a..000000000 --- a/examples-testing/examples/webgl_loader_gltf_dispersion.ts +++ /dev/null @@ -1,66 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; - -let camera, scene, renderer; - -init().then(render); - -async function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.01, 5); - camera.position.set(0.1, 0.05, 0.15); - - scene = new THREE.Scene(); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.toneMapping = THREE.NeutralToneMapping; - renderer.toneMappingExposure = 1; - container.appendChild(renderer.domElement); - - const environment = new RoomEnvironment(); - const pmremGenerator = new THREE.PMREMGenerator(renderer); - - scene = new THREE.Scene(); - scene.backgroundBlurriness = 0.5; - - const env = pmremGenerator.fromScene(environment, 0.04).texture; - scene.background = env; - scene.environment = env; - environment.dispose(); - - const loader = new GLTFLoader(); - const gltf = await loader.loadAsync('models/gltf/DispersionTest.glb'); - - scene.add(gltf.scene); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.addEventListener('change', render); // use if there is no animation loop - controls.minDistance = 0.1; - controls.maxDistance = 10; - controls.target.set(0, 0, 0); - controls.update(); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - render(); -} - -// - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_loader_gltf_instancing.ts b/examples-testing/examples/webgl_loader_gltf_instancing.ts deleted file mode 100644 index 6acb140ec..000000000 --- a/examples-testing/examples/webgl_loader_gltf_instancing.ts +++ /dev/null @@ -1,69 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; - -let camera, scene, renderer; - -init(); -render(); - -function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); - camera.position.set(-0.9, 0.41, -0.89); - - scene = new THREE.Scene(); - - new UltraHDRLoader().setPath('textures/equirectangular/').load('royal_esplanade_2k.hdr.jpg', function (texture) { - texture.mapping = THREE.EquirectangularReflectionMapping; - - scene.background = texture; - scene.environment = texture; - - render(); - - // model - - const loader = new GLTFLoader().setPath('models/gltf/DamagedHelmet/glTF-instancing/'); - loader.load('DamagedHelmetGpuInstancing.gltf', function (gltf) { - scene.add(gltf.scene); - - render(); - }); - }); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = 1; - container.appendChild(renderer.domElement); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.addEventListener('change', render); // use if there is no animation loop - controls.minDistance = 0.2; - controls.maxDistance = 10; - controls.target.set(0, 0.25, 0); - controls.update(); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - render(); -} - -// - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_loader_gltf_iridescence.ts b/examples-testing/examples/webgl_loader_gltf_iridescence.ts deleted file mode 100644 index 5519835fe..000000000 --- a/examples-testing/examples/webgl_loader_gltf_iridescence.ts +++ /dev/null @@ -1,66 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; - -let renderer, scene, camera, controls; - -init().catch(function (err) { - console.error(err); -}); - -async function init() { - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setAnimationLoop(animate); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - document.body.appendChild(renderer.domElement); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.05, 20); - camera.position.set(0.35, 0.05, 0.35); - - controls = new OrbitControls(camera, renderer.domElement); - controls.autoRotate = true; - controls.autoRotateSpeed = -0.5; - controls.target.set(0, 0.2, 0); - controls.update(); - - const hdrLoader = new HDRLoader().setPath('textures/equirectangular/'); - - const gltfLoader = new GLTFLoader().setPath('models/gltf/'); - - const [texture, gltf] = await Promise.all([ - hdrLoader.loadAsync('venice_sunset_1k.hdr'), - gltfLoader.loadAsync('IridescenceLamp.glb'), - ]); - - // environment - - texture.mapping = THREE.EquirectangularReflectionMapping; - - scene.background = texture; - scene.environment = texture; - - // model - - scene.add(gltf.scene); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - controls.update(); - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_loader_gltf_progressive_lod.ts b/examples-testing/examples/webgl_loader_gltf_progressive_lod.ts deleted file mode 100644 index 8d1c60480..000000000 --- a/examples-testing/examples/webgl_loader_gltf_progressive_lod.ts +++ /dev/null @@ -1,143 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; -import { useNeedleProgressive } from '@needle-tools/gltf-progressive'; - -let camera, scene, renderer, mixer; -let airshipModel; - -init(); - -function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - scene = new THREE.Scene(); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = 1; - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.1, 40); - camera.position.set(-9, 2, -13); - - const fog = new THREE.Fog('#131055', 15, 50); - scene.fog = fog; - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 0.1; - controls.maxDistance = 20; - controls.target.set(-1, 2.1, 0); - controls.update(); - - new HDRLoader().setPath('textures/equirectangular/').load('quarry_01_1k.hdr', function (texture) { - texture.mapping = THREE.EquirectangularReflectionMapping; - - scene.background = new THREE.Color('#192022'); - scene.backgroundBlurriness = 0.5; - scene.environment = texture; - scene.environmentRotation = new THREE.Euler(0, Math.PI / -2, 0, 'XYZ'); - }); - - mixer = new THREE.AnimationMixer(scene); - - const loader = new GLTFLoader(); - - useNeedleProgressive(loader, renderer); - - loader.load('https://cloud.needle.tools/-/assets/Z23hmXBZ2sPRdk-world/file', function (gltf) { - const model = gltf.scene; - - model.scale.multiplyScalar(0.1); - - scene.add(model); - - const animations = gltf.animations; - if (animations && animations.length) { - for (const animation of animations) { - mixer.clipAction(animation).play(); - } - } - }); - - loader.load('https://cloud.needle.tools/-/assets/Z23hmXBZnlceI-ZnlceI-world/file', function (gltf) { - const model = gltf.scene; - - model.scale.multiplyScalar(0.0005); - - model.position.set(1.6, 6, 7); - - model.rotation.set(0, Math.PI * 1.4, 0); - - scene.add(model); - - airshipModel = model; - - const animations = gltf.animations; - - if (animations && animations.length) { - for (const animation of animations) { - mixer.clipAction(animation).play(); - } - } - }); - - loader.load('https://cloud.needle.tools/-/assets/Z23hmXBZ21QnG-Z21QnG-product/file', function (gltf) { - const model = gltf.scene; - - model.scale.multiplyScalar(0.5); - - model.position.set(2, 5.15, 2.3); - - model.rotation.set(0, Math.PI * 1, 0); - - scene.add(model); - - const animations = gltf.animations; - if (animations && animations.length) { - for (const animation of animations) { - mixer.clipAction(animation).play(); - } - } - }); - - container.appendChild(renderer.domElement); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -const timer = new THREE.Timer(); -timer.connect(document); -let time = 0; - -function animate() { - timer.update(); - - const dt = timer.getDelta(); - time += dt; - - mixer.update(dt); - - if (airshipModel) { - airshipModel.position.y += Math.sin(time) * 0.002; - } - - renderer.render(scene, camera); - - window.requestAnimationFrame(animate); -} - -animate(); diff --git a/examples-testing/examples/webgl_loader_gltf_sheen.ts b/examples-testing/examples/webgl_loader_gltf_sheen.ts deleted file mode 100644 index b058f1e27..000000000 --- a/examples-testing/examples/webgl_loader_gltf_sheen.ts +++ /dev/null @@ -1,72 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let camera, scene, renderer, controls; - -init(); - -function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 20); - camera.position.set(-0.75, 0.7, 1.25); - - scene = new THREE.Scene(); - - // model - - new GLTFLoader().setPath('models/gltf/').load('SheenChair.glb', function (gltf) { - scene.add(gltf.scene); - - const object = gltf.scene.getObjectByName('SheenChair_fabric'); - - const gui = new GUI(); - - gui.add(object.material, 'sheen', 0, 1); - gui.open(); - }); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = 1; - container.appendChild(renderer.domElement); - - const environment = new RoomEnvironment(); - const pmremGenerator = new THREE.PMREMGenerator(renderer); - - scene.background = new THREE.Color(0xbbbbbb); - scene.environment = pmremGenerator.fromScene(environment, 0.04).texture; - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - controls.minDistance = 1; - controls.maxDistance = 10; - controls.target.set(0, 0.35, 0); - controls.update(); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - controls.update(); // required if damping enabled - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_loader_gltf_transmission.ts b/examples-testing/examples/webgl_loader_gltf_transmission.ts deleted file mode 100644 index e8ea0a927..000000000 --- a/examples-testing/examples/webgl_loader_gltf_transmission.ts +++ /dev/null @@ -1,83 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; - -import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; - -let camera, scene, renderer, controls, timer, mixer; - -init(); - -function init() { - timer = new THREE.Timer(); - timer.connect(document); - - const container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); - camera.position.set(0, 0.4, 0.7); - - scene = new THREE.Scene(); - - new UltraHDRLoader().setPath('textures/equirectangular/').load('royal_esplanade_2k.hdr.jpg', function (texture) { - texture.mapping = THREE.EquirectangularReflectionMapping; - - scene.background = texture; - scene.backgroundBlurriness = 0.35; - - scene.environment = texture; - - // model - - new GLTFLoader() - .setPath('models/gltf/') - .setDRACOLoader(new DRACOLoader().setDecoderPath('jsm/libs/draco/gltf/')) - .load('IridescentDishWithOlives.glb', function (gltf) { - mixer = new THREE.AnimationMixer(gltf.scene); - mixer.clipAction(gltf.animations[0]).play(); - - scene.add(gltf.scene); - }); - }); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = 1; - container.appendChild(renderer.domElement); - - controls = new OrbitControls(camera, renderer.domElement); - controls.autoRotate = true; - controls.autoRotateSpeed = -0.75; - controls.enableDamping = true; - controls.minDistance = 0.5; - controls.maxDistance = 1; - controls.target.set(0, 0.1, 0); - controls.update(); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - timer.update(); - - if (mixer) mixer.update(timer.getDelta()); - - controls.update(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_loader_imagebitmap.ts b/examples-testing/examples/webgl_loader_imagebitmap.ts deleted file mode 100644 index c02a03e17..000000000 --- a/examples-testing/examples/webgl_loader_imagebitmap.ts +++ /dev/null @@ -1,113 +0,0 @@ -import * as THREE from 'three'; - -let camera, scene, renderer; -let group, cubes; - -init(); - -function addImageBitmap() { - new THREE.ImageBitmapLoader().setOptions({ imageOrientation: 'flipY' }).load( - 'textures/planets/earth_atmos_2048.jpg?' + performance.now(), - function (imageBitmap) { - const texture = new THREE.CanvasTexture(imageBitmap); - texture.colorSpace = THREE.SRGBColorSpace; - const material = new THREE.MeshBasicMaterial({ map: texture }); - - // ImageBitmap should be disposed when done with it. - - texture.onUpdate = disposeImageBitmap; - - addCube(material); - }, - function (p) { - console.log(p); - }, - function (e) { - console.log(e); - }, - ); -} - -function addImage() { - new THREE.ImageLoader() - .setCrossOrigin('*') - .load('textures/planets/earth_atmos_2048.jpg?' + performance.now(), function (image) { - const texture = new THREE.CanvasTexture(image); - texture.colorSpace = THREE.SRGBColorSpace; - const material = new THREE.MeshBasicMaterial({ color: 0xff8888, map: texture }); - addCube(material); - }); -} - -const geometry = new THREE.BoxGeometry(); - -function addCube(material) { - const cube = new THREE.Mesh(geometry, material); - cube.position.set(Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1); - cube.rotation.set(Math.random() * 2 * Math.PI, Math.random() * 2 * Math.PI, Math.random() * 2 * Math.PI); - cubes.add(cube); -} - -function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - // CAMERA - - camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 1500); - camera.position.set(0, 4, 7); - camera.lookAt(0, 0, 0); - - // SCENE - - scene = new THREE.Scene(); - - // - - group = new THREE.Group(); - scene.add(group); - - group.add(new THREE.GridHelper(4, 12, 0x888888, 0x444444)); - - cubes = new THREE.Group(); - group.add(cubes); - - // RENDERER - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - // TESTS - - setTimeout(addImage, 300); - setTimeout(addImage, 600); - setTimeout(addImage, 900); - setTimeout(addImageBitmap, 1300); - setTimeout(addImageBitmap, 1600); - setTimeout(addImageBitmap, 1900); - - // EVENTS - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - group.rotation.y = performance.now() / 3000; - - renderer.render(scene, camera); -} - -function disposeImageBitmap(texture) { - texture.source.data.close(); - texture.onUpdate = null; // make sure this callback is executed only once per texture -} diff --git a/examples-testing/examples/webgl_loader_kmz.ts b/examples-testing/examples/webgl_loader_kmz.ts deleted file mode 100644 index f93555e41..000000000 --- a/examples-testing/examples/webgl_loader_kmz.ts +++ /dev/null @@ -1,59 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { KMZLoader } from 'three/addons/loaders/KMZLoader.js'; - -let camera, scene, renderer; - -init(); - -function init() { - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x999999); - - const light = new THREE.DirectionalLight(0xffffff, 3); - light.position.set(0.5, 1.0, 0.5).normalize(); - - scene.add(light); - - camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 500); - - camera.position.y = 5; - camera.position.z = 10; - - scene.add(camera); - - const grid = new THREE.GridHelper(50, 50, 0xffffff, 0x7b7b7b); - scene.add(grid); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - document.body.appendChild(renderer.domElement); - - const loader = new KMZLoader(); - loader.load('./models/kmz/Box.kmz', function (kmz) { - kmz.scene.position.y = 0.5; - scene.add(kmz.scene); - render(); - }); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.addEventListener('change', render); - controls.update(); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - render(); -} - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_loader_lwo.ts b/examples-testing/examples/webgl_loader_lwo.ts deleted file mode 100644 index fb10c8340..000000000 --- a/examples-testing/examples/webgl_loader_lwo.ts +++ /dev/null @@ -1,69 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { LWOLoader } from 'three/addons/loaders/LWOLoader.js'; - -let camera, scene, renderer; - -init(); - -function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 200); - camera.position.set(0.7, 14.6, -43.2); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xa0a0a0); - - const ambientLight = new THREE.AmbientLight(0xbbbbbb); - scene.add(ambientLight); - - const light1 = new THREE.DirectionalLight(0xc1c1c1, 3); - light1.position.set(0, 200, -100); - scene.add(light1); - - const grid = new THREE.GridHelper(200, 20, 0x000000, 0x000000); - grid.material.opacity = 0.3; - grid.material.transparent = true; - scene.add(grid); - - const loader = new LWOLoader(); - loader.load('models/lwo/Objects/LWO3/Demo.lwo', function (object) { - const phong = object.meshes[0]; - phong.position.set(2, 12, 0); - - const standard = object.meshes[1]; - standard.position.set(-2, 12, 0); - - const rocket = object.meshes[2]; - rocket.position.set(0, 10.5, 1); - - scene.add(phong, standard, rocket); - }); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - container.appendChild(renderer.domElement); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(-1.33, 10, 6.7); - controls.update(); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_loader_md2_control.ts b/examples-testing/examples/webgl_loader_md2_control.ts deleted file mode 100644 index 56b7dc3c4..000000000 --- a/examples-testing/examples/webgl_loader_md2_control.ts +++ /dev/null @@ -1,292 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { MD2CharacterComplex } from 'three/addons/misc/MD2CharacterComplex.js'; -import { Gyroscope } from 'three/addons/misc/Gyroscope.js'; - -let SCREEN_WIDTH = window.innerWidth; -let SCREEN_HEIGHT = window.innerHeight; - -let container, stats; -let camera, scene, renderer; - -const characters = []; -let nCharacters = 0; - -let cameraControls; - -const controls = { - moveForward: false, - moveBackward: false, - moveLeft: false, - moveRight: false, -}; - -const timer = new THREE.Timer(); -timer.connect(document); - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - // CAMERA - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 4000); - camera.position.set(0, 150, 1300); - - // SCENE - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xffffff); - scene.fog = new THREE.Fog(0xffffff, 1000, 4000); - - scene.add(camera); - - // LIGHTS - - scene.add(new THREE.AmbientLight(0x666666, 3)); - - const light = new THREE.DirectionalLight(0xffffff, 7); - light.position.set(200, 450, 500); - - light.castShadow = true; - - light.shadow.mapSize.width = 1024; - light.shadow.mapSize.height = 512; - - light.shadow.camera.near = 100; - light.shadow.camera.far = 1200; - - light.shadow.camera.left = -1000; - light.shadow.camera.right = 1000; - light.shadow.camera.top = 350; - light.shadow.camera.bottom = -350; - - scene.add(light); - // scene.add( new THREE.CameraHelper( light.shadow.camera ) ); - - // GROUND - - const gt = new THREE.TextureLoader().load('textures/terrain/grasslight-big.jpg'); - const gg = new THREE.PlaneGeometry(16000, 16000); - const gm = new THREE.MeshPhongMaterial({ color: 0xffffff, map: gt }); - - const ground = new THREE.Mesh(gg, gm); - ground.rotation.x = -Math.PI / 2; - ground.material.map.repeat.set(64, 64); - ground.material.map.wrapS = THREE.RepeatWrapping; - ground.material.map.wrapT = THREE.RepeatWrapping; - ground.material.map.colorSpace = THREE.SRGBColorSpace; - // note that because the ground does not cast a shadow, .castShadow is left false - ground.receiveShadow = true; - - scene.add(ground); - - // RENDERER - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - // - - renderer.shadowMap.enabled = true; - renderer.shadowMap.type = THREE.PCFShadowMap; - - // STATS - - stats = new Stats(); - container.appendChild(stats.dom); - - // EVENTS - - window.addEventListener('resize', onWindowResize); - document.addEventListener('keydown', onKeyDown); - document.addEventListener('keyup', onKeyUp); - - // CONTROLS - - cameraControls = new OrbitControls(camera, renderer.domElement); - cameraControls.target.set(0, 50, 0); - cameraControls.update(); - - // CHARACTER - - const configOgro = { - baseUrl: 'models/md2/ogro/', - - body: 'ogro.md2', - skins: [ - 'grok.jpg', - 'ogrobase.png', - 'arboshak.png', - 'ctf_r.png', - 'ctf_b.png', - 'darkam.png', - 'freedom.png', - 'gib.png', - 'gordogh.png', - 'igdosh.png', - 'khorne.png', - 'nabogro.png', - 'sharokh.png', - ], - weapons: [['weapon.md2', 'weapon.jpg']], - animations: { - move: 'run', - idle: 'stand', - jump: 'jump', - attack: 'attack', - crouchMove: 'cwalk', - crouchIdle: 'cstand', - crouchAttach: 'crattack', - }, - - walkSpeed: 350, - crouchSpeed: 175, - }; - - const nRows = 1; - const nSkins = configOgro.skins.length; - - nCharacters = nSkins * nRows; - - for (let i = 0; i < nCharacters; i++) { - const character = new MD2CharacterComplex(); - character.scale = 3; - character.controls = controls; - characters.push(character); - } - - const baseCharacter = new MD2CharacterComplex(); - baseCharacter.scale = 3; - - baseCharacter.onLoadComplete = function () { - let k = 0; - - for (let j = 0; j < nRows; j++) { - for (let i = 0; i < nSkins; i++) { - const cloneCharacter = characters[k]; - - cloneCharacter.shareParts(baseCharacter); - - // cast and receive shadows - cloneCharacter.enableShadows(true); - - cloneCharacter.setWeapon(0); - cloneCharacter.setSkin(i); - - cloneCharacter.root.position.x = (i - nSkins / 2) * 150; - cloneCharacter.root.position.z = j * 250; - - scene.add(cloneCharacter.root); - - k++; - } - } - - const gyro = new Gyroscope(); - gyro.add(camera); - gyro.add(light, light.target); - - characters[Math.floor(nSkins / 2)].root.add(gyro); - }; - - baseCharacter.loadParts(configOgro); -} - -// EVENT HANDLERS - -function onWindowResize() { - SCREEN_WIDTH = window.innerWidth; - SCREEN_HEIGHT = window.innerHeight; - - renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); - - camera.aspect = SCREEN_WIDTH / SCREEN_HEIGHT; - camera.updateProjectionMatrix(); -} - -function onKeyDown(event) { - switch (event.code) { - case 'ArrowUp': - case 'KeyW': - controls.moveForward = true; - break; - - case 'ArrowDown': - case 'KeyS': - controls.moveBackward = true; - break; - - case 'ArrowLeft': - case 'KeyA': - controls.moveLeft = true; - break; - - case 'ArrowRight': - case 'KeyD': - controls.moveRight = true; - break; - - // case 'KeyC': controls.crouch = true; break; - // case 'Space': controls.jump = true; break; - // case 'ControlLeft': - // case 'ControlRight': controls.attack = true; break; - } -} - -function onKeyUp(event) { - switch (event.code) { - case 'ArrowUp': - case 'KeyW': - controls.moveForward = false; - break; - - case 'ArrowDown': - case 'KeyS': - controls.moveBackward = false; - break; - - case 'ArrowLeft': - case 'KeyA': - controls.moveLeft = false; - break; - - case 'ArrowRight': - case 'KeyD': - controls.moveRight = false; - break; - - // case 'KeyC': controls.crouch = false; break; - // case 'Space': controls.jump = false; break; - // case 'ControlLeft': - // case 'ControlRight': controls.attack = false; break; - } -} - -// - -function animate() { - timer.update(); - - render(); - - stats.update(); -} - -function render() { - const delta = timer.getDelta(); - - for (let i = 0; i < nCharacters; i++) { - characters[i].update(delta); - } - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_loader_mdd.ts b/examples-testing/examples/webgl_loader_mdd.ts deleted file mode 100644 index 16e49221c..000000000 --- a/examples-testing/examples/webgl_loader_mdd.ts +++ /dev/null @@ -1,65 +0,0 @@ -import * as THREE from 'three'; - -import { MDDLoader } from 'three/addons/loaders/MDDLoader.js'; - -let camera, scene, renderer, mixer, timer; - -init(); - -function init() { - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(8, 8, 8); - camera.lookAt(scene.position); - - timer = new THREE.Timer(); - timer.connect(document); - - // - - const loader = new MDDLoader(); - loader.load('models/mdd/cube.mdd', function (result) { - const morphTargets = result.morphTargets; - const clip = result.clip; - // clip.optimize(); // optional - - const geometry = new THREE.BoxGeometry(); - geometry.morphAttributes.position = morphTargets; // apply morph targets - - const material = new THREE.MeshNormalMaterial(); - - const mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - mixer = new THREE.AnimationMixer(mesh); - mixer.clipAction(clip).play(); // use clip - }); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - const delta = timer.getDelta(); - - if (mixer) mixer.update(delta); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_loader_obj.ts b/examples-testing/examples/webgl_loader_obj.ts deleted file mode 100644 index 42f1f3257..000000000 --- a/examples-testing/examples/webgl_loader_obj.ts +++ /dev/null @@ -1,72 +0,0 @@ -import * as THREE from 'three'; - -import { MTLLoader } from 'three/addons/loaders/MTLLoader.js'; -import { OBJLoader } from 'three/addons/loaders/OBJLoader.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let camera, scene, renderer, controls; - -init(); - -async function init() { - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 20); - camera.position.z = 2.5; - - // scene - - scene = new THREE.Scene(); - - const ambientLight = new THREE.AmbientLight(0xffffff); - scene.add(ambientLight); - - const pointLight = new THREE.PointLight(0xffffff, 15); - camera.add(pointLight); - scene.add(camera); - - // model - - const mtlLoader = new MTLLoader().setPath('models/obj/male02/'); - const materials = await mtlLoader.loadAsync('male02.mtl'); - materials.preload(); - - const objLoader = new OBJLoader().setPath('models/obj/male02/'); - objLoader.setMaterials(materials); // optional since OBJ assets can be loaded without an accompanying MTL file - - const object = await objLoader.loadAsync('male02.obj'); - - object.position.y = -0.95; - object.scale.setScalar(0.01); - scene.add(object); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - controls.minDistance = 2; - controls.maxDistance = 5; - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - controls.update(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_loader_pcd.ts b/examples-testing/examples/webgl_loader_pcd.ts deleted file mode 100644 index dd0f0b0f5..000000000 --- a/examples-testing/examples/webgl_loader_pcd.ts +++ /dev/null @@ -1,78 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { PCDLoader } from 'three/addons/loaders/PCDLoader.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let camera, scene, renderer; - -init(); -render(); - -function init() { - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - document.body.appendChild(renderer.domElement); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 0.01, 40); - camera.position.set(0, 0, 1); - scene.add(camera); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.addEventListener('change', render); // use if there is no animation loop - controls.minDistance = 0.5; - controls.maxDistance = 10; - - //scene.add( new THREE.AxesHelper( 1 ) ); - - const loader = new PCDLoader(); - - const loadPointCloud = function (file) { - loader.load('./models/pcd/' + file, function (points) { - points.geometry.center(); - points.geometry.rotateX(Math.PI); - points.name = file; - scene.add(points); - - const gui = new GUI(); - - gui.add(points.material, 'size', 0.001, 0.01).onChange(render); - gui.addColor(points.material, 'color').onChange(render); - gui.add(points, 'name', [ - 'ascii/simple.pcd', - 'binary/Zaghetto.pcd', - 'binary/Zaghetto_8bit.pcd', - 'binary_compressed/pcl_logo.pcd', - ]) - .name('type') - .onChange(e => { - gui.destroy(); - scene.remove(points); - loadPointCloud(e); - }); - gui.open(); - - render(); - }); - }; - - loadPointCloud('binary/Zaghetto.pcd'); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - render(); -} - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_loader_pdb.ts b/examples-testing/examples/webgl_loader_pdb.ts deleted file mode 100644 index b560efa73..000000000 --- a/examples-testing/examples/webgl_loader_pdb.ts +++ /dev/null @@ -1,208 +0,0 @@ -import * as THREE from 'three'; - -import { TrackballControls } from 'three/addons/controls/TrackballControls.js'; -import { PDBLoader } from 'three/addons/loaders/PDBLoader.js'; -import { CSS2DRenderer, CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let camera, scene, renderer, labelRenderer; -let controls; - -let root; - -const MOLECULES = { - Ethanol: 'ethanol.pdb', - Aspirin: 'aspirin.pdb', - Caffeine: 'caffeine.pdb', - Nicotine: 'nicotine.pdb', - LSD: 'lsd.pdb', - Cocaine: 'cocaine.pdb', - Cholesterol: 'cholesterol.pdb', - Lycopene: 'lycopene.pdb', - Glucose: 'glucose.pdb', - 'Aluminium oxide': 'Al2O3.pdb', - Cubane: 'cubane.pdb', - Copper: 'cu.pdb', - Fluorite: 'caf2.pdb', - Salt: 'nacl.pdb', - 'YBCO superconductor': 'ybco.pdb', - Buckyball: 'buckyball.pdb', - Graphite: 'graphite.pdb', -}; - -const params = { - molecule: 'caffeine.pdb', -}; - -const loader = new PDBLoader(); -const offset = new THREE.Vector3(); - -init(); - -function init() { - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x050505); - - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 5000); - camera.position.z = 1000; - scene.add(camera); - - const light1 = new THREE.DirectionalLight(0xffffff, 2.5); - light1.position.set(1, 1, 1); - scene.add(light1); - - const light2 = new THREE.DirectionalLight(0xffffff, 1.5); - light2.position.set(-1, -1, 1); - scene.add(light2); - - root = new THREE.Group(); - scene.add(root); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.getElementById('container').appendChild(renderer.domElement); - - labelRenderer = new CSS2DRenderer(); - labelRenderer.setSize(window.innerWidth, window.innerHeight); - labelRenderer.domElement.style.position = 'absolute'; - labelRenderer.domElement.style.top = '0px'; - labelRenderer.domElement.style.pointerEvents = 'none'; - document.getElementById('container').appendChild(labelRenderer.domElement); - - // - - controls = new TrackballControls(camera, renderer.domElement); - controls.minDistance = 500; - controls.maxDistance = 2000; - - // - - loadMolecule(params.molecule); - - // - - window.addEventListener('resize', onWindowResize); - - // - - const gui = new GUI(); - - gui.add(params, 'molecule', MOLECULES).onChange(loadMolecule); - gui.open(); -} - -// - -function loadMolecule(model) { - const url = 'models/pdb/' + model; - - while (root.children.length > 0) { - const object = root.children[0]; - object.parent.remove(object); - } - - loader.load(url, function (pdb) { - const geometryAtoms = pdb.geometryAtoms; - const geometryBonds = pdb.geometryBonds; - const json = pdb.json; - - const boxGeometry = new THREE.BoxGeometry(1, 1, 1); - const sphereGeometry = new THREE.IcosahedronGeometry(1, 3); - - geometryAtoms.computeBoundingBox(); - geometryAtoms.boundingBox.getCenter(offset).negate(); - - geometryAtoms.translate(offset.x, offset.y, offset.z); - geometryBonds.translate(offset.x, offset.y, offset.z); - - let positions = geometryAtoms.getAttribute('position'); - const colors = geometryAtoms.getAttribute('color'); - - const position = new THREE.Vector3(); - const color = new THREE.Color(); - - for (let i = 0; i < positions.count; i++) { - position.x = positions.getX(i); - position.y = positions.getY(i); - position.z = positions.getZ(i); - - color.r = colors.getX(i); - color.g = colors.getY(i); - color.b = colors.getZ(i); - - const material = new THREE.MeshPhongMaterial({ color: color }); - - const object = new THREE.Mesh(sphereGeometry, material); - object.position.copy(position); - object.position.multiplyScalar(75); - object.scale.multiplyScalar(25); - root.add(object); - - const atom = json.atoms[i]; - - const text = document.createElement('div'); - text.className = 'label'; - text.style.color = 'rgb(' + atom[3][0] + ',' + atom[3][1] + ',' + atom[3][2] + ')'; - text.textContent = atom[4]; - - const label = new CSS2DObject(text); - label.position.copy(object.position); - root.add(label); - } - - positions = geometryBonds.getAttribute('position'); - - const start = new THREE.Vector3(); - const end = new THREE.Vector3(); - - for (let i = 0; i < positions.count; i += 2) { - start.x = positions.getX(i); - start.y = positions.getY(i); - start.z = positions.getZ(i); - - end.x = positions.getX(i + 1); - end.y = positions.getY(i + 1); - end.z = positions.getZ(i + 1); - - start.multiplyScalar(75); - end.multiplyScalar(75); - - const object = new THREE.Mesh(boxGeometry, new THREE.MeshPhongMaterial({ color: 0xffffff })); - object.position.copy(start); - object.position.lerp(end, 0.5); - object.scale.set(5, 5, start.distanceTo(end)); - object.lookAt(end); - root.add(object); - } - }); -} - -// - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - labelRenderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - controls.update(); - - const time = Date.now() * 0.0004; - - root.rotation.x = time; - root.rotation.y = time * 0.7; - - render(); -} - -function render() { - renderer.render(scene, camera); - labelRenderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_loader_ply.ts b/examples-testing/examples/webgl_loader_ply.ts deleted file mode 100644 index 0f4042b7d..000000000 --- a/examples-testing/examples/webgl_loader_ply.ts +++ /dev/null @@ -1,146 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { PLYLoader } from 'three/addons/loaders/PLYLoader.js'; - -let container, stats; - -let camera, cameraTarget, scene, renderer; - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 15); - camera.position.set(3, 0.15, 3); - - cameraTarget = new THREE.Vector3(0, -0.1, 0); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x72645b); - scene.fog = new THREE.Fog(0x72645b, 2, 15); - - // Ground - - const plane = new THREE.Mesh( - new THREE.PlaneGeometry(40, 40), - new THREE.MeshPhongMaterial({ color: 0xcbcbcb, specular: 0x474747 }), - ); - plane.rotation.x = -Math.PI / 2; - plane.position.y = -0.5; - scene.add(plane); - - plane.receiveShadow = true; - - // PLY file - - const loader = new PLYLoader(); - loader.load('./models/ply/ascii/dolphins.ply', function (geometry) { - geometry.computeVertexNormals(); - - const material = new THREE.MeshStandardMaterial({ color: 0x009cff, flatShading: true }); - const mesh = new THREE.Mesh(geometry, material); - - mesh.position.y = -0.2; - mesh.position.z = 0.3; - mesh.rotation.x = -Math.PI / 2; - mesh.scale.multiplyScalar(0.001); - - mesh.castShadow = true; - mesh.receiveShadow = true; - - scene.add(mesh); - }); - - loader.load('./models/ply/binary/Lucy100k.ply', function (geometry) { - geometry.computeVertexNormals(); - - const material = new THREE.MeshStandardMaterial({ color: 0x009cff, flatShading: true }); - const mesh = new THREE.Mesh(geometry, material); - - mesh.position.x = -0.2; - mesh.position.y = -0.02; - mesh.position.z = -0.2; - mesh.scale.multiplyScalar(0.0006); - - mesh.castShadow = true; - mesh.receiveShadow = true; - - scene.add(mesh); - }); - - // Lights - - scene.add(new THREE.HemisphereLight(0x8d7c7c, 0x494966, 3)); - - addShadowedLight(1, 1, 1, 0xffffff, 3.5); - addShadowedLight(0.5, 1, -1, 0xffd500, 3); - - // renderer - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - - renderer.shadowMap.enabled = true; - - container.appendChild(renderer.domElement); - - // stats - - stats = new Stats(); - container.appendChild(stats.dom); - - // resize - - window.addEventListener('resize', onWindowResize); -} - -function addShadowedLight(x, y, z, color, intensity) { - const directionalLight = new THREE.DirectionalLight(color, intensity); - directionalLight.position.set(x, y, z); - scene.add(directionalLight); - - directionalLight.castShadow = true; - - const d = 1; - directionalLight.shadow.camera.left = -d; - directionalLight.shadow.camera.right = d; - directionalLight.shadow.camera.top = d; - directionalLight.shadow.camera.bottom = -d; - - directionalLight.shadow.camera.near = 1; - directionalLight.shadow.camera.far = 4; - - directionalLight.shadow.mapSize.width = 1024; - directionalLight.shadow.mapSize.height = 1024; - - directionalLight.shadow.bias = -0.001; -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - render(); - stats.update(); -} - -function render() { - const timer = Date.now() * 0.0005; - - camera.position.x = Math.sin(timer) * 2.5; - camera.position.z = Math.cos(timer) * 2.5; - - camera.lookAt(cameraTarget); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_loader_svg.ts b/examples-testing/examples/webgl_loader_svg.ts deleted file mode 100644 index a04b79de2..000000000 --- a/examples-testing/examples/webgl_loader_svg.ts +++ /dev/null @@ -1,206 +0,0 @@ -import * as THREE from 'three'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { SVGLoader } from 'three/addons/loaders/SVGLoader.js'; - -let renderer, scene, camera, gui, guiData; - -init(); - -// - -function init() { - const container = document.getElementById('container'); - - // - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(0, 0, 200); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - container.appendChild(renderer.domElement); - - // - - const controls = new OrbitControls(camera, renderer.domElement); - controls.addEventListener('change', render); - controls.screenSpacePanning = true; - - // - - window.addEventListener('resize', onWindowResize); - - guiData = { - currentURL: 'models/svg/tiger.svg', - drawFillShapes: true, - drawStrokes: true, - fillShapesWireframe: false, - strokesWireframe: false, - }; - - loadSVG(guiData.currentURL); - - createGUI(); -} - -function createGUI() { - if (gui) gui.destroy(); - - gui = new GUI(); - - gui.add(guiData, 'currentURL', { - Tiger: 'models/svg/tiger.svg', - 'Joins and caps': 'models/svg/lineJoinsAndCaps.svg', - Hexagon: 'models/svg/hexagon.svg', - Energy: 'models/svg/energy.svg', - 'Test 1': 'models/svg/tests/1.svg', - 'Test 2': 'models/svg/tests/2.svg', - 'Test 3': 'models/svg/tests/3.svg', - 'Test 4': 'models/svg/tests/4.svg', - 'Test 5': 'models/svg/tests/5.svg', - 'Test 6': 'models/svg/tests/6.svg', - 'Test 7': 'models/svg/tests/7.svg', - 'Test 8': 'models/svg/tests/8.svg', - 'Test 9': 'models/svg/tests/9.svg', - Units: 'models/svg/tests/units.svg', - Ordering: 'models/svg/tests/ordering.svg', - Defs: 'models/svg/tests/testDefs/Svg-defs.svg', - Defs2: 'models/svg/tests/testDefs/Svg-defs2.svg', - Defs3: 'models/svg/tests/testDefs/Wave-defs.svg', - Defs4: 'models/svg/tests/testDefs/defs4.svg', - Defs5: 'models/svg/tests/testDefs/defs5.svg', - 'Style CSS inside defs': 'models/svg/style-css-inside-defs.svg', - 'Multiple CSS classes': 'models/svg/multiple-css-classes.svg', - 'Zero Radius': 'models/svg/zero-radius.svg', - 'Styles in svg tag': 'models/svg/tests/styles.svg', - 'Round join': 'models/svg/tests/roundJoinPrecisionIssue.svg', - 'Ellipse Transformations': 'models/svg/tests/ellipseTransform.svg', - singlePointTest: 'models/svg/singlePointTest.svg', - singlePointTest2: 'models/svg/singlePointTest2.svg', - singlePointTest3: 'models/svg/singlePointTest3.svg', - emptyPath: 'models/svg/emptyPath.svg', - }) - .name('SVG File') - .onChange(update); - - gui.add(guiData, 'drawStrokes').name('Draw strokes').onChange(update); - - gui.add(guiData, 'drawFillShapes').name('Draw fill shapes').onChange(update); - - gui.add(guiData, 'strokesWireframe').name('Wireframe strokes').onChange(update); - - gui.add(guiData, 'fillShapesWireframe').name('Wireframe fill shapes').onChange(update); - - function update() { - loadSVG(guiData.currentURL); - } -} - -function loadSVG(url) { - // - - if (scene) disposeScene(scene); - - // - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xb0b0b0); - - // - - const helper = new THREE.GridHelper(160, 10, 0x8d8d8d, 0xc1c1c1); - helper.rotation.x = Math.PI / 2; - scene.add(helper); - - // - - const loader = new SVGLoader(); - - loader.load(url, function (data) { - const group = new THREE.Group(); - group.scale.multiplyScalar(0.25); - group.position.x = -70; - group.position.y = 70; - group.scale.y *= -1; - - let renderOrder = 0; - - for (const path of data.paths) { - const fillColor = path.userData.style.fill; - - if (guiData.drawFillShapes && fillColor !== undefined && fillColor !== 'none') { - const material = new THREE.MeshBasicMaterial({ - color: new THREE.Color().setStyle(fillColor), - opacity: path.userData.style.fillOpacity, - transparent: true, - side: THREE.DoubleSide, - depthWrite: false, - wireframe: guiData.fillShapesWireframe, - }); - - const shapes = SVGLoader.createShapes(path); - - for (const shape of shapes) { - const geometry = new THREE.ShapeGeometry(shape); - const mesh = new THREE.Mesh(geometry, material); - mesh.renderOrder = renderOrder++; - - group.add(mesh); - } - } - - const strokeColor = path.userData.style.stroke; - - if (guiData.drawStrokes && strokeColor !== undefined && strokeColor !== 'none') { - const material = new THREE.MeshBasicMaterial({ - color: new THREE.Color().setStyle(strokeColor), - opacity: path.userData.style.strokeOpacity, - transparent: true, - side: THREE.DoubleSide, - depthWrite: false, - wireframe: guiData.strokesWireframe, - }); - - for (const subPath of path.subPaths) { - const geometry = SVGLoader.pointsToStroke(subPath.getPoints(), path.userData.style); - - if (geometry) { - const mesh = new THREE.Mesh(geometry, material); - mesh.renderOrder = renderOrder++; - - group.add(mesh); - } - } - } - } - - scene.add(group); - - render(); - }); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - render(); -} - -function render() { - renderer.render(scene, camera); -} - -function disposeScene(scene) { - scene.traverse(function (object) { - if (object.isMesh || object.isLine) { - object.geometry.dispose(); - object.material.dispose(); - } - }); -} diff --git a/examples-testing/examples/webgl_loader_texture_dds.ts b/examples-testing/examples/webgl_loader_texture_dds.ts deleted file mode 100644 index ba9b18e9d..000000000 --- a/examples-testing/examples/webgl_loader_texture_dds.ts +++ /dev/null @@ -1,218 +0,0 @@ -import * as THREE from 'three'; - -import { DDSLoader } from 'three/addons/loaders/DDSLoader.js'; - -let camera, scene, renderer; -const meshes = []; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 100); - camera.position.y = -2; - camera.position.z = 16; - - scene = new THREE.Scene(); - - const geometry = new THREE.BoxGeometry(2, 2, 2); - - /* - This is how compressed textures are supposed to be used: - - DXT1 - RGB - opaque textures - DXT3 - RGBA - transparent textures with sharp alpha transitions - DXT5 - RGBA - transparent textures with full alpha range - */ - - const loader = new DDSLoader(); - - const map1 = loader.load('textures/compressed/disturb_dxt1_nomip.dds'); - map1.minFilter = map1.magFilter = THREE.LinearFilter; - map1.anisotropy = 4; - map1.colorSpace = THREE.SRGBColorSpace; - - const map2 = loader.load('textures/compressed/disturb_dxt1_mip.dds'); - map2.anisotropy = 4; - map2.colorSpace = THREE.SRGBColorSpace; - - const map3 = loader.load('textures/compressed/hepatica_dxt3_mip.dds'); - map3.anisotropy = 4; - map3.colorSpace = THREE.SRGBColorSpace; - - const map4 = loader.load('textures/compressed/explosion_dxt5_mip.dds'); - map4.anisotropy = 4; - map4.colorSpace = THREE.SRGBColorSpace; - - const map5 = loader.load('textures/compressed/disturb_argb_nomip.dds'); - map5.minFilter = map5.magFilter = THREE.LinearFilter; - map5.anisotropy = 4; - map5.colorSpace = THREE.SRGBColorSpace; - - const map6 = loader.load('textures/compressed/disturb_argb_mip.dds'); - map6.anisotropy = 4; - map6.colorSpace = THREE.SRGBColorSpace; - - const map7 = loader.load('textures/compressed/disturb_dx10_bc6h_signed_nomip.dds'); - map7.anisotropy = 4; - - const map8 = loader.load('textures/compressed/disturb_dx10_bc6h_signed_mip.dds'); - map8.anisotropy = 4; - - const map9 = loader.load('textures/compressed/disturb_dx10_bc6h_unsigned_nomip.dds'); - map9.anisotropy = 4; - - const map10 = loader.load('textures/compressed/disturb_dx10_bc6h_unsigned_mip.dds'); - map10.anisotropy = 4; - - const map11 = loader.load('textures/wave_normals_24bit_uncompressed.dds'); - map11.anisotropy = 4; - - const cubemap1 = loader.load('textures/compressed/Mountains.dds', function (texture) { - texture.magFilter = THREE.LinearFilter; - texture.minFilter = THREE.LinearFilter; - texture.mapping = THREE.CubeReflectionMapping; - texture.colorSpace = THREE.SRGBColorSpace; - material1.needsUpdate = true; - }); - - const cubemap2 = loader.load('textures/compressed/Mountains_argb_mip.dds', function (texture) { - texture.magFilter = THREE.LinearFilter; - texture.minFilter = THREE.LinearFilter; - texture.mapping = THREE.CubeReflectionMapping; - texture.colorSpace = THREE.SRGBColorSpace; - material5.needsUpdate = true; - }); - - const cubemap3 = loader.load('textures/compressed/Mountains_argb_nomip.dds', function (texture) { - texture.magFilter = THREE.LinearFilter; - texture.minFilter = THREE.LinearFilter; - texture.mapping = THREE.CubeReflectionMapping; - texture.colorSpace = THREE.SRGBColorSpace; - material6.needsUpdate = true; - }); - - const material1 = new THREE.MeshBasicMaterial({ map: map1, envMap: cubemap1 }); - const material2 = new THREE.MeshBasicMaterial({ map: map2 }); - const material3 = new THREE.MeshBasicMaterial({ map: map3, alphaTest: 0.5, side: THREE.DoubleSide }); - const material4 = new THREE.MeshBasicMaterial({ - map: map4, - side: THREE.DoubleSide, - blending: THREE.AdditiveBlending, - depthTest: false, - transparent: true, - }); - const material5 = new THREE.MeshBasicMaterial({ envMap: cubemap2 }); - const material6 = new THREE.MeshBasicMaterial({ envMap: cubemap3 }); - const material7 = new THREE.MeshBasicMaterial({ map: map5 }); - const material8 = new THREE.MeshBasicMaterial({ map: map6 }); - const material9 = new THREE.MeshBasicMaterial({ map: map7 }); - const material10 = new THREE.MeshBasicMaterial({ map: map8 }); - const material11 = new THREE.MeshBasicMaterial({ map: map9 }); - const material12 = new THREE.MeshBasicMaterial({ map: map10 }); - const material13 = new THREE.MeshBasicMaterial({ map: map11, transparent: true }); - - let mesh = new THREE.Mesh(new THREE.TorusGeometry(), material1); - mesh.position.x = -10; - mesh.position.y = -2; - scene.add(mesh); - meshes.push(mesh); - - mesh = new THREE.Mesh(geometry, material2); - mesh.position.x = -6; - mesh.position.y = -2; - scene.add(mesh); - meshes.push(mesh); - - mesh = new THREE.Mesh(geometry, material3); - mesh.position.x = -6; - mesh.position.y = 2; - scene.add(mesh); - meshes.push(mesh); - - mesh = new THREE.Mesh(geometry, material4); - mesh.position.x = -10; - mesh.position.y = 2; - scene.add(mesh); - meshes.push(mesh); - - mesh = new THREE.Mesh(geometry, material5); - mesh.position.x = -2; - mesh.position.y = 2; - scene.add(mesh); - meshes.push(mesh); - - mesh = new THREE.Mesh(geometry, material6); - mesh.position.x = -2; - mesh.position.y = -2; - scene.add(mesh); - meshes.push(mesh); - - mesh = new THREE.Mesh(geometry, material7); - mesh.position.x = 2; - mesh.position.y = -2; - scene.add(mesh); - meshes.push(mesh); - - mesh = new THREE.Mesh(geometry, material8); - mesh.position.x = 2; - mesh.position.y = 2; - scene.add(mesh); - meshes.push(mesh); - - mesh = new THREE.Mesh(geometry, material9); - mesh.position.x = 6; - mesh.position.y = -2; - scene.add(mesh); - meshes.push(mesh); - - mesh = new THREE.Mesh(geometry, material10); - mesh.position.x = 6; - mesh.position.y = 2; - scene.add(mesh); - meshes.push(mesh); - - mesh = new THREE.Mesh(geometry, material11); - mesh.position.x = 10; - mesh.position.y = -2; - scene.add(mesh); - meshes.push(mesh); - - mesh = new THREE.Mesh(geometry, material12); - mesh.position.x = 10; - mesh.position.y = 2; - scene.add(mesh); - meshes.push(mesh); - - mesh = new THREE.Mesh(geometry, material13); - mesh.position.x = -10; - mesh.position.y = -6; - scene.add(mesh); - meshes.push(mesh); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - const time = Date.now() * 0.001; - - for (let i = 0; i < meshes.length; i++) { - const mesh = meshes[i]; - mesh.rotation.x = time; - mesh.rotation.y = time; - } - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_loader_texture_ktx.ts b/examples-testing/examples/webgl_loader_texture_ktx.ts deleted file mode 100644 index a4e749301..000000000 --- a/examples-testing/examples/webgl_loader_texture_ktx.ts +++ /dev/null @@ -1,165 +0,0 @@ -import * as THREE from 'three'; - -import { KTXLoader } from 'three/addons/loaders/KTXLoader.js'; - -/* - This is how compressed textures are supposed to be used: - - best for desktop: - BC1(DXT1) - opaque textures - BC3(DXT5) - transparent textures with full alpha range - - best for iOS: - PVR2, PVR4 - opaque textures or alpha - - best for Android: - ETC1 - opaque textures - ASTC_4x4, ASTC8x8 - transparent textures with full alpha range - */ - -let camera, scene, renderer; -const meshes = []; - -init(); - -function init() { - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - const formats = { - astc: renderer.extensions.has('WEBGL_compressed_texture_astc'), - etc1: renderer.extensions.has('WEBGL_compressed_texture_etc1'), - etc2: renderer.extensions.has('WEBGL_compressed_texture_etc'), - s3tc: renderer.extensions.has('WEBGL_compressed_texture_s3tc'), - pvrtc: renderer.extensions.has('WEBGL_compressed_texture_pvrtc'), - }; - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 2000); - camera.position.z = 1000; - - scene = new THREE.Scene(); - - const ambientLight = new THREE.AmbientLight(0xffffff, 0.02); - scene.add(ambientLight); - - const pointLight = new THREE.PointLight(0xffffff, 2, 0, 0); - pointLight.position.z = 300; - scene.add(pointLight); - - const geometry = new THREE.BoxGeometry(200, 200, 200); - let material1, material2, material3; - - // TODO: add cubemap support - const loader = new KTXLoader(); - - if (formats.pvrtc) { - material1 = new THREE.MeshBasicMaterial({ - map: loader.load('textures/compressed/disturb_PVR2bpp.ktx'), - }); - material1.map.colorSpace = THREE.SRGBColorSpace; - material2 = new THREE.MeshBasicMaterial({ - map: loader.load('textures/compressed/lensflare_PVR4bpp.ktx'), - depthTest: false, - transparent: true, - side: THREE.DoubleSide, - }); - material2.map.colorSpace = THREE.SRGBColorSpace; - - meshes.push(new THREE.Mesh(geometry, material1)); - meshes.push(new THREE.Mesh(geometry, material2)); - } - - if (formats.s3tc) { - material1 = new THREE.MeshBasicMaterial({ - map: loader.load('textures/compressed/disturb_BC1.ktx'), - }); - material1.map.colorSpace = THREE.SRGBColorSpace; - material2 = new THREE.MeshBasicMaterial({ - map: loader.load('textures/compressed/lensflare_BC3.ktx'), - depthTest: false, - transparent: true, - side: THREE.DoubleSide, - }); - material2.map.colorSpace = THREE.SRGBColorSpace; - material3 = new THREE.MeshStandardMaterial({ - normalMap: loader.load('textures/compressed/normal.bc5.ktx'), - }); - - meshes.push(new THREE.Mesh(geometry, material1)); - meshes.push(new THREE.Mesh(geometry, material2)); - meshes.push(new THREE.Mesh(geometry, material3)); - } - - if (formats.etc1) { - material1 = new THREE.MeshBasicMaterial({ - map: loader.load('textures/compressed/disturb_ETC1.ktx'), - }); - - meshes.push(new THREE.Mesh(geometry, material1)); - } - - if (formats.etc2) { - material1 = new THREE.MeshStandardMaterial({ - normalMap: loader.load('textures/compressed/normal.eac_rg.ktx'), - }); - - meshes.push(new THREE.Mesh(geometry, material1)); - } - - if (formats.astc) { - material1 = new THREE.MeshBasicMaterial({ - map: loader.load('textures/compressed/disturb_ASTC4x4.ktx'), - }); - material1.map.colorSpace = THREE.SRGBColorSpace; - material2 = new THREE.MeshBasicMaterial({ - map: loader.load('textures/compressed/lensflare_ASTC8x8.ktx'), - depthTest: false, - transparent: true, - side: THREE.DoubleSide, - }); - material2.map.colorSpace = THREE.SRGBColorSpace; - - meshes.push(new THREE.Mesh(geometry, material1)); - meshes.push(new THREE.Mesh(geometry, material2)); - } - - let x0 = (-Math.min(4, meshes.length) * 300) / 2 + 150; - for (let i = 0; i < Math.min(4, meshes.length); ++i, x0 += 300) { - const mesh = meshes[i]; - mesh.position.x = x0; - mesh.position.y = 150; - scene.add(mesh); - } - - let x1 = (-(meshes.length - 4) * 300) / 2 + 150; - for (let i = 4; i < meshes.length; ++i, x1 += 300) { - const mesh = meshes[i]; - mesh.position.x = x1; - mesh.position.y = -150; - scene.add(mesh); - } - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - const time = Date.now() * 0.001; - - for (let i = 0; i < meshes.length; i++) { - const mesh = meshes[i]; - mesh.rotation.x = time; - mesh.rotation.y = time; - } - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_loader_texture_tga.ts b/examples-testing/examples/webgl_loader_texture_tga.ts deleted file mode 100644 index c4f65b79a..000000000 --- a/examples-testing/examples/webgl_loader_texture_tga.ts +++ /dev/null @@ -1,90 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { TGALoader } from 'three/addons/loaders/TGALoader.js'; - -let camera, scene, renderer, stats; - -init(); - -function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(0, 1, 5); - - scene = new THREE.Scene(); - - // - - const loader = new TGALoader(); - const geometry = new THREE.BoxGeometry(); - - // add box 1 - grey8 texture - - const texture1 = loader.load('textures/crate_grey8.tga'); - texture1.colorSpace = THREE.SRGBColorSpace; - const material1 = new THREE.MeshPhongMaterial({ color: 0xffffff, map: texture1 }); - - const mesh1 = new THREE.Mesh(geometry, material1); - mesh1.position.x = -1; - - scene.add(mesh1); - - // add box 2 - tga texture - - const texture2 = loader.load('textures/crate_color8.tga'); - texture2.colorSpace = THREE.SRGBColorSpace; - const material2 = new THREE.MeshPhongMaterial({ color: 0xffffff, map: texture2 }); - - const mesh2 = new THREE.Mesh(geometry, material2); - mesh2.position.x = 1; - - scene.add(mesh2); - - // - - const ambientLight = new THREE.AmbientLight(0xffffff, 1.5); - scene.add(ambientLight); - - const light = new THREE.DirectionalLight(0xffffff, 2.5); - light.position.set(1, 1, 1); - scene.add(light); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - // - - const controls = new OrbitControls(camera, renderer.domElement); - controls.enableZoom = false; - - // - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - renderer.render(scene, camera); - stats.update(); -} diff --git a/examples-testing/examples/webgl_loader_texture_tiff.ts b/examples-testing/examples/webgl_loader_texture_tiff.ts deleted file mode 100644 index f097774aa..000000000 --- a/examples-testing/examples/webgl_loader_texture_tiff.ts +++ /dev/null @@ -1,87 +0,0 @@ -import * as THREE from 'three'; - -import { TIFFLoader } from 'three/addons/loaders/TIFFLoader.js'; - -let renderer, scene, camera; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.01, 10); - camera.position.set(0, 0, 4); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - document.body.appendChild(renderer.domElement); - - scene = new THREE.Scene(); - - const loader = new TIFFLoader(); - - const geometry = new THREE.PlaneGeometry(); - - // uncompressed - - loader.load('textures/tiff/crate_uncompressed.tif', function (texture) { - texture.colorSpace = THREE.SRGBColorSpace; - - const material = new THREE.MeshBasicMaterial({ map: texture }); - - const mesh = new THREE.Mesh(geometry, material); - mesh.position.set(-1.5, 0, 0); - - scene.add(mesh); - - render(); - }); - - // LZW - - loader.load('textures/tiff/crate_lzw.tif', function (texture) { - texture.colorSpace = THREE.SRGBColorSpace; - - const material = new THREE.MeshBasicMaterial({ map: texture }); - - const mesh = new THREE.Mesh(geometry, material); - mesh.position.set(0, 0, 0); - - scene.add(mesh); - - render(); - }); - - // JPEG - - loader.load('textures/tiff/crate_jpeg.tif', function (texture) { - texture.colorSpace = THREE.SRGBColorSpace; - - const material = new THREE.MeshBasicMaterial({ map: texture }); - - const mesh = new THREE.Mesh(geometry, material); - mesh.position.set(1.5, 0, 0); - - scene.add(mesh); - - render(); - }); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - render(); -} - -// - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_loader_texture_ultrahdr.ts b/examples-testing/examples/webgl_loader_texture_ultrahdr.ts deleted file mode 100644 index c8bce4bf9..000000000 --- a/examples-testing/examples/webgl_loader_texture_ultrahdr.ts +++ /dev/null @@ -1,101 +0,0 @@ -import * as THREE from 'three'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -const params = { - autoRotate: true, - metalness: 1.0, - roughness: 0.0, - exposure: 1.0, - resolution: '2k', - type: 'HalfFloatType', -}; - -let renderer, scene, camera, controls, torusMesh, loader; - -init(); - -function init() { - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - document.body.appendChild(renderer.domElement); - - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = params.exposure; - - renderer.setAnimationLoop(render); - - scene = new THREE.Scene(); - - torusMesh = new THREE.Mesh( - new THREE.TorusKnotGeometry(1, 0.4, 128, 128, 1, 3), - new THREE.MeshStandardMaterial({ roughness: params.roughness, metalness: params.metalness }), - ); - scene.add(torusMesh); - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 500); - camera.position.set(0.0, 0.0, -6.0); - - controls = new OrbitControls(camera, renderer.domElement); - - loader = new UltraHDRLoader(); - loader.setDataType(THREE.FloatType); - - const loadEnvironment = function (resolution = '2k', type = 'HalfFloatType') { - loader.setDataType(THREE[type]); - - loader.load(`textures/equirectangular/spruit_sunrise_${resolution}.hdr.jpg`, function (texture) { - texture.mapping = THREE.EquirectangularReflectionMapping; - texture.needsUpdate = true; - - scene.background = texture; - scene.environment = texture; - }); - }; - - loadEnvironment(params.resolution, params.type); - - const gui = new GUI(); - - gui.add(params, 'autoRotate'); - gui.add(params, 'metalness', 0, 1, 0.01); - gui.add(params, 'roughness', 0, 1, 0.01); - gui.add(params, 'exposure', 0, 4, 0.01); - gui.add(params, 'resolution', ['2k', '4k']).onChange(value => { - loadEnvironment(value, params.type); - }); - gui.add(params, 'type', ['HalfFloatType', 'FloatType']).onChange(value => { - loadEnvironment(params.resolution, value); - }); - - gui.open(); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function render() { - torusMesh.material.roughness = params.roughness; - torusMesh.material.metalness = params.metalness; - - if (params.autoRotate) { - torusMesh.rotation.y += 0.005; - } - - renderer.toneMappingExposure = params.exposure; - - controls.update(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_loader_ttf.ts b/examples-testing/examples/webgl_loader_ttf.ts deleted file mode 100644 index b54b8c290..000000000 --- a/examples-testing/examples/webgl_loader_ttf.ts +++ /dev/null @@ -1,231 +0,0 @@ -import * as THREE from 'three'; - -import { TTFLoader } from 'three/addons/loaders/TTFLoader.js'; -import { Font } from 'three/addons/loaders/FontLoader.js'; -import { TextGeometry } from 'three/addons/geometries/TextGeometry.js'; - -let container; -let camera, cameraTarget, scene, renderer; -let group, textMesh1, textMesh2, textGeo, material; -let firstLetter = true; - -let text = 'three.js'; -const depth = 20, - size = 70, - hover = 30, - curveSegments = 4, - bevelThickness = 2, - bevelSize = 1.5; - -let font = null; -const mirror = true; - -let targetRotation = 0; -let targetRotationOnPointerDown = 0; - -let pointerX = 0; -let pointerXOnPointerDown = 0; - -let windowHalfX = window.innerWidth / 2; - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - // CAMERA - - camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 1500); - camera.position.set(0, 400, 700); - - cameraTarget = new THREE.Vector3(0, 150, 0); - - // SCENE - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x000000); - scene.fog = new THREE.Fog(0x000000, 250, 1400); - - // LIGHTS - - const dirLight1 = new THREE.DirectionalLight(0xffffff, 0.4); - dirLight1.position.set(0, 0, 1).normalize(); - scene.add(dirLight1); - - const dirLight2 = new THREE.DirectionalLight(0xffffff, 2); - dirLight2.position.set(0, hover, 10).normalize(); - dirLight2.color.setHSL(Math.random(), 1, 0.5, THREE.SRGBColorSpace); - scene.add(dirLight2); - - material = new THREE.MeshPhongMaterial({ color: 0xffffff, flatShading: true }); - - group = new THREE.Group(); - group.position.y = 100; - - scene.add(group); - - const loader = new TTFLoader(); - - loader.load('fonts/ttf/kenpixel.ttf', function (json) { - font = new Font(json); - createText(); - }); - - const plane = new THREE.Mesh( - new THREE.PlaneGeometry(10000, 10000), - new THREE.MeshBasicMaterial({ color: 0xffffff, opacity: 0.5, transparent: true }), - ); - plane.position.y = 100; - plane.rotation.x = -Math.PI / 2; - scene.add(plane); - - // RENDERER - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - // EVENTS - - container.style.touchAction = 'none'; - container.addEventListener('pointerdown', onPointerDown); - - document.addEventListener('keypress', onDocumentKeyPress); - document.addEventListener('keydown', onDocumentKeyDown); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - windowHalfX = window.innerWidth / 2; - - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function onDocumentKeyDown(event) { - if (firstLetter) { - firstLetter = false; - text = ''; - } - - const keyCode = event.keyCode; - - // backspace - - if (keyCode === 8) { - event.preventDefault(); - - text = text.substring(0, text.length - 1); - refreshText(); - - return false; - } -} - -function onDocumentKeyPress(event) { - const keyCode = event.which; - - // backspace - - if (keyCode === 8) { - event.preventDefault(); - } else { - const ch = String.fromCharCode(keyCode); - text += ch; - - refreshText(); - } -} - -function createText() { - textGeo = new TextGeometry(text, { - font: font, - - size: size, - depth: depth, - curveSegments: curveSegments, - - bevelThickness: bevelThickness, - bevelSize: bevelSize, - bevelEnabled: true, - }); - - textGeo.computeBoundingBox(); - textGeo.computeVertexNormals(); - - const centerOffset = -0.5 * (textGeo.boundingBox.max.x - textGeo.boundingBox.min.x); - - textMesh1 = new THREE.Mesh(textGeo, material); - - textMesh1.position.x = centerOffset; - textMesh1.position.y = hover; - textMesh1.position.z = 0; - - textMesh1.rotation.x = 0; - textMesh1.rotation.y = Math.PI * 2; - - group.add(textMesh1); - - if (mirror) { - textMesh2 = new THREE.Mesh(textGeo, material); - - textMesh2.position.x = centerOffset; - textMesh2.position.y = -hover; - textMesh2.position.z = depth; - - textMesh2.rotation.x = Math.PI; - textMesh2.rotation.y = Math.PI * 2; - - group.add(textMesh2); - } -} - -function refreshText() { - group.remove(textMesh1); - if (mirror) group.remove(textMesh2); - - if (!text) return; - - createText(); -} - -function onPointerDown(event) { - if (event.isPrimary === false) return; - - pointerXOnPointerDown = event.clientX - windowHalfX; - targetRotationOnPointerDown = targetRotation; - - document.addEventListener('pointermove', onPointerMove); - document.addEventListener('pointerup', onPointerUp); -} - -function onPointerMove(event) { - if (event.isPrimary === false) return; - - pointerX = event.clientX - windowHalfX; - - targetRotation = targetRotationOnPointerDown + (pointerX - pointerXOnPointerDown) * 0.02; -} - -function onPointerUp(event) { - if (event.isPrimary === false) return; - - document.removeEventListener('pointermove', onPointerMove); - document.removeEventListener('pointerup', onPointerUp); -} - -// - -function animate() { - group.rotation.y += (targetRotation - group.rotation.y) * 0.05; - - camera.lookAt(cameraTarget); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_loader_usdz.ts b/examples-testing/examples/webgl_loader_usdz.ts deleted file mode 100644 index 409c6b597..000000000 --- a/examples-testing/examples/webgl_loader_usdz.ts +++ /dev/null @@ -1,68 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; -import { USDLoader } from 'three/addons/loaders/USDLoader.js'; - -let camera, scene, renderer; - -init(); - -async function init() { - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(0, 0.75, -1.5); - - scene = new THREE.Scene(); - - const hdrLoader = new HDRLoader().setPath('textures/equirectangular/'); - - const usdzLoader = new USDLoader().setPath('models/usdz/'); - - const [texture, model] = await Promise.all([ - hdrLoader.loadAsync('venice_sunset_1k.hdr'), - usdzLoader.loadAsync('saeukkang.usdz'), - ]); - - // environment - - texture.mapping = THREE.EquirectangularReflectionMapping; - - scene.background = texture; - scene.backgroundBlurriness = 0.5; - scene.environment = texture; - - // model - - model.position.y = 0.25; - model.position.z = -0.25; - scene.add(model); - - // renderer - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = 2.0; - document.body.appendChild(renderer.domElement); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 1; - controls.maxDistance = 8; - // controls.target.y = 15; - // controls.update(); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_loader_vox.ts b/examples-testing/examples/webgl_loader_vox.ts deleted file mode 100644 index 87e0b2f81..000000000 --- a/examples-testing/examples/webgl_loader_vox.ts +++ /dev/null @@ -1,99 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { VOXLoader } from 'three/addons/loaders/VOXLoader.js'; - -let camera, controls, scene, renderer; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.01, 10); - camera.position.set(0.175, 0.075, 0.175); - - scene = new THREE.Scene(); - scene.add(camera); - - // light - - const hemiLight = new THREE.HemisphereLight(0xcccccc, 0x444444, 3); - scene.add(hemiLight); - - const dirLight = new THREE.DirectionalLight(0xffffff, 2.5); - dirLight.position.set(1.5, 3, 2.5); - scene.add(dirLight); - - const dirLight2 = new THREE.DirectionalLight(0xffffff, 1.5); - dirLight2.position.set(-1.5, -3, -2.5); - scene.add(dirLight2); - - const loader = new VOXLoader(); - loader.load('models/vox/monu10.vox', function (result) { - const mesh = result.scene.children[0]; - mesh.position.y = 0; - mesh.scale.setScalar(0.0015); - scene.add(mesh); - }); - - // renderer - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // controls - - controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 0.1; - controls.maxDistance = 0.5; - - // - - window.addEventListener('resize', onWindowResize); -} - -/* - function displayPalette( palette ) { - - const canvas = document.createElement( 'canvas' ); - canvas.width = 8; - canvas.height = 32; - canvas.style.position = 'absolute'; - canvas.style.top = '0'; - canvas.style.width = '100px'; - canvas.style.imageRendering = 'pixelated'; - document.body.appendChild( canvas ); - - const context = canvas.getContext( '2d' ); - - for ( let c = 0; c < 256; c ++ ) { - - const x = c % 8; - const y = Math.floor( c / 8 ); - - const hex = palette[ c + 1 ]; - const r = hex >> 0 & 0xff; - const g = hex >> 8 & 0xff; - const b = hex >> 16 & 0xff; - context.fillStyle = `rgba(${r},${g},${b},1)`; - context.fillRect( x, 31 - y, 1, 1 ); - - } - - } - */ - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - controls.update(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_loader_vrml.ts b/examples-testing/examples/webgl_loader_vrml.ts deleted file mode 100644 index 1dda79f2b..000000000 --- a/examples-testing/examples/webgl_loader_vrml.ts +++ /dev/null @@ -1,119 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { VRMLLoader } from 'three/addons/loaders/VRMLLoader.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let camera, scene, renderer, stats, controls, loader; - -const params = { - asset: 'house', -}; - -const assets = [ - 'creaseAngle', - 'crystal', - 'house', - 'elevationGrid1', - 'elevationGrid2', - 'extrusion1', - 'extrusion2', - 'extrusion3', - 'lines', - 'linesTransparent', - 'meshWithLines', - 'meshWithTexture', - 'pixelTexture', - 'points', - 'camera', -]; - -let vrmlScene; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1e10); - camera.position.set(-10, 5, 10); - - scene = new THREE.Scene(); - scene.add(camera); - - // light - - const ambientLight = new THREE.AmbientLight(0xffffff, 1.2); - scene.add(ambientLight); - - const dirLight = new THREE.DirectionalLight(0xffffff, 2.0); - dirLight.position.set(200, 200, 200); - scene.add(dirLight); - - loader = new VRMLLoader(); - loadAsset(params.asset); - - // renderer - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // controls - - controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 1; - controls.maxDistance = 200; - controls.enableDamping = true; - - // - - stats = new Stats(); - document.body.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); - - // - - const gui = new GUI(); - gui.add(params, 'asset', assets).onChange(function (value) { - if (vrmlScene) { - vrmlScene.traverse(function (object) { - if (object.material) object.material.dispose(); - if (object.material && object.material.map) object.material.map.dispose(); - if (object.geometry) object.geometry.dispose(); - }); - - scene.remove(vrmlScene); - } - - loadAsset(value); - }); -} - -function loadAsset(asset) { - loader.load('models/vrml/' + asset + '.wrl', function (object) { - vrmlScene = object; - scene.add(object); - controls.reset(); - }); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - controls.update(); // to support damping - - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/webgl_loader_vtk.ts b/examples-testing/examples/webgl_loader_vtk.ts deleted file mode 100644 index dfc798657..000000000 --- a/examples-testing/examples/webgl_loader_vtk.ts +++ /dev/null @@ -1,123 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { TrackballControls } from 'three/addons/controls/TrackballControls.js'; -import { VTKLoader } from 'three/addons/loaders/VTKLoader.js'; - -let stats; - -let camera, controls, scene, renderer; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.01, 100); - camera.position.z = 0.2; - - scene = new THREE.Scene(); - - scene.add(camera); - - // light - - const hemiLight = new THREE.HemisphereLight(0xffffff, 0x000000, 3); - scene.add(hemiLight); - - const dirLight = new THREE.DirectionalLight(0xffffff, 1.5); - dirLight.position.set(2, 2, 2); - scene.add(dirLight); - - const loader = new VTKLoader(); - loader.load('models/vtk/bunny.vtk', function (geometry) { - geometry.center(); - geometry.computeVertexNormals(); - - const material = new THREE.MeshLambertMaterial({ color: 0xffffff }); - const mesh = new THREE.Mesh(geometry, material); - mesh.position.set(-0.075, 0.005, 0); - mesh.scale.multiplyScalar(0.2); - scene.add(mesh); - }); - - const loader1 = new VTKLoader(); - loader1.load('models/vtk/cube_ascii.vtp', function (geometry) { - geometry.computeVertexNormals(); - geometry.center(); - - const material = new THREE.MeshLambertMaterial({ color: 0x00ff00 }); - const mesh = new THREE.Mesh(geometry, material); - - mesh.position.set(-0.025, 0, 0); - mesh.scale.multiplyScalar(0.01); - - scene.add(mesh); - }); - - const loader2 = new VTKLoader(); - loader2.load('models/vtk/cube_binary.vtp', function (geometry) { - geometry.computeVertexNormals(); - geometry.center(); - - const material = new THREE.MeshLambertMaterial({ color: 0x0000ff }); - const mesh = new THREE.Mesh(geometry, material); - - mesh.position.set(0.025, 0, 0); - mesh.scale.multiplyScalar(0.01); - - scene.add(mesh); - }); - - const loader3 = new VTKLoader(); - loader3.load('models/vtk/cube_no_compression.vtp', function (geometry) { - geometry.computeVertexNormals(); - geometry.center(); - - const material = new THREE.MeshLambertMaterial({ color: 0xff0000 }); - const mesh = new THREE.Mesh(geometry, material); - - mesh.position.set(0.075, 0, 0); - mesh.scale.multiplyScalar(0.01); - - scene.add(mesh); - }); - - // renderer - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // controls - - controls = new TrackballControls(camera, renderer.domElement); - controls.minDistance = 0.1; - controls.maxDistance = 0.5; - controls.rotateSpeed = 5.0; - - stats = new Stats(); - document.body.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - controls.handleResize(); -} - -function animate() { - controls.update(); - - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/webgl_loader_xyz.ts b/examples-testing/examples/webgl_loader_xyz.ts deleted file mode 100644 index 315c6de39..000000000 --- a/examples-testing/examples/webgl_loader_xyz.ts +++ /dev/null @@ -1,65 +0,0 @@ -import * as THREE from 'three'; - -import { XYZLoader } from 'three/addons/loaders/XYZLoader.js'; - -let camera, scene, renderer, timer; - -let points; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(10, 7, 10); - - scene = new THREE.Scene(); - scene.add(camera); - camera.lookAt(scene.position); - - timer = new THREE.Timer(); - timer.connect(document); - - const loader = new XYZLoader(); - loader.load('models/xyz/helix_201.xyz', function (geometry) { - geometry.center(); - - const vertexColors = geometry.hasAttribute('color') === true; - - const material = new THREE.PointsMaterial({ size: 0.1, vertexColors: vertexColors }); - - points = new THREE.Points(geometry, material); - scene.add(points); - }); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - const delta = timer.getDelta(); - - if (points) { - points.rotation.x += delta * 0.2; - points.rotation.y += delta * 0.5; - } - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_lod.ts b/examples-testing/examples/webgl_lod.ts deleted file mode 100644 index d957efbbd..000000000 --- a/examples-testing/examples/webgl_lod.ts +++ /dev/null @@ -1,91 +0,0 @@ -import * as THREE from 'three'; - -import { FlyControls } from 'three/addons/controls/FlyControls.js'; - -let container; - -let camera, scene, renderer, controls; - -const timer = new THREE.Timer(); -timer.connect(document); - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 15000); - camera.position.z = 1000; - - scene = new THREE.Scene(); - scene.fog = new THREE.Fog(0x000000, 1, 15000); - - const pointLight = new THREE.PointLight(0xff2200, 3, 0, 0); - pointLight.position.set(0, 0, 0); - scene.add(pointLight); - - const dirLight = new THREE.DirectionalLight(0xffffff, 3); - dirLight.position.set(0, 0, 1).normalize(); - scene.add(dirLight); - - const geometry = [ - [new THREE.IcosahedronGeometry(100, 16), 50], - [new THREE.IcosahedronGeometry(100, 8), 300], - [new THREE.IcosahedronGeometry(100, 4), 1000], - [new THREE.IcosahedronGeometry(100, 2), 2000], - [new THREE.IcosahedronGeometry(100, 1), 8000], - ]; - - const material = new THREE.MeshLambertMaterial({ color: 0xffffff, wireframe: true }); - - for (let j = 0; j < 1000; j++) { - const lod = new THREE.LOD(); - - for (let i = 0; i < geometry.length; i++) { - const mesh = new THREE.Mesh(geometry[i][0], material); - mesh.scale.set(1.5, 1.5, 1.5); - mesh.updateMatrix(); - mesh.matrixAutoUpdate = false; - lod.addLevel(mesh, geometry[i][1]); - } - - lod.position.x = 10000 * (0.5 - Math.random()); - lod.position.y = 7500 * (0.5 - Math.random()); - lod.position.z = 10000 * (0.5 - Math.random()); - lod.updateMatrix(); - lod.matrixAutoUpdate = false; - scene.add(lod); - } - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - // - - controls = new FlyControls(camera, renderer.domElement); - controls.movementSpeed = 1000; - controls.rollSpeed = Math.PI / 10; - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - controls.update(timer.getDelta()); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_marchingcubes.ts b/examples-testing/examples/webgl_marchingcubes.ts deleted file mode 100644 index ea96c0f9f..000000000 --- a/examples-testing/examples/webgl_marchingcubes.ts +++ /dev/null @@ -1,314 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { MarchingCubes } from 'three/addons/objects/MarchingCubes.js'; -import { ToonShader1, ToonShader2, ToonShaderHatching, ToonShaderDotted } from 'three/addons/shaders/ToonShader.js'; - -let container, stats; - -let camera, scene, renderer; - -let materials, current_material; - -let light, pointLight, ambientLight; - -let effect, resolution; - -let effectController; - -let time = 0; - -const timer = new THREE.Timer(); -timer.connect(document); - -init(); - -function init() { - container = document.getElementById('container'); - - // CAMERA - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000); - camera.position.set(-500, 500, 1500); - - // SCENE - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x050505); - - // LIGHTS - - light = new THREE.DirectionalLight(0xffffff, 3); - light.position.set(0.5, 0.5, 1); - scene.add(light); - - pointLight = new THREE.PointLight(0xff7c00, 3, 0, 0); - pointLight.position.set(0, 0, 100); - scene.add(pointLight); - - ambientLight = new THREE.AmbientLight(0x323232, 3); - scene.add(ambientLight); - - // MATERIALS - - materials = generateMaterials(); - current_material = 'shiny'; - - // MARCHING CUBES - - resolution = 28; - - effect = new MarchingCubes(resolution, materials[current_material], true, true, 100000); - effect.position.set(0, 0, 0); - effect.scale.set(700, 700, 700); - - effect.enableUvs = false; - effect.enableColors = false; - - scene.add(effect); - - // RENDERER - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - // CONTROLS - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 500; - controls.maxDistance = 5000; - - // STATS - - stats = new Stats(); - container.appendChild(stats.dom); - - // GUI - - setupGui(); - - // EVENTS - - window.addEventListener('resize', onWindowResize); -} - -// - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function generateMaterials() { - // environment map - - const path = 'textures/cube/SwedishRoyalCastle/'; - const format = '.jpg'; - const urls = [ - path + 'px' + format, - path + 'nx' + format, - path + 'py' + format, - path + 'ny' + format, - path + 'pz' + format, - path + 'nz' + format, - ]; - - const cubeTextureLoader = new THREE.CubeTextureLoader(); - - const reflectionCube = cubeTextureLoader.load(urls); - const refractionCube = cubeTextureLoader.load(urls); - refractionCube.mapping = THREE.CubeRefractionMapping; - - // toons - - const toonMaterial1 = createShaderMaterial(ToonShader1, light, ambientLight); - const toonMaterial2 = createShaderMaterial(ToonShader2, light, ambientLight); - const hatchingMaterial = createShaderMaterial(ToonShaderHatching, light, ambientLight); - const dottedMaterial = createShaderMaterial(ToonShaderDotted, light, ambientLight); - - const texture = new THREE.TextureLoader().load('textures/uv_grid_opengl.jpg'); - texture.wrapS = THREE.RepeatWrapping; - texture.wrapT = THREE.RepeatWrapping; - texture.colorSpace = THREE.SRGBColorSpace; - - const materials = { - shiny: new THREE.MeshStandardMaterial({ - color: 0x9c0000, - envMap: reflectionCube, - roughness: 0.1, - metalness: 1.0, - }), - chrome: new THREE.MeshLambertMaterial({ color: 0xffffff, envMap: reflectionCube }), - liquid: new THREE.MeshLambertMaterial({ color: 0xffffff, envMap: refractionCube, refractionRatio: 0.85 }), - matte: new THREE.MeshPhongMaterial({ specular: 0x494949, shininess: 1 }), - flat: new THREE.MeshLambertMaterial({ - /*TODO flatShading: true */ - }), - textured: new THREE.MeshPhongMaterial({ color: 0xffffff, specular: 0x111111, shininess: 1, map: texture }), - colors: new THREE.MeshPhongMaterial({ color: 0xffffff, specular: 0xffffff, shininess: 2, vertexColors: true }), - multiColors: new THREE.MeshPhongMaterial({ shininess: 2, vertexColors: true }), - plastic: new THREE.MeshPhongMaterial({ specular: 0xc1c1c1, shininess: 250 }), - toon1: toonMaterial1, - toon2: toonMaterial2, - hatching: hatchingMaterial, - dotted: dottedMaterial, - }; - - return materials; -} - -function createShaderMaterial(shader, light, ambientLight) { - const u = THREE.UniformsUtils.clone(shader.uniforms); - - const vs = shader.vertexShader; - const fs = shader.fragmentShader; - - const material = new THREE.ShaderMaterial({ uniforms: u, vertexShader: vs, fragmentShader: fs }); - - material.uniforms['uDirLightPos'].value = light.position; - material.uniforms['uDirLightColor'].value = light.color; - - material.uniforms['uAmbientLightColor'].value = ambientLight.color; - - return material; -} - -// - -function setupGui() { - const createHandler = function (id) { - return function () { - current_material = id; - - effect.material = materials[id]; - effect.enableUvs = current_material === 'textured' ? true : false; - effect.enableColors = current_material === 'colors' || current_material === 'multiColors' ? true : false; - }; - }; - - effectController = { - material: 'shiny', - - speed: 1.0, - numBlobs: 10, - resolution: 28, - isolation: 80, - - floor: true, - wallx: false, - wallz: false, - - dummy: function () {}, - }; - - let h; - - const gui = new GUI(); - - // material (type) - - h = gui.addFolder('Materials'); - - for (const m in materials) { - effectController[m] = createHandler(m); - h.add(effectController, m).name(m); - } - - // simulation - - h = gui.addFolder('Simulation'); - - h.add(effectController, 'speed', 0.1, 8.0, 0.05); - h.add(effectController, 'numBlobs', 1, 50, 1); - h.add(effectController, 'resolution', 14, 100, 1); - h.add(effectController, 'isolation', 10, 300, 1); - - h.add(effectController, 'floor'); - h.add(effectController, 'wallx'); - h.add(effectController, 'wallz'); -} - -// this controls content of marching cubes voxel field - -function updateCubes(object, time, numblobs, floor, wallx, wallz) { - object.reset(); - - // fill the field with some metaballs - - const rainbow = [ - new THREE.Color(0xff0000), - new THREE.Color(0xffbb00), - new THREE.Color(0xffff00), - new THREE.Color(0x00ff00), - new THREE.Color(0x0000ff), - new THREE.Color(0x9400bd), - new THREE.Color(0xc800eb), - ]; - const subtract = 12; - const strength = 1.2 / ((Math.sqrt(numblobs) - 1) / 4 + 1); - - for (let i = 0; i < numblobs; i++) { - const ballx = Math.sin(i + 1.26 * time * (1.03 + 0.5 * Math.cos(0.21 * i))) * 0.27 + 0.5; - const bally = Math.abs(Math.cos(i + 1.12 * time * Math.cos(1.22 + 0.1424 * i))) * 0.77; // dip into the floor - const ballz = Math.cos(i + 1.32 * time * 0.1 * Math.sin(0.92 + 0.53 * i)) * 0.27 + 0.5; - - if (current_material === 'multiColors') { - object.addBall(ballx, bally, ballz, strength, subtract, rainbow[i % 7]); - } else { - object.addBall(ballx, bally, ballz, strength, subtract); - } - } - - if (floor) object.addPlaneY(2, 12); - if (wallz) object.addPlaneZ(2, 12); - if (wallx) object.addPlaneX(2, 12); - - object.update(); -} - -// - -function animate() { - timer.update(); - - render(); - stats.update(); -} - -function render() { - const delta = timer.getDelta(); - - time += delta * effectController.speed * 0.5; - - // marching cubes - - if (effectController.resolution !== resolution) { - resolution = effectController.resolution; - effect.init(Math.floor(resolution)); - } - - if (effectController.isolation !== effect.isolation) { - effect.isolation = effectController.isolation; - } - - updateCubes( - effect, - time, - effectController.numBlobs, - effectController.floor, - effectController.wallx, - effectController.wallz, - ); - - // render - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_materials_alphahash.ts b/examples-testing/examples/webgl_materials_alphahash.ts deleted file mode 100644 index 790e31be4..000000000 --- a/examples-testing/examples/webgl_materials_alphahash.ts +++ /dev/null @@ -1,178 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; - -import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; -import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; -import { TAARenderPass } from 'three/addons/postprocessing/TAARenderPass.js'; -import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; - -let camera, scene, renderer, controls, stats, mesh, material; - -let composer, renderPass, taaRenderPass, outputPass; - -let needsUpdate = false; - -const amount = parseInt(window.location.search.slice(1)) || 3; -const count = Math.pow(amount, 3); - -const color = new THREE.Color(); - -const params = { - alpha: 0.5, - alphaHash: true, - taa: true, - sampleLevel: 2, -}; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(amount, amount, amount); - camera.lookAt(0, 0, 0); - - scene = new THREE.Scene(); - - const geometry = new THREE.IcosahedronGeometry(0.5, 3); - - material = new THREE.MeshStandardMaterial({ - color: 0xffffff, - alphaHash: params.alphaHash, - opacity: params.alpha, - }); - - mesh = new THREE.InstancedMesh(geometry, material, count); - - let i = 0; - const offset = (amount - 1) / 2; - - const matrix = new THREE.Matrix4(); - - for (let x = 0; x < amount; x++) { - for (let y = 0; y < amount; y++) { - for (let z = 0; z < amount; z++) { - matrix.setPosition(offset - x, offset - y, offset - z); - - mesh.setMatrixAt(i, matrix); - mesh.setColorAt(i, color.setHex(Math.random() * 0xffffff)); - - i++; - } - } - } - - scene.add(mesh); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // - - const environment = new RoomEnvironment(); - const pmremGenerator = new THREE.PMREMGenerator(renderer); - - scene.environment = pmremGenerator.fromScene(environment, 0.04).texture; - environment.dispose(); - - // - - composer = new EffectComposer(renderer); - - renderPass = new RenderPass(scene, camera); - renderPass.enabled = false; - - taaRenderPass = new TAARenderPass(scene, camera); - - outputPass = new OutputPass(); - - composer.addPass(renderPass); - composer.addPass(taaRenderPass); - composer.addPass(outputPass); - - // - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableZoom = false; - controls.enablePan = false; - - controls.addEventListener('change', () => (needsUpdate = true)); - - // - - const gui = new GUI(); - - gui.add(params, 'alpha', 0, 1).onChange(onMaterialUpdate); - gui.add(params, 'alphaHash').onChange(onMaterialUpdate); - - const taaFolder = gui.addFolder('Temporal Anti-Aliasing'); - - taaFolder - .add(params, 'taa') - .name('enabled') - .onChange(() => { - renderPass.enabled = !params.taa; - taaRenderPass.enabled = params.taa; - - sampleLevelCtrl.enable(params.taa); - - needsUpdate = true; - }); - - const sampleLevelCtrl = taaFolder.add(params, 'sampleLevel', 0, 6, 1).onChange(() => (needsUpdate = true)); - - // - - stats = new Stats(); - document.body.appendChild(stats.dom); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - composer.setSize(window.innerWidth, window.innerHeight); - - needsUpdate = true; -} - -function onMaterialUpdate() { - material.opacity = params.alpha; - material.alphaHash = params.alphaHash; - material.transparent = !params.alphaHash; - material.depthWrite = params.alphaHash; - - material.needsUpdate = true; - needsUpdate = true; -} - -function animate() { - render(); - - stats.update(); -} - -function render() { - if (needsUpdate) { - taaRenderPass.accumulate = false; - taaRenderPass.sampleLevel = 0; - - needsUpdate = false; - } else { - taaRenderPass.accumulate = true; - taaRenderPass.sampleLevel = params.sampleLevel; - } - - composer.render(); -} diff --git a/examples-testing/examples/webgl_materials_blending.ts b/examples-testing/examples/webgl_materials_blending.ts deleted file mode 100644 index fb2e6a91e..000000000 --- a/examples-testing/examples/webgl_materials_blending.ts +++ /dev/null @@ -1,149 +0,0 @@ -import * as THREE from 'three'; - -let camera, scene, renderer; -let mapBg; - -const textureLoader = new THREE.TextureLoader(); - -init(); - -function init() { - // CAMERA - - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.z = 600; - - // SCENE - - scene = new THREE.Scene(); - - // BACKGROUND - - const canvas = document.createElement('canvas'); - const ctx = canvas.getContext('2d'); - canvas.width = canvas.height = 128; - ctx.fillStyle = '#ddd'; - ctx.fillRect(0, 0, 128, 128); - ctx.fillStyle = '#555'; - ctx.fillRect(0, 0, 64, 64); - ctx.fillStyle = '#999'; - ctx.fillRect(32, 32, 32, 32); - ctx.fillStyle = '#555'; - ctx.fillRect(64, 64, 64, 64); - ctx.fillStyle = '#777'; - ctx.fillRect(96, 96, 32, 32); - - mapBg = new THREE.CanvasTexture(canvas); - mapBg.colorSpace = THREE.SRGBColorSpace; - mapBg.wrapS = mapBg.wrapT = THREE.RepeatWrapping; - mapBg.repeat.set(64, 32); - - scene.background = mapBg; - - // OBJECTS - - const blendings = [ - { name: 'No', constant: THREE.NoBlending }, - { name: 'Normal', constant: THREE.NormalBlending }, - { name: 'Additive', constant: THREE.AdditiveBlending }, - { name: 'Subtractive', constant: THREE.SubtractiveBlending }, - { name: 'Multiply', constant: THREE.MultiplyBlending }, - ]; - - const assignSRGB = texture => { - texture.colorSpace = THREE.SRGBColorSpace; - }; - - const map0 = textureLoader.load('textures/uv_grid_opengl.jpg', assignSRGB); - const map1 = textureLoader.load('textures/sprite0.jpg', assignSRGB); - const map2 = textureLoader.load('textures/sprite0.png', assignSRGB); - const map3 = textureLoader.load('textures/lensflare/lensflare0.png', assignSRGB); - const map4 = textureLoader.load('textures/lensflare/lensflare0_alpha.png', assignSRGB); - - const geo1 = new THREE.PlaneGeometry(100, 100); - const geo2 = new THREE.PlaneGeometry(100, 25); - - addImageRow(map0, 300); - addImageRow(map1, 150); - addImageRow(map2, 0); - addImageRow(map3, -150); - addImageRow(map4, -300); - - function addImageRow(map, y) { - for (let i = 0; i < blendings.length; i++) { - const blending = blendings[i]; - - const material = new THREE.MeshBasicMaterial({ map: map }); - material.transparent = true; - material.blending = blending.constant; - - material.premultipliedAlpha = true; - - const x = (i - blendings.length / 2) * 110; - const z = 0; - - let mesh = new THREE.Mesh(geo1, material); - mesh.position.set(x, y, z); - scene.add(mesh); - - mesh = new THREE.Mesh(geo2, generateLabelMaterial(blending.name)); - mesh.position.set(x, y - 75, z); - scene.add(mesh); - } - } - - // RENDERER - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // EVENTS - - window.addEventListener('resize', onWindowResize); -} - -// - -function onWindowResize() { - const SCREEN_WIDTH = window.innerWidth; - const SCREEN_HEIGHT = window.innerHeight; - - renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); - - camera.aspect = SCREEN_WIDTH / SCREEN_HEIGHT; - camera.updateProjectionMatrix(); -} - -function generateLabelMaterial(text) { - const canvas = document.createElement('canvas'); - const ctx = canvas.getContext('2d'); - canvas.width = 128; - canvas.height = 32; - - ctx.fillStyle = 'rgba( 0, 0, 0, 0.95 )'; - ctx.fillRect(0, 0, 128, 32); - - ctx.fillStyle = 'white'; - ctx.font = 'bold 12pt arial'; - ctx.fillText(text, 10, 22); - - const map = new THREE.CanvasTexture(canvas); - map.colorSpace = THREE.SRGBColorSpace; - - const material = new THREE.MeshBasicMaterial({ map: map, transparent: true }); - - return material; -} - -function animate() { - const time = Date.now() * 0.00025; - const ox = (time * -0.01 * mapBg.repeat.x) % 1; - const oy = (time * -0.01 * mapBg.repeat.y) % 1; - - mapBg.offset.set(ox, oy); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_materials_blending_custom.ts b/examples-testing/examples/webgl_materials_blending_custom.ts deleted file mode 100644 index 072447426..000000000 --- a/examples-testing/examples/webgl_materials_blending_custom.ts +++ /dev/null @@ -1,214 +0,0 @@ -import * as THREE from 'three'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let camera, scene, renderer; - -let mapBg; -const materials = []; - -const params = { - blendEquation: THREE.AddEquation, -}; - -const equations = { - Add: THREE.AddEquation, - Subtract: THREE.SubtractEquation, - ReverseSubtract: THREE.ReverseSubtractEquation, - Min: THREE.MinEquation, - Max: THREE.MaxEquation, -}; - -init(); - -function init() { - // CAMERA - - camera = new THREE.PerspectiveCamera(80, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.z = 700; - - // SCENE - - scene = new THREE.Scene(); - - // BACKGROUND - - const canvas = document.createElement('canvas'); - const ctx = canvas.getContext('2d'); - canvas.width = canvas.height = 128; - ctx.fillStyle = '#ddd'; - ctx.fillRect(0, 0, 128, 128); - ctx.fillStyle = '#555'; - ctx.fillRect(0, 0, 64, 64); - ctx.fillStyle = '#999'; - ctx.fillRect(32, 32, 32, 32); - ctx.fillStyle = '#555'; - ctx.fillRect(64, 64, 64, 64); - ctx.fillStyle = '#777'; - ctx.fillRect(96, 96, 32, 32); - - mapBg = new THREE.CanvasTexture(canvas); - mapBg.colorSpace = THREE.SRGBColorSpace; - mapBg.wrapS = mapBg.wrapT = THREE.RepeatWrapping; - mapBg.repeat.set(64, 32); - - scene.background = mapBg; - - // FOREGROUND OBJECTS - - const src = [ - { name: 'Zero', constant: THREE.ZeroFactor }, - { name: 'One', constant: THREE.OneFactor }, - { name: 'SrcColor', constant: THREE.SrcColorFactor }, - { name: 'OneMinusSrcColor', constant: THREE.OneMinusSrcColorFactor }, - { name: 'SrcAlpha', constant: THREE.SrcAlphaFactor }, - { name: 'OneMinusSrcAlpha', constant: THREE.OneMinusSrcAlphaFactor }, - { name: 'DstAlpha', constant: THREE.DstAlphaFactor }, - { name: 'OneMinusDstAlpha', constant: THREE.OneMinusDstAlphaFactor }, - { name: 'DstColor', constant: THREE.DstColorFactor }, - { name: 'OneMinusDstColor', constant: THREE.OneMinusDstColorFactor }, - { name: 'SrcAlphaSaturate', constant: THREE.SrcAlphaSaturateFactor }, - ]; - - const dst = [ - { name: 'Zero', constant: THREE.ZeroFactor }, - { name: 'One', constant: THREE.OneFactor }, - { name: 'SrcColor', constant: THREE.SrcColorFactor }, - { name: 'OneMinusSrcColor', constant: THREE.OneMinusSrcColorFactor }, - { name: 'SrcAlpha', constant: THREE.SrcAlphaFactor }, - { name: 'OneMinusSrcAlpha', constant: THREE.OneMinusSrcAlphaFactor }, - { name: 'DstAlpha', constant: THREE.DstAlphaFactor }, - { name: 'OneMinusDstAlpha', constant: THREE.OneMinusDstAlphaFactor }, - { name: 'DstColor', constant: THREE.DstColorFactor }, - { name: 'OneMinusDstColor', constant: THREE.OneMinusDstColorFactor }, - ]; - - const geo1 = new THREE.PlaneGeometry(100, 100); - const geo2 = new THREE.PlaneGeometry(100, 25); - - const texture = new THREE.TextureLoader().load('textures/lensflare/lensflare0_alpha.png'); - texture.colorSpace = THREE.SRGBColorSpace; - - for (let i = 0; i < dst.length; i++) { - const blendDst = dst[i]; - - for (let j = 0; j < src.length; j++) { - const blendSrc = src[j]; - - const material = new THREE.MeshBasicMaterial({ map: texture }); - material.transparent = true; - - material.blending = THREE.CustomBlending; - material.blendSrc = blendSrc.constant; - material.blendDst = blendDst.constant; - material.blendEquation = THREE.AddEquation; - - const x = (j - src.length / 2) * 110; - const z = 0; - const y = (i - dst.length / 2) * 110 + 50; - - const mesh = new THREE.Mesh(geo1, material); - mesh.position.set(x, -y, z); - mesh.matrixAutoUpdate = false; - mesh.updateMatrix(); - scene.add(mesh); - - materials.push(material); - } - } - - for (let j = 0; j < src.length; j++) { - const blendSrc = src[j]; - - const x = (j - src.length / 2) * 110; - const z = 0; - const y = (0 - dst.length / 2) * 110 + 50; - - const mesh = new THREE.Mesh(geo2, generateLabelMaterial(blendSrc.name, 'rgba( 0, 150, 0, 1 )')); - mesh.position.set(x, -(y - 70), z); - mesh.matrixAutoUpdate = false; - mesh.updateMatrix(); - scene.add(mesh); - } - - for (let i = 0; i < dst.length; i++) { - const blendDst = dst[i]; - - const x = (0 - src.length / 2) * 110 - 125; - const z = 0; - const y = (i - dst.length / 2) * 110 + 165; - - const mesh = new THREE.Mesh(geo2, generateLabelMaterial(blendDst.name, 'rgba( 150, 0, 0, 1 )')); - mesh.position.set(x, -(y - 120), z); - mesh.matrixAutoUpdate = false; - mesh.updateMatrix(); - scene.add(mesh); - } - - // RENDERER - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // EVENTS - - window.addEventListener('resize', onWindowResize); - - // GUI - - // - const gui = new GUI({ width: 300 }); - - gui.add(params, 'blendEquation', equations).onChange(updateBlendEquation); - gui.open(); -} - -// - -function onWindowResize() { - renderer.setSize(window.innerWidth, window.innerHeight); - - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); -} - -// - -function generateLabelMaterial(text, bg) { - const canvas = document.createElement('canvas'); - const ctx = canvas.getContext('2d'); - canvas.width = 128; - canvas.height = 32; - - ctx.fillStyle = bg; - ctx.fillRect(0, 0, 128, 32); - - ctx.fillStyle = 'white'; - ctx.font = 'bold 11pt arial'; - ctx.fillText(text, 8, 22); - - const map = new THREE.CanvasTexture(canvas); - map.colorSpace = THREE.SRGBColorSpace; - - const material = new THREE.MeshBasicMaterial({ map: map, transparent: true }); - return material; -} - -function updateBlendEquation(value) { - for (const material of materials) { - material.blendEquation = value; - } -} - -function animate() { - const time = Date.now() * 0.00025; - const ox = (time * -0.01 * mapBg.repeat.x) % 1; - const oy = (time * -0.01 * mapBg.repeat.y) % 1; - - mapBg.offset.set(ox, oy); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_materials_bumpmap.ts b/examples-testing/examples/webgl_materials_bumpmap.ts deleted file mode 100644 index a09c21573..000000000 --- a/examples-testing/examples/webgl_materials_bumpmap.ts +++ /dev/null @@ -1,134 +0,0 @@ -import * as THREE from 'three'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let container, loader; - -let camera, scene, renderer, controls; - -let mesh; - -let spotLight; - -const params = { - enableBumpMap: true, -}; - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - // - - camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.z = 12; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x060708); - - // LIGHTS - - scene.add(new THREE.HemisphereLight(0x8d7c7c, 0x494966, 3)); - - spotLight = new THREE.SpotLight(0xffffde, 200); - spotLight.position.set(3.5, 0, 7); - scene.add(spotLight); - - spotLight.castShadow = true; - - spotLight.shadow.mapSize.width = 2048; - spotLight.shadow.mapSize.height = 2048; - - spotLight.shadow.camera.near = 2; - spotLight.shadow.camera.far = 15; - - spotLight.shadow.camera.fov = 40; - - spotLight.shadow.bias = -0.005; - - // - - const mapHeight = new THREE.TextureLoader().load( - 'models/gltf/LeePerrySmith/Infinite-Level_02_Disp_NoSmoothUV-4096.jpg', - ); - - const material = new THREE.MeshPhongMaterial({ - color: 0x9c6e49, - specular: 0x666666, - shininess: 25, - bumpMap: mapHeight, - bumpScale: 10, - }); - - loader = new GLTFLoader(); - loader.load('models/gltf/LeePerrySmith/LeePerrySmith.glb', function (gltf) { - createScene(gltf.scene.children[0].geometry, 1, material); - }); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - renderer.shadowMap.enabled = true; - - // EVENTS - - window.addEventListener('resize', onWindowResize); - - // GUI - - const gui = new GUI(); - - gui.add(params, 'enableBumpMap') - .name('enable bump map') - .onChange(value => { - mesh.material.bumpMap = value === true ? mapHeight : null; - mesh.material.needsUpdate = true; - }); - gui.add(material, 'bumpScale', 0, 40).name('bump scale'); - gui.open(); - - // CONTROLS - - controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 8; - controls.maxDistance = 50; - controls.enablePan = false; - controls.enableDamping = true; -} - -function createScene(geometry, scale, material) { - mesh = new THREE.Mesh(geometry, material); - - mesh.position.y = -0.5; - mesh.scale.set(scale, scale, scale); - - mesh.castShadow = true; - mesh.receiveShadow = true; - - scene.add(mesh); -} - -// - -function onWindowResize() { - renderer.setSize(window.innerWidth, window.innerHeight); - - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); -} - -// - -function animate() { - controls.update(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_materials_car.ts b/examples-testing/examples/webgl_materials_car.ts deleted file mode 100644 index 44be6e0ee..000000000 --- a/examples-testing/examples/webgl_materials_car.ts +++ /dev/null @@ -1,168 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; -import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; - -let camera, scene, renderer; -let stats; - -let grid; -let controls; - -const wheels = []; - -function init() { - const container = document.getElementById('container'); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = 0.85; - container.appendChild(renderer.domElement); - - window.addEventListener('resize', onWindowResize); - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(4.25, 1.4, -4.5); - - controls = new OrbitControls(camera, container); - controls.maxDistance = 9; - controls.maxPolarAngle = THREE.MathUtils.degToRad(90); - controls.target.set(0, 0.5, 0); - controls.update(); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x333333); - scene.environment = new HDRLoader().load('textures/equirectangular/venice_sunset_1k.hdr'); - scene.environment.mapping = THREE.EquirectangularReflectionMapping; - scene.fog = new THREE.Fog(0x333333, 10, 15); - - grid = new THREE.GridHelper(20, 40, 0xffffff, 0xffffff); - grid.material.opacity = 0.2; - grid.material.depthWrite = false; - grid.material.transparent = true; - scene.add(grid); - - // materials - - const bodyMaterial = new THREE.MeshPhysicalMaterial({ - color: 0xff0000, - metalness: 1.0, - roughness: 0.5, - clearcoat: 1.0, - clearcoatRoughness: 0.03, - }); - - const detailsMaterial = new THREE.MeshStandardMaterial({ - color: 0xffffff, - metalness: 1.0, - roughness: 0.5, - }); - - const glassMaterial = new THREE.MeshPhysicalMaterial({ - color: 0xffffff, - metalness: 0.25, - roughness: 0, - transmission: 1.0, - }); - - const bodyColorInput = document.getElementById('body-color'); - bodyColorInput.addEventListener('input', function () { - bodyMaterial.color.set(this.value); - }); - - const detailsColorInput = document.getElementById('details-color'); - detailsColorInput.addEventListener('input', function () { - detailsMaterial.color.set(this.value); - }); - - const glassColorInput = document.getElementById('glass-color'); - glassColorInput.addEventListener('input', function () { - glassMaterial.color.set(this.value); - }); - - // Car - - const shadow = new THREE.TextureLoader().load('models/gltf/ferrari_ao.png'); - - const dracoLoader = new DRACOLoader(); - dracoLoader.setDecoderPath('jsm/libs/draco/gltf/'); - - const loader = new GLTFLoader(); - loader.setDRACOLoader(dracoLoader); - - loader.load('models/gltf/ferrari.glb', function (gltf) { - const carModel = gltf.scene.children[0]; - - carModel.getObjectByName('body').material = bodyMaterial; - - carModel.getObjectByName('rim_fl').material = detailsMaterial; - carModel.getObjectByName('rim_fr').material = detailsMaterial; - carModel.getObjectByName('rim_rr').material = detailsMaterial; - carModel.getObjectByName('rim_rl').material = detailsMaterial; - carModel.getObjectByName('trim').material = detailsMaterial; - - carModel.getObjectByName('glass').material = glassMaterial; - - wheels.push( - carModel.getObjectByName('wheel_fl'), - carModel.getObjectByName('wheel_fr'), - carModel.getObjectByName('wheel_rl'), - carModel.getObjectByName('wheel_rr'), - ); - - // shadow - const mesh = new THREE.Mesh( - new THREE.PlaneGeometry(0.655 * 4, 1.3 * 4), - new THREE.MeshBasicMaterial({ - map: shadow, - blending: THREE.MultiplyBlending, - toneMapped: false, - transparent: true, - premultipliedAlpha: true, - }), - ); - mesh.rotation.x = -Math.PI / 2; - mesh.renderOrder = 2; - carModel.add(mesh); - - scene.add(carModel); - }); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - controls.update(); - - const time = -performance.now() / 1000; - - for (let i = 0; i < wheels.length; i++) { - wheels[i].rotation.x = time * Math.PI * 2; - } - - grid.position.z = -time % 1; - - renderer.render(scene, camera); - - stats.update(); -} - -init(); diff --git a/examples-testing/examples/webgl_materials_cubemap.ts b/examples-testing/examples/webgl_materials_cubemap.ts deleted file mode 100644 index 5f2692751..000000000 --- a/examples-testing/examples/webgl_materials_cubemap.ts +++ /dev/null @@ -1,115 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { OBJLoader } from 'three/addons/loaders/OBJLoader.js'; - -let container, stats; - -let camera, scene, renderer; - -let pointLight; - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.z = 13; - - //cubemap - const path = 'textures/cube/SwedishRoyalCastle/'; - const format = '.jpg'; - const urls = [ - path + 'px' + format, - path + 'nx' + format, - path + 'py' + format, - path + 'ny' + format, - path + 'pz' + format, - path + 'nz' + format, - ]; - - const reflectionCube = new THREE.CubeTextureLoader().load(urls); - const refractionCube = new THREE.CubeTextureLoader().load(urls); - refractionCube.mapping = THREE.CubeRefractionMapping; - - scene = new THREE.Scene(); - scene.background = reflectionCube; - - //lights - const ambient = new THREE.AmbientLight(0xffffff, 3); - scene.add(ambient); - - pointLight = new THREE.PointLight(0xffffff, 200); - scene.add(pointLight); - - //materials - const cubeMaterial3 = new THREE.MeshLambertMaterial({ - color: 0xffaa00, - envMap: reflectionCube, - combine: THREE.MixOperation, - reflectivity: 0.3, - }); - const cubeMaterial2 = new THREE.MeshLambertMaterial({ - color: 0xfff700, - envMap: refractionCube, - refractionRatio: 0.95, - }); - const cubeMaterial1 = new THREE.MeshLambertMaterial({ color: 0xffffff, envMap: reflectionCube }); - - //models - const objLoader = new OBJLoader(); - - objLoader.setPath('models/obj/walt/'); - objLoader.load('WaltHead.obj', function (object) { - const head = object.children[0]; - head.scale.setScalar(0.1); - head.position.y = -3; - head.material = cubeMaterial1; - - const head2 = head.clone(); - head2.position.x = -6; - head2.material = cubeMaterial2; - - const head3 = head.clone(); - head3.position.x = 6; - head3.material = cubeMaterial3; - - scene.add(head, head2, head3); - }); - - //renderer - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - //controls - const controls = new OrbitControls(camera, renderer.domElement); - controls.enableZoom = false; - controls.enablePan = false; - controls.minPolarAngle = Math.PI / 4; - controls.maxPolarAngle = Math.PI / 1.5; - - //stats - stats = new Stats(); - container.appendChild(stats.dom); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - renderer.render(scene, camera); - stats.update(); -} diff --git a/examples-testing/examples/webgl_materials_cubemap_dynamic.ts b/examples-testing/examples/webgl_materials_cubemap_dynamic.ts deleted file mode 100644 index 301835dda..000000000 --- a/examples-testing/examples/webgl_materials_cubemap_dynamic.ts +++ /dev/null @@ -1,115 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import Stats from 'three/addons/libs/stats.module.js'; - -let camera, scene, renderer, stats; -let cube, sphere, torus, material; - -let cubeCamera, cubeRenderTarget; - -let controls; - -init(); - -function init() { - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - document.body.appendChild(renderer.domElement); - - window.addEventListener('resize', onWindowResized); - - stats = new Stats(); - document.body.appendChild(stats.dom); - - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.z = 75; - - scene = new THREE.Scene(); - scene.rotation.y = 0.5; // avoid flying objects occluding the sun - - new HDRLoader().setPath('textures/equirectangular/').load('quarry_01_1k.hdr', function (texture) { - texture.mapping = THREE.EquirectangularReflectionMapping; - - scene.background = texture; - scene.environment = texture; - }); - - // - - cubeRenderTarget = new THREE.WebGLCubeRenderTarget(256); - cubeRenderTarget.texture.type = THREE.HalfFloatType; - - cubeCamera = new THREE.CubeCamera(1, 1000, cubeRenderTarget); - - // - - material = new THREE.MeshStandardMaterial({ - envMap: cubeRenderTarget.texture, - roughness: 0.05, - metalness: 1, - }); - - const gui = new GUI(); - gui.add(material, 'roughness', 0, 1); - gui.add(material, 'metalness', 0, 1); - gui.add(renderer, 'toneMappingExposure', 0, 2).name('exposure'); - - sphere = new THREE.Mesh(new THREE.IcosahedronGeometry(15, 8), material); - scene.add(sphere); - - const material2 = new THREE.MeshStandardMaterial({ - roughness: 0.1, - metalness: 0, - }); - - cube = new THREE.Mesh(new THREE.BoxGeometry(15, 15, 15), material2); - scene.add(cube); - - torus = new THREE.Mesh(new THREE.TorusKnotGeometry(8, 3, 128, 16), material2); - scene.add(torus); - - // - - controls = new OrbitControls(camera, renderer.domElement); - controls.autoRotate = true; -} - -function onWindowResized() { - renderer.setSize(window.innerWidth, window.innerHeight); - - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); -} - -function animate(msTime) { - const time = msTime / 1000; - - cube.position.x = Math.cos(time) * 30; - cube.position.y = Math.sin(time) * 30; - cube.position.z = Math.sin(time) * 30; - - cube.rotation.x += 0.02; - cube.rotation.y += 0.03; - - torus.position.x = Math.cos(time + 10) * 30; - torus.position.y = Math.sin(time + 10) * 30; - torus.position.z = Math.sin(time + 10) * 30; - - torus.rotation.x += 0.02; - torus.rotation.y += 0.03; - - cubeCamera.update(renderer, scene); - - controls.update(); - - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/webgl_materials_cubemap_mipmaps.ts b/examples-testing/examples/webgl_materials_cubemap_mipmaps.ts deleted file mode 100644 index 944f4c18e..000000000 --- a/examples-testing/examples/webgl_materials_cubemap_mipmaps.ts +++ /dev/null @@ -1,119 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let container; - -let camera, scene, renderer; - -init(); - -//load customized cube texture -async function loadCubeTextureWithMipmaps() { - const path = 'textures/cube/angus/'; - const format = '.jpg'; - const mipmaps = []; - const maxLevel = 8; - - async function loadCubeTexture(urls) { - return new Promise(function (resolve) { - new THREE.CubeTextureLoader().load(urls, function (cubeTexture) { - resolve(cubeTexture); - }); - }); - } - - // load mipmaps - const pendings = []; - - for (let level = 0; level <= maxLevel; ++level) { - const urls = []; - - for (let face = 0; face < 6; ++face) { - urls.push(path + 'cube_m0' + level + '_c0' + face + format); - } - - const mipmapLevel = level; - - pendings.push( - loadCubeTexture(urls).then(function (cubeTexture) { - mipmaps[mipmapLevel] = cubeTexture; - }), - ); - } - - await Promise.all(pendings); - - const customizedCubeTexture = mipmaps.shift(); - customizedCubeTexture.mipmaps = mipmaps; - customizedCubeTexture.colorSpace = THREE.SRGBColorSpace; - customizedCubeTexture.minFilter = THREE.LinearMipMapLinearFilter; - customizedCubeTexture.magFilter = THREE.LinearFilter; - customizedCubeTexture.generateMipmaps = false; - customizedCubeTexture.needsUpdate = true; - - return customizedCubeTexture; -} - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000); - camera.position.z = 500; - - scene = new THREE.Scene(); - - loadCubeTextureWithMipmaps().then(function (cubeTexture) { - //model - const sphere = new THREE.SphereGeometry(100, 128, 128); - - //manual mipmaps - let material = new THREE.MeshBasicMaterial({ color: 0xffffff, envMap: cubeTexture }); - material.name = 'manual mipmaps'; - - let mesh = new THREE.Mesh(sphere, material); - mesh.position.set(100, 0, 0); - scene.add(mesh); - - //webgl mipmaps - material = material.clone(); - material.name = 'auto mipmaps'; - - const autoCubeTexture = cubeTexture.clone(); - autoCubeTexture.mipmaps = []; - autoCubeTexture.generateMipmaps = true; - autoCubeTexture.needsUpdate = true; - - material.envMap = autoCubeTexture; - - mesh = new THREE.Mesh(sphere, material); - mesh.position.set(-100, 0, 0); - scene.add(mesh); - }); - - //renderer - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - //controls - const controls = new OrbitControls(camera, renderer.domElement); - controls.minPolarAngle = Math.PI / 4; - controls.maxPolarAngle = Math.PI / 1.5; - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_materials_cubemap_refraction.ts b/examples-testing/examples/webgl_materials_cubemap_refraction.ts deleted file mode 100644 index 8c025071f..000000000 --- a/examples-testing/examples/webgl_materials_cubemap_refraction.ts +++ /dev/null @@ -1,126 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { PLYLoader } from 'three/addons/loaders/PLYLoader.js'; - -let container, stats; - -let camera, scene, renderer; - -let mouseX = 0, - mouseY = 0; - -let windowHalfX = window.innerWidth / 2; -let windowHalfY = window.innerHeight / 2; - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 100000); - camera.position.z = -4000; - - // - - const r = 'textures/cube/Park3Med/'; - - const urls = [r + 'px.jpg', r + 'nx.jpg', r + 'py.jpg', r + 'ny.jpg', r + 'pz.jpg', r + 'nz.jpg']; - - const textureCube = new THREE.CubeTextureLoader().load(urls); - textureCube.mapping = THREE.CubeRefractionMapping; - - scene = new THREE.Scene(); - scene.background = textureCube; - - // LIGHTS - - const ambient = new THREE.AmbientLight(0xffffff, 3.5); - scene.add(ambient); - - // material samples - - const cubeMaterial3 = new THREE.MeshPhongMaterial({ - color: 0xccddff, - envMap: textureCube, - refractionRatio: 0.98, - reflectivity: 0.9, - }); - const cubeMaterial2 = new THREE.MeshPhongMaterial({ color: 0xccfffd, envMap: textureCube, refractionRatio: 0.985 }); - const cubeMaterial1 = new THREE.MeshPhongMaterial({ color: 0xffffff, envMap: textureCube, refractionRatio: 0.98 }); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - stats = new Stats(); - container.appendChild(stats.dom); - - const loader = new PLYLoader(); - loader.load('models/ply/binary/Lucy100k.ply', function (geometry) { - createScene(geometry, cubeMaterial1, cubeMaterial2, cubeMaterial3); - }); - - document.addEventListener('mousemove', onDocumentMouseMove); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - windowHalfX = window.innerWidth / 2; - windowHalfY = window.innerHeight / 2; - - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function createScene(geometry, m1, m2, m3) { - geometry.computeVertexNormals(); - - const s = 1.5; - - let mesh = new THREE.Mesh(geometry, m1); - mesh.scale.x = mesh.scale.y = mesh.scale.z = s; - scene.add(mesh); - - mesh = new THREE.Mesh(geometry, m2); - mesh.position.x = -1500; - mesh.scale.x = mesh.scale.y = mesh.scale.z = s; - scene.add(mesh); - - mesh = new THREE.Mesh(geometry, m3); - mesh.position.x = 1500; - mesh.scale.x = mesh.scale.y = mesh.scale.z = s; - scene.add(mesh); -} - -function onDocumentMouseMove(event) { - mouseX = (event.clientX - windowHalfX) * 4; - mouseY = (event.clientY - windowHalfY) * 4; -} - -// - -function animate() { - render(); - stats.update(); -} - -function render() { - camera.position.x += (mouseX - camera.position.x) * 0.05; - camera.position.y += (-mouseY - camera.position.y) * 0.05; - - camera.lookAt(scene.position); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_materials_cubemap_render_to_mipmaps.ts b/examples-testing/examples/webgl_materials_cubemap_render_to_mipmaps.ts deleted file mode 100644 index 599a1369b..000000000 --- a/examples-testing/examples/webgl_materials_cubemap_render_to_mipmaps.ts +++ /dev/null @@ -1,183 +0,0 @@ -import * as THREE from 'three'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let container; -let camera, scene, renderer; - -const CubemapFilterShader = { - name: 'CubemapFilterShader', - - uniforms: { - cubeTexture: { value: null }, - mipIndex: { value: 0 }, - }, - - vertexShader: /* glsl */ ` - - varying vec3 vWorldDirection; - - #include - - void main() { - vWorldDirection = transformDirection(position, modelMatrix); - #include - #include - gl_Position.z = gl_Position.w; // set z to camera.far - } - - `, - - fragmentShader: /* glsl */ ` - - uniform samplerCube cubeTexture; - varying vec3 vWorldDirection; - - uniform float mipIndex; - - #include - - void main() { - vec3 cubeCoordinates = normalize(vWorldDirection); - - // Colorize mip levels - vec4 color = vec4(1.0, 0.0, 0.0, 1.0); - if (mipIndex == 0.0) color.rgb = vec3(1.0, 1.0, 1.0); - else if (mipIndex == 1.0) color.rgb = vec3(0.0, 0.0, 1.0); - else if (mipIndex == 2.0) color.rgb = vec3(0.0, 1.0, 1.0); - else if (mipIndex == 3.0) color.rgb = vec3(0.0, 1.0, 0.0); - else if (mipIndex == 4.0) color.rgb = vec3(1.0, 1.0, 0.0); - - gl_FragColor = textureCube(cubeTexture, cubeCoordinates, 0.0) * color; - } - - `, -}; - -init(); - -async function loadCubeTexture(urls) { - return new Promise(function (resolve) { - new THREE.CubeTextureLoader().load(urls, function (cubeTexture) { - resolve(cubeTexture); - }); - }); -} - -function allocateCubemapRenderTarget(cubeMapSize) { - const params = { - magFilter: THREE.LinearFilter, - minFilter: THREE.LinearMipMapLinearFilter, - generateMipmaps: false, - type: THREE.HalfFloatType, - format: THREE.RGBAFormat, - colorSpace: THREE.LinearSRGBColorSpace, - depthBuffer: false, - }; - - const rt = new THREE.WebGLCubeRenderTarget(cubeMapSize, params); - - const mipLevels = Math.log(cubeMapSize) * Math.LOG2E + 1.0; - for (let i = 0; i < mipLevels; i++) rt.texture.mipmaps.push({}); - - rt.texture.mapping = THREE.CubeReflectionMapping; - return rt; -} - -function renderToCubeTexture(cubeMapRenderTarget, sourceCubeTexture) { - const geometry = new THREE.BoxGeometry(5, 5, 5); - - const material = new THREE.ShaderMaterial({ - name: CubemapFilterShader.name, - uniforms: THREE.UniformsUtils.clone(CubemapFilterShader.uniforms), - vertexShader: CubemapFilterShader.vertexShader, - fragmentShader: CubemapFilterShader.fragmentShader, - side: THREE.BackSide, - blending: THREE.NoBlending, - }); - - material.uniforms.cubeTexture.value = sourceCubeTexture; - - const mesh = new THREE.Mesh(geometry, material); - const cubeCamera = new THREE.CubeCamera(1, 10, cubeMapRenderTarget); - const mipmapCount = Math.floor(Math.log2(Math.max(cubeMapRenderTarget.width, cubeMapRenderTarget.height))); - - for (let mipmap = 0; mipmap < mipmapCount; mipmap++) { - material.uniforms.mipIndex.value = mipmap; - material.needsUpdate = true; - - cubeMapRenderTarget.viewport.set( - 0, - 0, - cubeMapRenderTarget.width >> mipmap, - cubeMapRenderTarget.height >> mipmap, - ); - - cubeCamera.activeMipmapLevel = mipmap; - cubeCamera.update(renderer, mesh); - } - - mesh.geometry.dispose(); - mesh.material.dispose(); -} - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - // Create renderer - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000); - camera.position.z = 500; - - // Create controls - const controls = new OrbitControls(camera, renderer.domElement); - controls.minPolarAngle = Math.PI / 4; - controls.maxPolarAngle = Math.PI / 1.5; - - window.addEventListener('resize', onWindowResize); - - // Load a cube texture - const r = 'textures/cube/Park3Med/'; - const urls = [r + 'px.jpg', r + 'nx.jpg', r + 'py.jpg', r + 'ny.jpg', r + 'pz.jpg', r + 'nz.jpg']; - - loadCubeTexture(urls).then(cubeTexture => { - // Allocate a cube map render target - const cubeMapRenderTarget = allocateCubemapRenderTarget(512); - - // Render to all the mip levels of cubeMapRenderTarget - renderToCubeTexture(cubeMapRenderTarget, cubeTexture); - - // Create geometry - const sphere = new THREE.SphereGeometry(100, 128, 128); - let material = new THREE.MeshBasicMaterial({ color: 0xffffff, envMap: cubeTexture }); - - let mesh = new THREE.Mesh(sphere, material); - mesh.position.set(-100, 0, 0); - scene.add(mesh); - - material = material.clone(); - material.envMap = cubeMapRenderTarget.texture; - - mesh = new THREE.Mesh(sphere, material); - mesh.position.set(100, 0, 0); - scene.add(mesh); - }); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_materials_displacementmap.ts b/examples-testing/examples/webgl_materials_displacementmap.ts deleted file mode 100644 index fd0be9a5e..000000000 --- a/examples-testing/examples/webgl_materials_displacementmap.ts +++ /dev/null @@ -1,224 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { OBJLoader } from 'three/addons/loaders/OBJLoader.js'; - -let stats; -let camera, scene, renderer, controls; - -const settings = { - metalness: 1.0, - roughness: 0.4, - ambientIntensity: 0.2, - aoMapIntensity: 1.0, - envMapIntensity: 1.0, - displacementScale: 2.436143, // from original model - normalScale: 1.0, -}; - -let mesh, material; - -let pointLight, ambientLight; - -const height = 500; // of camera frustum - -let r = 0.0; - -init(); -initGui(); - -// Init gui -function initGui() { - const gui = new GUI(); - //let gui = gui.addFolder( "Material" ); - gui.add(settings, 'metalness') - .min(0) - .max(1) - .onChange(function (value) { - material.metalness = value; - }); - - gui.add(settings, 'roughness') - .min(0) - .max(1) - .onChange(function (value) { - material.roughness = value; - }); - - gui.add(settings, 'aoMapIntensity') - .min(0) - .max(1) - .onChange(function (value) { - material.aoMapIntensity = value; - }); - - gui.add(settings, 'ambientIntensity') - .min(0) - .max(1) - .onChange(function (value) { - ambientLight.intensity = value; - }); - - gui.add(settings, 'envMapIntensity') - .min(0) - .max(3) - .onChange(function (value) { - material.envMapIntensity = value; - }); - - gui.add(settings, 'displacementScale') - .min(0) - .max(3.0) - .onChange(function (value) { - material.displacementScale = value; - }); - - gui.add(settings, 'normalScale') - .min(-1) - .max(1) - .onChange(function (value) { - material.normalScale.set(1, -1).multiplyScalar(value); - }); -} - -function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - // - - scene = new THREE.Scene(); - - const aspect = window.innerWidth / window.innerHeight; - camera = new THREE.OrthographicCamera(-height * aspect, height * aspect, height, -height, 1, 10000); - camera.position.z = 1500; - scene.add(camera); - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableZoom = false; - controls.enableDamping = true; - - // lights - - ambientLight = new THREE.AmbientLight(0xffffff, settings.ambientIntensity); - scene.add(ambientLight); - - pointLight = new THREE.PointLight(0xff0000, 1.5, 0, 0); - pointLight.position.z = 2500; - scene.add(pointLight); - - const pointLight2 = new THREE.PointLight(0xff6666, 3, 0, 0); - camera.add(pointLight2); - - const pointLight3 = new THREE.PointLight(0x0000ff, 1.5, 0, 0); - pointLight3.position.x = -1000; - pointLight3.position.z = 1000; - scene.add(pointLight3); - - // env map - - const path = 'textures/cube/SwedishRoyalCastle/'; - const format = '.jpg'; - const urls = [ - path + 'px' + format, - path + 'nx' + format, - path + 'py' + format, - path + 'ny' + format, - path + 'pz' + format, - path + 'nz' + format, - ]; - - const reflectionCube = new THREE.CubeTextureLoader().load(urls); - - // textures - - const textureLoader = new THREE.TextureLoader(); - const normalMap = textureLoader.load('models/obj/ninja/normal.png'); - const aoMap = textureLoader.load('models/obj/ninja/ao.jpg'); - const displacementMap = textureLoader.load('models/obj/ninja/displacement.jpg'); - - // material - - material = new THREE.MeshStandardMaterial({ - color: 0xc1c1c1, - roughness: settings.roughness, - metalness: settings.metalness, - - normalMap: normalMap, - normalScale: new THREE.Vector2(1, -1), // why does the normal map require negation in this case? - - aoMap: aoMap, - aoMapIntensity: 1, - - displacementMap: displacementMap, - displacementScale: settings.displacementScale, - displacementBias: -0.428408, // from original model - - envMap: reflectionCube, - envMapIntensity: settings.envMapIntensity, - - side: THREE.DoubleSide, - }); - - // - - const loader = new OBJLoader(); - loader.load('models/obj/ninja/ninjaHead_Low.obj', function (group) { - const geometry = group.children[0].geometry; - geometry.center(); - - mesh = new THREE.Mesh(geometry, material); - mesh.scale.multiplyScalar(25); - scene.add(mesh); - }); - - // - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - const aspect = window.innerWidth / window.innerHeight; - - camera.left = -height * aspect; - camera.right = height * aspect; - camera.top = height; - camera.bottom = -height; - - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - controls.update(); - - stats.begin(); - render(); - stats.end(); -} - -function render() { - pointLight.position.x = 2500 * Math.cos(r); - pointLight.position.z = 2500 * Math.sin(r); - - r += 0.01; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_materials_envmaps.ts b/examples-testing/examples/webgl_materials_envmaps.ts deleted file mode 100644 index 18a5542ed..000000000 --- a/examples-testing/examples/webgl_materials_envmaps.ts +++ /dev/null @@ -1,131 +0,0 @@ -import * as THREE from 'three'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let controls, camera, scene, renderer; -let textureEquirec, textureCube; -let sphereMesh, sphereMaterial, params; - -init(); - -function init() { - // CAMERAS - - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(0, 0, 2.5); - - // SCENE - - scene = new THREE.Scene(); - - // Textures - - const loader = new THREE.CubeTextureLoader(); - loader.setPath('textures/cube/Bridge2/'); - - textureCube = loader.load(['posx.jpg', 'negx.jpg', 'posy.jpg', 'negy.jpg', 'posz.jpg', 'negz.jpg']); - - const textureLoader = new THREE.TextureLoader(); - - textureEquirec = textureLoader.load('textures/2294472375_24a3b8ef46_o.jpg'); - textureEquirec.mapping = THREE.EquirectangularReflectionMapping; - textureEquirec.colorSpace = THREE.SRGBColorSpace; - - scene.background = textureCube; - - // - - const geometry = new THREE.IcosahedronGeometry(1, 15); - sphereMaterial = new THREE.MeshBasicMaterial({ envMap: textureCube }); - sphereMesh = new THREE.Mesh(geometry, sphereMaterial); - scene.add(sphereMesh); - - // - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // - - controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 1.5; - controls.maxDistance = 6; - - // - - params = { - Cube: function () { - scene.background = textureCube; - - sphereMaterial.envMap = textureCube; - sphereMaterial.needsUpdate = true; - }, - Equirectangular: function () { - scene.background = textureEquirec; - - sphereMaterial.envMap = textureEquirec; - sphereMaterial.needsUpdate = true; - }, - Refraction: false, - backgroundRotationX: false, - backgroundRotationY: false, - backgroundRotationZ: false, - syncMaterial: false, - }; - - const gui = new GUI({ width: 300 }); - gui.add(params, 'Cube'); - gui.add(params, 'Equirectangular'); - gui.add(params, 'Refraction').onChange(function (value) { - if (value) { - textureEquirec.mapping = THREE.EquirectangularRefractionMapping; - textureCube.mapping = THREE.CubeRefractionMapping; - } else { - textureEquirec.mapping = THREE.EquirectangularReflectionMapping; - textureCube.mapping = THREE.CubeReflectionMapping; - } - - sphereMaterial.needsUpdate = true; - }); - gui.add(params, 'backgroundRotationX'); - gui.add(params, 'backgroundRotationY'); - gui.add(params, 'backgroundRotationZ'); - gui.add(params, 'syncMaterial'); - gui.open(); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - if (params.backgroundRotationX) { - scene.backgroundRotation.x += 0.001; - } - - if (params.backgroundRotationY) { - scene.backgroundRotation.y += 0.001; - } - - if (params.backgroundRotationZ) { - scene.backgroundRotation.z += 0.001; - } - - if (params.syncMaterial) { - sphereMesh.material.envMapRotation.copy(scene.backgroundRotation); - } - - camera.lookAt(scene.position); - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_materials_envmaps_exr.ts b/examples-testing/examples/webgl_materials_envmaps_exr.ts deleted file mode 100644 index c3f3f4f7d..000000000 --- a/examples-testing/examples/webgl_materials_envmaps_exr.ts +++ /dev/null @@ -1,153 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { EXRLoader } from 'three/addons/loaders/EXRLoader.js'; - -const params = { - envMap: 'EXR', - roughness: 0.0, - metalness: 0.0, - exposure: 1.0, - debug: false, -}; - -let container, stats; -let camera, scene, renderer, controls; -let torusMesh, planeMesh; -let pngCubeRenderTarget, exrCubeRenderTarget; -let pngBackground, exrBackground; - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(0, 0, 120); - - scene = new THREE.Scene(); - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - - container.appendChild(renderer.domElement); - - renderer.toneMapping = THREE.ACESFilmicToneMapping; - - // - - let geometry = new THREE.TorusKnotGeometry(18, 8, 150, 20); - let material = new THREE.MeshStandardMaterial({ - metalness: params.metalness, - roughness: params.roughness, - envMapIntensity: 1.0, - }); - - torusMesh = new THREE.Mesh(geometry, material); - scene.add(torusMesh); - - geometry = new THREE.PlaneGeometry(200, 200); - material = new THREE.MeshBasicMaterial(); - - planeMesh = new THREE.Mesh(geometry, material); - planeMesh.position.y = -50; - planeMesh.rotation.x = -Math.PI * 0.5; - scene.add(planeMesh); - - THREE.DefaultLoadingManager.onLoad = function () { - pmremGenerator.dispose(); - }; - - new EXRLoader().load('textures/piz_compressed.exr', function (texture) { - texture.mapping = THREE.EquirectangularReflectionMapping; - - exrCubeRenderTarget = pmremGenerator.fromEquirectangular(texture); - exrBackground = texture; - }); - - new THREE.TextureLoader().load('textures/equirectangular.png', function (texture) { - texture.mapping = THREE.EquirectangularReflectionMapping; - texture.colorSpace = THREE.SRGBColorSpace; - - pngCubeRenderTarget = pmremGenerator.fromEquirectangular(texture); - pngBackground = texture; - }); - - const pmremGenerator = new THREE.PMREMGenerator(renderer); - pmremGenerator.compileEquirectangularShader(); - - stats = new Stats(); - container.appendChild(stats.dom); - - controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 50; - controls.maxDistance = 300; - - window.addEventListener('resize', onWindowResize); - - const gui = new GUI(); - - gui.add(params, 'envMap', ['EXR', 'PNG']); - gui.add(params, 'roughness', 0, 1, 0.01); - gui.add(params, 'metalness', 0, 1, 0.01); - gui.add(params, 'exposure', 0, 2, 0.01); - gui.add(params, 'debug'); - gui.open(); -} - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); -} - -function animate() { - stats.begin(); - render(); - stats.end(); -} - -function render() { - torusMesh.material.roughness = params.roughness; - torusMesh.material.metalness = params.metalness; - - let newEnvMap = torusMesh.material.envMap; - let background = scene.background; - - switch (params.envMap) { - case 'EXR': - newEnvMap = exrCubeRenderTarget ? exrCubeRenderTarget.texture : null; - background = exrBackground; - break; - case 'PNG': - newEnvMap = pngCubeRenderTarget ? pngCubeRenderTarget.texture : null; - background = pngBackground; - break; - } - - if (newEnvMap !== torusMesh.material.envMap) { - torusMesh.material.envMap = newEnvMap; - torusMesh.material.needsUpdate = true; - - planeMesh.material.map = newEnvMap; - planeMesh.material.needsUpdate = true; - } - - torusMesh.rotation.y += 0.005; - planeMesh.visible = params.debug; - - scene.background = background; - renderer.toneMappingExposure = params.exposure; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_materials_envmaps_fasthdr.ts b/examples-testing/examples/webgl_materials_envmaps_fasthdr.ts deleted file mode 100644 index de8715eb7..000000000 --- a/examples-testing/examples/webgl_materials_envmaps_fasthdr.ts +++ /dev/null @@ -1,169 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js'; - -const params = { - image: 'ballroom', - fov: 40, - exposure: 1.0, - backgroundBlurriness: 0.0, -}; - -let container, stats; -let camera, scene, renderer, controls; - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(params.fov, window.innerWidth / window.innerHeight, 0.1, 50); - camera.position.set(7, 0, 0); - - scene = new THREE.Scene(); - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - - container.appendChild(renderer.domElement); - - renderer.toneMapping = THREE.ACESFilmicToneMapping; - - // - - const sphereGeometry = new THREE.SphereGeometry(0.45, 64, 32); - - const sphere01 = new THREE.Mesh( - sphereGeometry, - new THREE.MeshPhysicalMaterial({ - transmission: 1.0, - thickness: 2.0, - metalness: 0.0, - roughness: 0.0, - }), - ); - sphere01.position.z += 2; - scene.add(sphere01); - - const sphere02 = new THREE.Mesh( - sphereGeometry, - new THREE.MeshStandardMaterial({ - metalness: 0.0, - roughness: 1.0, - }), - ); - sphere02.position.z += 1; - scene.add(sphere02); - - const sphere03 = new THREE.Mesh( - sphereGeometry, - new THREE.MeshStandardMaterial({ - metalness: 1.0, - roughness: 0.0, - }), - ); - sphere03.position.z += 0; - scene.add(sphere03); - - const sphere04 = new THREE.Mesh( - sphereGeometry, - new THREE.MeshStandardMaterial({ - metalness: 1.0, - roughness: 0.5, - color: 0x888888, - }), - ); - sphere04.position.z -= 1; - scene.add(sphere04); - - const sphere05 = new THREE.Mesh( - sphereGeometry, - new THREE.MeshStandardMaterial({ - metalness: 0.0, - roughness: 0.0, - color: 0x6ab440, - }), - ); - sphere05.position.z -= 2; - scene.add(sphere05); - - const loader = new KTX2Loader().setTranscoderPath('jsm/libs/basis/').detectSupport(renderer); - - function loadTexture(url) { - loader.load(url, texture => { - texture.mapping = THREE.CubeUVReflectionMapping; - scene.environment = texture; - scene.background = texture; - }); - } - - loadTexture('https://cdn.needle.tools/static/hdris/ballroom_2k.pmrem.ktx2'); - - stats = new Stats(); - container.appendChild(stats.dom); - - controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 0.1; - controls.maxDistance = 20; - controls.enableDamping = true; - - window.addEventListener('resize', onWindowResize); - - const gui = new GUI(); - - gui.add(params, 'image', { - ballroom: 'https://cdn.needle.tools/static/hdris/ballroom_2k.pmrem.ktx2', - 'brown photostudio': 'https://cdn.needle.tools/static/hdris/brown_photostudio_02_2k.pmrem.ktx2', - 'cape hill': 'https://cdn.needle.tools/static/hdris/cape_hill_2k.pmrem.ktx2', - cannon: 'https://cdn.needle.tools/static/hdris/cannon_2k.pmrem.ktx2', - 'metro noord': 'https://cdn.needle.tools/static/hdris/metro_noord_2k.pmrem.ktx2', - 'the sky is on fire': 'https://cdn.needle.tools/static/hdris/the_sky_is_on_fire_2k.pmrem.ktx2', - 'studio small 09': 'https://cdn.needle.tools/static/hdris/studio_small_09_2k.pmrem.ktx2', - 'wide street 01': 'https://cdn.needle.tools/static/hdris/wide_street_01_2k.pmrem.ktx2', - }).onChange(() => { - loadTexture(params.image); - }); - - gui.add(params, 'exposure', 0, 2, 0.01); - - gui.add(params, 'fov', 10, 100).onChange(() => { - camera.fov = params.fov; - camera.updateProjectionMatrix(); - }); - - gui.add(params, 'backgroundBlurriness', 0, 1, 0.01).name('background blurriness'); - - gui.open(); -} - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); -} - -function animate() { - stats.begin(); - render(); - stats.end(); -} - -function render() { - renderer.toneMappingExposure = params.exposure; - scene.backgroundBlurriness = params.backgroundBlurriness; - - controls.update(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_materials_envmaps_groundprojected.ts b/examples-testing/examples/webgl_materials_envmaps_groundprojected.ts deleted file mode 100644 index c1e0ed83b..000000000 --- a/examples-testing/examples/webgl_materials_envmaps_groundprojected.ts +++ /dev/null @@ -1,151 +0,0 @@ -import * as THREE from 'three'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GroundedSkybox } from 'three/addons/objects/GroundedSkybox.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; -import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; - -const params = { - height: 15, - radius: 100, - enabled: true, -}; - -let camera, scene, renderer, skybox; - -init().then(render); - -async function init() { - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(-20, 7, 20); - camera.lookAt(0, 4, 0); - - scene = new THREE.Scene(); - - const hdrLoader = new HDRLoader(); - const envMap = await hdrLoader.loadAsync('textures/equirectangular/blouberg_sunrise_2_1k.hdr'); - envMap.mapping = THREE.EquirectangularReflectionMapping; - - skybox = new GroundedSkybox(envMap, params.height, params.radius); - skybox.position.y = params.height - 0.01; - scene.add(skybox); - - scene.environment = envMap; - - const dracoLoader = new DRACOLoader(); - dracoLoader.setDecoderPath('jsm/libs/draco/gltf/'); - - const loader = new GLTFLoader(); - loader.setDRACOLoader(dracoLoader); - - const shadow = new THREE.TextureLoader().load('models/gltf/ferrari_ao.png'); - - loader.load('models/gltf/ferrari.glb', function (gltf) { - const bodyMaterial = new THREE.MeshPhysicalMaterial({ - color: 0x000000, - metalness: 1.0, - roughness: 0.8, - clearcoat: 1.0, - clearcoatRoughness: 0.2, - }); - - const detailsMaterial = new THREE.MeshStandardMaterial({ - color: 0xffffff, - metalness: 1.0, - roughness: 0.5, - }); - - const glassMaterial = new THREE.MeshPhysicalMaterial({ - color: 0xffffff, - metalness: 0.25, - roughness: 0, - transmission: 1.0, - }); - - const carModel = gltf.scene.children[0]; - carModel.scale.multiplyScalar(4); - carModel.rotation.y = Math.PI; - - carModel.getObjectByName('body').material = bodyMaterial; - - carModel.getObjectByName('rim_fl').material = detailsMaterial; - carModel.getObjectByName('rim_fr').material = detailsMaterial; - carModel.getObjectByName('rim_rr').material = detailsMaterial; - carModel.getObjectByName('rim_rl').material = detailsMaterial; - carModel.getObjectByName('trim').material = detailsMaterial; - - carModel.getObjectByName('glass').material = glassMaterial; - - // shadow - const mesh = new THREE.Mesh( - new THREE.PlaneGeometry(0.655 * 4, 1.3 * 4), - new THREE.MeshBasicMaterial({ - map: shadow, - blending: THREE.MultiplyBlending, - toneMapped: false, - transparent: true, - premultipliedAlpha: true, - }), - ); - mesh.rotation.x = -Math.PI / 2; - carModel.add(mesh); - - scene.add(carModel); - - render(); - }); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - - // - - const controls = new OrbitControls(camera, renderer.domElement); - controls.addEventListener('change', render); - controls.target.set(0, 2, 0); - controls.maxPolarAngle = THREE.MathUtils.degToRad(90); - controls.maxDistance = 80; - controls.minDistance = 20; - controls.enablePan = false; - controls.update(); - - document.body.appendChild(renderer.domElement); - window.addEventListener('resize', onWindowResize); - - const gui = new GUI(); - - gui.add(params, 'enabled') - .name('Grounded') - .onChange(function (value) { - if (value) { - scene.add(skybox); - scene.background = null; - } else { - scene.remove(skybox); - scene.background = scene.environment; - } - - render(); - }); - - gui.open(); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - render(); -} - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_materials_envmaps_hdr.ts b/examples-testing/examples/webgl_materials_envmaps_hdr.ts deleted file mode 100644 index 2dc2b808a..000000000 --- a/examples-testing/examples/webgl_materials_envmaps_hdr.ts +++ /dev/null @@ -1,163 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { HDRCubeTextureLoader } from 'three/addons/loaders/HDRCubeTextureLoader.js'; -import { DebugEnvironment } from 'three/addons/environments/DebugEnvironment.js'; - -const params = { - envMap: 'HDR', - roughness: 0.0, - metalness: 0.0, - exposure: 1.0, - debug: false, -}; - -let container, stats; -let camera, scene, renderer, controls; -let torusMesh, planeMesh; -let generatedCubeRenderTarget, ldrCubeRenderTarget, hdrCubeRenderTarget; -let ldrCubeMap, hdrCubeMap; - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(0, 0, 120); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x000000); - - renderer = new THREE.WebGLRenderer(); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - - // - - let geometry = new THREE.TorusKnotGeometry(18, 8, 150, 20); - // let geometry = new THREE.SphereGeometry( 26, 64, 32 ); - let material = new THREE.MeshStandardMaterial({ - color: 0xffffff, - metalness: params.metalness, - roughness: params.roughness, - }); - - torusMesh = new THREE.Mesh(geometry, material); - scene.add(torusMesh); - - geometry = new THREE.PlaneGeometry(200, 200); - material = new THREE.MeshBasicMaterial(); - - planeMesh = new THREE.Mesh(geometry, material); - planeMesh.position.y = -50; - planeMesh.rotation.x = -Math.PI * 0.5; - scene.add(planeMesh); - - THREE.DefaultLoadingManager.onLoad = function () { - pmremGenerator.dispose(); - }; - - const hdrUrls = ['px.hdr', 'nx.hdr', 'py.hdr', 'ny.hdr', 'pz.hdr', 'nz.hdr']; - hdrCubeMap = new HDRCubeTextureLoader().setPath('./textures/cube/pisaHDR/').load(hdrUrls, function () { - hdrCubeRenderTarget = pmremGenerator.fromCubemap(hdrCubeMap); - - hdrCubeMap.magFilter = THREE.LinearFilter; - hdrCubeMap.needsUpdate = true; - }); - - const ldrUrls = ['px.png', 'nx.png', 'py.png', 'ny.png', 'pz.png', 'nz.png']; - ldrCubeMap = new THREE.CubeTextureLoader().setPath('./textures/cube/pisa/').load(ldrUrls, function () { - ldrCubeRenderTarget = pmremGenerator.fromCubemap(ldrCubeMap); - }); - - const pmremGenerator = new THREE.PMREMGenerator(renderer); - pmremGenerator.compileCubemapShader(); - - const envScene = new DebugEnvironment(); - generatedCubeRenderTarget = pmremGenerator.fromScene(envScene); - - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - //renderer.toneMapping = ReinhardToneMapping; - - stats = new Stats(); - container.appendChild(stats.dom); - - controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 50; - controls.maxDistance = 300; - - window.addEventListener('resize', onWindowResize); - - const gui = new GUI(); - - gui.add(params, 'envMap', ['Generated', 'LDR', 'HDR']); - gui.add(params, 'roughness', 0, 1, 0.01); - gui.add(params, 'metalness', 0, 1, 0.01); - gui.add(params, 'exposure', 0, 2, 0.01); - gui.add(params, 'debug'); - gui.open(); -} - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); -} - -function animate() { - stats.begin(); - render(); - stats.end(); -} - -function render() { - torusMesh.material.roughness = params.roughness; - torusMesh.material.metalness = params.metalness; - - let renderTarget, cubeMap; - - switch (params.envMap) { - case 'Generated': - renderTarget = generatedCubeRenderTarget; - cubeMap = generatedCubeRenderTarget.texture; - break; - case 'LDR': - renderTarget = ldrCubeRenderTarget; - cubeMap = ldrCubeMap; - break; - case 'HDR': - renderTarget = hdrCubeRenderTarget; - cubeMap = hdrCubeMap; - break; - } - - const newEnvMap = renderTarget ? renderTarget.texture : null; - - if (newEnvMap && newEnvMap !== torusMesh.material.envMap) { - torusMesh.material.envMap = newEnvMap; - torusMesh.material.needsUpdate = true; - - planeMesh.material.map = newEnvMap; - planeMesh.material.needsUpdate = true; - } - - torusMesh.rotation.y += 0.005; - planeMesh.visible = params.debug; - - scene.background = cubeMap; - renderer.toneMappingExposure = params.exposure; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_materials_modified.ts b/examples-testing/examples/webgl_materials_modified.ts deleted file mode 100644 index de36aeb7d..000000000 --- a/examples-testing/examples/webgl_materials_modified.ts +++ /dev/null @@ -1,115 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -let camera, scene, renderer, stats; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.z = 20; - - scene = new THREE.Scene(); - - const loader = new GLTFLoader(); - loader.load('models/gltf/LeePerrySmith/LeePerrySmith.glb', function (gltf) { - const geometry = gltf.scene.children[0].geometry; - - let mesh = new THREE.Mesh(geometry, buildTwistMaterial(2.0)); - mesh.position.x = -3.5; - mesh.position.y = -0.5; - scene.add(mesh); - - mesh = new THREE.Mesh(geometry, buildTwistMaterial(-2.0)); - mesh.position.x = 3.5; - mesh.position.y = -0.5; - scene.add(mesh); - }); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 10; - controls.maxDistance = 50; - - // - - stats = new Stats(); - document.body.appendChild(stats.dom); - - // EVENTS - - window.addEventListener('resize', onWindowResize); -} - -function buildTwistMaterial(amount) { - const material = new THREE.MeshNormalMaterial(); - material.onBeforeCompile = function (shader) { - shader.uniforms.time = { value: 0 }; - - shader.vertexShader = 'uniform float time;\n' + shader.vertexShader; - shader.vertexShader = shader.vertexShader.replace( - '#include ', - [ - `float theta = sin( time + position.y ) / ${amount.toFixed(1)};`, - 'float c = cos( theta );', - 'float s = sin( theta );', - 'mat3 m = mat3( c, 0, s, 0, 1, 0, -s, 0, c );', - 'vec3 transformed = vec3( position ) * m;', - 'vNormal = vNormal * m;', - ].join('\n'), - ); - - material.userData.shader = shader; - }; - - // Make sure WebGLRenderer doesn't reuse a single program - - material.customProgramCacheKey = function () { - return amount.toFixed(1); - }; - - return material; -} - -// - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); -} - -// - -function animate() { - render(); - - stats.update(); -} - -function render() { - scene.traverse(function (child) { - if (child.isMesh) { - const shader = child.material.userData.shader; - - if (shader) { - shader.uniforms.time.value = performance.now() / 1000; - } - } - }); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_materials_normalmap_object_space.ts b/examples-testing/examples/webgl_materials_normalmap_object_space.ts deleted file mode 100644 index 1fc6f8066..000000000 --- a/examples-testing/examples/webgl_materials_normalmap_object_space.ts +++ /dev/null @@ -1,82 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -let renderer, scene, camera; - -init(); - -function init() { - // renderer - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setSize(window.innerWidth, window.innerHeight); - document.body.appendChild(renderer.domElement); - - // scene - scene = new THREE.Scene(); - - // camera - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(-10, 0, 23); - scene.add(camera); - - // controls - const controls = new OrbitControls(camera, renderer.domElement); - controls.addEventListener('change', render); - controls.minDistance = 10; - controls.maxDistance = 50; - controls.enablePan = false; - - // ambient - scene.add(new THREE.AmbientLight(0xffffff, 0.6)); - - // light - const light = new THREE.PointLight(0xffffff, 4.5, 0, 0); - camera.add(light); - - // model - new GLTFLoader().load('models/gltf/Nefertiti/Nefertiti.glb', function (gltf) { - gltf.scene.traverse(function (child) { - if (child.isMesh) { - // glTF currently supports only tangent-space normal maps. - // this model has been modified to demonstrate the use of an object-space normal map. - - child.material.normalMapType = THREE.ObjectSpaceNormalMap; - - // attribute normals are not required with an object-space normal map. remove them. - - child.geometry.deleteAttribute('normal'); - - // - - child.material.side = THREE.DoubleSide; - - child.scale.multiplyScalar(0.5); - - // recenter - - new THREE.Box3().setFromObject(child).getCenter(child.position).multiplyScalar(-1); - - scene.add(child); - } - }); - - render(); - }); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - renderer.setSize(window.innerWidth, window.innerHeight); - - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - render(); -} - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_materials_physical_clearcoat.ts b/examples-testing/examples/webgl_materials_physical_clearcoat.ts deleted file mode 100644 index 408fd9921..000000000 --- a/examples-testing/examples/webgl_materials_physical_clearcoat.ts +++ /dev/null @@ -1,208 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { HDRCubeTextureLoader } from 'three/addons/loaders/HDRCubeTextureLoader.js'; - -import { FlakesTexture } from 'three/addons/textures/FlakesTexture.js'; - -let container, stats; - -let camera, scene, renderer; - -let particleLight; -let group; - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 0.25, 50); - camera.position.z = 10; - - scene = new THREE.Scene(); - - group = new THREE.Group(); - scene.add(group); - - new HDRCubeTextureLoader() - .setPath('textures/cube/pisaHDR/') - .load(['px.hdr', 'nx.hdr', 'py.hdr', 'ny.hdr', 'pz.hdr', 'nz.hdr'], function (texture) { - const geometry = new THREE.SphereGeometry(0.8, 64, 32); - - const textureLoader = new THREE.TextureLoader(); - - const diffuse = textureLoader.load('textures/carbon/Carbon.png'); - diffuse.colorSpace = THREE.SRGBColorSpace; - diffuse.wrapS = THREE.RepeatWrapping; - diffuse.wrapT = THREE.RepeatWrapping; - diffuse.repeat.x = 10; - diffuse.repeat.y = 10; - - const normalMap = textureLoader.load('textures/carbon/Carbon_Normal.png'); - normalMap.wrapS = THREE.RepeatWrapping; - normalMap.wrapT = THREE.RepeatWrapping; - normalMap.repeat.x = 10; - normalMap.repeat.y = 10; - - const normalMap2 = textureLoader.load('textures/water/Water_1_M_Normal.jpg'); - - const normalMap3 = new THREE.CanvasTexture(new FlakesTexture()); - normalMap3.wrapS = THREE.RepeatWrapping; - normalMap3.wrapT = THREE.RepeatWrapping; - normalMap3.repeat.x = 10; - normalMap3.repeat.y = 6; - normalMap3.anisotropy = 16; - - const normalMap4 = textureLoader.load('textures/golfball.jpg'); - - const clearcoatNormalMap = textureLoader.load( - 'textures/pbr/Scratched_gold/Scratched_gold_01_1K_Normal.png', - ); - - // car paint - - let material = new THREE.MeshPhysicalMaterial({ - clearcoat: 1.0, - clearcoatRoughness: 0.1, - metalness: 0.9, - roughness: 0.5, - color: 0x0000ff, - normalMap: normalMap3, - normalScale: new THREE.Vector2(0.15, 0.15), - }); - - let mesh = new THREE.Mesh(geometry, material); - mesh.position.x = -1; - mesh.position.y = 1; - group.add(mesh); - - // fibers - - material = new THREE.MeshPhysicalMaterial({ - roughness: 0.5, - clearcoat: 1.0, - clearcoatRoughness: 0.1, - map: diffuse, - normalMap: normalMap, - }); - mesh = new THREE.Mesh(geometry, material); - mesh.position.x = 1; - mesh.position.y = 1; - group.add(mesh); - - // golf - - material = new THREE.MeshPhysicalMaterial({ - metalness: 0.0, - roughness: 0.1, - clearcoat: 1.0, - normalMap: normalMap4, - clearcoatNormalMap: clearcoatNormalMap, - - // y scale is negated to compensate for normal map handedness. - clearcoatNormalScale: new THREE.Vector2(2.0, -2.0), - }); - mesh = new THREE.Mesh(geometry, material); - mesh.position.x = -1; - mesh.position.y = -1; - group.add(mesh); - - // clearcoat + normalmap - - material = new THREE.MeshPhysicalMaterial({ - clearcoat: 1.0, - metalness: 1.0, - color: 0xff0000, - normalMap: normalMap2, - normalScale: new THREE.Vector2(0.15, 0.15), - clearcoatNormalMap: clearcoatNormalMap, - - // y scale is negated to compensate for normal map handedness. - clearcoatNormalScale: new THREE.Vector2(2.0, -2.0), - }); - mesh = new THREE.Mesh(geometry, material); - mesh.position.x = 1; - mesh.position.y = -1; - group.add(mesh); - - // - - scene.background = texture; - scene.environment = texture; - }); - - // LIGHTS - - particleLight = new THREE.Mesh( - new THREE.SphereGeometry(0.05, 8, 8), - new THREE.MeshBasicMaterial({ color: 0xffffff }), - ); - scene.add(particleLight); - - particleLight.add(new THREE.PointLight(0xffffff, 30)); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - // - - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = 1.25; - - // - - // - - stats = new Stats(); - container.appendChild(stats.dom); - - // EVENTS - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 3; - controls.maxDistance = 30; - - window.addEventListener('resize', onWindowResize); -} - -// - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); -} - -// - -function animate() { - render(); - - stats.update(); -} - -function render() { - const timer = Date.now() * 0.00025; - - particleLight.position.x = Math.sin(timer * 7) * 3; - particleLight.position.y = Math.cos(timer * 5) * 4; - particleLight.position.z = Math.cos(timer * 3) * 3; - - for (let i = 0; i < group.children.length; i++) { - const child = group.children[i]; - child.rotation.y += 0.005; - } - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_materials_physical_transmission.ts b/examples-testing/examples/webgl_materials_physical_transmission.ts deleted file mode 100644 index 08ee08cac..000000000 --- a/examples-testing/examples/webgl_materials_physical_transmission.ts +++ /dev/null @@ -1,192 +0,0 @@ -import * as THREE from 'three'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; - -const params = { - color: 0xffffff, - transmission: 1, - opacity: 1, - metalness: 0, - roughness: 0, - ior: 1.5, - thickness: 0.01, - specularIntensity: 1, - specularColor: 0xffffff, - envMapIntensity: 1, - lightIntensity: 1, - exposure: 1, - transmissionResolutionScale: 1, -}; - -let camera, scene, renderer; - -let mesh; - -const hdrEquirect = new UltraHDRLoader() - .setPath('textures/equirectangular/') - .load('royal_esplanade_2k.hdr.jpg', function () { - hdrEquirect.mapping = THREE.EquirectangularReflectionMapping; - - init(); - render(); - }); - -function init() { - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.shadowMap.enabled = true; - document.body.appendChild(renderer.domElement); - - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = params.exposure; - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 2000); - camera.position.set(0, 0, 120); - - // - - scene.background = hdrEquirect; - - // - - const geometry = new THREE.SphereGeometry(20, 64, 32); - - const texture = new THREE.CanvasTexture(generateTexture()); - texture.magFilter = THREE.NearestFilter; - texture.wrapT = THREE.RepeatWrapping; - texture.wrapS = THREE.RepeatWrapping; - texture.repeat.set(1, 3.5); - - const material = new THREE.MeshPhysicalMaterial({ - color: params.color, - metalness: params.metalness, - roughness: params.roughness, - ior: params.ior, - alphaMap: texture, - envMap: hdrEquirect, - envMapIntensity: params.envMapIntensity, - transmission: params.transmission, // use material.transmission for glass materials - specularIntensity: params.specularIntensity, - specularColor: params.specularColor, - opacity: params.opacity, - side: THREE.DoubleSide, - transparent: true, - }); - - mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - // - - const controls = new OrbitControls(camera, renderer.domElement); - controls.addEventListener('change', render); // use if there is no animation loop - controls.minDistance = 10; - controls.maxDistance = 150; - - window.addEventListener('resize', onWindowResize); - - // - - const gui = new GUI(); - - gui.addColor(params, 'color').onChange(function () { - material.color.set(params.color); - render(); - }); - - gui.add(params, 'transmission', 0, 1, 0.01).onChange(function () { - material.transmission = params.transmission; - render(); - }); - - gui.add(params, 'opacity', 0, 1, 0.01).onChange(function () { - material.opacity = params.opacity; - render(); - }); - - gui.add(params, 'metalness', 0, 1, 0.01).onChange(function () { - material.metalness = params.metalness; - render(); - }); - - gui.add(params, 'roughness', 0, 1, 0.01).onChange(function () { - material.roughness = params.roughness; - render(); - }); - - gui.add(params, 'ior', 1, 2, 0.01).onChange(function () { - material.ior = params.ior; - render(); - }); - - gui.add(params, 'thickness', 0, 5, 0.01).onChange(function () { - material.thickness = params.thickness; - render(); - }); - - gui.add(params, 'specularIntensity', 0, 1, 0.01).onChange(function () { - material.specularIntensity = params.specularIntensity; - render(); - }); - - gui.addColor(params, 'specularColor').onChange(function () { - material.specularColor.set(params.specularColor); - render(); - }); - - gui.add(params, 'envMapIntensity', 0, 1, 0.01) - .name('envMap intensity') - .onChange(function () { - material.envMapIntensity = params.envMapIntensity; - render(); - }); - - gui.add(params, 'exposure', 0, 1, 0.01).onChange(function () { - renderer.toneMappingExposure = params.exposure; - render(); - }); - - gui.add(params, 'transmissionResolutionScale', 0.01, 1, 0.01) - .name('transmission resolution') - .onChange(function () { - renderer.transmissionResolutionScale = params.transmissionResolutionScale; - render(); - }); - - gui.open(); -} - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); - - render(); -} - -// - -function generateTexture() { - const canvas = document.createElement('canvas'); - canvas.width = 2; - canvas.height = 2; - - const context = canvas.getContext('2d'); - context.fillStyle = 'white'; - context.fillRect(0, 1, 2, 1); - - return canvas; -} - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_materials_physical_transmission_alpha.ts b/examples-testing/examples/webgl_materials_physical_transmission_alpha.ts deleted file mode 100644 index 6318d7844..000000000 --- a/examples-testing/examples/webgl_materials_physical_transmission_alpha.ts +++ /dev/null @@ -1,194 +0,0 @@ -import * as THREE from 'three'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -const params = { - color: 0xffffff, - transmission: 1, - opacity: 1, - metalness: 0, - roughness: 0, - ior: 1.5, - thickness: 0.01, - attenuationColor: 0xffffff, - attenuationDistance: 1, - specularIntensity: 1, - specularColor: 0xffffff, - envMapIntensity: 1, - lightIntensity: 1, - exposure: 1, -}; - -let camera, scene, renderer; - -let mesh, material; - -const hdrEquirect = new UltraHDRLoader() - .setPath('textures/equirectangular/') - .load('royal_esplanade_2k.hdr.jpg', function () { - hdrEquirect.mapping = THREE.EquirectangularReflectionMapping; - - new GLTFLoader().setPath('models/gltf/').load('DragonAttenuation.glb', function (gltf) { - gltf.scene.traverse(function (child) { - if (child.isMesh && child.material.isMeshPhysicalMaterial) { - mesh = child; - material = mesh.material; - - const color = new THREE.Color(); - - params.color = color.copy(mesh.material.color).getHex(); - params.roughness = mesh.material.roughness; - params.metalness = mesh.material.metalness; - - params.ior = mesh.material.ior; - params.specularIntensity = mesh.material.specularIntensity; - - params.transmission = mesh.material.transmission; - params.thickness = mesh.material.thickness; - params.attenuationColor = color.copy(mesh.material.attenuationColor).getHex(); - params.attenuationDistance = mesh.material.attenuationDistance; - } - }); - - init(); - - scene.add(gltf.scene); - - scene.environment = hdrEquirect; - //scene.background = hdrEquirect; - - render(); - }); - }); - -function init() { - renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.shadowMap.enabled = true; - document.body.appendChild(renderer.domElement); - - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = params.exposure; - - // accommodate CSS table - renderer.domElement.style.position = 'absolute'; - renderer.domElement.style.top = 0; - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 2000); - camera.position.set(-5, 0.5, 0); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.addEventListener('change', render); // use if there is no animation loop - controls.minDistance = 5; - controls.maxDistance = 20; - controls.target.y = 0.5; - controls.update(); - - window.addEventListener('resize', onWindowResize); - - // - - const gui = new GUI(); - - gui.addColor(params, 'color').onChange(function () { - material.color.set(params.color); - render(); - }); - - gui.add(params, 'transmission', 0, 1, 0.01).onChange(function () { - material.transmission = params.transmission; - render(); - }); - - gui.add(params, 'opacity', 0, 1, 0.01).onChange(function () { - material.opacity = params.opacity; - const transparent = params.opacity < 1; - - if (transparent !== material.transparent) { - material.transparent = transparent; - material.needsUpdate = true; - } - - render(); - }); - - gui.add(params, 'metalness', 0, 1, 0.01).onChange(function () { - material.metalness = params.metalness; - render(); - }); - - gui.add(params, 'roughness', 0, 1, 0.01).onChange(function () { - material.roughness = params.roughness; - render(); - }); - - gui.add(params, 'ior', 1, 2, 0.01).onChange(function () { - material.ior = params.ior; - render(); - }); - - gui.add(params, 'thickness', 0, 5, 0.01).onChange(function () { - material.thickness = params.thickness; - render(); - }); - - gui.addColor(params, 'attenuationColor') - .name('attenuation color') - .onChange(function () { - material.attenuationColor.set(params.attenuationColor); - render(); - }); - - gui.add(params, 'attenuationDistance', 0, 1, 0.01).onChange(function () { - material.attenuationDistance = params.attenuationDistance; - render(); - }); - - gui.add(params, 'specularIntensity', 0, 1, 0.01).onChange(function () { - material.specularIntensity = params.specularIntensity; - render(); - }); - - gui.addColor(params, 'specularColor').onChange(function () { - material.specularColor.set(params.specularColor); - render(); - }); - - gui.add(params, 'envMapIntensity', 0, 1, 0.01) - .name('envMap intensity') - .onChange(function () { - material.envMapIntensity = params.envMapIntensity; - render(); - }); - - gui.add(params, 'exposure', 0, 1, 0.01).onChange(function () { - renderer.toneMappingExposure = params.exposure; - render(); - }); - - gui.open(); -} - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); - - render(); -} - -// - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_materials_texture_anisotropy.ts b/examples-testing/examples/webgl_materials_texture_anisotropy.ts deleted file mode 100644 index 1e030d64d..000000000 --- a/examples-testing/examples/webgl_materials_texture_anisotropy.ts +++ /dev/null @@ -1,143 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -const SCREEN_WIDTH = window.innerWidth; -const SCREEN_HEIGHT = window.innerHeight; - -let container, stats; - -let camera, scene1, scene2, renderer; - -let mouseX = 0, - mouseY = 0; - -const windowHalfX = window.innerWidth / 2; -const windowHalfY = window.innerHeight / 2; - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - - // - - camera = new THREE.PerspectiveCamera(35, SCREEN_WIDTH / SCREEN_HEIGHT, 1, 25000); - camera.position.z = 1500; - - scene1 = new THREE.Scene(); - scene1.background = new THREE.Color(0xf2f7ff); - scene1.fog = new THREE.Fog(0xf2f7ff, 1, 25000); - - scene2 = new THREE.Scene(); - scene2.background = new THREE.Color(0xf2f7ff); - scene2.fog = new THREE.Fog(0xf2f7ff, 1, 25000); - - scene1.add(new THREE.AmbientLight(0xeef0ff, 3)); - scene2.add(new THREE.AmbientLight(0xeef0ff, 3)); - - const light1 = new THREE.DirectionalLight(0xffffff, 6); - light1.position.set(1, 1, 1); - scene1.add(light1); - - const light2 = new THREE.DirectionalLight(0xffffff, 6); - light2.position.set(1, 1, 1); - scene2.add(light2); - - // GROUND - - const textureLoader = new THREE.TextureLoader(); - - const maxAnisotropy = renderer.capabilities.getMaxAnisotropy(); - - const texture1 = textureLoader.load('textures/crate.gif'); - const material1 = new THREE.MeshPhongMaterial({ color: 0xffffff, map: texture1 }); - - texture1.colorSpace = THREE.SRGBColorSpace; - texture1.anisotropy = maxAnisotropy; - texture1.wrapS = texture1.wrapT = THREE.RepeatWrapping; - texture1.repeat.set(512, 512); - - const texture2 = textureLoader.load('textures/crate.gif'); - const material2 = new THREE.MeshPhongMaterial({ color: 0xffffff, map: texture2 }); - - texture2.colorSpace = THREE.SRGBColorSpace; - texture2.anisotropy = 1; - texture2.wrapS = texture2.wrapT = THREE.RepeatWrapping; - texture2.repeat.set(512, 512); - - if (maxAnisotropy > 0) { - document.getElementById('val_left').innerHTML = texture1.anisotropy; - document.getElementById('val_right').innerHTML = texture2.anisotropy; - } else { - document.getElementById('val_left').innerHTML = 'not supported'; - document.getElementById('val_right').innerHTML = 'not supported'; - } - - // - - const geometry = new THREE.PlaneGeometry(100, 100); - - const mesh1 = new THREE.Mesh(geometry, material1); - mesh1.rotation.x = -Math.PI / 2; - mesh1.scale.set(1000, 1000, 1000); - - const mesh2 = new THREE.Mesh(geometry, material2); - mesh2.rotation.x = -Math.PI / 2; - mesh2.scale.set(1000, 1000, 1000); - - scene1.add(mesh1); - scene2.add(mesh2); - - // RENDERER - - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); - renderer.setAnimationLoop(animate); - renderer.autoClear = false; - - renderer.domElement.style.position = 'relative'; - container.appendChild(renderer.domElement); - - // STATS1 - - stats = new Stats(); - container.appendChild(stats.dom); - - document.addEventListener('mousemove', onDocumentMouseMove); -} - -function onDocumentMouseMove(event) { - mouseX = event.clientX - windowHalfX; - mouseY = event.clientY - windowHalfY; -} - -function animate() { - render(); - stats.update(); -} - -function render() { - camera.position.x += (mouseX - camera.position.x) * 0.05; - camera.position.y = THREE.MathUtils.clamp( - camera.position.y + (-(mouseY - 200) - camera.position.y) * 0.05, - 50, - 1000, - ); - - camera.lookAt(scene1.position); - - renderer.clear(); - renderer.setScissorTest(true); - - renderer.setScissor(0, 0, SCREEN_WIDTH / 2 - 2, SCREEN_HEIGHT); - renderer.render(scene1, camera); - - renderer.setScissor(SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 2 - 2, SCREEN_HEIGHT); - renderer.render(scene2, camera); - - renderer.setScissorTest(false); -} diff --git a/examples-testing/examples/webgl_materials_texture_canvas.ts b/examples-testing/examples/webgl_materials_texture_canvas.ts deleted file mode 100644 index d23c68436..000000000 --- a/examples-testing/examples/webgl_materials_texture_canvas.ts +++ /dev/null @@ -1,92 +0,0 @@ -import * as THREE from 'three'; - -let camera, scene, renderer, mesh, material; -const drawStartPos = new THREE.Vector2(); - -init(); -setupCanvasDrawing(); - -function init() { - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 2000); - camera.position.z = 500; - - scene = new THREE.Scene(); - - material = new THREE.MeshBasicMaterial(); - - mesh = new THREE.Mesh(new THREE.BoxGeometry(200, 200, 200), material); - scene.add(mesh); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - window.addEventListener('resize', onWindowResize); -} - -// Sets up the drawing canvas and adds it as the material map - -function setupCanvasDrawing() { - // get canvas and context - - const drawingCanvas = document.getElementById('drawing-canvas'); - const drawingContext = drawingCanvas.getContext('2d'); - - // draw white background - - drawingContext.fillStyle = '#FFFFFF'; - drawingContext.fillRect(0, 0, 128, 128); - - // set canvas as material.map (this could be done to any map, bump, displacement etc.) - - material.map = new THREE.CanvasTexture(drawingCanvas); - - // set the variable to keep track of when to draw - - let paint = false; - - // add canvas event listeners - drawingCanvas.addEventListener('pointerdown', function (e) { - paint = true; - drawStartPos.set(e.offsetX, e.offsetY); - }); - - drawingCanvas.addEventListener('pointermove', function (e) { - if (paint) draw(drawingContext, e.offsetX, e.offsetY); - }); - - drawingCanvas.addEventListener('pointerup', function () { - paint = false; - }); - - drawingCanvas.addEventListener('pointerleave', function () { - paint = false; - }); -} - -function draw(drawContext, x, y) { - drawContext.moveTo(drawStartPos.x, drawStartPos.y); - drawContext.strokeStyle = '#000000'; - drawContext.lineTo(x, y); - drawContext.stroke(); - // reset drawing start position to current position. - drawStartPos.set(x, y); - // need to flag the map as needing updating. - material.map.needsUpdate = true; -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - mesh.rotation.x += 0.01; - mesh.rotation.y += 0.01; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_materials_texture_filters.ts b/examples-testing/examples/webgl_materials_texture_filters.ts deleted file mode 100644 index 77b254684..000000000 --- a/examples-testing/examples/webgl_materials_texture_filters.ts +++ /dev/null @@ -1,165 +0,0 @@ -import * as THREE from 'three'; - -const SCREEN_WIDTH = window.innerWidth; -const SCREEN_HEIGHT = window.innerHeight; - -let container; - -let camera, scene, scene2, renderer; - -let mouseX = 0, - mouseY = 0; - -const windowHalfX = window.innerWidth / 2; -const windowHalfY = window.innerHeight / 2; - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(35, SCREEN_WIDTH / SCREEN_HEIGHT, 1, 5000); - camera.position.z = 1500; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x000000); - scene.fog = new THREE.Fog(0x000000, 1500, 4000); - - scene2 = new THREE.Scene(); - scene2.background = new THREE.Color(0x000000); - scene2.fog = new THREE.Fog(0x000000, 1500, 4000); - - // GROUND - - const imageCanvas = document.createElement('canvas'); - const context = imageCanvas.getContext('2d'); - - imageCanvas.width = imageCanvas.height = 128; - - context.fillStyle = '#444'; - context.fillRect(0, 0, 128, 128); - - context.fillStyle = '#fff'; - context.fillRect(0, 0, 64, 64); - context.fillRect(64, 64, 64, 64); - - const textureCanvas = new THREE.CanvasTexture(imageCanvas); - textureCanvas.colorSpace = THREE.SRGBColorSpace; - textureCanvas.repeat.set(1000, 1000); - textureCanvas.wrapS = THREE.RepeatWrapping; - textureCanvas.wrapT = THREE.RepeatWrapping; - - const textureCanvas2 = textureCanvas.clone(); - textureCanvas2.magFilter = THREE.NearestFilter; - textureCanvas2.minFilter = THREE.NearestFilter; - textureCanvas2.generateMipmaps = false; - - const materialCanvas = new THREE.MeshBasicMaterial({ map: textureCanvas }); - const materialCanvas2 = new THREE.MeshBasicMaterial({ color: 0xffccaa, map: textureCanvas2 }); - - const geometry = new THREE.PlaneGeometry(100, 100); - - const meshCanvas = new THREE.Mesh(geometry, materialCanvas); - meshCanvas.rotation.x = -Math.PI / 2; - meshCanvas.scale.set(1000, 1000, 1000); - - const meshCanvas2 = new THREE.Mesh(geometry, materialCanvas2); - meshCanvas2.rotation.x = -Math.PI / 2; - meshCanvas2.scale.set(1000, 1000, 1000); - - // PAINTING - - const callbackPainting = function () { - const image = texturePainting.image; - - texturePainting2.image = image; - texturePainting2.needsUpdate = true; - - scene.add(meshCanvas); - scene2.add(meshCanvas2); - - const geometry = new THREE.PlaneGeometry(100, 100); - const mesh = new THREE.Mesh(geometry, materialPainting); - const mesh2 = new THREE.Mesh(geometry, materialPainting2); - - addPainting(scene, mesh); - addPainting(scene2, mesh2); - - function addPainting(zscene, zmesh) { - zmesh.scale.x = image.width / 100; - zmesh.scale.y = image.height / 100; - - zscene.add(zmesh); - - const meshFrame = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ color: 0x000000 })); - meshFrame.position.z = -10.0; - meshFrame.scale.x = (1.1 * image.width) / 100; - meshFrame.scale.y = (1.1 * image.height) / 100; - zscene.add(meshFrame); - - const meshShadow = new THREE.Mesh( - geometry, - new THREE.MeshBasicMaterial({ color: 0x000000, opacity: 0.75, transparent: true }), - ); - meshShadow.position.y = (-1.1 * image.height) / 2; - meshShadow.position.z = (-1.1 * image.height) / 2; - meshShadow.rotation.x = -Math.PI / 2; - meshShadow.scale.x = (1.1 * image.width) / 100; - meshShadow.scale.y = (1.1 * image.height) / 100; - zscene.add(meshShadow); - - const floorHeight = (-1.117 * image.height) / 2; - meshCanvas.position.y = meshCanvas2.position.y = floorHeight; - } - }; - - const texturePainting = new THREE.TextureLoader().load( - 'textures/758px-Canestra_di_frutta_(Caravaggio).jpg', - callbackPainting, - ); - const texturePainting2 = new THREE.Texture(); - - const materialPainting = new THREE.MeshBasicMaterial({ color: 0xffffff, map: texturePainting }); - const materialPainting2 = new THREE.MeshBasicMaterial({ color: 0xffccaa, map: texturePainting2 }); - - texturePainting.colorSpace = THREE.SRGBColorSpace; - texturePainting2.colorSpace = THREE.SRGBColorSpace; - texturePainting2.minFilter = texturePainting2.magFilter = THREE.NearestFilter; - texturePainting.minFilter = texturePainting.magFilter = THREE.LinearFilter; - texturePainting.mapping = THREE.UVMapping; - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); - renderer.setAnimationLoop(animate); - renderer.autoClear = false; - - renderer.domElement.style.position = 'relative'; - container.appendChild(renderer.domElement); - - document.addEventListener('mousemove', onDocumentMouseMove); -} - -function onDocumentMouseMove(event) { - mouseX = event.clientX - windowHalfX; - mouseY = event.clientY - windowHalfY; -} - -function animate() { - camera.position.x += (mouseX - camera.position.x) * 0.05; - camera.position.y += (-(mouseY - 200) - camera.position.y) * 0.05; - - camera.lookAt(scene.position); - - renderer.clear(); - renderer.setScissorTest(true); - - renderer.setScissor(0, 0, SCREEN_WIDTH / 2 - 2, SCREEN_HEIGHT); - renderer.render(scene, camera); - - renderer.setScissor(SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 2 - 2, SCREEN_HEIGHT); - renderer.render(scene2, camera); - - renderer.setScissorTest(false); -} diff --git a/examples-testing/examples/webgl_materials_texture_manualmipmap.ts b/examples-testing/examples/webgl_materials_texture_manualmipmap.ts deleted file mode 100644 index 24bd4eb9f..000000000 --- a/examples-testing/examples/webgl_materials_texture_manualmipmap.ts +++ /dev/null @@ -1,175 +0,0 @@ -import * as THREE from 'three'; - -const SCREEN_WIDTH = window.innerWidth; -const SCREEN_HEIGHT = window.innerHeight; - -let container; - -let camera, scene1, scene2, renderer; - -let mouseX = 0, - mouseY = 0; - -const windowHalfX = window.innerWidth / 2; -const windowHalfY = window.innerHeight / 2; - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(35, SCREEN_WIDTH / SCREEN_HEIGHT, 1, 5000); - camera.position.z = 1500; - - scene1 = new THREE.Scene(); - scene1.background = new THREE.Color(0x000000); - scene1.fog = new THREE.Fog(0x000000, 1500, 4000); - - scene2 = new THREE.Scene(); - scene2.background = new THREE.Color(0x000000); - scene2.fog = new THREE.Fog(0x000000, 1500, 4000); - - // GROUND - - function mipmap(size, color) { - const imageCanvas = document.createElement('canvas'); - const context = imageCanvas.getContext('2d'); - - imageCanvas.width = imageCanvas.height = size; - - context.fillStyle = '#444'; - context.fillRect(0, 0, size, size); - - context.fillStyle = color; - context.fillRect(0, 0, size / 2, size / 2); - context.fillRect(size / 2, size / 2, size / 2, size / 2); - return imageCanvas; - } - - const canvas = mipmap(128, '#f00'); - const textureCanvas1 = new THREE.CanvasTexture(canvas); - textureCanvas1.mipmaps[0] = canvas; - textureCanvas1.mipmaps[1] = mipmap(64, '#0f0'); - textureCanvas1.mipmaps[2] = mipmap(32, '#00f'); - textureCanvas1.mipmaps[3] = mipmap(16, '#400'); - textureCanvas1.mipmaps[4] = mipmap(8, '#040'); - textureCanvas1.mipmaps[5] = mipmap(4, '#004'); - textureCanvas1.mipmaps[6] = mipmap(2, '#044'); - textureCanvas1.mipmaps[7] = mipmap(1, '#404'); - textureCanvas1.colorSpace = THREE.SRGBColorSpace; - textureCanvas1.repeat.set(1000, 1000); - textureCanvas1.wrapS = THREE.RepeatWrapping; - textureCanvas1.wrapT = THREE.RepeatWrapping; - - const textureCanvas2 = textureCanvas1.clone(); - textureCanvas2.magFilter = THREE.NearestFilter; - textureCanvas2.minFilter = THREE.NearestMipmapNearestFilter; - - const materialCanvas1 = new THREE.MeshBasicMaterial({ map: textureCanvas1 }); - const materialCanvas2 = new THREE.MeshBasicMaterial({ color: 0xffccaa, map: textureCanvas2 }); - - const geometry = new THREE.PlaneGeometry(100, 100); - - const meshCanvas1 = new THREE.Mesh(geometry, materialCanvas1); - meshCanvas1.rotation.x = -Math.PI / 2; - meshCanvas1.scale.set(1000, 1000, 1000); - - const meshCanvas2 = new THREE.Mesh(geometry, materialCanvas2); - meshCanvas2.rotation.x = -Math.PI / 2; - meshCanvas2.scale.set(1000, 1000, 1000); - - // PAINTING - - const callbackPainting = function () { - const image = texturePainting1.image; - - texturePainting2.image = image; - texturePainting2.needsUpdate = true; - - scene1.add(meshCanvas1); - scene2.add(meshCanvas2); - - const geometry = new THREE.PlaneGeometry(100, 100); - const mesh1 = new THREE.Mesh(geometry, materialPainting1); - const mesh2 = new THREE.Mesh(geometry, materialPainting2); - - addPainting(scene1, mesh1); - addPainting(scene2, mesh2); - - function addPainting(zscene, zmesh) { - zmesh.scale.x = image.width / 100; - zmesh.scale.y = image.height / 100; - - zscene.add(zmesh); - - const meshFrame = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ color: 0x000000 })); - meshFrame.position.z = -10.0; - meshFrame.scale.x = (1.1 * image.width) / 100; - meshFrame.scale.y = (1.1 * image.height) / 100; - zscene.add(meshFrame); - - const meshShadow = new THREE.Mesh( - geometry, - new THREE.MeshBasicMaterial({ color: 0x000000, opacity: 0.75, transparent: true }), - ); - meshShadow.position.y = (-1.1 * image.height) / 2; - meshShadow.position.z = (-1.1 * image.height) / 2; - meshShadow.rotation.x = -Math.PI / 2; - meshShadow.scale.x = (1.1 * image.width) / 100; - meshShadow.scale.y = (1.1 * image.height) / 100; - zscene.add(meshShadow); - - const floorHeight = (-1.117 * image.height) / 2; - meshCanvas1.position.y = meshCanvas2.position.y = floorHeight; - } - }; - - const texturePainting1 = new THREE.TextureLoader().load( - 'textures/758px-Canestra_di_frutta_(Caravaggio).jpg', - callbackPainting, - ); - const texturePainting2 = new THREE.Texture(); - const materialPainting1 = new THREE.MeshBasicMaterial({ color: 0xffffff, map: texturePainting1 }); - const materialPainting2 = new THREE.MeshBasicMaterial({ color: 0xffccaa, map: texturePainting2 }); - - texturePainting1.colorSpace = THREE.SRGBColorSpace; - texturePainting2.colorSpace = THREE.SRGBColorSpace; - texturePainting2.minFilter = texturePainting2.magFilter = THREE.NearestFilter; - texturePainting1.minFilter = texturePainting1.magFilter = THREE.LinearFilter; - texturePainting1.mapping = THREE.UVMapping; - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); - renderer.setAnimationLoop(animate); - renderer.autoClear = false; - - renderer.domElement.style.position = 'relative'; - container.appendChild(renderer.domElement); - - document.addEventListener('mousemove', onDocumentMouseMove); -} - -function onDocumentMouseMove(event) { - mouseX = event.clientX - windowHalfX; - mouseY = event.clientY - windowHalfY; -} - -function animate() { - camera.position.x += (mouseX - camera.position.x) * 0.05; - camera.position.y += (-(mouseY - 200) - camera.position.y) * 0.05; - - camera.lookAt(scene1.position); - - renderer.clear(); - renderer.setScissorTest(true); - - renderer.setScissor(0, 0, SCREEN_WIDTH / 2 - 2, SCREEN_HEIGHT); - renderer.render(scene1, camera); - - renderer.setScissor(SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 2 - 2, SCREEN_HEIGHT); - renderer.render(scene2, camera); - - renderer.setScissorTest(false); -} diff --git a/examples-testing/examples/webgl_materials_texture_partialupdate.ts b/examples-testing/examples/webgl_materials_texture_partialupdate.ts deleted file mode 100644 index cae161939..000000000 --- a/examples-testing/examples/webgl_materials_texture_partialupdate.ts +++ /dev/null @@ -1,104 +0,0 @@ -import * as THREE from 'three'; - -let camera, scene, renderer, timer, dataTexture, diffuseMap; - -let last = 0; -const position = new THREE.Vector2(); -const color = new THREE.Color(); - -init(); - -async function init() { - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 10); - camera.position.z = 2; - - scene = new THREE.Scene(); - - timer = new THREE.Timer(); - timer.connect(document); - - const loader = new THREE.TextureLoader(); - diffuseMap = await loader.loadAsync('textures/floors/FloorsCheckerboard_S_Diffuse.jpg'); - diffuseMap.colorSpace = THREE.SRGBColorSpace; - diffuseMap.minFilter = THREE.LinearFilter; - diffuseMap.generateMipmaps = false; - - const geometry = new THREE.PlaneGeometry(2, 2); - const material = new THREE.MeshBasicMaterial({ map: diffuseMap }); - - const mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - // - - const width = 32; - const height = 32; - - const data = new Uint8Array(width * height * 4); - dataTexture = new THREE.DataTexture(data, width, height); - dataTexture.colorSpace = THREE.SRGBColorSpace; - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - const elapsedTime = timer.getElapsed(); - - if (elapsedTime - last > 0.1) { - last = elapsedTime; - - position.x = 32 * THREE.MathUtils.randInt(1, 16) - 32; - position.y = 32 * THREE.MathUtils.randInt(1, 16) - 32; - - // generate new color data - - updateDataTexture(dataTexture); - - // perform copy from src to dest texture to a random position - - renderer.copyTextureToTexture(dataTexture, diffuseMap, null, position); - } - - renderer.render(scene, camera); -} - -function updateDataTexture(texture) { - const size = texture.image.width * texture.image.height; - const data = texture.image.data; - - // generate a random color and update texture data - - color.setHex(Math.random() * 0xffffff); - - const r = Math.floor(color.r * 255); - const g = Math.floor(color.g * 255); - const b = Math.floor(color.b * 255); - - for (let i = 0; i < size; i++) { - const stride = i * 4; - - data[stride] = r; - data[stride + 1] = g; - data[stride + 2] = b; - data[stride + 3] = 1; - } -} diff --git a/examples-testing/examples/webgl_materials_texture_rotation.ts b/examples-testing/examples/webgl_materials_texture_rotation.ts deleted file mode 100644 index eedc80c6f..000000000 --- a/examples-testing/examples/webgl_materials_texture_rotation.ts +++ /dev/null @@ -1,112 +0,0 @@ -import * as THREE from 'three'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let mesh, renderer, scene, camera; - -let gui; - -const API = { - offsetX: 0, - offsetY: 0, - repeatX: 0.25, - repeatY: 0.25, - rotation: Math.PI / 4, // positive is counterclockwise - centerX: 0.5, - centerY: 0.5, -}; - -init(); - -function init() { - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - document.body.appendChild(renderer.domElement); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(10, 15, 25); - scene.add(camera); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.addEventListener('change', render); - controls.minDistance = 20; - controls.maxDistance = 50; - controls.maxPolarAngle = Math.PI / 2; - - const geometry = new THREE.BoxGeometry(10, 10, 10); - - new THREE.TextureLoader().load('textures/uv_grid_opengl.jpg', function (texture) { - texture.wrapS = texture.wrapT = THREE.RepeatWrapping; - texture.anisotropy = renderer.capabilities.getMaxAnisotropy(); - texture.colorSpace = THREE.SRGBColorSpace; - - //texture.matrixAutoUpdate = false; // default is true; set to false to update texture.matrix manually - - const material = new THREE.MeshBasicMaterial({ map: texture }); - - mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - updateUvTransform(); - - initGui(); - - render(); - }); - - window.addEventListener('resize', onWindowResize); -} - -function render() { - renderer.render(scene, camera); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - render(); -} - -function updateUvTransform() { - const texture = mesh.material.map; - - if (texture.matrixAutoUpdate === true) { - texture.offset.set(API.offsetX, API.offsetY); - texture.repeat.set(API.repeatX, API.repeatY); - texture.center.set(API.centerX, API.centerY); - texture.rotation = API.rotation; // rotation is around center - } else { - // setting the matrix uv transform directly - texture.matrix.setUvTransform( - API.offsetX, - API.offsetY, - API.repeatX, - API.repeatY, - API.rotation, - API.centerX, - API.centerY, - ); - } - - render(); -} - -function initGui() { - gui = new GUI(); - - gui.add(API, 'offsetX', 0.0, 1.0).name('offset.x').onChange(updateUvTransform); - gui.add(API, 'offsetY', 0.0, 1.0).name('offset.y').onChange(updateUvTransform); - gui.add(API, 'repeatX', 0.25, 2.0).name('repeat.x').onChange(updateUvTransform); - gui.add(API, 'repeatY', 0.25, 2.0).name('repeat.y').onChange(updateUvTransform); - gui.add(API, 'rotation', -2.0, 2.0).name('rotation').onChange(updateUvTransform); - gui.add(API, 'centerX', 0.0, 1.0).name('center.x').onChange(updateUvTransform); - gui.add(API, 'centerY', 0.0, 1.0).name('center.y').onChange(updateUvTransform); -} diff --git a/examples-testing/examples/webgl_materials_toon.ts b/examples-testing/examples/webgl_materials_toon.ts deleted file mode 100644 index 46c6a7e93..000000000 --- a/examples-testing/examples/webgl_materials_toon.ts +++ /dev/null @@ -1,152 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { OutlineEffect } from 'three/addons/effects/OutlineEffect.js'; -import { FontLoader } from 'three/addons/loaders/FontLoader.js'; -import { TextGeometry } from 'three/addons/geometries/TextGeometry.js'; - -let container, stats; - -let camera, scene, renderer, effect; -let particleLight; - -const loader = new FontLoader(); -loader.load('fonts/gentilis_regular.typeface.json', function (font) { - init(font); -}); - -function init(font) { - container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 2500); - camera.position.set(0.0, 400, 400 * 3.5); - - // - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x444488); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - // Materials - - const cubeWidth = 400; - const numberOfSpheresPerSide = 5; - const sphereRadius = (cubeWidth / numberOfSpheresPerSide) * 0.8 * 0.5; - const stepSize = 1.0 / numberOfSpheresPerSide; - - const geometry = new THREE.SphereGeometry(sphereRadius, 32, 16); - - for (let alpha = 0, alphaIndex = 0; alpha <= 1.0; alpha += stepSize, alphaIndex++) { - const colors = new Uint8Array(alphaIndex + 2); - - for (let c = 0; c <= colors.length; c++) { - colors[c] = (c / colors.length) * 256; - } - - const gradientMap = new THREE.DataTexture(colors, colors.length, 1, THREE.RedFormat); - gradientMap.needsUpdate = true; - - for (let beta = 0; beta <= 1.0; beta += stepSize) { - for (let gamma = 0; gamma <= 1.0; gamma += stepSize) { - // basic monochromatic energy preservation - const diffuseColor = new THREE.Color() - .setHSL(alpha, 0.5, gamma * 0.5 + 0.1) - .multiplyScalar(1 - beta * 0.2); - - const material = new THREE.MeshToonMaterial({ - color: diffuseColor, - gradientMap: gradientMap, - }); - - const mesh = new THREE.Mesh(geometry, material); - - mesh.position.x = alpha * 400 - 200; - mesh.position.y = beta * 400 - 200; - mesh.position.z = gamma * 400 - 200; - - scene.add(mesh); - } - } - } - - function addLabel(name, location) { - const textGeo = new TextGeometry(name, { - font: font, - - size: 20, - depth: 1, - curveSegments: 1, - }); - - const textMaterial = new THREE.MeshBasicMaterial(); - const textMesh = new THREE.Mesh(textGeo, textMaterial); - textMesh.position.copy(location); - scene.add(textMesh); - } - - addLabel('-gradientMap', new THREE.Vector3(-350, 0, 0)); - addLabel('+gradientMap', new THREE.Vector3(350, 0, 0)); - - addLabel('-diffuse', new THREE.Vector3(0, 0, -300)); - addLabel('+diffuse', new THREE.Vector3(0, 0, 300)); - - particleLight = new THREE.Mesh(new THREE.SphereGeometry(4, 8, 8), new THREE.MeshBasicMaterial({ color: 0xffffff })); - scene.add(particleLight); - - // Lights - - scene.add(new THREE.AmbientLight(0xc1c1c1, 3)); - - const pointLight = new THREE.PointLight(0xffffff, 2, 800, 0); - particleLight.add(pointLight); - - // - - effect = new OutlineEffect(renderer); - - // - - stats = new Stats(); - container.appendChild(stats.dom); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 200; - controls.maxDistance = 2000; - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - stats.begin(); - render(); - stats.end(); -} - -function render() { - const timer = Date.now() * 0.00025; - - particleLight.position.x = Math.sin(timer * 7) * 300; - particleLight.position.y = Math.cos(timer * 5) * 400; - particleLight.position.z = Math.cos(timer * 3) * 300; - - effect.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_materials_video.ts b/examples-testing/examples/webgl_materials_video.ts deleted file mode 100644 index 4f0d26a18..000000000 --- a/examples-testing/examples/webgl_materials_video.ts +++ /dev/null @@ -1,208 +0,0 @@ -import * as THREE from 'three'; - -import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; -import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; -import { BloomPass } from 'three/addons/postprocessing/BloomPass.js'; -import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; - -let container; - -let camera, scene, renderer; - -let video, texture, material, mesh; - -let composer; - -let mouseX = 0; -let mouseY = 0; - -let windowHalfX = window.innerWidth / 2; -let windowHalfY = window.innerHeight / 2; - -let cube_count; - -const meshes = [], - materials = [], - xgrid = 20, - ygrid = 10; - -const startButton = document.getElementById('startButton'); -startButton.addEventListener('click', function () { - init(); -}); - -function init() { - const overlay = document.getElementById('overlay'); - overlay.remove(); - - container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 10000); - camera.position.z = 500; - - scene = new THREE.Scene(); - - const light = new THREE.DirectionalLight(0xffffff, 3); - light.position.set(0.5, 1, 1).normalize(); - scene.add(light); - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - video = document.getElementById('video'); - video.play(); - video.addEventListener('play', function () { - this.currentTime = 3; - }); - - texture = new THREE.VideoTexture(video); - texture.colorSpace = THREE.SRGBColorSpace; - - // - - let i, j, ox, oy, geometry; - - const ux = 1 / xgrid; - const uy = 1 / ygrid; - - const xsize = 480 / xgrid; - const ysize = 204 / ygrid; - - const parameters = { color: 0xffffff, map: texture }; - - cube_count = 0; - - for (i = 0; i < xgrid; i++) { - for (j = 0; j < ygrid; j++) { - ox = i; - oy = j; - - geometry = new THREE.BoxGeometry(xsize, ysize, xsize); - - change_uvs(geometry, ux, uy, ox, oy); - - materials[cube_count] = new THREE.MeshLambertMaterial(parameters); - - material = materials[cube_count]; - - material.hue = i / xgrid; - material.saturation = 1 - j / ygrid; - - material.color.setHSL(material.hue, material.saturation, 0.5); - - mesh = new THREE.Mesh(geometry, material); - - mesh.position.x = (i - xgrid / 2) * xsize; - mesh.position.y = (j - ygrid / 2) * ysize; - mesh.position.z = 0; - - mesh.scale.x = mesh.scale.y = mesh.scale.z = 1; - - scene.add(mesh); - - mesh.dx = 0.001 * (0.5 - Math.random()); - mesh.dy = 0.001 * (0.5 - Math.random()); - - meshes[cube_count] = mesh; - - cube_count += 1; - } - } - - renderer.autoClear = false; - - document.addEventListener('mousemove', onDocumentMouseMove); - - // postprocessing - - const renderPass = new RenderPass(scene, camera); - const bloomPass = new BloomPass(1.3); - const outputPass = new OutputPass(); - - composer = new EffectComposer(renderer); - - composer.addPass(renderPass); - composer.addPass(bloomPass); - composer.addPass(outputPass); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - windowHalfX = window.innerWidth / 2; - windowHalfY = window.innerHeight / 2; - - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - composer.setSize(window.innerWidth, window.innerHeight); -} - -function change_uvs(geometry, unitx, unity, offsetx, offsety) { - const uvs = geometry.attributes.uv.array; - - for (let i = 0; i < uvs.length; i += 2) { - uvs[i] = (uvs[i] + offsetx) * unitx; - uvs[i + 1] = (uvs[i + 1] + offsety) * unity; - } -} - -function onDocumentMouseMove(event) { - mouseX = event.clientX - windowHalfX; - mouseY = (event.clientY - windowHalfY) * 0.3; -} - -// - -let h, - counter = 1; - -function animate() { - const time = Date.now() * 0.00005; - - camera.position.x += (mouseX - camera.position.x) * 0.05; - camera.position.y += (-mouseY - camera.position.y) * 0.05; - - camera.lookAt(scene.position); - - for (let i = 0; i < cube_count; i++) { - material = materials[i]; - - h = ((360 * (material.hue + time)) % 360) / 360; - material.color.setHSL(h, material.saturation, 0.5); - } - - if (counter % 1000 > 200) { - for (let i = 0; i < cube_count; i++) { - mesh = meshes[i]; - - mesh.rotation.x += 10 * mesh.dx; - mesh.rotation.y += 10 * mesh.dy; - - mesh.position.x -= 150 * mesh.dx; - mesh.position.y += 150 * mesh.dy; - mesh.position.z += 300 * mesh.dx; - } - } - - if (counter % 1000 === 0) { - for (let i = 0; i < cube_count; i++) { - mesh = meshes[i]; - - mesh.dx *= -1; - mesh.dy *= -1; - } - } - - counter++; - - renderer.clear(); - composer.render(); -} diff --git a/examples-testing/examples/webgl_materials_video_webcam.ts b/examples-testing/examples/webgl_materials_video_webcam.ts deleted file mode 100644 index cf6f8d50c..000000000 --- a/examples-testing/examples/webgl_materials_video_webcam.ts +++ /dev/null @@ -1,79 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let camera, scene, renderer, video; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.z = 0.01; - - scene = new THREE.Scene(); - - video = document.getElementById('video'); - - const texture = new THREE.VideoTexture(video); - texture.colorSpace = THREE.SRGBColorSpace; - - const geometry = new THREE.PlaneGeometry(16, 9); - geometry.scale(0.5, 0.5, 0.5); - const material = new THREE.MeshBasicMaterial({ map: texture }); - - const count = 128; - const radius = 32; - - for (let i = 1, l = count; i <= l; i++) { - const phi = Math.acos(-1 + (2 * i) / l); - const theta = Math.sqrt(l * Math.PI) * phi; - - const mesh = new THREE.Mesh(geometry, material); - mesh.position.setFromSphericalCoords(radius, phi, theta); - mesh.lookAt(camera.position); - scene.add(mesh); - } - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.enableZoom = false; - controls.enablePan = false; - - window.addEventListener('resize', onWindowResize); - - // - - if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { - const constraints = { video: { width: 1280, height: 720, facingMode: 'user' } }; - - navigator.mediaDevices - .getUserMedia(constraints) - .then(function (stream) { - // apply the stream to the video element used in the texture - - video.srcObject = stream; - video.play(); - }) - .catch(function (error) { - console.error('Unable to access the camera/webcam.', error); - }); - } else { - console.error('MediaDevices interface not available.'); - } -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_materials_wireframe.ts b/examples-testing/examples/webgl_materials_wireframe.ts deleted file mode 100644 index 8adbd71d6..000000000 --- a/examples-testing/examples/webgl_materials_wireframe.ts +++ /dev/null @@ -1,107 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -const API = { - thickness: 1, -}; - -let renderer, scene, camera, mesh2; - -init(); - -function init() { - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - document.body.appendChild(renderer.domElement); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 500); - camera.position.z = 200; - - const controls = new OrbitControls(camera, renderer.domElement); - controls.addEventListener('change', render); // use if there is no animation loop - controls.enablePan = false; - controls.enableZoom = false; - - new THREE.BufferGeometryLoader().load('models/json/WaltHeadLo_buffergeometry.json', function (geometry) { - geometry.deleteAttribute('normal'); - geometry.deleteAttribute('uv'); - - setupAttributes(geometry); - - // left - - const material1 = new THREE.MeshBasicMaterial({ - color: 0xe0e0ff, - wireframe: true, - }); - - const mesh1 = new THREE.Mesh(geometry, material1); - mesh1.position.set(-40, 0, 0); - - scene.add(mesh1); - - // right - - const material2 = new THREE.ShaderMaterial({ - uniforms: { thickness: { value: API.thickness } }, - vertexShader: document.getElementById('vertexShader').textContent, - fragmentShader: document.getElementById('fragmentShader').textContent, - side: THREE.DoubleSide, - alphaToCoverage: true, // only works when WebGLRenderer's "antialias" is set to "true" - }); - - mesh2 = new THREE.Mesh(geometry, material2); - mesh2.position.set(40, 0, 0); - - scene.add(mesh2); - - // - - render(); - }); - - // - - const gui = new GUI(); - - gui.add(API, 'thickness', 0, 4).onChange(function () { - mesh2.material.uniforms.thickness.value = API.thickness; - render(); - }); - - gui.open(); - - // - - window.addEventListener('resize', onWindowResize); -} - -function setupAttributes(geometry) { - const vectors = [new THREE.Vector3(1, 0, 0), new THREE.Vector3(0, 1, 0), new THREE.Vector3(0, 0, 1)]; - - const position = geometry.attributes.position; - const centers = new Float32Array(position.count * 3); - - for (let i = 0, l = position.count; i < l; i++) { - vectors[i % 3].toArray(centers, i * 3); - } - - geometry.setAttribute('center', new THREE.BufferAttribute(centers, 3)); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_math_obb.ts b/examples-testing/examples/webgl_math_obb.ts deleted file mode 100644 index 71cb5a916..000000000 --- a/examples-testing/examples/webgl_math_obb.ts +++ /dev/null @@ -1,192 +0,0 @@ -import * as THREE from 'three'; - -import { OBB } from 'three/addons/math/OBB.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import Stats from 'three/addons/libs/stats.module.js'; - -let camera, scene, renderer, timer, controls, stats, raycaster, hitbox; - -const objects = [], - mouse = new THREE.Vector2(); - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(0, 0, 75); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xffffff); - - timer = new THREE.Timer(); - timer.connect(document); - - raycaster = new THREE.Raycaster(); - - const hemiLight = new THREE.HemisphereLight(0xffffff, 0x222222, 4); - hemiLight.position.set(1, 1, 1); - scene.add(hemiLight); - - const size = new THREE.Vector3(10, 5, 6); - const geometry = new THREE.BoxGeometry(size.x, size.y, size.z); - - // setup OBB on geometry level (doing this manually for now) - - geometry.userData.obb = new OBB(); - geometry.userData.obb.halfSize.copy(size).multiplyScalar(0.5); - - for (let i = 0; i < 100; i++) { - const object = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({ color: 0x00ff00 })); - object.matrixAutoUpdate = false; - - object.position.x = Math.random() * 80 - 40; - object.position.y = Math.random() * 80 - 40; - object.position.z = Math.random() * 80 - 40; - - object.rotation.x = Math.random() * 2 * Math.PI; - object.rotation.y = Math.random() * 2 * Math.PI; - object.rotation.z = Math.random() * 2 * Math.PI; - - object.scale.x = Math.random() + 0.5; - object.scale.y = Math.random() + 0.5; - object.scale.z = Math.random() + 0.5; - - scene.add(object); - - // bounding volume on object level (this will reflect the current world transform) - - object.userData.obb = new OBB(); - - objects.push(object); - } - - // - - hitbox = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ color: 0x000000, wireframe: true })); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - - // - - stats = new Stats(); - document.body.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); - - document.addEventListener('click', onClick); -} - -function onClick(event) { - event.preventDefault(); - - mouse.x = (event.clientX / window.innerWidth) * 2 - 1; - mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; - - raycaster.setFromCamera(mouse, camera); - - const intersectionPoint = new THREE.Vector3(); - const intersections = []; - - for (let i = 0, il = objects.length; i < il; i++) { - const object = objects[i]; - const obb = object.userData.obb; - - const ray = raycaster.ray; - - if (obb.intersectRay(ray, intersectionPoint) !== null) { - const distance = ray.origin.distanceTo(intersectionPoint); - intersections.push({ distance: distance, object: object }); - } - } - - if (intersections.length > 0) { - // determine closest intersection and highlight the respective 3D object - - intersections.sort(sortIntersections); - - intersections[0].object.add(hitbox); - } else { - const parent = hitbox.parent; - - if (parent) parent.remove(hitbox); - } -} - -function sortIntersections(a, b) { - return a.distance - b.distance; -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - timer.update(); - - controls.update(); - - // transform cubes - - const delta = timer.getDelta(); - - for (let i = 0, il = objects.length; i < il; i++) { - const object = objects[i]; - - object.rotation.x += delta * Math.PI * 0.2; - object.rotation.y += delta * Math.PI * 0.1; - - object.updateMatrix(); - object.updateMatrixWorld(); - - // update OBB - - object.userData.obb.copy(object.geometry.userData.obb); - object.userData.obb.applyMatrix4(object.matrixWorld); - - // reset - - object.material.color.setHex(0x00ff00); - } - - // collision detection - - for (let i = 0, il = objects.length; i < il; i++) { - const object = objects[i]; - const obb = object.userData.obb; - - for (let j = i + 1, jl = objects.length; j < jl; j++) { - const objectToTest = objects[j]; - const obbToTest = objectToTest.userData.obb; - - // now perform intersection test - - if (obb.intersectsOBB(obbToTest) === true) { - object.material.color.setHex(0xff0000); - objectToTest.material.color.setHex(0xff0000); - } - } - } - - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/webgl_math_orientation_transform.ts b/examples-testing/examples/webgl_math_orientation_transform.ts deleted file mode 100644 index 1d66997c4..000000000 --- a/examples-testing/examples/webgl_math_orientation_transform.ts +++ /dev/null @@ -1,120 +0,0 @@ -import * as THREE from 'three'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let camera, scene, renderer, mesh, target; - -const spherical = new THREE.Spherical(); -const rotationMatrix = new THREE.Matrix4(); -const targetQuaternion = new THREE.Quaternion(); -const timer = new THREE.Timer(); -timer.connect(document); -const speed = Math.PI / 2; - -const params = { - useLookAt: false, -}; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 10); - camera.position.z = 5; - - scene = new THREE.Scene(); - - const geometry = new THREE.ConeGeometry(0.1, 0.5, 8); - geometry.rotateX(Math.PI * 0.5); - const material = new THREE.MeshNormalMaterial(); - - mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - // - - const targetGeometry = new THREE.SphereGeometry(0.05); - const targetMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 }); - target = new THREE.Mesh(targetGeometry, targetMaterial); - scene.add(target); - - // - - const sphereGeometry = new THREE.SphereGeometry(2, 32, 32); - const sphereMaterial = new THREE.MeshBasicMaterial({ - color: 0xcccccc, - wireframe: true, - transparent: true, - opacity: 0.3, - }); - const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial); - scene.add(sphere); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // - - window.addEventListener('resize', onWindowResize); - - // - - const gui = new GUI(); - - gui.add(params, 'useLookAt'); - gui.open(); - - // - - generateTarget(); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - const delta = timer.getDelta(); - - if (mesh.quaternion.equals(targetQuaternion) === false) { - if (params.useLookAt === true) { - // using lookAt() will make the mesh instantly look at the target - - mesh.lookAt(target.position); - } else { - // using rotateTowards() will gradually rotate the mesh towards the target - // the "speed" variable represents the rotation speed in radians per seconds - - const step = speed * delta; - mesh.quaternion.rotateTowards(targetQuaternion, step); - } - } - - renderer.render(scene, camera); -} - -function generateTarget() { - // generate a random point on a sphere - - spherical.theta = Math.random() * Math.PI * 2; - spherical.phi = Math.acos(2 * Math.random() - 1); - spherical.radius = 2; - - target.position.setFromSpherical(spherical); - - // compute target rotation - - rotationMatrix.lookAt(target.position, mesh.position, mesh.up); - targetQuaternion.setFromRotationMatrix(rotationMatrix); - - setTimeout(generateTarget, 2000); -} diff --git a/examples-testing/examples/webgl_mesh_batch.ts b/examples-testing/examples/webgl_mesh_batch.ts deleted file mode 100644 index e238e50a3..000000000 --- a/examples-testing/examples/webgl_mesh_batch.ts +++ /dev/null @@ -1,347 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { radixSort } from 'three/addons/utils/SortUtils.js'; - -let stats, gui, guiStatsEl; -let camera, controls, scene, renderer; -let geometries, mesh; -const ids = []; -const matrix = new THREE.Matrix4(); - -// - -const position = new THREE.Vector3(); -const rotation = new THREE.Euler(); -const quaternion = new THREE.Quaternion(); -const scale = new THREE.Vector3(); - -// - -const MAX_GEOMETRY_COUNT = 20000; - -const Method = { - BATCHED: 'BATCHED', - NAIVE: 'NAIVE', -}; - -const api = { - method: Method.BATCHED, - count: 256, - dynamic: 16, - - transparent: true, - sortObjects: true, - perObjectFrustumCulled: true, - useCustomSort: true, -}; - -init(); -initGeometries(); -initMesh(); - -// - -function randomizeMatrix(matrix) { - position.x = Math.random() * 40 - 20; - position.y = Math.random() * 40 - 20; - position.z = Math.random() * 40 - 20; - - rotation.x = Math.random() * 2 * Math.PI; - rotation.y = Math.random() * 2 * Math.PI; - rotation.z = Math.random() * 2 * Math.PI; - - quaternion.setFromEuler(rotation); - - scale.x = scale.y = scale.z = 0.5 + Math.random() * 0.5; - - return matrix.compose(position, quaternion, scale); -} - -function randomizeRotationSpeed(rotation) { - rotation.x = Math.random() * 0.01; - rotation.y = Math.random() * 0.01; - rotation.z = Math.random() * 0.01; - return rotation; -} - -function randomizeColor(color) { - return color.setHSL(Math.random() * 0.5, 0.6, 0.5); -} - -function randomizeAlpha() { - // make ~20% of all objects transparent - return Math.random() > 0.8 ? 0.5 : 1.0; -} - -function initGeometries() { - const cone = new THREE.ConeGeometry(1.0, 2.0); - const box = new THREE.BoxGeometry(2.0, 2.0, 2.0); - const sphere = new THREE.SphereGeometry(1.0, 16, 8); - - geometries = [cone, box, sphere]; - - for (const geometry of geometries) { - // add vertex colors for testing - const count = geometry.getAttribute('position').count; - const attribute = new THREE.BufferAttribute(new Float32Array(count * 3), 3); - geometry.setAttribute('color', attribute); - - for (let i = 0, l = attribute.array.length; i < l; ++i) { - attribute.array[i] = 1.0; - } - } -} - -function createMaterial() { - return new THREE.MeshPhongMaterial({ - vertexColors: true, - transparent: api.transparent, - depthWrite: !api.transparent, - }); -} - -function cleanup() { - if (mesh) { - mesh.traverse(node => { - if (node instanceof THREE.Mesh) { - node.material.dispose(); - } - }); - - mesh.parent.remove(mesh); - - if (mesh.dispose) { - mesh.dispose(); - } - } -} - -function initMesh() { - cleanup(); - - if (api.method === Method.BATCHED) { - initBatchedMesh(); - } else { - initRegularMesh(); - } -} - -function initRegularMesh() { - mesh = new THREE.Group(); - - for (let i = 0; i < api.count; i++) { - const material = createMaterial(); - randomizeColor(material.color); - material.opacity = randomizeAlpha(); - - const child = new THREE.Mesh(geometries[i % geometries.length], material); - randomizeMatrix(child.matrix); - child.matrix.decompose(child.position, child.quaternion, child.scale); - child.userData.rotationSpeed = randomizeRotationSpeed(new THREE.Euler()); - mesh.add(child); - } - - scene.add(mesh); -} - -function initBatchedMesh() { - const geometryCount = api.count; - const vertexCount = geometries.length * 512; - const indexCount = geometries.length * 1024; - - const euler = new THREE.Euler(); - const matrix = new THREE.Matrix4(); - const color = new THREE.Color(); - const colorWithAlpha = new THREE.Vector4(); - mesh = new THREE.BatchedMesh(geometryCount, vertexCount, indexCount, createMaterial()); - mesh.userData.rotationSpeeds = []; - - // disable full-object frustum culling since all of the objects can be dynamic. - mesh.frustumCulled = false; - - ids.length = 0; - - const geometryIds = [ - mesh.addGeometry(geometries[0]), - mesh.addGeometry(geometries[1]), - mesh.addGeometry(geometries[2]), - ]; - - for (let i = 0; i < api.count; i++) { - randomizeColor(color); - colorWithAlpha.set(color.r, color.g, color.b, randomizeAlpha()); - - const id = mesh.addInstance(geometryIds[i % geometryIds.length]); - mesh.setMatrixAt(id, randomizeMatrix(matrix)); - mesh.setColorAt(id, colorWithAlpha); - - const rotationMatrix = new THREE.Matrix4(); - rotationMatrix.makeRotationFromEuler(randomizeRotationSpeed(euler)); - mesh.userData.rotationSpeeds.push(rotationMatrix); - - ids.push(id); - } - - scene.add(mesh); -} - -function init() { - const width = window.innerWidth; - const height = window.innerHeight; - - // camera - - camera = new THREE.PerspectiveCamera(70, width / height, 1, 100); - camera.position.z = 30; - - // renderer - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(width, height); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // scene - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xffffff); - - // controls - - controls = new OrbitControls(camera, renderer.domElement); - controls.autoRotate = true; - controls.autoRotateSpeed = 1.0; - - // light - - const ambientLight = new THREE.AmbientLight(0xffffff, 2); - const directionalLight = new THREE.DirectionalLight(0xffffff, 2); - directionalLight.position.set(1, 1, 1); - scene.add(directionalLight, ambientLight); - - // stats - - stats = new Stats(); - document.body.appendChild(stats.dom); - - // gui - - gui = new GUI(); - gui.add(api, 'count', 1, MAX_GEOMETRY_COUNT).step(1).onChange(initMesh); - gui.add(api, 'dynamic', 0, MAX_GEOMETRY_COUNT).step(1); - gui.add(api, 'method', Method).onChange(initMesh); - - gui.add(api, 'transparent').onChange(v => - mesh.traverse(node => { - if (node instanceof THREE.Mesh) { - const material = node.material; - material.transparent = v; - material.depthWrite = !v; - material.needsUpdate = true; - } - }), - ); - - gui.add(api, 'sortObjects'); - gui.add(api, 'perObjectFrustumCulled'); - gui.add(api, 'useCustomSort'); - - guiStatsEl = document.createElement('li'); - guiStatsEl.classList.add('gui-stats'); - - // listeners - - window.addEventListener('resize', onWindowResize); -} - -// - -function sortFunction(list) { - // initialize options - this._options = this._options || { - get: el => el.z, - aux: new Array(this.maxInstanceCount), - }; - - const options = this._options; - options.reversed = this.material.transparent; - - let minZ = Infinity; - let maxZ = -Infinity; - for (let i = 0, l = list.length; i < l; i++) { - const z = list[i].z; - if (z > maxZ) maxZ = z; - if (z < minZ) minZ = z; - } - - // convert depth to unsigned 32 bit range - const depthDelta = maxZ - minZ; - const factor = (2 ** 32 - 1) / depthDelta; // UINT32_MAX / z range - for (let i = 0, l = list.length; i < l; i++) { - list[i].z -= minZ; - list[i].z *= factor; - } - - // perform a fast-sort using the hybrid radix sort function - radixSort(list, options); -} - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); -} - -function animate() { - animateMeshes(); - - controls.update(); - stats.update(); - - render(); -} - -function animateMeshes() { - const loopNum = Math.min(api.count, api.dynamic); - - if (api.method === Method.BATCHED) { - for (let i = 0; i < loopNum; i++) { - const rotationMatrix = mesh.userData.rotationSpeeds[i]; - const id = ids[i]; - - mesh.getMatrixAt(id, matrix); - matrix.multiply(rotationMatrix); - mesh.setMatrixAt(id, matrix); - } - } else { - for (let i = 0; i < loopNum; i++) { - const child = mesh.children[i]; - const rotationSpeed = child.userData.rotationSpeed; - - child.rotation.set( - child.rotation.x + rotationSpeed.x, - child.rotation.y + rotationSpeed.y, - child.rotation.z + rotationSpeed.z, - ); - } - } -} - -function render() { - if (mesh.isBatchedMesh) { - mesh.sortObjects = api.sortObjects; - mesh.perObjectFrustumCulled = api.perObjectFrustumCulled; - mesh.setCustomSort(api.useCustomSort ? sortFunction : null); - } - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_mirror.ts b/examples-testing/examples/webgl_mirror.ts deleted file mode 100644 index 4f1f17f7d..000000000 --- a/examples-testing/examples/webgl_mirror.ts +++ /dev/null @@ -1,193 +0,0 @@ -import * as THREE from 'three'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { Reflector } from 'three/addons/objects/Reflector.js'; - -let camera, scene, renderer; - -let cameraControls; - -let sphereGroup, smallSphere; - -let groundMirror, verticalMirror; - -let resolutionScale = 1; // render target scale factor in [ 0, 1 ] - -const size = new THREE.Vector2(); - -init(); - -function init() { - const container = document.getElementById('container'); - - // renderer - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - // scene - scene = new THREE.Scene(); - - // camera - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 500); - camera.position.set(0, 75, 160); - - cameraControls = new OrbitControls(camera, renderer.domElement); - cameraControls.target.set(0, 40, 0); - cameraControls.maxDistance = 400; - cameraControls.minDistance = 10; - cameraControls.update(); - - // - - const planeGeo = new THREE.PlaneGeometry(100.1, 100.1); - - // reflectors/mirrors - - let geometry, material; - - renderer.getDrawingBufferSize(size); - size.multiplyScalar(resolutionScale).round(); - - geometry = new THREE.CircleGeometry(40, 64); - groundMirror = new Reflector(geometry, { - clipBias: 0.003, - textureWidth: size.width, - textureHeight: size.height, - color: 0xb5b5b5, - }); - groundMirror.position.y = 0.5; - groundMirror.rotateX(-Math.PI / 2); - scene.add(groundMirror); - - geometry = new THREE.PlaneGeometry(100, 100); - verticalMirror = new Reflector(geometry, { - clipBias: 0.003, - textureWidth: size.width, - textureHeight: size.height, - color: 0xc1cbcb, - }); - verticalMirror.position.y = 50; - verticalMirror.position.z = -50; - scene.add(verticalMirror); - - sphereGroup = new THREE.Object3D(); - scene.add(sphereGroup); - - geometry = new THREE.CylinderGeometry(0.1, 15 * Math.cos((Math.PI / 180) * 30), 0.1, 24, 1); - material = new THREE.MeshPhongMaterial({ color: 0xffffff, emissive: 0x8d8d8d }); - const sphereCap = new THREE.Mesh(geometry, material); - sphereCap.position.y = -15 * Math.sin((Math.PI / 180) * 30) - 0.05; - sphereCap.rotateX(-Math.PI); - - geometry = new THREE.SphereGeometry(15, 24, 24, Math.PI / 2, Math.PI * 2, 0, (Math.PI / 180) * 120); - const halfSphere = new THREE.Mesh(geometry, material); - halfSphere.add(sphereCap); - halfSphere.rotateX((-Math.PI / 180) * 135); - halfSphere.rotateZ((-Math.PI / 180) * 20); - halfSphere.position.y = 7.5 + 15 * Math.sin((Math.PI / 180) * 30); - - sphereGroup.add(halfSphere); - - geometry = new THREE.IcosahedronGeometry(5, 0); - material = new THREE.MeshPhongMaterial({ color: 0xffffff, emissive: 0x7b7b7b, flatShading: true }); - smallSphere = new THREE.Mesh(geometry, material); - scene.add(smallSphere); - - // walls - const planeTop = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xffffff })); - planeTop.position.y = 100; - planeTop.rotateX(Math.PI / 2); - scene.add(planeTop); - - const planeBottom = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xffffff })); - planeBottom.rotateX(-Math.PI / 2); - scene.add(planeBottom); - - const planeFront = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0x7f7fff })); - planeFront.position.z = 50; - planeFront.position.y = 50; - planeFront.rotateY(Math.PI); - scene.add(planeFront); - - const planeRight = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0x00ff00 })); - planeRight.position.x = 50; - planeRight.position.y = 50; - planeRight.rotateY(-Math.PI / 2); - scene.add(planeRight); - - const planeLeft = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xff0000 })); - planeLeft.position.x = -50; - planeLeft.position.y = 50; - planeLeft.rotateY(Math.PI / 2); - scene.add(planeLeft); - - // lights - const mainLight = new THREE.PointLight(0xe7e7e7, 2.5, 250, 0); - mainLight.position.y = 60; - scene.add(mainLight); - - const greenLight = new THREE.PointLight(0x00ff00, 0.5, 1000, 0); - greenLight.position.set(550, 50, 0); - scene.add(greenLight); - - const redLight = new THREE.PointLight(0xff0000, 0.5, 1000, 0); - redLight.position.set(-550, 50, 0); - scene.add(redLight); - - const blueLight = new THREE.PointLight(0xbbbbfe, 0.5, 1000, 0); - blueLight.position.set(0, 50, 550); - scene.add(blueLight); - - // GUI - - const params = { - resolution: resolutionScale, - }; - - const gui = new GUI(); - - const folder = gui.addFolder('Mirrors'); - - folder.add(params, 'resolution', 0.2, 1, 0.1).onChange(function (val) { - resolutionScale = val; - onWindowResize(); - }); - - folder.open(); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - renderer.getDrawingBufferSize(size); - size.multiplyScalar(resolutionScale).round(); - - groundMirror.getRenderTarget().setSize(size.width, size.height); - verticalMirror.getRenderTarget().setSize(size.width, size.height); -} - -function animate() { - const timer = Date.now() * 0.01; - - sphereGroup.rotation.y -= 0.002; - - smallSphere.position.set( - Math.cos(timer * 0.1) * 30, - Math.abs(Math.cos(timer * 0.2)) * 20 + 5, - Math.sin(timer * 0.1) * 30, - ); - smallSphere.rotation.y = Math.PI / 2 - timer * 0.1; - smallSphere.rotation.z = timer * 0.8; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_modifier_edgesplit.ts b/examples-testing/examples/webgl_modifier_edgesplit.ts deleted file mode 100644 index 4725eff62..000000000 --- a/examples-testing/examples/webgl_modifier_edgesplit.ts +++ /dev/null @@ -1,136 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { OBJLoader } from 'three/addons/loaders/OBJLoader.js'; -import { EdgeSplitModifier } from 'three/addons/modifiers/EdgeSplitModifier.js'; -import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let renderer, scene, camera; -let modifier, mesh, baseGeometry; -let map; - -const params = { - smoothShading: true, - edgeSplit: true, - cutOffAngle: 20, - showMap: false, - tryKeepNormals: true, -}; - -init(); - -function init() { - const info = document.createElement('div'); - info.style.position = 'absolute'; - info.style.top = '10px'; - info.style.width = '100%'; - info.style.textAlign = 'center'; - info.innerHTML = 'three.js - Edge Split modifier'; - document.body.appendChild(info); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - document.body.appendChild(renderer.domElement); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.addEventListener('change', render); // use if there is no animation loop - controls.enableDamping = true; - controls.dampingFactor = 0.25; - controls.rotateSpeed = 0.35; - controls.minZoom = 1; - camera.position.set(0, 0, 4); - - scene.add(new THREE.HemisphereLight(0xffffff, 0x444444, 3)); - - new OBJLoader().load('./models/obj/cerberus/Cerberus.obj', function (group) { - const cerberus = group.children[0]; - const modelGeometry = cerberus.geometry; - - modifier = new EdgeSplitModifier(); - baseGeometry = BufferGeometryUtils.mergeVertices(modelGeometry); - - mesh = new THREE.Mesh(getGeometry(), new THREE.MeshStandardMaterial()); - mesh.material.flatShading = !params.smoothShading; - mesh.rotateY(-Math.PI / 2); - mesh.scale.set(3.5, 3.5, 3.5); - mesh.translateZ(1.5); - scene.add(mesh); - - if (map !== undefined && params.showMap) { - mesh.material.map = map; - mesh.material.needsUpdate = true; - } - - render(); - }); - - window.addEventListener('resize', onWindowResize); - - new THREE.TextureLoader().load('./models/obj/cerberus/Cerberus_A.jpg', function (texture) { - map = texture; - map.colorSpace = THREE.SRGBColorSpace; - - if (mesh !== undefined && params.showMap) { - mesh.material.map = map; - mesh.material.needsUpdate = true; - } - }); - - const gui = new GUI({ title: 'Edge split modifier parameters' }); - - gui.add(params, 'showMap').onFinishChange(updateMesh); - gui.add(params, 'smoothShading').onFinishChange(updateMesh); - gui.add(params, 'edgeSplit').onFinishChange(updateMesh); - gui.add(params, 'cutOffAngle').min(0).max(180).onFinishChange(updateMesh); - gui.add(params, 'tryKeepNormals').onFinishChange(updateMesh); -} - -function onWindowResize() { - renderer.setSize(window.innerWidth, window.innerHeight); - - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - render(); -} - -function getGeometry() { - let geometry; - - if (params.edgeSplit) { - geometry = modifier.modify(baseGeometry, (params.cutOffAngle * Math.PI) / 180, params.tryKeepNormals); - } else { - geometry = baseGeometry; - } - - return geometry; -} - -function updateMesh() { - if (mesh !== undefined) { - mesh.geometry = getGeometry(); - - let needsUpdate = mesh.material.flatShading === params.smoothShading; - mesh.material.flatShading = params.smoothShading === false; - - if (map !== undefined) { - needsUpdate = needsUpdate || mesh.material.map !== (params.showMap ? map : null); - mesh.material.map = params.showMap ? map : null; - } - - mesh.material.needsUpdate = needsUpdate; - - render(); - } -} - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_modifier_simplifier.ts b/examples-testing/examples/webgl_modifier_simplifier.ts deleted file mode 100644 index e6ea453b3..000000000 --- a/examples-testing/examples/webgl_modifier_simplifier.ts +++ /dev/null @@ -1,77 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { SimplifyModifier } from 'three/addons/modifiers/SimplifyModifier.js'; - -let renderer, scene, camera; - -init(); - -function init() { - const info = document.createElement('div'); - info.style.position = 'absolute'; - info.style.top = '10px'; - info.style.width = '100%'; - info.style.textAlign = 'center'; - info.innerHTML = - 'three.js - Vertex Reduction using SimplifyModifier'; - document.body.appendChild(info); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - document.body.appendChild(renderer.domElement); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.z = 15; - - const controls = new OrbitControls(camera, renderer.domElement); - controls.addEventListener('change', render); // use if there is no animation loop - controls.enablePan = false; - controls.enableZoom = false; - - scene.add(new THREE.AmbientLight(0xffffff, 0.6)); - - const light = new THREE.PointLight(0xffffff, 400); - camera.add(light); - scene.add(camera); - - new GLTFLoader().load('models/gltf/LeePerrySmith/LeePerrySmith.glb', function (gltf) { - const mesh = gltf.scene.children[0]; - mesh.position.x = -3; - mesh.rotation.y = Math.PI / 2; - scene.add(mesh); - - const modifier = new SimplifyModifier(); - - const simplified = mesh.clone(); - simplified.material = simplified.material.clone(); - simplified.material.flatShading = true; - const count = Math.floor(simplified.geometry.attributes.position.count * 0.875); // number of vertices to remove - simplified.geometry = modifier.modify(simplified.geometry, count); - - simplified.position.x = 3; - simplified.rotation.y = -Math.PI / 2; - scene.add(simplified); - - render(); - }); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - renderer.setSize(window.innerWidth, window.innerHeight); - - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - render(); -} - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_modifier_tessellation.ts b/examples-testing/examples/webgl_modifier_tessellation.ts deleted file mode 100644 index 4600fc6cb..000000000 --- a/examples-testing/examples/webgl_modifier_tessellation.ts +++ /dev/null @@ -1,142 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { TrackballControls } from 'three/addons/controls/TrackballControls.js'; -import { TessellateModifier } from 'three/addons/modifiers/TessellateModifier.js'; -import { FontLoader } from 'three/addons/loaders/FontLoader.js'; -import { TextGeometry } from 'three/addons/geometries/TextGeometry.js'; - -let renderer, scene, camera, stats; - -let controls; - -let mesh, uniforms; - -const WIDTH = window.innerWidth; -const HEIGHT = window.innerHeight; - -const loader = new FontLoader(); -loader.load('fonts/helvetiker_bold.typeface.json', function (font) { - init(font); -}); - -function init(font) { - camera = new THREE.PerspectiveCamera(40, WIDTH / HEIGHT, 1, 10000); - camera.position.set(-100, 100, 200); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x050505); - - // - - let geometry = new TextGeometry('THREE.JS', { - font: font, - - size: 40, - depth: 5, - curveSegments: 3, - - bevelThickness: 2, - bevelSize: 1, - bevelEnabled: true, - }); - - geometry.center(); - - const tessellateModifier = new TessellateModifier(8, 6); - - geometry = tessellateModifier.modify(geometry); - - // - - const numFaces = geometry.attributes.position.count / 3; - - const colors = new Float32Array(numFaces * 3 * 3); - const displacement = new Float32Array(numFaces * 3 * 3); - - const color = new THREE.Color(); - - for (let f = 0; f < numFaces; f++) { - const index = 9 * f; - - const h = 0.2 * Math.random(); - const s = 0.5 + 0.5 * Math.random(); - const l = 0.5 + 0.5 * Math.random(); - - color.setHSL(h, s, l); - - const d = 10 * (0.5 - Math.random()); - - for (let i = 0; i < 3; i++) { - colors[index + 3 * i] = color.r; - colors[index + 3 * i + 1] = color.g; - colors[index + 3 * i + 2] = color.b; - - displacement[index + 3 * i] = d; - displacement[index + 3 * i + 1] = d; - displacement[index + 3 * i + 2] = d; - } - } - - geometry.setAttribute('customColor', new THREE.BufferAttribute(colors, 3)); - geometry.setAttribute('displacement', new THREE.BufferAttribute(displacement, 3)); - - // - - uniforms = { - amplitude: { value: 0.0 }, - }; - - const shaderMaterial = new THREE.ShaderMaterial({ - uniforms: uniforms, - vertexShader: document.getElementById('vertexshader').textContent, - fragmentShader: document.getElementById('fragmentshader').textContent, - }); - - // - - mesh = new THREE.Mesh(geometry, shaderMaterial); - - scene.add(mesh); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(WIDTH, HEIGHT); - renderer.setAnimationLoop(animate); - - const container = document.getElementById('container'); - container.appendChild(renderer.domElement); - - controls = new TrackballControls(camera, renderer.domElement); - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - render(); - - stats.update(); -} - -function render() { - const time = Date.now() * 0.001; - - uniforms.amplitude.value = 1.0 + Math.sin(time * 0.5); - - controls.update(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_morphtargets.ts b/examples-testing/examples/webgl_morphtargets.ts deleted file mode 100644 index d8a4bbe8d..000000000 --- a/examples-testing/examples/webgl_morphtargets.ts +++ /dev/null @@ -1,116 +0,0 @@ -import * as THREE from 'three'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let container, camera, scene, renderer, mesh; - -init(); - -function init() { - container = document.getElementById('container'); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x8fbcd4); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 20); - camera.position.z = 10; - scene.add(camera); - - scene.add(new THREE.AmbientLight(0x8fbcd4, 1.5)); - - const pointLight = new THREE.PointLight(0xffffff, 200); - camera.add(pointLight); - - const geometry = createGeometry(); - - const material = new THREE.MeshPhongMaterial({ - color: 0xff0000, - flatShading: true, - }); - - mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - initGUI(); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(function () { - renderer.render(scene, camera); - }); - container.appendChild(renderer.domElement); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.enableZoom = false; - - window.addEventListener('resize', onWindowResize); -} - -function createGeometry() { - const geometry = new THREE.BoxGeometry(2, 2, 2, 32, 32, 32); - - // create an empty array to hold targets for the attribute we want to morph - // morphing positions and normals is supported - geometry.morphAttributes.position = []; - - // the original positions of the cube's vertices - const positionAttribute = geometry.attributes.position; - - // for the first morph target we'll move the cube's vertices onto the surface of a sphere - const spherePositions = []; - - // for the second morph target, we'll twist the cubes vertices - const twistPositions = []; - const direction = new THREE.Vector3(1, 0, 0); - const vertex = new THREE.Vector3(); - - for (let i = 0; i < positionAttribute.count; i++) { - const x = positionAttribute.getX(i); - const y = positionAttribute.getY(i); - const z = positionAttribute.getZ(i); - - spherePositions.push( - x * Math.sqrt(1 - (y * y) / 2 - (z * z) / 2 + (y * y * z * z) / 3), - y * Math.sqrt(1 - (z * z) / 2 - (x * x) / 2 + (z * z * x * x) / 3), - z * Math.sqrt(1 - (x * x) / 2 - (y * y) / 2 + (x * x * y * y) / 3), - ); - - // stretch along the x-axis so we can see the twist better - vertex.set(x * 2, y, z); - - vertex.applyAxisAngle(direction, (Math.PI * x) / 2).toArray(twistPositions, twistPositions.length); - } - - // add the spherical positions as the first morph target - geometry.morphAttributes.position[0] = new THREE.Float32BufferAttribute(spherePositions, 3); - - // add the twisted positions as the second morph target - geometry.morphAttributes.position[1] = new THREE.Float32BufferAttribute(twistPositions, 3); - - return geometry; -} - -function initGUI() { - // Set up dat.GUI to control targets - const params = { - Spherify: 0, - Twist: 0, - }; - const gui = new GUI({ title: 'Morph Targets' }); - - gui.add(params, 'Spherify', 0, 1, 0.01).onChange(function (value) { - mesh.morphTargetInfluences[0] = value; - }); - gui.add(params, 'Twist', 0, 1, 0.01).onChange(function (value) { - mesh.morphTargetInfluences[1] = value; - }); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} diff --git a/examples-testing/examples/webgl_morphtargets_face.ts b/examples-testing/examples/webgl_morphtargets_face.ts deleted file mode 100644 index 7f348c8a4..000000000 --- a/examples-testing/examples/webgl_morphtargets_face.ts +++ /dev/null @@ -1,108 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js'; -import { MeshoptDecoder } from 'three/addons/libs/meshopt_decoder.module.js'; - -import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let camera, scene, renderer, stats, mixer, timer, controls; - -init(); - -function init() { - timer = new THREE.Timer(); - timer.connect(document); - - const container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 20); - camera.position.set(-1.8, 0.8, 3); - - scene = new THREE.Scene(); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - - container.appendChild(renderer.domElement); - - const ktx2Loader = new KTX2Loader().setTranscoderPath('jsm/libs/basis/').detectSupport(renderer); - - new GLTFLoader() - .setKTX2Loader(ktx2Loader) - .setMeshoptDecoder(MeshoptDecoder) - .load('models/gltf/facecap.glb', gltf => { - const mesh = gltf.scene.children[0]; - - scene.add(mesh); - - mixer = new THREE.AnimationMixer(mesh); - - mixer.clipAction(gltf.animations[0]).play(); - - // GUI - - const head = mesh.getObjectByName('mesh_2'); - const influences = head.morphTargetInfluences; - - const gui = new GUI(); - gui.close(); - - for (const [key, value] of Object.entries(head.morphTargetDictionary)) { - gui.add(influences, value, 0, 1, 0.01).name(key.replace('blendShape1.', '')).listen(); - } - }); - - const environment = new RoomEnvironment(); - const pmremGenerator = new THREE.PMREMGenerator(renderer); - - scene.background = new THREE.Color(0x666666); - scene.environment = pmremGenerator.fromScene(environment, 0.04).texture; - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - controls.minDistance = 2.5; - controls.maxDistance = 5; - controls.minAzimuthAngle = -Math.PI / 2; - controls.maxAzimuthAngle = Math.PI / 2; - controls.maxPolarAngle = Math.PI / 1.8; - controls.target.set(0, 0.15, -0.2); - - stats = new Stats(); - container.appendChild(stats.dom); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - const delta = timer.getDelta(); - - if (mixer) { - mixer.update(delta); - } - - renderer.render(scene, camera); - - controls.update(); - - stats.update(); -} diff --git a/examples-testing/examples/webgl_morphtargets_horse.ts b/examples-testing/examples/webgl_morphtargets_horse.ts deleted file mode 100644 index 2c29e9c0e..000000000 --- a/examples-testing/examples/webgl_morphtargets_horse.ts +++ /dev/null @@ -1,100 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -let container, stats; -let camera, scene, renderer; -let mesh, mixer; - -const radius = 600; -let theta = 0; -let prevTime = Date.now(); - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - // - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000); - camera.position.y = 300; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xf0f0f0); - - // - - const light1 = new THREE.DirectionalLight(0xefefff, 5); - light1.position.set(1, 1, 1).normalize(); - scene.add(light1); - - const light2 = new THREE.DirectionalLight(0xffefef, 5); - light2.position.set(-1, -1, -1).normalize(); - scene.add(light2); - - const loader = new GLTFLoader(); - loader.load('models/gltf/Horse.glb', function (gltf) { - mesh = gltf.scene.children[0]; - mesh.scale.set(1.5, 1.5, 1.5); - scene.add(mesh); - - mixer = new THREE.AnimationMixer(mesh); - - mixer.clipAction(gltf.animations[0]).setDuration(1).play(); - }); - - // - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - - container.appendChild(renderer.domElement); - - // - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - render(); - stats.update(); -} - -function render() { - theta += 0.1; - - camera.position.x = radius * Math.sin(THREE.MathUtils.degToRad(theta)); - camera.position.z = radius * Math.cos(THREE.MathUtils.degToRad(theta)); - - camera.lookAt(0, 150, 0); - - if (mixer) { - const time = Date.now(); - - mixer.update((time - prevTime) * 0.001); - - prevTime = time; - } - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_morphtargets_sphere.ts b/examples-testing/examples/webgl_morphtargets_sphere.ts deleted file mode 100644 index 3e36f002c..000000000 --- a/examples-testing/examples/webgl_morphtargets_sphere.ts +++ /dev/null @@ -1,105 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -let camera, scene, renderer, timer; - -let mesh; - -let sign = 1; -const speed = 0.5; - -init(); - -function init() { - const container = document.getElementById('container'); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.2, 100); - camera.position.set(0, 5, 5); - - scene = new THREE.Scene(); - - timer = new THREE.Timer(); - timer.connect(document); - - const light1 = new THREE.PointLight(0xff2200, 50000); - light1.position.set(100, 100, 100); - scene.add(light1); - - const light2 = new THREE.PointLight(0x22ff00, 10000); - light2.position.set(-100, -100, -100); - scene.add(light2); - - scene.add(new THREE.AmbientLight(0x111111)); - - const loader = new GLTFLoader(); - loader.load('models/gltf/AnimatedMorphSphere/glTF/AnimatedMorphSphere.gltf', function (gltf) { - mesh = gltf.scene.getObjectByName('AnimatedMorphSphere'); - mesh.rotation.z = Math.PI / 2; - scene.add(mesh); - - // - - const pointsMaterial = new THREE.PointsMaterial({ - size: 10, - sizeAttenuation: false, - map: new THREE.TextureLoader().load('textures/sprites/disc.png'), - alphaTest: 0.5, - }); - - const points = new THREE.Points(mesh.geometry, pointsMaterial); - points.morphTargetInfluences = mesh.morphTargetInfluences; - points.morphTargetDictionary = mesh.morphTargetDictionary; - mesh.add(points); - }); - - // - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - - container.appendChild(renderer.domElement); - - // - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 1; - controls.maxDistance = 20; - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - render(); -} - -function render() { - const delta = timer.getDelta(); - - if (mesh !== undefined) { - const step = delta * speed; - - mesh.rotation.y += step; - - mesh.morphTargetInfluences[1] = mesh.morphTargetInfluences[1] + step * sign; - - if (mesh.morphTargetInfluences[1] <= 0 || mesh.morphTargetInfluences[1] >= 1) { - sign *= -1; - } - } - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_multiple_elements.ts b/examples-testing/examples/webgl_multiple_elements.ts deleted file mode 100644 index 64f8a9c5f..000000000 --- a/examples-testing/examples/webgl_multiple_elements.ts +++ /dev/null @@ -1,139 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let canvas, renderer; - -const scenes = []; - -init(); - -function init() { - canvas = document.getElementById('c'); - - const geometries = [ - new THREE.BoxGeometry(1, 1, 1), - new THREE.SphereGeometry(0.5, 12, 8), - new THREE.DodecahedronGeometry(0.5), - new THREE.CylinderGeometry(0.5, 0.5, 1, 12), - ]; - - const content = document.getElementById('content'); - - for (let i = 0; i < 40; i++) { - const scene = new THREE.Scene(); - - // make a list item - const element = document.createElement('div'); - element.className = 'list-item'; - - const sceneElement = document.createElement('div'); - element.appendChild(sceneElement); - - const descriptionElement = document.createElement('div'); - descriptionElement.innerText = 'Scene ' + (i + 1); - element.appendChild(descriptionElement); - - // the element that represents the area we want to render the scene - scene.userData.element = sceneElement; - content.appendChild(element); - - const camera = new THREE.PerspectiveCamera(50, 1, 1, 10); - camera.position.z = 2; - scene.userData.camera = camera; - - const controls = new OrbitControls(scene.userData.camera, scene.userData.element); - controls.minDistance = 2; - controls.maxDistance = 5; - controls.enablePan = false; - controls.enableZoom = false; - scene.userData.controls = controls; - - // add one random mesh to each scene - const geometry = geometries[(geometries.length * Math.random()) | 0]; - - const material = new THREE.MeshStandardMaterial({ - color: new THREE.Color().setHSL(Math.random(), 1, 0.75, THREE.SRGBColorSpace), - roughness: 0.5, - metalness: 0, - flatShading: true, - }); - - scene.add(new THREE.Mesh(geometry, material)); - - scene.add(new THREE.HemisphereLight(0xaaaaaa, 0x444444, 3)); - - const light = new THREE.DirectionalLight(0xffffff, 1.5); - light.position.set(1, 1, 1); - scene.add(light); - - scenes.push(scene); - } - - renderer = new THREE.WebGLRenderer({ canvas: canvas, antialias: true }); - renderer.setClearColor(0xffffff, 1); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setAnimationLoop(animate); -} - -function updateSize() { - const width = canvas.clientWidth; - const height = canvas.clientHeight; - - if (canvas.width !== width || canvas.height !== height) { - renderer.setSize(width, height, false); - } -} - -function animate() { - updateSize(); - - canvas.style.transform = `translateY(${window.scrollY}px)`; - - renderer.setClearColor(0xffffff); - renderer.setScissorTest(false); - renderer.clear(); - - renderer.setClearColor(0xe0e0e0); - renderer.setScissorTest(true); - - scenes.forEach(function (scene) { - // so something moves - scene.children[0].rotation.y = Date.now() * 0.001; - - // get the element that is a place holder for where we want to - // draw the scene - const element = scene.userData.element; - - // get its position relative to the page's viewport - const rect = element.getBoundingClientRect(); - - // check if it's offscreen. If so skip it - if ( - rect.bottom < 0 || - rect.top > renderer.domElement.clientHeight || - rect.right < 0 || - rect.left > renderer.domElement.clientWidth - ) { - return; // it's off screen - } - - // set the viewport - const width = rect.right - rect.left; - const height = rect.bottom - rect.top; - const left = rect.left; - const bottom = renderer.domElement.clientHeight - rect.bottom; - - renderer.setViewport(left, bottom, width, height); - renderer.setScissor(left, bottom, width, height); - - const camera = scene.userData.camera; - - //camera.aspect = width / height; // not changing in this example - //camera.updateProjectionMatrix(); - - //scene.userData.controls.update(); - - renderer.render(scene, camera); - }); -} diff --git a/examples-testing/examples/webgl_multiple_rendertargets.ts b/examples-testing/examples/webgl_multiple_rendertargets.ts deleted file mode 100644 index 86708082b..000000000 --- a/examples-testing/examples/webgl_multiple_rendertargets.ts +++ /dev/null @@ -1,133 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let camera, scene, renderer, controls; -let renderTarget; -let postScene, postCamera; - -const parameters = { - samples: 4, - wireframe: false, -}; - -const gui = new GUI(); -gui.add(parameters, 'samples', 0, 4).step(1); -gui.add(parameters, 'wireframe'); -gui.onChange(render); - -init(); - -function init() { - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - document.body.appendChild(renderer.domElement); - - // Create a multi render target with Float buffers - - renderTarget = new THREE.WebGLRenderTarget( - window.innerWidth * window.devicePixelRatio, - window.innerHeight * window.devicePixelRatio, - { - count: 2, - minFilter: THREE.NearestFilter, - magFilter: THREE.NearestFilter, - }, - ); - - // Name our G-Buffer attachments for debugging - - renderTarget.textures[0].name = 'diffuse'; - renderTarget.textures[1].name = 'normal'; - - // Scene setup - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x222222); - - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 50); - camera.position.z = 4; - - const loader = new THREE.TextureLoader(); - - const diffuse = loader.load('textures/hardwood2_diffuse.jpg', render); - diffuse.wrapS = THREE.RepeatWrapping; - diffuse.wrapT = THREE.RepeatWrapping; - diffuse.colorSpace = THREE.SRGBColorSpace; - - scene.add( - new THREE.Mesh( - new THREE.TorusKnotGeometry(1, 0.3, 128, 32), - new THREE.RawShaderMaterial({ - name: 'G-Buffer Shader', - vertexShader: document.querySelector('#gbuffer-vert').textContent.trim(), - fragmentShader: document.querySelector('#gbuffer-frag').textContent.trim(), - uniforms: { - tDiffuse: { value: diffuse }, - repeat: { value: new THREE.Vector2(5, 0.5) }, - }, - glslVersion: THREE.GLSL3, - }), - ), - ); - - // PostProcessing setup - - postScene = new THREE.Scene(); - postCamera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1); - - postScene.add( - new THREE.Mesh( - new THREE.PlaneGeometry(2, 2), - new THREE.RawShaderMaterial({ - name: 'Post-FX Shader', - vertexShader: document.querySelector('#render-vert').textContent.trim(), - fragmentShader: document.querySelector('#render-frag').textContent.trim(), - uniforms: { - tDiffuse: { value: renderTarget.textures[0] }, - tNormal: { value: renderTarget.textures[1] }, - }, - glslVersion: THREE.GLSL3, - }), - ), - ); - - // Controls - - controls = new OrbitControls(camera, renderer.domElement); - controls.addEventListener('change', render); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - const dpr = renderer.getPixelRatio(); - renderTarget.setSize(window.innerWidth * dpr, window.innerHeight * dpr); - - render(); -} - -function render() { - renderTarget.samples = parameters.samples; - - scene.traverse(function (child) { - if (child.material !== undefined) { - child.material.wireframe = parameters.wireframe; - } - }); - - // render scene into target - renderer.setRenderTarget(renderTarget); - renderer.render(scene, camera); - - // render post FX - renderer.setRenderTarget(null); - renderer.render(postScene, postCamera); -} diff --git a/examples-testing/examples/webgl_multiple_scenes_comparison.ts b/examples-testing/examples/webgl_multiple_scenes_comparison.ts deleted file mode 100644 index 41a5130d4..000000000 --- a/examples-testing/examples/webgl_multiple_scenes_comparison.ts +++ /dev/null @@ -1,98 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let container, camera, renderer, controls; -let sceneL, sceneR; - -let sliderPos = window.innerWidth / 2; - -init(); - -function init() { - container = document.querySelector('.container'); - - sceneL = new THREE.Scene(); - sceneL.background = new THREE.Color(0xbcd48f); - - sceneR = new THREE.Scene(); - sceneR.background = new THREE.Color(0x8fbcd4); - - camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.z = 6; - - controls = new OrbitControls(camera, container); - - const light = new THREE.HemisphereLight(0xffffff, 0x444444, 3); - light.position.set(-2, 2, 2); - sceneL.add(light.clone()); - sceneR.add(light.clone()); - - initMeshes(); - initSlider(); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setScissorTest(true); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - window.addEventListener('resize', onWindowResize); -} - -function initMeshes() { - const geometry = new THREE.IcosahedronGeometry(1, 3); - - const meshL = new THREE.Mesh(geometry, new THREE.MeshStandardMaterial()); - sceneL.add(meshL); - - const meshR = new THREE.Mesh(geometry, new THREE.MeshStandardMaterial({ wireframe: true })); - sceneR.add(meshR); -} - -function initSlider() { - const slider = document.querySelector('.slider'); - - function onPointerDown() { - if (event.isPrimary === false) return; - - controls.enabled = false; - - window.addEventListener('pointermove', onPointerMove); - window.addEventListener('pointerup', onPointerUp); - } - - function onPointerUp() { - controls.enabled = true; - - window.removeEventListener('pointermove', onPointerMove); - window.removeEventListener('pointerup', onPointerUp); - } - - function onPointerMove(e) { - if (event.isPrimary === false) return; - - sliderPos = Math.max(0, Math.min(window.innerWidth, e.pageX)); - - slider.style.left = sliderPos - slider.offsetWidth / 2 + 'px'; - } - - slider.style.touchAction = 'none'; // disable touch scroll - slider.addEventListener('pointerdown', onPointerDown); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - renderer.setScissor(0, 0, sliderPos, window.innerHeight); - renderer.render(sceneL, camera); - - renderer.setScissor(sliderPos, 0, window.innerWidth, window.innerHeight); - renderer.render(sceneR, camera); -} diff --git a/examples-testing/examples/webgl_multiple_views.ts b/examples-testing/examples/webgl_multiple_views.ts deleted file mode 100644 index 672846f9f..000000000 --- a/examples-testing/examples/webgl_multiple_views.ts +++ /dev/null @@ -1,235 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -let stats; - -let scene, renderer; - -let mouseX = 0; - -let windowWidth, windowHeight; - -const views = [ - { - left: 0, - bottom: 0, - width: 0.5, - height: 1.0, - background: new THREE.Color().setRGB(0.5, 0.5, 0.7, THREE.SRGBColorSpace), - eye: [0, 300, 1800], - up: [0, 1, 0], - fov: 30, - updateCamera: function (camera, scene, mouseX) { - camera.position.x += mouseX * 0.05; - camera.position.x = Math.max(Math.min(camera.position.x, 2000), -2000); - camera.lookAt(scene.position); - }, - }, - { - left: 0.5, - bottom: 0, - width: 0.5, - height: 0.5, - background: new THREE.Color().setRGB(0.7, 0.5, 0.5, THREE.SRGBColorSpace), - eye: [0, 1800, 0], - up: [0, 0, 1], - fov: 45, - updateCamera: function (camera, scene, mouseX) { - camera.position.x -= mouseX * 0.05; - camera.position.x = Math.max(Math.min(camera.position.x, 2000), -2000); - camera.lookAt(camera.position.clone().setY(0)); - }, - }, - { - left: 0.5, - bottom: 0.5, - width: 0.5, - height: 0.5, - background: new THREE.Color().setRGB(0.5, 0.7, 0.7, THREE.SRGBColorSpace), - eye: [1400, 800, 1400], - up: [0, 1, 0], - fov: 60, - updateCamera: function (camera, scene, mouseX) { - camera.position.y -= mouseX * 0.05; - camera.position.y = Math.max(Math.min(camera.position.y, 1600), -1600); - camera.lookAt(scene.position); - }, - }, -]; - -init(); - -function init() { - const container = document.getElementById('container'); - - for (let ii = 0; ii < views.length; ++ii) { - const view = views[ii]; - const camera = new THREE.PerspectiveCamera(view.fov, window.innerWidth / window.innerHeight, 1, 10000); - camera.position.fromArray(view.eye); - camera.up.fromArray(view.up); - view.camera = camera; - } - - scene = new THREE.Scene(); - - const light = new THREE.DirectionalLight(0xffffff, 3); - light.position.set(0, 0, 1); - scene.add(light); - - // shadow - - const canvas = document.createElement('canvas'); - canvas.width = 128; - canvas.height = 128; - - const context = canvas.getContext('2d'); - const gradient = context.createRadialGradient( - canvas.width / 2, - canvas.height / 2, - 0, - canvas.width / 2, - canvas.height / 2, - canvas.width / 2, - ); - gradient.addColorStop(0.1, 'rgba(0,0,0,0.15)'); - gradient.addColorStop(1, 'rgba(0,0,0,0)'); - - context.fillStyle = gradient; - context.fillRect(0, 0, canvas.width, canvas.height); - - const shadowTexture = new THREE.CanvasTexture(canvas); - - const shadowMaterial = new THREE.MeshBasicMaterial({ map: shadowTexture, transparent: true }); - const shadowGeo = new THREE.PlaneGeometry(300, 300, 1, 1); - - let shadowMesh; - - shadowMesh = new THREE.Mesh(shadowGeo, shadowMaterial); - shadowMesh.position.y = -250; - shadowMesh.rotation.x = -Math.PI / 2; - scene.add(shadowMesh); - - shadowMesh = new THREE.Mesh(shadowGeo, shadowMaterial); - shadowMesh.position.x = -400; - shadowMesh.position.y = -250; - shadowMesh.rotation.x = -Math.PI / 2; - scene.add(shadowMesh); - - shadowMesh = new THREE.Mesh(shadowGeo, shadowMaterial); - shadowMesh.position.x = 400; - shadowMesh.position.y = -250; - shadowMesh.rotation.x = -Math.PI / 2; - scene.add(shadowMesh); - - const radius = 200; - - const geometry1 = new THREE.IcosahedronGeometry(radius, 1); - - const count = geometry1.attributes.position.count; - geometry1.setAttribute('color', new THREE.BufferAttribute(new Float32Array(count * 3), 3)); - - const geometry2 = geometry1.clone(); - const geometry3 = geometry1.clone(); - - const color = new THREE.Color(); - const positions1 = geometry1.attributes.position; - const positions2 = geometry2.attributes.position; - const positions3 = geometry3.attributes.position; - const colors1 = geometry1.attributes.color; - const colors2 = geometry2.attributes.color; - const colors3 = geometry3.attributes.color; - - for (let i = 0; i < count; i++) { - color.setHSL((positions1.getY(i) / radius + 1) / 2, 1.0, 0.5, THREE.SRGBColorSpace); - colors1.setXYZ(i, color.r, color.g, color.b); - - color.setHSL(0, (positions2.getY(i) / radius + 1) / 2, 0.5, THREE.SRGBColorSpace); - colors2.setXYZ(i, color.r, color.g, color.b); - - color.setRGB(1, 0.8 - (positions3.getY(i) / radius + 1) / 2, 0, THREE.SRGBColorSpace); - colors3.setXYZ(i, color.r, color.g, color.b); - } - - const material = new THREE.MeshPhongMaterial({ - color: 0xffffff, - flatShading: true, - vertexColors: true, - shininess: 0, - }); - - const wireframeMaterial = new THREE.MeshBasicMaterial({ color: 0x000000, wireframe: true, transparent: true }); - - let mesh = new THREE.Mesh(geometry1, material); - let wireframe = new THREE.Mesh(geometry1, wireframeMaterial); - mesh.add(wireframe); - mesh.position.x = -400; - mesh.rotation.x = -1.87; - scene.add(mesh); - - mesh = new THREE.Mesh(geometry2, material); - wireframe = new THREE.Mesh(geometry2, wireframeMaterial); - mesh.add(wireframe); - mesh.position.x = 400; - scene.add(mesh); - - mesh = new THREE.Mesh(geometry3, material); - wireframe = new THREE.Mesh(geometry3, wireframeMaterial); - mesh.add(wireframe); - scene.add(mesh); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - stats = new Stats(); - container.appendChild(stats.dom); - - document.addEventListener('mousemove', onDocumentMouseMove); -} - -function onDocumentMouseMove(event) { - mouseX = event.clientX - windowWidth / 2; -} - -function updateSize() { - if (windowWidth != window.innerWidth || windowHeight != window.innerHeight) { - windowWidth = window.innerWidth; - windowHeight = window.innerHeight; - - renderer.setSize(windowWidth, windowHeight); - } -} - -function animate() { - render(); - stats.update(); -} - -function render() { - updateSize(); - - for (let ii = 0; ii < views.length; ++ii) { - const view = views[ii]; - const camera = view.camera; - - view.updateCamera(camera, scene, mouseX); - - const left = Math.floor(windowWidth * view.left); - const bottom = Math.floor(windowHeight * view.bottom); - const width = Math.floor(windowWidth * view.width); - const height = Math.floor(windowHeight * view.height); - - renderer.setViewport(left, bottom, width, height); - renderer.setScissor(left, bottom, width, height); - renderer.setScissorTest(true); - renderer.setClearColor(view.background); - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.render(scene, camera); - } -} diff --git a/examples-testing/examples/webgl_multisampled_renderbuffers.ts b/examples-testing/examples/webgl_multisampled_renderbuffers.ts deleted file mode 100644 index df84fb144..000000000 --- a/examples-testing/examples/webgl_multisampled_renderbuffers.ts +++ /dev/null @@ -1,133 +0,0 @@ -import * as THREE from 'three'; - -import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; -import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; -import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let camera, renderer, group, container; - -let composer1, composer2; - -const params = { - animate: true, -}; - -init(); - -function init() { - container = document.getElementById('container'); - - camera = new THREE.PerspectiveCamera(45, container.offsetWidth / container.offsetHeight, 10, 2000); - camera.position.z = 500; - - const scene = new THREE.Scene(); - scene.background = new THREE.Color(0xffffff); - scene.fog = new THREE.Fog(0xcccccc, 100, 1500); - - // - - const hemiLight = new THREE.HemisphereLight(0xffffff, 0x222222, 5); - hemiLight.position.set(1, 1, 1); - scene.add(hemiLight); - - // - - group = new THREE.Group(); - - const geometry = new THREE.SphereGeometry(10, 64, 40); - const material = new THREE.MeshLambertMaterial({ - color: 0xee0808, - polygonOffset: true, - polygonOffsetFactor: 1, // positive value pushes polygon further away - polygonOffsetUnits: 1, - }); - const material2 = new THREE.MeshBasicMaterial({ color: 0xffffff, wireframe: true }); - - for (let i = 0; i < 50; i++) { - const mesh = new THREE.Mesh(geometry, material); - mesh.position.x = Math.random() * 600 - 300; - mesh.position.y = Math.random() * 600 - 300; - mesh.position.z = Math.random() * 600 - 300; - mesh.rotation.x = Math.random(); - mesh.rotation.z = Math.random(); - mesh.scale.setScalar(Math.random() * 5 + 5); - group.add(mesh); - - const mesh2 = new THREE.Mesh(geometry, material2); - mesh2.position.copy(mesh.position); - mesh2.rotation.copy(mesh.rotation); - mesh2.scale.copy(mesh.scale); - group.add(mesh2); - } - - scene.add(group); - - // - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(container.offsetWidth, container.offsetHeight); - renderer.setAnimationLoop(animate); - renderer.autoClear = false; - container.appendChild(renderer.domElement); - - // - - const size = renderer.getDrawingBufferSize(new THREE.Vector2()); - const renderTarget = new THREE.WebGLRenderTarget(size.width, size.height, { - samples: 4, - type: THREE.HalfFloatType, - }); - - const renderPass = new RenderPass(scene, camera); - const outputPass = new OutputPass(); - - // - - composer1 = new EffectComposer(renderer); - composer1.addPass(renderPass); - composer1.addPass(outputPass); - - // - - composer2 = new EffectComposer(renderer, renderTarget); - composer2.addPass(renderPass); - composer2.addPass(outputPass); - - // - - const gui = new GUI(); - gui.add(params, 'animate'); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = container.offsetWidth / container.offsetHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(container.offsetWidth, container.offsetHeight); - composer1.setSize(container.offsetWidth, container.offsetHeight); - composer2.setSize(container.offsetWidth, container.offsetHeight); -} - -function animate() { - const halfWidth = container.offsetWidth / 2; - - if (params.animate) { - group.rotation.y += 0.002; - } - - renderer.setScissorTest(true); - - renderer.setScissor(0, 0, halfWidth - 1, container.offsetHeight); - composer1.render(); - - renderer.setScissor(halfWidth, 0, halfWidth, container.offsetHeight); - composer2.render(); - - renderer.setScissorTest(false); -} diff --git a/examples-testing/examples/webgl_panorama_cube.ts b/examples-testing/examples/webgl_panorama_cube.ts deleted file mode 100644 index efd09cfc5..000000000 --- a/examples-testing/examples/webgl_panorama_cube.ts +++ /dev/null @@ -1,83 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let camera, controls; -let renderer; -let scene; - -init(); - -function init() { - const container = document.getElementById('container'); - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(90, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.z = 0.01; - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableZoom = false; - controls.enablePan = false; - controls.enableDamping = true; - controls.rotateSpeed = -0.25; - - const textures = getTexturesFromAtlasFile('textures/cube/sun_temple_stripe.jpg', 6); - - const materials = []; - - for (let i = 0; i < 6; i++) { - materials.push(new THREE.MeshBasicMaterial({ map: textures[i] })); - } - - const skyBox = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), materials); - skyBox.geometry.scale(1, 1, -1); - scene.add(skyBox); - - window.addEventListener('resize', onWindowResize); -} - -function getTexturesFromAtlasFile(atlasImgUrl, tilesNum) { - const textures = []; - - for (let i = 0; i < tilesNum; i++) { - textures[i] = new THREE.Texture(); - } - - new THREE.ImageLoader().load(atlasImgUrl, image => { - let canvas, context; - const tileWidth = image.height; - - for (let i = 0; i < textures.length; i++) { - canvas = document.createElement('canvas'); - context = canvas.getContext('2d'); - canvas.height = tileWidth; - canvas.width = tileWidth; - context.drawImage(image, tileWidth * i, 0, tileWidth, tileWidth, 0, 0, tileWidth, tileWidth); - textures[i].colorSpace = THREE.SRGBColorSpace; - textures[i].image = canvas; - textures[i].needsUpdate = true; - } - }); - - return textures; -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - controls.update(); // required when damping is enabled - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_panorama_equirectangular.ts b/examples-testing/examples/webgl_panorama_equirectangular.ts deleted file mode 100644 index 35949ee6f..000000000 --- a/examples-testing/examples/webgl_panorama_equirectangular.ts +++ /dev/null @@ -1,112 +0,0 @@ -import * as THREE from 'three'; - -let camera, scene, renderer; - -let isUserInteracting = false, - onPointerDownMouseX = 0, - onPointerDownMouseY = 0, - lon = 0, - onPointerDownLon = 0, - lat = 0, - onPointerDownLat = 0, - phi = 0, - theta = 0; - -init(); - -function init() { - const container = document.getElementById('container'); - - camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1100); - - scene = new THREE.Scene(); - - const geometry = new THREE.SphereGeometry(500, 60, 40); - // invert the geometry on the x-axis so that all of the faces point inward - geometry.scale(-1, 1, 1); - - const texture = new THREE.TextureLoader().load('textures/2294472375_24a3b8ef46_o.jpg'); - texture.colorSpace = THREE.SRGBColorSpace; - const material = new THREE.MeshBasicMaterial({ map: texture }); - - const mesh = new THREE.Mesh(geometry, material); - - scene.add(mesh); - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - container.style.touchAction = 'none'; - container.addEventListener('pointerdown', onPointerDown); - - document.addEventListener('wheel', onDocumentMouseWheel); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function onPointerDown(event) { - if (event.isPrimary === false) return; - - isUserInteracting = true; - - onPointerDownMouseX = event.clientX; - onPointerDownMouseY = event.clientY; - - onPointerDownLon = lon; - onPointerDownLat = lat; - - document.addEventListener('pointermove', onPointerMove); - document.addEventListener('pointerup', onPointerUp); -} - -function onPointerMove(event) { - if (event.isPrimary === false) return; - - lon = (onPointerDownMouseX - event.clientX) * 0.1 + onPointerDownLon; - lat = (event.clientY - onPointerDownMouseY) * 0.1 + onPointerDownLat; -} - -function onPointerUp(event) { - if (event.isPrimary === false) return; - - isUserInteracting = false; - - document.removeEventListener('pointermove', onPointerMove); - document.removeEventListener('pointerup', onPointerUp); -} - -function onDocumentMouseWheel(event) { - const fov = camera.fov + event.deltaY * 0.05; - - camera.fov = THREE.MathUtils.clamp(fov, 10, 75); - - camera.updateProjectionMatrix(); -} - -function animate() { - if (isUserInteracting === false) { - lon += 0.1; - } - - lat = Math.max(-85, Math.min(85, lat)); - phi = THREE.MathUtils.degToRad(90 - lat); - theta = THREE.MathUtils.degToRad(lon); - - const x = 500 * Math.sin(phi) * Math.cos(theta); - const y = 500 * Math.cos(phi); - const z = 500 * Math.sin(phi) * Math.sin(theta); - - camera.lookAt(x, y, z); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_performance.ts b/examples-testing/examples/webgl_performance.ts deleted file mode 100644 index 697ea36fb..000000000 --- a/examples-testing/examples/webgl_performance.ts +++ /dev/null @@ -1,77 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; - -let camera, scene, renderer, stats; - -init(); - -function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(60, 60, 60); - - scene = new THREE.Scene(); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = 1; - - renderer.setAnimationLoop(render); - container.appendChild(renderer.domElement); - - // - - stats = new Stats(); - document.body.appendChild(stats.dom); - - new UltraHDRLoader().setPath('textures/equirectangular/').load('royal_esplanade_2k.hdr.jpg', function (texture) { - texture.mapping = THREE.EquirectangularReflectionMapping; - - scene.environment = texture; - - // model - - const loader = new GLTFLoader().setPath('models/gltf/'); - loader.load('dungeon_warkarma.glb', async function (gltf) { - const model = gltf.scene; - - // wait until the model can be added to the scene without blocking due to shader compilation - - await renderer.compileAsync(model, camera, scene); - - scene.add(model); - }); - }); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 2; - controls.maxDistance = 60; - controls.target.set(0, 0, -0.2); - controls.update(); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function render() { - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/webgl_pmrem_cubemap.ts b/examples-testing/examples/webgl_pmrem_cubemap.ts deleted file mode 100644 index fb5bdafc1..000000000 --- a/examples-testing/examples/webgl_pmrem_cubemap.ts +++ /dev/null @@ -1,76 +0,0 @@ -import * as THREE from 'three'; - -import { HDRCubeTextureLoader } from 'three/addons/loaders/HDRCubeTextureLoader.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let camera, scene, renderer; - -init(); - -async function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); - camera.position.set(0, 0, 8); - - scene = new THREE.Scene(); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(render); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - - container.appendChild(renderer.domElement); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 2; - controls.maxDistance = 10; - controls.update(); - - new HDRCubeTextureLoader() - .setPath('./textures/cube/pisaHDR/') - .load(['px.hdr', 'nx.hdr', 'py.hdr', 'ny.hdr', 'pz.hdr', 'nz.hdr'], function (map) { - const pmremGenerator = new THREE.PMREMGenerator(renderer); - const envMap = pmremGenerator.fromCubemap(map).texture; - - scene.background = envMap; - scene.backgroundBlurriness = 0.5; - - pmremGenerator.dispose(); - - const geometry = new THREE.SphereGeometry(0.4, 64, 64); - - for (let i = 0; i < 6; i++) { - for (let j = 0; j < 5; j++) { - const material = new THREE.MeshPhysicalMaterial({ - roughness: i / 5, - metalness: j / 4, - envMap: envMap, - }); - - const mesh = new THREE.Mesh(geometry, material); - mesh.position.x = i - 2.5; - mesh.position.y = j - 2; - scene.add(mesh); - } - } - }); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_pmrem_equirectangular.ts b/examples-testing/examples/webgl_pmrem_equirectangular.ts deleted file mode 100644 index e7b8e76df..000000000 --- a/examples-testing/examples/webgl_pmrem_equirectangular.ts +++ /dev/null @@ -1,76 +0,0 @@ -import * as THREE from 'three'; - -import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let camera, scene, renderer; - -init(); - -async function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); - camera.position.set(0, 0, 8); - - scene = new THREE.Scene(); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(render); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - - container.appendChild(renderer.domElement); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 2; - controls.maxDistance = 10; - controls.update(); - - new UltraHDRLoader().setPath('textures/equirectangular/').load('royal_esplanade_2k.hdr.jpg', function (map) { - map.mapping = THREE.EquirectangularReflectionMapping; - - const pmremGenerator = new THREE.PMREMGenerator(renderer); - const envMap = pmremGenerator.fromEquirectangular(map).texture; - - scene.background = envMap; - scene.backgroundBlurriness = 0.5; - - pmremGenerator.dispose(); - - const geometry = new THREE.SphereGeometry(0.4, 64, 64); - - for (let i = 0; i < 6; i++) { - for (let j = 0; j < 5; j++) { - const material = new THREE.MeshPhysicalMaterial({ - roughness: i / 5, - metalness: j / 4, - envMap: envMap, - }); - - const mesh = new THREE.Mesh(geometry, material); - mesh.position.x = i - 2.5; - mesh.position.y = j - 2; - scene.add(mesh); - } - } - }); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_pmrem_test.ts b/examples-testing/examples/webgl_pmrem_test.ts deleted file mode 100644 index 3c482338f..000000000 --- a/examples-testing/examples/webgl_pmrem_test.ts +++ /dev/null @@ -1,141 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let scene, camera, controls, renderer; - -function init() { - const width = window.innerWidth; - const height = window.innerHeight; - const aspect = width / height; - - // renderer - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(width, height); - - // tonemapping - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = 1; - - document.body.appendChild(renderer.domElement); - - window.addEventListener('resize', onWindowResize); - - // scene - - scene = new THREE.Scene(); - - // camera - - camera = new THREE.PerspectiveCamera(40, aspect, 1, 30); - updateCamera(); - camera.position.set(0, 0, 16); - - // controls - - controls = new OrbitControls(camera, renderer.domElement); - controls.addEventListener('change', render); // use if there is no animation loop - controls.minDistance = 4; - controls.maxDistance = 20; - - // light - - const directionalLight = new THREE.DirectionalLight(0xffffff, 0); // set intensity to 0 to start - const x = 597; - const y = 213; - const theta = ((x + 0.5) * Math.PI) / 512; - const phi = ((y + 0.5) * Math.PI) / 512; - - directionalLight.position.setFromSphericalCoords(100, -phi, Math.PI / 2 - theta); - - scene.add(directionalLight); - // scene.add( new THREE.DirectionalLightHelper( directionalLight ) ); - - // The spot1Lux HDR environment map is expressed in nits (lux / sr). The directional light has units of lux, - // so to match a 1 lux light, we set a single pixel with a value equal to 1 divided by the solid - // angle of the pixel in steradians. This image is 1024 x 512, - // so the value is 1 / ( sin( phi ) * ( pi / 512 ) ^ 2 ) = 27,490 nits. - - const gui = new GUI(); - gui.add({ enabled: true }, 'enabled') - .name('PMREM') - .onChange(value => { - directionalLight.intensity = value ? 0 : 1; - - scene.traverse(function (child) { - if (child.isMesh) { - child.material.envMapIntensity = 1 - directionalLight.intensity; - } - }); - - render(); - }); -} - -function createObjects() { - let radianceMap = null; - new HDRLoader() - // .setDataType( THREE.FloatType ) - .setPath('textures/equirectangular/') - .load('spot1Lux.hdr', function (texture) { - radianceMap = pmremGenerator.fromEquirectangular(texture).texture; - pmremGenerator.dispose(); - - scene.background = radianceMap; - - const geometry = new THREE.SphereGeometry(0.4, 32, 32); - - for (let x = 0; x <= 10; x++) { - for (let y = 0; y <= 2; y++) { - const material = new THREE.MeshPhysicalMaterial({ - roughness: x / 10, - metalness: y < 1 ? 1 : 0, - color: y < 2 ? 0xffffff : 0x000000, - envMap: radianceMap, - envMapIntensity: 1, - }); - - const mesh = new THREE.Mesh(geometry, material); - mesh.position.x = x - 5; - mesh.position.y = 1 - y; - scene.add(mesh); - } - } - - render(); - }); - - const pmremGenerator = new THREE.PMREMGenerator(renderer); - pmremGenerator.compileEquirectangularShader(); -} - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - updateCamera(); - - renderer.setSize(width, height); - - render(); -} - -function updateCamera() { - const horizontalFoV = 40; - const verticalFoV = - (2 * Math.atan(Math.tan(((horizontalFoV / 2) * Math.PI) / 180) / camera.aspect) * 180) / Math.PI; - camera.fov = verticalFoV; - camera.updateProjectionMatrix(); -} - -function render() { - renderer.render(scene, camera); -} - -Promise.resolve().then(init).then(createObjects).then(render); diff --git a/examples-testing/examples/webgl_points_billboards.ts b/examples-testing/examples/webgl_points_billboards.ts deleted file mode 100644 index 24d4de1a9..000000000 --- a/examples-testing/examples/webgl_points_billboards.ts +++ /dev/null @@ -1,120 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let camera, scene, renderer, stats, material; -let mouseX = 0, - mouseY = 0; - -let windowHalfX = window.innerWidth / 2; -let windowHalfY = window.innerHeight / 2; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight, 2, 2000); - camera.position.z = 1000; - - scene = new THREE.Scene(); - scene.fog = new THREE.FogExp2(0x000000, 0.001); - - const geometry = new THREE.BufferGeometry(); - const vertices = []; - - const sprite = new THREE.TextureLoader().load('textures/sprites/disc.png'); - sprite.colorSpace = THREE.SRGBColorSpace; - - for (let i = 0; i < 10000; i++) { - const x = 2000 * Math.random() - 1000; - const y = 2000 * Math.random() - 1000; - const z = 2000 * Math.random() - 1000; - - vertices.push(x, y, z); - } - - geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); - - material = new THREE.PointsMaterial({ - size: 35, - sizeAttenuation: true, - map: sprite, - alphaTest: 0.5, - transparent: true, - }); - material.color.setHSL(1.0, 0.3, 0.7, THREE.SRGBColorSpace); - - const particles = new THREE.Points(geometry, material); - scene.add(particles); - - // - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // - - stats = new Stats(); - document.body.appendChild(stats.dom); - - // - - const gui = new GUI(); - - gui.add(material, 'sizeAttenuation').onChange(function () { - material.needsUpdate = true; - }); - - gui.open(); - - // - - document.body.style.touchAction = 'none'; - document.body.addEventListener('pointermove', onPointerMove); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - windowHalfX = window.innerWidth / 2; - windowHalfY = window.innerHeight / 2; - - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function onPointerMove(event) { - if (event.isPrimary === false) return; - - mouseX = event.clientX - windowHalfX; - mouseY = event.clientY - windowHalfY; -} - -// - -function animate() { - render(); - stats.update(); -} - -function render() { - const time = Date.now() * 0.00005; - - camera.position.x += (mouseX - camera.position.x) * 0.05; - camera.position.y += (-mouseY - camera.position.y) * 0.05; - - camera.lookAt(scene.position); - - const h = ((360 * (1.0 + time)) % 360) / 360; - material.color.setHSL(h, 0.5, 0.5); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_points_sprites.ts b/examples-testing/examples/webgl_points_sprites.ts deleted file mode 100644 index 31b9e2ce1..000000000 --- a/examples-testing/examples/webgl_points_sprites.ts +++ /dev/null @@ -1,167 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let camera, scene, renderer, stats, parameters; -let mouseX = 0, - mouseY = 0; - -let windowHalfX = window.innerWidth / 2; -let windowHalfY = window.innerHeight / 2; - -const materials = []; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 2000); - camera.position.z = 1000; - - scene = new THREE.Scene(); - scene.fog = new THREE.FogExp2(0x000000, 0.0008); - - const geometry = new THREE.BufferGeometry(); - const vertices = []; - - const textureLoader = new THREE.TextureLoader(); - - const assignSRGB = texture => { - texture.colorSpace = THREE.SRGBColorSpace; - }; - - const sprite1 = textureLoader.load('textures/sprites/snowflake1.png', assignSRGB); - const sprite2 = textureLoader.load('textures/sprites/snowflake2.png', assignSRGB); - const sprite3 = textureLoader.load('textures/sprites/snowflake3.png', assignSRGB); - const sprite4 = textureLoader.load('textures/sprites/snowflake4.png', assignSRGB); - const sprite5 = textureLoader.load('textures/sprites/snowflake5.png', assignSRGB); - - for (let i = 0; i < 10000; i++) { - const x = Math.random() * 2000 - 1000; - const y = Math.random() * 2000 - 1000; - const z = Math.random() * 2000 - 1000; - - vertices.push(x, y, z); - } - - geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); - - parameters = [ - [[1.0, 0.2, 0.5], sprite2, 20], - [[0.95, 0.1, 0.5], sprite3, 15], - [[0.9, 0.05, 0.5], sprite1, 10], - [[0.85, 0, 0.5], sprite5, 8], - [[0.8, 0, 0.5], sprite4, 5], - ]; - - for (let i = 0; i < parameters.length; i++) { - const color = parameters[i][0]; - const sprite = parameters[i][1]; - const size = parameters[i][2]; - - materials[i] = new THREE.PointsMaterial({ - size: size, - map: sprite, - blending: THREE.AdditiveBlending, - depthTest: false, - transparent: true, - }); - materials[i].color.setHSL(color[0], color[1], color[2], THREE.SRGBColorSpace); - - const particles = new THREE.Points(geometry, materials[i]); - - particles.rotation.x = Math.random() * 6; - particles.rotation.y = Math.random() * 6; - particles.rotation.z = Math.random() * 6; - - scene.add(particles); - } - - // - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // - - stats = new Stats(); - document.body.appendChild(stats.dom); - - // - - const gui = new GUI(); - - const params = { - texture: true, - }; - - gui.add(params, 'texture').onChange(function (value) { - for (let i = 0; i < materials.length; i++) { - materials[i].map = value === true ? parameters[i][1] : null; - materials[i].needsUpdate = true; - } - }); - - gui.open(); - - document.body.style.touchAction = 'none'; - document.body.addEventListener('pointermove', onPointerMove); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - windowHalfX = window.innerWidth / 2; - windowHalfY = window.innerHeight / 2; - - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function onPointerMove(event) { - if (event.isPrimary === false) return; - - mouseX = event.clientX - windowHalfX; - mouseY = event.clientY - windowHalfY; -} - -// - -function animate() { - render(); - stats.update(); -} - -function render() { - const time = Date.now() * 0.00005; - - camera.position.x += (mouseX - camera.position.x) * 0.05; - camera.position.y += (-mouseY - camera.position.y) * 0.05; - - camera.lookAt(scene.position); - - for (let i = 0; i < scene.children.length; i++) { - const object = scene.children[i]; - - if (object instanceof THREE.Points) { - object.rotation.y = time * (i < 4 ? i + 1 : -(i + 1)); - } - } - - for (let i = 0; i < materials.length; i++) { - const color = parameters[i][0]; - - const h = ((360 * (color[0] + time)) % 360) / 360; - materials[i].color.setHSL(h, color[1], color[2], THREE.SRGBColorSpace); - } - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_points_waves.ts b/examples-testing/examples/webgl_points_waves.ts deleted file mode 100644 index 91986e9e9..000000000 --- a/examples-testing/examples/webgl_points_waves.ts +++ /dev/null @@ -1,145 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -const SEPARATION = 100, - AMOUNTX = 50, - AMOUNTY = 50; - -let container, stats; -let camera, scene, renderer; - -let particles, - count = 0; - -let mouseX = 0, - mouseY = 0; - -let windowHalfX = window.innerWidth / 2; -let windowHalfY = window.innerHeight / 2; - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000); - camera.position.z = 1000; - - scene = new THREE.Scene(); - - // - - const numParticles = AMOUNTX * AMOUNTY; - - const positions = new Float32Array(numParticles * 3); - const scales = new Float32Array(numParticles); - - let i = 0, - j = 0; - - for (let ix = 0; ix < AMOUNTX; ix++) { - for (let iy = 0; iy < AMOUNTY; iy++) { - positions[i] = ix * SEPARATION - (AMOUNTX * SEPARATION) / 2; // x - positions[i + 1] = 0; // y - positions[i + 2] = iy * SEPARATION - (AMOUNTY * SEPARATION) / 2; // z - - scales[j] = 1; - - i += 3; - j++; - } - } - - const geometry = new THREE.BufferGeometry(); - geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); - geometry.setAttribute('scale', new THREE.BufferAttribute(scales, 1)); - - const material = new THREE.ShaderMaterial({ - uniforms: { - color: { value: new THREE.Color(0xffffff) }, - }, - vertexShader: document.getElementById('vertexshader').textContent, - fragmentShader: document.getElementById('fragmentshader').textContent, - }); - - // - - particles = new THREE.Points(geometry, material); - scene.add(particles); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - stats = new Stats(); - container.appendChild(stats.dom); - - container.style.touchAction = 'none'; - container.addEventListener('pointermove', onPointerMove); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - windowHalfX = window.innerWidth / 2; - windowHalfY = window.innerHeight / 2; - - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function onPointerMove(event) { - if (event.isPrimary === false) return; - - mouseX = event.clientX - windowHalfX; - mouseY = event.clientY - windowHalfY; -} - -// - -function animate() { - render(); - stats.update(); -} - -function render() { - camera.position.x += (mouseX - camera.position.x) * 0.05; - camera.position.y += (-mouseY - camera.position.y) * 0.05; - camera.lookAt(scene.position); - - const positions = particles.geometry.attributes.position.array; - const scales = particles.geometry.attributes.scale.array; - - let i = 0, - j = 0; - - for (let ix = 0; ix < AMOUNTX; ix++) { - for (let iy = 0; iy < AMOUNTY; iy++) { - positions[i + 1] = Math.sin((ix + count) * 0.3) * 50 + Math.sin((iy + count) * 0.5) * 50; - - scales[j] = (Math.sin((ix + count) * 0.3) + 1) * 20 + (Math.sin((iy + count) * 0.5) + 1) * 20; - - i += 3; - j++; - } - } - - particles.geometry.attributes.position.needsUpdate = true; - particles.geometry.attributes.scale.needsUpdate = true; - - renderer.render(scene, camera); - - count += 0.1; -} diff --git a/examples-testing/examples/webgl_portal.ts b/examples-testing/examples/webgl_portal.ts deleted file mode 100644 index 4bc59593f..000000000 --- a/examples-testing/examples/webgl_portal.ts +++ /dev/null @@ -1,218 +0,0 @@ -import * as THREE from 'three'; - -import * as CameraUtils from 'three/addons/utils/CameraUtils.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let camera, scene, renderer; - -let cameraControls; - -let smallSphereOne, smallSphereTwo; - -let portalCamera, - leftPortal, - rightPortal, - leftPortalTexture, - reflectedPosition, - rightPortalTexture, - bottomLeftCorner, - bottomRightCorner, - topLeftCorner; - -init(); - -function init() { - const container = document.getElementById('container'); - - // renderer - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.localClippingEnabled = true; - renderer.toneMapping = THREE.ACESFilmicToneMapping; - container.appendChild(renderer.domElement); - - // scene - scene = new THREE.Scene(); - - // camera - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 5000); - camera.position.set(0, 75, 160); - - cameraControls = new OrbitControls(camera, renderer.domElement); - cameraControls.target.set(0, 40, 0); - cameraControls.maxDistance = 400; - cameraControls.minDistance = 10; - cameraControls.update(); - - // - - const planeGeo = new THREE.PlaneGeometry(100.1, 100.1); - - // bouncing icosphere - const portalPlane = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0.0); - const geometry = new THREE.IcosahedronGeometry(5, 0); - const material = new THREE.MeshPhongMaterial({ - color: 0xffffff, - emissive: 0x333333, - flatShading: true, - clippingPlanes: [portalPlane], - clipShadows: true, - }); - smallSphereOne = new THREE.Mesh(geometry, material); - scene.add(smallSphereOne); - smallSphereTwo = new THREE.Mesh(geometry, material); - scene.add(smallSphereTwo); - - // portals - portalCamera = new THREE.PerspectiveCamera(45, 1.0, 0.1, 500.0); - scene.add(portalCamera); - //frustumHelper = new THREE.CameraHelper( portalCamera ); - //scene.add( frustumHelper ); - bottomLeftCorner = new THREE.Vector3(); - bottomRightCorner = new THREE.Vector3(); - topLeftCorner = new THREE.Vector3(); - reflectedPosition = new THREE.Vector3(); - - leftPortalTexture = new THREE.WebGLRenderTarget(256, 256); - leftPortal = new THREE.Mesh(planeGeo, new THREE.MeshBasicMaterial({ map: leftPortalTexture.texture })); - leftPortal.position.x = -30; - leftPortal.position.y = 20; - leftPortal.scale.set(0.35, 0.35, 0.35); - scene.add(leftPortal); - - rightPortalTexture = new THREE.WebGLRenderTarget(256, 256); - rightPortal = new THREE.Mesh(planeGeo, new THREE.MeshBasicMaterial({ map: rightPortalTexture.texture })); - rightPortal.position.x = 30; - rightPortal.position.y = 20; - rightPortal.scale.set(0.35, 0.35, 0.35); - scene.add(rightPortal); - - // walls - const planeTop = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xffffff })); - planeTop.position.y = 100; - planeTop.rotateX(Math.PI / 2); - scene.add(planeTop); - - const planeBottom = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xffffff })); - planeBottom.rotateX(-Math.PI / 2); - scene.add(planeBottom); - - const planeFront = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0x7f7fff })); - planeFront.position.z = 50; - planeFront.position.y = 50; - planeFront.rotateY(Math.PI); - scene.add(planeFront); - - const planeBack = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xff7fff })); - planeBack.position.z = -50; - planeBack.position.y = 50; - //planeBack.rotateY( Math.PI ); - scene.add(planeBack); - - const planeRight = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0x00ff00 })); - planeRight.position.x = 50; - planeRight.position.y = 50; - planeRight.rotateY(-Math.PI / 2); - scene.add(planeRight); - - const planeLeft = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xff0000 })); - planeLeft.position.x = -50; - planeLeft.position.y = 50; - planeLeft.rotateY(Math.PI / 2); - scene.add(planeLeft); - - // lights - const mainLight = new THREE.PointLight(0xe7e7e7, 2.5, 250, 0); - mainLight.position.y = 60; - scene.add(mainLight); - - const greenLight = new THREE.PointLight(0x00ff00, 0.5, 1000, 0); - greenLight.position.set(550, 50, 0); - scene.add(greenLight); - - const redLight = new THREE.PointLight(0xff0000, 0.5, 1000, 0); - redLight.position.set(-550, 50, 0); - scene.add(redLight); - - const blueLight = new THREE.PointLight(0xbbbbfe, 0.5, 1000, 0); - blueLight.position.set(0, 50, 550); - scene.add(blueLight); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function renderPortal(thisPortalMesh, otherPortalMesh, thisPortalTexture) { - // set the portal camera position to be reflected about the portal plane - thisPortalMesh.worldToLocal(reflectedPosition.copy(camera.position)); - reflectedPosition.x *= -1.0; - reflectedPosition.z *= -1.0; - otherPortalMesh.localToWorld(reflectedPosition); - portalCamera.position.copy(reflectedPosition); - - // grab the corners of the other portal - // - note: the portal is viewed backwards; flip the left/right coordinates - otherPortalMesh.localToWorld(bottomLeftCorner.set(50.05, -50.05, 0.0)); - otherPortalMesh.localToWorld(bottomRightCorner.set(-50.05, -50.05, 0.0)); - otherPortalMesh.localToWorld(topLeftCorner.set(50.05, 50.05, 0.0)); - // set the projection matrix to encompass the portal's frame - CameraUtils.frameCorners(portalCamera, bottomLeftCorner, bottomRightCorner, topLeftCorner, false); - - // render the portal - thisPortalTexture.texture.colorSpace = renderer.outputColorSpace; - renderer.setRenderTarget(thisPortalTexture); - renderer.state.buffers.depth.setMask(true); // make sure the depth buffer is writable so it can be properly cleared, see #18897 - if (renderer.autoClear === false) renderer.clear(); - thisPortalMesh.visible = false; // hide this portal from its own rendering - renderer.render(scene, portalCamera); - thisPortalMesh.visible = true; // re-enable this portal's visibility for general rendering -} - -function animate() { - // move the bouncing sphere(s) - const timerOne = Date.now() * 0.01; - const timerTwo = timerOne + Math.PI * 10.0; - - smallSphereOne.position.set( - Math.cos(timerOne * 0.1) * 30, - Math.abs(Math.cos(timerOne * 0.2)) * 20 + 5, - Math.sin(timerOne * 0.1) * 30, - ); - smallSphereOne.rotation.y = Math.PI / 2 - timerOne * 0.1; - smallSphereOne.rotation.z = timerOne * 0.8; - - smallSphereTwo.position.set( - Math.cos(timerTwo * 0.1) * 30, - Math.abs(Math.cos(timerTwo * 0.2)) * 20 + 5, - Math.sin(timerTwo * 0.1) * 30, - ); - smallSphereTwo.rotation.y = Math.PI / 2 - timerTwo * 0.1; - smallSphereTwo.rotation.z = timerTwo * 0.8; - - // save the original camera properties - const currentRenderTarget = renderer.getRenderTarget(); - const currentXrEnabled = renderer.xr.enabled; - const currentShadowAutoUpdate = renderer.shadowMap.autoUpdate; - renderer.xr.enabled = false; // Avoid camera modification - renderer.shadowMap.autoUpdate = false; // Avoid re-computing shadows - - // render the portal effect - renderPortal(leftPortal, rightPortal, leftPortalTexture); - renderPortal(rightPortal, leftPortal, rightPortalTexture); - - // restore the original rendering properties - renderer.xr.enabled = currentXrEnabled; - renderer.shadowMap.autoUpdate = currentShadowAutoUpdate; - renderer.setRenderTarget(currentRenderTarget); - - // render the main scene - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_postprocessing.ts b/examples-testing/examples/webgl_postprocessing.ts deleted file mode 100644 index ecc9b28ee..000000000 --- a/examples-testing/examples/webgl_postprocessing.ts +++ /dev/null @@ -1,86 +0,0 @@ -import * as THREE from 'three'; - -import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; -import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; -import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js'; - -import { RGBShiftShader } from 'three/addons/shaders/RGBShiftShader.js'; -import { DotScreenShader } from 'three/addons/shaders/DotScreenShader.js'; -import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; - -let camera, renderer, composer; -let object; - -init(); - -function init() { - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // - - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.z = 400; - - const scene = new THREE.Scene(); - scene.fog = new THREE.Fog(0x000000, 1, 1000); - - object = new THREE.Object3D(); - scene.add(object); - - const geometry = new THREE.SphereGeometry(1, 4, 4); - const material = new THREE.MeshPhongMaterial({ color: 0xffffff, flatShading: true }); - - for (let i = 0; i < 100; i++) { - const mesh = new THREE.Mesh(geometry, material); - mesh.position.set(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5).normalize(); - mesh.position.multiplyScalar(Math.random() * 400); - mesh.rotation.set(Math.random() * 2, Math.random() * 2, Math.random() * 2); - mesh.scale.x = mesh.scale.y = mesh.scale.z = Math.random() * 50; - object.add(mesh); - } - - scene.add(new THREE.AmbientLight(0xcccccc)); - - const light = new THREE.DirectionalLight(0xffffff, 3); - light.position.set(1, 1, 1); - scene.add(light); - - // postprocessing - - composer = new EffectComposer(renderer); - composer.addPass(new RenderPass(scene, camera)); - - const effect1 = new ShaderPass(DotScreenShader); - effect1.uniforms['scale'].value = 4; - composer.addPass(effect1); - - const effect2 = new ShaderPass(RGBShiftShader); - effect2.uniforms['amount'].value = 0.0015; - composer.addPass(effect2); - - const effect3 = new OutputPass(); - composer.addPass(effect3); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - composer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - object.rotation.x += 0.005; - object.rotation.y += 0.01; - - composer.render(); -} diff --git a/examples-testing/examples/webgl_postprocessing_advanced.ts b/examples-testing/examples/webgl_postprocessing_advanced.ts deleted file mode 100644 index 82fc39be3..000000000 --- a/examples-testing/examples/webgl_postprocessing_advanced.ts +++ /dev/null @@ -1,304 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; -import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; -import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js'; -import { BloomPass } from 'three/addons/postprocessing/BloomPass.js'; -import { FilmPass } from 'three/addons/postprocessing/FilmPass.js'; -import { DotScreenPass } from 'three/addons/postprocessing/DotScreenPass.js'; -import { MaskPass, ClearMaskPass } from 'three/addons/postprocessing/MaskPass.js'; -import { TexturePass } from 'three/addons/postprocessing/TexturePass.js'; - -import { BleachBypassShader } from 'three/addons/shaders/BleachBypassShader.js'; -import { ColorifyShader } from 'three/addons/shaders/ColorifyShader.js'; -import { HorizontalBlurShader } from 'three/addons/shaders/HorizontalBlurShader.js'; -import { VerticalBlurShader } from 'three/addons/shaders/VerticalBlurShader.js'; -import { SepiaShader } from 'three/addons/shaders/SepiaShader.js'; -import { VignetteShader } from 'three/addons/shaders/VignetteShader.js'; -import { GammaCorrectionShader } from 'three/addons/shaders/GammaCorrectionShader.js'; - -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -let container, stats; - -let composerScene, composer1, composer2, composer3, composer4; - -let cameraOrtho, cameraPerspective, sceneModel, sceneBG, renderer, mesh, directionalLight; - -const width = window.innerWidth || 2; -const height = window.innerHeight || 2; - -let halfWidth = width / 2; -let halfHeight = height / 2; - -let quadBG, quadMask, renderScene; - -const delta = 0.01; - -init(); - -function init() { - container = document.getElementById('container'); - - // - - cameraOrtho = new THREE.OrthographicCamera(-halfWidth, halfWidth, halfHeight, -halfHeight, -10000, 10000); - cameraOrtho.position.z = 100; - - cameraPerspective = new THREE.PerspectiveCamera(50, width / height, 1, 10000); - cameraPerspective.position.z = 900; - - // - - sceneModel = new THREE.Scene(); - sceneBG = new THREE.Scene(); - - // - - directionalLight = new THREE.DirectionalLight(0xffffff, 3); - directionalLight.position.set(0, -0.1, 1).normalize(); - sceneModel.add(directionalLight); - - const loader = new GLTFLoader(); - loader.load('models/gltf/LeePerrySmith/LeePerrySmith.glb', function (gltf) { - createMesh(gltf.scene.children[0].geometry, sceneModel, 100); - }); - - // - - const diffuseMap = new THREE.TextureLoader().load('textures/cube/SwedishRoyalCastle/pz.jpg'); - diffuseMap.colorSpace = THREE.SRGBColorSpace; - - const materialColor = new THREE.MeshBasicMaterial({ - map: diffuseMap, - depthTest: false, - }); - - quadBG = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), materialColor); - quadBG.position.z = -500; - quadBG.scale.set(width, height, 1); - sceneBG.add(quadBG); - - // - - const sceneMask = new THREE.Scene(); - - quadMask = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), new THREE.MeshBasicMaterial({ color: 0xffaa00 })); - quadMask.position.z = -300; - quadMask.scale.set(width / 2, height / 2, 1); - sceneMask.add(quadMask); - - // - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(width, height); - renderer.setAnimationLoop(animate); - renderer.autoClear = false; - - // - - container.appendChild(renderer.domElement); - - // - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - const shaderBleach = BleachBypassShader; - const shaderSepia = SepiaShader; - const shaderVignette = VignetteShader; - - const effectBleach = new ShaderPass(shaderBleach); - const effectSepia = new ShaderPass(shaderSepia); - const effectVignette = new ShaderPass(shaderVignette); - const gammaCorrection = new ShaderPass(GammaCorrectionShader); - - effectBleach.uniforms['opacity'].value = 0.95; - - effectSepia.uniforms['amount'].value = 0.9; - - effectVignette.uniforms['offset'].value = 1.6; - effectVignette.uniforms['darkness'].value = 0.95; - - const effectBloom = new BloomPass(0.5); - const effectFilm = new FilmPass(0.35); - const effectFilmBW = new FilmPass(0.35, true); - const effectDotScreen = new DotScreenPass(new THREE.Vector2(0, 0), 0.5, 0.8); - - const effectHBlur = new ShaderPass(HorizontalBlurShader); - const effectVBlur = new ShaderPass(VerticalBlurShader); - effectHBlur.uniforms['h'].value = 2 / (width / 2); - effectVBlur.uniforms['v'].value = 2 / (height / 2); - - const effectColorify1 = new ShaderPass(ColorifyShader); - const effectColorify2 = new ShaderPass(ColorifyShader); - effectColorify1.uniforms['color'] = new THREE.Uniform(new THREE.Color(1, 0.8, 0.8)); - effectColorify2.uniforms['color'] = new THREE.Uniform(new THREE.Color(1, 0.75, 0.5)); - - const clearMask = new ClearMaskPass(); - const renderMask = new MaskPass(sceneModel, cameraPerspective); - const renderMaskInverse = new MaskPass(sceneModel, cameraPerspective); - - renderMaskInverse.inverse = true; - - // - - const rtParameters = { - stencilBuffer: true, - }; - - const rtWidth = width / 2; - const rtHeight = height / 2; - - // - - const renderBackground = new RenderPass(sceneBG, cameraOrtho); - const renderModel = new RenderPass(sceneModel, cameraPerspective); - - renderModel.clear = false; - - composerScene = new EffectComposer(renderer, new THREE.WebGLRenderTarget(rtWidth * 2, rtHeight * 2, rtParameters)); - - composerScene.addPass(renderBackground); - composerScene.addPass(renderModel); - composerScene.addPass(renderMaskInverse); - composerScene.addPass(effectHBlur); - composerScene.addPass(effectVBlur); - composerScene.addPass(clearMask); - - // - - renderScene = new TexturePass(composerScene.renderTarget2.texture); - - // - - composer1 = new EffectComposer(renderer, new THREE.WebGLRenderTarget(rtWidth, rtHeight, rtParameters)); - - composer1.addPass(renderScene); - composer1.addPass(gammaCorrection); - composer1.addPass(effectFilmBW); - composer1.addPass(effectVignette); - - // - - composer2 = new EffectComposer(renderer, new THREE.WebGLRenderTarget(rtWidth, rtHeight, rtParameters)); - - composer2.addPass(renderScene); - composer2.addPass(gammaCorrection); - composer2.addPass(effectDotScreen); - composer2.addPass(renderMask); - composer2.addPass(effectColorify1); - composer2.addPass(clearMask); - composer2.addPass(renderMaskInverse); - composer2.addPass(effectColorify2); - composer2.addPass(clearMask); - composer2.addPass(effectVignette); - - // - - composer3 = new EffectComposer(renderer, new THREE.WebGLRenderTarget(rtWidth, rtHeight, rtParameters)); - - composer3.addPass(renderScene); - composer3.addPass(gammaCorrection); - composer3.addPass(effectSepia); - composer3.addPass(effectFilm); - composer3.addPass(effectVignette); - - // - - composer4 = new EffectComposer(renderer, new THREE.WebGLRenderTarget(rtWidth, rtHeight, rtParameters)); - - composer4.addPass(renderScene); - composer4.addPass(gammaCorrection); - composer4.addPass(effectBloom); - composer4.addPass(effectFilm); - composer4.addPass(effectBleach); - composer4.addPass(effectVignette); - - renderScene.uniforms['tDiffuse'].value = composerScene.renderTarget2.texture; - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - halfWidth = window.innerWidth / 2; - halfHeight = window.innerHeight / 2; - - cameraPerspective.aspect = window.innerWidth / window.innerHeight; - cameraPerspective.updateProjectionMatrix(); - - cameraOrtho.left = -halfWidth; - cameraOrtho.right = halfWidth; - cameraOrtho.top = halfHeight; - cameraOrtho.bottom = -halfHeight; - - cameraOrtho.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - composerScene.setSize(halfWidth * 2, halfHeight * 2); - - composer1.setSize(halfWidth, halfHeight); - composer2.setSize(halfWidth, halfHeight); - composer3.setSize(halfWidth, halfHeight); - composer4.setSize(halfWidth, halfHeight); - - renderScene.uniforms['tDiffuse'].value = composerScene.renderTarget2.texture; - - quadBG.scale.set(window.innerWidth, window.innerHeight, 1); - quadMask.scale.set(window.innerWidth / 2, window.innerHeight / 2, 1); -} - -function createMesh(geometry, scene, scale) { - const diffuseMap = new THREE.TextureLoader().load('models/gltf/LeePerrySmith/Map-COL.jpg'); - diffuseMap.colorSpace = THREE.SRGBColorSpace; - - const mat2 = new THREE.MeshPhongMaterial({ - color: 0xcbcbcb, - specular: 0x080808, - shininess: 20, - map: diffuseMap, - normalMap: new THREE.TextureLoader().load('models/gltf/LeePerrySmith/Infinite-Level_02_Tangent_SmoothUV.jpg'), - normalScale: new THREE.Vector2(0.75, 0.75), - }); - - mesh = new THREE.Mesh(geometry, mat2); - mesh.position.set(0, -50, 0); - mesh.scale.set(scale, scale, scale); - - scene.add(mesh); -} - -// - -function animate() { - stats.begin(); - render(); - stats.end(); -} - -function render() { - const time = Date.now() * 0.0004; - - if (mesh) mesh.rotation.y = -time; - - renderer.setViewport(0, 0, halfWidth, halfHeight); - composerScene.render(delta); - - renderer.setViewport(0, 0, halfWidth, halfHeight); - composer1.render(delta); - - renderer.setViewport(halfWidth, 0, halfWidth, halfHeight); - composer2.render(delta); - - renderer.setViewport(0, halfHeight, halfWidth, halfHeight); - composer3.render(delta); - - renderer.setViewport(halfWidth, halfHeight, halfWidth, halfHeight); - composer4.render(delta); -} diff --git a/examples-testing/examples/webgl_postprocessing_afterimage.ts b/examples-testing/examples/webgl_postprocessing_afterimage.ts deleted file mode 100644 index 97353dcd2..000000000 --- a/examples-testing/examples/webgl_postprocessing_afterimage.ts +++ /dev/null @@ -1,72 +0,0 @@ -import * as THREE from 'three'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; -import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; -import { AfterimagePass } from 'three/addons/postprocessing/AfterimagePass.js'; -import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; - -let camera, scene, renderer, composer; -let mesh; - -let afterimagePass; - -const params = { - enable: true, -}; - -init(); - -function init() { - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.z = 400; - - scene = new THREE.Scene(); - scene.fog = new THREE.Fog(0x000000, 1, 1000); - - const geometry = new THREE.BoxGeometry(150, 150, 150, 2, 2, 2); - const material = new THREE.MeshNormalMaterial(); - mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - // postprocessing - - composer = new EffectComposer(renderer); - composer.addPass(new RenderPass(scene, camera)); - - afterimagePass = new AfterimagePass(); - composer.addPass(afterimagePass); - - const outputPass = new OutputPass(); - composer.addPass(outputPass); - - window.addEventListener('resize', onWindowResize); - - const gui = new GUI({ title: 'Damp setting' }); - gui.add(afterimagePass, 'damp', 0, 1).step(0.001); - gui.add(params, 'enable'); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - composer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - mesh.rotation.x += 0.005; - mesh.rotation.y += 0.01; - - afterimagePass.enabled = params.enable; - - composer.render(); -} diff --git a/examples-testing/examples/webgl_postprocessing_backgrounds.ts b/examples-testing/examples/webgl_postprocessing_backgrounds.ts deleted file mode 100644 index 57a6a2dbd..000000000 --- a/examples-testing/examples/webgl_postprocessing_backgrounds.ts +++ /dev/null @@ -1,214 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; -import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; -import { TexturePass } from 'three/addons/postprocessing/TexturePass.js'; -import { CubeTexturePass } from 'three/addons/postprocessing/CubeTexturePass.js'; -import { ClearPass } from 'three/addons/postprocessing/ClearPass.js'; -import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let scene, renderer, composer; -let clearPass, texturePass, renderPass; -let cameraP, cubeTexturePassP; -let gui, stats; - -const params = { - clearPass: true, - clearColor: 'white', - clearAlpha: 1.0, - - texturePass: true, - texturePassOpacity: 1.0, - - cubeTexturePass: true, - cubeTexturePassOpacity: 1.0, - - renderPass: true, -}; - -init(); - -clearGui(); - -function clearGui() { - if (gui) gui.destroy(); - - gui = new GUI(); - - gui.add(params, 'clearPass'); - gui.add(params, 'clearColor', ['black', 'white', 'blue', 'green', 'red']); - gui.add(params, 'clearAlpha', 0, 1); - - gui.add(params, 'texturePass'); - gui.add(params, 'texturePassOpacity', 0, 1); - - gui.add(params, 'cubeTexturePass'); - gui.add(params, 'cubeTexturePassOpacity', 0, 1); - - gui.add(params, 'renderPass'); - - gui.open(); -} - -function init() { - const container = document.getElementById('container'); - - const width = window.innerWidth || 1; - const height = window.innerHeight || 1; - const aspect = width / height; - const devicePixelRatio = window.devicePixelRatio || 1; - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(devicePixelRatio); - renderer.setSize(width, height); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - cameraP = new THREE.PerspectiveCamera(65, aspect, 1, 10); - cameraP.position.z = 7; - - scene = new THREE.Scene(); - - const group = new THREE.Group(); - scene.add(group); - - const light = new THREE.PointLight(0xefffef, 500); - light.position.z = 10; - light.position.y = -10; - light.position.x = -10; - scene.add(light); - - const light2 = new THREE.PointLight(0xffefef, 500); - light2.position.z = 10; - light2.position.x = -10; - light2.position.y = 10; - scene.add(light2); - - const light3 = new THREE.PointLight(0xefefff, 500); - light3.position.z = 10; - light3.position.x = 10; - light3.position.y = -10; - scene.add(light3); - - const geometry = new THREE.SphereGeometry(1, 48, 24); - - const material = new THREE.MeshStandardMaterial(); - material.roughness = 0.5 * Math.random() + 0.25; - material.metalness = 0; - material.color.setHSL(Math.random(), 1.0, 0.3); - - const mesh = new THREE.Mesh(geometry, material); - group.add(mesh); - - // postprocessing - - const genCubeUrls = function (prefix, postfix) { - return [ - prefix + 'px' + postfix, - prefix + 'nx' + postfix, - prefix + 'py' + postfix, - prefix + 'ny' + postfix, - prefix + 'pz' + postfix, - prefix + 'nz' + postfix, - ]; - }; - - composer = new EffectComposer(renderer); - - clearPass = new ClearPass(params.clearColor, params.clearAlpha); - composer.addPass(clearPass); - - texturePass = new TexturePass(); - composer.addPass(texturePass); - - const textureLoader = new THREE.TextureLoader(); - textureLoader.load('textures/hardwood2_diffuse.jpg', function (map) { - map.colorSpace = THREE.SRGBColorSpace; - texturePass.map = map; - }); - - cubeTexturePassP = null; - - const ldrUrls = genCubeUrls('textures/cube/pisa/', '.png'); - new THREE.CubeTextureLoader().load(ldrUrls, function (ldrCubeMap) { - cubeTexturePassP = new CubeTexturePass(cameraP, ldrCubeMap); - composer.insertPass(cubeTexturePassP, 2); - }); - - renderPass = new RenderPass(scene, cameraP); - renderPass.clear = false; - composer.addPass(renderPass); - - const outputPass = new OutputPass(); - composer.addPass(outputPass); - - const controls = new OrbitControls(cameraP, renderer.domElement); - controls.enableZoom = false; - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - const aspect = width / height; - - cameraP.aspect = aspect; - cameraP.updateProjectionMatrix(); - - renderer.setSize(width, height); - composer.setSize(width, height); -} - -function animate() { - stats.begin(); - - cameraP.updateMatrixWorld(true); - - let newColor = clearPass.clearColor; - - switch (params.clearColor) { - case 'blue': - newColor = 0x0000ff; - break; - case 'red': - newColor = 0xff0000; - break; - case 'green': - newColor = 0x00ff00; - break; - case 'white': - newColor = 0xffffff; - break; - case 'black': - newColor = 0x000000; - break; - } - - clearPass.enabled = params.clearPass; - clearPass.clearColor = newColor; - clearPass.clearAlpha = params.clearAlpha; - - texturePass.enabled = params.texturePass; - texturePass.opacity = params.texturePassOpacity; - - if (cubeTexturePassP !== null) { - cubeTexturePassP.enabled = params.cubeTexturePass; - cubeTexturePassP.opacity = params.cubeTexturePassOpacity; - } - - renderPass.enabled = params.renderPass; - - composer.render(); - - stats.end(); -} diff --git a/examples-testing/examples/webgl_postprocessing_fxaa.ts b/examples-testing/examples/webgl_postprocessing_fxaa.ts deleted file mode 100644 index c5e632ad7..000000000 --- a/examples-testing/examples/webgl_postprocessing_fxaa.ts +++ /dev/null @@ -1,122 +0,0 @@ -import * as THREE from 'three'; - -import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; -import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; -import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; -import { FXAAPass } from 'three/addons/postprocessing/FXAAPass.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let camera, scene, renderer, controls, container; - -let composer1, composer2, fxaaPass; - -init(); - -function init() { - container = document.getElementById('container'); - - camera = new THREE.PerspectiveCamera(45, container.offsetWidth / container.offsetHeight, 1, 2000); - camera.position.z = 500; - - scene = new THREE.Scene(); - - // - - const hemiLight = new THREE.HemisphereLight(0xffffff, 0x8d8d8d); - hemiLight.position.set(0, 1000, 0); - scene.add(hemiLight); - - const dirLight = new THREE.DirectionalLight(0xffffff, 3); - dirLight.position.set(-3000, 1000, -1000); - scene.add(dirLight); - - // - - const geometry = new THREE.TetrahedronGeometry(10); - const material = new THREE.MeshStandardMaterial({ color: 0xf73232, flatShading: true }); - - const mesh = new THREE.InstancedMesh(geometry, material, 100); - const dummy = new THREE.Object3D(); - - for (let i = 0; i < 100; i++) { - dummy.position.x = Math.random() * 500 - 250; - dummy.position.y = Math.random() * 500 - 250; - dummy.position.z = Math.random() * 500 - 250; - - dummy.scale.setScalar(Math.random() * 2 + 1); - - dummy.rotation.x = Math.random() * Math.PI; - dummy.rotation.y = Math.random() * Math.PI; - dummy.rotation.z = Math.random() * Math.PI; - - dummy.updateMatrix(); - mesh.setMatrixAt(i, dummy.matrix); - } - - scene.add(mesh); - - // - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(container.offsetWidth, container.offsetHeight); - renderer.setAnimationLoop(animate); - renderer.autoClear = false; - controls = new OrbitControls(camera, renderer.domElement); - controls.autoRotate = true; - container.appendChild(renderer.domElement); - - // - - const renderPass = new RenderPass(scene, camera); - renderPass.clearAlpha = 0; - - // - - fxaaPass = new FXAAPass(); - - const outputPass = new OutputPass(); - - composer1 = new EffectComposer(renderer); - composer1.addPass(renderPass); - composer1.addPass(outputPass); - - // - - composer2 = new EffectComposer(renderer); - composer2.addPass(renderPass); - composer2.addPass(outputPass); - - // FXAA is engineered to be applied towards the end of engine post processing after conversion to low dynamic range and conversion to the sRGB color space for display. - - composer2.addPass(fxaaPass); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = container.offsetWidth / container.offsetHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(container.offsetWidth, container.offsetHeight); - composer1.setSize(container.offsetWidth, container.offsetHeight); - composer2.setSize(container.offsetWidth, container.offsetHeight); -} - -function animate() { - const halfWidth = container.offsetWidth / 2; - - controls.update(); - - renderer.setScissorTest(true); - - renderer.setScissor(0, 0, halfWidth - 1, container.offsetHeight); - composer1.render(); - - renderer.setScissor(halfWidth, 0, halfWidth, container.offsetHeight); - composer2.render(); - - renderer.setScissorTest(false); -} diff --git a/examples-testing/examples/webgl_postprocessing_glitch.ts b/examples-testing/examples/webgl_postprocessing_glitch.ts deleted file mode 100644 index 02acda572..000000000 --- a/examples-testing/examples/webgl_postprocessing_glitch.ts +++ /dev/null @@ -1,108 +0,0 @@ -import * as THREE from 'three'; - -import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; -import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; -import { GlitchPass } from 'three/addons/postprocessing/GlitchPass.js'; -import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; - -let camera, scene, renderer, composer; -let object, light; - -let glitchPass; - -const button = document.querySelector('#startButton'); -button.addEventListener('click', function () { - const overlay = document.getElementById('overlay'); - overlay.remove(); - - init(); -}); - -function updateOptions() { - const wildGlitch = document.getElementById('wildGlitch'); - glitchPass.goWild = wildGlitch.checked; -} - -function init() { - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // - - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.z = 400; - - scene = new THREE.Scene(); - scene.fog = new THREE.Fog(0x000000, 1, 1000); - - object = new THREE.Object3D(); - scene.add(object); - - const geometry = new THREE.SphereGeometry(1, 4, 4); - const material = new THREE.MeshPhongMaterial({ flatShading: true }); - - const mesh = new THREE.InstancedMesh(geometry, material, 100); - const dummy = new THREE.Object3D(); - const color = new THREE.Color(); - - for (let i = 0; i < 100; i++) { - dummy.position.set(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5).normalize(); - dummy.position.multiplyScalar(Math.random() * 400); - dummy.rotation.set(Math.random() * 2, Math.random() * 2, Math.random() * 2); - - const scale = Math.random() * 50; - dummy.scale.set(scale, scale, scale); - - dummy.updateMatrix(); - mesh.setMatrixAt(i, dummy.matrix); - - color.setHex(0xffffff * Math.random()); - mesh.setColorAt(i, color); - } - - object.add(mesh); - - scene.add(new THREE.AmbientLight(0xcccccc)); - - light = new THREE.DirectionalLight(0xffffff, 3); - light.position.set(1, 1, 1); - scene.add(light); - - // postprocessing - - composer = new EffectComposer(renderer); - composer.addPass(new RenderPass(scene, camera)); - - glitchPass = new GlitchPass(); - composer.addPass(glitchPass); - - const outputPass = new OutputPass(); - composer.addPass(outputPass); - - // - - window.addEventListener('resize', onWindowResize); - - const wildGlitchOption = document.getElementById('wildGlitch'); - wildGlitchOption.addEventListener('change', updateOptions); - - updateOptions(); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - composer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - object.rotation.x += 0.005; - object.rotation.y += 0.01; - - composer.render(); -} diff --git a/examples-testing/examples/webgl_postprocessing_godrays.ts b/examples-testing/examples/webgl_postprocessing_godrays.ts deleted file mode 100644 index b7c2c6661..000000000 --- a/examples-testing/examples/webgl_postprocessing_godrays.ts +++ /dev/null @@ -1,176 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { EffectComposer, RenderPass } from 'postprocessing'; -import { GodraysPass } from 'goodrays'; - -let camera, scene, renderer, composer; -let controls, stats; - -init(); - -async function init() { - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000); - camera.position.set(-175, 50, 0); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x000000); - - // asset - - const loader = new GLTFLoader(); - const gltf = await loader.loadAsync('models/gltf/godrays_demo.glb'); - scene.add(gltf.scene); - - const pillars = gltf.scene.getObjectByName('concrete'); - pillars.material = new THREE.MeshStandardMaterial({ - color: 0x333333, - }); - - const base = gltf.scene.getObjectByName('base'); - base.material = new THREE.MeshStandardMaterial({ - color: 0x333333, - side: THREE.DoubleSide, - }); - - setupBackdrop(); - - // lights - - const lightPos = new THREE.Vector3(0, 50, 0); - const lightSphereMaterial = new THREE.MeshBasicMaterial({ - color: 0xffffff, - }); - const lightSphere = new THREE.Mesh(new THREE.SphereGeometry(0.5, 16, 16), lightSphereMaterial); - lightSphere.position.copy(lightPos); - scene.add(lightSphere); - - scene.add(new THREE.AmbientLight(0xcccccc, 0.4)); - - const pointLight = new THREE.PointLight(0xf6287d, 10000); - pointLight.castShadow = true; - pointLight.shadow.bias = -0.001; - pointLight.shadow.mapSize.width = 1024; - pointLight.shadow.mapSize.height = 1024; - pointLight.position.copy(lightPos); - scene.add(pointLight); - - // shadow setup - - scene.traverse(obj => { - if (obj.isMesh === true) { - obj.castShadow = true; - obj.receiveShadow = true; - } - }); - - lightSphere.castShadow = false; - lightSphere.receiveShadow = false; - - // - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.shadowMap.enabled = true; - document.body.appendChild(renderer.domElement); - - // - - composer = new EffectComposer(renderer, { frameBufferType: THREE.HalfFloatType }); - - const renderPass = new RenderPass(scene, camera); - composer.addPass(renderPass); - - const params = { - density: 1 / 128, - maxDensity: 0.5, - edgeStrength: 2, - edgeRadius: 2, - distanceAttenuation: 2, - color: new THREE.Color(0xf6287d), - raymarchSteps: 60, - blur: true, - gammaCorrection: true, - }; - - const godraysPass = new GodraysPass(pointLight, camera, params); - godraysPass.renderToScreen = true; - composer.addPass(godraysPass); - - // - - controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, 0.5, 0); - controls.enableDamping = true; - controls.maxDistance = 200; - controls.update(); - - // - - stats = new Stats(); - document.body.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); -} - -// - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function setupBackdrop() { - const backdropDistance = 200; - // Add backdrop walls `backdropDistance` units away from the origin - const backdropGeometry = new THREE.PlaneGeometry(400, 200); - const backdropMaterial = new THREE.MeshBasicMaterial({ - color: 0x200808, - side: THREE.DoubleSide, - }); - const backdropLeft = new THREE.Mesh(backdropGeometry, backdropMaterial); - backdropLeft.position.set(-backdropDistance, 100, 0); - backdropLeft.rotateY(Math.PI / 2); - scene.add(backdropLeft); - - const backdropRight = new THREE.Mesh(backdropGeometry, backdropMaterial); - backdropRight.position.set(backdropDistance, 100, 0); - backdropRight.rotateY(Math.PI / 2); - scene.add(backdropRight); - - const backdropFront = new THREE.Mesh(backdropGeometry, backdropMaterial); - backdropFront.position.set(0, 100, -backdropDistance); - scene.add(backdropFront); - - const backdropBack = new THREE.Mesh(backdropGeometry, backdropMaterial); - backdropBack.position.set(0, 100, backdropDistance); - scene.add(backdropBack); - - const backdropTop = new THREE.Mesh(backdropGeometry, backdropMaterial); - backdropTop.position.set(0, 200, 0); - backdropTop.rotateX(Math.PI / 2); - backdropTop.scale.set(3, 6, 1); - scene.add(backdropTop); -} - -function animate() { - controls.update(); - - stats.begin(); - - composer.render(); - - //renderer.render( scene, camera ); - - stats.end(); -} diff --git a/examples-testing/examples/webgl_postprocessing_gtao.ts b/examples-testing/examples/webgl_postprocessing_gtao.ts deleted file mode 100644 index a37d3041b..000000000 --- a/examples-testing/examples/webgl_postprocessing_gtao.ts +++ /dev/null @@ -1,218 +0,0 @@ -import * as THREE from 'three'; -import Stats from 'three/addons/libs/stats.module.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; -import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; -import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; -import { GTAOPass } from 'three/addons/postprocessing/GTAOPass.js'; -import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; - -let camera, scene, renderer, composer, controls, timer, stats, mixer; - -init(); - -function init() { - const dracoLoader = new DRACOLoader(); - dracoLoader.setDecoderPath('jsm/libs/draco/'); - dracoLoader.setDecoderConfig({ type: 'js' }); - const loader = new GLTFLoader(); - loader.setDRACOLoader(dracoLoader); - loader.setPath('models/gltf/'); - - timer = new THREE.Timer(); - timer.connect(document); - const container = document.createElement('div'); - document.body.appendChild(container); - - stats = new Stats(); - container.appendChild(stats.dom); - - renderer = new THREE.WebGLRenderer(); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - const pmremGenerator = new THREE.PMREMGenerator(renderer); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xbfe3dd); - scene.environment = pmremGenerator.fromScene(new RoomEnvironment(), 0.04).texture; - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 100); - camera.position.set(5, 2, 8); - - controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, 0.5, 0); - controls.update(); - controls.enablePan = false; - controls.enableDamping = true; - - const width = window.innerWidth; - const height = window.innerHeight; - - composer = new EffectComposer(renderer); - - const renderPass = new RenderPass(scene, camera); - composer.addPass(renderPass); - - const gtaoPass = new GTAOPass(scene, camera, width, height); - gtaoPass.output = GTAOPass.OUTPUT.Denoise; - composer.addPass(gtaoPass); - - const outputPass = new OutputPass(); - composer.addPass(outputPass); - - // - - loader.load( - 'LittlestTokyo.glb', - gltf => { - const model = gltf.scene; - model.position.set(1, 1, 0); - model.scale.set(0.01, 0.01, 0.01); - scene.add(model); - - mixer = new THREE.AnimationMixer(model); - mixer.clipAction(gltf.animations[0]).play(); - - const box = new THREE.Box3().setFromObject(scene); - gtaoPass.setSceneClipBox(box); - }, - undefined, - e => console.error(e), - ); - - // Init gui - const gui = new GUI(); - - gui.add(gtaoPass, 'output', { - Default: GTAOPass.OUTPUT.Default, - Diffuse: GTAOPass.OUTPUT.Diffuse, - 'AO Only': GTAOPass.OUTPUT.AO, - 'AO Only + Denoise': GTAOPass.OUTPUT.Denoise, - Depth: GTAOPass.OUTPUT.Depth, - Normal: GTAOPass.OUTPUT.Normal, - }).onChange(function (value) { - gtaoPass.output = value; - }); - - const aoParameters = { - radius: 0.25, - distanceExponent: 1, - thickness: 1, - scale: 1, - samples: 16, - distanceFallOff: 1, - screenSpaceRadius: false, - }; - const pdParameters = { - lumaPhi: 10, - depthPhi: 2, - normalPhi: 3, - radius: 4, - radiusExponent: 1, - rings: 2, - samples: 16, - }; - gtaoPass.updateGtaoMaterial(aoParameters); - gtaoPass.updatePdMaterial(pdParameters); - gui.add(gtaoPass, 'blendIntensity').min(0).max(1).step(0.01); - gui.add(aoParameters, 'radius') - .min(0.01) - .max(1) - .step(0.01) - .onChange(() => gtaoPass.updateGtaoMaterial(aoParameters)); - gui.add(aoParameters, 'distanceExponent') - .min(1) - .max(4) - .step(0.01) - .onChange(() => gtaoPass.updateGtaoMaterial(aoParameters)); - gui.add(aoParameters, 'thickness') - .min(0.01) - .max(10) - .step(0.01) - .onChange(() => gtaoPass.updateGtaoMaterial(aoParameters)); - gui.add(aoParameters, 'distanceFallOff') - .min(0) - .max(1) - .step(0.01) - .onChange(() => gtaoPass.updateGtaoMaterial(aoParameters)); - gui.add(aoParameters, 'scale') - .min(0.01) - .max(2.0) - .step(0.01) - .onChange(() => gtaoPass.updateGtaoMaterial(aoParameters)); - gui.add(aoParameters, 'samples') - .min(2) - .max(32) - .step(1) - .onChange(() => gtaoPass.updateGtaoMaterial(aoParameters)); - gui.add(aoParameters, 'screenSpaceRadius').onChange(() => gtaoPass.updateGtaoMaterial(aoParameters)); - gui.add(pdParameters, 'lumaPhi') - .min(0) - .max(20) - .step(0.01) - .onChange(() => gtaoPass.updatePdMaterial(pdParameters)); - gui.add(pdParameters, 'depthPhi') - .min(0.01) - .max(20) - .step(0.01) - .onChange(() => gtaoPass.updatePdMaterial(pdParameters)); - gui.add(pdParameters, 'normalPhi') - .min(0.01) - .max(20) - .step(0.01) - .onChange(() => gtaoPass.updatePdMaterial(pdParameters)); - gui.add(pdParameters, 'radius') - .min(0) - .max(32) - .step(1) - .onChange(() => gtaoPass.updatePdMaterial(pdParameters)); - gui.add(pdParameters, 'radiusExponent') - .min(0.1) - .max(4) - .step(0.1) - .onChange(() => gtaoPass.updatePdMaterial(pdParameters)); - gui.add(pdParameters, 'rings') - .min(1) - .max(16) - .step(0.125) - .onChange(() => gtaoPass.updatePdMaterial(pdParameters)); - gui.add(pdParameters, 'samples') - .min(2) - .max(32) - .step(1) - .onChange(() => gtaoPass.updatePdMaterial(pdParameters)); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); - composer.setSize(width, height); -} - -function animate() { - timer.update(); - - const delta = timer.getDelta(); - - if (mixer) { - mixer.update(delta); - } - - controls.update(); - - stats.begin(); - composer.render(); - stats.end(); -} diff --git a/examples-testing/examples/webgl_postprocessing_masking.ts b/examples-testing/examples/webgl_postprocessing_masking.ts deleted file mode 100644 index a4d09866d..000000000 --- a/examples-testing/examples/webgl_postprocessing_masking.ts +++ /dev/null @@ -1,101 +0,0 @@ -import * as THREE from 'three'; - -import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; -import { TexturePass } from 'three/addons/postprocessing/TexturePass.js'; -import { ClearPass } from 'three/addons/postprocessing/ClearPass.js'; -import { MaskPass, ClearMaskPass } from 'three/addons/postprocessing/MaskPass.js'; -import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; - -let camera, composer, renderer; -let box, torus; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.z = 10; - - const scene1 = new THREE.Scene(); - const scene2 = new THREE.Scene(); - - box = new THREE.Mesh(new THREE.BoxGeometry(4, 4, 4)); - scene1.add(box); - - torus = new THREE.Mesh(new THREE.TorusGeometry(3, 1, 16, 32)); - scene2.add(torus); - - renderer = new THREE.WebGLRenderer(); - renderer.setClearColor(0xe0e0e0); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.autoClear = false; - document.body.appendChild(renderer.domElement); - - // - - const clearPass = new ClearPass(); - - const clearMaskPass = new ClearMaskPass(); - - const maskPass1 = new MaskPass(scene1, camera); - const maskPass2 = new MaskPass(scene2, camera); - - const texture1 = new THREE.TextureLoader().load('textures/758px-Canestra_di_frutta_(Caravaggio).jpg'); - texture1.colorSpace = THREE.SRGBColorSpace; - texture1.minFilter = THREE.LinearFilter; - texture1.generateMipmaps = false; - const texture2 = new THREE.TextureLoader().load('textures/2294472375_24a3b8ef46_o.jpg'); - texture2.colorSpace = THREE.SRGBColorSpace; - - const texturePass1 = new TexturePass(texture1); - const texturePass2 = new TexturePass(texture2); - - const outputPass = new OutputPass(); - - const parameters = { - stencilBuffer: true, - }; - - const renderTarget = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight, parameters); - - composer = new EffectComposer(renderer, renderTarget); - composer.addPass(clearPass); - composer.addPass(maskPass1); - composer.addPass(texturePass1); - composer.addPass(clearMaskPass); - composer.addPass(maskPass2); - composer.addPass(texturePass2); - composer.addPass(clearMaskPass); - composer.addPass(outputPass); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); - composer.setSize(width, height); -} - -function animate() { - const time = performance.now() * 0.001 + 6000; - - box.position.x = Math.cos(time / 1.5) * 2; - box.position.y = Math.sin(time) * 2; - box.rotation.x = time; - box.rotation.y = time / 2; - - torus.position.x = Math.cos(time) * 2; - torus.position.y = Math.sin(time / 1.5) * 2; - torus.rotation.x = time; - torus.rotation.y = time / 2; - - renderer.clear(); - composer.render(time); -} diff --git a/examples-testing/examples/webgl_postprocessing_outline.ts b/examples-testing/examples/webgl_postprocessing_outline.ts deleted file mode 100644 index 31ef6b9b2..000000000 --- a/examples-testing/examples/webgl_postprocessing_outline.ts +++ /dev/null @@ -1,282 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { OBJLoader } from 'three/addons/loaders/OBJLoader.js'; -import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; -import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; -import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js'; -import { OutlinePass } from 'three/addons/postprocessing/OutlinePass.js'; -import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; -import { FXAAShader } from 'three/addons/shaders/FXAAShader.js'; - -let container, stats; -let camera, scene, renderer, controls; -let composer, effectFXAA, outlinePass; - -let selectedObjects = []; - -const raycaster = new THREE.Raycaster(); -const mouse = new THREE.Vector2(); - -const obj3d = new THREE.Object3D(); -const group = new THREE.Group(); - -const params = { - edgeStrength: 3.0, - edgeGlow: 0.0, - edgeThickness: 1.0, - pulsePeriod: 0, - rotate: false, - usePatternTexture: false, -}; - -// Init gui - -const gui = new GUI({ width: 280 }); - -gui.add(params, 'edgeStrength', 0.01, 10).onChange(function (value) { - outlinePass.edgeStrength = Number(value); -}); - -gui.add(params, 'edgeGlow', 0.0, 1).onChange(function (value) { - outlinePass.edgeGlow = Number(value); -}); - -gui.add(params, 'edgeThickness', 1, 4).onChange(function (value) { - outlinePass.edgeThickness = Number(value); -}); - -gui.add(params, 'pulsePeriod', 0.0, 5).onChange(function (value) { - outlinePass.pulsePeriod = Number(value); -}); - -gui.add(params, 'rotate'); - -gui.add(params, 'usePatternTexture').onChange(function (value) { - outlinePass.usePatternTexture = value; -}); - -function Configuration() { - this.visibleEdgeColor = '#ffffff'; - this.hiddenEdgeColor = '#190a05'; -} - -const conf = new Configuration(); - -gui.addColor(conf, 'visibleEdgeColor').onChange(function (value) { - outlinePass.visibleEdgeColor.set(value); -}); - -gui.addColor(conf, 'hiddenEdgeColor').onChange(function (value) { - outlinePass.hiddenEdgeColor.set(value); -}); - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - const width = window.innerWidth; - const height = window.innerHeight; - - renderer = new THREE.WebGLRenderer(); - renderer.shadowMap.enabled = true; - // todo - support pixelRatio in this demo - renderer.setSize(width, height); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 100); - camera.position.set(0, 0, 8); - - controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 5; - controls.maxDistance = 20; - controls.enablePan = false; - controls.enableDamping = true; - controls.dampingFactor = 0.05; - - // - - scene.add(new THREE.AmbientLight(0xaaaaaa, 0.6)); - - const light = new THREE.DirectionalLight(0xddffdd, 2); - light.position.set(5, 5, 5); - light.castShadow = true; - light.shadow.mapSize.width = 1024; - light.shadow.mapSize.height = 1024; - - const d = 10; - - light.shadow.camera.left = -d; - light.shadow.camera.right = d; - light.shadow.camera.top = d; - light.shadow.camera.bottom = -d; - light.shadow.camera.far = 25; - - scene.add(light); - - // model - - const loader = new OBJLoader(); - loader.load('models/obj/tree.obj', function (object) { - let scale = 1.0; - - object.traverse(function (child) { - if (child instanceof THREE.Mesh) { - child.geometry.center(); - child.geometry.computeBoundingSphere(); - scale = 0.2 * child.geometry.boundingSphere.radius; - - const phongMaterial = new THREE.MeshPhongMaterial({ - color: 0xffffff, - specular: 0x111111, - shininess: 5, - }); - child.material = phongMaterial; - child.receiveShadow = true; - child.castShadow = true; - } - }); - - object.position.y = 1; - object.scale.divideScalar(scale); - obj3d.add(object); - }); - - scene.add(group); - - group.add(obj3d); - - // - - const geometry = new THREE.SphereGeometry(3, 48, 24); - - for (let i = 0; i < 20; i++) { - const material = new THREE.MeshLambertMaterial(); - material.color.setHSL(Math.random(), 1.0, 0.3); - - const mesh = new THREE.Mesh(geometry, material); - mesh.position.x = Math.random() * 4 - 2; - mesh.position.y = Math.random() * 4 - 2; - mesh.position.z = Math.random() * 4 - 2; - mesh.receiveShadow = true; - mesh.castShadow = true; - mesh.scale.multiplyScalar(Math.random() * 0.3 + 0.1); - group.add(mesh); - } - - const floorMaterial = new THREE.MeshLambertMaterial({ side: THREE.DoubleSide }); - - const floorGeometry = new THREE.PlaneGeometry(12, 12); - const floorMesh = new THREE.Mesh(floorGeometry, floorMaterial); - floorMesh.rotation.x -= Math.PI * 0.5; - floorMesh.position.y -= 1.5; - group.add(floorMesh); - floorMesh.receiveShadow = true; - - const torusGeometry = new THREE.TorusGeometry(1, 0.3, 16, 100); - const torusMaterial = new THREE.MeshPhongMaterial({ color: 0xffaaff }); - const torus = new THREE.Mesh(torusGeometry, torusMaterial); - torus.position.z = -4; - group.add(torus); - torus.receiveShadow = true; - torus.castShadow = true; - - // - - stats = new Stats(); - container.appendChild(stats.dom); - - // postprocessing - - composer = new EffectComposer(renderer); - - const renderPass = new RenderPass(scene, camera); - composer.addPass(renderPass); - - outlinePass = new OutlinePass(new THREE.Vector2(window.innerWidth, window.innerHeight), scene, camera); - composer.addPass(outlinePass); - - const textureLoader = new THREE.TextureLoader(); - textureLoader.load('textures/tri_pattern.jpg', function (texture) { - outlinePass.patternTexture = texture; - texture.wrapS = THREE.RepeatWrapping; - texture.wrapT = THREE.RepeatWrapping; - }); - - const outputPass = new OutputPass(); - composer.addPass(outputPass); - - effectFXAA = new ShaderPass(FXAAShader); - effectFXAA.uniforms['resolution'].value.set(1 / window.innerWidth, 1 / window.innerHeight); - composer.addPass(effectFXAA); - - window.addEventListener('resize', onWindowResize); - - renderer.domElement.style.touchAction = 'none'; - renderer.domElement.addEventListener('pointermove', onPointerMove); - - function onPointerMove(event) { - if (event.isPrimary === false) return; - - mouse.x = (event.clientX / window.innerWidth) * 2 - 1; - mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; - - checkIntersection(); - } - - function addSelectedObject(object) { - selectedObjects = []; - selectedObjects.push(object); - } - - function checkIntersection() { - raycaster.setFromCamera(mouse, camera); - - const intersects = raycaster.intersectObject(scene, true); - - if (intersects.length > 0) { - const selectedObject = intersects[0].object; - addSelectedObject(selectedObject); - outlinePass.selectedObjects = selectedObjects; - } else { - // outlinePass.selectedObjects = []; - } - } -} - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); - composer.setSize(width, height); - - effectFXAA.uniforms['resolution'].value.set(1 / window.innerWidth, 1 / window.innerHeight); -} - -function animate() { - stats.begin(); - - const timer = performance.now(); - - if (params.rotate) { - group.rotation.y = timer * 0.0001; - } - - controls.update(); - - composer.render(); - - stats.end(); -} diff --git a/examples-testing/examples/webgl_postprocessing_pixel.ts b/examples-testing/examples/webgl_postprocessing_pixel.ts deleted file mode 100644 index 04aec4816..000000000 --- a/examples-testing/examples/webgl_postprocessing_pixel.ts +++ /dev/null @@ -1,231 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; -import { RenderPixelatedPass } from 'three/addons/postprocessing/RenderPixelatedPass.js'; -import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let camera, scene, renderer, composer, crystalMesh, timer; -let gui, params; - -init(); - -function init() { - const aspectRatio = window.innerWidth / window.innerHeight; - - camera = new THREE.OrthographicCamera(-aspectRatio, aspectRatio, 1, -1, 0.1, 10); - camera.position.y = 2 * Math.tan(Math.PI / 6); - camera.position.z = 2; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x151729); - - timer = new THREE.Timer(); - timer.connect(document); - - renderer = new THREE.WebGLRenderer(); - renderer.shadowMap.enabled = true; - //renderer.setPixelRatio( window.devicePixelRatio ); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - composer = new EffectComposer(renderer); - const renderPixelatedPass = new RenderPixelatedPass(6, scene, camera); - composer.addPass(renderPixelatedPass); - - const outputPass = new OutputPass(); - composer.addPass(outputPass); - - window.addEventListener('resize', onWindowResize); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.maxZoom = 2; - - // gui - - gui = new GUI(); - params = { pixelSize: 6, normalEdgeStrength: 0.3, depthEdgeStrength: 0.4, pixelAlignedPanning: true }; - gui.add(params, 'pixelSize') - .min(1) - .max(16) - .step(1) - .onChange(() => { - renderPixelatedPass.setPixelSize(params.pixelSize); - }); - gui.add(renderPixelatedPass, 'normalEdgeStrength').min(0).max(2).step(0.05); - gui.add(renderPixelatedPass, 'depthEdgeStrength').min(0).max(1).step(0.05); - gui.add(params, 'pixelAlignedPanning'); - - // textures - - const loader = new THREE.TextureLoader(); - const texChecker = pixelTexture(loader.load('textures/checker.png')); - const texChecker2 = pixelTexture(loader.load('textures/checker.png')); - texChecker.repeat.set(3, 3); - texChecker2.repeat.set(1.5, 1.5); - - // meshes - - const boxMaterial = new THREE.MeshPhongMaterial({ map: texChecker2 }); - - function addBox(boxSideLength, x, z, rotation) { - const mesh = new THREE.Mesh(new THREE.BoxGeometry(boxSideLength, boxSideLength, boxSideLength), boxMaterial); - mesh.castShadow = true; - mesh.receiveShadow = true; - mesh.rotation.y = rotation; - mesh.position.y = boxSideLength / 2; - mesh.position.set(x, boxSideLength / 2 + 0.0001, z); - scene.add(mesh); - return mesh; - } - - addBox(0.4, 0, 0, Math.PI / 4); - addBox(0.5, -0.5, -0.5, Math.PI / 4); - - const planeSideLength = 2; - const planeMesh = new THREE.Mesh( - new THREE.PlaneGeometry(planeSideLength, planeSideLength), - new THREE.MeshPhongMaterial({ map: texChecker }), - ); - planeMesh.receiveShadow = true; - planeMesh.rotation.x = -Math.PI / 2; - scene.add(planeMesh); - - const radius = 0.2; - const geometry = new THREE.IcosahedronGeometry(radius); - crystalMesh = new THREE.Mesh( - geometry, - new THREE.MeshPhongMaterial({ - color: 0x68b7e9, - emissive: 0x4f7e8b, - shininess: 10, - specular: 0xffffff, - }), - ); - crystalMesh.receiveShadow = true; - crystalMesh.castShadow = true; - scene.add(crystalMesh); - - // lights - - scene.add(new THREE.AmbientLight(0x757f8e, 3)); - - const directionalLight = new THREE.DirectionalLight(0xfffecd, 1.5); - directionalLight.position.set(100, 100, 100); - directionalLight.castShadow = true; - directionalLight.shadow.mapSize.set(2048, 2048); - scene.add(directionalLight); - - const spotLight = new THREE.SpotLight(0xffc100, 10, 10, Math.PI / 16, 0.02, 2); - spotLight.position.set(2, 2, 0); - const target = spotLight.target; - scene.add(target); - target.position.set(0, 0, 0); - spotLight.castShadow = true; - scene.add(spotLight); -} - -function onWindowResize() { - const aspectRatio = window.innerWidth / window.innerHeight; - camera.left = -aspectRatio; - camera.right = aspectRatio; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - composer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - const t = timer.getElapsed(); - - crystalMesh.material.emissiveIntensity = Math.sin(t * 3) * 0.5 + 0.5; - crystalMesh.position.y = 0.7 + Math.sin(t * 2) * 0.05; - crystalMesh.rotation.y = stopGoEased(t, 2, 4) * 2 * Math.PI; - - const rendererSize = renderer.getSize(new THREE.Vector2()); - const aspectRatio = rendererSize.x / rendererSize.y; - if (params['pixelAlignedPanning']) { - pixelAlignFrustum( - camera, - aspectRatio, - Math.floor(rendererSize.x / params['pixelSize']), - Math.floor(rendererSize.y / params['pixelSize']), - ); - } else if (camera.left != -aspectRatio || camera.top != 1.0) { - // Reset the Camera Frustum if it has been modified - camera.left = -aspectRatio; - camera.right = aspectRatio; - camera.top = 1.0; - camera.bottom = -1.0; - camera.updateProjectionMatrix(); - } - - composer.render(); -} - -// Helper functions - -function pixelTexture(texture) { - texture.minFilter = THREE.NearestFilter; - texture.magFilter = THREE.NearestFilter; - texture.generateMipmaps = false; - texture.wrapS = THREE.RepeatWrapping; - texture.wrapT = THREE.RepeatWrapping; - texture.colorSpace = THREE.SRGBColorSpace; - return texture; -} - -function easeInOutCubic(x) { - return x ** 2 * 3 - x ** 3 * 2; -} - -function linearStep(x, edge0, edge1) { - const w = edge1 - edge0; - const m = 1 / w; - const y0 = -m * edge0; - return THREE.MathUtils.clamp(y0 + m * x, 0, 1); -} - -function stopGoEased(x, downtime, period) { - const cycle = (x / period) | 0; - const tween = x - cycle * period; - const linStep = easeInOutCubic(linearStep(tween, downtime, period)); - return cycle + linStep; -} - -function pixelAlignFrustum(camera, aspectRatio, pixelsPerScreenWidth, pixelsPerScreenHeight) { - // 0. Get Pixel Grid Units - const worldScreenWidth = (camera.right - camera.left) / camera.zoom; - const worldScreenHeight = (camera.top - camera.bottom) / camera.zoom; - const pixelWidth = worldScreenWidth / pixelsPerScreenWidth; - const pixelHeight = worldScreenHeight / pixelsPerScreenHeight; - - // 1. Project the current camera position along its local rotation bases - const camPos = new THREE.Vector3(); - camera.getWorldPosition(camPos); - const camRot = new THREE.Quaternion(); - camera.getWorldQuaternion(camRot); - const camRight = new THREE.Vector3(1.0, 0.0, 0.0).applyQuaternion(camRot); - const camUp = new THREE.Vector3(0.0, 1.0, 0.0).applyQuaternion(camRot); - const camPosRight = camPos.dot(camRight); - const camPosUp = camPos.dot(camUp); - - // 2. Find how far along its position is along these bases in pixel units - const camPosRightPx = camPosRight / pixelWidth; - const camPosUpPx = camPosUp / pixelHeight; - - // 3. Find the fractional pixel units and convert to world units - const fractX = camPosRightPx - Math.round(camPosRightPx); - const fractY = camPosUpPx - Math.round(camPosUpPx); - - // 4. Add fractional world units to the left/right top/bottom to align with the pixel grid - camera.left = -aspectRatio - fractX * pixelWidth; - camera.right = aspectRatio - fractX * pixelWidth; - camera.top = 1.0 - fractY * pixelHeight; - camera.bottom = -1.0 - fractY * pixelHeight; - camera.updateProjectionMatrix(); -} diff --git a/examples-testing/examples/webgl_postprocessing_procedural.ts b/examples-testing/examples/webgl_postprocessing_procedural.ts deleted file mode 100644 index 869824270..000000000 --- a/examples-testing/examples/webgl_postprocessing_procedural.ts +++ /dev/null @@ -1,77 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let postCamera, postScene, renderer; -let postMaterial, noiseRandom1DMaterial, noiseRandom2DMaterial, noiseRandom3DMaterial, postQuad; -let stats; - -const params = { procedure: 'noiseRandom3D' }; - -init(); - -function init() { - const container = document.getElementById('container'); - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - stats = new Stats(); - container.appendChild(stats.dom); - - // Setup post processing stage - postCamera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1); - noiseRandom1DMaterial = new THREE.ShaderMaterial({ - vertexShader: document.querySelector('#procedural-vert').textContent.trim(), - fragmentShader: document.querySelector('#noiseRandom1D-frag').textContent.trim(), - }); - noiseRandom2DMaterial = new THREE.ShaderMaterial({ - vertexShader: document.querySelector('#procedural-vert').textContent.trim(), - fragmentShader: document.querySelector('#noiseRandom2D-frag').textContent.trim(), - }); - noiseRandom3DMaterial = new THREE.ShaderMaterial({ - vertexShader: document.querySelector('#procedural-vert').textContent.trim(), - fragmentShader: document.querySelector('#noiseRandom3D-frag').textContent.trim(), - }); - postMaterial = noiseRandom3DMaterial; - const postPlane = new THREE.PlaneGeometry(2, 2); - postQuad = new THREE.Mesh(postPlane, postMaterial); - postScene = new THREE.Scene(); - postScene.add(postQuad); - - window.addEventListener('resize', onWindowResize); - - // - - const gui = new GUI(); - gui.add(params, 'procedure', ['noiseRandom1D', 'noiseRandom2D', 'noiseRandom3D']); -} - -function onWindowResize() { - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - switch (params.procedure) { - case 'noiseRandom1D': - postMaterial = noiseRandom1DMaterial; - break; - case 'noiseRandom2D': - postMaterial = noiseRandom2DMaterial; - break; - case 'noiseRandom3D': - postMaterial = noiseRandom3DMaterial; - break; - } - - postQuad.material = postMaterial; - - // render post FX - renderer.render(postScene, postCamera); - - stats.update(); -} diff --git a/examples-testing/examples/webgl_postprocessing_rgb_halftone.ts b/examples-testing/examples/webgl_postprocessing_rgb_halftone.ts deleted file mode 100644 index 5a40f9793..000000000 --- a/examples-testing/examples/webgl_postprocessing_rgb_halftone.ts +++ /dev/null @@ -1,170 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; -import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; -import { HalftonePass } from 'three/addons/postprocessing/HalftonePass.js'; - -let renderer, timer, camera, stats; - -const rotationSpeed = Math.PI / 64; - -let composer, group; - -init(); - -function init() { - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - - timer = new THREE.Timer(); - timer.connect(document); - - camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.z = 12; - - stats = new Stats(); - - document.body.appendChild(renderer.domElement); - document.body.appendChild(stats.dom); - - // camera controls - const controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, 0, 0); - controls.update(); - - // scene - - const scene = new THREE.Scene(); - scene.background = new THREE.Color(0x444444); - - group = new THREE.Group(); - const floor = new THREE.Mesh(new THREE.BoxGeometry(100, 1, 100), new THREE.MeshPhongMaterial({})); - floor.position.y = -10; - const light = new THREE.PointLight(0xffffff, 250); - light.position.y = 2; - group.add(floor, light); - scene.add(group); - - const mat = new THREE.ShaderMaterial({ - uniforms: {}, - - vertexShader: [ - 'varying vec2 vUV;', - 'varying vec3 vNormal;', - - 'void main() {', - - 'vUV = uv;', - 'vNormal = vec3( normal );', - 'gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );', - - '}', - ].join('\n'), - - fragmentShader: [ - 'varying vec2 vUV;', - 'varying vec3 vNormal;', - - 'void main() {', - - 'vec4 c = vec4( abs( vNormal ) + vec3( vUV, 0.0 ), 0.0 );', - 'gl_FragColor = c;', - - '}', - ].join('\n'), - }); - - for (let i = 0; i < 50; ++i) { - // fill scene with coloured cubes - const mesh = new THREE.Mesh(new THREE.BoxGeometry(2, 2, 2), mat); - mesh.position.set(Math.random() * 16 - 8, Math.random() * 16 - 8, Math.random() * 16 - 8); - mesh.rotation.set(Math.random() * Math.PI * 2, Math.random() * Math.PI * 2, Math.random() * Math.PI * 2); - group.add(mesh); - } - - // post-processing - - composer = new EffectComposer(renderer); - const renderPass = new RenderPass(scene, camera); - const params = { - shape: 1, - radius: 4, - rotateR: Math.PI / 12, - rotateB: (Math.PI / 12) * 2, - rotateG: (Math.PI / 12) * 3, - scatter: 0, - blending: 1, - blendingMode: 1, - greyscale: false, - disable: false, - }; - const halftonePass = new HalftonePass(params); - composer.addPass(renderPass); - composer.addPass(halftonePass); - - window.onresize = function () { - // resize composer - renderer.setSize(window.innerWidth, window.innerHeight); - composer.setSize(window.innerWidth, window.innerHeight); - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - }; - - // GUI - - const controller = { - radius: halftonePass.uniforms['radius'].value, - rotateR: halftonePass.uniforms['rotateR'].value / (Math.PI / 180), - rotateG: halftonePass.uniforms['rotateG'].value / (Math.PI / 180), - rotateB: halftonePass.uniforms['rotateB'].value / (Math.PI / 180), - scatter: halftonePass.uniforms['scatter'].value, - shape: halftonePass.uniforms['shape'].value, - greyscale: halftonePass.uniforms['greyscale'].value, - blending: halftonePass.uniforms['blending'].value, - blendingMode: halftonePass.uniforms['blendingMode'].value, - disable: halftonePass.uniforms['disable'].value, - }; - - function onGUIChange() { - // update uniforms - halftonePass.uniforms['radius'].value = controller.radius; - halftonePass.uniforms['rotateR'].value = controller.rotateR * (Math.PI / 180); - halftonePass.uniforms['rotateG'].value = controller.rotateG * (Math.PI / 180); - halftonePass.uniforms['rotateB'].value = controller.rotateB * (Math.PI / 180); - halftonePass.uniforms['scatter'].value = controller.scatter; - halftonePass.uniforms['shape'].value = controller.shape; - halftonePass.uniforms['greyscale'].value = controller.greyscale; - halftonePass.uniforms['blending'].value = controller.blending; - halftonePass.uniforms['blendingMode'].value = controller.blendingMode; - halftonePass.uniforms['disable'].value = controller.disable; - } - - const gui = new GUI(); - gui.add(controller, 'shape', { Dot: 1, Ellipse: 2, Line: 3, Square: 4, Diamond: 5 }).onChange(onGUIChange); - gui.add(controller, 'radius', 1, 25).onChange(onGUIChange); - gui.add(controller, 'rotateR', 0, 90).onChange(onGUIChange); - gui.add(controller, 'rotateG', 0, 90).onChange(onGUIChange); - gui.add(controller, 'rotateB', 0, 90).onChange(onGUIChange); - gui.add(controller, 'scatter', 0, 1, 0.01).onChange(onGUIChange); - gui.add(controller, 'greyscale').onChange(onGUIChange); - gui.add(controller, 'blending', 0, 1, 0.01).onChange(onGUIChange); - gui.add(controller, 'blendingMode', { Linear: 1, Multiply: 2, Add: 3, Lighter: 4, Darker: 5 }).onChange( - onGUIChange, - ); - gui.add(controller, 'disable').onChange(onGUIChange); -} - -function animate() { - timer.update(); - - const delta = timer.getDelta(); - stats.update(); - group.rotation.y += delta * rotationSpeed; - composer.render(delta); -} diff --git a/examples-testing/examples/webgl_postprocessing_sao.ts b/examples-testing/examples/webgl_postprocessing_sao.ts deleted file mode 100644 index 0c6298e6b..000000000 --- a/examples-testing/examples/webgl_postprocessing_sao.ts +++ /dev/null @@ -1,146 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; -import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; -import { SAOPass } from 'three/addons/postprocessing/SAOPass.js'; -import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; - -let container, stats; -let camera, scene, renderer; -let composer, renderPass, saoPass; -let group; - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - const width = window.innerWidth; - const height = window.innerHeight; - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(width, height); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - camera = new THREE.PerspectiveCamera(65, width / height, 3, 10); - camera.position.z = 7; - - scene = new THREE.Scene(); - - group = new THREE.Object3D(); - scene.add(group); - - const light = new THREE.PointLight(0xefffef, 500); - light.position.z = 10; - light.position.y = -10; - light.position.x = -10; - scene.add(light); - - const light2 = new THREE.PointLight(0xffefef, 500); - light2.position.z = 10; - light2.position.x = -10; - light2.position.y = 10; - scene.add(light2); - - const light3 = new THREE.PointLight(0xefefff, 500); - light3.position.z = 10; - light3.position.x = 10; - light3.position.y = -10; - scene.add(light3); - - const light4 = new THREE.AmbientLight(0xffffff, 0.2); - scene.add(light4); - - const geometry = new THREE.SphereGeometry(3, 48, 24); - const material = new THREE.MeshStandardMaterial(); - material.roughness = 0.5; - material.metalness = 0; - - const mesh = new THREE.InstancedMesh(geometry, material, 120); - const dummy = new THREE.Object3D(); - const color = new THREE.Color(); - - for (let i = 0; i < 120; i++) { - dummy.position.x = Math.random() * 4 - 2; - dummy.position.y = Math.random() * 4 - 2; - dummy.position.z = Math.random() * 4 - 2; - dummy.rotation.x = Math.random(); - dummy.rotation.y = Math.random(); - dummy.rotation.z = Math.random(); - - const scale = Math.random() * 0.2 + 0.05; - dummy.scale.set(scale, scale, scale); - - dummy.updateMatrix(); - mesh.setMatrixAt(i, dummy.matrix); - - color.setHSL(Math.random(), 1.0, 0.3); - mesh.setColorAt(i, color); - } - - group.add(mesh); - - stats = new Stats(); - container.appendChild(stats.dom); - - composer = new EffectComposer(renderer); - renderPass = new RenderPass(scene, camera); - composer.addPass(renderPass); - saoPass = new SAOPass(scene, camera); - composer.addPass(saoPass); - const outputPass = new OutputPass(); - composer.addPass(outputPass); - - // Init gui - const gui = new GUI(); - gui.add(saoPass.params, 'output', { - Default: SAOPass.OUTPUT.Default, - 'SAO Only': SAOPass.OUTPUT.SAO, - Normal: SAOPass.OUTPUT.Normal, - }).onChange(function (value) { - saoPass.params.output = value; - }); - gui.add(saoPass.params, 'saoBias', -1, 1); - gui.add(saoPass.params, 'saoIntensity', 0, 1); - gui.add(saoPass.params, 'saoScale', 0, 10); - gui.add(saoPass.params, 'saoKernelRadius', 1, 100); - gui.add(saoPass.params, 'saoMinResolution', 0, 1); - gui.add(saoPass.params, 'saoBlur'); - gui.add(saoPass.params, 'saoBlurRadius', 0, 200); - gui.add(saoPass.params, 'saoBlurStdDev', 0.5, 150); - gui.add(saoPass.params, 'saoBlurDepthCutoff', 0.0, 0.1); - gui.add(saoPass, 'enabled'); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - const width = window.innerWidth || 1; - const height = window.innerHeight || 1; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - renderer.setSize(width, height); - - composer.setSize(width, height); -} - -function animate() { - stats.begin(); - render(); - stats.end(); -} - -function render() { - const timer = performance.now(); - group.rotation.x = timer * 0.0002; - group.rotation.y = timer * 0.0001; - - composer.render(); -} diff --git a/examples-testing/examples/webgl_postprocessing_smaa.ts b/examples-testing/examples/webgl_postprocessing_smaa.ts deleted file mode 100644 index 9e73d38b0..000000000 --- a/examples-testing/examples/webgl_postprocessing_smaa.ts +++ /dev/null @@ -1,106 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; -import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; -import { SMAAPass } from 'three/addons/postprocessing/SMAAPass.js'; -import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; - -let camera, scene, renderer, composer, stats, smaaPass; - -const params = { - enabled: true, - autoRotate: true, -}; - -init(); - -function init() { - const container = document.getElementById('container'); - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.z = 300; - - scene = new THREE.Scene(); - - const geometry = new THREE.BoxGeometry(120, 120, 120); - const material1 = new THREE.MeshBasicMaterial({ color: 0xffffff, wireframe: true }); - - const mesh1 = new THREE.Mesh(geometry, material1); - mesh1.position.x = -100; - scene.add(mesh1); - - const texture = new THREE.TextureLoader().load('textures/brick_diffuse.jpg'); - texture.anisotropy = renderer.capabilities.getMaxAnisotropy(); - texture.colorSpace = THREE.SRGBColorSpace; - - const material2 = new THREE.MeshBasicMaterial({ map: texture }); - - const mesh2 = new THREE.Mesh(geometry, material2); - mesh2.position.x = 100; - scene.add(mesh2); - - // postprocessing - - composer = new EffectComposer(renderer); - composer.addPass(new RenderPass(scene, camera)); - - smaaPass = new SMAAPass(); - composer.addPass(smaaPass); - - const outputPass = new OutputPass(); - composer.addPass(outputPass); - - window.addEventListener('resize', onWindowResize); - - const gui = new GUI(); - - const smaaFolder = gui.addFolder('SMAA'); - smaaFolder.add(params, 'enabled'); - - const sceneFolder = gui.addFolder('Scene'); - sceneFolder.add(params, 'autoRotate'); -} - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); - composer.setSize(width, height); -} - -function animate() { - stats.begin(); - - if (params.autoRotate === true) { - for (let i = 0; i < scene.children.length; i++) { - const child = scene.children[i]; - - child.rotation.x += 0.005; - child.rotation.y += 0.01; - } - } - - smaaPass.enabled = params.enabled; - - composer.render(); - - stats.end(); -} diff --git a/examples-testing/examples/webgl_postprocessing_sobel.ts b/examples-testing/examples/webgl_postprocessing_sobel.ts deleted file mode 100644 index 55d88dc02..000000000 --- a/examples-testing/examples/webgl_postprocessing_sobel.ts +++ /dev/null @@ -1,111 +0,0 @@ -import * as THREE from 'three'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; -import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; -import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js'; - -import { LuminosityShader } from 'three/addons/shaders/LuminosityShader.js'; -import { SobelOperatorShader } from 'three/addons/shaders/SobelOperatorShader.js'; - -let camera, scene, renderer, composer; - -let effectSobel; - -const params = { - enable: true, -}; - -init(); - -function init() { - // - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(0, 1, 3); - camera.lookAt(scene.position); - - // - - const geometry = new THREE.TorusKnotGeometry(1, 0.3, 256, 32); - const material = new THREE.MeshPhongMaterial({ color: 0xffff00 }); - - const mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - // - - const ambientLight = new THREE.AmbientLight(0xe7e7e7); - scene.add(ambientLight); - - const pointLight = new THREE.PointLight(0xffffff, 20); - camera.add(pointLight); - scene.add(camera); - - // - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // postprocessing - - composer = new EffectComposer(renderer); - const renderPass = new RenderPass(scene, camera); - composer.addPass(renderPass); - - // color to grayscale conversion - - const effectGrayScale = new ShaderPass(LuminosityShader); - composer.addPass(effectGrayScale); - - // you might want to use a gaussian blur filter before - // the next pass to improve the result of the Sobel operator - - // Sobel operator - - effectSobel = new ShaderPass(SobelOperatorShader); - effectSobel.uniforms['resolution'].value.x = window.innerWidth * window.devicePixelRatio; - effectSobel.uniforms['resolution'].value.y = window.innerHeight * window.devicePixelRatio; - composer.addPass(effectSobel); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.enableZoom = false; - - // - - const gui = new GUI(); - - gui.add(params, 'enable'); - gui.open(); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - composer.setSize(window.innerWidth, window.innerHeight); - - effectSobel.uniforms['resolution'].value.x = window.innerWidth * window.devicePixelRatio; - effectSobel.uniforms['resolution'].value.y = window.innerHeight * window.devicePixelRatio; -} - -function animate() { - if (params.enable === true) { - composer.render(); - } else { - renderer.render(scene, camera); - } -} diff --git a/examples-testing/examples/webgl_postprocessing_ssaa.ts b/examples-testing/examples/webgl_postprocessing_ssaa.ts deleted file mode 100644 index 45d8767b1..000000000 --- a/examples-testing/examples/webgl_postprocessing_ssaa.ts +++ /dev/null @@ -1,214 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; -import { SSAARenderPass } from 'three/addons/postprocessing/SSAARenderPass.js'; -import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; - -let scene, renderer, composer; -let cameraP, ssaaRenderPassP; -let cameraO, ssaaRenderPassO; -let gui, stats; - -const params = { - sampleLevel: 4, - unbiased: true, - camera: 'perspective', - clearColor: 'black', - clearAlpha: 1.0, - viewOffsetX: 0, - autoRotate: true, -}; - -init(); - -clearGui(); - -function clearGui() { - if (gui) gui.destroy(); - - gui = new GUI(); - - gui.add(params, 'unbiased'); - gui.add(params, 'sampleLevel', { - 'Level 0: 1 Sample': 0, - 'Level 1: 2 Samples': 1, - 'Level 2: 4 Samples': 2, - 'Level 3: 8 Samples': 3, - 'Level 4: 16 Samples': 4, - 'Level 5: 32 Samples': 5, - }); - gui.add(params, 'camera', ['perspective', 'orthographic']); - gui.add(params, 'clearColor', ['black', 'white', 'blue', 'green', 'red']); - gui.add(params, 'clearAlpha', 0, 1); - gui.add(params, 'viewOffsetX', -100, 100); - gui.add(params, 'autoRotate'); - - gui.open(); -} - -function init() { - const container = document.getElementById('container'); - - const width = window.innerWidth || 1; - const height = window.innerHeight || 1; - const aspect = width / height; - const devicePixelRatio = window.devicePixelRatio || 1; - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(devicePixelRatio); - renderer.setSize(width, height); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - stats = new Stats(); - container.appendChild(stats.dom); - - cameraP = new THREE.PerspectiveCamera(65, aspect, 3, 10); - cameraP.position.z = 7; - cameraP.setViewOffset(width, height, params.viewOffsetX, 0, width, height); - - cameraO = new THREE.OrthographicCamera(width / -2, width / 2, height / 2, height / -2, 3, 10); - cameraO.position.z = 7; - - const fov = THREE.MathUtils.degToRad(cameraP.fov); - const hyperfocus = (cameraP.near + cameraP.far) / 2; - const _height = 2 * Math.tan(fov / 2) * hyperfocus; - cameraO.zoom = height / _height; - - scene = new THREE.Scene(); - - const group = new THREE.Group(); - scene.add(group); - - const light = new THREE.PointLight(0xefffef, 500); - light.position.z = 10; - light.position.y = -10; - light.position.x = -10; - scene.add(light); - - const light2 = new THREE.PointLight(0xffefef, 500); - light2.position.z = 10; - light2.position.x = -10; - light2.position.y = 10; - scene.add(light2); - - const light3 = new THREE.PointLight(0xefefff, 500); - light3.position.z = 10; - light3.position.x = 10; - light3.position.y = -10; - scene.add(light3); - - const light4 = new THREE.AmbientLight(0xffffff, 0.2); - scene.add(light4); - - const geometry = new THREE.SphereGeometry(3, 48, 24); - const material = new THREE.MeshStandardMaterial(); - material.roughness = 0.5; - material.metalness = 0; - - const mesh = new THREE.InstancedMesh(geometry, material, 120); - const dummy = new THREE.Object3D(); - const color = new THREE.Color(); - - for (let i = 0; i < 120; i++) { - dummy.position.x = Math.random() * 4 - 2; - dummy.position.y = Math.random() * 4 - 2; - dummy.position.z = Math.random() * 4 - 2; - dummy.rotation.x = Math.random(); - dummy.rotation.y = Math.random(); - dummy.rotation.z = Math.random(); - - dummy.scale.setScalar(Math.random() * 0.2 + 0.05); - - dummy.updateMatrix(); - mesh.setMatrixAt(i, dummy.matrix); - - color.setHSL(Math.random(), 1.0, 0.3); - mesh.setColorAt(i, color); - } - - group.add(mesh); - - // postprocessing - - composer = new EffectComposer(renderer); - composer.setPixelRatio(1); // ensure pixel ratio is always 1 for performance reasons - ssaaRenderPassP = new SSAARenderPass(scene, cameraP); - composer.addPass(ssaaRenderPassP); - ssaaRenderPassO = new SSAARenderPass(scene, cameraO); - composer.addPass(ssaaRenderPassO); - const outputPass = new OutputPass(); - composer.addPass(outputPass); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - const aspect = width / height; - - cameraP.aspect = aspect; - cameraP.setViewOffset(width, height, params.viewOffsetX, 0, width, height); - cameraO.updateProjectionMatrix(); - - cameraO.left = -height * aspect; - cameraO.right = height * aspect; - cameraO.top = height; - cameraO.bottom = -height; - cameraO.updateProjectionMatrix(); - - renderer.setSize(width, height); - composer.setSize(width, height); -} - -function animate() { - stats.begin(); - - if (params.autoRotate) { - for (let i = 0; i < scene.children.length; i++) { - const child = scene.children[i]; - - child.rotation.x += 0.005; - child.rotation.y += 0.01; - } - } - - let newColor = ssaaRenderPassP.clearColor; - - switch (params.clearColor) { - case 'blue': - newColor = 0x0000ff; - break; - case 'red': - newColor = 0xff0000; - break; - case 'green': - newColor = 0x00ff00; - break; - case 'white': - newColor = 0xffffff; - break; - case 'black': - newColor = 0x000000; - break; - } - - ssaaRenderPassP.clearColor = ssaaRenderPassO.clearColor = newColor; - ssaaRenderPassP.clearAlpha = ssaaRenderPassO.clearAlpha = params.clearAlpha; - - ssaaRenderPassP.sampleLevel = ssaaRenderPassO.sampleLevel = params.sampleLevel; - ssaaRenderPassP.unbiased = ssaaRenderPassO.unbiased = params.unbiased; - - ssaaRenderPassP.enabled = params.camera === 'perspective'; - ssaaRenderPassO.enabled = params.camera === 'orthographic'; - - cameraP.view.offsetX = params.viewOffsetX; - - composer.render(); - - stats.end(); -} diff --git a/examples-testing/examples/webgl_postprocessing_ssao.ts b/examples-testing/examples/webgl_postprocessing_ssao.ts deleted file mode 100644 index fd3739af3..000000000 --- a/examples-testing/examples/webgl_postprocessing_ssao.ts +++ /dev/null @@ -1,125 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; -import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; -import { SSAOPass } from 'three/addons/postprocessing/SSAOPass.js'; -import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; - -let container, stats; -let camera, scene, renderer; -let composer; -let group; - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - renderer = new THREE.WebGLRenderer(); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - camera = new THREE.PerspectiveCamera(65, window.innerWidth / window.innerHeight, 100, 700); - camera.position.z = 500; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xaaaaaa); - - scene.add(new THREE.DirectionalLight(0xffffff, 4)); - scene.add(new THREE.AmbientLight(0xffffff)); - - group = new THREE.Group(); - scene.add(group); - - const geometry = new THREE.BoxGeometry(10, 10, 10); - const material = new THREE.MeshLambertMaterial(); - - const mesh = new THREE.InstancedMesh(geometry, material, 100); - const dummy = new THREE.Object3D(); - const color = new THREE.Color(); - - for (let i = 0; i < 100; i++) { - dummy.position.x = Math.random() * 400 - 200; - dummy.position.y = Math.random() * 400 - 200; - dummy.position.z = Math.random() * 400 - 200; - dummy.rotation.x = Math.random(); - dummy.rotation.y = Math.random(); - dummy.rotation.z = Math.random(); - - dummy.scale.setScalar(Math.random() * 10 + 2); - - dummy.updateMatrix(); - mesh.setMatrixAt(i, dummy.matrix); - - color.setHex(Math.random() * 0xffffff); - mesh.setColorAt(i, color); - } - - group.add(mesh); - - stats = new Stats(); - container.appendChild(stats.dom); - - const width = window.innerWidth; - const height = window.innerHeight; - - composer = new EffectComposer(renderer); - - const renderPass = new RenderPass(scene, camera); - composer.addPass(renderPass); - - const ssaoPass = new SSAOPass(scene, camera, width, height); - composer.addPass(ssaoPass); - - const outputPass = new OutputPass(); - composer.addPass(outputPass); - - // Init gui - const gui = new GUI(); - - gui.add(ssaoPass, 'output', { - Default: SSAOPass.OUTPUT.Default, - 'SSAO Only': SSAOPass.OUTPUT.SSAO, - 'SSAO Only + Blur': SSAOPass.OUTPUT.Blur, - Depth: SSAOPass.OUTPUT.Depth, - Normal: SSAOPass.OUTPUT.Normal, - }).onChange(function (value) { - ssaoPass.output = value; - }); - gui.add(ssaoPass, 'kernelRadius').min(0).max(32); - gui.add(ssaoPass, 'minDistance').min(0.001).max(0.02); - gui.add(ssaoPass, 'maxDistance').min(0.01).max(0.3); - gui.add(ssaoPass, 'enabled'); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); - composer.setSize(width, height); -} - -function animate() { - stats.begin(); - render(); - stats.end(); -} - -function render() { - const timer = performance.now(); - group.rotation.x = timer * 0.0002; - group.rotation.y = timer * 0.0001; - - composer.render(); -} diff --git a/examples-testing/examples/webgl_postprocessing_ssr.ts b/examples-testing/examples/webgl_postprocessing_ssr.ts deleted file mode 100644 index 1f5e48d3b..000000000 --- a/examples-testing/examples/webgl_postprocessing_ssr.ts +++ /dev/null @@ -1,262 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; -import { SSRPass } from 'three/addons/postprocessing/SSRPass.js'; -import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; -import { ReflectorForSSRPass } from 'three/addons/objects/ReflectorForSSRPass.js'; - -import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; - -const params = { - enableSSR: true, - autoRotate: true, - otherMeshes: true, - groundReflector: true, -}; -let composer; -let ssrPass; -let gui; -let stats; -let controls; -let camera, scene, renderer; -const otherMeshes = []; -let groundReflector; -const selects = []; - -const container = document.querySelector('#container'); - -// Configure and create Draco decoder. -const dracoLoader = new DRACOLoader(); -dracoLoader.setDecoderPath('jsm/libs/draco/'); -dracoLoader.setDecoderConfig({ type: 'js' }); - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 0.1, 15); - camera.position.set(0.13271600513224902, 0.3489546826045913, 0.43921296427927076); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x443333); - scene.fog = new THREE.Fog(0x443333, 1, 4); - - // Ground - const plane = new THREE.Mesh(new THREE.PlaneGeometry(8, 8), new THREE.MeshPhongMaterial({ color: 0xcbcbcb })); - plane.rotation.x = -Math.PI / 2; - plane.position.y = -0.0001; - // plane.receiveShadow = true; - scene.add(plane); - - // Lights - const hemiLight = new THREE.HemisphereLight(0x8d7c7c, 0x494966, 3); - scene.add(hemiLight); - - const spotLight = new THREE.SpotLight(); - spotLight.intensity = 8; - spotLight.angle = Math.PI / 16; - spotLight.penumbra = 0.5; - // spotLight.castShadow = true; - spotLight.position.set(-1, 1, 1); - scene.add(spotLight); - - dracoLoader.load('models/draco/bunny.drc', function (geometry) { - geometry.computeVertexNormals(); - - const material = new THREE.MeshStandardMaterial({ color: 0xa5a5a5 }); - const mesh = new THREE.Mesh(geometry, material); - mesh.position.y = -0.0365; - scene.add(mesh); - selects.push(mesh); - - // Release decoder resources. - dracoLoader.dispose(); - }); - - let geometry, material, mesh; - - geometry = new THREE.BoxGeometry(0.05, 0.05, 0.05); - material = new THREE.MeshStandardMaterial({ color: 'green' }); - mesh = new THREE.Mesh(geometry, material); - mesh.position.set(-0.12, 0.025, 0.015); - scene.add(mesh); - otherMeshes.push(mesh); - selects.push(mesh); - - geometry = new THREE.IcosahedronGeometry(0.025, 4); - material = new THREE.MeshStandardMaterial({ color: 'cyan' }); - mesh = new THREE.Mesh(geometry, material); - mesh.position.set(-0.05, 0.025, 0.08); - scene.add(mesh); - otherMeshes.push(mesh); - selects.push(mesh); - - geometry = new THREE.ConeGeometry(0.025, 0.05, 64); - material = new THREE.MeshStandardMaterial({ color: 'yellow' }); - mesh = new THREE.Mesh(geometry, material); - mesh.position.set(-0.05, 0.025, -0.055); - scene.add(mesh); - otherMeshes.push(mesh); - selects.push(mesh); - - geometry = new THREE.PlaneGeometry(1, 1); - groundReflector = new ReflectorForSSRPass(geometry, { - clipBias: 0.0003, - textureWidth: window.innerWidth, - textureHeight: window.innerHeight, - color: 0x888888, - useDepthTexture: true, - }); - groundReflector.material.depthWrite = false; - groundReflector.rotation.x = -Math.PI / 2; - groundReflector.visible = false; - scene.add(groundReflector); - - // renderer - renderer = new THREE.WebGLRenderer({ antialias: false }); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - // - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - controls.target.set(0, 0.0635, 0); - controls.update(); - controls.enabled = !params.autoRotate; - - // STATS - - stats = new Stats(); - container.appendChild(stats.dom); - - window.addEventListener('resize', onWindowResize); - - // composer - - composer = new EffectComposer(renderer); - ssrPass = new SSRPass({ - renderer, - scene, - camera, - width: innerWidth, - height: innerHeight, - groundReflector: params.groundReflector ? groundReflector : null, - selects: params.groundReflector ? selects : null, - }); - - composer.addPass(ssrPass); - composer.addPass(new OutputPass()); - - // GUI - - gui = new GUI({ width: 260 }); - gui.add(params, 'enableSSR').name('Enable SSR'); - gui.add(params, 'groundReflector').onChange(() => { - if (params.groundReflector) { - ((ssrPass.groundReflector = groundReflector), (ssrPass.selects = selects)); - } else { - ((ssrPass.groundReflector = null), (ssrPass.selects = null)); - } - }); - ssrPass.thickness = 0.018; - gui.add(ssrPass, 'resolutionScale').min(0).max(1); - gui.add(ssrPass, 'thickness').min(0).max(0.1).step(0.0001); - ssrPass.infiniteThick = false; - gui.add(ssrPass, 'infiniteThick'); - gui.add(params, 'autoRotate').onChange(() => { - controls.enabled = !params.autoRotate; - }); - - const folder = gui.addFolder('more settings'); - folder.add(ssrPass, 'fresnel').onChange(() => { - groundReflector.fresnel = ssrPass.fresnel; - }); - folder.add(ssrPass, 'distanceAttenuation').onChange(() => { - groundReflector.distanceAttenuation = ssrPass.distanceAttenuation; - }); - ssrPass.maxDistance = 0.1; - groundReflector.maxDistance = ssrPass.maxDistance; - folder - .add(ssrPass, 'maxDistance') - .min(0) - .max(0.5) - .step(0.001) - .onChange(() => { - groundReflector.maxDistance = ssrPass.maxDistance; - }); - folder.add(params, 'otherMeshes').onChange(() => { - if (params.otherMeshes) { - otherMeshes.forEach(mesh => (mesh.visible = true)); - } else { - otherMeshes.forEach(mesh => (mesh.visible = false)); - } - }); - folder.add(ssrPass, 'bouncing'); - folder - .add(ssrPass, 'output', { - Default: SSRPass.OUTPUT.Default, - 'SSR Only': SSRPass.OUTPUT.SSR, - Beauty: SSRPass.OUTPUT.Beauty, - Depth: SSRPass.OUTPUT.Depth, - Normal: SSRPass.OUTPUT.Normal, - Metalness: SSRPass.OUTPUT.Metalness, - }) - .onChange(function (value) { - ssrPass.output = value; - }); - ssrPass.opacity = 1; - groundReflector.opacity = ssrPass.opacity; - folder - .add(ssrPass, 'opacity') - .min(0) - .max(1) - .onChange(() => { - groundReflector.opacity = ssrPass.opacity; - }); - folder.add(ssrPass, 'blur'); - // folder.open() - // gui.close() -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - composer.setSize(window.innerWidth, window.innerHeight); - groundReflector.getRenderTarget().setSize(window.innerWidth, window.innerHeight); - groundReflector.resolution.set(window.innerWidth, window.innerHeight); -} - -function animate() { - stats.begin(); - render(); - stats.end(); -} - -function render() { - if (params.autoRotate) { - const timer = Date.now() * 0.0003; - - camera.position.x = Math.sin(timer) * 0.5; - camera.position.y = 0.2135; - camera.position.z = Math.cos(timer) * 0.5; - camera.lookAt(0, 0.0635, 0); - } else { - controls.update(); - } - - if (params.enableSSR) { - // TODO: groundReflector has full ground info, need use it to solve reflection gaps problem on objects when camera near ground. - // TODO: the normal and depth info where groundReflector reflected need to be changed. - composer.render(); - } else { - renderer.render(scene, camera); - } -} diff --git a/examples-testing/examples/webgl_postprocessing_taa.ts b/examples-testing/examples/webgl_postprocessing_taa.ts deleted file mode 100644 index 11a986741..000000000 --- a/examples-testing/examples/webgl_postprocessing_taa.ts +++ /dev/null @@ -1,139 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; -import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; -import { TAARenderPass } from 'three/addons/postprocessing/TAARenderPass.js'; -import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; - -let camera, scene, renderer, composer, taaRenderPass, renderPass; -let gui, stats; -let index = 0; - -const param = { TAAEnabled: '1', TAASampleLevel: 0 }; - -init(); - -clearGui(); - -function clearGui() { - if (gui) gui.destroy(); - - gui = new GUI(); - - gui.add(param, 'TAAEnabled', { - Disabled: '0', - Enabled: '1', - }).onFinishChange(function () { - if (taaRenderPass) { - taaRenderPass.enabled = param.TAAEnabled === '1'; - renderPass.enabled = param.TAAEnabled !== '1'; - } - }); - - gui.add(param, 'TAASampleLevel', { - 'Level 0: 1 Sample': 0, - 'Level 1: 2 Samples': 1, - 'Level 2: 4 Samples': 2, - 'Level 3: 8 Samples': 3, - 'Level 4: 16 Samples': 4, - 'Level 5: 32 Samples': 5, - }).onFinishChange(function () { - if (taaRenderPass) { - taaRenderPass.sampleLevel = param.TAASampleLevel; - } - }); - - gui.open(); -} - -function init() { - const container = document.getElementById('container'); - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.z = 300; - - scene = new THREE.Scene(); - - const geometry = new THREE.BoxGeometry(120, 120, 120); - const material1 = new THREE.MeshBasicMaterial({ color: 0xffffff, wireframe: true }); - - const mesh1 = new THREE.Mesh(geometry, material1); - mesh1.position.x = -100; - scene.add(mesh1); - - const texture = new THREE.TextureLoader().load('textures/brick_diffuse.jpg'); - texture.minFilter = THREE.NearestFilter; - texture.magFilter = THREE.NearestFilter; - texture.anisotropy = 1; - texture.generateMipmaps = false; - texture.colorSpace = THREE.SRGBColorSpace; - - const material2 = new THREE.MeshBasicMaterial({ map: texture }); - - const mesh2 = new THREE.Mesh(geometry, material2); - mesh2.position.x = 100; - scene.add(mesh2); - - // postprocessing - - composer = new EffectComposer(renderer); - - taaRenderPass = new TAARenderPass(scene, camera); - taaRenderPass.unbiased = false; - composer.addPass(taaRenderPass); - - renderPass = new RenderPass(scene, camera); - renderPass.enabled = false; - composer.addPass(renderPass); - - const outputPass = new OutputPass(); - composer.addPass(outputPass); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); - composer.setSize(width, height); -} - -function animate() { - index++; - - if (Math.round(index / 200) % 2 === 0) { - for (let i = 0; i < scene.children.length; i++) { - const child = scene.children[i]; - - child.rotation.x += 0.005; - child.rotation.y += 0.01; - } - - if (taaRenderPass) taaRenderPass.accumulate = false; - } else { - if (taaRenderPass) taaRenderPass.accumulate = true; - } - - composer.render(); - - stats.update(); -} diff --git a/examples-testing/examples/webgl_postprocessing_transition.ts b/examples-testing/examples/webgl_postprocessing_transition.ts deleted file mode 100644 index 1cf5dd3cb..000000000 --- a/examples-testing/examples/webgl_postprocessing_transition.ts +++ /dev/null @@ -1,214 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import TWEEN from 'three/addons/libs/tween.module.js'; -import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; -import { RenderTransitionPass } from 'three/addons/postprocessing/RenderTransitionPass.js'; -import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; - -let stats; -let renderer, composer, renderTransitionPass; - -const textures = []; -const timer = new THREE.Timer(); -timer.connect(document); - -const params = { - sceneAnimate: true, - transitionAnimate: true, - transition: 0, - useTexture: true, - texture: 5, - cycle: true, - threshold: 0.1, -}; - -const fxSceneA = new FXScene(new THREE.BoxGeometry(2, 2, 2), new THREE.Vector3(0, -0.4, 0), 0xffffff); -const fxSceneB = new FXScene(new THREE.IcosahedronGeometry(1, 1), new THREE.Vector3(0, 0.2, 0.1), 0x000000); - -init(); - -function init() { - initGUI(); - initTextures(); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - composer = new EffectComposer(renderer); - - stats = new Stats(); - document.body.appendChild(stats.dom); - - renderTransitionPass = new RenderTransitionPass(fxSceneA.scene, fxSceneA.camera, fxSceneB.scene, fxSceneB.camera); - renderTransitionPass.setTexture(textures[0]); - composer.addPass(renderTransitionPass); - - const outputPass = new OutputPass(); - composer.addPass(outputPass); -} - -window.addEventListener('resize', onWindowResize); - -function onWindowResize() { - fxSceneA.resize(); - fxSceneB.resize(); - renderer.setSize(window.innerWidth, window.innerHeight); - composer.setSize(window.innerWidth, window.innerHeight); -} - -new TWEEN.Tween(params) - .to({ transition: 1 }, 1500) - .onUpdate(function () { - renderTransitionPass.setTransition(params.transition); - - // Change the current alpha texture after each transition - if (params.cycle) { - if (params.transition == 0 || params.transition == 1) { - params.texture = (params.texture + 1) % textures.length; - renderTransitionPass.setTexture(textures[params.texture]); - } - } - }) - .repeat(Infinity) - .delay(2000) - .yoyo(true) - .start(); - -function animate() { - timer.update(); - - // Transition animation - if (params.transitionAnimate) TWEEN.update(); - - const delta = timer.getDelta(); - fxSceneA.update(delta); - fxSceneB.update(delta); - - render(); - stats.update(); -} - -function initTextures() { - const loader = new THREE.TextureLoader(); - - for (let i = 0; i < 6; i++) { - textures[i] = loader.load('textures/transition/transition' + (i + 1) + '.png'); - } -} - -function initGUI() { - const gui = new GUI(); - - gui.add(params, 'sceneAnimate').name('Animate scene'); - gui.add(params, 'transitionAnimate').name('Animate transition'); - gui.add(params, 'transition', 0, 1, 0.01) - .onChange(function (value) { - renderTransitionPass.setTransition(value); - }) - .listen(); - - gui.add(params, 'useTexture').onChange(function (value) { - renderTransitionPass.useTexture(value); - }); - - gui.add(params, 'texture', { Perlin: 0, Squares: 1, Cells: 2, Distort: 3, Gradient: 4, Radial: 5 }) - .onChange(function (value) { - renderTransitionPass.setTexture(textures[value]); - }) - .listen(); - - gui.add(params, 'cycle'); - - gui.add(params, 'threshold', 0, 1, 0.01).onChange(function (value) { - renderTransitionPass.setTextureThreshold(value); - }); -} - -function render() { - // Prevent render both scenes when it's not necessary - if (params.transition === 0) { - renderer.render(fxSceneB.scene, fxSceneB.camera); - } else if (params.transition === 1) { - renderer.render(fxSceneA.scene, fxSceneA.camera); - } else { - // When 0 < transition < 1 render transition between two scenes - composer.render(); - } -} - -function FXScene(geometry, rotationSpeed, backgroundColor) { - const camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.z = 20; - - // Setup scene - const scene = new THREE.Scene(); - scene.background = new THREE.Color(backgroundColor); - scene.add(new THREE.AmbientLight(0xaaaaaa, 3)); - - const light = new THREE.DirectionalLight(0xffffff, 3); - light.position.set(0, 1, 4); - scene.add(light); - - this.rotationSpeed = rotationSpeed; - - const color = geometry.type === 'BoxGeometry' ? 0x0000ff : 0xff0000; - const material = new THREE.MeshPhongMaterial({ color: color, flatShading: true }); - const mesh = generateInstancedMesh(geometry, material, 500); - scene.add(mesh); - - this.scene = scene; - this.camera = camera; - this.mesh = mesh; - - this.update = function (delta) { - if (params.sceneAnimate) { - mesh.rotation.x += this.rotationSpeed.x * delta; - mesh.rotation.y += this.rotationSpeed.y * delta; - mesh.rotation.z += this.rotationSpeed.z * delta; - } - }; - - this.resize = function () { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - }; -} - -function generateInstancedMesh(geometry, material, count) { - const mesh = new THREE.InstancedMesh(geometry, material, count); - - const dummy = new THREE.Object3D(); - const color = new THREE.Color(); - - for (let i = 0; i < count; i++) { - dummy.position.x = Math.random() * 100 - 50; - dummy.position.y = Math.random() * 60 - 30; - dummy.position.z = Math.random() * 80 - 40; - - dummy.rotation.x = Math.random() * 2 * Math.PI; - dummy.rotation.y = Math.random() * 2 * Math.PI; - dummy.rotation.z = Math.random() * 2 * Math.PI; - - dummy.scale.x = Math.random() * 2 + 1; - - if (geometry.type === 'BoxGeometry') { - dummy.scale.y = Math.random() * 2 + 1; - dummy.scale.z = Math.random() * 2 + 1; - } else { - dummy.scale.y = dummy.scale.x; - dummy.scale.z = dummy.scale.x; - } - - dummy.updateMatrix(); - - mesh.setMatrixAt(i, dummy.matrix); - mesh.setColorAt(i, color.setScalar(0.1 + 0.9 * Math.random())); - } - - return mesh; -} diff --git a/examples-testing/examples/webgl_postprocessing_unreal_bloom.ts b/examples-testing/examples/webgl_postprocessing_unreal_bloom.ts deleted file mode 100644 index b5e2ee0f9..000000000 --- a/examples-testing/examples/webgl_postprocessing_unreal_bloom.ts +++ /dev/null @@ -1,139 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; -import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; -import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js'; -import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; - -let camera, stats; -let composer, renderer, mixer, timer; - -const params = { - threshold: 0, - strength: 1, - radius: 0.5, - exposure: 1, -}; - -init(); - -async function init() { - const container = document.getElementById('container'); - - timer = new THREE.Timer(); - timer.connect(document); - - const scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 100); - camera.position.set(-5, 2.5, -3.5); - scene.add(camera); - - scene.add(new THREE.AmbientLight(0xcccccc)); - - const pointLight = new THREE.PointLight(0xffffff, 100); - camera.add(pointLight); - - const loader = new GLTFLoader(); - const gltf = await loader.loadAsync('models/gltf/PrimaryIonDrive.glb'); - - const model = gltf.scene; - scene.add(model); - - mixer = new THREE.AnimationMixer(model); - const clip = gltf.animations[0]; - mixer.clipAction(clip.optimize()).play(); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - container.appendChild(renderer.domElement); - - // - - const renderScene = new RenderPass(scene, camera); - - const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85); - bloomPass.threshold = params.threshold; - bloomPass.strength = params.strength; - bloomPass.radius = params.radius; - - const outputPass = new OutputPass(); - - composer = new EffectComposer(renderer); - composer.addPass(renderScene); - composer.addPass(bloomPass); - composer.addPass(outputPass); - - // - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - const controls = new OrbitControls(camera, renderer.domElement); - controls.maxPolarAngle = Math.PI * 0.5; - controls.minDistance = 3; - controls.maxDistance = 8; - - // - - const gui = new GUI(); - - const bloomFolder = gui.addFolder('bloom'); - - bloomFolder.add(params, 'threshold', 0.0, 1.0).onChange(function (value) { - bloomPass.threshold = Number(value); - }); - - bloomFolder.add(params, 'strength', 0.0, 3.0).onChange(function (value) { - bloomPass.strength = Number(value); - }); - - gui.add(params, 'radius', 0.0, 1.0) - .step(0.01) - .onChange(function (value) { - bloomPass.radius = Number(value); - }); - - const toneMappingFolder = gui.addFolder('tone mapping'); - - toneMappingFolder.add(params, 'exposure', 0.1, 2).onChange(function (value) { - renderer.toneMappingExposure = Math.pow(value, 4.0); - }); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); - composer.setSize(width, height); -} - -function animate() { - timer.update(); - - const delta = timer.getDelta(); - - mixer.update(delta); - - stats.update(); - - composer.render(); -} diff --git a/examples-testing/examples/webgl_postprocessing_unreal_bloom_selective.ts b/examples-testing/examples/webgl_postprocessing_unreal_bloom_selective.ts deleted file mode 100644 index 288b4477d..000000000 --- a/examples-testing/examples/webgl_postprocessing_unreal_bloom_selective.ts +++ /dev/null @@ -1,208 +0,0 @@ -import * as THREE from 'three'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; -import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; -import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js'; -import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js'; -import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; -import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; - -const BLOOM_SCENE = 1; - -const bloomLayer = new THREE.Layers(); -bloomLayer.set(BLOOM_SCENE); - -const params = { - threshold: 0, - strength: 1, - radius: 0.5, - exposure: 1, -}; - -const darkMaterial = new THREE.MeshBasicMaterial({ color: 'black' }); -const materials = {}; - -const renderer = new THREE.WebGLRenderer({ antialias: false }); -renderer.setPixelRatio(window.devicePixelRatio); -renderer.setSize(window.innerWidth, window.innerHeight); -renderer.toneMapping = THREE.NeutralToneMapping; -document.body.appendChild(renderer.domElement); - -const scene = new THREE.Scene(); -const pmremGenerator = new THREE.PMREMGenerator(renderer); -scene.environment = pmremGenerator.fromScene(new RoomEnvironment(), 0.04).texture; - -const camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 200); -camera.position.set(0, 0, 20); -camera.lookAt(0, 0, 0); - -const controls = new OrbitControls(camera, renderer.domElement); -controls.maxPolarAngle = Math.PI * 0.5; -controls.minDistance = 1; -controls.maxDistance = 100; -controls.addEventListener('change', render); - -const renderScene = new RenderPass(scene, camera); - -const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85); -bloomPass.threshold = params.threshold; -bloomPass.strength = params.strength; -bloomPass.radius = params.radius; - -const bloomRenderTarget = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight, { - type: THREE.HalfFloatType, -}); -const bloomComposer = new EffectComposer(renderer, bloomRenderTarget); -bloomComposer.renderToScreen = false; -bloomComposer.addPass(renderScene); -bloomComposer.addPass(bloomPass); - -const mixPass = new ShaderPass( - new THREE.ShaderMaterial({ - uniforms: { - baseTexture: { value: null }, - bloomTexture: { value: bloomComposer.renderTarget2.texture }, - bloomStrength: { value: params.strength }, - }, - vertexShader: document.getElementById('vertexshader').textContent, - fragmentShader: document.getElementById('fragmentshader').textContent, - defines: {}, - }), - 'baseTexture', -); -mixPass.needsSwap = true; - -const outputPass = new OutputPass(); - -const finalRenderTarget = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight, { - type: THREE.HalfFloatType, - samples: 4, -}); -const finalComposer = new EffectComposer(renderer, finalRenderTarget); -finalComposer.addPass(renderScene); -finalComposer.addPass(mixPass); -finalComposer.addPass(outputPass); - -const raycaster = new THREE.Raycaster(); - -const mouse = new THREE.Vector2(); - -window.addEventListener('pointerdown', onPointerDown); - -const gui = new GUI(); - -const bloomFolder = gui.addFolder('bloom'); - -bloomFolder.add(params, 'threshold', 0.0, 1.0).onChange(function (value) { - bloomPass.threshold = Number(value); - render(); -}); - -bloomFolder.add(params, 'strength', 0.0, 3).onChange(function (value) { - bloomPass.strength = Number(value); - mixPass.material.uniforms.bloomStrength.value = bloomPass.strength; - - render(); -}); - -bloomFolder - .add(params, 'radius', 0.0, 1.0) - .step(0.01) - .onChange(function (value) { - bloomPass.radius = Number(value); - render(); - }); - -const toneMappingFolder = gui.addFolder('tone mapping'); - -toneMappingFolder.add(params, 'exposure', 0.1, 2).onChange(function (value) { - renderer.toneMappingExposure = Math.pow(value, 4.0); - render(); -}); - -setupScene(); - -function onPointerDown(event) { - mouse.x = (event.clientX / window.innerWidth) * 2 - 1; - mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; - - raycaster.setFromCamera(mouse, camera); - const intersects = raycaster.intersectObjects(scene.children, false); - if (intersects.length > 0) { - const object = intersects[0].object; - object.layers.toggle(BLOOM_SCENE); - render(); - } -} - -window.onresize = function () { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); - - bloomComposer.setSize(width, height); - finalComposer.setSize(width, height); - - render(); -}; - -function setupScene() { - scene.traverse(disposeMaterial); - scene.children.length = 0; - - const geometry = new THREE.IcosahedronGeometry(1, 15); - - for (let i = 0; i < 50; i++) { - const color = new THREE.Color(); - color.setHSL(Math.random(), 0.7, Math.random() * 0.2 + 0.05); - - const material = new THREE.MeshStandardMaterial({ color: color, roughness: 1, metalness: 1 }); - const sphere = new THREE.Mesh(geometry, material); - sphere.position.x = Math.random() * 10 - 5; - sphere.position.y = Math.random() * 10 - 5; - sphere.position.z = Math.random() * 10 - 5; - sphere.position.normalize().multiplyScalar(Math.random() * 4.0 + 2.0); - sphere.scale.setScalar(Math.random() * Math.random() + 0.5); - scene.add(sphere); - - if (Math.random() < 0.25) sphere.layers.enable(BLOOM_SCENE); - } - - render(); -} - -function disposeMaterial(obj) { - if (obj.material) { - obj.material.dispose(); - } -} - -function render() { - scene.traverse(darkenNonBloomed); - bloomComposer.render(); - scene.traverse(restoreMaterial); - - // render the entire scene, then render bloom scene on top - finalComposer.render(); -} - -function darkenNonBloomed(obj) { - if (obj.isMesh && bloomLayer.test(obj.layers) === false) { - materials[obj.uuid] = obj.material; - obj.material = darkMaterial; - } -} - -function restoreMaterial(obj) { - if (materials[obj.uuid]) { - obj.material = materials[obj.uuid]; - delete materials[obj.uuid]; - } -} diff --git a/examples-testing/examples/webgl_random_uv.ts b/examples-testing/examples/webgl_random_uv.ts deleted file mode 100644 index fea6b3478..000000000 --- a/examples-testing/examples/webgl_random_uv.ts +++ /dev/null @@ -1,338 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; -import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let camera, scene, renderer, dirLight, ground, gui, material, materialIn, uniforms, uniformsIn; - -init(); -render(); - -function init() { - const container = document.getElementById('container'); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 20); - camera.position.set(-0.8, 0.6, 1.5); - - scene = new THREE.Scene(); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = 0.7; - renderer.shadowMap.enabled = true; - renderer.shadowMap.type = THREE.VSMShadowMap; - container.appendChild(renderer.domElement); - - dirLight = new THREE.DirectionalLight(0xffffff, 3); - dirLight.position.set(-0.5, 1, 0.8); - dirLight.castShadow = true; - scene.add(dirLight); - const shadow = dirLight.shadow; - shadow.mapSize.width = shadow.mapSize.height = 1024; - shadow.radius = 16; - shadow.bias = -0.0005; - const shadowCam = shadow.camera, - s = 2; - shadowCam.near = 0.5; - shadowCam.far = 3; - shadowCam.right = shadowCam.top = s; - shadowCam.left = shadowCam.bottom = -s; - // debug shadow - //scene.add( new THREE.CameraHelper(shadowCam) ); - - // add ground plane - const plane = new THREE.PlaneGeometry(2, 2); - plane.rotateX(-Math.PI * 0.5); - ground = new THREE.Mesh(plane, new THREE.ShadowMaterial({ opacity: 0.5 })); - ground.receiveShadow = true; - ground.position.z = -0.5; - scene.add(ground); - - const map = new THREE.TextureLoader().load('textures/jade.jpg'); - map.colorSpace = THREE.SRGBColorSpace; - map.wrapS = map.wrapT = THREE.RepeatWrapping; - map.repeat.set(20, 20); - map.flipY = false; - - const disolveMap = new THREE.TextureLoader().load('textures/shaderball_ds.jpg'); - disolveMap.flipY = false; - - const noise = new THREE.TextureLoader().load('textures/noise.png'); - - new HDRLoader().setPath('textures/equirectangular/').load('lobe.hdr', function (texture) { - texture.mapping = THREE.EquirectangularReflectionMapping; - - scene.background = texture; - scene.environment = texture; - scene.backgroundBlurriness = 0.5; - scene.backgroundIntensity = 1.0; - scene.environmentIntensity = 1.5; - - render(); - - // model - - const loader = new GLTFLoader().setPath('models/gltf/'); - loader.setDRACOLoader(new DRACOLoader().setDecoderPath('jsm/libs/draco/gltf/')); - loader.load('ShaderBall2.glb', function (gltf) { - const shaderBall = gltf.scene.children[0]; - - // shaderBall is a group with 3 children : base, inside and logo - // ao map is include in model - - let i = shaderBall.children.length, - n = 0; - - while (i--) { - shaderBall.children[i].receiveShadow = true; - shaderBall.children[i].castShadow = true; - shaderBall.children[i].renderOrder = n++; - } - - material = shaderBall.children[0].material; - material.map = map; - material.alphaMap = disolveMap; - material.transparent = true; - - materialIn = shaderBall.children[1].material; - materialIn.alphaMap = disolveMap; - materialIn.transparent = true; - - material.onBeforeCompile = function (shader) { - shader.uniforms['disolve'] = { value: 0 }; - shader.uniforms['threshold'] = { value: 0.2 }; - - shader.uniforms['noiseMap'] = { value: noise }; - shader.uniforms['enableRandom'] = { value: 1 }; - shader.uniforms['useNoiseMap'] = { value: 1 }; - shader.uniforms['useSuslikMethod'] = { value: 0 }; - shader.uniforms['debugNoise'] = { value: 0 }; - - shader.fragmentShader = shader.fragmentShader.replace( - '#include ', - '#include ' + randomUV, - ); - shader.fragmentShader = shader.fragmentShader.replace('#include ', map_fragment); - - // for disolve - shader.fragmentShader = shader.fragmentShader.replace( - '#include ', - alphamap_pars_fragment, - ); - shader.fragmentShader = shader.fragmentShader.replace( - '#include ', - alphamap_fragment, - ); - - uniforms = shader.uniforms; - }; - - materialIn.onBeforeCompile = function (shader) { - shader.uniforms['disolve'] = { value: 0 }; - shader.uniforms['threshold'] = { value: 0.2 }; - // for disolve - shader.fragmentShader = shader.fragmentShader.replace( - '#include ', - alphamap_pars_fragment, - ); - shader.fragmentShader = shader.fragmentShader.replace( - '#include ', - alphamap_fragment, - ); - - uniformsIn = shader.uniforms; - }; - - scene.add(shaderBall); - - render(); - - createGUI(); - }); - }); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.addEventListener('change', render); // use if there is no animation loop - controls.minDistance = 0.3; - controls.maxDistance = 10; - controls.target.set(0, 0.4, 0); - controls.update(); - - window.addEventListener('resize', onWindowResize); -} - -function createGUI() { - const setting = { - get Enabled() { - return uniforms.enableRandom.value ? true : false; - }, - set Enabled(v) { - uniforms.enableRandom.value = v ? 1 : 0; - render(); - }, - - get UseNoiseMap() { - return uniforms.useNoiseMap.value ? true : false; - }, - set UseNoiseMap(v) { - uniforms.useNoiseMap.value = v ? 1 : 0; - render(); - }, - - get SuslikMethod() { - return uniforms.useSuslikMethod.value ? true : false; - }, - set SuslikMethod(v) { - uniforms.useSuslikMethod.value = v ? 1 : 0; - render(); - }, - - get DebugNoise() { - return uniforms.debugNoise.value ? true : false; - }, - set DebugNoise(v) { - uniforms.debugNoise.value = v ? 1 : 0; - render(); - }, - - // disolve - get disolve() { - return uniforms.disolve.value; - }, - set disolve(v) { - uniforms.disolve.value = v; - uniformsIn.disolve.value = v; - ground.material.opacity = (1 - v) * 0.5; - render(); - }, - - get threshold() { - return uniforms.threshold.value; - }, - set threshold(v) { - uniforms.threshold.value = v; - uniformsIn.threshold.value = v; - render(); - }, - }; - - gui = new GUI(); - gui.add(material, 'roughness', 0, 1, 0.01).onChange(render); - gui.add(material, 'metalness', 0, 1, 0.01).onChange(render); - gui.add(setting, 'disolve', 0, 1, 0.01).onChange(render); - gui.add(setting, 'threshold', 0, 1, 0.01).onChange(render); - gui.add(setting, 'Enabled'); - gui.add(setting, 'UseNoiseMap'); - gui.add(setting, 'SuslikMethod'); - gui.add(setting, 'DebugNoise'); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - renderer.setSize(window.innerWidth, window.innerHeight); - - render(); -} - -// - -function render() { - renderer.render(scene, camera); -} - -const randomUV = /* glsl */ ` - - uniform sampler2D noiseMap; - uniform float enableRandom; - uniform float useNoiseMap; - uniform float debugNoise; - uniform float useSuslikMethod; - - float directNoise(vec2 p){ - vec2 ip = floor(p); - vec2 u = fract(p); - u = u*u*(3.0-2.0*u); - - float res = mix( - mix(rand(ip),rand(ip+vec2(1.0,0.0)),u.x), - mix(rand(ip+vec2(0.0,1.0)),rand(ip+vec2(1.0,1.0)),u.x),u.y); - return res*res; - } - - float sum( vec4 v ) { return v.x+v.y+v.z; } - - vec4 textureNoTile( sampler2D mapper, in vec2 uv ){ - - // sample variation pattern - float k = 0.0; - if( useNoiseMap == 1.0 ) k = texture2D( noiseMap, 0.005*uv ).x; - else k = directNoise( uv ); - - // compute index - float index = k*8.0; - float f = fract( index ); - float ia = 0.0; - float ib = 0.0; - - if( useSuslikMethod == 1.0 ){ - ia = floor(index+0.5); - ib = floor(index); - f = min(f, 1.0-f)*2.0; - } else { - ia = floor( index ); - ib = ia + 1.0; - } - - // offsets for the different virtual patterns - vec2 offa = sin(vec2(3.0,7.0)*ia); // can replace with any other hash - vec2 offb = sin(vec2(3.0,7.0)*ib); // can replace with any other hash - - // compute derivatives for mip-mapping - vec2 dx = dFdx(uv); - vec2 dy = dFdy(uv); - - // sample the two closest virtual patterns - vec4 cola = textureGrad( mapper, uv + offa, dx, dy ); - vec4 colb = textureGrad( mapper, uv + offb, dx, dy ); - if( debugNoise == 1.0 ){ - cola = vec4( 0.1,0.0,0.0,1.0 ); - colb = vec4( 0.0,0.0,1.0,1.0 ); - } - - // interpolate between the two virtual patterns - return mix( cola, colb, smoothstep(0.2,0.8,f-0.1*sum(cola-colb)) ); - - }`; - -const map_fragment = /* glsl */ ` - #ifdef USE_MAP - - if( enableRandom == 1.0 ) diffuseColor *= textureNoTile( map, vMapUv ); - else diffuseColor *= texture2D( map, vMapUv ); - - #endif - `; - -const alphamap_pars_fragment = /* glsl */ ` - #ifdef USE_ALPHAMAP - uniform sampler2D alphaMap; - uniform float disolve; - uniform float threshold; - #endif - `; - -const alphamap_fragment = /* glsl */ ` - #ifdef USE_ALPHAMAP - float vv = texture2D( alphaMap, vAlphaMapUv ).g; - float r = disolve * (1.0 + threshold * 2.0) - threshold; - float mixf = clamp((vv - r)*(1.0/threshold), 0.0, 1.0); - diffuseColor.a = mixf; - #endif - `; diff --git a/examples-testing/examples/webgl_raycaster_sprite.ts b/examples-testing/examples/webgl_raycaster_sprite.ts deleted file mode 100644 index f35d5de17..000000000 --- a/examples-testing/examples/webgl_raycaster_sprite.ts +++ /dev/null @@ -1,103 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let renderer, scene, camera; -let group; - -let selectedObject = null; -const raycaster = new THREE.Raycaster(); -const pointer = new THREE.Vector2(); - -init(); - -function init() { - // init renderer - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // init scene - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xffffff); - - group = new THREE.Group(); - scene.add(group); - - // init camera - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(15, 15, 15); - camera.lookAt(scene.position); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 15; - controls.maxDistance = 250; - - // add sprites - - const sprite1 = new THREE.Sprite(new THREE.SpriteMaterial({ color: '#69f' })); - sprite1.position.set(6, 5, 5); - sprite1.scale.set(2, 5, 1); - group.add(sprite1); - - const sprite2 = new THREE.Sprite(new THREE.SpriteMaterial({ color: '#69f', sizeAttenuation: false })); - sprite2.material.rotation = (Math.PI / 3) * 4; - sprite2.position.set(8, -2, 2); - sprite2.center.set(0.5, 0); - sprite2.scale.set(0.1, 0.5, 0.1); - group.add(sprite2); - - const group2 = new THREE.Object3D(); - group2.scale.set(1, 2, 1); - group2.position.set(-5, 0, 0); - group2.rotation.set(Math.PI / 2, 0, 0); - group.add(group2); - - const sprite3 = new THREE.Sprite(new THREE.SpriteMaterial({ color: '#69f' })); - sprite3.position.set(0, 2, 5); - sprite3.scale.set(10, 2, 3); - sprite3.center.set(-0.1, 0); - sprite3.material.rotation = Math.PI / 3; - group2.add(sprite3); - - window.addEventListener('resize', onWindowResize); - document.addEventListener('pointermove', onPointerMove); -} - -function animate() { - renderer.render(scene, camera); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function onPointerMove(event) { - if (selectedObject) { - selectedObject.material.color.set('#69f'); - selectedObject = null; - } - - pointer.x = (event.clientX / window.innerWidth) * 2 - 1; - pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; - - raycaster.setFromCamera(pointer, camera); - - const intersects = raycaster.intersectObject(group, true); - - if (intersects.length > 0) { - const res = intersects.filter(function (res) { - return res && res.object; - })[0]; - - if (res && res.object) { - selectedObject = res.object; - selectedObject.material.color.set('#f00'); - } - } -} diff --git a/examples-testing/examples/webgl_raycaster_texture.ts b/examples-testing/examples/webgl_raycaster_texture.ts deleted file mode 100644 index 72c7054dc..000000000 --- a/examples-testing/examples/webgl_raycaster_texture.ts +++ /dev/null @@ -1,286 +0,0 @@ -import * as THREE from 'three'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -const WRAPPING = { - RepeatWrapping: THREE.RepeatWrapping, - ClampToEdgeWrapping: THREE.ClampToEdgeWrapping, - MirroredRepeatWrapping: THREE.MirroredRepeatWrapping, -}; - -const params = { - wrapS: THREE.RepeatWrapping, - wrapT: THREE.RepeatWrapping, - offsetX: 0, - offsetY: 0, - repeatX: 1, - repeatY: 1, - rotation: 0, -}; - -function CanvasTexture(parentTexture) { - this._canvas = document.createElement('canvas'); - this._canvas.width = this._canvas.height = 1024; - this._context2D = this._canvas.getContext('2d'); - - if (parentTexture) { - this._parentTexture.push(parentTexture); - parentTexture.image = this._canvas; - } - - const that = this; - this._background = document.createElement('img'); - this._background.addEventListener('load', function () { - that._canvas.width = that._background.naturalWidth; - that._canvas.height = that._background.naturalHeight; - - that._crossRadius = Math.ceil(Math.min(that._canvas.width, that._canvas.height / 30)); - that._crossMax = Math.ceil(0.70710678 * that._crossRadius); - that._crossMin = Math.ceil(that._crossMax / 10); - that._crossThickness = Math.ceil(that._crossMax / 10); - - that._draw(); - }); - this._background.crossOrigin = ''; - this._background.src = 'textures/uv_grid_opengl.jpg'; - - this._draw(); -} - -CanvasTexture.prototype = { - constructor: CanvasTexture, - - _canvas: null, - _context2D: null, - _xCross: 0, - _yCross: 0, - - _crossRadius: 57, - _crossMax: 40, - _crossMin: 4, - _crossThickness: 4, - - _parentTexture: [], - - addParent: function (parentTexture) { - if (this._parentTexture.indexOf(parentTexture) === -1) { - this._parentTexture.push(parentTexture); - parentTexture.image = this._canvas; - } - }, - - setCrossPosition: function (x, y) { - this._xCross = x * this._canvas.width; - this._yCross = y * this._canvas.height; - - this._draw(); - }, - - _draw: function () { - if (!this._context2D) return; - - this._context2D.clearRect(0, 0, this._canvas.width, this._canvas.height); - - // Background. - this._context2D.drawImage(this._background, 0, 0); - - // Yellow cross. - this._context2D.lineWidth = this._crossThickness * 3; - this._context2D.strokeStyle = '#FFFF00'; - - this._context2D.beginPath(); - this._context2D.moveTo(this._xCross - this._crossMax - 2, this._yCross - this._crossMax - 2); - this._context2D.lineTo(this._xCross - this._crossMin, this._yCross - this._crossMin); - - this._context2D.moveTo(this._xCross + this._crossMin, this._yCross + this._crossMin); - this._context2D.lineTo(this._xCross + this._crossMax + 2, this._yCross + this._crossMax + 2); - - this._context2D.moveTo(this._xCross - this._crossMax - 2, this._yCross + this._crossMax + 2); - this._context2D.lineTo(this._xCross - this._crossMin, this._yCross + this._crossMin); - - this._context2D.moveTo(this._xCross + this._crossMin, this._yCross - this._crossMin); - this._context2D.lineTo(this._xCross + this._crossMax + 2, this._yCross - this._crossMax - 2); - - this._context2D.stroke(); - - for (let i = 0; i < this._parentTexture.length; i++) { - this._parentTexture[i].needsUpdate = true; - } - }, -}; - -const width = window.innerWidth; -const height = window.innerHeight; - -let canvas; -let planeTexture, cubeTexture, circleTexture; - -let container; - -let camera, scene, renderer; - -const raycaster = new THREE.Raycaster(); -const mouse = new THREE.Vector2(); -const onClickPosition = new THREE.Vector2(); - -init(); - -function init() { - container = document.getElementById('container'); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xeeeeee); - - camera = new THREE.PerspectiveCamera(45, width / height, 1, 1000); - camera.position.x = -30; - camera.position.y = 40; - camera.position.z = 50; - camera.lookAt(scene.position); - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(width, height); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - // A cube, in the middle. - cubeTexture = new THREE.Texture(undefined, THREE.UVMapping, THREE.RepeatWrapping, THREE.RepeatWrapping); - cubeTexture.colorSpace = THREE.SRGBColorSpace; - canvas = new CanvasTexture(cubeTexture); - const cubeMaterial = new THREE.MeshBasicMaterial({ map: cubeTexture }); - const cubeGeometry = new THREE.BoxGeometry(20, 20, 20); - let uvs = cubeGeometry.attributes.uv.array; - // Set a specific texture mapping. - for (let i = 0; i < uvs.length; i++) { - uvs[i] *= 2; - } - - const cube = new THREE.Mesh(cubeGeometry, cubeMaterial); - cube.position.x = 4; - cube.position.y = -5; - cube.position.z = 0; - scene.add(cube); - - // A plane on the left - - planeTexture = new THREE.Texture( - undefined, - THREE.UVMapping, - THREE.MirroredRepeatWrapping, - THREE.MirroredRepeatWrapping, - ); - planeTexture.colorSpace = THREE.SRGBColorSpace; - canvas.addParent(planeTexture); - const planeMaterial = new THREE.MeshBasicMaterial({ map: planeTexture }); - const planeGeometry = new THREE.PlaneGeometry(25, 25, 1, 1); - uvs = planeGeometry.attributes.uv.array; - - // Set a specific texture mapping. - - for (let i = 0; i < uvs.length; i++) { - uvs[i] *= 2; - } - - const plane = new THREE.Mesh(planeGeometry, planeMaterial); - plane.position.x = -16; - plane.position.y = -5; - plane.position.z = 0; - scene.add(plane); - - // A circle on the right. - - circleTexture = new THREE.Texture(undefined, THREE.UVMapping, THREE.RepeatWrapping, THREE.RepeatWrapping); - circleTexture.colorSpace = THREE.SRGBColorSpace; - canvas.addParent(circleTexture); - const circleMaterial = new THREE.MeshBasicMaterial({ map: circleTexture }); - const circleGeometry = new THREE.CircleGeometry(25, 40, 0, Math.PI * 2); - uvs = circleGeometry.attributes.uv.array; - - // Set a specific texture mapping. - - for (let i = 0; i < uvs.length; i++) { - uvs[i] = (uvs[i] - 0.25) * 2; - } - - const circle = new THREE.Mesh(circleGeometry, circleMaterial); - circle.position.x = 24; - circle.position.y = -5; - circle.position.z = 0; - scene.add(circle); - - window.addEventListener('resize', onWindowResize); - container.addEventListener('mousemove', onMouseMove); - - // - - const gui = new GUI(); - gui.title('Circle Texture Settings'); - - gui.add(params, 'wrapS', WRAPPING).onChange(setwrapS); - gui.add(params, 'wrapT', WRAPPING).onChange(setwrapT); - gui.add(params, 'offsetX', 0, 5); - gui.add(params, 'offsetY', 0, 5); - gui.add(params, 'repeatX', 0, 5); - gui.add(params, 'repeatY', 0, 5); - gui.add(params, 'rotation', 0, 2 * Math.PI); - gui.open(); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function onMouseMove(evt) { - evt.preventDefault(); - - const array = getMousePosition(container, evt.clientX, evt.clientY); - onClickPosition.fromArray(array); - - const intersects = getIntersects(onClickPosition, scene.children); - - if (intersects.length > 0 && intersects[0].uv) { - const uv = intersects[0].uv; - intersects[0].object.material.map.transformUv(uv); - canvas.setCrossPosition(uv.x, uv.y); - } -} - -function getMousePosition(dom, x, y) { - const rect = dom.getBoundingClientRect(); - return [(x - rect.left) / rect.width, (y - rect.top) / rect.height]; -} - -function getIntersects(point, objects) { - mouse.set(point.x * 2 - 1, -(point.y * 2) + 1); - - raycaster.setFromCamera(mouse, camera); - - return raycaster.intersectObjects(objects, false); -} - -function animate() { - // update texture parameters - - circleTexture.offset.x = params.offsetX; - circleTexture.offset.y = params.offsetY; - circleTexture.repeat.x = params.repeatX; - circleTexture.repeat.y = params.repeatY; - circleTexture.rotation = params.rotation; - - // - - renderer.render(scene, camera); -} - -function setwrapS(value) { - circleTexture.wrapS = value; - circleTexture.needsUpdate = true; -} - -function setwrapT(value) { - circleTexture.wrapT = value; - circleTexture.needsUpdate = true; -} diff --git a/examples-testing/examples/webgl_read_float_buffer.ts b/examples-testing/examples/webgl_read_float_buffer.ts deleted file mode 100644 index 69f847729..000000000 --- a/examples-testing/examples/webgl_read_float_buffer.ts +++ /dev/null @@ -1,153 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -let container, stats; - -let cameraRTT, sceneRTT, sceneScreen, renderer, zmesh1, zmesh2; - -let mouseX = 0, - mouseY = 0; - -const windowHalfX = window.innerWidth / 2; -const windowHalfY = window.innerHeight / 2; - -let rtTexture, material, quad; - -let delta = 0.01; -let valueNode; - -init(); - -function init() { - container = document.getElementById('container'); - - cameraRTT = new THREE.OrthographicCamera( - window.innerWidth / -2, - window.innerWidth / 2, - window.innerHeight / 2, - window.innerHeight / -2, - 1, - 1000, - ); - cameraRTT.position.z = 500; - - // - - sceneRTT = new THREE.Scene(); - sceneScreen = new THREE.Scene(); - - let light = new THREE.DirectionalLight(0xffffff, 3); - light.position.set(0, 0, 1).normalize(); - sceneRTT.add(light); - - light = new THREE.DirectionalLight(0xffd5d5, 4.5); - light.position.set(0, 0, -1).normalize(); - sceneRTT.add(light); - - rtTexture = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight, { - minFilter: THREE.LinearFilter, - magFilter: THREE.NearestFilter, - format: THREE.RGBAFormat, - type: THREE.FloatType, - }); - - material = new THREE.ShaderMaterial({ - uniforms: { time: { value: 0.0 } }, - vertexShader: document.getElementById('vertexShader').textContent, - fragmentShader: document.getElementById('fragment_shader_pass_1').textContent, - }); - - const materialScreen = new THREE.ShaderMaterial({ - uniforms: { tDiffuse: { value: rtTexture.texture } }, - vertexShader: document.getElementById('vertexShader').textContent, - fragmentShader: document.getElementById('fragment_shader_screen').textContent, - - depthWrite: false, - }); - - const plane = new THREE.PlaneGeometry(window.innerWidth, window.innerHeight); - - quad = new THREE.Mesh(plane, material); - quad.position.z = -100; - sceneRTT.add(quad); - - const geometry = new THREE.TorusGeometry(100, 25, 15, 30); - - const mat1 = new THREE.MeshPhongMaterial({ color: 0x9c9c9c, specular: 0xffaa00, shininess: 5 }); - const mat2 = new THREE.MeshPhongMaterial({ color: 0x9c0000, specular: 0xff2200, shininess: 5 }); - - zmesh1 = new THREE.Mesh(geometry, mat1); - zmesh1.position.set(0, 0, 100); - zmesh1.scale.set(1.5, 1.5, 1.5); - sceneRTT.add(zmesh1); - - zmesh2 = new THREE.Mesh(geometry, mat2); - zmesh2.position.set(0, 150, 100); - zmesh2.scale.set(0.75, 0.75, 0.75); - sceneRTT.add(zmesh2); - - quad = new THREE.Mesh(plane, materialScreen); - quad.position.z = -100; - sceneScreen.add(quad); - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.autoClear = false; - - container.appendChild(renderer.domElement); - - stats = new Stats(); - container.appendChild(stats.dom); - - valueNode = document.getElementById('values'); - - document.addEventListener('mousemove', onDocumentMouseMove); -} - -function onDocumentMouseMove(event) { - mouseX = event.clientX - windowHalfX; - mouseY = event.clientY - windowHalfY; -} - -// - -function animate() { - render(); - stats.update(); -} - -function render() { - const time = Date.now() * 0.0015; - - if (zmesh1 && zmesh2) { - zmesh1.rotation.y = -time; - zmesh2.rotation.y = -time + Math.PI / 2; - } - - if (material.uniforms['time'].value > 1 || material.uniforms['time'].value < 0) { - delta *= -1; - } - - material.uniforms['time'].value += delta; - - renderer.clear(); - - // Render first scene into texture - - renderer.setRenderTarget(rtTexture); - renderer.clear(); - renderer.render(sceneRTT, cameraRTT); - - // Render full screen quad with generated texture - - renderer.setRenderTarget(null); - renderer.render(sceneScreen, cameraRTT); - - const read = new Float32Array(4); - renderer.readRenderTargetPixels(rtTexture, windowHalfX + mouseX, windowHalfY - mouseY, 1, 1, read); - - valueNode.innerHTML = 'r:' + read[0] + '
g:' + read[1] + '
b:' + read[2]; -} diff --git a/examples-testing/examples/webgl_refraction.ts b/examples-testing/examples/webgl_refraction.ts deleted file mode 100644 index b8ef7143d..000000000 --- a/examples-testing/examples/webgl_refraction.ts +++ /dev/null @@ -1,138 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { Refractor } from 'three/addons/objects/Refractor.js'; -import { WaterRefractionShader } from 'three/addons/shaders/WaterRefractionShader.js'; - -let camera, scene, renderer, timer; - -let refractor, smallSphere; - -init(); - -async function init() { - const container = document.getElementById('container'); - - timer = new THREE.Timer(); - timer.connect(document); - - // scene - scene = new THREE.Scene(); - - // camera - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 500); - camera.position.set(0, 75, 160); - - // refractor - - const refractorGeometry = new THREE.PlaneGeometry(90, 90); - - refractor = new Refractor(refractorGeometry, { - color: 0xcbcbcb, - textureWidth: 1024, - textureHeight: 1024, - shader: WaterRefractionShader, - }); - - refractor.position.set(0, 50, 0); - - scene.add(refractor); - - // load dudv map for distortion effect - - const loader = new THREE.TextureLoader(); - const dudvMap = await loader.loadAsync('textures/waterdudv.jpg'); - - dudvMap.wrapS = dudvMap.wrapT = THREE.RepeatWrapping; - refractor.material.uniforms.tDudv.value = dudvMap; - - // - - const geometry = new THREE.IcosahedronGeometry(5, 0); - const material = new THREE.MeshPhongMaterial({ color: 0xffffff, emissive: 0x333333, flatShading: true }); - smallSphere = new THREE.Mesh(geometry, material); - scene.add(smallSphere); - - // walls - const planeGeo = new THREE.PlaneGeometry(100.1, 100.1); - - const planeTop = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xffffff })); - planeTop.position.y = 100; - planeTop.rotateX(Math.PI / 2); - scene.add(planeTop); - - const planeBottom = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xffffff })); - planeBottom.rotateX(-Math.PI / 2); - scene.add(planeBottom); - - const planeBack = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0x7f7fff })); - planeBack.position.z = -50; - planeBack.position.y = 50; - scene.add(planeBack); - - const planeRight = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0x00ff00 })); - planeRight.position.x = 50; - planeRight.position.y = 50; - planeRight.rotateY(-Math.PI / 2); - scene.add(planeRight); - - const planeLeft = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xff0000 })); - planeLeft.position.x = -50; - planeLeft.position.y = 50; - planeLeft.rotateY(Math.PI / 2); - scene.add(planeLeft); - - // lights - const mainLight = new THREE.PointLight(0xe7e7e7, 2.5, 250, 0); - mainLight.position.y = 60; - scene.add(mainLight); - - const greenLight = new THREE.PointLight(0x00ff00, 0.5, 1000, 0); - greenLight.position.set(550, 50, 0); - scene.add(greenLight); - - const redLight = new THREE.PointLight(0xff0000, 0.5, 1000, 0); - redLight.position.set(-550, 50, 0); - scene.add(redLight); - - const blueLight = new THREE.PointLight(0xbbbbfe, 0.5, 1000, 0); - blueLight.position.set(0, 50, 550); - scene.add(blueLight); - - // renderer - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - // controls - const controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, 40, 0); - controls.maxDistance = 400; - controls.minDistance = 10; - controls.update(); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - const time = timer.getElapsed(); - - refractor.material.uniforms.time.value = time; - - smallSphere.position.set(Math.cos(time) * 30, Math.abs(Math.cos(time * 2)) * 20 + 5, Math.sin(time) * 30); - smallSphere.rotation.y = Math.PI / 2 - time; - smallSphere.rotation.z = time * 8; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_rtt.ts b/examples-testing/examples/webgl_rtt.ts deleted file mode 100644 index b80e78ed3..000000000 --- a/examples-testing/examples/webgl_rtt.ts +++ /dev/null @@ -1,171 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -let container, stats; - -let cameraRTT, camera, sceneRTT, sceneScreen, scene, renderer, zmesh1, zmesh2; - -let mouseX = 0, - mouseY = 0; - -const windowHalfX = window.innerWidth / 2; -const windowHalfY = window.innerHeight / 2; - -let rtTexture, material, quad; - -let delta = 0.01; - -init(); - -function init() { - container = document.getElementById('container'); - - camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 10000); - camera.position.z = 100; - - cameraRTT = new THREE.OrthographicCamera( - window.innerWidth / -2, - window.innerWidth / 2, - window.innerHeight / 2, - window.innerHeight / -2, - 1, - 1000, - ); - cameraRTT.position.z = 500; - - // - - scene = new THREE.Scene(); - sceneRTT = new THREE.Scene(); - sceneScreen = new THREE.Scene(); - - let light = new THREE.DirectionalLight(0xffffff, 3); - light.position.set(0, 0, 1).normalize(); - sceneRTT.add(light); - - light = new THREE.DirectionalLight(0xffd5d5, 4.5); - light.position.set(0, 0, -1).normalize(); - sceneRTT.add(light); - - rtTexture = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight); - - material = new THREE.ShaderMaterial({ - uniforms: { time: { value: 0.0 } }, - vertexShader: document.getElementById('vertexShader').textContent, - fragmentShader: document.getElementById('fragment_shader_pass_1').textContent, - }); - - const materialScreen = new THREE.ShaderMaterial({ - uniforms: { tDiffuse: { value: rtTexture.texture } }, - vertexShader: document.getElementById('vertexShader').textContent, - fragmentShader: document.getElementById('fragment_shader_screen').textContent, - - depthWrite: false, - }); - - const plane = new THREE.PlaneGeometry(window.innerWidth, window.innerHeight); - - quad = new THREE.Mesh(plane, material); - quad.position.z = -100; - sceneRTT.add(quad); - - const torusGeometry = new THREE.TorusGeometry(100, 25, 15, 30); - - const mat1 = new THREE.MeshPhongMaterial({ color: 0x9c9c9c, specular: 0xffaa00, shininess: 5 }); - const mat2 = new THREE.MeshPhongMaterial({ color: 0x9c0000, specular: 0xff2200, shininess: 5 }); - - zmesh1 = new THREE.Mesh(torusGeometry, mat1); - zmesh1.position.set(0, 0, 100); - zmesh1.scale.set(1.5, 1.5, 1.5); - sceneRTT.add(zmesh1); - - zmesh2 = new THREE.Mesh(torusGeometry, mat2); - zmesh2.position.set(0, 150, 100); - zmesh2.scale.set(0.75, 0.75, 0.75); - sceneRTT.add(zmesh2); - - quad = new THREE.Mesh(plane, materialScreen); - quad.position.z = -100; - sceneScreen.add(quad); - - const n = 5, - geometry = new THREE.SphereGeometry(10, 64, 32), - material2 = new THREE.MeshBasicMaterial({ color: 0xffffff, map: rtTexture.texture }); - - for (let j = 0; j < n; j++) { - for (let i = 0; i < n; i++) { - const mesh = new THREE.Mesh(geometry, material2); - - mesh.position.x = (i - (n - 1) / 2) * 20; - mesh.position.y = (j - (n - 1) / 2) * 20; - mesh.position.z = 0; - - mesh.rotation.y = -Math.PI / 2; - - scene.add(mesh); - } - } - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.autoClear = false; - - container.appendChild(renderer.domElement); - - stats = new Stats(); - container.appendChild(stats.dom); - - document.addEventListener('mousemove', onDocumentMouseMove); -} - -function onDocumentMouseMove(event) { - mouseX = event.clientX - windowHalfX; - mouseY = event.clientY - windowHalfY; -} - -// - -function animate() { - render(); - stats.update(); -} - -function render() { - const time = Date.now() * 0.0015; - - camera.position.x += (mouseX - camera.position.x) * 0.05; - camera.position.y += (-mouseY - camera.position.y) * 0.05; - - camera.lookAt(scene.position); - - if (zmesh1 && zmesh2) { - zmesh1.rotation.y = -time; - zmesh2.rotation.y = -time + Math.PI / 2; - } - - if (material.uniforms['time'].value > 1 || material.uniforms['time'].value < 0) { - delta *= -1; - } - - material.uniforms['time'].value += delta; - - // Render first scene into texture - - renderer.setRenderTarget(rtTexture); - renderer.clear(); - renderer.render(sceneRTT, cameraRTT); - - // Render full screen quad with generated texture - - renderer.setRenderTarget(null); - renderer.clear(); - renderer.render(sceneScreen, cameraRTT); - - // Render second scene to screen - // (using first scene as regular texture) - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_shader.ts b/examples-testing/examples/webgl_shader.ts deleted file mode 100644 index 47a6c7ece..000000000 --- a/examples-testing/examples/webgl_shader.ts +++ /dev/null @@ -1,50 +0,0 @@ -import * as THREE from 'three'; - -let camera, scene, renderer; - -let uniforms; - -init(); - -function init() { - const container = document.getElementById('container'); - - camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1); - - scene = new THREE.Scene(); - - const geometry = new THREE.PlaneGeometry(2, 2); - - uniforms = { - time: { value: 1.0 }, - }; - - const material = new THREE.ShaderMaterial({ - uniforms: uniforms, - vertexShader: document.getElementById('vertexShader').textContent, - fragmentShader: document.getElementById('fragmentShader').textContent, - }); - - const mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - uniforms['time'].value = performance.now() / 1000; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_shader_lava.ts b/examples-testing/examples/webgl_shader_lava.ts deleted file mode 100644 index 0c974f5b9..000000000 --- a/examples-testing/examples/webgl_shader_lava.ts +++ /dev/null @@ -1,104 +0,0 @@ -import * as THREE from 'three'; - -import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; -import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; -import { BloomPass } from 'three/addons/postprocessing/BloomPass.js'; -import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; - -let camera, renderer, composer, timer; - -let uniforms, mesh; - -init(); - -function init() { - const container = document.getElementById('container'); - - camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 3000); - camera.position.z = 4; - - const scene = new THREE.Scene(); - - timer = new THREE.Timer(); - timer.connect(document); - - const textureLoader = new THREE.TextureLoader(); - - const cloudTexture = textureLoader.load('textures/lava/cloud.png'); - const lavaTexture = textureLoader.load('textures/lava/lavatile.jpg'); - - lavaTexture.colorSpace = THREE.SRGBColorSpace; - - cloudTexture.wrapS = cloudTexture.wrapT = THREE.RepeatWrapping; - lavaTexture.wrapS = lavaTexture.wrapT = THREE.RepeatWrapping; - - uniforms = { - fogDensity: { value: 0.45 }, - fogColor: { value: new THREE.Vector3(0, 0, 0) }, - time: { value: 1.0 }, - uvScale: { value: new THREE.Vector2(3.0, 1.0) }, - texture1: { value: cloudTexture }, - texture2: { value: lavaTexture }, - }; - - const size = 0.65; - - const material = new THREE.ShaderMaterial({ - uniforms: uniforms, - vertexShader: document.getElementById('vertexShader').textContent, - fragmentShader: document.getElementById('fragmentShader').textContent, - }); - - mesh = new THREE.Mesh(new THREE.TorusGeometry(size, 0.3, 30, 30), material); - mesh.rotation.x = 0.3; - scene.add(mesh); - - // - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.autoClear = false; - container.appendChild(renderer.domElement); - - // - - const renderModel = new RenderPass(scene, camera); - const effectBloom = new BloomPass(1.25); - const outputPass = new OutputPass(); - - composer = new EffectComposer(renderer); - - composer.addPass(renderModel); - composer.addPass(effectBloom); - composer.addPass(outputPass); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - composer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - timer.update(); - - const delta = 5 * timer.getDelta(); - - uniforms['time'].value += 0.2 * delta; - - mesh.rotation.y += 0.0125 * delta; - mesh.rotation.x += 0.05 * delta; - - renderer.clear(); - composer.render(0.01); -} diff --git a/examples-testing/examples/webgl_shaders_ocean.ts b/examples-testing/examples/webgl_shaders_ocean.ts deleted file mode 100644 index e0e26ed33..000000000 --- a/examples-testing/examples/webgl_shaders_ocean.ts +++ /dev/null @@ -1,195 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { Water } from 'three/addons/objects/Water.js'; -import { Sky } from 'three/addons/objects/Sky.js'; -import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js'; - -let container, stats; -let camera, scene, renderer; -let controls, water, sun, sky, mesh, bloomPass; - -init(); - -function init() { - container = document.getElementById('container'); - - // - - renderer = new THREE.WebGLRenderer({ outputBufferType: THREE.HalfFloatType }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = 0.1; - container.appendChild(renderer.domElement); - - bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85); - bloomPass.threshold = 0; - bloomPass.strength = 0.1; - bloomPass.radius = 0; - renderer.setEffects([bloomPass]); - - // - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight, 1, 20000); - camera.position.set(30, 30, 100); - - // - - sun = new THREE.Vector3(); - - // Water - - const waterGeometry = new THREE.PlaneGeometry(10000, 10000); - - water = new Water(waterGeometry, { - textureWidth: 512, - textureHeight: 512, - waterNormals: new THREE.TextureLoader().load('textures/waternormals.jpg', function (texture) { - texture.wrapS = texture.wrapT = THREE.RepeatWrapping; - }), - sunDirection: new THREE.Vector3(), - sunColor: 0xffffff, - waterColor: 0x001e0f, - distortionScale: 3.7, - fog: scene.fog !== undefined, - }); - - water.rotation.x = -Math.PI / 2; - - scene.add(water); - - // Skybox - - sky = new Sky(); - sky.scale.setScalar(10000); - scene.add(sky); - - const skyUniforms = sky.material.uniforms; - - skyUniforms['turbidity'].value = 10; - skyUniforms['rayleigh'].value = 2; - skyUniforms['mieCoefficient'].value = 0.005; - skyUniforms['mieDirectionalG'].value = 0.8; - skyUniforms['cloudCoverage'].value = 0.4; - skyUniforms['cloudDensity'].value = 0.5; - skyUniforms['cloudElevation'].value = 0.5; - - const parameters = { - elevation: 2, - azimuth: 180, - exposure: 0.1, - }; - - const pmremGenerator = new THREE.PMREMGenerator(renderer); - const sceneEnv = new THREE.Scene(); - - let renderTarget; - - function updateSun() { - const phi = THREE.MathUtils.degToRad(90 - parameters.elevation); - const theta = THREE.MathUtils.degToRad(parameters.azimuth); - - sun.setFromSphericalCoords(1, phi, theta); - - sky.material.uniforms['sunPosition'].value.copy(sun); - water.material.uniforms['sunDirection'].value.copy(sun).normalize(); - - if (renderTarget !== undefined) renderTarget.dispose(); - - sceneEnv.add(sky); - renderTarget = pmremGenerator.fromScene(sceneEnv); - scene.add(sky); - - scene.environment = renderTarget.texture; - } - - updateSun(); - - // - - const geometry = new THREE.BoxGeometry(30, 30, 30); - const material = new THREE.MeshStandardMaterial({ roughness: 0 }); - - mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - // - - controls = new OrbitControls(camera, renderer.domElement); - controls.maxPolarAngle = Math.PI * 0.495; - controls.target.set(0, 10, 0); - controls.minDistance = 40.0; - controls.maxDistance = 200.0; - controls.update(); - - // - - stats = new Stats(); - container.appendChild(stats.dom); - - // GUI - - const gui = new GUI(); - - const folderSky = gui.addFolder('Sky'); - folderSky.add(parameters, 'elevation', 0, 90, 0.1).onChange(updateSun); - folderSky.add(parameters, 'azimuth', -180, 180, 0.1).onChange(updateSun); - folderSky.add(parameters, 'exposure', 0, 1, 0.0001).onChange(function (value) { - renderer.toneMappingExposure = value; - }); - folderSky.open(); - - const waterUniforms = water.material.uniforms; - - const folderWater = gui.addFolder('Water'); - folderWater.add(waterUniforms.distortionScale, 'value', 0, 8, 0.1).name('distortionScale'); - folderWater.add(waterUniforms.size, 'value', 0.1, 10, 0.1).name('size'); - folderWater.open(); - - const folderBloom = gui.addFolder('Bloom'); - folderBloom.add(bloomPass, 'strength', 0, 3, 0.01); - folderBloom.add(bloomPass, 'radius', 0, 1, 0.01); - folderBloom.open(); - - const folderClouds = gui.addFolder('Clouds'); - folderClouds.add(skyUniforms.cloudCoverage, 'value', 0, 1, 0.01).name('coverage'); - folderClouds.add(skyUniforms.cloudDensity, 'value', 0, 1, 0.01).name('density'); - folderClouds.add(skyUniforms.cloudElevation, 'value', 0, 1, 0.01).name('elevation'); - folderClouds.open(); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - render(); - stats.update(); -} - -function render() { - const time = performance.now() * 0.001; - - mesh.position.y = Math.sin(time) * 20 + 5; - mesh.rotation.x = time * 0.5; - mesh.rotation.z = time * 0.51; - - water.material.uniforms['time'].value += 1.0 / 60.0; - sky.material.uniforms['time'].value = time; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_shaders_sky.ts b/examples-testing/examples/webgl_shaders_sky.ts deleted file mode 100644 index 01cdddec4..000000000 --- a/examples-testing/examples/webgl_shaders_sky.ts +++ /dev/null @@ -1,114 +0,0 @@ -import * as THREE from 'three'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { Sky } from 'three/addons/objects/Sky.js'; - -let camera, scene, renderer; - -let sky, sun; - -init(); - -function initSky() { - // Add Sky - sky = new Sky(); - sky.scale.setScalar(450000); - scene.add(sky); - - sun = new THREE.Vector3(); - - /// GUI - - const effectController = { - turbidity: 10, - rayleigh: 3, - mieCoefficient: 0.005, - mieDirectionalG: 0.7, - elevation: 2, - azimuth: 180, - exposure: renderer.toneMappingExposure, - cloudCoverage: 0.4, - cloudDensity: 0.4, - cloudElevation: 0.5, - showSunDisc: true, - }; - - function guiChanged() { - const uniforms = sky.material.uniforms; - uniforms['turbidity'].value = effectController.turbidity; - uniforms['rayleigh'].value = effectController.rayleigh; - uniforms['mieCoefficient'].value = effectController.mieCoefficient; - uniforms['mieDirectionalG'].value = effectController.mieDirectionalG; - uniforms['cloudCoverage'].value = effectController.cloudCoverage; - uniforms['cloudDensity'].value = effectController.cloudDensity; - uniforms['cloudElevation'].value = effectController.cloudElevation; - uniforms['showSunDisc'].value = effectController.showSunDisc; - - const phi = THREE.MathUtils.degToRad(90 - effectController.elevation); - const theta = THREE.MathUtils.degToRad(effectController.azimuth); - - sun.setFromSphericalCoords(1, phi, theta); - - uniforms['sunPosition'].value.copy(sun); - - renderer.toneMappingExposure = effectController.exposure; - } - - const gui = new GUI(); - - gui.add(effectController, 'turbidity', 0.0, 20.0, 0.1).onChange(guiChanged); - gui.add(effectController, 'rayleigh', 0.0, 4, 0.001).onChange(guiChanged); - gui.add(effectController, 'mieCoefficient', 0.0, 0.1, 0.001).onChange(guiChanged); - gui.add(effectController, 'mieDirectionalG', 0.0, 1, 0.001).onChange(guiChanged); - gui.add(effectController, 'elevation', 0, 90, 0.1).onChange(guiChanged); - gui.add(effectController, 'azimuth', -180, 180, 0.1).onChange(guiChanged); - gui.add(effectController, 'exposure', 0, 1, 0.0001).onChange(guiChanged); - gui.add(effectController, 'showSunDisc').onChange(guiChanged); - - const folderClouds = gui.addFolder('Clouds'); - folderClouds.add(effectController, 'cloudCoverage', 0, 1, 0.01).name('coverage').onChange(guiChanged); - folderClouds.add(effectController, 'cloudDensity', 0, 1, 0.01).name('density').onChange(guiChanged); - folderClouds.add(effectController, 'cloudElevation', 0, 1, 0.01).name('elevation').onChange(guiChanged); - - guiChanged(); -} - -function init() { - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 100, 2000000); - camera.position.set(0, 100, 2000); - - scene = new THREE.Scene(); - - const helper = new THREE.GridHelper(10000, 2, 0xffffff, 0xffffff); - scene.add(helper); - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = 0.5; - document.body.appendChild(renderer.domElement); - - const controls = new OrbitControls(camera, renderer.domElement); - //controls.maxPolarAngle = Math.PI / 2; - controls.enableZoom = false; - controls.enablePan = false; - - initSky(); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - sky.material.uniforms['time'].value = performance.now() * 0.001; - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_shadow_contact.ts b/examples-testing/examples/webgl_shadow_contact.ts deleted file mode 100644 index f402fa20d..000000000 --- a/examples-testing/examples/webgl_shadow_contact.ts +++ /dev/null @@ -1,272 +0,0 @@ -import * as THREE from 'three'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import Stats from 'three/addons/libs/stats.module.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import { HorizontalBlurShader } from 'three/addons/shaders/HorizontalBlurShader.js'; -import { VerticalBlurShader } from 'three/addons/shaders/VerticalBlurShader.js'; - -let camera, scene, renderer, stats, gui; - -const meshes = []; - -const PLANE_WIDTH = 2.5; -const PLANE_HEIGHT = 2.5; -const CAMERA_HEIGHT = 0.3; - -const state = { - shadow: { - blur: 3.5, - darkness: 1, - opacity: 1, - }, - plane: { - color: '#ffffff', - opacity: 1, - }, - showWireframe: false, -}; - -let shadowGroup, - renderTarget, - renderTargetBlur, - shadowCamera, - cameraHelper, - depthMaterial, - horizontalBlurMaterial, - verticalBlurMaterial; - -let plane, blurPlane, fillPlane; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(0.5, 1, 2); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xffffff); - - stats = new Stats(); - document.body.appendChild(stats.dom); - - window.addEventListener('resize', onWindowResize); - - // add the example meshes - - const geometries = [ - new THREE.BoxGeometry(0.4, 0.4, 0.4), - new THREE.IcosahedronGeometry(0.3), - new THREE.TorusKnotGeometry(0.4, 0.05, 256, 24, 1, 3), - ]; - - const material = new THREE.MeshNormalMaterial(); - - for (let i = 0, l = geometries.length; i < l; i++) { - const angle = (i / l) * Math.PI * 2; - - const geometry = geometries[i]; - const mesh = new THREE.Mesh(geometry, material); - mesh.position.y = 0.1; - mesh.position.x = Math.cos(angle) / 2.0; - mesh.position.z = Math.sin(angle) / 2.0; - scene.add(mesh); - meshes.push(mesh); - } - - // the container, if you need to move the plane just move this - shadowGroup = new THREE.Group(); - shadowGroup.position.y = -0.3; - scene.add(shadowGroup); - - // the render target that will show the shadows in the plane texture - renderTarget = new THREE.WebGLRenderTarget(512, 512); - renderTarget.texture.generateMipmaps = false; - - // the render target that we will use to blur the first render target - renderTargetBlur = new THREE.WebGLRenderTarget(512, 512); - renderTargetBlur.texture.generateMipmaps = false; - - // make a plane and make it face up - const planeGeometry = new THREE.PlaneGeometry(PLANE_WIDTH, PLANE_HEIGHT).rotateX(Math.PI / 2); - const planeMaterial = new THREE.MeshBasicMaterial({ - map: renderTarget.texture, - opacity: state.shadow.opacity, - transparent: true, - depthWrite: false, - }); - plane = new THREE.Mesh(planeGeometry, planeMaterial); - // make sure it's rendered after the fillPlane - plane.renderOrder = 1; - shadowGroup.add(plane); - - // the y from the texture is flipped! - plane.scale.y = -1; - - // the plane onto which to blur the texture - blurPlane = new THREE.Mesh(planeGeometry); - blurPlane.visible = false; - shadowGroup.add(blurPlane); - - // the plane with the color of the ground - const fillPlaneMaterial = new THREE.MeshBasicMaterial({ - color: state.plane.color, - opacity: state.plane.opacity, - transparent: true, - depthWrite: false, - }); - fillPlane = new THREE.Mesh(planeGeometry, fillPlaneMaterial); - fillPlane.rotateX(Math.PI); - shadowGroup.add(fillPlane); - - // the camera to render the depth material from - shadowCamera = new THREE.OrthographicCamera( - -PLANE_WIDTH / 2, - PLANE_WIDTH / 2, - PLANE_HEIGHT / 2, - -PLANE_HEIGHT / 2, - 0, - CAMERA_HEIGHT, - ); - shadowCamera.rotation.x = Math.PI / 2; // get the camera to look up - shadowGroup.add(shadowCamera); - - cameraHelper = new THREE.CameraHelper(shadowCamera); - - // like MeshDepthMaterial, but goes from black to transparent - depthMaterial = new THREE.MeshDepthMaterial(); - depthMaterial.userData.darkness = { value: state.shadow.darkness }; - depthMaterial.onBeforeCompile = function (shader) { - shader.uniforms.darkness = depthMaterial.userData.darkness; - shader.fragmentShader = /* glsl */ ` - uniform float darkness; - ${shader.fragmentShader.replace( - 'gl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity );', - 'gl_FragColor = vec4( vec3( 0.0 ), ( 1.0 - fragCoordZ ) * darkness );', - )} - `; - }; - - depthMaterial.depthTest = false; - depthMaterial.depthWrite = false; - - horizontalBlurMaterial = new THREE.ShaderMaterial(HorizontalBlurShader); - horizontalBlurMaterial.depthTest = false; - - verticalBlurMaterial = new THREE.ShaderMaterial(VerticalBlurShader); - verticalBlurMaterial.depthTest = false; - - // - - gui = new GUI(); - const shadowFolder = gui.addFolder('shadow'); - shadowFolder.open(); - const planeFolder = gui.addFolder('plane'); - planeFolder.open(); - - shadowFolder.add(state.shadow, 'blur', 0, 15, 0.1); - shadowFolder.add(state.shadow, 'darkness', 1, 5, 0.1).onChange(function () { - depthMaterial.userData.darkness.value = state.shadow.darkness; - }); - shadowFolder.add(state.shadow, 'opacity', 0, 1, 0.01).onChange(function () { - plane.material.opacity = state.shadow.opacity; - }); - planeFolder.addColor(state.plane, 'color').onChange(function () { - fillPlane.material.color = new THREE.Color(state.plane.color); - }); - planeFolder.add(state.plane, 'opacity', 0, 1, 0.01).onChange(function () { - fillPlane.material.opacity = state.plane.opacity; - }); - - gui.add(state, 'showWireframe').onChange(function () { - if (state.showWireframe) { - scene.add(cameraHelper); - } else { - scene.remove(cameraHelper); - } - }); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // - - new OrbitControls(camera, renderer.domElement); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// renderTarget --> blurPlane (horizontalBlur) --> renderTargetBlur --> blurPlane (verticalBlur) --> renderTarget -function blurShadow(amount) { - blurPlane.visible = true; - - // blur horizontally and draw in the renderTargetBlur - blurPlane.material = horizontalBlurMaterial; - blurPlane.material.uniforms.tDiffuse.value = renderTarget.texture; - horizontalBlurMaterial.uniforms.h.value = (amount * 1) / 256; - - renderer.setRenderTarget(renderTargetBlur); - renderer.render(blurPlane, shadowCamera); - - // blur vertically and draw in the main renderTarget - blurPlane.material = verticalBlurMaterial; - blurPlane.material.uniforms.tDiffuse.value = renderTargetBlur.texture; - verticalBlurMaterial.uniforms.v.value = (amount * 1) / 256; - - renderer.setRenderTarget(renderTarget); - renderer.render(blurPlane, shadowCamera); - - blurPlane.visible = false; -} - -function animate() { - meshes.forEach(mesh => { - mesh.rotation.x += 0.01; - mesh.rotation.y += 0.02; - }); - - // - - // remove the background - const initialBackground = scene.background; - scene.background = null; - - // force the depthMaterial to everything - cameraHelper.visible = false; - scene.overrideMaterial = depthMaterial; - - // set renderer clear alpha - const initialClearAlpha = renderer.getClearAlpha(); - renderer.setClearAlpha(0); - - // render to the render target to get the depths - renderer.setRenderTarget(renderTarget); - renderer.render(scene, shadowCamera); - - // and reset the override material - scene.overrideMaterial = null; - cameraHelper.visible = true; - - blurShadow(state.shadow.blur); - - // a second pass to reduce the artifacts - // (0.4 is the minimum blur amount so that the artifacts are gone) - blurShadow(state.shadow.blur * 0.4); - - // reset and render the normal scene - renderer.setRenderTarget(null); - renderer.setClearAlpha(initialClearAlpha); - scene.background = initialBackground; - - renderer.render(scene, camera); - stats.update(); -} diff --git a/examples-testing/examples/webgl_shadowmap.ts b/examples-testing/examples/webgl_shadowmap.ts deleted file mode 100644 index 9557bb051..000000000 --- a/examples-testing/examples/webgl_shadowmap.ts +++ /dev/null @@ -1,310 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { FontLoader } from 'three/addons/loaders/FontLoader.js'; -import { TextGeometry } from 'three/addons/geometries/TextGeometry.js'; -import { ShadowMapViewer } from 'three/addons/utils/ShadowMapViewer.js'; - -const SHADOW_MAP_WIDTH = 2048, - SHADOW_MAP_HEIGHT = 1024; - -const SCREEN_WIDTH = window.innerWidth; -const SCREEN_HEIGHT = window.innerHeight; -const FLOOR = -250; - -let camera, controls, scene, renderer; -let container, stats; - -const NEAR = 10, - FAR = 3000; - -let mixer; - -const morphs = []; - -let light; -let lightShadowMapViewer; - -const timer = new THREE.Timer(); -timer.connect(document); - -let showHUD = false; - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - // CAMERA - - camera = new THREE.PerspectiveCamera(23, SCREEN_WIDTH / SCREEN_HEIGHT, NEAR, FAR); - camera.position.set(700, 50, 1900); - - // SCENE - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x59472b); - scene.fog = new THREE.Fog(0x59472b, 1000, FAR); - - // LIGHTS - - const ambient = new THREE.AmbientLight(0xffffff); - scene.add(ambient); - - light = new THREE.DirectionalLight(0xffffff, 3); - light.position.set(0, 1500, 1000); - light.castShadow = true; - light.shadow.camera.top = 2000; - light.shadow.camera.bottom = -2000; - light.shadow.camera.left = -2000; - light.shadow.camera.right = 2000; - light.shadow.camera.near = 1200; - light.shadow.camera.far = 2500; - light.shadow.bias = 0.0001; - - light.shadow.mapSize.width = SHADOW_MAP_WIDTH; - light.shadow.mapSize.height = SHADOW_MAP_HEIGHT; - - scene.add(light); - - createHUD(); - createScene(); - - // RENDERER - - renderer = new THREE.WebGLRenderer({ antialias: true, reversedDepthBuffer: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - renderer.autoClear = false; - - // - - renderer.shadowMap.enabled = true; - renderer.shadowMap.type = THREE.PCFShadowMap; - - // CONTROLS - - controls = new OrbitControls(camera, renderer.domElement); - controls.enablePan = false; - controls.maxPolarAngle = Math.PI / 2; - controls.minDistance = 200; - controls.maxDistance = 2200; - - controls.target.set(0, -75, 25); - controls.update(); - - // STATS - - stats = new Stats(); - //container.appendChild( stats.dom ); - - // - - window.addEventListener('resize', onWindowResize); - window.addEventListener('keydown', onKeyDown); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function onKeyDown(event) { - switch (event.keyCode) { - case 84 /*t*/: - showHUD = !showHUD; - break; - } -} - -function createHUD() { - lightShadowMapViewer = new ShadowMapViewer(light); - lightShadowMapViewer.position.x = 10; - lightShadowMapViewer.position.y = SCREEN_HEIGHT - SHADOW_MAP_HEIGHT / 4 - 10; - lightShadowMapViewer.size.width = SHADOW_MAP_WIDTH / 4; - lightShadowMapViewer.size.height = SHADOW_MAP_HEIGHT / 4; - lightShadowMapViewer.update(); -} - -function createScene() { - // GROUND - - const geometry = new THREE.PlaneGeometry(100, 100); - const planeMaterial = new THREE.MeshPhongMaterial({ color: 0xffdd99 }); - - const ground = new THREE.Mesh(geometry, planeMaterial); - - ground.position.set(0, FLOOR, 0); - ground.rotation.x = -Math.PI / 2; - ground.scale.set(100, 100, 100); - - ground.castShadow = false; - ground.receiveShadow = true; - - scene.add(ground); - - // TEXT - - const loader = new FontLoader(); - loader.load('fonts/helvetiker_bold.typeface.json', function (font) { - const textGeo = new TextGeometry('THREE.JS', { - font: font, - - size: 200, - depth: 50, - curveSegments: 12, - - bevelThickness: 2, - bevelSize: 5, - bevelEnabled: true, - }); - - textGeo.computeBoundingBox(); - const centerOffset = -0.5 * (textGeo.boundingBox.max.x - textGeo.boundingBox.min.x); - - const textMaterial = new THREE.MeshPhongMaterial({ color: 0xff0000, specular: 0xffffff }); - - const mesh = new THREE.Mesh(textGeo, textMaterial); - mesh.position.x = centerOffset; - mesh.position.y = FLOOR + 67; - - mesh.castShadow = true; - mesh.receiveShadow = true; - - scene.add(mesh); - }); - - // CUBES - - const cubes1 = new THREE.Mesh(new THREE.BoxGeometry(1500, 220, 150), planeMaterial); - - cubes1.position.y = FLOOR - 50; - cubes1.position.z = 20; - - cubes1.castShadow = true; - cubes1.receiveShadow = true; - - scene.add(cubes1); - - const cubes2 = new THREE.Mesh(new THREE.BoxGeometry(1600, 170, 250), planeMaterial); - - cubes2.position.y = FLOOR - 50; - cubes2.position.z = 20; - - cubes2.castShadow = true; - cubes2.receiveShadow = true; - - scene.add(cubes2); - - // MORPHS - - mixer = new THREE.AnimationMixer(scene); - - function addMorph(mesh, clip, speed, duration, x, y, z, fudgeColor) { - mesh = mesh.clone(); - mesh.material = mesh.material.clone(); - - if (fudgeColor) { - mesh.material.color.offsetHSL(0, Math.random() * 0.5 - 0.25, Math.random() * 0.5 - 0.25); - } - - mesh.speed = speed; - - mixer - .clipAction(clip, mesh) - .setDuration(duration) - // to shift the playback out of phase: - .startAt(-duration * Math.random()) - .play(); - - mesh.position.set(x, y, z); - mesh.rotation.y = Math.PI / 2; - - mesh.castShadow = true; - mesh.receiveShadow = true; - - scene.add(mesh); - - morphs.push(mesh); - } - - const gltfloader = new GLTFLoader(); - - gltfloader.load('models/gltf/Horse.glb', function (gltf) { - const mesh = gltf.scene.children[0]; - - const clip = gltf.animations[0]; - - addMorph(mesh, clip, 550, 1, 100 - Math.random() * 1000, FLOOR, 300, true); - addMorph(mesh, clip, 550, 1, 100 - Math.random() * 1000, FLOOR, 450, true); - addMorph(mesh, clip, 550, 1, 100 - Math.random() * 1000, FLOOR, 600, true); - - addMorph(mesh, clip, 550, 1, 100 - Math.random() * 1000, FLOOR, -300, true); - addMorph(mesh, clip, 550, 1, 100 - Math.random() * 1000, FLOOR, -450, true); - addMorph(mesh, clip, 550, 1, 100 - Math.random() * 1000, FLOOR, -600, true); - }); - - gltfloader.load('models/gltf/Flamingo.glb', function (gltf) { - const mesh = gltf.scene.children[0]; - const clip = gltf.animations[0]; - - addMorph(mesh, clip, 500, 1, 500 - Math.random() * 500, FLOOR + 350, 40); - }); - - gltfloader.load('models/gltf/Stork.glb', function (gltf) { - const mesh = gltf.scene.children[0]; - const clip = gltf.animations[0]; - - addMorph(mesh, clip, 350, 1, 500 - Math.random() * 500, FLOOR + 350, 340); - }); - - gltfloader.load('models/gltf/Parrot.glb', function (gltf) { - const mesh = gltf.scene.children[0]; - const clip = gltf.animations[0]; - - addMorph(mesh, clip, 450, 0.5, 500 - Math.random() * 500, FLOOR + 300, 700); - }); -} - -function animate() { - timer.update(); - - render(); - stats.update(); -} - -function render() { - const delta = timer.getDelta(); - - mixer.update(delta); - - for (let i = 0; i < morphs.length; i++) { - const morph = morphs[i]; - - morph.position.x += morph.speed * delta; - - if (morph.position.x > 2000) { - morph.position.x = -1000 - Math.random() * 500; - } - } - - controls.update(delta); - - renderer.clear(); - renderer.render(scene, camera); - - // Render debug HUD with shadow map - - if (showHUD) { - lightShadowMapViewer.render(renderer); - } -} diff --git a/examples-testing/examples/webgl_shadowmap_csm.ts b/examples-testing/examples/webgl_shadowmap_csm.ts deleted file mode 100644 index c8e959a0b..000000000 --- a/examples-testing/examples/webgl_shadowmap_csm.ts +++ /dev/null @@ -1,253 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import { CSM } from 'three/addons/csm/CSM.js'; -import { CSMHelper } from 'three/addons/csm/CSMHelper.js'; - -let renderer, scene, camera, orthoCamera, controls, csm, csmHelper; - -const params = { - orthographic: false, - fade: false, - shadows: true, - far: 1000, - mode: 'practical', - lightX: -1, - lightY: -1, - lightZ: -1, - margin: 100, - lightFar: 5000, - lightNear: 1, - autoUpdateHelper: true, - updateHelper: function () { - csmHelper.update(); - }, -}; - -init(); - -function updateOrthoCamera() { - const size = controls.target.distanceTo(camera.position); - const aspect = camera.aspect; - - orthoCamera.left = (size * aspect) / -2; - orthoCamera.right = (size * aspect) / 2; - - orthoCamera.top = size / 2; - orthoCamera.bottom = size / -2; - orthoCamera.position.copy(camera.position); - orthoCamera.rotation.copy(camera.rotation); - orthoCamera.updateProjectionMatrix(); -} - -function init() { - scene = new THREE.Scene(); - scene.background = new THREE.Color('#454e61'); - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 5000); - orthoCamera = new THREE.OrthographicCamera(); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - renderer.shadowMap.enabled = params.shadows; - renderer.shadowMap.type = THREE.PCFShadowMap; - - controls = new OrbitControls(camera, renderer.domElement); - controls.maxPolarAngle = Math.PI / 2; - camera.position.set(60, 60, 0); - controls.target = new THREE.Vector3(-100, 10, 0); - controls.update(); - - const ambientLight = new THREE.AmbientLight(0xffffff, 1.5); - scene.add(ambientLight); - - const additionalDirectionalLight = new THREE.DirectionalLight(0x000020, 1.5); - additionalDirectionalLight.position - .set(params.lightX, params.lightY, params.lightZ) - .normalize() - .multiplyScalar(-200); - scene.add(additionalDirectionalLight); - - csm = new CSM({ - maxFar: params.far, - cascades: 4, - mode: params.mode, - parent: scene, - shadowMapSize: 1024, - lightDirection: new THREE.Vector3(params.lightX, params.lightY, params.lightZ).normalize(), - camera: camera, - }); - - csmHelper = new CSMHelper(csm); - csmHelper.visible = false; - scene.add(csmHelper); - - const floorMaterial = new THREE.MeshPhongMaterial({ color: '#252a34' }); - csm.setupMaterial(floorMaterial); - - const floor = new THREE.Mesh(new THREE.PlaneGeometry(10000, 10000, 8, 8), floorMaterial); - floor.rotation.x = -Math.PI / 2; - floor.castShadow = true; - floor.receiveShadow = true; - scene.add(floor); - - const material1 = new THREE.MeshPhongMaterial({ color: '#08d9d6' }); - csm.setupMaterial(material1); - - const material2 = new THREE.MeshPhongMaterial({ color: '#ff2e63' }); - csm.setupMaterial(material2); - - const geometry = new THREE.BoxGeometry(10, 10, 10); - - for (let i = 0; i < 40; i++) { - const cube1 = new THREE.Mesh(geometry, i % 2 === 0 ? material1 : material2); - cube1.castShadow = true; - cube1.receiveShadow = true; - scene.add(cube1); - cube1.position.set(-i * 25, 20, 30); - cube1.scale.y = Math.random() * 2 + 6; - - const cube2 = new THREE.Mesh(geometry, i % 2 === 0 ? material2 : material1); - cube2.castShadow = true; - cube2.receiveShadow = true; - scene.add(cube2); - cube2.position.set(-i * 25, 20, -30); - cube2.scale.y = Math.random() * 2 + 6; - } - - const gui = new GUI(); - - gui.add(params, 'orthographic').onChange(function (value) { - csm.camera = value ? orthoCamera : camera; - csm.updateFrustums(); - }); - - gui.add(params, 'fade').onChange(function (value) { - csm.fade = value; - csm.updateFrustums(); - }); - - gui.add(params, 'shadows').onChange(function (value) { - renderer.shadowMap.enabled = value; - - scene.traverse(function (child) { - if (child.material) { - child.material.needsUpdate = true; - } - }); - }); - - gui.add(params, 'far', 1, 5000) - .step(1) - .name('shadow far') - .onChange(function (value) { - csm.maxFar = value; - csm.updateFrustums(); - }); - - gui.add(params, 'mode', ['uniform', 'logarithmic', 'practical']) - .name('frustum split mode') - .onChange(function (value) { - csm.mode = value; - csm.updateFrustums(); - }); - - gui.add(params, 'lightX', -1, 1) - .name('light direction x') - .onChange(function (value) { - csm.lightDirection.x = value; - }); - - gui.add(params, 'lightY', -1, 1) - .name('light direction y') - .onChange(function (value) { - csm.lightDirection.y = value; - }); - - gui.add(params, 'lightZ', -1, 1) - .name('light direction z') - .onChange(function (value) { - csm.lightDirection.z = value; - }); - - gui.add(params, 'margin', 0, 200) - .name('light margin') - .onChange(function (value) { - csm.lightMargin = value; - }); - - gui.add(params, 'lightNear', 1, 10000) - .name('light near') - .onChange(function (value) { - for (let i = 0; i < csm.lights.length; i++) { - csm.lights[i].shadow.camera.near = value; - csm.lights[i].shadow.camera.updateProjectionMatrix(); - } - }); - - gui.add(params, 'lightFar', 1, 10000) - .name('light far') - .onChange(function (value) { - for (let i = 0; i < csm.lights.length; i++) { - csm.lights[i].shadow.camera.far = value; - csm.lights[i].shadow.camera.updateProjectionMatrix(); - } - }); - - const helperFolder = gui.addFolder('helper'); - - helperFolder.add(csmHelper, 'visible'); - - helperFolder.add(csmHelper, 'displayFrustum').onChange(function () { - csmHelper.updateVisibility(); - }); - - helperFolder.add(csmHelper, 'displayPlanes').onChange(function () { - csmHelper.updateVisibility(); - }); - - helperFolder.add(csmHelper, 'displayShadowBounds').onChange(function () { - csmHelper.updateVisibility(); - }); - - helperFolder.add(params, 'autoUpdateHelper').name('auto update'); - - helperFolder.add(params, 'updateHelper').name('update'); - - helperFolder.open(); - - window.addEventListener('resize', function () { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - updateOrthoCamera(); - csm.updateFrustums(); - - renderer.setSize(window.innerWidth, window.innerHeight); - }); -} - -function animate() { - camera.updateMatrixWorld(); - csm.update(); - controls.update(); - - if (params.orthographic) { - updateOrthoCamera(); - csm.updateFrustums(); - - if (params.autoUpdateHelper) { - csmHelper.update(); - } - - renderer.render(scene, orthoCamera); - } else { - if (params.autoUpdateHelper) { - csmHelper.update(); - } - - renderer.render(scene, camera); - } -} diff --git a/examples-testing/examples/webgl_shadowmap_pcss.ts b/examples-testing/examples/webgl_shadowmap_pcss.ts deleted file mode 100644 index de5426883..000000000 --- a/examples-testing/examples/webgl_shadowmap_pcss.ts +++ /dev/null @@ -1,165 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let stats; -let camera, scene, renderer; - -let group; - -init(); - -function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - // scene - - scene = new THREE.Scene(); - scene.fog = new THREE.Fog(0xcce0ff, 5, 100); - - // camera - - camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 10000); - - // We use this particular camera position in order to expose a bug that can sometimes happen presumably - // due to lack of precision when interpolating values over really large triangles. - // It reproduced on at least NVIDIA GTX 1080 and GTX 1050 Ti GPUs when the ground plane was not - // subdivided into segments. - camera.position.x = 7; - camera.position.y = 13; - camera.position.z = 7; - - scene.add(camera); - - // lights - - scene.add(new THREE.AmbientLight(0xaaaaaa, 3)); - - const light = new THREE.DirectionalLight(0xf0f6ff, 4.5); - light.position.set(2, 8, 4); - - light.castShadow = true; - light.shadow.mapSize.width = 1024; - light.shadow.mapSize.height = 1024; - light.shadow.camera.far = 20; - - scene.add(light); - - // scene.add( new DirectionalLightHelper( light ) ); - scene.add(new THREE.CameraHelper(light.shadow.camera)); - - // group - - group = new THREE.Group(); - scene.add(group); - - const geometry = new THREE.SphereGeometry(0.3, 20, 20); - - for (let i = 0; i < 20; i++) { - const material = new THREE.MeshPhongMaterial({ color: Math.random() * 0xffffff }); - - const sphere = new THREE.Mesh(geometry, material); - sphere.position.x = Math.random() - 0.5; - sphere.position.z = Math.random() - 0.5; - sphere.position.normalize(); - sphere.position.multiplyScalar(Math.random() * 2 + 1); - sphere.castShadow = true; - sphere.receiveShadow = true; - sphere.userData.phase = Math.random() * Math.PI; - group.add(sphere); - } - - // ground - - const groundMaterial = new THREE.MeshPhongMaterial({ color: 0x898989 }); - - const ground = new THREE.Mesh(new THREE.PlaneGeometry(20000, 20000, 8, 8), groundMaterial); - ground.rotation.x = -Math.PI / 2; - ground.receiveShadow = true; - scene.add(ground); - - // column - - const column = new THREE.Mesh(new THREE.BoxGeometry(1, 4, 1), groundMaterial); - column.position.y = 2; - column.castShadow = true; - column.receiveShadow = true; - scene.add(column); - - // overwrite shadowmap code - - let shader = THREE.ShaderChunk.shadowmap_pars_fragment; - - shader = shader.replace( - '#ifdef USE_SHADOWMAP', - '#ifdef USE_SHADOWMAP' + document.getElementById('PCSS').textContent, - ); - - shader = shader.replace( - '\t\t\tif ( frustumTest ) {\n\t\t\t\tfloat depth = texture2D( shadowMap, shadowCoord.xy ).r;', - '\t\t\tif ( frustumTest ) {\n' + - document.getElementById('PCSSGetShadow').textContent + - '\n' + - '\t\t\t\tfloat depth = texture2D( shadowMap, shadowCoord.xy ).r;', - ); - - THREE.ShaderChunk.shadowmap_pars_fragment = shader; - - // renderer - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.setClearColor(scene.fog.color); - - container.appendChild(renderer.domElement); - - renderer.shadowMap.enabled = true; - renderer.shadowMap.type = THREE.BasicShadowMap; // PCSS requires reading raw depth values - - // controls - const controls = new OrbitControls(camera, renderer.domElement); - controls.maxPolarAngle = Math.PI * 0.5; - controls.minDistance = 10; - controls.maxDistance = 75; - controls.target.set(0, 2.5, 0); - controls.update(); - - // performance monitor - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); -} - -// - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - const time = performance.now() / 1000; - - group.traverse(function (child) { - if ('phase' in child.userData) { - child.position.y = Math.abs(Math.sin(time + child.userData.phase)) * 4 + 0.3; - } - }); - - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/webgl_shadowmap_performance.ts b/examples-testing/examples/webgl_shadowmap_performance.ts deleted file mode 100644 index de54f335f..000000000 --- a/examples-testing/examples/webgl_shadowmap_performance.ts +++ /dev/null @@ -1,282 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { FirstPersonControls } from 'three/addons/controls/FirstPersonControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { FontLoader } from 'three/addons/loaders/FontLoader.js'; -import { TextGeometry } from 'three/addons/geometries/TextGeometry.js'; - -const SHADOW_MAP_WIDTH = 2048, - SHADOW_MAP_HEIGHT = 1024; - -let SCREEN_WIDTH = window.innerWidth; -let SCREEN_HEIGHT = window.innerHeight; -const FLOOR = -250; - -const ANIMATION_GROUPS = 25; - -let camera, controls, scene, renderer; -let stats; - -const NEAR = 5, - FAR = 3000; - -let morph, mixer; - -const morphs = [], - animGroups = []; - -const timer = new THREE.Timer(); -timer.connect(document); - -init(); - -function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - // CAMERA - - camera = new THREE.PerspectiveCamera(23, SCREEN_WIDTH / SCREEN_HEIGHT, NEAR, FAR); - camera.position.set(700, 50, 1900); - - // SCENE - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x59472b); - scene.fog = new THREE.Fog(0x59472b, 1000, FAR); - - // LIGHTS - - const ambient = new THREE.AmbientLight(0xffffff); - scene.add(ambient); - - const light = new THREE.DirectionalLight(0xffffff, 3); - light.position.set(0, 1500, 1000); - light.castShadow = true; - light.shadow.camera.top = 2000; - light.shadow.camera.bottom = -2000; - light.shadow.camera.left = -2000; - light.shadow.camera.right = 2000; - light.shadow.camera.near = 1200; - light.shadow.camera.far = 2500; - light.shadow.bias = 0.0001; - - light.shadow.mapSize.width = SHADOW_MAP_WIDTH; - light.shadow.mapSize.height = SHADOW_MAP_HEIGHT; - - scene.add(light); - - createScene(); - - // RENDERER - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - renderer.autoClear = false; - - // - - renderer.shadowMap.enabled = true; - renderer.shadowMap.type = THREE.PCFShadowMap; - - // CONTROLS - - controls = new FirstPersonControls(camera, renderer.domElement); - - controls.lookSpeed = 0.0125; - controls.movementSpeed = 500; - controls.lookVertical = true; - - controls.lookAt(scene.position); - - // STATS - - stats = new Stats(); - container.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - SCREEN_WIDTH = window.innerWidth; - SCREEN_HEIGHT = window.innerHeight; - - camera.aspect = SCREEN_WIDTH / SCREEN_HEIGHT; - camera.updateProjectionMatrix(); - - renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); -} - -function createScene() { - // GROUND - - const geometry = new THREE.PlaneGeometry(100, 100); - const planeMaterial = new THREE.MeshPhongMaterial({ color: 0xffdd99 }); - - const ground = new THREE.Mesh(geometry, planeMaterial); - - ground.position.set(0, FLOOR, 0); - ground.rotation.x = -Math.PI / 2; - ground.scale.set(100, 100, 100); - - ground.castShadow = false; - ground.receiveShadow = true; - - scene.add(ground); - - // TEXT - - const loader = new FontLoader(); - loader.load('fonts/helvetiker_bold.typeface.json', function (font) { - const textGeo = new TextGeometry('THREE.JS', { - font: font, - - size: 200, - depth: 50, - curveSegments: 12, - - bevelThickness: 2, - bevelSize: 5, - bevelEnabled: true, - }); - - textGeo.computeBoundingBox(); - const centerOffset = -0.5 * (textGeo.boundingBox.max.x - textGeo.boundingBox.min.x); - - const textMaterial = new THREE.MeshPhongMaterial({ color: 0xff0000, specular: 0xffffff }); - - const mesh = new THREE.Mesh(textGeo, textMaterial); - mesh.position.x = centerOffset; - mesh.position.y = FLOOR + 67; - - mesh.castShadow = true; - mesh.receiveShadow = true; - - scene.add(mesh); - }); - - // CUBES - - const cubes1 = new THREE.Mesh(new THREE.BoxGeometry(1500, 220, 150), planeMaterial); - - cubes1.position.y = FLOOR - 50; - cubes1.position.z = 20; - - cubes1.castShadow = true; - cubes1.receiveShadow = true; - - scene.add(cubes1); - - const cubes2 = new THREE.Mesh(new THREE.BoxGeometry(1600, 170, 250), planeMaterial); - - cubes2.position.y = FLOOR - 50; - cubes2.position.z = 20; - - cubes2.castShadow = true; - cubes2.receiveShadow = true; - - scene.add(cubes2); - - mixer = new THREE.AnimationMixer(scene); - - for (let i = 0; i !== ANIMATION_GROUPS; ++i) { - const group = new THREE.AnimationObjectGroup(); - animGroups.push(group); - } - - // MORPHS - - function addMorph(mesh, clip, speed, duration, x, y, z, fudgeColor, massOptimization) { - mesh = mesh.clone(); - mesh.material = mesh.material.clone(); - - if (fudgeColor) { - mesh.material.color.offsetHSL(0, Math.random() * 0.5 - 0.25, Math.random() * 0.5 - 0.25); - } - - mesh.speed = speed; - - if (massOptimization) { - const index = Math.floor(Math.random() * ANIMATION_GROUPS), - animGroup = animGroups[index]; - - animGroup.add(mesh); - - if (!mixer.existingAction(clip, animGroup)) { - const randomness = 0.6 * Math.random() - 0.3; - const phase = (index + randomness) / ANIMATION_GROUPS; - - mixer - .clipAction(clip, animGroup) - .setDuration(duration) - .startAt(-duration * phase) - .play(); - } - } else { - mixer - .clipAction(clip, mesh) - .setDuration(duration) - .startAt(-duration * Math.random()) - .play(); - } - - mesh.position.set(x, y, z); - mesh.rotation.y = Math.PI / 2; - - mesh.castShadow = true; - mesh.receiveShadow = true; - - scene.add(mesh); - - morphs.push(mesh); - } - - const gltfLoader = new GLTFLoader(); - gltfLoader.load('models/gltf/Horse.glb', function (gltf) { - const mesh = gltf.scene.children[0]; - const clip = gltf.animations[0]; - - for (let i = -600; i < 601; i += 2) { - addMorph(mesh, clip, 550, 1, 100 - Math.random() * 3000, FLOOR, i, true, true); - } - }); -} - -// - -function animate() { - timer.update(); - - stats.begin(); - render(); - stats.end(); -} - -function render() { - const delta = timer.getDelta(); - - if (mixer) mixer.update(delta); - - for (let i = 0; i < morphs.length; i++) { - morph = morphs[i]; - - morph.position.x += morph.speed * delta; - - if (morph.position.x > 2000) { - morph.position.x = -1000 - Math.random() * 500; - } - } - - controls.update(delta); - - renderer.clear(); - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_shadowmap_pointlight.ts b/examples-testing/examples/webgl_shadowmap_pointlight.ts deleted file mode 100644 index 4323655dc..000000000 --- a/examples-testing/examples/webgl_shadowmap_pointlight.ts +++ /dev/null @@ -1,141 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let camera, scene, renderer, stats; -let pointLight, pointLight2; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(0, 10, 40); - - scene = new THREE.Scene(); - scene.add(new THREE.AmbientLight(0x111122, 3)); - - // lights - - function createLight(color) { - const intensity = 200; - - const light = new THREE.PointLight(color, intensity, 20); - light.castShadow = true; - light.shadow.bias = -0.005; // reduces self-shadowing on double-sided objects - light.shadow.mapSize.width = 128; - light.shadow.radius = 10; - - let geometry = new THREE.SphereGeometry(0.3, 12, 6); - let material = new THREE.MeshBasicMaterial({ color: color }); - material.color.multiplyScalar(intensity); - let sphere = new THREE.Mesh(geometry, material); - light.add(sphere); - - const texture = new THREE.CanvasTexture(generateTexture()); - texture.magFilter = THREE.NearestFilter; - texture.wrapT = THREE.RepeatWrapping; - texture.wrapS = THREE.RepeatWrapping; - texture.repeat.set(1, 4.5); - - geometry = new THREE.SphereGeometry(2, 32, 8); - material = new THREE.MeshPhongMaterial({ - side: THREE.DoubleSide, - alphaMap: texture, - alphaTest: 0.5, - }); - - sphere = new THREE.Mesh(geometry, material); - sphere.castShadow = true; - sphere.receiveShadow = true; - light.add(sphere); - - return light; - } - - pointLight = createLight(0x0088ff); - scene.add(pointLight); - - pointLight2 = createLight(0xff8888); - scene.add(pointLight2); - // - - const geometry = new THREE.BoxGeometry(30, 30, 30); - - const material = new THREE.MeshPhongMaterial({ - color: 0xa0adaf, - shininess: 10, - specular: 0x111111, - side: THREE.BackSide, - }); - - const mesh = new THREE.Mesh(geometry, material); - mesh.position.y = 10; - mesh.receiveShadow = true; - scene.add(mesh); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.shadowMap.enabled = true; - // renderer.shadowMap.type = THREE.BasicShadowMap; - document.body.appendChild(renderer.domElement); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, 10, 0); - controls.update(); - - stats = new Stats(); - document.body.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function generateTexture() { - const canvas = document.createElement('canvas'); - canvas.width = 2; - canvas.height = 2; - - const context = canvas.getContext('2d'); - context.fillStyle = 'white'; - context.fillRect(0, 1, 2, 1); - - return canvas; -} - -function animate() { - let time = performance.now() * 0.001; - - pointLight.position.x = Math.sin(time * 0.6) * 9; - pointLight.position.y = Math.sin(time * 0.7) * 9 + 6; - pointLight.position.z = Math.sin(time * 0.8) * 9; - - pointLight.rotation.x = time; - pointLight.rotation.z = time; - - time += 10000; - - pointLight2.position.x = Math.sin(time * 0.6) * 9; - pointLight2.position.y = Math.sin(time * 0.7) * 9 + 6; - pointLight2.position.z = Math.sin(time * 0.8) * 9; - - pointLight2.rotation.x = time; - pointLight2.rotation.z = time; - - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/webgl_shadowmap_progressive.ts b/examples-testing/examples/webgl_shadowmap_progressive.ts deleted file mode 100644 index 29298630f..000000000 --- a/examples-testing/examples/webgl_shadowmap_progressive.ts +++ /dev/null @@ -1,204 +0,0 @@ -import * as THREE from 'three'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { TransformControls } from 'three/addons/controls/TransformControls.js'; -import { ProgressiveLightMap } from 'three/addons/misc/ProgressiveLightMap.js'; - -// ShadowMap + LightMap Res and Number of Directional Lights -const shadowMapRes = 512, - lightMapRes = 1024, - lightCount = 8; -let camera, - scene, - renderer, - controls, - control, - control2, - object = new THREE.Mesh(), - lightOrigin = null, - progressiveSurfacemap; -const dirLights = [], - lightmapObjects = []; -const params = { - Enable: true, - 'Blur Edges': true, - 'Blend Window': 200, - 'Light Radius': 50, - 'Ambient Weight': 0.5, - 'Debug Lightmap': false, -}; -init(); -createGUI(); - -function init() { - // renderer - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.shadowMap.enabled = true; - document.body.appendChild(renderer.domElement); - - // camera - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(0, 100, 200); - camera.name = 'Camera'; - - // scene - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x949494); - scene.fog = new THREE.Fog(0x949494, 1000, 3000); - - // progressive lightmap - progressiveSurfacemap = new ProgressiveLightMap(renderer, lightMapRes); - - // directional lighting "origin" - lightOrigin = new THREE.Group(); - lightOrigin.position.set(60, 150, 100); - scene.add(lightOrigin); - - // transform gizmo - control = new TransformControls(camera, renderer.domElement); - control.addEventListener('dragging-changed', event => { - controls.enabled = !event.value; - }); - control.attach(lightOrigin); - scene.add(control.getHelper()); - - // create 8 directional lights to speed up the convergence - for (let l = 0; l < lightCount; l++) { - const dirLight = new THREE.DirectionalLight(0xffffff, Math.PI / lightCount); - dirLight.name = 'Dir. Light ' + l; - dirLight.position.set(200, 200, 200); - dirLight.castShadow = true; - dirLight.shadow.camera.near = 100; - dirLight.shadow.camera.far = 5000; - dirLight.shadow.camera.right = 150; - dirLight.shadow.camera.left = -150; - dirLight.shadow.camera.top = 150; - dirLight.shadow.camera.bottom = -150; - dirLight.shadow.mapSize.width = shadowMapRes; - dirLight.shadow.mapSize.height = shadowMapRes; - lightmapObjects.push(dirLight); - dirLights.push(dirLight); - } - - // ground - const groundMesh = new THREE.Mesh( - new THREE.PlaneGeometry(600, 600), - new THREE.MeshPhongMaterial({ color: 0xffffff, depthWrite: true }), - ); - groundMesh.position.y = -0.1; - groundMesh.rotation.x = -Math.PI / 2; - groundMesh.name = 'Ground Mesh'; - lightmapObjects.push(groundMesh); - scene.add(groundMesh); - - // model - function loadModel() { - object.traverse(function (child) { - if (child.isMesh) { - child.name = 'Loaded Mesh'; - child.castShadow = true; - child.receiveShadow = true; - child.material = new THREE.MeshPhongMaterial(); - - // This adds the model to the lightmap - lightmapObjects.push(child); - progressiveSurfacemap.addObjectsToLightMap(lightmapObjects); - } else { - child.layers.disableAll(); // Disable Rendering for this - } - }); - scene.add(object); - object.scale.set(2, 2, 2); - object.position.set(0, -16, 0); - control2 = new TransformControls(camera, renderer.domElement); - control2.addEventListener('dragging-changed', event => { - controls.enabled = !event.value; - }); - control2.attach(object); - scene.add(control2.getHelper()); - const lightTarget = new THREE.Group(); - lightTarget.position.set(0, 20, 0); - for (let l = 0; l < dirLights.length; l++) { - dirLights[l].target = lightTarget; - } - - object.add(lightTarget); - } - - const manager = new THREE.LoadingManager(loadModel); - const loader = new GLTFLoader(manager); - loader.load('models/gltf/ShadowmappableMesh.glb', function (obj) { - object = obj.scene.children[0]; - }); - - // controls - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; // an animation loop is required when either damping or auto-rotation are enabled - controls.dampingFactor = 0.05; - controls.screenSpacePanning = true; - controls.minDistance = 100; - controls.maxDistance = 500; - controls.maxPolarAngle = Math.PI / 1.5; - controls.target.set(0, 100, 0); - - window.addEventListener('resize', onWindowResize); -} - -function createGUI() { - const gui = new GUI({ title: 'Accumulation Settings' }); - gui.add(params, 'Enable'); - gui.add(params, 'Blur Edges'); - gui.add(params, 'Blend Window', 1, 500).step(1); - gui.add(params, 'Light Radius', 0, 200).step(10); - gui.add(params, 'Ambient Weight', 0, 1).step(0.1); - gui.add(params, 'Debug Lightmap'); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - // Update the inertia on the orbit controls - controls.update(); - - // Accumulate Surface Maps - if (params['Enable']) { - progressiveSurfacemap.update(camera, params['Blend Window'], params['Blur Edges']); - - if (!progressiveSurfacemap.firstUpdate) { - progressiveSurfacemap.showDebugLightmap(params['Debug Lightmap']); - } - } - - // Manually Update the Directional Lights - for (let l = 0; l < dirLights.length; l++) { - // Sometimes they will be sampled from the target direction - // Sometimes they will be uniformly sampled from the upper hemisphere - if (Math.random() > params['Ambient Weight']) { - dirLights[l].position.set( - lightOrigin.position.x + Math.random() * params['Light Radius'], - lightOrigin.position.y + Math.random() * params['Light Radius'], - lightOrigin.position.z + Math.random() * params['Light Radius'], - ); - } else { - // Uniform Hemispherical Surface Distribution for Ambient Occlusion - const lambda = Math.acos(2 * Math.random() - 1) - 3.14159 / 2.0; - const phi = 2 * 3.14159 * Math.random(); - dirLights[l].position.set( - Math.cos(lambda) * Math.cos(phi) * 300 + object.position.x, - Math.abs(Math.cos(lambda) * Math.sin(phi) * 300) + object.position.y + 20, - Math.sin(lambda) * 300 + object.position.z, - ); - } - } - - // Render Scene - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_shadowmap_viewer.ts b/examples-testing/examples/webgl_shadowmap_viewer.ts deleted file mode 100644 index 2ed16e992..000000000 --- a/examples-testing/examples/webgl_shadowmap_viewer.ts +++ /dev/null @@ -1,181 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { ShadowMapViewer } from 'three/addons/utils/ShadowMapViewer.js'; - -let camera, scene, renderer, timer, stats; -let dirLight, spotLight; -let torusKnot, cube; -let dirLightShadowMapViewer, spotLightShadowMapViewer; - -init(); - -function init() { - initScene(); - initShadowMapViewers(); - initMisc(); - - document.body.appendChild(renderer.domElement); - window.addEventListener('resize', onWindowResize); -} - -function initScene() { - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(0, 15, 35); - - scene = new THREE.Scene(); - - // Lights - - scene.add(new THREE.AmbientLight(0x404040, 3)); - - spotLight = new THREE.SpotLight(0xffffff, 500); - spotLight.name = 'Spot Light'; - spotLight.angle = Math.PI / 5; - spotLight.penumbra = 0.3; - spotLight.position.set(10, 10, 5); - spotLight.castShadow = true; - spotLight.shadow.camera.near = 8; - spotLight.shadow.camera.far = 30; - spotLight.shadow.mapSize.width = 1024; - spotLight.shadow.mapSize.height = 1024; - scene.add(spotLight); - - scene.add(new THREE.CameraHelper(spotLight.shadow.camera)); - - dirLight = new THREE.DirectionalLight(0xffffff, 3); - dirLight.name = 'Dir. Light'; - dirLight.position.set(0, 10, 0); - dirLight.castShadow = true; - dirLight.shadow.camera.near = 1; - dirLight.shadow.camera.far = 10; - dirLight.shadow.camera.right = 15; - dirLight.shadow.camera.left = -15; - dirLight.shadow.camera.top = 15; - dirLight.shadow.camera.bottom = -15; - dirLight.shadow.mapSize.width = 1024; - dirLight.shadow.mapSize.height = 1024; - scene.add(dirLight); - - scene.add(new THREE.CameraHelper(dirLight.shadow.camera)); - - // Geometry - let geometry = new THREE.TorusKnotGeometry(25, 8, 75, 20); - let material = new THREE.MeshPhongMaterial({ - color: 0xff0000, - shininess: 150, - specular: 0x222222, - }); - - torusKnot = new THREE.Mesh(geometry, material); - torusKnot.scale.multiplyScalar(1 / 18); - torusKnot.position.y = 3; - torusKnot.castShadow = true; - torusKnot.receiveShadow = true; - scene.add(torusKnot); - - geometry = new THREE.BoxGeometry(3, 3, 3); - cube = new THREE.Mesh(geometry, material); - cube.position.set(8, 3, 8); - cube.castShadow = true; - cube.receiveShadow = true; - scene.add(cube); - - geometry = new THREE.BoxGeometry(10, 0.15, 10); - material = new THREE.MeshPhongMaterial({ - color: 0xa0adaf, - shininess: 150, - specular: 0x111111, - }); - - const ground = new THREE.Mesh(geometry, material); - ground.scale.multiplyScalar(3); - ground.castShadow = false; - ground.receiveShadow = true; - scene.add(ground); -} - -function initShadowMapViewers() { - dirLightShadowMapViewer = new ShadowMapViewer(dirLight); - spotLightShadowMapViewer = new ShadowMapViewer(spotLight); - resizeShadowMapViewers(); -} - -function initMisc() { - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.shadowMap.enabled = true; - renderer.shadowMap.type = THREE.BasicShadowMap; - - // Mouse control - const controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, 2, 0); - controls.update(); - - timer = new THREE.Timer(); - timer.connect(document); - - stats = new Stats(); - document.body.appendChild(stats.dom); -} - -function resizeShadowMapViewers() { - const size = window.innerWidth * 0.15; - - dirLightShadowMapViewer.position.x = 10; - dirLightShadowMapViewer.position.y = 10; - dirLightShadowMapViewer.size.width = size; - dirLightShadowMapViewer.size.height = size; - dirLightShadowMapViewer.update(); //Required when setting position or size directly - - spotLightShadowMapViewer.size.set(size, size); - spotLightShadowMapViewer.position.set(size + 20, 10); - // spotLightShadowMapViewer.update(); //NOT required because .set updates automatically -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - resizeShadowMapViewers(); - dirLightShadowMapViewer.updateForWindowResize(); - spotLightShadowMapViewer.updateForWindowResize(); -} - -function animate() { - timer.update(); - - render(); - - stats.update(); -} - -function renderScene() { - renderer.render(scene, camera); -} - -function renderShadowMapViewers() { - dirLightShadowMapViewer.render(renderer); - spotLightShadowMapViewer.render(renderer); -} - -function render() { - const delta = timer.getDelta(); - - renderScene(); - renderShadowMapViewers(); - - torusKnot.rotation.x += 0.25 * delta; - torusKnot.rotation.y += 2 * delta; - torusKnot.rotation.z += 1 * delta; - - cube.rotation.x += 0.25 * delta; - cube.rotation.y += 2 * delta; - cube.rotation.z += 1 * delta; -} diff --git a/examples-testing/examples/webgl_shadowmap_vsm.ts b/examples-testing/examples/webgl_shadowmap_vsm.ts deleted file mode 100644 index d5bf4f7bb..000000000 --- a/examples-testing/examples/webgl_shadowmap_vsm.ts +++ /dev/null @@ -1,203 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let camera, scene, renderer, timer, stats; -let dirLight, spotLight; -let torusKnot, dirGroup; - -init(); - -function init() { - initScene(); - initMisc(); - - // Init gui - const gui = new GUI(); - - const config = { - spotlightRadius: 4, - spotlightSamples: 8, - dirlightRadius: 4, - dirlightSamples: 8, - }; - - const spotlightFolder = gui.addFolder('Spotlight'); - spotlightFolder - .add(config, 'spotlightRadius') - .name('radius') - .min(0) - .max(25) - .onChange(function (value) { - spotLight.shadow.radius = value; - }); - - spotlightFolder - .add(config, 'spotlightSamples', 1, 25, 1) - .name('samples') - .onChange(function (value) { - spotLight.shadow.blurSamples = value; - }); - spotlightFolder.open(); - - const dirlightFolder = gui.addFolder('Directional Light'); - dirlightFolder - .add(config, 'dirlightRadius') - .name('radius') - .min(0) - .max(25) - .onChange(function (value) { - dirLight.shadow.radius = value; - }); - - dirlightFolder - .add(config, 'dirlightSamples', 1, 25, 1) - .name('samples') - .onChange(function (value) { - dirLight.shadow.blurSamples = value; - }); - dirlightFolder.open(); - - document.body.appendChild(renderer.domElement); - window.addEventListener('resize', onWindowResize); -} - -function initScene() { - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(0, 10, 30); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x222244); - scene.fog = new THREE.Fog(0x222244, 50, 100); - - // Lights - - scene.add(new THREE.AmbientLight(0x444444)); - - spotLight = new THREE.SpotLight(0xff8888, 400); - spotLight.angle = Math.PI / 5; - spotLight.penumbra = 0.3; - spotLight.position.set(8, 10, 5); - spotLight.castShadow = true; - spotLight.shadow.camera.near = 8; - spotLight.shadow.camera.far = 200; - spotLight.shadow.mapSize.width = 256; - spotLight.shadow.mapSize.height = 256; - spotLight.shadow.bias = -0.002; - spotLight.shadow.radius = 4; - scene.add(spotLight); - - dirLight = new THREE.DirectionalLight(0x8888ff, 3); - dirLight.position.set(3, 12, 17); - dirLight.castShadow = true; - dirLight.shadow.camera.near = 0.1; - dirLight.shadow.camera.far = 500; - dirLight.shadow.camera.right = 17; - dirLight.shadow.camera.left = -17; - dirLight.shadow.camera.top = 17; - dirLight.shadow.camera.bottom = -17; - dirLight.shadow.mapSize.width = 512; - dirLight.shadow.mapSize.height = 512; - dirLight.shadow.radius = 4; - dirLight.shadow.bias = -0.0005; - - dirGroup = new THREE.Group(); - dirGroup.add(dirLight); - scene.add(dirGroup); - - // Geometry - - const geometry = new THREE.TorusKnotGeometry(25, 8, 75, 20); - const material = new THREE.MeshPhongMaterial({ - color: 0x999999, - shininess: 0, - specular: 0x222222, - }); - - torusKnot = new THREE.Mesh(geometry, material); - torusKnot.scale.multiplyScalar(1 / 18); - torusKnot.position.y = 3; - torusKnot.castShadow = true; - torusKnot.receiveShadow = true; - scene.add(torusKnot); - - const cylinderGeometry = new THREE.CylinderGeometry(0.75, 0.75, 7, 32); - - const pillar1 = new THREE.Mesh(cylinderGeometry, material); - pillar1.position.set(8, 3.5, 8); - pillar1.castShadow = true; - pillar1.receiveShadow = true; - - const pillar2 = pillar1.clone(); - pillar2.position.set(8, 3.5, -8); - const pillar3 = pillar1.clone(); - pillar3.position.set(-8, 3.5, 8); - const pillar4 = pillar1.clone(); - pillar4.position.set(-8, 3.5, -8); - - scene.add(pillar1); - scene.add(pillar2); - scene.add(pillar3); - scene.add(pillar4); - - const planeGeometry = new THREE.PlaneGeometry(200, 200); - const planeMaterial = new THREE.MeshPhongMaterial({ - color: 0x999999, - shininess: 0, - specular: 0x111111, - }); - - const ground = new THREE.Mesh(planeGeometry, planeMaterial); - ground.rotation.x = -Math.PI / 2; - ground.scale.multiplyScalar(3); - ground.castShadow = true; - ground.receiveShadow = true; - scene.add(ground); -} - -function initMisc() { - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.shadowMap.enabled = true; - renderer.shadowMap.type = THREE.VSMShadowMap; - - // Mouse control - const controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, 2, 0); - controls.update(); - - timer = new THREE.Timer(); - timer.connect(document); - - stats = new Stats(); - document.body.appendChild(stats.dom); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate(time) { - timer.update(); - - const delta = timer.getDelta(); - - torusKnot.rotation.x += 0.25 * delta; - torusKnot.rotation.y += 0.5 * delta; - torusKnot.rotation.z += 1 * delta; - - dirGroup.rotation.y += 0.7 * delta; - dirLight.position.z = 17 + Math.sin(time * 0.001) * 5; - - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/webgl_shadowmesh.ts b/examples-testing/examples/webgl_shadowmesh.ts deleted file mode 100644 index e4f7ebb8f..000000000 --- a/examples-testing/examples/webgl_shadowmesh.ts +++ /dev/null @@ -1,253 +0,0 @@ -import * as THREE from 'three'; - -import { ShadowMesh } from 'three/addons/objects/ShadowMesh.js'; - -let SCREEN_WIDTH = window.innerWidth; -let SCREEN_HEIGHT = window.innerHeight; - -const scene = new THREE.Scene(); -const camera = new THREE.PerspectiveCamera(55, SCREEN_WIDTH / SCREEN_HEIGHT, 1, 3000); -const timer = new THREE.Timer(); -timer.connect(document); -const renderer = new THREE.WebGLRenderer({ stencil: true }); - -const sunLight = new THREE.DirectionalLight('rgb(255,255,255)', 3); -let useDirectionalLight = true; -let arrowHelper1, arrowHelper2, arrowHelper3; -const arrowDirection = new THREE.Vector3(); -const arrowPosition1 = new THREE.Vector3(); -const arrowPosition2 = new THREE.Vector3(); -const arrowPosition3 = new THREE.Vector3(); -let groundMesh; -let lightSphere, lightHolder; -let pyramid, pyramidShadow; -let sphere, sphereShadow; -let cube, cubeShadow; -let cylinder, cylinderShadow; -let torus, torusShadow; -const normalVector = new THREE.Vector3(0, 1, 0); -const planeConstant = 0.01; // this value must be slightly higher than the groundMesh's y position of 0.0 -const groundPlane = new THREE.Plane(normalVector, planeConstant); -const lightPosition4D = new THREE.Vector4(); -let verticalAngle = 0; -let horizontalAngle = 0; -let frameTime = 0; -const TWO_PI = Math.PI * 2; - -init(); - -function init() { - scene.background = new THREE.Color(0x0096ff); - - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); - renderer.setAnimationLoop(animate); - document.getElementById('container').appendChild(renderer.domElement); - - window.addEventListener('resize', onWindowResize); - - camera.position.set(0, 2.5, 10); - scene.add(camera); - onWindowResize(); - - sunLight.position.set(5, 7, -1); - sunLight.lookAt(scene.position); - scene.add(sunLight); - - lightPosition4D.x = sunLight.position.x; - lightPosition4D.y = sunLight.position.y; - lightPosition4D.z = sunLight.position.z; - // amount of light-ray divergence. Ranging from: - // 0.001 = sunlight(min divergence) to 1.0 = pointlight(max divergence) - lightPosition4D.w = 0.001; // must be slightly greater than 0, due to 0 causing matrixInverse errors - - // YELLOW ARROW HELPERS - arrowDirection.subVectors(scene.position, sunLight.position).normalize(); - - arrowPosition1.copy(sunLight.position); - arrowHelper1 = new THREE.ArrowHelper(arrowDirection, arrowPosition1, 0.9, 0xffff00, 0.25, 0.08); - scene.add(arrowHelper1); - - arrowPosition2.copy(sunLight.position).add(new THREE.Vector3(0, 0.2, 0)); - arrowHelper2 = new THREE.ArrowHelper(arrowDirection, arrowPosition2, 0.9, 0xffff00, 0.25, 0.08); - scene.add(arrowHelper2); - - arrowPosition3.copy(sunLight.position).add(new THREE.Vector3(0, -0.2, 0)); - arrowHelper3 = new THREE.ArrowHelper(arrowDirection, arrowPosition3, 0.9, 0xffff00, 0.25, 0.08); - scene.add(arrowHelper3); - - // LIGHTBULB - const lightSphereGeometry = new THREE.SphereGeometry(0.09); - const lightSphereMaterial = new THREE.MeshBasicMaterial({ color: 'rgb(255,255,255)' }); - lightSphere = new THREE.Mesh(lightSphereGeometry, lightSphereMaterial); - scene.add(lightSphere); - lightSphere.visible = false; - - const lightHolderGeometry = new THREE.CylinderGeometry(0.05, 0.05, 0.13); - const lightHolderMaterial = new THREE.MeshBasicMaterial({ color: 'rgb(75,75,75)' }); - lightHolder = new THREE.Mesh(lightHolderGeometry, lightHolderMaterial); - scene.add(lightHolder); - lightHolder.visible = false; - - // GROUND - const groundGeometry = new THREE.BoxGeometry(30, 0.01, 40); - const groundMaterial = new THREE.MeshLambertMaterial({ color: 'rgb(0,130,0)' }); - groundMesh = new THREE.Mesh(groundGeometry, groundMaterial); - groundMesh.position.y = 0.0; //this value must be slightly lower than the planeConstant (0.01) parameter above - scene.add(groundMesh); - - // RED CUBE and CUBE's SHADOW - const cubeGeometry = new THREE.BoxGeometry(1, 1, 1); - const cubeMaterial = new THREE.MeshLambertMaterial({ color: 'rgb(255,0,0)', emissive: 0x200000 }); - cube = new THREE.Mesh(cubeGeometry, cubeMaterial); - cube.position.z = -1; - scene.add(cube); - - cubeShadow = new ShadowMesh(cube); - scene.add(cubeShadow); - - // BLUE CYLINDER and CYLINDER's SHADOW - const cylinderGeometry = new THREE.CylinderGeometry(0.3, 0.3, 2); - const cylinderMaterial = new THREE.MeshPhongMaterial({ color: 'rgb(0,0,255)', emissive: 0x000020 }); - cylinder = new THREE.Mesh(cylinderGeometry, cylinderMaterial); - cylinder.position.z = -2.5; - scene.add(cylinder); - - cylinderShadow = new ShadowMesh(cylinder); - scene.add(cylinderShadow); - - // MAGENTA TORUS and TORUS' SHADOW - const torusGeometry = new THREE.TorusGeometry(1, 0.2, 10, 16, TWO_PI); - const torusMaterial = new THREE.MeshPhongMaterial({ color: 'rgb(255,0,255)', emissive: 0x200020 }); - torus = new THREE.Mesh(torusGeometry, torusMaterial); - torus.position.z = -6; - scene.add(torus); - - torusShadow = new ShadowMesh(torus); - scene.add(torusShadow); - - // WHITE SPHERE and SPHERE'S SHADOW - const sphereGeometry = new THREE.SphereGeometry(0.5, 20, 10); - const sphereMaterial = new THREE.MeshPhongMaterial({ color: 'rgb(255,255,255)', emissive: 0x222222 }); - sphere = new THREE.Mesh(sphereGeometry, sphereMaterial); - sphere.position.set(4, 0.5, 2); - scene.add(sphere); - - sphereShadow = new ShadowMesh(sphere); - scene.add(sphereShadow); - - // YELLOW PYRAMID and PYRAMID'S SHADOW - const pyramidGeometry = new THREE.CylinderGeometry(0, 0.5, 2, 4); - const pyramidMaterial = new THREE.MeshPhongMaterial({ - color: 'rgb(255,255,0)', - emissive: 0x440000, - flatShading: true, - shininess: 0, - }); - pyramid = new THREE.Mesh(pyramidGeometry, pyramidMaterial); - pyramid.position.set(-4, 1, 2); - scene.add(pyramid); - - pyramidShadow = new ShadowMesh(pyramid); - scene.add(pyramidShadow); - - document.getElementById('lightButton').addEventListener('click', lightButtonHandler); -} - -function animate() { - timer.update(); - - frameTime = timer.getDelta(); - - cube.rotation.x += 1.0 * frameTime; - cube.rotation.y += 1.0 * frameTime; - - cylinder.rotation.y += 1.0 * frameTime; - cylinder.rotation.z -= 1.0 * frameTime; - - torus.rotation.x -= 1.0 * frameTime; - torus.rotation.y -= 1.0 * frameTime; - - pyramid.rotation.y += 0.5 * frameTime; - - horizontalAngle += 0.5 * frameTime; - if (horizontalAngle > TWO_PI) horizontalAngle -= TWO_PI; - cube.position.x = Math.sin(horizontalAngle) * 4; - cylinder.position.x = Math.sin(horizontalAngle) * -4; - torus.position.x = Math.cos(horizontalAngle) * 4; - - verticalAngle += 1.5 * frameTime; - if (verticalAngle > TWO_PI) verticalAngle -= TWO_PI; - cube.position.y = Math.sin(verticalAngle) * 2 + 2.9; - cylinder.position.y = Math.sin(verticalAngle) * 2 + 3.1; - torus.position.y = Math.cos(verticalAngle) * 2 + 3.3; - - // update the ShadowMeshes to follow their shadow-casting objects - cubeShadow.update(groundPlane, lightPosition4D); - cylinderShadow.update(groundPlane, lightPosition4D); - torusShadow.update(groundPlane, lightPosition4D); - sphereShadow.update(groundPlane, lightPosition4D); - pyramidShadow.update(groundPlane, lightPosition4D); - - renderer.render(scene, camera); -} - -function onWindowResize() { - SCREEN_WIDTH = window.innerWidth; - SCREEN_HEIGHT = window.innerHeight; - - renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); - - camera.aspect = SCREEN_WIDTH / SCREEN_HEIGHT; - camera.updateProjectionMatrix(); -} - -function lightButtonHandler() { - useDirectionalLight = !useDirectionalLight; - - if (useDirectionalLight) { - scene.background.setHex(0x0096ff); - - groundMesh.material.color.setHex(0x008200); - sunLight.position.set(5, 7, -1); - sunLight.lookAt(scene.position); - - lightPosition4D.x = sunLight.position.x; - lightPosition4D.y = sunLight.position.y; - lightPosition4D.z = sunLight.position.z; - lightPosition4D.w = 0.001; // more of a directional Light value - - arrowHelper1.visible = true; - arrowHelper2.visible = true; - arrowHelper3.visible = true; - - lightSphere.visible = false; - lightHolder.visible = false; - - document.getElementById('lightButton').value = 'Switch to PointLight'; - } else { - scene.background.setHex(0x000000); - - groundMesh.material.color.setHex(0x969696); - - sunLight.position.set(0, 6, -2); - sunLight.lookAt(scene.position); - lightSphere.position.copy(sunLight.position); - lightHolder.position.copy(lightSphere.position); - lightHolder.position.y += 0.12; - - lightPosition4D.x = sunLight.position.x; - lightPosition4D.y = sunLight.position.y; - lightPosition4D.z = sunLight.position.z; - lightPosition4D.w = 0.9; // more of a point Light value - - arrowHelper1.visible = false; - arrowHelper2.visible = false; - arrowHelper3.visible = false; - - lightSphere.visible = true; - lightHolder.visible = true; - - document.getElementById('lightButton').value = 'Switch to THREE.DirectionalLight'; - } -} diff --git a/examples-testing/examples/webgl_simple_gi.ts b/examples-testing/examples/webgl_simple_gi.ts deleted file mode 100644 index 4ab6dc895..000000000 --- a/examples-testing/examples/webgl_simple_gi.ts +++ /dev/null @@ -1,172 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -class GIMesh extends THREE.Mesh { - copy(source) { - super.copy(source); - - this.geometry = source.geometry.clone(); - - return this; - } -} - -// - -const SimpleGI = function (renderer, scene) { - const SIZE = 32, - SIZE2 = SIZE * SIZE; - - const camera = new THREE.PerspectiveCamera(90, 1, 0.01, 100); - - scene.updateMatrixWorld(true); - - let clone = scene.clone(); - clone.matrixWorldAutoUpdate = false; - - const rt = new THREE.WebGLRenderTarget(SIZE, SIZE); - - const normalMatrix = new THREE.Matrix3(); - - const position = new THREE.Vector3(); - const normal = new THREE.Vector3(); - - let bounces = 0; - let currentVertex = 0; - - const color = new Float32Array(3); - const buffer = new Uint8Array(SIZE2 * 4); - - function compute() { - if (bounces === 3) return; - - const object = scene.children[0]; // torusKnot - const geometry = object.geometry; - - const attributes = geometry.attributes; - const positions = attributes.position.array; - const normals = attributes.normal.array; - - if (attributes.color === undefined) { - const colors = new Float32Array(positions.length); - geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3).setUsage(THREE.DynamicDrawUsage)); - } - - const colors = attributes.color.array; - - const startVertex = currentVertex; - const totalVertex = positions.length / 3; - - for (let i = 0; i < 32; i++) { - if (currentVertex >= totalVertex) break; - - position.fromArray(positions, currentVertex * 3); - position.applyMatrix4(object.matrixWorld); - - normal.fromArray(normals, currentVertex * 3); - normal.applyMatrix3(normalMatrix.getNormalMatrix(object.matrixWorld)).normalize(); - - camera.position.copy(position); - camera.lookAt(position.add(normal)); - - renderer.setRenderTarget(rt); - renderer.render(clone, camera); - - renderer.readRenderTargetPixels(rt, 0, 0, SIZE, SIZE, buffer); - - color[0] = 0; - color[1] = 0; - color[2] = 0; - - for (let k = 0, kl = buffer.length; k < kl; k += 4) { - color[0] += buffer[k + 0]; - color[1] += buffer[k + 1]; - color[2] += buffer[k + 2]; - } - - colors[currentVertex * 3 + 0] = color[0] / (SIZE2 * 255); - colors[currentVertex * 3 + 1] = color[1] / (SIZE2 * 255); - colors[currentVertex * 3 + 2] = color[2] / (SIZE2 * 255); - - currentVertex++; - } - - attributes.color.addUpdateRange(startVertex * 3, (currentVertex - startVertex) * 3); - attributes.color.needsUpdate = true; - - if (currentVertex >= totalVertex) { - clone = scene.clone(); - clone.matrixWorldAutoUpdate = false; - - bounces++; - currentVertex = 0; - } - - requestAnimationFrame(compute); - } - - requestAnimationFrame(compute); -}; - -// - -let camera, scene, renderer; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.z = 4; - - scene = new THREE.Scene(); - - // torus knot - - const torusGeometry = new THREE.TorusKnotGeometry(0.75, 0.3, 128, 32, 1); - const material = new THREE.MeshBasicMaterial({ vertexColors: true }); - - const torusKnot = new GIMesh(torusGeometry, material); - scene.add(torusKnot); - - // room - - const materials = []; - - for (let i = 0; i < 8; i++) { - materials.push(new THREE.MeshBasicMaterial({ color: Math.random() * 0xffffff, side: THREE.BackSide })); - } - - const boxGeometry = new THREE.BoxGeometry(3, 3, 3); - - const box = new THREE.Mesh(boxGeometry, materials); - scene.add(box); - - // - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - new SimpleGI(renderer, scene); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 1; - controls.maxDistance = 10; - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - renderer.setRenderTarget(null); - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_sprites.ts b/examples-testing/examples/webgl_sprites.ts deleted file mode 100644 index 2e4189347..000000000 --- a/examples-testing/examples/webgl_sprites.ts +++ /dev/null @@ -1,187 +0,0 @@ -import * as THREE from 'three'; - -let camera, scene, renderer; -let cameraOrtho, sceneOrtho; - -let spriteTL, spriteTR, spriteBL, spriteBR, spriteC; - -let mapC; - -let group; - -init(); - -function init() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera = new THREE.PerspectiveCamera(60, width / height, 1, 2100); - camera.position.z = 1500; - - cameraOrtho = new THREE.OrthographicCamera(-width / 2, width / 2, height / 2, -height / 2, 1, 10); - cameraOrtho.position.z = 10; - - scene = new THREE.Scene(); - scene.fog = new THREE.Fog(0x000000, 1500, 2100); - - sceneOrtho = new THREE.Scene(); - - // create sprites - - const amount = 200; - const radius = 500; - - const textureLoader = new THREE.TextureLoader(); - - textureLoader.load('textures/sprite0.png', createHUDSprites); - const mapB = textureLoader.load('textures/sprite1.png'); - mapC = textureLoader.load('textures/sprite2.png'); - - mapB.colorSpace = THREE.SRGBColorSpace; - mapC.colorSpace = THREE.SRGBColorSpace; - - group = new THREE.Group(); - - const materialC = new THREE.SpriteMaterial({ map: mapC, color: 0xffffff, fog: true }); - const materialB = new THREE.SpriteMaterial({ map: mapB, color: 0xffffff, fog: true }); - - for (let a = 0; a < amount; a++) { - const x = Math.random() - 0.5; - const y = Math.random() - 0.5; - const z = Math.random() - 0.5; - - let material; - - if (z < 0) { - material = materialB.clone(); - } else { - material = materialC.clone(); - material.color.setHSL(0.5 * Math.random(), 0.75, 0.5); - material.map.offset.set(-0.5, -0.5); - material.map.repeat.set(2, 2); - } - - const sprite = new THREE.Sprite(material); - - sprite.position.set(x, y, z); - sprite.position.normalize(); - sprite.position.multiplyScalar(radius); - - group.add(sprite); - } - - scene.add(group); - - // renderer - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.autoClear = false; // To allow render overlay on top of sprited sphere - - document.body.appendChild(renderer.domElement); - - // - - window.addEventListener('resize', onWindowResize); -} - -function createHUDSprites(texture) { - texture.colorSpace = THREE.SRGBColorSpace; - - const material = new THREE.SpriteMaterial({ map: texture }); - - const width = material.map.image.width; - const height = material.map.image.height; - - spriteTL = new THREE.Sprite(material); - spriteTL.center.set(0.0, 1.0); - spriteTL.scale.set(width, height, 1); - sceneOrtho.add(spriteTL); - - spriteTR = new THREE.Sprite(material); - spriteTR.center.set(1.0, 1.0); - spriteTR.scale.set(width, height, 1); - sceneOrtho.add(spriteTR); - - spriteBL = new THREE.Sprite(material); - spriteBL.center.set(0.0, 0.0); - spriteBL.scale.set(width, height, 1); - sceneOrtho.add(spriteBL); - - spriteBR = new THREE.Sprite(material); - spriteBR.center.set(1.0, 0.0); - spriteBR.scale.set(width, height, 1); - sceneOrtho.add(spriteBR); - - spriteC = new THREE.Sprite(material); - spriteC.center.set(0.5, 0.5); - spriteC.scale.set(width, height, 1); - sceneOrtho.add(spriteC); - - updateHUDSprites(); -} - -function updateHUDSprites() { - const width = window.innerWidth / 2; - const height = window.innerHeight / 2; - - spriteTL.position.set(-width, height, 1); // top left - spriteTR.position.set(width, height, 1); // top right - spriteBL.position.set(-width, -height, 1); // bottom left - spriteBR.position.set(width, -height, 1); // bottom right - spriteC.position.set(0, 0, 1); // center -} - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - cameraOrtho.left = -width / 2; - cameraOrtho.right = width / 2; - cameraOrtho.top = height / 2; - cameraOrtho.bottom = -height / 2; - cameraOrtho.updateProjectionMatrix(); - - updateHUDSprites(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - const time = Date.now() / 1000; - - for (let i = 0, l = group.children.length; i < l; i++) { - const sprite = group.children[i]; - const material = sprite.material; - const scale = Math.sin(time + sprite.position.x * 0.01) * 0.3 + 1.0; - - let imageWidth = 1; - let imageHeight = 1; - - if (material.map && material.map.image && material.map.image.width) { - imageWidth = material.map.image.width; - imageHeight = material.map.image.height; - } - - sprite.material.rotation += 0.1 * (i / l); - sprite.scale.set(scale * imageWidth, scale * imageHeight, 1.0); - - if (material.map !== mapC) { - material.opacity = Math.sin(time + sprite.position.x * 0.01) * 0.4 + 0.6; - } - } - - group.rotation.x = time * 0.5; - group.rotation.y = time * 0.75; - group.rotation.z = time * 1.0; - - renderer.clear(); - renderer.render(scene, camera); - renderer.clearDepth(); - renderer.render(sceneOrtho, cameraOrtho); -} diff --git a/examples-testing/examples/webgl_test_memory.ts b/examples-testing/examples/webgl_test_memory.ts deleted file mode 100644 index f5d0e112d..000000000 --- a/examples-testing/examples/webgl_test_memory.ts +++ /dev/null @@ -1,65 +0,0 @@ -import * as THREE from 'three'; - -let camera, scene, renderer; - -init(); - -function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 10000); - camera.position.z = 200; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xffffff); - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); -} - -function createImage() { - const canvas = document.createElement('canvas'); - canvas.width = 256; - canvas.height = 256; - - const context = canvas.getContext('2d'); - context.fillStyle = - 'rgb(' + - Math.floor(Math.random() * 256) + - ',' + - Math.floor(Math.random() * 256) + - ',' + - Math.floor(Math.random() * 256) + - ')'; - context.fillRect(0, 0, 256, 256); - - return canvas; -} - -// - -function animate() { - const geometry = new THREE.SphereGeometry(50, Math.random() * 64, Math.random() * 32); - - const texture = new THREE.CanvasTexture(createImage()); - - const material = new THREE.MeshBasicMaterial({ map: texture, wireframe: true }); - - const mesh = new THREE.Mesh(geometry, material); - - scene.add(mesh); - - renderer.render(scene, camera); - - scene.remove(mesh); - - // clean up - - geometry.dispose(); - material.dispose(); - texture.dispose(); -} diff --git a/examples-testing/examples/webgl_test_memory2.ts b/examples-testing/examples/webgl_test_memory2.ts deleted file mode 100644 index 366a27914..000000000 --- a/examples-testing/examples/webgl_test_memory2.ts +++ /dev/null @@ -1,81 +0,0 @@ -import * as THREE from 'three'; - -const N = 100; - -let container; - -let camera, scene, renderer; - -let geometry; - -const meshes = []; - -let fragmentShader, vertexShader; - -init(); -setInterval(render, 1000 / 60); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - vertexShader = document.getElementById('vertexShader').textContent; - fragmentShader = document.getElementById('fragmentShader').textContent; - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 10000); - camera.position.z = 2000; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xffffff); - - geometry = new THREE.SphereGeometry(15, 64, 32); - - for (let i = 0; i < N; i++) { - const material = new THREE.ShaderMaterial({ - vertexShader: vertexShader, - fragmentShader: generateFragmentShader(), - }); - - const mesh = new THREE.Mesh(geometry, material); - - mesh.position.x = (0.5 - Math.random()) * 1000; - mesh.position.y = (0.5 - Math.random()) * 1000; - mesh.position.z = (0.5 - Math.random()) * 1000; - - scene.add(mesh); - - meshes.push(mesh); - } - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - container.appendChild(renderer.domElement); -} - -// - -function generateFragmentShader() { - return fragmentShader.replace('XXX', Math.random() + ',' + Math.random() + ',' + Math.random()); -} - -function render() { - for (let i = 0; i < N; i++) { - const mesh = meshes[i]; - mesh.material = new THREE.ShaderMaterial({ - vertexShader: vertexShader, - fragmentShader: generateFragmentShader(), - }); - } - - renderer.render(scene, camera); - - console.log('before', renderer.info.programs.length); - - for (let i = 0; i < N; i++) { - const mesh = meshes[i]; - mesh.material.dispose(); - } - - console.log('after', renderer.info.programs.length); -} diff --git a/examples-testing/examples/webgl_test_wide_gamut.ts b/examples-testing/examples/webgl_test_wide_gamut.ts deleted file mode 100644 index 5988299e1..000000000 --- a/examples-testing/examples/webgl_test_wide_gamut.ts +++ /dev/null @@ -1,130 +0,0 @@ -import * as THREE from 'three'; - -import { - DisplayP3ColorSpace, - DisplayP3ColorSpaceImpl, - LinearDisplayP3ColorSpace, - LinearDisplayP3ColorSpaceImpl, -} from 'three/addons/math/ColorSpaces.js'; - -import WebGL from 'three/addons/capabilities/WebGL.js'; - -let container, camera, renderer, loader; -let sceneL, sceneR, textureL, textureR; - -let sliderPos = window.innerWidth / 2; - -const slider = document.querySelector('.slider'); - -const isP3Context = WebGL.isColorSpaceAvailable(DisplayP3ColorSpace); - -THREE.ColorManagement.define({ - [DisplayP3ColorSpace]: DisplayP3ColorSpaceImpl, - [LinearDisplayP3ColorSpace]: LinearDisplayP3ColorSpaceImpl, -}); - -if (isP3Context) { - THREE.ColorManagement.workingColorSpace = LinearDisplayP3ColorSpace; -} - -init(); - -function init() { - container = document.querySelector('.container'); - - sceneL = new THREE.Scene(); - sceneR = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.z = 6; - - loader = new THREE.TextureLoader(); - - initTextures(); - initSlider(); - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.setScissorTest(true); - container.appendChild(renderer.domElement); - - if (isP3Context && window.matchMedia('( color-gamut: p3 )').matches) { - renderer.outputColorSpace = DisplayP3ColorSpace; - } - - window.addEventListener('resize', onWindowResize); - window.matchMedia('( color-gamut: p3 )').addEventListener('change', onGamutChange); -} - -async function initTextures() { - const path = 'textures/wide_gamut/logo_{colorSpace}.png'; - - textureL = await loader.loadAsync(path.replace('{colorSpace}', 'srgb')); - textureR = await loader.loadAsync(path.replace('{colorSpace}', 'p3')); - - textureL.colorSpace = THREE.SRGBColorSpace; - textureR.colorSpace = DisplayP3ColorSpace; - - sceneL.background = THREE.TextureUtils.contain(textureL, window.innerWidth / window.innerHeight); - sceneR.background = THREE.TextureUtils.contain(textureR, window.innerWidth / window.innerHeight); -} - -function initSlider() { - function onPointerDown() { - if (event.isPrimary === false) return; - - window.addEventListener('pointermove', onPointerMove); - window.addEventListener('pointerup', onPointerUp); - } - - function onPointerUp() { - window.removeEventListener('pointermove', onPointerMove); - window.removeEventListener('pointerup', onPointerUp); - } - - function onPointerMove(e) { - if (event.isPrimary === false) return; - - updateSlider(e.pageX); - } - - updateSlider(sliderPos); - - slider.style.touchAction = 'none'; // disable touch scroll - slider.addEventListener('pointerdown', onPointerDown); -} - -function updateSlider(offset) { - sliderPos = Math.max(10, Math.min(window.innerWidth - 10, offset)); - - slider.style.left = sliderPos - slider.offsetWidth / 2 + 'px'; -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - THREE.TextureUtils.contain(sceneL.background, window.innerWidth / window.innerHeight); - THREE.TextureUtils.contain(sceneR.background, window.innerWidth / window.innerHeight); - - updateSlider(sliderPos); -} - -function onGamutChange({ matches }) { - renderer.outputColorSpace = isP3Context && matches ? DisplayP3ColorSpace : THREE.SRGBColorSpace; - - textureL.needsUpdate = true; - textureR.needsUpdate = true; -} - -function animate() { - renderer.setScissor(0, 0, sliderPos, window.innerHeight); - renderer.render(sceneL, camera); - - renderer.setScissor(sliderPos, 0, window.innerWidth, window.innerHeight); - renderer.render(sceneR, camera); -} diff --git a/examples-testing/examples/webgl_texture2darray_compressed.ts b/examples-testing/examples/webgl_texture2darray_compressed.ts deleted file mode 100644 index e074be576..000000000 --- a/examples-testing/examples/webgl_texture2darray_compressed.ts +++ /dev/null @@ -1,91 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; -import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js'; - -let camera, scene, mesh, renderer, stats, timer; - -const planeWidth = 50; -const planeHeight = 25; - -let depthStep = 1; - -init(); - -function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 2000); - camera.position.z = 70; - - scene = new THREE.Scene(); - - // - timer = new THREE.Timer(); - timer.connect(document); - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - // - - const ktx2Loader = new KTX2Loader(); - ktx2Loader.setTranscoderPath('jsm/libs/basis/'); - ktx2Loader.detectSupport(renderer); - - ktx2Loader.load('textures/spiritedaway.ktx2', function (texturearray) { - const material = new THREE.ShaderMaterial({ - uniforms: { - diffuse: { value: texturearray }, - depth: { value: 55 }, - size: { value: new THREE.Vector2(planeWidth, planeHeight) }, - }, - vertexShader: document.getElementById('vs').textContent.trim(), - fragmentShader: document.getElementById('fs').textContent.trim(), - glslVersion: THREE.GLSL3, - }); - - const geometry = new THREE.PlaneGeometry(planeWidth, planeHeight); - - mesh = new THREE.Mesh(geometry, material); - - scene.add(mesh); - }); - - stats = new Stats(); - container.appendChild(stats.dom); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - if (mesh) { - const delta = timer.getDelta() * 10; - - depthStep += delta; - - const value = depthStep % 5; - - mesh.material.uniforms['depth'].value = value; - } - - render(); - stats.update(); -} - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_texture2darray_layerupdate.ts b/examples-testing/examples/webgl_texture2darray_layerupdate.ts deleted file mode 100644 index 0cc136cb7..000000000 --- a/examples-testing/examples/webgl_texture2darray_layerupdate.ts +++ /dev/null @@ -1,131 +0,0 @@ -import * as THREE from 'three'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js'; - -let camera, scene, mesh, renderer; - -const planeWidth = 20; -const planeHeight = 10; - -init(); - -async function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 2000); - camera.position.z = 70; - - scene = new THREE.Scene(); - - // Configure the renderer. - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - container.appendChild(renderer.domElement); - - // Configure the KTX2 loader. - - const ktx2Loader = new KTX2Loader(); - ktx2Loader.setTranscoderPath('jsm/libs/basis/'); - ktx2Loader.detectSupport(renderer); - - // Load several KTX2 textures which will later be used to modify - // specific texture array layers. - - const spiritedaway = await ktx2Loader.loadAsync('textures/spiritedaway.ktx2'); - - // Create a texture array for rendering. - - const layerByteLength = THREE.TextureUtils.getByteLength( - spiritedaway.image.width, - spiritedaway.image.height, - spiritedaway.format, - spiritedaway.type, - ); - - const textureArray = new THREE.CompressedArrayTexture( - [ - { - data: new Uint8Array(layerByteLength * 3), - width: spiritedaway.image.width, - height: spiritedaway.image.height, - }, - ], - spiritedaway.image.width, - spiritedaway.image.height, - 3, - spiritedaway.format, - spiritedaway.type, - ); - - // Setup the GUI - - const formData = { - srcLayer: 0, - destLayer: 0, - transfer() { - const layerElementLength = layerByteLength / spiritedaway.mipmaps[0].data.BYTES_PER_ELEMENT; - textureArray.mipmaps[0].data.set( - spiritedaway.mipmaps[0].data.subarray( - layerElementLength * (formData.srcLayer % spiritedaway.image.depth), - layerElementLength * ((formData.srcLayer % spiritedaway.image.depth) + 1), - ), - layerByteLength * formData.destLayer, - ); - textureArray.addLayerUpdate(formData.destLayer); - textureArray.needsUpdate = true; - - renderer.render(scene, camera); - }, - }; - - const gui = new GUI(); - gui.add(formData, 'srcLayer', 0, spiritedaway.image.depth - 1, 1); - gui.add(formData, 'destLayer', 0, textureArray.image.depth - 1, 1); - gui.add(formData, 'transfer'); - - /// Setup the scene. - - const material = new THREE.ShaderMaterial({ - uniforms: { - diffuse: { value: textureArray }, - size: { value: new THREE.Vector2(planeWidth, planeHeight) }, - }, - vertexShader: document.getElementById('vs').textContent.trim(), - fragmentShader: document.getElementById('fs').textContent.trim(), - glslVersion: THREE.GLSL3, - }); - - const geometry = new THREE.InstancedBufferGeometry(); - geometry.copy(new THREE.PlaneGeometry(planeWidth, planeHeight)); - geometry.instanceCount = 3; - - const instancedIndexAttribute = new THREE.InstancedBufferAttribute(new Uint16Array([0, 1, 2]), 1, false, 1); - instancedIndexAttribute.gpuType = THREE.IntType; - geometry.setAttribute('instancedIndex', instancedIndexAttribute); - - mesh = new THREE.InstancedMesh(geometry, material, 3); - - scene.add(mesh); - - window.addEventListener('resize', onWindowResize); - - // Initialize the texture array by first rendering the spirited away - // frames in order. - - textureArray.mipmaps[0].data.set(spiritedaway.mipmaps[0].data.subarray(0, textureArray.mipmaps[0].data.length)); - textureArray.needsUpdate = true; - renderer.render(scene, camera); -} - -function onWindowResize() { - renderer.setSize(window.innerWidth, window.innerHeight); - - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_texture3d.ts b/examples-testing/examples/webgl_texture3d.ts deleted file mode 100644 index 977dbadb7..000000000 --- a/examples-testing/examples/webgl_texture3d.ts +++ /dev/null @@ -1,128 +0,0 @@ -import * as THREE from 'three'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { NRRDLoader } from 'three/addons/loaders/NRRDLoader.js'; -import { VolumeRenderShader1 } from 'three/addons/shaders/VolumeShader.js'; - -let renderer, scene, camera, controls, material, volconfig, cmtextures; - -init(); - -function init() { - scene = new THREE.Scene(); - - // Create renderer - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - document.body.appendChild(renderer.domElement); - - // Create camera (The volume renderer does not work very well with perspective yet) - const h = 512; // frustum height - const aspect = window.innerWidth / window.innerHeight; - camera = new THREE.OrthographicCamera((-h * aspect) / 2, (h * aspect) / 2, h / 2, -h / 2, 1, 1000); - camera.position.set(-64, -64, 128); - camera.up.set(0, 0, 1); // In our data, z is up - - // Create controls - controls = new OrbitControls(camera, renderer.domElement); - controls.addEventListener('change', render); - controls.target.set(64, 64, 128); - controls.minZoom = 0.5; - controls.maxZoom = 4; - controls.enablePan = false; - controls.update(); - - // scene.add( new AxesHelper( 128 ) ); - - // Lighting is baked into the shader a.t.m. - // let dirLight = new DirectionalLight( 0xffffff ); - - // The gui for interaction - volconfig = { clim1: 0, clim2: 1, renderstyle: 'iso', isothreshold: 0.15, colormap: 'viridis' }; - const gui = new GUI(); - gui.add(volconfig, 'clim1', 0, 1, 0.01).onChange(updateUniforms); - gui.add(volconfig, 'clim2', 0, 1, 0.01).onChange(updateUniforms); - gui.add(volconfig, 'colormap', { gray: 'gray', viridis: 'viridis' }).onChange(updateUniforms); - gui.add(volconfig, 'renderstyle', { mip: 'mip', iso: 'iso' }).onChange(updateUniforms); - gui.add(volconfig, 'isothreshold', 0, 1, 0.01).onChange(updateUniforms); - - // Load the data ... - new NRRDLoader().load('models/nrrd/stent.nrrd', function (volume) { - // Texture to hold the volume. We have scalars, so we put our data in the red channel. - // THREEJS will select R32F (33326) based on the THREE.RedFormat and THREE.FloatType. - // Also see https://www.khronos.org/registry/webgl/specs/latest/2.0/#TEXTURE_TYPES_FORMATS_FROM_DOM_ELEMENTS_TABLE - // TODO: look the dtype up in the volume metadata - const texture = new THREE.Data3DTexture(volume.data, volume.xLength, volume.yLength, volume.zLength); - texture.format = THREE.RedFormat; - texture.type = THREE.FloatType; - texture.minFilter = texture.magFilter = THREE.LinearFilter; - texture.unpackAlignment = 1; - texture.needsUpdate = true; - - // Colormap textures - cmtextures = { - viridis: new THREE.TextureLoader().load('textures/cm_viridis.png', render), - gray: new THREE.TextureLoader().load('textures/cm_gray.png', render), - }; - - // Material - const shader = VolumeRenderShader1; - - const uniforms = THREE.UniformsUtils.clone(shader.uniforms); - - uniforms['u_data'].value = texture; - uniforms['u_size'].value.set(volume.xLength, volume.yLength, volume.zLength); - uniforms['u_clim'].value.set(volconfig.clim1, volconfig.clim2); - uniforms['u_renderstyle'].value = volconfig.renderstyle == 'mip' ? 0 : 1; // 0: MIP, 1: ISO - uniforms['u_renderthreshold'].value = volconfig.isothreshold; // For ISO renderstyle - uniforms['u_cmdata'].value = cmtextures[volconfig.colormap]; - - material = new THREE.ShaderMaterial({ - uniforms: uniforms, - vertexShader: shader.vertexShader, - fragmentShader: shader.fragmentShader, - side: THREE.BackSide, // The volume shader uses the backface as its "reference point" - }); - - // THREE.Mesh - const geometry = new THREE.BoxGeometry(volume.xLength, volume.yLength, volume.zLength); - geometry.translate(volume.xLength / 2 - 0.5, volume.yLength / 2 - 0.5, volume.zLength / 2 - 0.5); - - const mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - render(); - }); - - window.addEventListener('resize', onWindowResize); -} - -function updateUniforms() { - material.uniforms['u_clim'].value.set(volconfig.clim1, volconfig.clim2); - material.uniforms['u_renderstyle'].value = volconfig.renderstyle == 'mip' ? 0 : 1; // 0: MIP, 1: ISO - material.uniforms['u_renderthreshold'].value = volconfig.isothreshold; // For ISO renderstyle - material.uniforms['u_cmdata'].value = cmtextures[volconfig.colormap]; - - render(); -} - -function onWindowResize() { - renderer.setSize(window.innerWidth, window.innerHeight); - - const aspect = window.innerWidth / window.innerHeight; - - const frustumHeight = camera.top - camera.bottom; - - camera.left = (-frustumHeight * aspect) / 2; - camera.right = (frustumHeight * aspect) / 2; - - camera.updateProjectionMatrix(); - - render(); -} - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_texture3d_partialupdate.ts b/examples-testing/examples/webgl_texture3d_partialupdate.ts deleted file mode 100644 index 58615db84..000000000 --- a/examples-testing/examples/webgl_texture3d_partialupdate.ts +++ /dev/null @@ -1,326 +0,0 @@ -import * as THREE from 'three'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { ImprovedNoise } from 'three/addons/math/ImprovedNoise.js'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -const INITIAL_CLOUD_SIZE = 128; - -let renderer, scene, camera; -let mesh; -let prevTime = performance.now(); -let cloudTexture = null; - -init(); - -function generateCloudTexture(size, scaleFactor = 1.0) { - const data = new Uint8Array(size * size * size); - const scale = (scaleFactor * 10.0) / size; - - let i = 0; - const perlin = new ImprovedNoise(); - const vector = new THREE.Vector3(); - - for (let z = 0; z < size; z++) { - for (let y = 0; y < size; y++) { - for (let x = 0; x < size; x++) { - const dist = vector - .set(x, y, z) - .subScalar(size / 2) - .divideScalar(size) - .length(); - const fadingFactor = (1.0 - dist) * (1.0 - dist); - data[i] = (128 + 128 * perlin.noise((x * scale) / 1.5, y * scale, (z * scale) / 1.5)) * fadingFactor; - - i++; - } - } - } - - return new THREE.Data3DTexture(data, size, size, size); -} - -function init() { - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(0, 0, 1.5); - - new OrbitControls(camera, renderer.domElement); - - // Sky - - const canvas = document.createElement('canvas'); - canvas.width = 1; - canvas.height = 32; - - const context = canvas.getContext('2d'); - const gradient = context.createLinearGradient(0, 0, 0, 32); - gradient.addColorStop(0.0, '#014a84'); - gradient.addColorStop(0.5, '#0561a0'); - gradient.addColorStop(1.0, '#437ab6'); - context.fillStyle = gradient; - context.fillRect(0, 0, 1, 32); - - const skyMap = new THREE.CanvasTexture(canvas); - skyMap.colorSpace = THREE.SRGBColorSpace; - - const sky = new THREE.Mesh( - new THREE.SphereGeometry(10), - new THREE.MeshBasicMaterial({ map: skyMap, side: THREE.BackSide }), - ); - scene.add(sky); - - // Texture - - const texture = new THREE.Data3DTexture( - new Uint8Array(INITIAL_CLOUD_SIZE * INITIAL_CLOUD_SIZE * INITIAL_CLOUD_SIZE).fill(0), - INITIAL_CLOUD_SIZE, - INITIAL_CLOUD_SIZE, - INITIAL_CLOUD_SIZE, - ); - texture.format = THREE.RedFormat; - texture.minFilter = THREE.LinearFilter; - texture.magFilter = THREE.LinearFilter; - texture.unpackAlignment = 1; - texture.needsUpdate = true; - - cloudTexture = texture; - - // Material - - const vertexShader = /* glsl */ ` - in vec3 position; - - uniform mat4 modelMatrix; - uniform mat4 modelViewMatrix; - uniform mat4 projectionMatrix; - uniform vec3 cameraPos; - - out vec3 vOrigin; - out vec3 vDirection; - - void main() { - vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); - - vOrigin = vec3( inverse( modelMatrix ) * vec4( cameraPos, 1.0 ) ).xyz; - vDirection = position - vOrigin; - - gl_Position = projectionMatrix * mvPosition; - } - `; - - const fragmentShader = /* glsl */ ` - precision highp float; - precision highp sampler3D; - - uniform mat4 modelViewMatrix; - uniform mat4 projectionMatrix; - - in vec3 vOrigin; - in vec3 vDirection; - - out vec4 color; - - uniform vec3 base; - uniform sampler3D map; - - uniform float threshold; - uniform float range; - uniform float opacity; - uniform float steps; - uniform float frame; - - uint wang_hash(uint seed) - { - seed = (seed ^ 61u) ^ (seed >> 16u); - seed *= 9u; - seed = seed ^ (seed >> 4u); - seed *= 0x27d4eb2du; - seed = seed ^ (seed >> 15u); - return seed; - } - - float randomFloat(inout uint seed) - { - return float(wang_hash(seed)) / 4294967296.; - } - - vec2 hitBox( vec3 orig, vec3 dir ) { - const vec3 box_min = vec3( - 0.5 ); - const vec3 box_max = vec3( 0.5 ); - vec3 inv_dir = 1.0 / dir; - vec3 tmin_tmp = ( box_min - orig ) * inv_dir; - vec3 tmax_tmp = ( box_max - orig ) * inv_dir; - vec3 tmin = min( tmin_tmp, tmax_tmp ); - vec3 tmax = max( tmin_tmp, tmax_tmp ); - float t0 = max( tmin.x, max( tmin.y, tmin.z ) ); - float t1 = min( tmax.x, min( tmax.y, tmax.z ) ); - return vec2( t0, t1 ); - } - - float sample1( vec3 p ) { - return texture( map, p ).r; - } - - float shading( vec3 coord ) { - float step = 0.01; - return sample1( coord + vec3( - step ) ) - sample1( coord + vec3( step ) ); - } - - vec4 linearToSRGB( in vec4 value ) { - return vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a ); - } - - void main(){ - vec3 rayDir = normalize( vDirection ); - vec2 bounds = hitBox( vOrigin, rayDir ); - - if ( bounds.x > bounds.y ) discard; - - bounds.x = max( bounds.x, 0.0 ); - - vec3 p = vOrigin + bounds.x * rayDir; - vec3 inc = 1.0 / abs( rayDir ); - float delta = min( inc.x, min( inc.y, inc.z ) ); - delta /= steps; - - // Jitter - - // Nice little seed from - // https://blog.demofox.org/2020/05/25/casual-shadertoy-path-tracing-1-basic-camera-diffuse-emissive/ - uint seed = uint( gl_FragCoord.x ) * uint( 1973 ) + uint( gl_FragCoord.y ) * uint( 9277 ) + uint( frame ) * uint( 26699 ); - vec3 size = vec3( textureSize( map, 0 ) ); - float randNum = randomFloat( seed ) * 2.0 - 1.0; - p += rayDir * randNum * ( 1.0 / size ); - - // - - vec4 ac = vec4( base, 0.0 ); - - for ( float t = bounds.x; t < bounds.y; t += delta ) { - - float d = sample1( p + 0.5 ); - - d = smoothstep( threshold - range, threshold + range, d ) * opacity; - - float col = shading( p + 0.5 ) * 3.0 + ( ( p.x + p.y ) * 0.25 ) + 0.2; - - ac.rgb += ( 1.0 - ac.a ) * d * col; - - ac.a += ( 1.0 - ac.a ) * d; - - if ( ac.a >= 0.95 ) break; - - p += rayDir * delta; - - } - - color = linearToSRGB( ac ); - - if ( color.a == 0.0 ) discard; - - } - `; - - const geometry = new THREE.BoxGeometry(1, 1, 1); - const material = new THREE.RawShaderMaterial({ - glslVersion: THREE.GLSL3, - uniforms: { - base: { value: new THREE.Color(0x798aa0) }, - map: { value: texture }, - cameraPos: { value: new THREE.Vector3() }, - threshold: { value: 0.25 }, - opacity: { value: 0.25 }, - range: { value: 0.1 }, - steps: { value: 100 }, - frame: { value: 0 }, - }, - vertexShader, - fragmentShader, - side: THREE.BackSide, - transparent: true, - }); - - mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - // - - const parameters = { - threshold: 0.25, - opacity: 0.25, - range: 0.1, - steps: 100, - }; - - function update() { - material.uniforms.threshold.value = parameters.threshold; - material.uniforms.opacity.value = parameters.opacity; - material.uniforms.range.value = parameters.range; - material.uniforms.steps.value = parameters.steps; - } - - const gui = new GUI(); - gui.add(parameters, 'threshold', 0, 1, 0.01).onChange(update); - gui.add(parameters, 'opacity', 0, 1, 0.01).onChange(update); - gui.add(parameters, 'range', 0, 1, 0.01).onChange(update); - gui.add(parameters, 'steps', 0, 200, 1).onChange(update); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -let curr = 0; -const countPerRow = 4; -const countPerSlice = countPerRow * countPerRow; -const sliceCount = 4; -const totalCount = sliceCount * countPerSlice; -const margins = 8; - -const perElementPaddedSize = (INITIAL_CLOUD_SIZE - margins) / countPerRow; -const perElementSize = Math.floor((INITIAL_CLOUD_SIZE - 1) / countPerRow); - -function animate() { - const time = performance.now(); - if (time - prevTime > 1500.0 && curr < totalCount) { - const position = new THREE.Vector3( - Math.floor(curr % countPerRow) * perElementSize + margins * 0.5, - Math.floor((curr % countPerSlice) / countPerRow) * perElementSize + margins * 0.5, - Math.floor(curr / countPerSlice) * perElementSize + margins * 0.5, - ).floor(); - - const maxDimension = perElementPaddedSize - 1; - const box = new THREE.Box3( - new THREE.Vector3(0, 0, 0), - new THREE.Vector3(maxDimension, maxDimension, maxDimension), - ); - const scaleFactor = (Math.random() + 0.5) * 0.5; - const source = generateCloudTexture(perElementPaddedSize, scaleFactor); - - renderer.copyTextureToTexture(source, cloudTexture, box, position); - - prevTime = time; - - curr++; - } - - mesh.material.uniforms.cameraPos.value.copy(camera.position); - // mesh.rotation.y = - performance.now() / 7500; - - mesh.material.uniforms.frame.value++; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_tonemapping.ts b/examples-testing/examples/webgl_tonemapping.ts deleted file mode 100644 index 2163e1b06..000000000 --- a/examples-testing/examples/webgl_tonemapping.ts +++ /dev/null @@ -1,172 +0,0 @@ -import * as THREE from 'three'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; -import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; - -let renderer, scene, camera, controls; -let gui, - guiExposure = null; - -const params = { - exposure: 1.0, - toneMapping: 'Neutral', - blurriness: 0.3, - intensity: 1.0, -}; - -const toneMappingOptions = { - None: THREE.NoToneMapping, - Linear: THREE.LinearToneMapping, - Reinhard: THREE.ReinhardToneMapping, - Cineon: THREE.CineonToneMapping, - ACESFilmic: THREE.ACESFilmicToneMapping, - AgX: THREE.AgXToneMapping, - Neutral: THREE.NeutralToneMapping, - Custom: THREE.CustomToneMapping, -}; - -init().catch(function (err) { - console.error(err); -}); - -async function init() { - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - renderer.toneMapping = toneMappingOptions[params.toneMapping]; - renderer.toneMappingExposure = params.exposure; - - // Set CustomToneMapping to Uncharted2 - // source: http://filmicworlds.com/blog/filmic-tonemapping-operators/ - - THREE.ShaderChunk.tonemapping_pars_fragment = THREE.ShaderChunk.tonemapping_pars_fragment.replace( - 'vec3 CustomToneMapping( vec3 color ) { return color; }', - - `#define Uncharted2Helper( x ) max( ( ( x * ( 0.15 * x + 0.10 * 0.50 ) + 0.20 * 0.02 ) / ( x * ( 0.15 * x + 0.50 ) + 0.20 * 0.30 ) ) - 0.02 / 0.30, vec3( 0.0 ) ) - - float toneMappingWhitePoint = 1.0; - - vec3 CustomToneMapping( vec3 color ) { - color *= toneMappingExposure; - return saturate( Uncharted2Helper( color ) / Uncharted2Helper( vec3( toneMappingWhitePoint ) ) ); - - }`, - ); - - scene = new THREE.Scene(); - scene.backgroundBlurriness = params.blurriness; - - const light = new THREE.DirectionalLight(0xfff3ee, 3); // simulate sun - light.position.set(1, 0.05, 0.7); - scene.add(light); - - // scene.add( new THREE.DirectionalLightHelper( light, 1, 0x000000 ) ); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.01, 10); - camera.position.set(-0.02, 0.03, 0.05); - - controls = new OrbitControls(camera, renderer.domElement); - controls.enablePan = false; - controls.enableDamping = true; - controls.minDistance = 0.03; - controls.maxDistance = 0.2; - controls.target.set(0, 0.03, 0); - controls.update(); - - const hdrLoader = new HDRLoader().setPath('textures/equirectangular/'); - - const dracoLoader = new DRACOLoader(); - dracoLoader.setDecoderPath('jsm/libs/draco/gltf/'); - - const gltfLoader = new GLTFLoader(); - gltfLoader.setDRACOLoader(dracoLoader); - gltfLoader.setPath('models/gltf/'); - - const [texture, gltf] = await Promise.all([ - hdrLoader.loadAsync('venice_sunset_1k.hdr'), - gltfLoader.loadAsync('venice_mask.glb'), - ]); - - // environment - - texture.mapping = THREE.EquirectangularReflectionMapping; - - scene.background = texture; - scene.environment = texture; - - // model - - scene.add(gltf.scene); - - window.addEventListener('resize', onWindowResize); - - // - - gui = new GUI(); - const toneMappingFolder = gui.addFolder('Tone Mapping'); - - toneMappingFolder - .add(params, 'toneMapping', Object.keys(toneMappingOptions)) - - .name('type') - .onChange(function () { - updateGUI(); - - renderer.toneMapping = toneMappingOptions[params.toneMapping]; - }); - - guiExposure = toneMappingFolder - .add(params, 'exposure', 0, 2) - - .onChange(function (value) { - renderer.toneMappingExposure = value; - }); - - const backgroundFolder = gui.addFolder('Background'); - - backgroundFolder - .add(params, 'blurriness', 0, 1) - - .onChange(function (value) { - scene.backgroundBlurriness = value; - }); - - backgroundFolder - .add(params, 'intensity', 0, 1) - - .onChange(function (value) { - scene.backgroundIntensity = value; - }); - - updateGUI(); - - gui.open(); -} - -function updateGUI() { - if (params.toneMapping === 'None') { - guiExposure.hide(); - } else { - guiExposure.show(); - } -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - controls.update(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_tsl_clearcoat.ts b/examples-testing/examples/webgl_tsl_clearcoat.ts deleted file mode 100644 index 6a123032e..000000000 --- a/examples-testing/examples/webgl_tsl_clearcoat.ts +++ /dev/null @@ -1,194 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { WebGLNodesHandler } from 'three/addons/tsl/WebGLNodesHandler.js'; -import { WebGLRenderer } from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { HDRCubeTextureLoader } from 'three/addons/loaders/HDRCubeTextureLoader.js'; - -import { FlakesTexture } from 'three/addons/textures/FlakesTexture.js'; - -let camera, scene, renderer; - -let particleLight; -let group; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 0.25, 50); - camera.position.z = 10; - - scene = new THREE.Scene(); - - group = new THREE.Group(); - scene.add(group); - - new HDRCubeTextureLoader() - .setPath('textures/cube/pisaHDR/') - .load(['px.hdr', 'nx.hdr', 'py.hdr', 'ny.hdr', 'pz.hdr', 'nz.hdr'], function (texture) { - const geometry = new THREE.SphereGeometry(0.8, 64, 32); - - const textureLoader = new THREE.TextureLoader(); - - const diffuse = textureLoader.load('textures/carbon/Carbon.png'); - diffuse.colorSpace = THREE.SRGBColorSpace; - diffuse.wrapS = THREE.RepeatWrapping; - diffuse.wrapT = THREE.RepeatWrapping; - diffuse.repeat.x = 10; - diffuse.repeat.y = 10; - - const normalMap = textureLoader.load('textures/carbon/Carbon_Normal.png'); - normalMap.wrapS = THREE.RepeatWrapping; - normalMap.wrapT = THREE.RepeatWrapping; - normalMap.repeat.x = 10; - normalMap.repeat.y = 10; - - const normalMap2 = textureLoader.load('textures/water/Water_1_M_Normal.jpg'); - - const normalMap3 = new THREE.CanvasTexture(new FlakesTexture()); - normalMap3.wrapS = THREE.RepeatWrapping; - normalMap3.wrapT = THREE.RepeatWrapping; - normalMap3.repeat.x = 10; - normalMap3.repeat.y = 6; - normalMap3.anisotropy = 16; - - const normalMap4 = textureLoader.load('textures/golfball.jpg'); - - const clearcoatNormalMap = textureLoader.load( - 'textures/pbr/Scratched_gold/Scratched_gold_01_1K_Normal.png', - ); - - // car paint - - let material = new THREE.MeshPhysicalNodeMaterial({ - clearcoat: 1.0, - clearcoatRoughness: 0.1, - metalness: 0.9, - roughness: 0.5, - color: 0x0000ff, - normalMap: normalMap3, - normalScale: new THREE.Vector2(0.15, 0.15), - }); - let mesh = new THREE.Mesh(geometry, material); - mesh.position.x = -1; - mesh.position.y = 1; - group.add(mesh); - - // fibers - - material = new THREE.MeshPhysicalNodeMaterial({ - roughness: 0.5, - clearcoat: 1.0, - clearcoatRoughness: 0.1, - map: diffuse, - normalMap: normalMap, - }); - mesh = new THREE.Mesh(geometry, material); - mesh.position.x = 1; - mesh.position.y = 1; - group.add(mesh); - - // golf - - material = new THREE.MeshPhysicalNodeMaterial({ - metalness: 0.0, - roughness: 0.1, - clearcoat: 1.0, - normalMap: normalMap4, - clearcoatNormalMap: clearcoatNormalMap, - - // y scale is negated to compensate for normal map handedness. - clearcoatNormalScale: new THREE.Vector2(2.0, -2.0), - }); - mesh = new THREE.Mesh(geometry, material); - mesh.position.x = -1; - mesh.position.y = -1; - group.add(mesh); - - // clearcoat + normalmap - - material = new THREE.MeshPhysicalNodeMaterial({ - clearcoat: 1.0, - metalness: 1.0, - color: 0xff0000, - normalMap: normalMap2, - normalScale: new THREE.Vector2(0.15, 0.15), - clearcoatNormalMap: clearcoatNormalMap, - - // y scale is negated to compensate for normal map handedness. - clearcoatNormalScale: new THREE.Vector2(2.0, -2.0), - }); - mesh = new THREE.Mesh(geometry, material); - mesh.position.x = 1; - mesh.position.y = -1; - group.add(mesh); - - // - - scene.background = texture; - scene.environment = texture; - }); - - // LIGHTS - - particleLight = new THREE.Mesh( - new THREE.SphereGeometry(0.05, 8, 8), - new THREE.MeshBasicMaterial({ color: 0xffffff }), - ); - scene.add(particleLight); - - particleLight.add(new THREE.PointLight(0xffffff, 30)); - - renderer = new WebGLRenderer({ antialias: true }); - renderer.setNodesHandler(new WebGLNodesHandler()); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // - - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = 1.25; - - // EVENTS - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 3; - controls.maxDistance = 30; - - window.addEventListener('resize', onWindowResize); -} - -// - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); -} - -// - -function animate() { - render(); -} - -function render() { - const timer = Date.now() * 0.00025; - - particleLight.position.x = Math.sin(timer * 7) * 3; - particleLight.position.y = Math.cos(timer * 5) * 4; - particleLight.position.z = Math.cos(timer * 3) * 3; - - for (let i = 0; i < group.children.length; i++) { - const child = group.children[i]; - child.rotation.y += 0.005; - } - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_tsl_instancing.ts b/examples-testing/examples/webgl_tsl_instancing.ts deleted file mode 100644 index c80bebf9b..000000000 --- a/examples-testing/examples/webgl_tsl_instancing.ts +++ /dev/null @@ -1,286 +0,0 @@ -import * as THREE from 'three'; -import { WebGLNodesHandler } from 'three/addons/tsl/WebGLNodesHandler.js'; - -import Stats from 'three/addons/libs/stats.module.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js'; -import { MeshPhongNodeMaterial } from 'three/webgpu'; -import { sin, time, vec3, positionWorld } from 'three/tsl'; - -let container, stats, gui, guiStatsEl; -let camera, controls, scene, renderer, material; - -// gui - -const Method = { - INSTANCED: 'INSTANCED', - MERGED: 'MERGED', - NAIVE: 'NAIVE', -}; - -const api = { - method: Method.INSTANCED, - count: 1000, -}; - -// - -init(); -initMesh(); - -// - -function clean() { - const meshes = []; - - scene.traverse(function (object) { - if (object.isMesh) meshes.push(object); - }); - - for (let i = 0; i < meshes.length; i++) { - const mesh = meshes[i]; - mesh.material.dispose(); - mesh.geometry.dispose(); - - scene.remove(mesh); - } -} - -const randomizeMatrix = (function () { - const position = new THREE.Vector3(); - const quaternion = new THREE.Quaternion(); - const scale = new THREE.Vector3(); - - return function (matrix) { - position.x = Math.random() * 40 - 20; - position.y = Math.random() * 40 - 20; - position.z = Math.random() * 40 - 20; - - quaternion.random(); - - scale.x = scale.y = scale.z = Math.random() * 1; - - matrix.compose(position, quaternion, scale); - }; -})(); - -function initMesh() { - clean(); - - // make instances - new THREE.BufferGeometryLoader().setPath('models/json/').load('suzanne_buffergeometry.json', function (geometry) { - const posY = positionWorld; - const t = time.mul(4); - material = new MeshPhongNodeMaterial(); - material.colorNode = vec3( - sin(posY.mul(0.1).add(t)).mul(0.5).add(0.5).x, - sin(posY.mul(0.1).add(t.mul(0.5).add(2))) - .mul(0.5) - .add(0.5).y, - sin(posY.mul(0.1).add(t.mul(1.5).add(4))) - .mul(0.5) - .add(0.5).z, - ); - - geometry.computeVertexNormals(); - - console.time(api.method + ' (build)'); - - switch (api.method) { - case Method.INSTANCED: - makeInstanced(geometry); - break; - - case Method.MERGED: - makeMerged(geometry); - break; - - case Method.NAIVE: - makeNaive(geometry); - break; - } - - console.timeEnd(api.method + ' (build)'); - }); -} - -function makeInstanced(geometry) { - const matrix = new THREE.Matrix4(); - const mesh = new THREE.InstancedMesh(geometry, material, api.count); - - for (let i = 0; i < api.count; i++) { - randomizeMatrix(matrix); - mesh.setMatrixAt(i, matrix); - } - - scene.add(mesh); - - // - - const geometryByteLength = getGeometryByteLength(geometry); - - guiStatsEl.innerHTML = [ - 'GPU draw calls: 1', - 'GPU memory: ' + formatBytes(api.count * 16 + geometryByteLength, 2), - ].join('
'); -} - -function makeMerged(geometry) { - const geometries = []; - const matrix = new THREE.Matrix4(); - - for (let i = 0; i < api.count; i++) { - randomizeMatrix(matrix); - - const instanceGeometry = geometry.clone(); - instanceGeometry.applyMatrix4(matrix); - - geometries.push(instanceGeometry); - } - - const mergedGeometry = BufferGeometryUtils.mergeGeometries(geometries); - - scene.add(new THREE.Mesh(mergedGeometry, material)); - - // - - guiStatsEl.innerHTML = [ - 'GPU draw calls: 1', - 'GPU memory: ' + formatBytes(getGeometryByteLength(mergedGeometry), 2), - ].join('
'); -} - -function makeNaive(geometry) { - const matrix = new THREE.Matrix4(); - - for (let i = 0; i < api.count; i++) { - randomizeMatrix(matrix); - - const mesh = new THREE.Mesh(geometry, material); - mesh.applyMatrix4(matrix); - - scene.add(mesh); - } - - // - - const geometryByteLength = getGeometryByteLength(geometry); - - guiStatsEl.innerHTML = [ - 'GPU draw calls: ' + api.count, - 'GPU memory: ' + formatBytes(api.count * 16 + geometryByteLength, 2), - ].join('
'); -} - -function init() { - const width = window.innerWidth; - const height = window.innerHeight; - - // camera - - camera = new THREE.PerspectiveCamera(70, width / height, 1, 100); - camera.position.z = 30; - - // renderer - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setNodesHandler(new WebGLNodesHandler()); - - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(width, height); - renderer.setAnimationLoop(animate); - container = document.getElementById('container'); - container.appendChild(renderer.domElement); - - // scene - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xffffff); - - // controls - - controls = new OrbitControls(camera, renderer.domElement); - controls.autoRotate = true; - controls.autoRotateSpeed = 0.5; - - // light - - const ambientLight = new THREE.AmbientLight(0xffffff, 2); - const directionalLight = new THREE.DirectionalLight(0xffffff, 2); - directionalLight.position.set(1, 1, 1); - scene.add(directionalLight, ambientLight); - - // stats - - stats = new Stats(); - container.appendChild(stats.dom); - - // gui - - gui = new GUI(); - gui.add(api, 'method', Method).onChange(initMesh); - gui.add(api, 'count', 1, 10000).step(1).onChange(initMesh); - - const perfFolder = gui.addFolder('Performance'); - - guiStatsEl = document.createElement('div'); - guiStatsEl.classList.add('gui-stats'); - - perfFolder.$children.appendChild(guiStatsEl); - perfFolder.open(); - - // listeners - - window.addEventListener('resize', onWindowResize); - - Object.assign(window, { scene }); -} - -// - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); -} - -function animate() { - controls.update(); - - renderer.render(scene, camera); - - stats.update(); -} - -// - -function getGeometryByteLength(geometry) { - let total = 0; - - if (geometry.index) total += geometry.index.array.byteLength; - - for (const name in geometry.attributes) { - total += geometry.attributes[name].array.byteLength; - } - - return total; -} - -// Source: https://stackoverflow.com/a/18650828/1314762 -function formatBytes(bytes, decimals) { - if (bytes === 0) return '0 bytes'; - - const k = 1024; - const dm = decimals < 0 ? 0 : decimals; - const sizes = ['bytes', 'KB', 'MB']; - - const i = Math.floor(Math.log(bytes) / Math.log(k)); - - return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; -} diff --git a/examples-testing/examples/webgl_tsl_shadowmap.ts b/examples-testing/examples/webgl_tsl_shadowmap.ts deleted file mode 100644 index f006d7c1a..000000000 --- a/examples-testing/examples/webgl_tsl_shadowmap.ts +++ /dev/null @@ -1,160 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { WebGLNodesHandler } from 'three/addons/tsl/WebGLNodesHandler.js'; -import { WebGLRenderer } from 'three'; -import { mx_fractal_noise_float, mx_fractal_noise_vec3, positionLocal, positionWorld, Fn, color } from 'three/tsl'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let camera, scene, renderer, timer; -let dirLight, spotLight; -let torusKnot, dirGroup; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(0, 10, 20); - - scene = new THREE.Scene(); - scene.backgroundNode = color(0x222244); - scene.fog = new THREE.Fog(0x222244, 50, 100); - - // lights - - scene.add(new THREE.AmbientLight(0x444444, 2)); - - spotLight = new THREE.SpotLight(0xff8888, 400); - spotLight.angle = Math.PI / 5; - spotLight.penumbra = 0.3; - spotLight.position.set(8, 10, 5); - spotLight.castShadow = true; - spotLight.shadow.camera.near = 8; - spotLight.shadow.camera.far = 200; - spotLight.shadow.mapSize.width = 2048; - spotLight.shadow.mapSize.height = 2048; - spotLight.shadow.radius = 4; - scene.add(spotLight); - - dirLight = new THREE.DirectionalLight(0x8888ff, 3); - dirLight.position.set(3, 12, 17); - dirLight.castShadow = true; - dirLight.shadow.camera.near = 0.1; - dirLight.shadow.camera.far = 500; - dirLight.shadow.camera.right = 17; - dirLight.shadow.camera.left = -17; - dirLight.shadow.camera.top = 17; - dirLight.shadow.camera.bottom = -17; - dirLight.shadow.mapSize.width = 2048; - dirLight.shadow.mapSize.height = 2048; - dirLight.shadow.radius = 4; - - dirGroup = new THREE.Group(); - dirGroup.add(dirLight); - scene.add(dirGroup); - - // geometry - - const geometry = new THREE.TorusKnotGeometry(25, 8, 75, 80); - const material = new THREE.MeshPhongNodeMaterial({ - color: 0x999999, - shininess: 0, - specular: 0x222222, - }); - - const materialCustomShadow = material.clone(); - materialCustomShadow.transparent = true; - - const discardNode = mx_fractal_noise_float(positionLocal.mul(0.1)).x.greaterThan(0.0); - - materialCustomShadow.maskNode = discardNode; - - torusKnot = new THREE.Mesh(geometry, materialCustomShadow); - torusKnot.scale.multiplyScalar(1 / 18); - torusKnot.position.y = 3; - torusKnot.castShadow = true; - torusKnot.receiveShadow = true; - scene.add(torusKnot); - - const cylinderGeometry = new THREE.CylinderGeometry(0.75, 0.75, 7, 32); - - const pillar1 = new THREE.Mesh(cylinderGeometry, material); - pillar1.position.set(8, 3.5, 8); - pillar1.castShadow = true; - - const pillar2 = pillar1.clone(); - pillar2.position.set(8, 3.5, -8); - const pillar3 = pillar1.clone(); - pillar3.position.set(-8, 3.5, 8); - const pillar4 = pillar1.clone(); - pillar4.position.set(-8, 3.5, -8); - - scene.add(pillar1); - scene.add(pillar2); - scene.add(pillar3); - scene.add(pillar4); - - const planeGeometry = new THREE.PlaneGeometry(200, 200); - - const planeMaterial = new THREE.MeshPhongNodeMaterial(); - planeMaterial.color.setHex(0x999999); - planeMaterial.shininess = 0; - planeMaterial.specular.setHex(0x111111); - - planeMaterial.colorNode = Fn(() => { - const pos = positionWorld.toVar(); - pos.xz.addAssign(mx_fractal_noise_vec3(positionWorld.mul(2)).saturate().xz); - return mx_fractal_noise_vec3(positionWorld.mul(2)).saturate().zzz.mul(0.2).add(0.5); - })(); - - const ground = new THREE.Mesh(planeGeometry, planeMaterial); - ground.rotation.x = -Math.PI / 2; - ground.scale.multiplyScalar(3); - ground.castShadow = true; - ground.receiveShadow = true; - scene.add(ground); - - // renderer - - renderer = new WebGLRenderer({ antialias: true }); - renderer.setNodesHandler(new WebGLNodesHandler()); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.shadowMap.enabled = true; - renderer.toneMapping = THREE.ACESFilmicToneMapping; - document.body.appendChild(renderer.domElement); - - // Mouse control - const controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, 2, 0); - controls.minDistance = 7; - controls.maxDistance = 40; - controls.update(); - - timer = new THREE.Timer(); - timer.connect(document); - - window.addEventListener('resize', resize); -} - -function resize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate(time) { - timer.update(); - - const delta = timer.getDelta(); - - torusKnot.rotation.x += 0.25 * delta; - torusKnot.rotation.y += 0.5 * delta; - torusKnot.rotation.z += 1 * delta; - - dirGroup.rotation.y += 0.7 * delta; - dirLight.position.z = 17 + Math.sin(time * 0.001) * 5; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_tsl_skinning.ts b/examples-testing/examples/webgl_tsl_skinning.ts deleted file mode 100644 index 6164dbab6..000000000 --- a/examples-testing/examples/webgl_tsl_skinning.ts +++ /dev/null @@ -1,74 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { WebGLNodesHandler } from 'three/addons/tsl/WebGLNodesHandler.js'; -import { WebGLRenderer } from 'three'; - -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -let camera, scene, renderer; - -let mixer, timer; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.01, 100); - camera.position.set(1, 2, 3); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x3355aa); - camera.lookAt(0, 1, 0); - - timer = new THREE.Timer(); - timer.connect(document); - - //lights - - const light = new THREE.PointLight(0xffffff, 1, 100); - light.power = 2500; - camera.add(light); - scene.add(camera); - - const ambient = new THREE.AmbientLight(0x4466ff, 1); - scene.add(ambient); - - const loader = new GLTFLoader(); - loader.load('models/gltf/Michelle.glb', function (gltf) { - const object = gltf.scene; - mixer = new THREE.AnimationMixer(object); - - const action = mixer.clipAction(gltf.animations[0]); - action.play(); - - scene.add(object); - }); - - //renderer - - renderer = new WebGLRenderer({ antialias: true }); - renderer.setNodesHandler(new WebGLNodesHandler()); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.toneMapping = THREE.LinearToneMapping; - renderer.toneMappingExposure = 0.4; - document.body.appendChild(renderer.domElement); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - const delta = timer.getDelta(); - - if (mixer) mixer.update(delta); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_ubo.ts b/examples-testing/examples/webgl_ubo.ts deleted file mode 100644 index a34a5b2ff..000000000 --- a/examples-testing/examples/webgl_ubo.ts +++ /dev/null @@ -1,140 +0,0 @@ -import * as THREE from 'three'; - -let camera, scene, renderer, timer; - -init(); - -function init() { - const container = document.getElementById('container'); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(0, 0, 25); - - scene = new THREE.Scene(); - camera.lookAt(scene.position); - - timer = new THREE.Timer(); - timer.connect(document); - - // geometry - - const geometry1 = new THREE.TetrahedronGeometry(); - const geometry2 = new THREE.BoxGeometry(); - - // texture - - const texture = new THREE.TextureLoader().load('textures/crate.gif'); - texture.colorSpace = THREE.SRGBColorSpace; - - // uniforms groups - - // Camera and lighting related data are perfect examples of using UBOs since you have to store these - // data just once. They can be shared across all shader programs. - - const cameraUniformsGroup = new THREE.UniformsGroup(); - cameraUniformsGroup.setName('ViewData'); - cameraUniformsGroup.add(new THREE.Uniform(camera.projectionMatrix)); // projection matrix - cameraUniformsGroup.add(new THREE.Uniform(camera.matrixWorldInverse)); // view matrix - - const lightingUniformsGroup = new THREE.UniformsGroup(); - lightingUniformsGroup.setName('LightingData'); - lightingUniformsGroup.add(new THREE.Uniform(new THREE.Vector3(0, 0, 10))); // light position - lightingUniformsGroup.add(new THREE.Uniform(new THREE.Color(0x7c7c7c))); // ambient color - lightingUniformsGroup.add(new THREE.Uniform(new THREE.Color(0xd5d5d5))); // diffuse color - lightingUniformsGroup.add(new THREE.Uniform(new THREE.Color(0xe7e7e7))); // specular color - lightingUniformsGroup.add(new THREE.Uniform(64)); // shininess - - // materials - - const material1 = new THREE.RawShaderMaterial({ - uniforms: { - modelMatrix: { value: null }, - normalMatrix: { value: null }, - color: { value: null }, - }, - vertexShader: document.getElementById('vertexShader1').textContent, - fragmentShader: document.getElementById('fragmentShader1').textContent, - glslVersion: THREE.GLSL3, - }); - - const material2 = new THREE.RawShaderMaterial({ - uniforms: { - modelMatrix: { value: null }, - diffuseMap: { value: null }, - }, - vertexShader: document.getElementById('vertexShader2').textContent, - fragmentShader: document.getElementById('fragmentShader2').textContent, - glslVersion: THREE.GLSL3, - }); - - // meshes - - for (let i = 0; i < 200; i++) { - let mesh; - - if (i % 2 === 0) { - mesh = new THREE.Mesh(geometry1, material1.clone()); - - mesh.material.uniformsGroups = [cameraUniformsGroup, lightingUniformsGroup]; - mesh.material.uniforms.modelMatrix.value = mesh.matrixWorld; - mesh.material.uniforms.normalMatrix.value = mesh.normalMatrix; - mesh.material.uniforms.color.value = new THREE.Color(0xffffff * Math.random()); - } else { - mesh = new THREE.Mesh(geometry2, material2.clone()); - - mesh.material.uniformsGroups = [cameraUniformsGroup, lightingUniformsGroup]; - mesh.material.uniforms.modelMatrix.value = mesh.matrixWorld; - mesh.material.uniforms.diffuseMap.value = texture; - } - - scene.add(mesh); - - const s = 1 + Math.random() * 0.5; - - mesh.scale.x = s; - mesh.scale.y = s; - mesh.scale.z = s; - - mesh.rotation.x = Math.random() * Math.PI; - mesh.rotation.y = Math.random() * Math.PI; - mesh.rotation.z = Math.random() * Math.PI; - - mesh.position.x = Math.random() * 40 - 20; - mesh.position.y = Math.random() * 40 - 20; - mesh.position.z = Math.random() * 20 - 10; - } - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - window.addEventListener('resize', onWindowResize, false); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - timer.update(); - - const delta = timer.getDelta(); - - scene.traverse(function (child) { - if (child.isMesh) { - child.rotation.x += delta * 0.5; - child.rotation.y += delta * 0.3; - } - }); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_ubo_arrays.ts b/examples-testing/examples/webgl_ubo_arrays.ts deleted file mode 100644 index 1d8f5f763..000000000 --- a/examples-testing/examples/webgl_ubo_arrays.ts +++ /dev/null @@ -1,174 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let camera, scene, renderer, timer, stats; - -let lightingUniformsGroup, lightCenters; - -const container = document.getElementById('container'); - -const pointLightsMax = 300; - -const api = { - count: 200, -}; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(0, 50, 50); - - scene = new THREE.Scene(); - camera.lookAt(scene.position); - - timer = new THREE.Timer(); - timer.connect(document); - - // geometry - - const geometry = new THREE.SphereGeometry(); - - // uniforms groups - - lightingUniformsGroup = new THREE.UniformsGroup(); - lightingUniformsGroup.setName('LightingData'); - - const data = []; - const dataColors = []; - lightCenters = []; - - for (let i = 0; i < pointLightsMax; i++) { - const col = new THREE.Color(0xffffff * Math.random()).toArray(); - const x = Math.random() * 50 - 25; - const z = Math.random() * 50 - 25; - - data.push(new THREE.Uniform(new THREE.Vector4(x, 1, z, 0))); // light position - dataColors.push(new THREE.Uniform(new THREE.Vector4(col[0], col[1], col[2], 0))); // light color - - // Store the center positions - lightCenters.push({ x, z }); - } - - lightingUniformsGroup.add(data); // light position - lightingUniformsGroup.add(dataColors); // light position - lightingUniformsGroup.add(new THREE.Uniform(pointLightsMax)); // light position - - const cameraUniformsGroup = new THREE.UniformsGroup(); - cameraUniformsGroup.setName('ViewData'); - cameraUniformsGroup.add(new THREE.Uniform(camera.projectionMatrix)); // projection matrix - cameraUniformsGroup.add(new THREE.Uniform(camera.matrixWorldInverse)); // view matrix - - const material = new THREE.RawShaderMaterial({ - uniforms: { - modelMatrix: { value: null }, - normalMatrix: { value: null }, - }, - // uniformsGroups: [ cameraUniformsGroup, lightingUniformsGroup ], - name: 'Box', - defines: { - POINTLIGHTS_MAX: pointLightsMax, - }, - vertexShader: document.getElementById('vertexShader').textContent, - fragmentShader: document.getElementById('fragmentShader').textContent, - glslVersion: THREE.GLSL3, - }); - - const plane = new THREE.Mesh(new THREE.PlaneGeometry(100, 100), material.clone()); - plane.material.uniformsGroups = [cameraUniformsGroup, lightingUniformsGroup]; - plane.material.uniforms.modelMatrix.value = plane.matrixWorld; - plane.material.uniforms.normalMatrix.value = plane.normalMatrix; - plane.rotation.x = -Math.PI / 2; - plane.position.y = -1; - scene.add(plane); - - // meshes - const gridSize = { x: 10, y: 1, z: 10 }; - const spacing = 6; - - for (let i = 0; i < gridSize.x; i++) { - for (let j = 0; j < gridSize.y; j++) { - for (let k = 0; k < gridSize.z; k++) { - const mesh = new THREE.Mesh(geometry, material.clone()); - mesh.name = 'Sphere'; - mesh.material.uniformsGroups = [cameraUniformsGroup, lightingUniformsGroup]; - mesh.material.uniforms.modelMatrix.value = mesh.matrixWorld; - mesh.material.uniforms.normalMatrix.value = mesh.normalMatrix; - scene.add(mesh); - - mesh.position.x = i * spacing - (gridSize.x * spacing) / 2; - mesh.position.y = 0; - mesh.position.z = k * spacing - (gridSize.z * spacing) / 2; - } - } - } - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - window.addEventListener('resize', onWindowResize, false); - - // controls - - const controls = new OrbitControls(camera, renderer.domElement); - controls.enablePan = false; - - // stats - - stats = new Stats(); - document.body.appendChild(stats.dom); - - // gui - const gui = new GUI(); - gui.add(api, 'count', 1, pointLightsMax) - .step(1) - .onChange(function () { - lightingUniformsGroup.uniforms[2].value = api.count; - }); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - timer.update(); - - const elapsedTime = timer.getElapsed(); - - const lights = lightingUniformsGroup.uniforms[0]; - - // Parameters for circular movement - const radius = 5; // Smaller radius for individual circular movements - const speed = 0.5; // Speed of rotation - - // Update each light's position - for (let i = 0; i < lights.length; i++) { - const light = lights[i]; - const center = lightCenters[i]; - - // Calculate circular movement around the light's center - const angle = speed * elapsedTime + i * 0.5; // Phase difference for each light - const x = center.x + Math.sin(angle) * radius; - const z = center.z + Math.cos(angle) * radius; - - // Update the light's position - light.value.set(x, 1, z, 0); - } - - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/webgl_video_kinect.ts b/examples-testing/examples/webgl_video_kinect.ts deleted file mode 100644 index 8abc93917..000000000 --- a/examples-testing/examples/webgl_video_kinect.ts +++ /dev/null @@ -1,114 +0,0 @@ -import * as THREE from 'three'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let scene, camera, renderer; -let geometry, mesh, material; -let mouse, center; - -init(); - -function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - const info = document.createElement('div'); - info.id = 'info'; - info.innerHTML = 'three.js - kinect'; - document.body.appendChild(info); - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000); - camera.position.set(0, 0, 500); - - scene = new THREE.Scene(); - center = new THREE.Vector3(); - center.z = -1000; - - const video = document.getElementById('video'); - - const texture = new THREE.VideoTexture(video); - texture.minFilter = THREE.NearestFilter; - texture.generateMipmaps = false; - - const width = 640, - height = 480; - const nearClipping = 850, - farClipping = 4000; - - geometry = new THREE.BufferGeometry(); - - const vertices = new Float32Array(width * height * 3); - - for (let i = 0, j = 0, l = vertices.length; i < l; i += 3, j++) { - vertices[i] = j % width; - vertices[i + 1] = Math.floor(j / width); - } - - geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3)); - - material = new THREE.ShaderMaterial({ - uniforms: { - map: { value: texture }, - width: { value: width }, - height: { value: height }, - nearClipping: { value: nearClipping }, - farClipping: { value: farClipping }, - - pointSize: { value: 2 }, - zOffset: { value: 1000 }, - }, - vertexShader: document.getElementById('vs').textContent, - fragmentShader: document.getElementById('fs').textContent, - blending: THREE.AdditiveBlending, - depthTest: false, - depthWrite: false, - transparent: true, - }); - - mesh = new THREE.Points(geometry, material); - scene.add(mesh); - - const gui = new GUI(); - gui.add(material.uniforms.nearClipping, 'value', 1, 10000, 1.0).name('nearClipping'); - gui.add(material.uniforms.farClipping, 'value', 1, 10000, 1.0).name('farClipping'); - gui.add(material.uniforms.pointSize, 'value', 1, 10, 1.0).name('pointSize'); - gui.add(material.uniforms.zOffset, 'value', 0, 4000, 1.0).name('zOffset'); - - video.play(); - - // - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - mouse = new THREE.Vector3(0, 0, 1); - - document.addEventListener('mousemove', onDocumentMouseMove); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function onDocumentMouseMove(event) { - mouse.x = (event.clientX - window.innerWidth / 2) * 8; - mouse.y = (event.clientY - window.innerHeight / 2) * 8; -} - -function animate() { - camera.position.x += (mouse.x - camera.position.x) * 0.05; - camera.position.y += (-mouse.y - camera.position.y) * 0.05; - camera.lookAt(center); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_video_panorama_equirectangular.ts b/examples-testing/examples/webgl_video_panorama_equirectangular.ts deleted file mode 100644 index 866eca16a..000000000 --- a/examples-testing/examples/webgl_video_panorama_equirectangular.ts +++ /dev/null @@ -1,95 +0,0 @@ -import * as THREE from 'three'; - -let camera, scene, renderer; - -let isUserInteracting = false, - lon = 0, - lat = 0, - phi = 0, - theta = 0, - onPointerDownPointerX = 0, - onPointerDownPointerY = 0, - onPointerDownLon = 0, - onPointerDownLat = 0; - -const distance = 0.5; - -init(); - -function init() { - const container = document.getElementById('container'); - - camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.25, 10); - - scene = new THREE.Scene(); - - const geometry = new THREE.SphereGeometry(5, 60, 40); - // invert the geometry on the x-axis so that all of the faces point inward - geometry.scale(-1, 1, 1); - - const video = document.getElementById('video'); - video.play(); - - const texture = new THREE.VideoTexture(video); - texture.colorSpace = THREE.SRGBColorSpace; - const material = new THREE.MeshBasicMaterial({ map: texture }); - - const mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - document.addEventListener('pointerdown', onPointerDown); - document.addEventListener('pointermove', onPointerMove); - document.addEventListener('pointerup', onPointerUp); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function onPointerDown(event) { - isUserInteracting = true; - - onPointerDownPointerX = event.clientX; - onPointerDownPointerY = event.clientY; - - onPointerDownLon = lon; - onPointerDownLat = lat; -} - -function onPointerMove(event) { - if (isUserInteracting === true) { - lon = (onPointerDownPointerX - event.clientX) * 0.1 + onPointerDownLon; - lat = (onPointerDownPointerY - event.clientY) * 0.1 + onPointerDownLat; - } -} - -function onPointerUp() { - isUserInteracting = false; -} - -function animate() { - lat = Math.max(-85, Math.min(85, lat)); - phi = THREE.MathUtils.degToRad(90 - lat); - theta = THREE.MathUtils.degToRad(lon); - - camera.position.x = distance * Math.sin(phi) * Math.cos(theta); - camera.position.y = distance * Math.cos(phi); - camera.position.z = distance * Math.sin(phi) * Math.sin(theta); - - camera.lookAt(0, 0, 0); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_volume_cloud.ts b/examples-testing/examples/webgl_volume_cloud.ts deleted file mode 100644 index 9aa07b98f..000000000 --- a/examples-testing/examples/webgl_volume_cloud.ts +++ /dev/null @@ -1,279 +0,0 @@ -import * as THREE from 'three'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { ImprovedNoise } from 'three/addons/math/ImprovedNoise.js'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let renderer, scene, camera; -let mesh; - -init(); - -function init() { - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(0, 0, 1.5); - - new OrbitControls(camera, renderer.domElement); - - // Sky - - const canvas = document.createElement('canvas'); - canvas.width = 1; - canvas.height = 32; - - const context = canvas.getContext('2d'); - const gradient = context.createLinearGradient(0, 0, 0, 32); - gradient.addColorStop(0.0, '#014a84'); - gradient.addColorStop(0.5, '#0561a0'); - gradient.addColorStop(1.0, '#437ab6'); - context.fillStyle = gradient; - context.fillRect(0, 0, 1, 32); - - const skyMap = new THREE.CanvasTexture(canvas); - skyMap.colorSpace = THREE.SRGBColorSpace; - - const sky = new THREE.Mesh( - new THREE.SphereGeometry(10), - new THREE.MeshBasicMaterial({ map: skyMap, side: THREE.BackSide }), - ); - scene.add(sky); - - // Texture - - const size = 128; - const data = new Uint8Array(size * size * size); - - let i = 0; - const scale = 0.05; - const perlin = new ImprovedNoise(); - const vector = new THREE.Vector3(); - - for (let z = 0; z < size; z++) { - for (let y = 0; y < size; y++) { - for (let x = 0; x < size; x++) { - const d = - 1.0 - - vector - .set(x, y, z) - .subScalar(size / 2) - .divideScalar(size) - .length(); - data[i] = (128 + 128 * perlin.noise((x * scale) / 1.5, y * scale, (z * scale) / 1.5)) * d * d; - i++; - } - } - } - - const texture = new THREE.Data3DTexture(data, size, size, size); - texture.format = THREE.RedFormat; - texture.minFilter = THREE.LinearFilter; - texture.magFilter = THREE.LinearFilter; - texture.unpackAlignment = 1; - texture.needsUpdate = true; - - // Material - - const vertexShader = /* glsl */ ` - in vec3 position; - - uniform mat4 modelMatrix; - uniform mat4 modelViewMatrix; - uniform mat4 projectionMatrix; - uniform vec3 cameraPos; - - out vec3 vOrigin; - out vec3 vDirection; - - void main() { - vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); - - vOrigin = vec3( inverse( modelMatrix ) * vec4( cameraPos, 1.0 ) ).xyz; - vDirection = position - vOrigin; - - gl_Position = projectionMatrix * mvPosition; - } - `; - - const fragmentShader = /* glsl */ ` - precision highp float; - precision highp sampler3D; - - uniform mat4 modelViewMatrix; - uniform mat4 projectionMatrix; - - in vec3 vOrigin; - in vec3 vDirection; - - out vec4 color; - - uniform vec3 base; - uniform sampler3D map; - - uniform float threshold; - uniform float range; - uniform float opacity; - uniform float steps; - uniform float frame; - - uint wang_hash(uint seed) - { - seed = (seed ^ 61u) ^ (seed >> 16u); - seed *= 9u; - seed = seed ^ (seed >> 4u); - seed *= 0x27d4eb2du; - seed = seed ^ (seed >> 15u); - return seed; - } - - float randomFloat(inout uint seed) - { - return float(wang_hash(seed)) / 4294967296.; - } - - vec2 hitBox( vec3 orig, vec3 dir ) { - const vec3 box_min = vec3( - 0.5 ); - const vec3 box_max = vec3( 0.5 ); - vec3 inv_dir = 1.0 / dir; - vec3 tmin_tmp = ( box_min - orig ) * inv_dir; - vec3 tmax_tmp = ( box_max - orig ) * inv_dir; - vec3 tmin = min( tmin_tmp, tmax_tmp ); - vec3 tmax = max( tmin_tmp, tmax_tmp ); - float t0 = max( tmin.x, max( tmin.y, tmin.z ) ); - float t1 = min( tmax.x, min( tmax.y, tmax.z ) ); - return vec2( t0, t1 ); - } - - float sample1( vec3 p ) { - return texture( map, p ).r; - } - - float shading( vec3 coord ) { - float step = 0.01; - return sample1( coord + vec3( - step ) ) - sample1( coord + vec3( step ) ); - } - - vec4 linearToSRGB( in vec4 value ) { - return vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a ); - } - - void main(){ - vec3 rayDir = normalize( vDirection ); - vec2 bounds = hitBox( vOrigin, rayDir ); - - if ( bounds.x > bounds.y ) discard; - - bounds.x = max( bounds.x, 0.0 ); - - float stepSize = ( bounds.y - bounds.x ) / steps; - - // Jitter - - // Nice little seed from - // https://blog.demofox.org/2020/05/25/casual-shadertoy-path-tracing-1-basic-camera-diffuse-emissive/ - uint seed = uint( gl_FragCoord.x ) * uint( 1973 ) + uint( gl_FragCoord.y ) * uint( 9277 ) + uint( frame ) * uint( 26699 ); - vec3 size = vec3( textureSize( map, 0 ) ); - float randNum = randomFloat( seed ) * 2.0 - 1.0; - vec3 p = vOrigin + bounds.x * rayDir; - p += rayDir * randNum * ( 1.0 / size ); - - // - - vec4 ac = vec4( base, 0.0 ); - - for ( float i = 0.0; i < steps; i += 1.0 ) { - - float t = bounds.x + i * stepSize; - - float d = sample1( p + 0.5 ); - - d = smoothstep( threshold - range, threshold + range, d ) * opacity; - - float col = shading( p + 0.5 ) * 3.0 + ( ( p.x + p.y ) * 0.25 ) + 0.2; - - ac.rgb += ( 1.0 - ac.a ) * d * col; - - ac.a += ( 1.0 - ac.a ) * d; - - if ( ac.a >= 0.95 ) break; - - p += rayDir * stepSize; - - } - - color = linearToSRGB( ac ); - - if ( color.a == 0.0 ) discard; - - } - `; - - const geometry = new THREE.BoxGeometry(1, 1, 1); - const material = new THREE.RawShaderMaterial({ - glslVersion: THREE.GLSL3, - uniforms: { - base: { value: new THREE.Color(0x798aa0) }, - map: { value: texture }, - cameraPos: { value: new THREE.Vector3() }, - threshold: { value: 0.25 }, - opacity: { value: 0.25 }, - range: { value: 0.1 }, - steps: { value: 100 }, - frame: { value: 0 }, - }, - vertexShader, - fragmentShader, - side: THREE.BackSide, - transparent: true, - }); - - mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - // - - const parameters = { - threshold: 0.25, - opacity: 0.25, - range: 0.1, - steps: 100, - }; - - function update() { - material.uniforms.threshold.value = parameters.threshold; - material.uniforms.opacity.value = parameters.opacity; - material.uniforms.range.value = parameters.range; - material.uniforms.steps.value = parameters.steps; - } - - const gui = new GUI(); - gui.add(parameters, 'threshold', 0, 1, 0.01).onChange(update); - gui.add(parameters, 'opacity', 0, 1, 0.01).onChange(update); - gui.add(parameters, 'range', 0, 1, 0.01).onChange(update); - gui.add(parameters, 'steps', 0, 200, 1).onChange(update); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - mesh.material.uniforms.cameraPos.value.copy(camera.position); - mesh.rotation.y = -performance.now() / 7500; - - mesh.material.uniforms.frame.value++; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_volume_instancing.ts b/examples-testing/examples/webgl_volume_instancing.ts deleted file mode 100644 index 7045732d6..000000000 --- a/examples-testing/examples/webgl_volume_instancing.ts +++ /dev/null @@ -1,222 +0,0 @@ -import * as THREE from 'three'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { VOXLoader, buildData3DTexture } from 'three/addons/loaders/VOXLoader.js'; - -let renderer, scene, camera, controls, timer; - -init(); - -function init() { - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000); - camera.position.set(0, 0, 4); - - controls = new OrbitControls(camera, renderer.domElement); - controls.autoRotate = true; - controls.autoRotateSpeed = -1.0; - controls.enableDamping = true; - - timer = new THREE.Timer(); - timer.connect(document); - - // Material - - const vertexShader = /* glsl */ ` - in vec3 position; - in mat4 instanceMatrix; - - uniform mat4 modelMatrix; - uniform mat4 modelViewMatrix; - uniform mat4 projectionMatrix; - uniform vec3 cameraPos; - - out vec4 vScreenPosition; - out mat4 vInstanceToViewMatrix; - - void main() { - vec4 mvPosition = modelViewMatrix * instanceMatrix * vec4( position, 1.0 ); - - gl_Position = projectionMatrix * mvPosition; - vScreenPosition = vec4( gl_Position.xy, 0.0, gl_Position.w ); - vInstanceToViewMatrix = modelViewMatrix * instanceMatrix; - } - `; - - const fragmentShader = /* glsl */ ` - precision highp float; - precision highp sampler3D; - - uniform mat4 viewMatrix; - uniform mat4 modelViewMatrix; - uniform mat4 projectionMatrix; - - in vec4 vScreenPosition; - in mat4 vInstanceToViewMatrix; - - out vec4 color; - - uniform sampler3D map; - - vec2 hitBox( vec3 orig, vec3 dir ) { - const vec3 box_min = vec3( - 0.5 ); - const vec3 box_max = vec3( 0.5 ); - vec3 inv_dir = 1.0 / dir; - vec3 tmin_tmp = ( box_min - orig ) * inv_dir; - vec3 tmax_tmp = ( box_max - orig ) * inv_dir; - vec3 tmin = min( tmin_tmp, tmax_tmp ); - vec3 tmax = max( tmin_tmp, tmax_tmp ); - float t0 = max( tmin.x, max( tmin.y, tmin.z ) ); - float t1 = min( tmax.x, min( tmax.y, tmax.z ) ); - return vec2( t0, t1 ); - } - - float sample1( vec3 p ) { - return texture( map, p ).r; - } - - #define epsilon .0001 - - vec3 normal( vec3 coord ) { - if ( coord.x < epsilon ) return vec3( 1.0, 0.0, 0.0 ); - if ( coord.y < epsilon ) return vec3( 0.0, 1.0, 0.0 ); - if ( coord.z < epsilon ) return vec3( 0.0, 0.0, 1.0 ); - if ( coord.x > 1.0 - epsilon ) return vec3( - 1.0, 0.0, 0.0 ); - if ( coord.y > 1.0 - epsilon ) return vec3( 0.0, - 1.0, 0.0 ); - if ( coord.z > 1.0 - epsilon ) return vec3( 0.0, 0.0, - 1.0 ); - - float step = 0.01; - float x = sample1( coord + vec3( - step, 0.0, 0.0 ) ) - sample1( coord + vec3( step, 0.0, 0.0 ) ); - float y = sample1( coord + vec3( 0.0, - step, 0.0 ) ) - sample1( coord + vec3( 0.0, step, 0.0 ) ); - float z = sample1( coord + vec3( 0.0, 0.0, - step ) ) - sample1( coord + vec3( 0.0, 0.0, step ) ); - - return normalize( vec3( x, y, z ) ); - } - - void main() { - - // perform w divide in the fragment shader to avoid interpolation artifacts - vec2 screenUv = vScreenPosition.xy / vScreenPosition.w; - mat4 invProjectionMatrix = inverse( projectionMatrix ); - mat4 invInstanceToViewMatrix = inverse( vInstanceToViewMatrix ); - - // get camera ray - vec4 temp; - vec3 camRayOrigin, camRayEnd; - temp = invProjectionMatrix * vec4( screenUv, - 1.0, 1.0 ); - camRayOrigin = temp.xyz / temp.w; - - temp = invProjectionMatrix * vec4( screenUv, 1.0, 1.0 ); - camRayEnd = temp.xyz / temp.w; - - // get local ray - vec3 instRayOrigin, instRayDirection, instRayEnd; - instRayOrigin = ( invInstanceToViewMatrix * vec4( camRayOrigin, 1.0 ) ).xyz; - instRayEnd = ( invInstanceToViewMatrix * vec4( camRayEnd, 1.0 ) ).xyz; - instRayDirection = normalize( instRayEnd - instRayOrigin ); - - // calculate the start of the ray at the box edge - vec2 bounds = hitBox( instRayOrigin, instRayDirection ); - - if ( bounds.x > bounds.y ) discard; - - bounds.x = max( bounds.x, 0.0 ); - - float stepSize = ( bounds.y - bounds.x ) / 100.0; - - vec3 p; - - // march through the volume - for ( float i = 0.0; i < 100.0; i += 1.0 ) { - - float t = bounds.x + i * stepSize; - p = instRayOrigin + t * instRayDirection; - float d = sample1( p + 0.5 ); - - if ( d > 0.5 ) { - - color.rgb = p * 2.0; - color.a = 1.; - break; - - } - - } - - if ( color.a == 0.0 ) discard; - - // calculate the final point in the ndc coords - vec4 ndc = projectionMatrix * vInstanceToViewMatrix * vec4( p, 1.0 ); - ndc /= ndc.w; - - // map the ndc coordinate to depth - // https://stackoverflow.com/questions/10264949/glsl-gl-fragcoord-z-calculation-and-setting-gl-fragdepth - float far = gl_DepthRange.far; - float near = gl_DepthRange.near; - gl_FragDepth = ( ( ( far - near ) * ndc.z ) + near + far ) / 2.0; - - } - `; - - const loader = new VOXLoader(); - loader.load('models/vox/menger.vox', function (chunks) { - for (let i = 0; i < chunks.length; i++) { - const chunk = chunks[i]; - - const geometry = new THREE.BoxGeometry(1, 1, 1); - const material = new THREE.RawShaderMaterial({ - glslVersion: THREE.GLSL3, - uniforms: { - map: { value: buildData3DTexture(chunk) }, - cameraPos: { value: new THREE.Vector3() }, - }, - vertexShader, - fragmentShader, - side: THREE.BackSide, - }); - - const mesh = new THREE.InstancedMesh(geometry, material, 50000); - mesh.onBeforeRender = function () { - this.material.uniforms.cameraPos.value.copy(camera.position); - }; - - const transform = new THREE.Object3D(); - - for (let i = 0; i < mesh.count; i++) { - transform.position.random().subScalar(0.5).multiplyScalar(150); - transform.rotation.x = Math.random() * Math.PI; - transform.rotation.y = Math.random() * Math.PI; - transform.rotation.z = Math.random() * Math.PI; - transform.updateMatrix(); - - mesh.setMatrixAt(i, transform.matrix); - } - - scene.add(mesh); - } - }); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - const delta = timer.getDelta(); - controls.update(delta); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_volume_perlin.ts b/examples-testing/examples/webgl_volume_perlin.ts deleted file mode 100644 index 0f299f66f..000000000 --- a/examples-testing/examples/webgl_volume_perlin.ts +++ /dev/null @@ -1,205 +0,0 @@ -import * as THREE from 'three'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { ImprovedNoise } from 'three/addons/math/ImprovedNoise.js'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let renderer, scene, camera; -let mesh; - -init(); - -function init() { - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(0, 0, 2); - - new OrbitControls(camera, renderer.domElement); - - // Texture - - const size = 128; - const data = new Uint8Array(size * size * size); - - let i = 0; - const perlin = new ImprovedNoise(); - const vector = new THREE.Vector3(); - - for (let z = 0; z < size; z++) { - for (let y = 0; y < size; y++) { - for (let x = 0; x < size; x++) { - vector.set(x, y, z).divideScalar(size); - - const d = perlin.noise(vector.x * 6.5, vector.y * 6.5, vector.z * 6.5); - - data[i++] = d * 128 + 128; - } - } - } - - const texture = new THREE.Data3DTexture(data, size, size, size); - texture.format = THREE.RedFormat; - texture.minFilter = THREE.LinearFilter; - texture.magFilter = THREE.LinearFilter; - texture.unpackAlignment = 1; - texture.needsUpdate = true; - - // Material - - const vertexShader = /* glsl */ ` - in vec3 position; - - uniform mat4 modelMatrix; - uniform mat4 modelViewMatrix; - uniform mat4 projectionMatrix; - uniform vec3 cameraPos; - - out vec3 vOrigin; - out vec3 vDirection; - - void main() { - vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); - - vOrigin = vec3( inverse( modelMatrix ) * vec4( cameraPos, 1.0 ) ).xyz; - vDirection = position - vOrigin; - - gl_Position = projectionMatrix * mvPosition; - } - `; - - const fragmentShader = /* glsl */ ` - precision highp float; - precision highp sampler3D; - - uniform mat4 modelViewMatrix; - uniform mat4 projectionMatrix; - - in vec3 vOrigin; - in vec3 vDirection; - - out vec4 color; - - uniform sampler3D map; - - uniform float threshold; - uniform float steps; - - vec2 hitBox( vec3 orig, vec3 dir ) { - const vec3 box_min = vec3( - 0.5 ); - const vec3 box_max = vec3( 0.5 ); - vec3 inv_dir = 1.0 / dir; - vec3 tmin_tmp = ( box_min - orig ) * inv_dir; - vec3 tmax_tmp = ( box_max - orig ) * inv_dir; - vec3 tmin = min( tmin_tmp, tmax_tmp ); - vec3 tmax = max( tmin_tmp, tmax_tmp ); - float t0 = max( tmin.x, max( tmin.y, tmin.z ) ); - float t1 = min( tmax.x, min( tmax.y, tmax.z ) ); - return vec2( t0, t1 ); - } - - float sample1( vec3 p ) { - return texture( map, p ).r; - } - - #define epsilon .0001 - - vec3 normal( vec3 coord ) { - if ( coord.x < epsilon ) return vec3( 1.0, 0.0, 0.0 ); - if ( coord.y < epsilon ) return vec3( 0.0, 1.0, 0.0 ); - if ( coord.z < epsilon ) return vec3( 0.0, 0.0, 1.0 ); - if ( coord.x > 1.0 - epsilon ) return vec3( - 1.0, 0.0, 0.0 ); - if ( coord.y > 1.0 - epsilon ) return vec3( 0.0, - 1.0, 0.0 ); - if ( coord.z > 1.0 - epsilon ) return vec3( 0.0, 0.0, - 1.0 ); - - float step = 0.01; - float x = sample1( coord + vec3( - step, 0.0, 0.0 ) ) - sample1( coord + vec3( step, 0.0, 0.0 ) ); - float y = sample1( coord + vec3( 0.0, - step, 0.0 ) ) - sample1( coord + vec3( 0.0, step, 0.0 ) ); - float z = sample1( coord + vec3( 0.0, 0.0, - step ) ) - sample1( coord + vec3( 0.0, 0.0, step ) ); - - return normalize( vec3( x, y, z ) ); - } - - void main(){ - - vec3 rayDir = normalize( vDirection ); - vec2 bounds = hitBox( vOrigin, rayDir ); - - if ( bounds.x > bounds.y ) discard; - - bounds.x = max( bounds.x, 0.0 ); - - float stepSize = ( bounds.y - bounds.x ) / steps; - - for ( float i = 0.0; i < steps; i += 1.0 ) { - - float t = bounds.x + i * stepSize; - vec3 p = vOrigin + t * rayDir; - float d = sample1( p + 0.5 ); - - if ( d > threshold ) { - - color.rgb = normal( p + 0.5 ) * 0.5 + ( p * 1.5 + 0.25 ); - color.a = 1.; - break; - - } - - } - - if ( color.a == 0.0 ) discard; - - } - `; - - const geometry = new THREE.BoxGeometry(1, 1, 1); - const material = new THREE.RawShaderMaterial({ - glslVersion: THREE.GLSL3, - uniforms: { - map: { value: texture }, - cameraPos: { value: new THREE.Vector3() }, - threshold: { value: 0.6 }, - steps: { value: 200 }, - }, - vertexShader, - fragmentShader, - side: THREE.BackSide, - }); - - mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - // - - const parameters = { threshold: 0.6, steps: 200 }; - - function update() { - material.uniforms.threshold.value = parameters.threshold; - material.uniforms.steps.value = parameters.steps; - } - - const gui = new GUI(); - gui.add(parameters, 'threshold', 0, 1, 0.01).onChange(update); - gui.add(parameters, 'steps', 0, 300, 1).onChange(update); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - mesh.material.uniforms.cameraPos.value.copy(camera.position); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgl_watch.ts b/examples-testing/examples/webgl_watch.ts deleted file mode 100644 index 8c7b7ecb6..000000000 --- a/examples-testing/examples/webgl_watch.ts +++ /dev/null @@ -1,243 +0,0 @@ -import * as THREE from 'three'; -import * as TWEEN from 'tween'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; -import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js'; -import { TAARenderPass } from 'three/addons/postprocessing/TAARenderPass.js'; - -let camera, scene, renderer; -let gui, dirLight, pointLight, controls, bloomPass, taaPass; -let ready = false; - -const meshes = {}; -const materials = {}; -const torad = Math.PI / 180; - -const setting = { - roughness: 0.1, - metalness: 1.0, - opacity: 0.8, - threshold: 0, - strength: 0.007, - radius: 0.0, - postProcess: false, -}; - -init(); - -function init() { - const container = document.getElementById('container'); - - camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight, 0.1, 20); - camera.position.set(0.8, 0.5, -1.5); - - scene = new THREE.Scene(); - - renderer = new THREE.WebGLRenderer({ antialias: true, outputBufferType: THREE.HalfFloatType }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.toneMapping = THREE.NeutralToneMapping; - renderer.toneMappingExposure = 0.7; - renderer.shadowMap.enabled = true; - container.appendChild(renderer.domElement); - - taaPass = new TAARenderPass(scene, camera); - taaPass.sampleLevel = 2; - - bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85); - bloomPass.threshold = setting.threshold; - bloomPass.strength = setting.strength; - bloomPass.radius = setting.radius; - - new HDRLoader().setPath('textures/equirectangular/').load('lobe.hdr', function (texture) { - texture.mapping = THREE.EquirectangularReflectionMapping; - scene.background = texture; - scene.environment = texture; - scene.backgroundBlurriness = 0.5; - scene.backgroundIntensity = 1.0; - scene.environmentIntensity = 1.5; - - // model - - const loader = new GLTFLoader().setPath('models/gltf/'); - loader.setDRACOLoader(new DRACOLoader().setDecoderPath('jsm/libs/draco/gltf/')); - loader.load('rolex.glb', function (gltf) { - gltf.scene.rotation.x = Math.PI * 0.25; - - gltf.scene.traverse(child => { - if (child.isMesh || child.isGroup) { - if (child.isMesh) { - child.material.vertexColors = false; - if (materials[child.material.name]) child.material = materials[child.material.name]; - else materials[child.material.name] = child.material; - if (child.name !== 'glass' && child.name !== 'floor') { - child.receiveShadow = true; - child.castShadow = true; - } - } - - meshes[child.name] = child; - } - }); - - scene.add(gltf.scene); - - meshes.glass.material = new THREE.MeshPhysicalMaterial({ - color: 0x020205, - transparent: true, - opacity: setting.opacity, - metalness: 0, - roughness: 0, - iridescence: 0.3, - clearcoat: 1.0, - blending: THREE.AdditiveBlending, - }); - - ready = true; - - createGUI(); - }); - }); - - controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 0.3; - controls.maxDistance = 10; - controls.target.set(0, -0.1, 0); - controls.enableDamping = true; - controls.dampingFactor = 0.05; - controls.update(); - - dirLight = new THREE.DirectionalLight(0xffffff, 6); - dirLight.position.set(0.2, 0.6, 0.4); - dirLight.castShadow = true; - scene.add(dirLight); - const shadow = dirLight.shadow; - shadow.mapSize.width = shadow.mapSize.height = 2048; - shadow.radius = 8; - shadow.bias = -0.0005; - const shadowCam = shadow.camera, - s = 0.5; - shadowCam.near = 0.1; - shadowCam.far = 2; - shadowCam.right = shadowCam.top = s; - shadowCam.left = shadowCam.bottom = -s; - // debug shadow - //scene.add( new THREE.CameraHelper(shadowCam) ); - - pointLight = new THREE.PointLight(0x7b8cad, 1, 0, 2); - pointLight.position.set(-0.3, -0.2, -0.2); - scene.add(pointLight); - - window.addEventListener('resize', onWindowResize); - - moveCamera(); -} - -function moveCamera() { - controls.enabled = false; - controls.enableDamping = false; - - const sph = new THREE.Spherical(); - const target = controls.target; - const tmp = { - distance: controls.getDistance(), - phi: controls.getPolarAngle(), - theta: controls.getAzimuthalAngle(), - }; - - new TWEEN.Tween(tmp) - .to({ distance: 1, theta: -Math.PI * 0.2 }, 6000) - .easing(TWEEN.Easing.Quadratic.Out) - .onUpdate(function (n) { - sph.set(n.distance, n.phi, n.theta); - camera.position.setFromSpherical(sph).add(target); - camera.lookAt(target); - }) - .onComplete(function () { - controls.enabled = true; - controls.enableDamping = true; - }) - .start(); -} - -function postProcess(b) { - if (b) { - renderer.setEffects([taaPass, bloomPass]); - } else { - renderer.setEffects(null); - } -} - -function createGUI() { - gui = new GUI(); - gui.add(setting, 'roughness', 0, 1, 0.01).onChange(upMaterial); - gui.add(setting, 'metalness', 0, 1, 0.01).onChange(upMaterial); - gui.add(setting, 'opacity', 0, 1, 0.01).onChange(upMaterial); - - // - - gui.add(setting, 'postProcess').onChange(postProcess); - gui.add(setting, 'threshold', 0, 1, 0.01).onChange(upBloom); - gui.add(setting, 'strength', 0, 0.1, 0.001).onChange(upBloom); - gui.add(setting, 'radius', 0, 1, 0.01).onChange(upBloom); -} - -function upMaterial() { - materials.Gold.metalness = materials.Silver.metalness = setting.metalness; - materials.Gold.roughness = materials.Silver.roughness = setting.roughness; - meshes.glass.material.opacity = setting.opacity; -} - -function upBloom() { - if (!bloomPass) return; - bloomPass.threshold = setting.threshold; - bloomPass.strength = setting.strength; - bloomPass.radius = setting.radius; -} - -function getTime() { - const currentDate = new Date(); - let hour = currentDate.getHours(); - const minute = currentDate.getMinutes(); - const second = currentDate.getSeconds(); - let day = currentDate.getDay(); - const month = currentDate.getMonth(); - const milli = currentDate.getMilliseconds(); - if (hour >= 12) hour -= 12; - if (day > 30) day = 30; - - meshes.hour.rotation.y = -hour * 30 * torad; - meshes.minute.rotation.y = -minute * 6 * torad; - meshes.second.rotation.y = -second * 6 * torad; - meshes.mini_03.rotation.y = -day * 12 * torad; - meshes.mini_02.rotation.y = -month * 30 * torad; - meshes.mini_01.rotation.y = -milli * 0.36 * torad; -} - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - renderer.setSize(width, height); -} - -// - -function animate() { - controls.update(); - - TWEEN.update(); - - renderer.render(scene, camera); - - if (ready) getTime(); -} diff --git a/examples-testing/examples/webgpu_animation_retargeting.ts b/examples-testing/examples/webgpu_animation_retargeting.ts deleted file mode 100644 index 23ebb2af4..000000000 --- a/examples-testing/examples/webgpu_animation_retargeting.ts +++ /dev/null @@ -1,295 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { - color, - screenUV, - hue, - reflector, - time, - Fn, - vec2, - length, - atan, - float, - sin, - cos, - vec3, - sub, - mul, - pow, - blendDodge, - normalWorldGeometry, -} from 'three/tsl'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import * as SkeletonUtils from 'three/addons/utils/SkeletonUtils.js'; - -const [sourceModel, targetModel] = await Promise.all([ - new Promise((resolve, reject) => { - new GLTFLoader().load('./models/gltf/Michelle.glb', resolve, undefined, reject); - }), - - new Promise((resolve, reject) => { - new GLTFLoader().load('./models/gltf/Soldier.glb', resolve, undefined, reject); - }), -]); - -// - -const timer = new THREE.Timer(); -timer.connect(document); - -export const lightSpeed = /*#__PURE__*/ Fn(([suv_immutable]) => { - // forked from https://www.shadertoy.com/view/7ly3D1 - - const suv = vec2(suv_immutable); - const uv = vec2(length(suv), atan(suv.y, suv.x)); - const offset = float( - float(0.1) - .mul(sin(uv.y.mul(10).sub(time.mul(0.6)))) - .mul(cos(uv.y.mul(48).add(time.mul(0.3)))) - .mul(cos(uv.y.mul(3.7).add(time))), - ); - const rays = vec3( - vec3(sin(uv.y.mul(150).add(time)).mul(0.5).add(0.5)) - .mul( - vec3( - sin(uv.y.mul(80).sub(time.mul(0.6))) - .mul(0.5) - .add(0.5), - ), - ) - .mul( - vec3( - sin(uv.y.mul(45).add(time.mul(0.8))) - .mul(0.5) - .add(0.5), - ), - ) - .mul(vec3(sub(1, cos(uv.y.add(mul(22, time).sub(pow(uv.x.add(offset), 0.3).mul(60))))))) - .mul(vec3(uv.x.mul(2))), - ); - - return rays; -}).setLayout({ - name: 'lightSpeed', - type: 'vec3', - inputs: [{ name: 'suv', type: 'vec2' }], -}); - -// scene - -const scene = new THREE.Scene(); - -// background - -const coloredVignette = screenUV - .distance(0.5) - .mix(hue(color(0x0175ad), time.mul(0.1)), hue(color(0x02274f), time.mul(0.5))); -const lightSpeedEffect = lightSpeed(normalWorldGeometry).clamp(); -const lightSpeedSky = normalWorldGeometry.y.remapClamp(-0.1, 1).mix(0, lightSpeedEffect); -const composedBackground = blendDodge(coloredVignette, lightSpeedSky); - -scene.backgroundNode = composedBackground; - -// - -const helpers = new THREE.Group(); -helpers.visible = false; -scene.add(helpers); - -const light = new THREE.HemisphereLight(0xe9c0a5, 0x0175ad, 5); -scene.add(light); - -const dirLight = new THREE.DirectionalLight(0xfff9ea, 4); -dirLight.position.set(2, 5, 2); -scene.add(dirLight); - -const camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.25, 50); -camera.position.set(0, 1, 4); - -// add models to scene -scene.add(sourceModel.scene); -scene.add(targetModel.scene); - -// reposition models -sourceModel.scene.position.x -= 0.8; -targetModel.scene.position.x += 0.7; - -targetModel.scene.position.z -= 0.1; - -// reajust model -targetModel.scene.scale.setScalar(0.01); - -// flip model -sourceModel.scene.rotation.y = Math.PI / 2; -targetModel.scene.rotation.y = -Math.PI / 2; - -// retarget -const source = getSource(sourceModel); -const mixer = retargetModel(source, targetModel); - -// floor -const reflection = reflector(); -reflection.target.rotateX(-Math.PI / 2); -scene.add(reflection.target); - -const floorMaterial = new THREE.NodeMaterial(); -floorMaterial.colorNode = reflection; -floorMaterial.opacity = 0.2; -floorMaterial.transparent = true; - -const floor = new THREE.Mesh(new THREE.BoxGeometry(50, 0.001, 50), floorMaterial); -floor.receiveShadow = true; - -floor.position.set(0, 0, 0); -scene.add(floor); - -// renderer -const renderer = new THREE.WebGPURenderer({ antialias: true }); -renderer.setPixelRatio(window.devicePixelRatio); -renderer.setSize(window.innerWidth, window.innerHeight); -renderer.setAnimationLoop(animate); -renderer.toneMapping = THREE.NeutralToneMapping; -renderer.inspector = new Inspector(); -document.body.appendChild(renderer.domElement); - -const controls = new OrbitControls(camera, renderer.domElement); -controls.minDistance = 3; -controls.maxDistance = 12; -controls.target.set(0, 1, 0); -controls.maxPolarAngle = Math.PI / 2; - -const gui = renderer.inspector.createParameters('Scene settings'); -gui.add(helpers, 'visible').name('show helpers'); - -// - -function getSource(sourceModel) { - const clip = sourceModel.animations[0]; - - const helper = new THREE.SkeletonHelper(sourceModel.scene); - helpers.add(helper); - - const skeleton = new THREE.Skeleton(helper.bones); - - const mixer = new THREE.AnimationMixer(sourceModel.scene); - mixer.clipAction(sourceModel.animations[0]).play(); - - return { clip, skeleton, mixer }; -} - -function retargetModel(sourceModel, targetModel) { - const targetSkin = targetModel.scene.children[0].children[0]; - - const targetSkelHelper = new THREE.SkeletonHelper(targetModel.scene); - helpers.add(targetSkelHelper); - - const rotateCW45 = new THREE.Matrix4().makeRotationY(THREE.MathUtils.degToRad(45)); - const rotateCCW180 = new THREE.Matrix4().makeRotationY(THREE.MathUtils.degToRad(-180)); - const rotateCW180 = new THREE.Matrix4().makeRotationY(THREE.MathUtils.degToRad(180)); - const rotateFoot = new THREE.Matrix4().makeRotationFromEuler( - new THREE.Euler(THREE.MathUtils.degToRad(45), THREE.MathUtils.degToRad(180), THREE.MathUtils.degToRad(0)), - ); - - const retargetOptions = { - // specify the name of the source's hip bone. - hip: 'mixamorigHips', - - // specify the influence of the source's hip bone. - // use ( 0, 1, 0 ) to ignore xz hip movement. - //hipInfluence: new THREE.Vector3( 0, 1, 0 ), - - // specify an animation range in seconds. - //trim: [ 3.0, 4.0 ], - - // preserve the scale of the target model - scale: 1 / targetModel.scene.scale.y, - - // offset target bones -> { targetBoneName: offsetMatrix } - localOffsets: { - mixamorigLeftShoulder: rotateCW45, - mixamorigRightShoulder: rotateCCW180, - mixamorigLeftArm: rotateCW45, - mixamorigRightArm: rotateCCW180, - mixamorigLeftForeArm: rotateCW45, - mixamorigRightForeArm: rotateCCW180, - mixamorigLeftHand: rotateCW45, - mixamorigRightHand: rotateCCW180, - - mixamorigLeftUpLeg: rotateCW180, - mixamorigRightUpLeg: rotateCW180, - mixamorigLeftLeg: rotateCW180, - mixamorigRightLeg: rotateCW180, - mixamorigLeftFoot: rotateFoot, - mixamorigRightFoot: rotateFoot, - mixamorigLeftToeBase: rotateCW180, - mixamorigRightToeBase: rotateCW180, - }, - - // Map of target's bone names to source's bone names -> { targetBoneName: sourceBoneName } - names: { - mixamorigHips: 'mixamorigHips', - - mixamorigSpine: 'mixamorigSpine', - mixamorigSpine2: 'mixamorigSpine2', - mixamorigHead: 'mixamorigHead', - - mixamorigLeftShoulder: 'mixamorigLeftShoulder', - mixamorigRightShoulder: 'mixamorigRightShoulder', - mixamorigLeftArm: 'mixamorigLeftArm', - mixamorigRightArm: 'mixamorigRightArm', - mixamorigLeftForeArm: 'mixamorigLeftForeArm', - mixamorigRightForeArm: 'mixamorigRightForeArm', - mixamorigLeftHand: 'mixamorigLeftHand', - mixamorigRightHand: 'mixamorigRightHand', - - mixamorigLeftUpLeg: 'mixamorigLeftUpLeg', - mixamorigRightUpLeg: 'mixamorigRightUpLeg', - mixamorigLeftLeg: 'mixamorigLeftLeg', - mixamorigRightLeg: 'mixamorigRightLeg', - mixamorigLeftFoot: 'mixamorigLeftFoot', - mixamorigRightFoot: 'mixamorigRightFoot', - mixamorigLeftToeBase: 'mixamorigLeftToeBase', - mixamorigRightToeBase: 'mixamorigRightToeBase', - }, - }; - - const retargetedClip = SkeletonUtils.retargetClip( - targetSkin, - sourceModel.skeleton, - sourceModel.clip, - retargetOptions, - ); - - // Apply the mixer directly to the SkinnedMesh, not any - // ancestor node, because that's what - // SkeletonUtils.retargetClip outputs the clip to be - // compatible with. - const mixer = new THREE.AnimationMixer(targetSkin); - mixer.clipAction(retargetedClip).play(); - - return mixer; -} - -window.onresize = function () { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -}; - -function animate() { - timer.update(); - - const delta = timer.getDelta(); - - source.mixer.update(delta); - mixer.update(delta); - - controls.update(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_animation_retargeting_readyplayer.ts b/examples-testing/examples/webgpu_animation_retargeting_readyplayer.ts deleted file mode 100644 index de90d890f..000000000 --- a/examples-testing/examples/webgpu_animation_retargeting_readyplayer.ts +++ /dev/null @@ -1,165 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { screenUV, color, vec2, vec4, reflector, positionWorld } from 'three/tsl'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { FBXLoader } from 'three/addons/loaders/FBXLoader.js'; -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import * as SkeletonUtils from 'three/addons/utils/SkeletonUtils.js'; - -const [sourceModel, targetModel] = await Promise.all([ - new Promise((resolve, reject) => { - new FBXLoader().load('./models/fbx/mixamo.fbx', resolve, undefined, reject); - }), - - new Promise((resolve, reject) => { - new GLTFLoader().load('./models/gltf/readyplayer.me.glb', resolve, undefined, reject); - }), -]); - -// - -const timer = new THREE.Timer(); -timer.connect(document); - -// scene - -const scene = new THREE.Scene(); - -// background - -const horizontalEffect = screenUV.x.mix(color(0x13172b), color(0x311649)); -const lightEffect = screenUV.distance(vec2(0.5, 1.0)).oneMinus().mul(color(0x0c5d68)); - -scene.backgroundNode = horizontalEffect.add(lightEffect); - -// - -const light = new THREE.HemisphereLight(0x311649, 0x0c5d68, 10); -scene.add(light); - -const backLight = new THREE.DirectionalLight(0xffffff, 10); -backLight.position.set(0, 5, -5); -scene.add(backLight); - -const keyLight = new THREE.DirectionalLight(0xfff9ea, 4); -keyLight.position.set(3, 5, 3); -scene.add(keyLight); - -const camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.25, 50); -camera.position.set(0, 3, 5); - -// add models to scene -scene.add(sourceModel); -scene.add(targetModel.scene); - -// reposition models -sourceModel.position.x -= 0.9; -targetModel.scene.position.x += 0.9; - -// reajust model - mixamo use centimeters, readyplayer.me use meters (three.js scale is meters) -sourceModel.scale.setScalar(0.01); - -// retarget -const source = getSource(sourceModel); -const mixer = retargetModel(source, targetModel); - -// renderer -const renderer = new THREE.WebGPURenderer({ antialias: true }); -renderer.setPixelRatio(window.devicePixelRatio); -renderer.setSize(window.innerWidth, window.innerHeight); -renderer.setAnimationLoop(animate); -renderer.inspector = new Inspector(); -renderer.toneMapping = THREE.NeutralToneMapping; -document.body.appendChild(renderer.domElement); - -const controls = new OrbitControls(camera, renderer.domElement); -controls.minDistance = 3; -controls.maxDistance = 12; -controls.target.set(0, 1, 0); -controls.maxPolarAngle = Math.PI / 2; - -// floor -const reflection = reflector(); -reflection.target.rotateX(-Math.PI / 2); -scene.add(reflection.target); - -const reflectionMask = positionWorld.xz.distance(0).mul(0.1).clamp().oneMinus(); - -const floorMaterial = new THREE.NodeMaterial(); -floorMaterial.colorNode = vec4(reflection.rgb, reflectionMask); -floorMaterial.opacity = 0.2; -floorMaterial.transparent = true; - -const floor = new THREE.Mesh(new THREE.BoxGeometry(50, 0.001, 50), floorMaterial); -floor.receiveShadow = true; - -floor.position.set(0, 0, 0); -scene.add(floor); - -// - -function getSource(sourceModel) { - const clip = sourceModel.animations[0]; - - const helper = new THREE.SkeletonHelper(sourceModel); - const skeleton = new THREE.Skeleton(helper.bones); - - const mixer = new THREE.AnimationMixer(sourceModel); - mixer.clipAction(sourceModel.animations[0]).play(); - - return { clip, skeleton, mixer }; -} - -function retargetModel(sourceModel, targetModel) { - const targetSkin = targetModel.scene.children[0].children[1]; - - const retargetOptions = { - // specify the name of the source's hip bone. - hip: 'mixamorigHips', - - // preserve the scale of the target model - scale: 0.01, - - // use ( 0, 1, 0 ) to ignore xz hip movement. - //hipInfluence: new THREE.Vector3( 0, 1, 0 ), - - // Map of target's bone names to source's bone names -> { targetBoneName: sourceBoneName } - getBoneName: function (bone) { - return 'mixamorig' + bone.name; - }, - }; - - const retargetedClip = SkeletonUtils.retargetClip( - targetSkin, - sourceModel.skeleton, - sourceModel.clip, - retargetOptions, - ); - - const mixer = new THREE.AnimationMixer(targetSkin); - mixer.clipAction(retargetedClip).play(); - - return mixer; -} - -window.onresize = function () { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -}; - -function animate() { - timer.update(); - - const delta = timer.getDelta(); - - source.mixer.update(delta); - mixer.update(delta); - - controls.update(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_backdrop.ts b/examples-testing/examples/webgpu_backdrop.ts deleted file mode 100644 index af6dcc399..000000000 --- a/examples-testing/examples/webgpu_backdrop.ts +++ /dev/null @@ -1,134 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { - float, - vec3, - color, - viewportSharedTexture, - hue, - blendOverlay, - posterize, - grayscale, - saturation, - viewportSafeUV, - screenUV, - checker, - uv, - time, - oscSine, - output, -} from 'three/tsl'; - -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let camera, scene, renderer; -let portals, - rotate = true; -let mixer, timer; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.01, 100); - camera.position.set(1, 2, 3); - - scene = new THREE.Scene(); - scene.backgroundNode = screenUV.y.mix(color(0x66bbff), color(0x4466ff)); - camera.lookAt(0, 1, 0); - - timer = new THREE.Timer(); - timer.connect(document); - - // lights - - const light = new THREE.SpotLight(0xffffff, 1); - light.power = 2000; - camera.add(light); - scene.add(camera); - - const loader = new GLTFLoader(); - loader.load('models/gltf/Michelle.glb', function (gltf) { - const object = gltf.scene; - mixer = new THREE.AnimationMixer(object); - - const material = object.children[0].children[0].material; - material.outputNode = oscSine(time.mul(0.1)).mix(output, posterize(output.add(0.1), 4).mul(2)); - - const action = mixer.clipAction(gltf.animations[0]); - action.play(); - - scene.add(object); - }); - - // portals - - const geometry = new THREE.SphereGeometry(0.3, 32, 16); - - portals = new THREE.Group(); - scene.add(portals); - - function addBackdropSphere(backdropNode, backdropAlphaNode = null) { - const distance = 1; - const id = portals.children.length; - const rotation = THREE.MathUtils.degToRad(id * 45); - - const material = new THREE.MeshStandardNodeMaterial({ color: 0x0066ff }); - material.roughnessNode = float(0.2); - material.metalnessNode = float(0); - material.backdropNode = backdropNode; - material.backdropAlphaNode = backdropAlphaNode; - material.transparent = true; - - const mesh = new THREE.Mesh(geometry, material); - mesh.position.set(Math.cos(rotation) * distance, 1, Math.sin(rotation) * distance); - - portals.add(mesh); - } - - addBackdropSphere(hue(viewportSharedTexture().bgr, oscSine().mul(Math.PI))); - addBackdropSphere(viewportSharedTexture().rgb.oneMinus()); - addBackdropSphere(grayscale(viewportSharedTexture().rgb)); - addBackdropSphere(saturation(viewportSharedTexture().rgb, 10), oscSine()); - addBackdropSphere(blendOverlay(viewportSharedTexture().rgb, checker(uv().mul(10)))); - addBackdropSphere(viewportSharedTexture(viewportSafeUV(screenUV.mul(40).floor().div(40)))); - addBackdropSphere(viewportSharedTexture(viewportSafeUV(screenUV.mul(80).floor().div(80))).add(color(0x0033ff))); - addBackdropSphere(vec3(0, 0, viewportSharedTexture().b)); - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: false }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.toneMapping = THREE.NeutralToneMapping; - renderer.toneMappingExposure = 0.3; - document.body.appendChild(renderer.domElement); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, 1, 0); - controls.addEventListener('start', () => (rotate = false)); - controls.addEventListener('end', () => (rotate = true)); - controls.update(); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - const delta = timer.getDelta(); - - if (mixer) mixer.update(delta); - - if (rotate) portals.rotation.y += delta * 0.5; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_backdrop_area.ts b/examples-testing/examples/webgpu_backdrop_area.ts deleted file mode 100644 index 384311dd4..000000000 --- a/examples-testing/examples/webgpu_backdrop_area.ts +++ /dev/null @@ -1,157 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { - color, - positionWorld, - linearDepth, - viewportLinearDepth, - viewportSharedTexture, - screenUV, - hue, - time, - checker, - uv, - modelScale, -} from 'three/tsl'; -import { hashBlur } from 'three/addons/tsl/display/hashBlur.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let camera, scene, renderer; -let mixer, timer; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.25, 25); - camera.position.set(3, 2, 3); - - scene = new THREE.Scene(); - scene.backgroundNode = hue(screenUV.y.mix(color(0x66bbff), color(0x4466ff)), time.mul(0.1)); - camera.lookAt(0, 1, 0); - - timer = new THREE.Timer(); - timer.connect(document); - - const ambient = new THREE.AmbientLight(0xffffff, 2.5); - scene.add(ambient); - - // model - - const loader = new GLTFLoader(); - loader.load('models/gltf/Michelle.glb', function (gltf) { - const object = gltf.scene; - mixer = new THREE.AnimationMixer(object); - - const action = mixer.clipAction(gltf.animations[0]); - action.play(); - - scene.add(object); - }); - - // volume - - // compare depth from viewportLinearDepth with linearDepth() to create a distance field - // viewportLinearDepth return the linear depth of the scene - // linearDepth() returns the linear depth of the mesh - const depthDistance = viewportLinearDepth.distance(linearDepth()); - - const depthAlphaNode = depthDistance.oneMinus().smoothstep(0.9, 2).mul(10).saturate(); - const depthBlurred = hashBlur(viewportSharedTexture(), depthDistance.smoothstep(0, 0.6).mul(40).clamp().mul(0.1)); - - const blurredBlur = new THREE.MeshBasicNodeMaterial(); - blurredBlur.backdropNode = depthBlurred.add(depthAlphaNode.mix(color(0x003399).mul(0.3), 0)); - blurredBlur.transparent = true; - blurredBlur.side = THREE.DoubleSide; - - const depthMaterial = new THREE.MeshBasicNodeMaterial(); - depthMaterial.backdropNode = depthAlphaNode; - depthMaterial.transparent = true; - depthMaterial.side = THREE.DoubleSide; - - const checkerMaterial = new THREE.MeshBasicNodeMaterial(); - checkerMaterial.backdropNode = hashBlur(viewportSharedTexture(), 0.05); - checkerMaterial.backdropAlphaNode = checker(uv().mul(3).mul(modelScale.xy)); - checkerMaterial.opacityNode = checkerMaterial.backdropAlphaNode; - checkerMaterial.transparent = true; - checkerMaterial.side = THREE.DoubleSide; - - const pixelMaterial = new THREE.MeshBasicNodeMaterial(); - pixelMaterial.backdropNode = viewportSharedTexture(screenUV.mul(100).floor().div(100)); - pixelMaterial.transparent = true; - - // box / floor - - const box = new THREE.Mesh(new THREE.BoxGeometry(2, 2, 2), blurredBlur); - box.position.set(0, 1, 0); - box.renderOrder = 1; - scene.add(box); - - const floor = new THREE.Mesh( - new THREE.BoxGeometry(5, 0.01, 5), - new THREE.MeshBasicNodeMaterial({ - color: 0xff6600, - opacityNode: positionWorld.xz.distance(0).oneMinus().clamp(), - transparent: true, - depthWrite: false, - }), - ); - floor.position.set(0, 0, 0); - scene.add(floor); - - // renderer - - renderer = new THREE.WebGPURenderer(/*{ antialias: true }*/); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.toneMapping = THREE.NeutralToneMapping; - renderer.toneMappingExposure = 0.9; - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, 1, 0); - controls.update(); - - window.addEventListener('resize', onWindowResize); - - // gui - - const materials = { - blurred: blurredBlur, - depth: depthMaterial, - checker: checkerMaterial, - pixel: pixelMaterial, - }; - - const options = { material: 'blurred' }; - box.material = materials[options.material]; - - const gui = renderer.inspector.createParameters('Scene settings'); - gui.add(box.scale, 'x', 0.1, 2, 0.01).name('box scale x'); - gui.add(box.scale, 'y', 0.1, 2, 0.01).name('box scale y'); - gui.add(options, 'material', Object.keys(materials)).onChange(name => { - box.material = materials[name]; - }); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - const delta = timer.getDelta(); - - if (mixer) mixer.update(delta); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_backdrop_water.ts b/examples-testing/examples/webgpu_backdrop_water.ts deleted file mode 100644 index a8538cf94..000000000 --- a/examples-testing/examples/webgpu_backdrop_water.ts +++ /dev/null @@ -1,247 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { - color, - vec2, - pass, - linearDepth, - normalWorld, - triplanarTexture, - texture, - objectPosition, - screenUV, - viewportLinearDepth, - viewportDepthTexture, - viewportSharedTexture, - mx_worley_noise_float, - positionWorld, - time, -} from 'three/tsl'; -import { gaussianBlur } from 'three/addons/tsl/display/GaussianBlurNode.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let camera, scene, renderer; -let mixer, objects, timer; -let model, floor, floorPosition; -let renderPipeline; -let controls; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.25, 30); - camera.position.set(3, 2, 4); - - scene = new THREE.Scene(); - scene.fog = new THREE.Fog(0x0487e2, 7, 25); - scene.backgroundNode = normalWorld.y.mix(color(0x0487e2), color(0x0066ff)); - camera.lookAt(0, 1, 0); - - const sunLight = new THREE.DirectionalLight(0xffe499, 5); - sunLight.position.set(0.5, 3, 0.5); - - const waterAmbientLight = new THREE.HemisphereLight(0x333366, 0x74ccf4, 5); - const skyAmbientLight = new THREE.HemisphereLight(0x74ccf4, 0, 1); - - scene.add(sunLight); - scene.add(skyAmbientLight); - scene.add(waterAmbientLight); - - timer = new THREE.Timer(); - timer.connect(document); - - // animated model - - const loader = new GLTFLoader(); - loader.load('models/gltf/Michelle.glb', function (gltf) { - model = gltf.scene; - - mixer = new THREE.AnimationMixer(model); - - const action = mixer.clipAction(gltf.animations[0]); - action.play(); - - scene.add(model); - }); - - // objects - - const textureLoader = new THREE.TextureLoader(); - const iceDiffuse = textureLoader.load('./textures/water.jpg'); - iceDiffuse.wrapS = THREE.RepeatWrapping; - iceDiffuse.wrapT = THREE.RepeatWrapping; - iceDiffuse.colorSpace = THREE.NoColorSpace; - - const iceColorNode = triplanarTexture(texture(iceDiffuse)).add(color(0x0066ff)).mul(0.8); - - const geometry = new THREE.IcosahedronGeometry(1, 3); - const material = new THREE.MeshStandardNodeMaterial({ colorNode: iceColorNode }); - - const count = 100; - const scale = 3.5; - const column = 10; - - objects = new THREE.Group(); - - for (let i = 0; i < count; i++) { - const x = i % column; - const y = i / column; - - const mesh = new THREE.Mesh(geometry, material); - mesh.position.set(x * scale, 0, y * scale); - mesh.rotation.set(Math.random(), Math.random(), Math.random()); - objects.add(mesh); - } - - objects.position.set((column - 1) * scale * -0.5, -1, (count / column) * scale * -0.5); - - scene.add(objects); - - // water - - const t = time.mul(0.8); - const floorUV = positionWorld.xzy; - - const waterLayer0 = mx_worley_noise_float(floorUV.mul(4).add(t)); - const waterLayer1 = mx_worley_noise_float(floorUV.mul(2).add(t)); - - const waterIntensity = waterLayer0.mul(waterLayer1); - const waterColor = waterIntensity.mul(1.4).mix(color(0x0487e2), color(0x74ccf4)); - - // linearDepth() returns the linear depth of the mesh - const depth = linearDepth(); - const depthWater = viewportLinearDepth.sub(depth).toInspector('Water / Depth', node => node.oneMinus()); - const depthEffect = depthWater.remapClamp(-0.002, 0.04); - - const refractionUV = screenUV.add(vec2(0, waterIntensity.mul(0.1))).toInspector('Water / Refraction UV'); - - // linearDepth( viewportDepthTexture( uv ) ) return the linear depth of the scene - const depthTestForRefraction = linearDepth(viewportDepthTexture(refractionUV)).sub(depth); - - const depthRefraction = depthTestForRefraction.remapClamp(0, 0.1); - - const finalUV = depthTestForRefraction.lessThan(0).select(screenUV, refractionUV); - - const viewportTexture = viewportSharedTexture(finalUV).toInspector('Water / Viewport Texture + Refraction UV'); - - const waterMaterial = new THREE.MeshBasicNodeMaterial(); - waterMaterial.colorNode = waterColor.toInspector('Water / Color'); - waterMaterial.backdropNode = depthEffect.mix( - viewportSharedTexture(), - viewportTexture.mul(depthRefraction.mix(1, waterColor)), - ); - waterMaterial.backdropAlphaNode = depthRefraction.oneMinus(); - waterMaterial.transparent = true; - - const water = new THREE.Mesh(new THREE.BoxGeometry(50, 0.001, 50), waterMaterial); - water.position.set(0, 0, 0); - scene.add(water); - - // floor - - floor = new THREE.Mesh( - new THREE.CylinderGeometry(1.1, 1.1, 10), - new THREE.MeshStandardNodeMaterial({ colorNode: iceColorNode }), - ); - floor.position.set(0, -5, 0); - scene.add(floor); - - // caustics - - const waterPosY = positionWorld.y.sub(water.position.y); - - let transition = waterPosY.add(0.1).saturate().oneMinus(); - transition = waterPosY.lessThan(0).select(transition, normalWorld.y.mix(transition, 0)).toVar(); - - const colorNode = transition.mix(material.colorNode, material.colorNode.add(waterLayer0)); - - //material.colorNode = colorNode; - floor.material.colorNode = colorNode; - - // renderer - - renderer = new THREE.WebGPURenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 1; - controls.maxDistance = 10; - controls.maxPolarAngle = Math.PI * 0.9; - controls.autoRotate = true; - controls.autoRotateSpeed = 1; - controls.target.set(0, 0.2, 0); - controls.update(); - - // gui - - const gui = renderer.inspector.createParameters('Settings'); - - floorPosition = new THREE.Vector3(0, 0.2, 0); - - gui.add(floorPosition, 'y', -1, 1, 0.001).name('floor position'); - - // post processing - - const scenePass = pass(scene, camera); - const scenePassColor = scenePass.getTextureNode(); - const scenePassDepth = scenePass.getLinearDepthNode().remapClamp(0.3, 0.5); - - const waterMask = objectPosition(camera) - .y.greaterThan(screenUV.y.sub(0.5).mul(camera.near)) - .toInspector('Post-Processing / Water Mask'); - - const scenePassColorBlurred = gaussianBlur(scenePassColor); - scenePassColorBlurred.directionNode = waterMask - .select(scenePassDepth, scenePass.getLinearDepthNode().mul(5)) - .toInspector('Post-Processing / Blur Strength [ Depth ]', node => node.toFloat()); - - const vignette = screenUV.distance(0.5).mul(1.35).clamp().oneMinus().toInspector('Post-Processing / Vignette'); - - renderPipeline = new THREE.RenderPipeline(renderer); - renderPipeline.outputNode = waterMask.select( - scenePassColorBlurred, - scenePassColorBlurred.mul(color(0x74ccf4)).mul(vignette), - ); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - controls.update(); - - const delta = timer.getDelta(); - - floor.position.y = floorPosition.y - 5; - - if (model) { - mixer.update(delta); - - model.position.y = floorPosition.y; - } - - for (const object of objects.children) { - object.position.y = Math.sin(timer.getElapsed() + object.id) * 0.3; - object.rotation.y += delta * 0.3; - } - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_camera.ts b/examples-testing/examples/webgpu_camera.ts deleted file mode 100644 index bf4724ac8..000000000 --- a/examples-testing/examples/webgpu_camera.ts +++ /dev/null @@ -1,211 +0,0 @@ -import * as THREE from 'three/webgpu'; - -let SCREEN_WIDTH = window.innerWidth; -let SCREEN_HEIGHT = window.innerHeight; -let aspect = SCREEN_WIDTH / SCREEN_HEIGHT; - -let container; -let camera, scene, renderer, mesh; -let cameraRig, activeCamera, activeHelper; -let cameraPerspective, cameraOrtho; -let cameraPerspectiveHelper, cameraOrthoHelper; -const frustumSize = 600; - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - scene = new THREE.Scene(); - - // - - camera = new THREE.PerspectiveCamera(50, 0.5 * aspect, 1, 10000); - camera.position.z = 2500; - - cameraPerspective = new THREE.PerspectiveCamera(50, 0.5 * aspect, 150, 1000); - - cameraPerspectiveHelper = new THREE.CameraHelper(cameraPerspective); - scene.add(cameraPerspectiveHelper); - - // - cameraOrtho = new THREE.OrthographicCamera( - (0.5 * frustumSize * aspect) / -2, - (0.5 * frustumSize * aspect) / 2, - frustumSize / 2, - frustumSize / -2, - 150, - 1000, - ); - - cameraOrthoHelper = new THREE.CameraHelper(cameraOrtho); - scene.add(cameraOrthoHelper); - - // - - activeCamera = cameraPerspective; - activeHelper = cameraPerspectiveHelper; - - // counteract different front orientation of cameras vs rig - - cameraOrtho.rotation.y = Math.PI; - cameraPerspective.rotation.y = Math.PI; - - cameraRig = new THREE.Group(); - - cameraRig.add(cameraPerspective); - cameraRig.add(cameraOrtho); - - scene.add(cameraRig); - - // - - mesh = new THREE.Mesh( - new THREE.SphereGeometry(100, 16, 8), - new THREE.MeshBasicMaterial({ color: 0xffffff, wireframe: true }), - ); - scene.add(mesh); - - const mesh2 = new THREE.Mesh( - new THREE.SphereGeometry(50, 16, 8), - new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true }), - ); - mesh2.position.y = 150; - mesh.add(mesh2); - - const mesh3 = new THREE.Mesh( - new THREE.SphereGeometry(5, 16, 8), - new THREE.MeshBasicMaterial({ color: 0x0000ff, wireframe: true }), - ); - mesh3.position.z = 150; - cameraRig.add(mesh3); - - // - - const geometry = new THREE.BufferGeometry(); - const vertices = []; - - for (let i = 0; i < 10000; i++) { - vertices.push(THREE.MathUtils.randFloatSpread(2000)); // x - vertices.push(THREE.MathUtils.randFloatSpread(2000)); // y - vertices.push(THREE.MathUtils.randFloatSpread(2000)); // z - } - - geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); - - const particles = new THREE.Points(geometry, new THREE.PointsMaterial({ color: 0xffffff })); - scene.add(particles); - - // - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - renderer.setScissorTest(true); - renderer.setClearColor(0x000000, 1); - - // - - window.addEventListener('resize', onWindowResize); - document.addEventListener('keydown', onKeyDown); -} - -// - -function onKeyDown(event) { - switch (event.keyCode) { - case 79 /*O*/: - activeCamera = cameraOrtho; - activeHelper = cameraOrthoHelper; - - break; - - case 80 /*P*/: - activeCamera = cameraPerspective; - activeHelper = cameraPerspectiveHelper; - - break; - } -} - -// - -function onWindowResize() { - SCREEN_WIDTH = window.innerWidth; - SCREEN_HEIGHT = window.innerHeight; - aspect = SCREEN_WIDTH / SCREEN_HEIGHT; - - renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); - - camera.aspect = 0.5 * aspect; - camera.updateProjectionMatrix(); - - cameraPerspective.aspect = 0.5 * aspect; - cameraPerspective.updateProjectionMatrix(); - - cameraOrtho.left = (-0.5 * frustumSize * aspect) / 2; - cameraOrtho.right = (0.5 * frustumSize * aspect) / 2; - cameraOrtho.top = frustumSize / 2; - cameraOrtho.bottom = -frustumSize / 2; - cameraOrtho.updateProjectionMatrix(); -} - -// - -function animate() { - render(); -} - -function render() { - const r = Date.now() * 0.0005; - - mesh.position.x = 700 * Math.cos(r); - mesh.position.z = 700 * Math.sin(r); - mesh.position.y = 700 * Math.sin(r); - - mesh.children[0].position.x = 70 * Math.cos(2 * r); - mesh.children[0].position.z = 70 * Math.sin(r); - - if (activeCamera === cameraPerspective) { - cameraPerspective.fov = 35 + 30 * Math.sin(0.5 * r); - cameraPerspective.far = mesh.position.length(); - cameraPerspective.updateProjectionMatrix(); - - cameraPerspectiveHelper.update(); - cameraPerspectiveHelper.visible = true; - - cameraOrthoHelper.visible = false; - } else { - cameraOrtho.far = mesh.position.length(); - cameraOrtho.updateProjectionMatrix(); - - cameraOrthoHelper.update(); - cameraOrthoHelper.visible = true; - - cameraPerspectiveHelper.visible = false; - } - - cameraRig.lookAt(mesh.position); - - // - - activeHelper.visible = false; - - renderer.setClearColor(0x000000, 1); - renderer.setScissor(0, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT); - renderer.setViewport(0, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT); - renderer.render(scene, activeCamera); - - // - - activeHelper.visible = true; - - renderer.setClearColor(0x111111, 1); - renderer.setScissor(SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT); - renderer.setViewport(SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT); - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_camera_array.ts b/examples-testing/examples/webgpu_camera_array.ts deleted file mode 100644 index a4d82a709..000000000 --- a/examples-testing/examples/webgpu_camera_array.ts +++ /dev/null @@ -1,101 +0,0 @@ -import * as THREE from 'three/webgpu'; - -let camera, scene, renderer; -let mesh; - -const AMOUNT = 6; - -init(); - -function init() { - const subCameras = []; - - for (let i = 0; i < AMOUNT * AMOUNT; i++) { - const subCamera = new THREE.PerspectiveCamera(40, 1, 0.1, 10); - subCamera.viewport = new THREE.Vector4(); - - subCameras.push(subCamera); - } - - camera = new THREE.ArrayCamera(subCameras); - camera.position.z = 3; - - updateCameras(); - - scene = new THREE.Scene(); - - scene.add(new THREE.AmbientLight(0x999999)); - - const light = new THREE.DirectionalLight(0xffffff, 3); - light.position.set(0.5, 0.5, 1); - light.castShadow = true; - light.shadow.camera.zoom = 4; // tighter shadow map - scene.add(light); - - const geometryBackground = new THREE.PlaneGeometry(100, 100); - const materialBackground = new THREE.MeshPhongMaterial({ color: 0x000066 }); - - const background = new THREE.Mesh(geometryBackground, materialBackground); - background.receiveShadow = true; - background.position.set(0, 0, -1); - scene.add(background); - - const geometryCylinder = new THREE.CylinderGeometry(0.5, 0.5, 1, 32); - const materialCylinder = new THREE.MeshPhongMaterial({ color: 0xff0000 }); - - mesh = new THREE.Mesh(geometryCylinder, materialCylinder); - mesh.castShadow = true; - mesh.receiveShadow = true; - scene.add(mesh); - - renderer = new THREE.WebGPURenderer(/*{ forceWebGL: true }*/); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.shadowMap.enabled = true; - document.body.appendChild(renderer.domElement); - - // - - window.addEventListener('resize', onWindowResize); -} - -function updateCameras() { - const ASPECT_RATIO = window.innerWidth / window.innerHeight; - const WIDTH = window.innerWidth / AMOUNT; - const HEIGHT = window.innerHeight / AMOUNT; - - camera.aspect = ASPECT_RATIO; - camera.updateProjectionMatrix(); - - for (let y = 0; y < AMOUNT; y++) { - for (let x = 0; x < AMOUNT; x++) { - const subcamera = camera.cameras[AMOUNT * y + x]; - subcamera.copy(camera); // copy fov, aspect ratio, near, far from the root camera - - subcamera.viewport.set(Math.floor(x * WIDTH), Math.floor(y * HEIGHT), Math.ceil(WIDTH), Math.ceil(HEIGHT)); - subcamera.updateProjectionMatrix(); - - subcamera.position.x = x / AMOUNT - 0.5; - subcamera.position.y = 0.5 - y / AMOUNT; - subcamera.position.z = 1.5 + (x + y) * 0.5; - subcamera.position.multiplyScalar(2); - - subcamera.lookAt(0, 0, 0); - subcamera.updateMatrixWorld(); - } - } -} - -function onWindowResize() { - updateCameras(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - mesh.rotation.x += 0.005; - mesh.rotation.z += 0.01; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_camera_logarithmicdepthbuffer.ts b/examples-testing/examples/webgpu_camera_logarithmicdepthbuffer.ts deleted file mode 100644 index a0e748e1b..000000000 --- a/examples-testing/examples/webgpu_camera_logarithmicdepthbuffer.ts +++ /dev/null @@ -1,239 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { FontLoader } from 'three/addons/loaders/FontLoader.js'; -import { TextGeometry } from 'three/addons/geometries/TextGeometry.js'; - -// 1 micrometer to 100 billion light years in one scene, with 1 unit = 1 meter? preposterous! and yet... -const NEAR = 1e-6, - FAR = 1e27; -let SCREEN_WIDTH = window.innerWidth; -let SCREEN_HEIGHT = window.innerHeight; -let screensplit = 0.25, - screensplit_right = 0; -const mouse = [0.5, 0.5]; -let zoompos = -100, - minzoomspeed = 0.015; -let zoomspeed = minzoomspeed; - -let border; -const objects = {}; - -// Generate a number of text labels, from 1µm in size up to 100,000,000 light years -// Try to use some descriptive real-world examples of objects at each scale - -const labeldata = [ - { size: 0.01, scale: 0.0001, label: 'microscopic (1µm)' }, // FIXME - triangulating text fails at this size, so we scale instead - { size: 0.01, scale: 0.1, label: 'minuscule (1mm)' }, - { size: 0.01, scale: 1.0, label: 'tiny (1cm)' }, - { size: 1, scale: 1.0, label: 'child-sized (1m)' }, - { size: 10, scale: 1.0, label: 'tree-sized (10m)' }, - { size: 100, scale: 1.0, label: 'building-sized (100m)' }, - { size: 1000, scale: 1.0, label: 'medium (1km)' }, - { size: 10000, scale: 1.0, label: 'city-sized (10km)' }, - { size: 3400000, scale: 1.0, label: 'moon-sized (3,400 Km)' }, - { size: 12000000, scale: 1.0, label: 'planet-sized (12,000 km)' }, - { size: 1400000000, scale: 1.0, label: 'sun-sized (1,400,000 km)' }, - { size: 7.47e12, scale: 1.0, label: 'solar system-sized (50Au)' }, - { size: 9.4605284e15, scale: 1.0, label: 'gargantuan (1 light year)' }, - { size: 3.08567758e16, scale: 1.0, label: 'ludicrous (1 parsec)' }, - { size: 1e19, scale: 1.0, label: 'mind boggling (1000 light years)' }, -]; - -init().then(animate); - -async function init() { - const loader = new FontLoader(); - const font = await loader.loadAsync('fonts/helvetiker_regular.typeface.json'); - - const scene = initScene(font); - - // Initialize two copies of the same scene, one with normal z-buffer and one with logarithmic z-buffer - objects.normal = await initView(scene, 'normal', false); - objects.logzbuf = await initView(scene, 'logzbuf', true); - - // Resize border allows the user to easily compare effects of logarithmic depth buffer over the whole scene - border = document.getElementById('renderer_border'); - border.addEventListener('pointerdown', onBorderPointerDown); - - window.addEventListener('mousemove', onMouseMove); - window.addEventListener('resize', onWindowResize); - window.addEventListener('wheel', onMouseWheel); -} - -async function initView(scene, name, logDepthBuf) { - const framecontainer = document.getElementById('container_' + name); - - const camera = new THREE.PerspectiveCamera(50, (screensplit * SCREEN_WIDTH) / SCREEN_HEIGHT, NEAR, FAR); - scene.add(camera); - - const renderer = new THREE.WebGPURenderer({ antialias: true, logarithmicDepthBuffer: logDepthBuf }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(SCREEN_WIDTH / 2, SCREEN_HEIGHT); - renderer.domElement.style.position = 'relative'; - renderer.domElement.id = 'renderer_' + name; - renderer.inspector = new Inspector(); - framecontainer.appendChild(renderer.domElement); - - await renderer.init(); - - return { container: framecontainer, renderer: renderer, scene: scene, camera: camera }; -} - -function initScene(font) { - const scene = new THREE.Scene(); - - scene.add(new THREE.AmbientLight(0x777777)); - - const light = new THREE.DirectionalLight(0xffffff, 3); - light.position.set(100, 100, 100); - scene.add(light); - - const materialargs = { - color: 0xffffff, - specular: 0x050505, - shininess: 50, - emissive: 0x000000, - }; - - const geometry = new THREE.SphereGeometry(0.5, 24, 12); - - for (let i = 0; i < labeldata.length; i++) { - const scale = labeldata[i].scale || 1; - - const labelgeo = new TextGeometry(labeldata[i].label, { - font: font, - size: labeldata[i].size, - depth: labeldata[i].size / 2, - }); - - labelgeo.computeBoundingSphere(); - - // center text - labelgeo.translate(-labelgeo.boundingSphere.radius, 0, 0); - - materialargs.color = new THREE.Color().setHSL(Math.random(), 0.5, 0.5); - - const material = new THREE.MeshPhongMaterial(materialargs); - - const group = new THREE.Group(); - group.position.z = -labeldata[i].size * scale; - scene.add(group); - - const textmesh = new THREE.Mesh(labelgeo, material); - textmesh.scale.set(scale, scale, scale); - textmesh.position.z = -labeldata[i].size * scale; - textmesh.position.y = (labeldata[i].size / 4) * scale; - group.add(textmesh); - - const dotmesh = new THREE.Mesh(geometry, material); - dotmesh.position.y = (-labeldata[i].size / 4) * scale; - dotmesh.scale.multiplyScalar(labeldata[i].size * scale); - group.add(dotmesh); - } - - return scene; -} - -function updateRendererSizes() { - // Recalculate size for both renderers when screen size or split location changes - - SCREEN_WIDTH = window.innerWidth; - SCREEN_HEIGHT = window.innerHeight; - - screensplit_right = 1 - screensplit; - - objects.normal.renderer.setSize(screensplit * SCREEN_WIDTH, SCREEN_HEIGHT); - objects.normal.camera.aspect = (screensplit * SCREEN_WIDTH) / SCREEN_HEIGHT; - objects.normal.camera.updateProjectionMatrix(); - objects.normal.camera.setViewOffset(SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, SCREEN_WIDTH * screensplit, SCREEN_HEIGHT); - objects.normal.container.style.width = screensplit * 100 + '%'; - - objects.logzbuf.renderer.setSize(screensplit_right * SCREEN_WIDTH, SCREEN_HEIGHT); - objects.logzbuf.camera.aspect = (screensplit_right * SCREEN_WIDTH) / SCREEN_HEIGHT; - objects.logzbuf.camera.updateProjectionMatrix(); - objects.logzbuf.camera.setViewOffset( - SCREEN_WIDTH, - SCREEN_HEIGHT, - SCREEN_WIDTH * screensplit, - 0, - SCREEN_WIDTH * screensplit_right, - SCREEN_HEIGHT, - ); - objects.logzbuf.container.style.width = screensplit_right * 100 + '%'; - - border.style.left = screensplit * 100 + '%'; -} - -function animate() { - requestAnimationFrame(animate); - - // Put some limits on zooming - const minzoom = labeldata[0].size * labeldata[0].scale * 1; - const maxzoom = labeldata[labeldata.length - 1].size * labeldata[labeldata.length - 1].scale * 100; - let damping = Math.abs(zoomspeed) > minzoomspeed ? 0.95 : 1.0; - - // Zoom out faster the further out you go - const zoom = THREE.MathUtils.clamp(Math.pow(Math.E, zoompos), minzoom, maxzoom); - zoompos = Math.log(zoom); - - // Slow down quickly at the zoom limits - if ((zoom == minzoom && zoomspeed < 0) || (zoom == maxzoom && zoomspeed > 0)) { - damping = 0.85; - } - - zoompos += zoomspeed; - zoomspeed *= damping; - - objects.normal.camera.position.x = Math.sin(0.5 * Math.PI * (mouse[0] - 0.5)) * zoom; - objects.normal.camera.position.y = Math.sin(0.25 * Math.PI * (mouse[1] - 0.5)) * zoom; - objects.normal.camera.position.z = Math.cos(0.5 * Math.PI * (mouse[0] - 0.5)) * zoom; - objects.normal.camera.lookAt(objects.normal.scene.position); - - // Clone camera settings across both scenes - objects.logzbuf.camera.position.copy(objects.normal.camera.position); - objects.logzbuf.camera.quaternion.copy(objects.normal.camera.quaternion); - - // Update renderer sizes if the split has changed - if (screensplit_right != 1 - screensplit) { - updateRendererSizes(); - } - - objects.normal.renderer.render(objects.normal.scene, objects.normal.camera); - objects.logzbuf.renderer.render(objects.logzbuf.scene, objects.logzbuf.camera); -} - -function onWindowResize() { - updateRendererSizes(); -} - -function onBorderPointerDown() { - // activate draggable window resizing bar - window.addEventListener('pointermove', onBorderPointerMove); - window.addEventListener('pointerup', onBorderPointerUp); -} - -function onBorderPointerMove(ev) { - screensplit = Math.max(0, Math.min(1, ev.clientX / window.innerWidth)); -} - -function onBorderPointerUp() { - window.removeEventListener('pointermove', onBorderPointerMove); - window.removeEventListener('pointerup', onBorderPointerUp); -} - -function onMouseMove(ev) { - mouse[0] = ev.clientX / window.innerWidth; - mouse[1] = ev.clientY / window.innerHeight; -} - -function onMouseWheel(ev) { - const amount = ev.deltaY; - if (amount === 0) return; - const dir = amount / Math.abs(amount); - zoomspeed = dir / 10; - - // Slow down default zoom speed after user starts zooming, to give them more control - minzoomspeed = 0.001; -} diff --git a/examples-testing/examples/webgpu_caustics.ts b/examples-testing/examples/webgpu_caustics.ts deleted file mode 100644 index 0829fb558..000000000 --- a/examples-testing/examples/webgpu_caustics.ts +++ /dev/null @@ -1,202 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { - uniform, - refract, - div, - positionViewDirection, - positionLocal, - normalView, - texture, - Fn, - vec2, - vec3, - vec4, -} from 'three/tsl'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer, controls; -let gltf; - -init(); - -async function init() { - camera = new THREE.PerspectiveCamera(25, window.innerWidth / window.innerHeight, 0.025, 5); - camera.position.set(-0.5, 0.35, 0.2); - - scene = new THREE.Scene(); - - // light - - const spotLight = new THREE.SpotLight(0xffffff, 1); - spotLight.position.set(0.2, 0.3, 0.2); - spotLight.castShadow = true; - spotLight.angle = Math.PI / 6; - spotLight.penumbra = 1; - spotLight.decay = 2; - spotLight.distance = 0; - spotLight.shadow.mapType = THREE.HalfFloatType; // For HDR Caustics - spotLight.shadow.mapSize.width = 1024; - spotLight.shadow.mapSize.height = 1024; - spotLight.shadow.camera.near = 0.1; - spotLight.shadow.camera.far = 1; - spotLight.shadow.intensity = 0.95; - scene.add(spotLight); - - // model / textures - - const dracoLoader = new DRACOLoader(); - dracoLoader.setDecoderPath('jsm/libs/draco/'); - dracoLoader.setDecoderConfig({ type: 'js' }); - - gltf = (await new GLTFLoader().setDRACOLoader(dracoLoader).loadAsync('./models/gltf/duck.glb')).scene; - gltf.scale.setScalar(0.5); - scene.add(gltf); - - const causticMap = new THREE.TextureLoader().load('./textures/opengameart/Caustic_Free.jpg'); - causticMap.wrapS = causticMap.wrapT = THREE.RepeatWrapping; - causticMap.colorSpace = THREE.SRGBColorSpace; - - // objects / material - - const duck = gltf.children[0]; - duck.material = new THREE.MeshPhysicalNodeMaterial(); - duck.material.side = THREE.DoubleSide; - duck.material.transparent = true; - duck.material.color = new THREE.Color(0xffd700); - duck.material.transmission = 1; - duck.material.thickness = 0.25; - duck.material.ior = 1.5; - duck.material.metalness = 0; - duck.material.roughness = 0.1; - duck.castShadow = true; - - // tsl shader - - const causticOcclusion = uniform(20); - - duck.material.castShadowPositionNode = Fn(() => { - // optional: add some distortion to the geometry shadow position if needed - - return positionLocal; - })(); - - duck.material.castShadowNode = Fn(() => { - const refractionVector = refract( - positionViewDirection.negate(), - normalView, - div(1.0, duck.material.ior), - ).normalize(); - const viewZ = normalView.z.pow(causticOcclusion); - - const textureUV = refractionVector.xy.mul(0.6); - - const causticColor = uniform(duck.material.color); - const chromaticAberrationOffset = normalView.z.pow(-0.9).mul(0.004); - - const causticProjection = vec3( - texture(causticMap, textureUV.add(vec2(chromaticAberrationOffset.negate(), 0))).r, - texture(causticMap, textureUV.add(vec2(0, chromaticAberrationOffset.negate()))).g, - texture(causticMap, textureUV.add(vec2(chromaticAberrationOffset, chromaticAberrationOffset))).b, - ); - - return causticProjection.mul(viewZ.mul(25)).add(viewZ).mul(causticColor); - })(); - - // - - const textureLoader = new THREE.TextureLoader(); - - // glass - - const colorMap = textureLoader.load('textures/colors.png'); - colorMap.wrapS = colorMap.wrapT = THREE.RepeatWrapping; - colorMap.colorSpace = THREE.SRGBColorSpace; - - const glassMaterial = new THREE.MeshPhysicalNodeMaterial(); - glassMaterial.map = colorMap; - glassMaterial.side = THREE.DoubleSide; - glassMaterial.transparent = true; - glassMaterial.color = new THREE.Color(0xffffff); - glassMaterial.transmission = 1; - glassMaterial.ior = 1.5; - glassMaterial.metalness = 0; - glassMaterial.roughness = 0.1; - glassMaterial.castShadowNode = vec4(texture(colorMap).rgb, 0.8); - - const glass = new THREE.Mesh(new THREE.PlaneGeometry(0.2, 0.2), glassMaterial); - glass.position.y = 0.1; - glass.castShadow = true; - glass.visible = false; - scene.add(glass); - - // ground - - const map = textureLoader.load('textures/hardwood2_diffuse.jpg'); - map.wrapS = map.wrapT = THREE.RepeatWrapping; - map.repeat.set(10, 10); - - const geometry = new THREE.PlaneGeometry(2, 2); - const material = new THREE.MeshStandardMaterial({ color: 0x999999, map }); - - const ground = new THREE.Mesh(geometry, material); - ground.rotation.x = -Math.PI / 2; - ground.receiveShadow = true; - scene.add(ground); - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.shadowMap.enabled = true; - renderer.shadowMap.transmitted = true; - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // gui - - const gui = renderer.inspector.createParameters('Settings'); - gui.add(causticOcclusion, 'value', 0, 20).name('caustic occlusion'); - gui.addColor(duck.material, 'color').name('material color'); - gui.add({ model: 'duck' }, 'model', ['duck', 'glass']).onChange(model => { - duck.visible = glass.visible = false; - - if (model === 'duck') { - duck.visible = true; - } else if (model === 'glass') { - glass.visible = true; - } - }); - - // controls - - controls = new OrbitControls(camera, renderer.domElement); - controls.maxDistance = 3; - controls.maxPolarAngle = Math.PI / 2; - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - for (const mesh of gltf.children) { - mesh.rotation.y -= 0.01; - } - - controls.update(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_centroid_sampling.ts b/examples-testing/examples/webgpu_centroid_sampling.ts deleted file mode 100644 index ca159d04c..000000000 --- a/examples-testing/examples/webgpu_centroid_sampling.ts +++ /dev/null @@ -1,199 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { varying, uv, texture, Fn } from 'three/tsl'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let rendererAntialiasingEnabled; -let rendererAntialiasingDisabled; -let camera; -let scene; -let gui; - -const effectController = { - sampling: 'normal', -}; - -const atlasCanvas = document.createElement('canvas'); -atlasCanvas.width = 16; -atlasCanvas.height = 16; - -const ctx = atlasCanvas.getContext('2d'); -ctx.fillStyle = 'red'; -ctx.fillRect(0, 0, 8, 8); - -const redUVs = [0, 1, 0.5, 1, 0.5, 0.5, 0, 0.5]; -ctx.fillStyle = 'green'; -ctx.fillRect(8, 0, 8, 8); - -const greenUVs = [1, 1, 0.5, 1, 0.5, 0.5, 1, 0.5]; - -ctx.fillStyle = 'blue'; -ctx.fillRect(0, 8, 8, 8); - -const blueUVs = [0, 0, 0.5, 0, 0.5, 0.5, 0, 0.5]; - -ctx.fillStyle = 'yellow'; -ctx.fillRect(8, 8, 8, 8); - -const yellowUVs = [1, 0, 0.5, 0, 0.5, 0.5, 1, 0.5]; - -const faces = [redUVs, greenUVs, blueUVs, yellowUVs]; - -const canvasTexture = new THREE.CanvasTexture(atlasCanvas); -canvasTexture.colorSpace = THREE.SRGBColorSpace; -canvasTexture.mapping = THREE.UVMapping; -canvasTexture.wrapS = THREE.RepeatWrapping; -canvasTexture.wrapT = THREE.RepeatWrapping; -canvasTexture.magFilter = THREE.NearestFilter; -canvasTexture.minFilter = THREE.NearestFilter; -canvasTexture.format = THREE.RGBAFormat; -canvasTexture.type = THREE.UnsignedByteType; - -const forceWebGL = false; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(); - camera.fov = 60; - camera.near = 1; - camera.far = 2100; - camera.position.z = 50; - - scene = new THREE.Scene(); - - const makeFaceGeometry = uvs => { - const geometry = new THREE.BufferGeometry(); - const positions = [-1, -1, 0, 1, -1, 0, 1, 1, 0, -1, 1, 0]; - geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(positions), 3)); - - const indices = [0, 1, 2, 2, 3, 0]; - geometry.setIndex(indices); - - geometry.setAttribute('uv', new THREE.BufferAttribute(new Float32Array(uvs), 2)); - - return geometry; - }; - - const material = new THREE.MeshBasicNodeMaterial(); - const testUV = varying(uv(), 'testUV'); - - const createShader = (type, sampling) => { - return Fn(() => { - testUV.setInterpolation(type, sampling); - - return texture(canvasTexture, testUV).rgb; - }); - }; - - const withFlatFirstShader = createShader( - THREE.InterpolationSamplingType.FLAT, - THREE.InterpolationSamplingMode.FIRST, - ); - const withFlatEitherShader = createShader( - THREE.InterpolationSamplingType.FLAT, - THREE.InterpolationSamplingMode.EITHER, - ); - - const withSampleShader = Fn(() => { - testUV.setInterpolation(THREE.InterpolationSamplingType.PERSPECTIVE, THREE.InterpolationSamplingMode.SAMPLE); - - return texture(canvasTexture, testUV).rgb; - }); - - const withInterpolationShader = Fn(() => { - testUV.setInterpolation(THREE.InterpolationSamplingType.PERSPECTIVE, THREE.InterpolationSamplingMode.CENTROID); - - return texture(canvasTexture, testUV).rgb; - }); - - const withoutInterpolationShader = Fn(() => { - return texture(canvasTexture, uv()).rgb; - }); - - material.colorNode = withoutInterpolationShader(); - - const faceMeshes = []; - - for (let x = -5; x < 5; x++) { - for (let y = -5; y < 5; y++) { - const face = faces[Math.floor(Math.random() * faces.length)]; - const geometry = makeFaceGeometry(face); - const mesh = new THREE.Mesh(geometry, material); - mesh.position.set(x * 2, y * 2, 0); - faceMeshes.push(mesh); - scene.add(mesh); - } - } - - // Create Standard Renderer - rendererAntialiasingDisabled = new THREE.WebGPURenderer({ - antialias: false, - forceWebGL: forceWebGL, - }); - - rendererAntialiasingDisabled.setPixelRatio(window.devicePixelRatio); - rendererAntialiasingDisabled.setSize(window.innerWidth / 2, window.innerHeight); - rendererAntialiasingDisabled.setAnimationLoop(animateStandard); - - // Create antialiased renderer - rendererAntialiasingEnabled = new THREE.WebGPURenderer({ - antialias: true, - forceWebGL: forceWebGL, - }); - - document.body.querySelector('#antialiasing-enabled').appendChild(rendererAntialiasingEnabled.domElement); - rendererAntialiasingEnabled.setPixelRatio(window.devicePixelRatio); - rendererAntialiasingEnabled.setSize(window.innerWidth / 2, window.innerHeight); - rendererAntialiasingEnabled.setAnimationLoop(animateAliased); - rendererAntialiasingEnabled.inspector = new Inspector(); - - document.body.querySelector('#antialiasing-disabled').appendChild(rendererAntialiasingDisabled.domElement); - document.body.querySelector('#antialiasing-disabled').appendChild(rendererAntialiasingDisabled.domElement); - - onWindowResize(); - - window.addEventListener('resize', onWindowResize); - - gui = rendererAntialiasingEnabled.inspector.createParameters('Settings'); - gui.add(effectController, 'sampling', [ - THREE.InterpolationSamplingMode.NORMAL, - THREE.InterpolationSamplingMode.CENTROID, - THREE.InterpolationSamplingMode.SAMPLE, - 'flat first', - 'flat either', - ]).onChange(() => { - const interpolationShaderLib = { - [THREE.InterpolationSamplingMode.NORMAL]: withoutInterpolationShader, - [THREE.InterpolationSamplingMode.CENTROID]: withInterpolationShader, - [THREE.InterpolationSamplingMode.SAMPLE]: withSampleShader, - ['flat first']: withFlatFirstShader, - ['flat either']: withFlatEitherShader, - }; - - const shader = interpolationShaderLib[effectController.sampling]; - - for (let i = 0; i < faceMeshes.length; i++) { - faceMeshes[i].material.colorNode = shader(); - faceMeshes[i].material.needsUpdate = true; - } - }); -} - -function onWindowResize() { - const halfWidth = window.innerWidth / 2; - rendererAntialiasingDisabled.setSize(halfWidth, window.innerHeight); - rendererAntialiasingEnabled.setSize(halfWidth, window.innerHeight); - const aspect = halfWidth / window.innerHeight; - - camera.aspect = aspect; - camera.updateProjectionMatrix(); -} - -function animateStandard() { - rendererAntialiasingDisabled.render(scene, camera); -} - -function animateAliased() { - rendererAntialiasingEnabled.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_clearcoat.ts b/examples-testing/examples/webgpu_clearcoat.ts deleted file mode 100644 index 02fbacb9d..000000000 --- a/examples-testing/examples/webgpu_clearcoat.ts +++ /dev/null @@ -1,194 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { HDRCubeTextureLoader } from 'three/addons/loaders/HDRCubeTextureLoader.js'; - -import { FlakesTexture } from 'three/addons/textures/FlakesTexture.js'; - -let camera, scene, renderer; - -let particleLight; -let group; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 0.25, 50); - camera.position.z = 10; - - scene = new THREE.Scene(); - - group = new THREE.Group(); - scene.add(group); - - new HDRCubeTextureLoader() - .setPath('textures/cube/pisaHDR/') - .load(['px.hdr', 'nx.hdr', 'py.hdr', 'ny.hdr', 'pz.hdr', 'nz.hdr'], function (texture) { - const geometry = new THREE.SphereGeometry(0.8, 64, 32); - - const textureLoader = new THREE.TextureLoader(); - - const diffuse = textureLoader.load('textures/carbon/Carbon.png'); - diffuse.colorSpace = THREE.SRGBColorSpace; - diffuse.wrapS = THREE.RepeatWrapping; - diffuse.wrapT = THREE.RepeatWrapping; - diffuse.repeat.x = 10; - diffuse.repeat.y = 10; - - const normalMap = textureLoader.load('textures/carbon/Carbon_Normal.png'); - normalMap.wrapS = THREE.RepeatWrapping; - normalMap.wrapT = THREE.RepeatWrapping; - normalMap.repeat.x = 10; - normalMap.repeat.y = 10; - - const normalMap2 = textureLoader.load('textures/water/Water_1_M_Normal.jpg'); - - const normalMap3 = new THREE.CanvasTexture(new FlakesTexture()); - normalMap3.wrapS = THREE.RepeatWrapping; - normalMap3.wrapT = THREE.RepeatWrapping; - normalMap3.repeat.x = 10; - normalMap3.repeat.y = 6; - normalMap3.anisotropy = 16; - - const normalMap4 = textureLoader.load('textures/golfball.jpg'); - - const clearcoatNormalMap = textureLoader.load( - 'textures/pbr/Scratched_gold/Scratched_gold_01_1K_Normal.png', - ); - - // car paint - - let material = new THREE.MeshPhysicalMaterial({ - clearcoat: 1.0, - clearcoatRoughness: 0.1, - metalness: 0.9, - roughness: 0.5, - color: 0x0000ff, - normalMap: normalMap3, - normalScale: new THREE.Vector2(0.15, 0.15), - }); - let mesh = new THREE.Mesh(geometry, material); - mesh.position.x = -1; - mesh.position.y = 1; - group.add(mesh); - - // fibers - - material = new THREE.MeshPhysicalMaterial({ - roughness: 0.5, - clearcoat: 1.0, - clearcoatRoughness: 0.1, - map: diffuse, - normalMap: normalMap, - }); - mesh = new THREE.Mesh(geometry, material); - mesh.position.x = 1; - mesh.position.y = 1; - group.add(mesh); - - // golf - - material = new THREE.MeshPhysicalMaterial({ - metalness: 0.0, - roughness: 0.1, - clearcoat: 1.0, - normalMap: normalMap4, - clearcoatNormalMap: clearcoatNormalMap, - - // y scale is negated to compensate for normal map handedness. - clearcoatNormalScale: new THREE.Vector2(2.0, -2.0), - }); - mesh = new THREE.Mesh(geometry, material); - mesh.position.x = -1; - mesh.position.y = -1; - group.add(mesh); - - // clearcoat + normalmap - - material = new THREE.MeshPhysicalMaterial({ - clearcoat: 1.0, - metalness: 1.0, - color: 0xff0000, - normalMap: normalMap2, - normalScale: new THREE.Vector2(0.15, 0.15), - clearcoatNormalMap: clearcoatNormalMap, - - // y scale is negated to compensate for normal map handedness. - clearcoatNormalScale: new THREE.Vector2(2.0, -2.0), - }); - mesh = new THREE.Mesh(geometry, material); - mesh.position.x = 1; - mesh.position.y = -1; - group.add(mesh); - - // - - scene.background = texture; - scene.environment = texture; - }); - - // LIGHTS - - particleLight = new THREE.Mesh( - new THREE.SphereGeometry(0.05, 8, 8), - new THREE.MeshBasicMaterial({ color: 0xffffff }), - ); - scene.add(particleLight); - - particleLight.add(new THREE.PointLight(0xffffff, 30)); - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // - - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = 1.25; - renderer.inspector = new Inspector(); - - // EVENTS - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 3; - controls.maxDistance = 30; - - window.addEventListener('resize', onWindowResize); -} - -// - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); -} - -// - -function animate() { - render(); -} - -function render() { - const timer = Date.now() * 0.00025; - - particleLight.position.x = Math.sin(timer * 7) * 3; - particleLight.position.y = Math.cos(timer * 5) * 4; - particleLight.position.z = Math.cos(timer * 3) * 3; - - for (let i = 0; i < group.children.length; i++) { - const child = group.children[i]; - child.rotation.y += 0.005; - } - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_clipping.ts b/examples-testing/examples/webgpu_clipping.ts deleted file mode 100644 index b7e94da98..000000000 --- a/examples-testing/examples/webgpu_clipping.ts +++ /dev/null @@ -1,203 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let camera, scene, renderer, startTime, object; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(36, window.innerWidth / window.innerHeight, 0.25, 16); - - camera.position.set(0, 1.3, 3); - - scene = new THREE.Scene(); - - // Lights - - scene.add(new THREE.AmbientLight(0xcccccc)); - - const spotLight = new THREE.SpotLight(0xffffff, 60); - spotLight.angle = Math.PI / 5; - spotLight.penumbra = 0.2; - spotLight.position.set(2, 3, 3); - spotLight.castShadow = true; - spotLight.shadow.camera.near = 3; - spotLight.shadow.camera.far = 10; - spotLight.shadow.mapSize.width = 2048; - spotLight.shadow.mapSize.height = 2048; - spotLight.shadow.radius = 4; - scene.add(spotLight); - - const dirLight = new THREE.DirectionalLight(0x55505a, 3); - dirLight.position.set(0, 3, 0); - dirLight.castShadow = true; - dirLight.shadow.camera.near = 1; - dirLight.shadow.camera.far = 10; - - dirLight.shadow.camera.right = 1; - dirLight.shadow.camera.left = -1; - dirLight.shadow.camera.top = 1; - dirLight.shadow.camera.bottom = -1; - - dirLight.shadow.mapSize.width = 1024; - dirLight.shadow.mapSize.height = 1024; - scene.add(dirLight); - - // Clipping planes - - const globalPlane = new THREE.Plane(new THREE.Vector3(-1, 0, 0), 0.1); - const localPlane1 = new THREE.Plane(new THREE.Vector3(0, -1, 0), 0.8); - const localPlane2 = new THREE.Plane(new THREE.Vector3(0, 0, -1), 0.1); - - // Clipping Groups - - const globalClippingGroup = new THREE.ClippingGroup(); - globalClippingGroup.clippingPlanes = [globalPlane]; - - const knotClippingGroup = new THREE.ClippingGroup(); - knotClippingGroup.clippingPlanes = [localPlane1, localPlane2]; - knotClippingGroup.clipIntersection = true; - - scene.add(globalClippingGroup); - globalClippingGroup.add(knotClippingGroup); - - // Geometry - - const material = new THREE.MeshPhongNodeMaterial({ - color: 0x80ee10, - shininess: 0, - side: THREE.DoubleSide, - - // ***** Clipping setup (material): ***** - alphaToCoverage: true, - }); - - const geometry = new THREE.TorusKnotGeometry(0.4, 0.08, 95, 20); - - object = new THREE.Mesh(geometry, material); - object.castShadow = true; - knotClippingGroup.add(object); - - const ground = new THREE.Mesh( - new THREE.PlaneGeometry(9, 9, 1, 1), - new THREE.MeshPhongNodeMaterial({ color: 0xa0adaf, shininess: 150, alphaToCoverage: true }), - ); - - ground.rotation.x = -Math.PI / 2; // rotates X/Y to X/Z - ground.receiveShadow = true; - globalClippingGroup.add(ground); - - // Renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.shadowMap.enabled = true; - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - window.addEventListener('resize', onWindowResize); - document.body.appendChild(renderer.domElement); - - // Controls - - const controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, 1, 0); - controls.update(); - - // GUI - - const gui = renderer.inspector.createParameters('Clipping settings'); - const props = { - alphaToCoverage: true, - }; - - const folderKnot = gui.addFolder('Knot Clipping Group'); - const propsKnot = { - get Enabled() { - return knotClippingGroup.enabled; - }, - set Enabled(v) { - knotClippingGroup.enabled = v; - }, - - get Shadows() { - return knotClippingGroup.clipShadows; - }, - set Shadows(v) { - knotClippingGroup.clipShadows = v; - }, - - get Intersection() { - return knotClippingGroup.clipIntersection; - }, - - set Intersection(v) { - knotClippingGroup.clipIntersection = v; - }, - - get Plane() { - return localPlane1.constant; - }, - set Plane(v) { - localPlane1.constant = v; - }, - }; - - const folderGlobal = gui.addFolder('Global Clipping Group'); - const propsGlobal = { - get Enabled() { - return globalClippingGroup.enabled; - }, - set Enabled(v) { - globalClippingGroup.enabled = v; - }, - - get Plane() { - return globalPlane.constant; - }, - set Plane(v) { - globalPlane.constant = v; - }, - }; - - gui.add(props, 'alphaToCoverage').onChange(function (value) { - ground.material.alphaToCoverage = value; - ground.material.needsUpdate = true; - - material.alphaToCoverage = value; - material.needsUpdate = true; - }); - - folderKnot.add(propsKnot, 'Enabled'); - folderKnot.add(propsKnot, 'Shadows'); - folderKnot.add(propsKnot, 'Intersection'); - folderKnot.add(propsKnot, 'Plane', 0.3, 1.25); - - folderGlobal.add(propsGlobal, 'Enabled'); - folderGlobal.add(propsGlobal, 'Plane', -0.4, 3); - - // Start - - startTime = Date.now(); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate(currentTime) { - const time = (currentTime - startTime) / 1000; - - object.position.y = 0.8; - object.rotation.x = time * 0.5; - object.rotation.y = time * 0.2; - object.scale.setScalar(Math.cos(time) * 0.125 + 0.875); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_compile_async.ts b/examples-testing/examples/webgpu_compile_async.ts deleted file mode 100644 index fbcf0a7b0..000000000 --- a/examples-testing/examples/webgpu_compile_async.ts +++ /dev/null @@ -1,265 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { - uv, - float, - vec3, - hash, - mx_noise_vec3, - mx_worley_noise_vec3, - mx_cell_noise_float, - mx_fractal_noise_vec3, -} from 'three/tsl'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let MESH_COUNT = 256; -let GRID_SIZE = 16; -const ADD_DELAY = 1000; - -let camera, scene, renderer; -let sphere; -let gui; - -// Frame timing -let lastFrameTime = 0; -let longestFrameTime = 0; -let isTracking = false; -let shouldStartTracking = false; -let sphereStartTime = 0; -let currentMode = ''; -let framesAfterComplete = 0; -let testDone = false; -let meshGroup = null; - -const longestFrameEl = document.getElementById('longestFrame'); -const meshCountEl = document.getElementById('meshCount'); -const compileModeEl = document.getElementById('compileMode'); - -const params = { - withoutCompile: function () { - window.location.href = window.location.pathname + '?mode=no-compile'; - }, - withCompileAsync: function () { - window.location.href = window.location.pathname + '?mode=compile-async'; - }, -}; - -init(); - -async function init() { - // GUI - gui = new GUI(); - gui.add(params, 'withoutCompile').name('Build on render'); - gui.add(params, 'withCompileAsync').name('Pre-build (compileAsync)'); - - window.addEventListener('resize', onWindowResize); - - // Orthographic camera - const aspect = window.innerWidth / window.innerHeight; - const frustumSize = 20; - camera = new THREE.OrthographicCamera( - (frustumSize * aspect) / -2, - (frustumSize * aspect) / 2, - frustumSize / 2, - frustumSize / -2, - 0.1, - 100, - ); - camera.position.set(0, 0, 20); - camera.lookAt(0, 0, 0); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x111111); - - // Create animated sphere - const sphereGeometry = new THREE.SphereGeometry(0.5, 32, 32); - const sphereMaterial = new THREE.MeshNormalMaterial(); - sphere = new THREE.Mesh(sphereGeometry, sphereMaterial); - scene.add(sphere); - - // Renderer - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - await renderer.init(); - - // Reduce mesh count for WebGL (slower compilation) - if (renderer.backend.isWebGLBackend) { - MESH_COUNT = 64; - GRID_SIZE = 8; - document.getElementById('materialCount').textContent = '64'; - meshCountEl.textContent = '0 / 64'; - } - - // Get mode from URL - const urlParams = new URLSearchParams(window.location.search); - const mode = urlParams.get('mode') || 'compile-async'; - - const modeLabel = mode === 'compile-async' ? 'Pre-build (compileAsync)' : 'Build on render'; - compileModeEl.textContent = modeLabel; - - // Start sphere animation - sphereStartTime = performance.now(); - - // Schedule mesh addition after delay - setTimeout(() => addMeshes(mode), ADD_DELAY); -} - -function createUniqueMaterial(index) { - const material = new THREE.MeshBasicNodeMaterial(); - - const seed = float(index * 0.1 + 1.0); - const scale = float((index % 5) + 2.0); - const uvNode = uv() - .mul(scale) - .add(hash(float(index))); - - const noiseType = index % 4; - let colorNode; - - switch (noiseType) { - case 0: - colorNode = mx_noise_vec3(uvNode.mul(seed)).mul(0.5).add(0.5); - break; - - case 1: - colorNode = mx_worley_noise_vec3(uvNode.mul(seed.mul(0.5))); - break; - - case 2: - const cellNoise = mx_cell_noise_float(uvNode.mul(seed)); - colorNode = vec3(cellNoise, cellNoise.mul(0.7), cellNoise.mul(0.4)); - break; - - case 3: - colorNode = mx_fractal_noise_vec3(uvNode.mul(seed.mul(0.3)), float(3), float(2.0), float(0.5)) - .mul(0.5) - .add(0.5); - break; - } - - const tintR = hash(float(index * 3)); - const tintG = hash(float(index * 3 + 1)); - const tintB = hash(float(index * 3 + 2)); - const tint = vec3(tintR, tintG, tintB).mul(0.3).add(0.7); - - material.colorNode = colorNode.mul(tint); - - return material; -} - -async function addMeshes(mode) { - const geometry = new THREE.PlaneGeometry(0.9, 0.9); - const startX = -(GRID_SIZE - 1) / 2; - const startY = -(GRID_SIZE - 1) / 2; - - meshGroup = new THREE.Group(); - - for (let i = 0; i < MESH_COUNT; i++) { - const material = createUniqueMaterial(i); - const mesh = new THREE.Mesh(geometry, material); - - const col = i % GRID_SIZE; - const row = Math.floor(i / GRID_SIZE); - - mesh.position.x = startX + col; - mesh.position.y = startY + row; - - meshGroup.add(mesh); - } - - currentMode = mode; - - if (mode === 'compile-async') { - // Pre-compile all meshes before adding to scene - // Start tracking BEFORE compile to measure longest frame during compile - shouldStartTracking = true; - - await renderer.compileAsync(meshGroup, camera, scene); - - // Add all meshes at once (already compiled - should render instantly) - scene.add(meshGroup); - testDone = true; - } else { - // Add all meshes at once - renderer compiles on-demand - // Meshes appear progressively as they compile - scene.add(meshGroup); - - // Start tracking on next animate() frame - shouldStartTracking = true; - testDone = true; - } -} - -function finishTest() { - isTracking = false; - - longestFrameEl.textContent = longestFrameTime.toFixed(1) + ' ms'; - longestFrameEl.className = longestFrameTime > 100 ? 'value bad' : 'value'; -} - -function onWindowResize() { - if (!renderer || !camera) return; - - const aspect = window.innerWidth / window.innerHeight; - const frustumSize = 12; - - camera.left = (frustumSize * aspect) / -2; - camera.right = (frustumSize * aspect) / 2; - camera.top = frustumSize / 2; - camera.bottom = frustumSize / -2; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - const now = performance.now(); - - // Initialize tracking on first frame after meshes added - if (shouldStartTracking) { - shouldStartTracking = false; - isTracking = true; - lastFrameTime = now; - longestFrameTime = 0; - framesAfterComplete = 0; - } - - // Track longest frame during test - if (isTracking && lastFrameTime > 0 && lastFrameTime !== now) { - const frameTime = now - lastFrameTime; - if (frameTime > longestFrameTime) { - longestFrameTime = frameTime; - } - } - - lastFrameTime = now; - - // Animate sphere left to right - if (sphere && sphereStartTime > 0) { - const elapsed = (now - sphereStartTime) / 1000; - sphere.position.x = Math.sin(elapsed * 2) * 8; - } - - renderer.render(scene, camera); - - // Update mesh count display - if (meshGroup) { - meshCountEl.textContent = meshGroup.children.length + ' / ' + MESH_COUNT; - } - - // Finish test - wait a few frames after testDone to capture frame times - if (isTracking && testDone) { - framesAfterComplete++; - - // Wait longer for deferred mode (meshes compile progressively) - const framesToWait = currentMode === 'compile-async' ? 2 : 60; - - if (framesAfterComplete >= framesToWait) { - finishTest(); - } - } -} diff --git a/examples-testing/examples/webgpu_compute_audio.ts b/examples-testing/examples/webgpu_compute_audio.ts deleted file mode 100644 index 229033d79..000000000 --- a/examples-testing/examples/webgpu_compute_audio.ts +++ /dev/null @@ -1,178 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { Fn, uniform, instanceIndex, instancedArray, float, texture, screenUV, color } from 'three/tsl'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer; -let computeNode; -let waveBuffer, sampleRate; -let waveArray; -let currentAudio, currentAnalyser; -const analyserBuffer = new Uint8Array(1024); -let analyserTexture; - -const startButton = document.getElementById('startButton'); -startButton.addEventListener('click', init); - -async function playAudioBuffer() { - if (currentAudio) currentAudio.stop(); - - // compute audio - - renderer.compute(computeNode); - - const wave = new Float32Array(await renderer.getArrayBufferAsync(waveArray.value)); - - // play result - - const audioOutputContext = new AudioContext({ sampleRate }); - const audioOutputBuffer = audioOutputContext.createBuffer(1, wave.length, sampleRate); - - audioOutputBuffer.copyToChannel(wave, 0); - - const source = audioOutputContext.createBufferSource(); - source.connect(audioOutputContext.destination); - source.buffer = audioOutputBuffer; - source.start(); - - currentAudio = source; - - // visual feedback - - currentAnalyser = audioOutputContext.createAnalyser(); - currentAnalyser.fftSize = 2048; - - source.connect(currentAnalyser); -} - -async function init() { - const overlay = document.getElementById('overlay'); - overlay.remove(); - - // audio buffer - - const soundBuffer = await fetch('sounds/webgpu-audio-processing.mp3').then(res => res.arrayBuffer()); - const audioContext = new AudioContext(); - - const audioBuffer = await audioContext.decodeAudioData(soundBuffer); - - waveBuffer = audioBuffer.getChannelData(0); - - // adding extra silence to delay and pitch - waveBuffer = new Float32Array([...waveBuffer, ...new Float32Array(200000)]); - - sampleRate = audioBuffer.sampleRate / audioBuffer.numberOfChannels; - - // create webgpu buffers - - waveArray = instancedArray(waveBuffer); - - // read-only buffer - - const originalWave = instancedArray(waveBuffer).toReadOnly(); - - // The Pixel Buffer Object (PBO) is required to get the GPU computed data to the CPU in the WebGL2 fallback. - // As used in `renderer.getArrayBufferAsync( waveArray.value )`. - - originalWave.setPBO(true); - waveArray.setPBO(true); - - // params - - const pitch = uniform(1.5); - const delayVolume = uniform(0.2); - const delayOffset = uniform(0.55); - - // compute (shader-node) - - const computeShaderFn = Fn(() => { - const index = float(instanceIndex); - - // pitch - - const time = index.mul(pitch); - - let wave = originalWave.element(time); - - // delay - - for (let i = 1; i < 7; i++) { - const waveOffset = originalWave.element(index.sub(delayOffset.mul(sampleRate).mul(i)).mul(pitch)); - const waveOffsetVolume = waveOffset.mul(delayVolume.div(i * i)); - - wave = wave.add(waveOffsetVolume); - } - - // store - - const waveStorageElementNode = waveArray.element(instanceIndex); - - waveStorageElementNode.assign(wave); - }); - - // compute - - computeNode = computeShaderFn().compute(waveBuffer.length); - - // renderer - - const container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.01, 30); - - // nodes - - analyserTexture = new THREE.DataTexture(analyserBuffer, analyserBuffer.length, 1, THREE.RedFormat); - - const spectrum = texture(analyserTexture, screenUV.x).x.mul(screenUV.y); - const backgroundNode = color(0x0000ff).mul(spectrum); - - // scene - - scene = new THREE.Scene(); - scene.backgroundNode = backgroundNode; - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(render); - renderer.inspector = new Inspector(); - container.appendChild(renderer.domElement); - - await renderer.init(); - - window.addEventListener('resize', onWindowResize); - document.addEventListener('click', playAudioBuffer); - - // gui - - const gui = renderer.inspector.createParameters('Audio'); - - gui.add(pitch, 'value', 0.5, 2, 0.01).name('pitch'); - gui.add(delayVolume, 'value', 0, 1, 0.01).name('delayVolume'); - gui.add(delayOffset, 'value', 0.1, 1, 0.01).name('delayOffset'); - - // - - playAudioBuffer(); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function render() { - if (currentAnalyser) { - currentAnalyser.getByteFrequencyData(analyserBuffer); - - analyserTexture.needsUpdate = true; - } - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_compute_birds.ts b/examples-testing/examples/webgpu_compute_birds.ts deleted file mode 100644 index 665a54528..000000000 --- a/examples-testing/examples/webgpu_compute_birds.ts +++ /dev/null @@ -1,424 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { - uniform, - varying, - vec4, - add, - sub, - max, - dot, - sin, - mat3, - uint, - negate, - instancedArray, - cameraProjectionMatrix, - cameraViewMatrix, - positionLocal, - modelWorldMatrix, - sqrt, - float, - Fn, - If, - cos, - Loop, - Continue, - normalize, - instanceIndex, - length, - vertexIndex, -} from 'three/tsl'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import WebGPU from 'three/addons/capabilities/WebGPU.js'; - -let container; -let camera, scene, renderer; - -let last = performance.now(); - -let pointer, raycaster; -let computeVelocity, computePosition, effectController; - -const BIRDS = 8192; -const SPEED_LIMIT = 9.0; -const BOUNDS = 800, - BOUNDS_HALF = BOUNDS / 2; - -// Custom Geometry - using 3 triangles each. No normals currently. -class BirdGeometry extends THREE.BufferGeometry { - constructor() { - super(); - - const points = 3 * 3; - - const vertices = new THREE.BufferAttribute(new Float32Array(points * 3), 3); - - this.setAttribute('position', vertices); - - let v = 0; - - function verts_push() { - for (let i = 0; i < arguments.length; i++) { - vertices.array[v++] = arguments[i]; - } - } - - const wingsSpan = 20; - - // Body - verts_push(0, 0, -20, 0, -8, 10, 0, 0, 30); - - // Left Wing - verts_push(0, 0, -15, -wingsSpan, 0, 5, 0, 0, 15); - - // Right Wing - verts_push(0, 0, 15, wingsSpan, 0, 5, 0, 0, -15); - - this.scale(0.2, 0.2, 0.2); - } -} - -// TODO: Fix example with WebGL backend - -if (WebGPU.isAvailable() === false) { - document.body.appendChild(WebGPU.getErrorMessage()); - - throw new Error('No WebGPU support'); -} - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 5000); - camera.position.z = 1000; - - scene = new THREE.Scene(); - scene.fog = new THREE.Fog(0xffffff, 700, 3000); - - // Pointer - - pointer = new THREE.Vector2(); - raycaster = new THREE.Raycaster(); - - // Sky - - const geometry = new THREE.IcosahedronGeometry(1, 6); - const material = new THREE.MeshBasicNodeMaterial({ - // Use vertex positions to create atmosphere colors - colorNode: varying( - vec4(sub(0.25, positionLocal.y), sub(-0.25, positionLocal.y), add(1.5, positionLocal.y), 1.0), - ), - side: THREE.BackSide, - }); - - const mesh = new THREE.Mesh(geometry, material); - mesh.rotation.z = 0.75; - mesh.scale.setScalar(1200); - scene.add(mesh); - - // - - renderer = new THREE.WebGPURenderer({ - antialias: true, - forceWebGL: false, - requiredLimits: { maxStorageBuffersInVertexStage: 3 }, - }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(render); - renderer.toneMapping = THREE.NeutralToneMapping; - renderer.inspector = new Inspector(); - container.appendChild(renderer.domElement); - - const controls = new OrbitControls(camera); - controls.connect(renderer.domElement); - - // Initialize position, velocity, and phase values - - const positionArray = new Float32Array(BIRDS * 3); - const velocityArray = new Float32Array(BIRDS * 3); - const phaseArray = new Float32Array(BIRDS); - - for (let i = 0; i < BIRDS; i++) { - const posX = Math.random() * BOUNDS - BOUNDS_HALF; - const posY = Math.random() * BOUNDS - BOUNDS_HALF; - const posZ = Math.random() * BOUNDS - BOUNDS_HALF; - - positionArray[i * 3 + 0] = posX; - positionArray[i * 3 + 1] = posY; - positionArray[i * 3 + 2] = posZ; - - const velX = Math.random() - 0.5; - const velY = Math.random() - 0.5; - const velZ = Math.random() - 0.5; - - velocityArray[i * 3 + 0] = velX * 10; - velocityArray[i * 3 + 1] = velY * 10; - velocityArray[i * 3 + 2] = velZ * 10; - - phaseArray[i] = 1; - } - - // Labels applied to storage nodes and uniform nodes are reflected within the shader output, - // and are useful for debugging purposes. - - const positionStorage = instancedArray(positionArray, 'vec3').setName('positionStorage'); - const velocityStorage = instancedArray(velocityArray, 'vec3').setName('velocityStorage'); - const phaseStorage = instancedArray(phaseArray, 'float').setName('phaseStorage'); - - // The Pixel Buffer Object (PBO) is required to get the GPU computed data in the WebGL2 fallback. - - positionStorage.setPBO(true); - velocityStorage.setPBO(true); - phaseStorage.setPBO(true); - - // Define Uniforms. Uniforms only need to be defined once rather than per shader. - - effectController = { - separation: uniform(15.0).setName('separation'), - alignment: uniform(20.0).setName('alignment'), - cohesion: uniform(20.0).setName('cohesion'), - freedom: uniform(0.75).setName('freedom'), - now: uniform(0.0), - deltaTime: uniform(0.0).setName('deltaTime'), - rayOrigin: uniform(new THREE.Vector3()).setName('rayOrigin'), - rayDirection: uniform(new THREE.Vector3()).setName('rayDirection'), - }; - - // Create geometry - - const birdGeometry = new BirdGeometry(); - const birdMaterial = new THREE.NodeMaterial(); - - // Animate bird mesh within vertex shader, then apply position offset to vertices. - - const birdVertexTSL = Fn(() => { - const position = positionLocal.toVar(); - const newPhase = phaseStorage.element(instanceIndex).toVar(); - const newVelocity = normalize(velocityStorage.element(instanceIndex)).toVar(); - - If(vertexIndex.equal(4).or(vertexIndex.equal(7)), () => { - // flap wings - position.y = sin(newPhase).mul(5.0); - }); - - const newPosition = modelWorldMatrix.mul(position); - - newVelocity.z.mulAssign(-1.0); - const xz = length(newVelocity.xz); - const xyz = float(1.0); - const x = sqrt(newVelocity.y.mul(newVelocity.y).oneMinus()); - - const cosry = newVelocity.x.div(xz).toVar(); - const sinry = newVelocity.z.div(xz).toVar(); - - const cosrz = x.div(xyz); - const sinrz = newVelocity.y.div(xyz).toVar(); - - // Nodes must be negated with negate(). Using '-', their values will resolve to NaN. - const maty = mat3(cosry, 0, negate(sinry), 0, 1, 0, sinry, 0, cosry); - - const matz = mat3(cosrz, sinrz, 0, negate(sinrz), cosrz, 0, 0, 0, 1); - - const finalVert = maty.mul(matz).mul(newPosition); - finalVert.addAssign(positionStorage.element(instanceIndex)); - - return cameraProjectionMatrix.mul(cameraViewMatrix).mul(finalVert); - }); - - birdMaterial.vertexNode = birdVertexTSL(); - birdMaterial.side = THREE.DoubleSide; - - const birdMesh = new THREE.InstancedMesh(birdGeometry, birdMaterial, BIRDS); - birdMesh.rotation.y = Math.PI / 2; - birdMesh.matrixAutoUpdate = false; - birdMesh.frustumCulled = false; - birdMesh.updateMatrix(); - - // Define GPU Compute shaders. - // Shaders are computationally identical to their GLSL counterparts outside of texture destructuring. - - computeVelocity = Fn(() => { - // Define consts - const PI = float(3.141592653589793); - const PI_2 = PI.mul(2.0); - const limit = float(SPEED_LIMIT).toVar('limit'); - - // Destructure uniforms - const { alignment, separation, cohesion, deltaTime, rayOrigin, rayDirection } = effectController; - - const zoneRadius = separation.add(alignment).add(cohesion).toConst(); - const separationThresh = separation.div(zoneRadius).toConst(); - const alignmentThresh = separation.add(alignment).div(zoneRadius).toConst(); - const zoneRadiusSq = zoneRadius.mul(zoneRadius).toConst(); - - // Cache current bird's position and velocity outside the loop - const birdIndex = instanceIndex.toConst('birdIndex'); - const position = positionStorage.element(birdIndex).toVar(); - const velocity = velocityStorage.element(birdIndex).toVar(); - - // Add influence of pointer position to velocity using cached position - const directionToRay = rayOrigin.sub(position).toConst(); - const projectionLength = dot(directionToRay, rayDirection).toConst(); - const closestPoint = rayOrigin.sub(rayDirection.mul(projectionLength)).toConst(); - const directionToClosestPoint = closestPoint.sub(position).toConst(); - const distanceToClosestPoint = length(directionToClosestPoint).toConst(); - const distanceToClosestPointSq = distanceToClosestPoint.mul(distanceToClosestPoint).toConst(); - - const rayRadius = float(150.0).toConst(); - const rayRadiusSq = rayRadius.mul(rayRadius).toConst(); - - If(distanceToClosestPointSq.lessThan(rayRadiusSq), () => { - const velocityAdjust = distanceToClosestPointSq.div(rayRadiusSq).sub(1.0).mul(deltaTime).mul(100.0); - velocity.addAssign(normalize(directionToClosestPoint).mul(velocityAdjust)); - limit.addAssign(5.0); - }); - - // Attract flocks to center - const dirToCenter = position.toVar(); - dirToCenter.y.mulAssign(2.5); - velocity.subAssign(normalize(dirToCenter).mul(deltaTime).mul(5.0)); - - Loop({ start: uint(0), end: uint(BIRDS), type: 'uint', condition: '<' }, ({ i }) => { - If(i.equal(birdIndex), () => { - Continue(); - }); - - // Cache bird's position and velocity - - const birdPosition = positionStorage.element(i); - const dirToBird = birdPosition.sub(position); - const distToBird = length(dirToBird); - - If(distToBird.lessThan(0.0001), () => { - Continue(); - }); - - const distToBirdSq = distToBird.mul(distToBird); - - // Don't apply any changes to velocity if changes if the bird is outsize the zone's radius. - If(distToBirdSq.greaterThan(zoneRadiusSq), () => { - Continue(); - }); - - // Determine which threshold the bird is flying within and adjust its velocity accordingly - - const percent = distToBirdSq.div(zoneRadiusSq); - - If(percent.lessThan(separationThresh), () => { - // Separation - Move apart for comfort - const velocityAdjust = separationThresh.div(percent).sub(1.0).mul(deltaTime); - velocity.subAssign(normalize(dirToBird).mul(velocityAdjust)); - }) - .ElseIf(percent.lessThan(alignmentThresh), () => { - // Alignment - fly the same direction - const threshDelta = alignmentThresh.sub(separationThresh); - const adjustedPercent = percent.sub(separationThresh).div(threshDelta); - const birdVelocity = velocityStorage.element(i); - - const cosRange = cos(adjustedPercent.mul(PI_2)); - const cosRangeAdjust = float(0.5).sub(cosRange.mul(0.5)).add(0.5); - const velocityAdjust = cosRangeAdjust.mul(deltaTime); - velocity.addAssign(normalize(birdVelocity).mul(velocityAdjust)); - }) - .Else(() => { - // Attraction / Cohesion - move closer - const threshDelta = alignmentThresh.oneMinus(); - const adjustedPercent = threshDelta - .equal(0.0) - .select(1.0, percent.sub(alignmentThresh).div(threshDelta)); - - const cosRange = cos(adjustedPercent.mul(PI_2)); - const adj1 = cosRange.mul(-0.5); - const adj2 = adj1.add(0.5); - const adj3 = float(0.5).sub(adj2); - - const velocityAdjust = adj3.mul(deltaTime); - velocity.addAssign(normalize(dirToBird).mul(velocityAdjust)); - }); - }); - - If(length(velocity).greaterThan(limit), () => { - velocity.assign(normalize(velocity).mul(limit)); - }); - - // Write back the final velocity to storage - velocityStorage.element(birdIndex).assign(velocity); - })() - .compute(BIRDS) - .setName('Birds Velocity'); - - computePosition = Fn(() => { - const { deltaTime } = effectController; - positionStorage - .element(instanceIndex) - .addAssign(velocityStorage.element(instanceIndex).mul(deltaTime).mul(15.0)); - - const velocity = velocityStorage.element(instanceIndex); - const phase = phaseStorage.element(instanceIndex); - - const modValue = phase - .add(deltaTime) - .add(length(velocity.xz).mul(deltaTime).mul(3.0)) - .add(max(velocity.y, 0.0).mul(deltaTime).mul(6.0)); - phaseStorage.element(instanceIndex).assign(modValue.mod(62.83)); - })() - .compute(BIRDS) - .setName('Birds Position'); - - scene.add(birdMesh); - - container.style.touchAction = 'none'; - container.addEventListener('pointermove', onPointerMove); - - window.addEventListener('resize', onWindowResize); - - const gui = renderer.inspector.createParameters('Birds settings'); - gui.add(effectController.separation, 'value', 0.0, 100.0, 1.0).name('Separation'); - gui.add(effectController.alignment, 'value', 0.0, 100, 0.001).name('Alignment '); - gui.add(effectController.cohesion, 'value', 0.0, 100, 0.025).name('Cohesion'); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function onPointerMove(event) { - if (event.isPrimary === false) return; - - pointer.x = (event.clientX / window.innerWidth) * 2.0 - 1.0; - pointer.y = 1.0 - (event.clientY / window.innerHeight) * 2.0; -} - -function render() { - const now = performance.now(); - let deltaTime = (now - last) / 1000; - - if (deltaTime > 1) deltaTime = 1; // safety cap on large deltas - last = now; - - raycaster.setFromCamera(pointer, camera); - - effectController.now.value = now; - effectController.deltaTime.value = deltaTime; - effectController.rayOrigin.value.copy(raycaster.ray.origin); - effectController.rayDirection.value.copy(raycaster.ray.direction); - - renderer.compute(computeVelocity); - renderer.compute(computePosition); - - renderer.render(scene, camera); - - // Move pointer away so we only affect birds when moving the mouse - pointer.y = 10; -} - -init(); diff --git a/examples-testing/examples/webgpu_compute_cloth.ts b/examples-testing/examples/webgpu_compute_cloth.ts deleted file mode 100644 index c18af53f2..000000000 --- a/examples-testing/examples/webgpu_compute_cloth.ts +++ /dev/null @@ -1,487 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { - Fn, - If, - Return, - instancedArray, - instanceIndex, - uniform, - select, - attribute, - Loop, - float, - transformNormalToView, - cross, - triNoise3D, - time, -} from 'three/tsl'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; -import WebGPU from 'three/addons/capabilities/WebGPU.js'; - -let renderer, scene, camera, controls; - -const clothWidth = 1; -const clothHeight = 1; -const clothNumSegmentsX = 30; -const clothNumSegmentsY = 30; -const sphereRadius = 0.15; - -let vertexPositionBuffer, vertexForceBuffer, vertexParamsBuffer; -let springVertexIdBuffer, springRestLengthBuffer, springForceBuffer; -let springListBuffer; -let computeSpringForces, computeVertexForces; -let dampeningUniform, spherePositionUniform, stiffnessUniform, sphereUniform, windUniform; -let vertexWireframeObject, springWireframeObject; -let clothMesh, clothMaterial, sphere; -let timeSinceLastStep = 0; -let timestamp = 0; -const verletVertices = []; -const verletSprings = []; -const verletVertexColumns = []; - -const timer = new THREE.Timer(); -timer.connect(document); - -const params = { - wireframe: false, - sphere: true, - wind: 1.0, -}; - -const API = { - color: 0x204080, // sRGB - sheenColor: 0xffffff, // sRGB -}; - -// TODO: Fix example with WebGL backend - -if (WebGPU.isAvailable() === false) { - document.body.appendChild(WebGPU.getErrorMessage()); - - throw new Error('No WebGPU support'); -} - -init(); - -async function init() { - renderer = new THREE.WebGPURenderer({ antialias: true, requiredLimits: { maxStorageBuffersInVertexStage: 1 } }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.toneMapping = THREE.NeutralToneMapping; - renderer.toneMappingExposure = 1; - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.01, 10); - camera.position.set(-1.6, -0.1, -1.6); - - controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 1; - controls.maxDistance = 3; - controls.target.set(0, -0.1, 0); - controls.update(); - - const hdrLoader = new UltraHDRLoader().setPath('textures/equirectangular/'); - - const hdrTexture = await hdrLoader.loadAsync('royal_esplanade_2k.hdr.jpg'); - hdrTexture.mapping = THREE.EquirectangularReflectionMapping; - scene.background = hdrTexture; - scene.backgroundBlurriness = 0.5; - scene.environment = hdrTexture; - - setupCloth(); - - const gui = renderer.inspector.createParameters('Settings'); - gui.add(stiffnessUniform, 'value', 0.1, 0.5, 0.01).name('stiffness'); - gui.add(params, 'wireframe'); - gui.add(params, 'sphere'); - gui.add(params, 'wind', 0, 5, 0.1); - - const materialFolder = gui.addFolder('material'); - materialFolder.addColor(API, 'color').onChange(function (color) { - clothMaterial.color.setHex(color); - }); - materialFolder.add(clothMaterial, 'roughness', 0.0, 1, 0.01); - materialFolder.add(clothMaterial, 'sheen', 0.0, 1, 0.01); - materialFolder.add(clothMaterial, 'sheenRoughness', 0.0, 1, 0.01); - materialFolder.addColor(API, 'sheenColor').onChange(function (color) { - clothMaterial.sheenColor.setHex(color); - }); - - window.addEventListener('resize', onWindowResize); - - renderer.setAnimationLoop(render); -} - -function setupVerletGeometry() { - // this function sets up the geometry of the verlet system, a grid of vertices connected by springs - - const addVerletVertex = (x, y, z, isFixed) => { - const id = verletVertices.length; - const vertex = { - id, - position: new THREE.Vector3(x, y, z), - isFixed, - springIds: [], - }; - verletVertices.push(vertex); - return vertex; - }; - - const addVerletSpring = (vertex0, vertex1) => { - const id = verletSprings.length; - const spring = { - id, - vertex0, - vertex1, - }; - vertex0.springIds.push(id); - vertex1.springIds.push(id); - verletSprings.push(spring); - return spring; - }; - - // create the cloth's verlet vertices - for (let x = 0; x <= clothNumSegmentsX; x++) { - const column = []; - for (let y = 0; y <= clothNumSegmentsY; y++) { - const posX = x * (clothWidth / clothNumSegmentsX) - clothWidth * 0.5; - const posZ = y * (clothHeight / clothNumSegmentsY); - const isFixed = y === 0 && x % 5 === 0; // make some of the top vertices' positions fixed - const vertex = addVerletVertex(posX, clothHeight * 0.5, posZ, isFixed); - column.push(vertex); - } - - verletVertexColumns.push(column); - } - - // create the cloth's verlet springs - for (let x = 0; x <= clothNumSegmentsX; x++) { - for (let y = 0; y <= clothNumSegmentsY; y++) { - const vertex0 = verletVertexColumns[x][y]; - if (x > 0) addVerletSpring(vertex0, verletVertexColumns[x - 1][y]); - if (y > 0) addVerletSpring(vertex0, verletVertexColumns[x][y - 1]); - if (x > 0 && y > 0) addVerletSpring(vertex0, verletVertexColumns[x - 1][y - 1]); - if (x > 0 && y < clothNumSegmentsY) addVerletSpring(vertex0, verletVertexColumns[x - 1][y + 1]); - - // You can make the cloth more rigid by adding more springs between further apart vertices - //if (x > 1) addVerletSpring(vertex0, verletVertexColumns[x - 2][y]); - //if (y > 1) addVerletSpring(vertex0, verletVertexColumns[x][y - 2]); - } - } -} - -function setupVerletVertexBuffers() { - // setup the buffers holding the vertex data for the compute shaders - - const vertexCount = verletVertices.length; - - const springListArray = []; - // this springListArray will hold a list of spring ids, ordered by the id of the vertex affected by that spring. - // this is so the compute shader that accumulates the spring forces for each vertex can efficiently iterate over all springs affecting that vertex - - const vertexPositionArray = new Float32Array(vertexCount * 3); - const vertexParamsArray = new Uint32Array(vertexCount * 3); - // the params Array holds three values for each verlet vertex: - // x: isFixed, y: springCount, z: springPointer - // isFixed is 1 if the verlet is marked as immovable, 0 if not - // springCount is the number of springs connected to that vertex - // springPointer is the index of the first spring in the springListArray that is connected to that vertex - - for (let i = 0; i < vertexCount; i++) { - const vertex = verletVertices[i]; - vertexPositionArray[i * 3] = vertex.position.x; - vertexPositionArray[i * 3 + 1] = vertex.position.y; - vertexPositionArray[i * 3 + 2] = vertex.position.z; - vertexParamsArray[i * 3] = vertex.isFixed ? 1 : 0; - if (!vertex.isFixed) { - vertexParamsArray[i * 3 + 1] = vertex.springIds.length; - vertexParamsArray[i * 3 + 2] = springListArray.length; - springListArray.push(...vertex.springIds); - } - } - - vertexPositionBuffer = instancedArray(vertexPositionArray, 'vec3').setPBO(true); // setPBO(true) is only important for the WebGL Fallback - vertexForceBuffer = instancedArray(vertexCount, 'vec3'); - vertexParamsBuffer = instancedArray(vertexParamsArray, 'uvec3'); - - springListBuffer = instancedArray(new Uint32Array(springListArray), 'uint').setPBO(true); -} - -function setupVerletSpringBuffers() { - // setup the buffers holding the spring data for the compute shaders - - const springCount = verletSprings.length; - - const springVertexIdArray = new Uint32Array(springCount * 2); - const springRestLengthArray = new Float32Array(springCount); - - for (let i = 0; i < springCount; i++) { - const spring = verletSprings[i]; - springVertexIdArray[i * 2] = spring.vertex0.id; - springVertexIdArray[i * 2 + 1] = spring.vertex1.id; - springRestLengthArray[i] = spring.vertex0.position.distanceTo(spring.vertex1.position); - } - - springVertexIdBuffer = instancedArray(springVertexIdArray, 'uvec2').setPBO(true); - springRestLengthBuffer = instancedArray(springRestLengthArray, 'float'); - springForceBuffer = instancedArray(springCount * 3, 'vec3').setPBO(true); -} - -function setupUniforms() { - dampeningUniform = uniform(0.99); - spherePositionUniform = uniform(new THREE.Vector3(0, 0, 0)); - sphereUniform = uniform(1.0); - windUniform = uniform(1.0); - stiffnessUniform = uniform(0.2); -} - -function setupComputeShaders() { - // This function sets up the compute shaders for the verlet simulation - // There are two shaders that are executed for each simulation step - - const vertexCount = verletVertices.length; - const springCount = verletSprings.length; - - // 1. computeSpringForces: - // This shader computes a force for each spring, depending on the distance between the two vertices connected by that spring and the targeted rest length - computeSpringForces = Fn(() => { - const vertexIds = springVertexIdBuffer.element(instanceIndex); - const restLength = springRestLengthBuffer.element(instanceIndex); - - const vertex0Position = vertexPositionBuffer.element(vertexIds.x); - const vertex1Position = vertexPositionBuffer.element(vertexIds.y); - - const delta = vertex1Position.sub(vertex0Position).toVar(); - const dist = delta.length().max(0.000001).toVar(); - const force = dist.sub(restLength).mul(stiffnessUniform).mul(delta).mul(0.5).div(dist); - springForceBuffer.element(instanceIndex).assign(force); - })() - .compute(springCount) - .setName('Spring Forces'); - - // 2. computeVertexForces: - // This shader accumulates the force for each vertex. - // First it iterates over all springs connected to this vertex and accumulates their forces. - // Then it adds a gravital force, wind force, and the collision with the sphere. - // In the end it adds the force to the vertex' position. - computeVertexForces = Fn(() => { - const params = vertexParamsBuffer.element(instanceIndex).toVar(); - const isFixed = params.x; - const springCount = params.y; - const springPointer = params.z; - - If(isFixed, () => { - // don't need to calculate vertex forces if the vertex is set as immovable - Return(); - }); - - const position = vertexPositionBuffer.element(instanceIndex).toVar('vertexPosition'); - const force = vertexForceBuffer.element(instanceIndex).toVar('vertexForce'); - - force.mulAssign(dampeningUniform); - - const ptrStart = springPointer.toVar('ptrStart'); - const ptrEnd = ptrStart.add(springCount).toVar('ptrEnd'); - - Loop({ start: ptrStart, end: ptrEnd, type: 'uint', condition: '<' }, ({ i }) => { - const springId = springListBuffer.element(i).toVar('springId'); - const springForce = springForceBuffer.element(springId); - const springVertexIds = springVertexIdBuffer.element(springId); - const factor = select(springVertexIds.x.equal(instanceIndex), 1.0, -1.0); - force.addAssign(springForce.mul(factor)); - }); - - // gravity - force.y.subAssign(0.00005); - - // wind - const noise = triNoise3D(position, 1, time).sub(0.2).mul(0.0001); - const windForce = noise.mul(windUniform); - force.z.subAssign(windForce); - - // collision with sphere - const deltaSphere = position.add(force).sub(spherePositionUniform); - const dist = deltaSphere.length(); - const sphereForce = float(sphereRadius).sub(dist).max(0).mul(deltaSphere).div(dist).mul(sphereUniform); - force.addAssign(sphereForce); - - vertexForceBuffer.element(instanceIndex).assign(force); - vertexPositionBuffer.element(instanceIndex).addAssign(force); - })() - .compute(vertexCount) - .setName('Vertex Forces'); -} - -function setupWireframe() { - // adds helpers to visualize the verlet system - - // verlet vertex visualizer - const vertexWireframeMaterial = new THREE.SpriteNodeMaterial(); - vertexWireframeMaterial.positionNode = vertexPositionBuffer.element(instanceIndex); - vertexWireframeObject = new THREE.Mesh(new THREE.PlaneGeometry(0.01, 0.01), vertexWireframeMaterial); - vertexWireframeObject.frustumCulled = false; - vertexWireframeObject.count = verletVertices.length; - scene.add(vertexWireframeObject); - - // verlet spring visualizer - const springWireframePositionBuffer = new THREE.BufferAttribute(new Float32Array(6), 3, false); - const springWireframeIndexBuffer = new THREE.BufferAttribute(new Uint32Array([0, 1]), 1, false); - const springWireframeMaterial = new THREE.LineBasicNodeMaterial(); - springWireframeMaterial.positionNode = Fn(() => { - const vertexIds = springVertexIdBuffer.element(instanceIndex); - const vertexId = select(attribute('vertexIndex').equal(0), vertexIds.x, vertexIds.y); - return vertexPositionBuffer.element(vertexId); - })(); - - const springWireframeGeometry = new THREE.InstancedBufferGeometry(); - springWireframeGeometry.setAttribute('position', springWireframePositionBuffer); - springWireframeGeometry.setAttribute('vertexIndex', springWireframeIndexBuffer); - springWireframeGeometry.instanceCount = verletSprings.length; - - springWireframeObject = new THREE.Line(springWireframeGeometry, springWireframeMaterial); - springWireframeObject.frustumCulled = false; - springWireframeObject.count = verletSprings.length; - scene.add(springWireframeObject); -} - -function setupSphere() { - const geometry = new THREE.IcosahedronGeometry(sphereRadius * 0.95, 4); - const material = new THREE.MeshStandardNodeMaterial(); - sphere = new THREE.Mesh(geometry, material); - scene.add(sphere); -} - -function setupClothMesh() { - // This function generates a three Geometry and Mesh to render the cloth based on the verlet systems position data. - // Therefore it creates a plane mesh, in which each vertex will be centered in the center of 4 verlet vertices. - - const vertexCount = clothNumSegmentsX * clothNumSegmentsY; - const geometry = new THREE.BufferGeometry(); - - // verletVertexIdArray will hold the 4 verlet vertex ids that contribute to each geometry vertex's position - const verletVertexIdArray = new Uint32Array(vertexCount * 4); - const indices = []; - - const getIndex = (x, y) => { - return y * clothNumSegmentsX + x; - }; - - for (let x = 0; x < clothNumSegmentsX; x++) { - for (let y = 0; y < clothNumSegmentsX; y++) { - const index = getIndex(x, y); - verletVertexIdArray[index * 4] = verletVertexColumns[x][y].id; - verletVertexIdArray[index * 4 + 1] = verletVertexColumns[x + 1][y].id; - verletVertexIdArray[index * 4 + 2] = verletVertexColumns[x][y + 1].id; - verletVertexIdArray[index * 4 + 3] = verletVertexColumns[x + 1][y + 1].id; - - if (x > 0 && y > 0) { - indices.push(getIndex(x, y), getIndex(x - 1, y), getIndex(x - 1, y - 1)); - indices.push(getIndex(x, y), getIndex(x - 1, y - 1), getIndex(x, y - 1)); - } - } - } - - const verletVertexIdBuffer = new THREE.BufferAttribute(verletVertexIdArray, 4, false); - const positionBuffer = new THREE.BufferAttribute(new Float32Array(vertexCount * 3), 3, false); - geometry.setAttribute('position', positionBuffer); - geometry.setAttribute('vertexIds', verletVertexIdBuffer); - geometry.setIndex(indices); - - clothMaterial = new THREE.MeshPhysicalNodeMaterial({ - color: new THREE.Color().setHex(API.color), - side: THREE.DoubleSide, - transparent: true, - opacity: 0.85, - sheen: 1.0, - sheenRoughness: 0.5, - sheenColor: new THREE.Color().setHex(API.sheenColor), - }); - - clothMaterial.positionNode = Fn(({ material }) => { - // gather the position of the 4 verlet vertices and calculate the center position and normal from that - const vertexIds = attribute('vertexIds'); - const v0 = vertexPositionBuffer.element(vertexIds.x).toVar(); - const v1 = vertexPositionBuffer.element(vertexIds.y).toVar(); - const v2 = vertexPositionBuffer.element(vertexIds.z).toVar(); - const v3 = vertexPositionBuffer.element(vertexIds.w).toVar(); - - const top = v0.add(v1); - const right = v1.add(v3); - const bottom = v2.add(v3); - const left = v0.add(v2); - - const tangent = right.sub(left).normalize(); - const bitangent = bottom.sub(top).normalize(); - - const normal = cross(tangent, bitangent); - - // send the normalView from the vertex shader to the fragment shader - material.normalNode = transformNormalToView(normal).toVarying(); - - return v0.add(v1).add(v2).add(v3).mul(0.25); - })(); - - clothMesh = new THREE.Mesh(geometry, clothMaterial); - clothMesh.frustumCulled = false; - scene.add(clothMesh); -} - -function setupCloth() { - setupVerletGeometry(); - setupVerletVertexBuffers(); - setupVerletSpringBuffers(); - setupUniforms(); - setupComputeShaders(); - setupWireframe(); - setupSphere(); - setupClothMesh(); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function updateSphere() { - sphere.position.set(Math.sin(timestamp * 2.1) * 0.1, 0, Math.sin(timestamp * 0.8)); - spherePositionUniform.value.copy(sphere.position); -} - -async function render() { - timer.update(); - - sphere.visible = params.sphere; - sphereUniform.value = params.sphere ? 1 : 0; - windUniform.value = params.wind; - clothMesh.visible = !params.wireframe; - vertexWireframeObject.visible = params.wireframe; - springWireframeObject.visible = params.wireframe; - - const deltaTime = Math.min(timer.getDelta(), 1 / 60); // don't advance the time too far, for example when the window is out of focus - const stepsPerSecond = 360; // ensure the same amount of simulation steps per second on all systems, independent of refresh rate - const timePerStep = 1 / stepsPerSecond; - - timeSinceLastStep += deltaTime; - - while (timeSinceLastStep >= timePerStep) { - // run a verlet system simulation step - timestamp += timePerStep; - timeSinceLastStep -= timePerStep; - updateSphere(); - renderer.compute(computeSpringForces); - renderer.compute(computeVertexForces); - } - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_compute_geometry.ts b/examples-testing/examples/webgpu_compute_geometry.ts deleted file mode 100644 index 9299e215b..000000000 --- a/examples-testing/examples/webgpu_compute_geometry.ts +++ /dev/null @@ -1,190 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { - vec4, - storage, - Fn, - If, - uniform, - instanceIndex, - objectWorldMatrix, - color, - screenUV, - attribute, -} from 'three/tsl'; - -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer; -let raycaster, pointer; - -let mesh; - -const pointerPosition = uniform(vec4(0)); -const elasticity = uniform(0.4); // elasticity ( how "strong" the spring is ) -const damping = uniform(0.94); // damping factor ( energy loss ) -const brushSize = uniform(0.25); -const brushStrength = uniform(0.22); - -init(); - -const jelly = Fn(({ renderer, geometry, object }) => { - const count = geometry.attributes.position.count; - - // Create storage buffer attribute for modified position. - - const positionBaseAttribute = geometry.attributes.position; - const positionStorageBufferAttribute = new THREE.StorageBufferAttribute(count, 3); - const speedBufferAttribute = new THREE.StorageBufferAttribute(count, 3); - - geometry.setAttribute('storagePosition', positionStorageBufferAttribute); - - // Attributes - - const positionAttribute = storage(positionBaseAttribute, 'vec3', count); - const positionStorageAttribute = storage(positionStorageBufferAttribute, 'vec3', count); - - const speedAttribute = storage(speedBufferAttribute, 'vec3', count); - - // Vectors - - // Base vec3 position of the mesh vertices. - const basePosition = positionAttribute.element(instanceIndex); - // Mesh vertices after compute modification. - const currentPosition = positionStorageAttribute.element(instanceIndex); - // Speed of each mesh vertex. - const currentSpeed = speedAttribute.element(instanceIndex); - - // - - const computeInit = Fn(() => { - // Modified storage position starts out the same as the base position. - - currentPosition.assign(basePosition); - })().compute(count); - - // - - const computeUpdate = Fn(() => { - // pinch - - If(pointerPosition.w.equal(1), () => { - const worldPosition = objectWorldMatrix(object).mul(currentPosition); - - const dist = worldPosition.distance(pointerPosition.xyz); - const direction = pointerPosition.xyz.sub(worldPosition).normalize(); - - const power = brushSize.sub(dist).max(0).mul(brushStrength); - - currentPosition.addAssign(direction.mul(power)); - }); - - // compute ( jelly ) - - const distance = basePosition.distance(currentPosition); - const force = elasticity.mul(distance).mul(basePosition.sub(currentPosition)); - - currentSpeed.addAssign(force); - currentSpeed.mulAssign(damping); - - currentPosition.addAssign(currentSpeed); - })() - .compute(count) - .setName('Update Jelly'); - - // initialize the storage buffer with the base position - - computeUpdate.onInit(() => renderer.compute(computeInit)); - - // - - return computeUpdate; -}); - -function init() { - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 10); - camera.position.set(0, 0, 1); - - scene = new THREE.Scene(); - - raycaster = new THREE.Raycaster(); - pointer = new THREE.Vector2(); - - // background - - const bgColor = screenUV.y.mix(color(0x9f87f7), color(0xf2cdcd)); - const bgVignette = screenUV.distance(0.5).remapClamp(0.3, 0.8).oneMinus(); - const bgIntensity = 4; - - scene.backgroundNode = bgColor.mul(bgVignette.mul(color(0xa78ff6).mul(bgIntensity))); - - // model - - new GLTFLoader().load('models/gltf/LeePerrySmith/LeePerrySmith.glb', function (gltf) { - // create jelly effect material - - const material = new THREE.MeshNormalNodeMaterial(); - material.geometryNode = jelly(); - material.positionNode = attribute('storagePosition'); - - // apply the material to the mesh - - mesh = gltf.scene.children[0]; - mesh.scale.setScalar(0.1); - mesh.material = material; - scene.add(mesh); - }); - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 0.7; - controls.maxDistance = 2; - - const gui = renderer.inspector.createParameters('Settings'); - gui.add(elasticity, 'value', 0, 0.5).name('elasticity'); - gui.add(damping, 'value', 0.9, 0.98).name('damping'); - gui.add(brushSize, 'value', 0.1, 0.5).name('brush size'); - gui.add(brushStrength, 'value', 0.1, 0.3).name('brush strength'); - - window.addEventListener('resize', onWindowResize); - window.addEventListener('pointermove', onPointerMove); -} - -function onPointerMove(event) { - pointer.set((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1); - - raycaster.setFromCamera(pointer, camera); - - const intersects = raycaster.intersectObject(scene); - - if (intersects.length > 0) { - const intersect = intersects[0]; - - pointerPosition.value.copy(intersect.point); - pointerPosition.value.w = 1; // enable - } else { - pointerPosition.value.w = 0; // disable - } -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -async function animate() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_compute_particles.ts b/examples-testing/examples/webgpu_compute_particles.ts deleted file mode 100644 index 71fbd6622..000000000 --- a/examples-testing/examples/webgpu_compute_particles.ts +++ /dev/null @@ -1,225 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { Fn, If, uniform, float, uv, vec3, hash, shapeCircle, instancedArray, instanceIndex } from 'three/tsl'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -const particleCount = 200000; - -const gravity = uniform(-0.00098); -const bounce = uniform(0.8); -const friction = uniform(0.99); -const size = uniform(0.12); - -const clickPosition = uniform(new THREE.Vector3()); - -let camera, scene, renderer; -let controls; -let computeParticles; - -let isOrbitControlsActive; - -init(); - -async function init() { - const { innerWidth, innerHeight } = window; - - camera = new THREE.PerspectiveCamera(50, innerWidth / innerHeight, 0.1, 1000); - camera.position.set(0, 5, 20); - - scene = new THREE.Scene(); - - // - - const positions = instancedArray(particleCount, 'vec3'); - const velocities = instancedArray(particleCount, 'vec3'); - const colors = instancedArray(particleCount, 'vec3'); - - // compute - - const separation = 0.2; - const amount = Math.sqrt(particleCount); - const offset = float(amount / 2); - - const computeInit = Fn(() => { - const position = positions.element(instanceIndex); - const color = colors.element(instanceIndex); - - const x = instanceIndex.mod(amount); - const z = instanceIndex.div(amount); - - position.x = offset.sub(x).mul(separation); - position.z = offset.sub(z).mul(separation); - - color.x = hash(instanceIndex); - color.y = hash(instanceIndex.add(2)); - })() - .compute(particleCount) - .setName('Init Particles'); - - // - - const computeUpdate = Fn(() => { - const position = positions.element(instanceIndex); - const velocity = velocities.element(instanceIndex); - - velocity.addAssign(vec3(0.0, gravity, 0.0)); - position.addAssign(velocity); - - velocity.mulAssign(friction); - - // floor - - If(position.y.lessThan(0), () => { - position.y = 0; - velocity.y = velocity.y.negate().mul(bounce); - - // floor friction - - velocity.x = velocity.x.mul(0.9); - velocity.z = velocity.z.mul(0.9); - }); - }); - - computeParticles = computeUpdate().compute(particleCount).setName('Update Particles'); - - // create particles - - const material = new THREE.SpriteNodeMaterial(); - material.colorNode = uv().mul(colors.element(instanceIndex)); - material.positionNode = positions.toAttribute(); - material.scaleNode = size; - material.opacityNode = shapeCircle(); - material.alphaToCoverage = true; - material.transparent = true; - - const particles = new THREE.Sprite(material); - particles.count = particleCount; - particles.frustumCulled = false; - scene.add(particles); - - // - - const helper = new THREE.GridHelper(90, 45, 0x303030, 0x303030); - scene.add(helper); - - const geometry = new THREE.PlaneGeometry(200, 200); - geometry.rotateX(-Math.PI / 2); - - const plane = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ visible: false })); - scene.add(plane); - - const raycaster = new THREE.Raycaster(); - const pointer = new THREE.Vector2(); - - // - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - await renderer.init(); - - // - - renderer.compute(computeInit); - - // Hit - - const computeHit = Fn(() => { - const position = positions.element(instanceIndex); - const velocity = velocities.element(instanceIndex); - - const dist = position.distance(clickPosition); - const direction = position.sub(clickPosition).normalize(); - const distArea = float(3).sub(dist).max(0); - - const power = distArea.mul(0.01); - const relativePower = power.mul(hash(instanceIndex).mul(1.5).add(0.5)); - - velocity.assign(velocity.add(direction.mul(relativePower))); - })() - .compute(particleCount) - .setName('Hit Particles'); - - // - - function onMove(event) { - if (isOrbitControlsActive) return; - - pointer.set((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1); - - raycaster.setFromCamera(pointer, camera); - - const intersects = raycaster.intersectObject(plane, false); - - if (intersects.length > 0) { - const { point } = intersects[0]; - - // move to uniform - - clickPosition.value.copy(point); - clickPosition.value.y = -1; - - // compute - - renderer.compute(computeHit); - } - } - - renderer.domElement.addEventListener('pointermove', onMove); - - // controls - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - controls.minDistance = 5; - controls.maxDistance = 200; - controls.target.set(0, -8, 0); - controls.update(); - - controls.addEventListener('start', () => { - isOrbitControlsActive = true; - }); - controls.addEventListener('end', () => { - isOrbitControlsActive = false; - }); - - controls.touches = { - ONE: null, - TWO: THREE.TOUCH.DOLLY_PAN, - }; - - // - - window.addEventListener('resize', onWindowResize); - - // gui - - const gui = renderer.inspector.createParameters('Settings'); - - gui.add(gravity, 'value', -0.0098, 0, 0.0001).name('gravity'); - gui.add(bounce, 'value', 0.1, 1, 0.01).name('bounce'); - gui.add(friction, 'value', 0.96, 0.99, 0.01).name('friction'); - gui.add(size, 'value', 0.12, 0.5, 0.01).name('size'); -} - -function onWindowResize() { - const { innerWidth, innerHeight } = window; - - camera.aspect = innerWidth / innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(innerWidth, innerHeight); -} - -function animate() { - controls.update(); - - renderer.compute(computeParticles); - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_compute_particles_rain.ts b/examples-testing/examples/webgpu_compute_particles_rain.ts deleted file mode 100644 index eaa4e957e..000000000 --- a/examples-testing/examples/webgpu_compute_particles_rain.ts +++ /dev/null @@ -1,329 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { - Fn, - texture, - uv, - uint, - instancedArray, - positionWorld, - billboarding, - time, - hash, - deltaTime, - vec2, - instanceIndex, - positionGeometry, - If, -} from 'three/tsl'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js'; - -const maxParticleCount = 50000; -const instanceCount = maxParticleCount / 2; - -let camera, scene, renderer; -let controls; -let computeParticles; -let monkey; -let timer; - -let collisionBox, collisionCamera, collisionPosRT, collisionPosMaterial; -let collisionBoxPos, collisionBoxPosUI; - -init(); - -async function init() { - const { innerWidth, innerHeight } = window; - - camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 0.1, 110); - camera.position.set(40, 8, 0); - camera.lookAt(0, 0, 0); - - scene = new THREE.Scene(); - - const dirLight = new THREE.DirectionalLight(0xffffff, 0.5); - dirLight.position.set(3, 17, 17); - scene.add(dirLight); - - scene.add(new THREE.AmbientLight(0x111111)); - - // - - collisionCamera = new THREE.OrthographicCamera(-50, 50, 50, -50, 0.1, 50); - collisionCamera.position.y = 50; - collisionCamera.lookAt(0, 0, 0); - collisionCamera.layers.disableAll(); - collisionCamera.layers.enable(1); - - collisionPosRT = new THREE.RenderTarget(1024, 1024); - collisionPosRT.texture.type = THREE.HalfFloatType; - collisionPosRT.texture.magFilter = THREE.NearestFilter; - collisionPosRT.texture.minFilter = THREE.NearestFilter; - collisionPosRT.texture.generateMipmaps = false; - - collisionPosMaterial = new THREE.MeshBasicNodeMaterial(); - collisionPosMaterial.colorNode = positionWorld; - - // - - const positionBuffer = instancedArray(maxParticleCount, 'vec3'); - const velocityBuffer = instancedArray(maxParticleCount, 'vec3'); - const ripplePositionBuffer = instancedArray(maxParticleCount, 'vec3'); - const rippleTimeBuffer = instancedArray(maxParticleCount, 'vec3'); - - // compute - - const randUint = () => uint(Math.random() * 0xffffff); - - const computeInit = Fn(() => { - const position = positionBuffer.element(instanceIndex); - const velocity = velocityBuffer.element(instanceIndex); - const rippleTime = rippleTimeBuffer.element(instanceIndex); - - const randX = hash(instanceIndex); - const randY = hash(instanceIndex.add(randUint())); - const randZ = hash(instanceIndex.add(randUint())); - - position.x = randX.mul(100).add(-50); - position.y = randY.mul(25); - position.z = randZ.mul(100).add(-50); - - velocity.y = randX.mul(-0.04).add(-0.2); - - rippleTime.x = 1000; - })().compute(maxParticleCount); - - // - - const computeUpdate = Fn(() => { - const getCoord = pos => pos.add(50).div(100); - - const position = positionBuffer.element(instanceIndex); - const velocity = velocityBuffer.element(instanceIndex); - const ripplePosition = ripplePositionBuffer.element(instanceIndex); - const rippleTime = rippleTimeBuffer.element(instanceIndex); - - position.addAssign(velocity); - - rippleTime.x = rippleTime.x.add(deltaTime.mul(4)); - - // - - const collisionArea = texture(collisionPosRT.texture, getCoord(position.xz)); - - const surfaceOffset = 0.05; - - const floorPosition = collisionArea.y.add(surfaceOffset); - - // floor - - const ripplePivotOffsetY = -0.9; - - If(position.y.add(ripplePivotOffsetY).lessThan(floorPosition), () => { - position.y = 25; - - ripplePosition.xz = position.xz; - ripplePosition.y = floorPosition; - - // reset hit time: x = time - - rippleTime.x = 1; - - // next drops will not fall in the same place - - position.x = hash(instanceIndex.add(time)).mul(100).add(-50); - position.z = hash(instanceIndex.add(time.add(randUint()))) - .mul(100) - .add(-50); - }); - - const rippleOnSurface = texture(collisionPosRT.texture, getCoord(ripplePosition.xz)); - - const rippleFloorArea = rippleOnSurface.y.add(surfaceOffset); - - If(ripplePosition.y.greaterThan(rippleFloorArea), () => { - rippleTime.x = 1000; - }); - }); - - computeParticles = computeUpdate().compute(maxParticleCount).setName('Particles'); - - // rain - - const rainMaterial = new THREE.MeshBasicNodeMaterial(); - rainMaterial.colorNode = uv().distance(vec2(0.5, 0)).oneMinus().mul(3).exp().mul(0.1); - rainMaterial.vertexNode = billboarding({ position: positionBuffer.toAttribute() }); - rainMaterial.opacity = 0.2; - rainMaterial.side = THREE.DoubleSide; - rainMaterial.forceSinglePass = true; - rainMaterial.depthWrite = false; - rainMaterial.depthTest = true; - rainMaterial.transparent = true; - - const rainParticles = new THREE.Mesh(new THREE.PlaneGeometry(0.1, 2), rainMaterial); - rainParticles.count = instanceCount; - scene.add(rainParticles); - - // ripple - - const rippleTime = rippleTimeBuffer.element(instanceIndex).x; - - const rippleEffect = Fn(() => { - const center = uv().add(vec2(-0.5)).length().mul(7); - const distance = rippleTime.sub(center); - - return distance.min(1).sub(distance.max(1).sub(1)); - }); - - const rippleMaterial = new THREE.MeshBasicNodeMaterial(); - rippleMaterial.colorNode = rippleEffect(); - rippleMaterial.positionNode = positionGeometry.add(ripplePositionBuffer.toAttribute()); - rippleMaterial.opacityNode = rippleTime.mul(0.3).oneMinus().max(0).mul(0.5); - rippleMaterial.side = THREE.DoubleSide; - rippleMaterial.forceSinglePass = true; - rippleMaterial.depthWrite = false; - rippleMaterial.depthTest = true; - rippleMaterial.transparent = true; - - // ripple geometry - - const surfaceRippleGeometry = new THREE.PlaneGeometry(2.5, 2.5); - surfaceRippleGeometry.rotateX(-Math.PI / 2); - - const xRippleGeometry = new THREE.PlaneGeometry(1, 2); - xRippleGeometry.rotateY(-Math.PI / 2); - - const zRippleGeometry = new THREE.PlaneGeometry(1, 2); - - const rippleGeometry = BufferGeometryUtils.mergeGeometries([ - surfaceRippleGeometry, - xRippleGeometry, - zRippleGeometry, - ]); - - const rippleParticles = new THREE.Mesh(rippleGeometry, rippleMaterial); - rippleParticles.count = instanceCount; - scene.add(rippleParticles); - - // floor geometry - - const floorGeometry = new THREE.PlaneGeometry(1000, 1000); - floorGeometry.rotateX(-Math.PI / 2); - - const plane = new THREE.Mesh(floorGeometry, new THREE.MeshBasicMaterial({ color: 0x050505 })); - scene.add(plane); - - // - - collisionBox = new THREE.Mesh(new THREE.BoxGeometry(30, 1, 15), new THREE.MeshStandardMaterial()); - collisionBox.material.color.set(0x333333); - collisionBox.position.y = 12; - collisionBox.scale.x = 3.5; - collisionBox.layers.enable(1); - scene.add(collisionBox); - - // - - const loader = new THREE.BufferGeometryLoader(); - loader.load('models/json/suzanne_buffergeometry.json', function (geometry) { - geometry.computeVertexNormals(); - - monkey = new THREE.Mesh(geometry, new THREE.MeshStandardMaterial({ roughness: 1, metalness: 0 })); - monkey.scale.setScalar(5); - monkey.rotation.y = Math.PI / 2; - monkey.position.y = 4.5; - monkey.layers.enable(1); // add to collision layer - - scene.add(monkey); - }); - - // - - timer = new THREE.Timer(); - timer.connect(document); - - // - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - await renderer.init(); - - // - - renderer.compute(computeInit); - - // - - controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 5; - controls.maxDistance = 50; - controls.update(); - - // - - window.addEventListener('resize', onWindowResize); - - // gui - - const gui = renderer.inspector.createParameters('Settings'); - - // use lerp to smooth the movement - collisionBoxPosUI = new THREE.Vector3().copy(collisionBox.position); - collisionBoxPos = new THREE.Vector3(); - - gui.add(collisionBoxPosUI, 'z', -50, 50, 0.001).name('position'); - gui.add(collisionBox.scale, 'x', 0.1, 3.5, 0.01).name('scale'); - gui.add(rainParticles, 'count', 200, maxParticleCount, 1) - .name('drop count') - .onChange(v => (rippleParticles.count = v)); -} - -function onWindowResize() { - const { innerWidth, innerHeight } = window; - - camera.aspect = innerWidth / innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(innerWidth, innerHeight); -} - -function animate() { - timer.update(); - - const delta = timer.getDelta(); - - if (monkey) { - monkey.rotation.y += delta; - } - - collisionBoxPos.set(collisionBoxPosUI.x, collisionBoxPosUI.y, -collisionBoxPosUI.z); - - collisionBox.position.lerp(collisionBoxPos, 10 * delta); - - // position - - scene.overrideMaterial = collisionPosMaterial; - scene.name = 'Collision Scene'; - renderer.setRenderTarget(collisionPosRT); - renderer.render(scene, collisionCamera); - - // compute - - renderer.compute(computeParticles); - - // result - - scene.overrideMaterial = null; - scene.name = 'Scene'; - renderer.setRenderTarget(null); - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_compute_particles_snow.ts b/examples-testing/examples/webgpu_compute_particles_snow.ts deleted file mode 100644 index e00fe068f..000000000 --- a/examples-testing/examples/webgpu_compute_particles_snow.ts +++ /dev/null @@ -1,328 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { - Fn, - texture, - vec3, - pass, - color, - uint, - screenUV, - instancedArray, - positionWorld, - positionLocal, - time, - vec2, - hash, - instanceIndex, - If, -} from 'three/tsl'; -import { gaussianBlur } from 'three/addons/tsl/display/GaussianBlurNode.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { TeapotGeometry } from 'three/addons/geometries/TeapotGeometry.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -const maxParticleCount = 100000; - -let camera, scene, renderer; -let controls; -let computeParticles; -let renderPipeline; - -let collisionCamera, collisionPosRT, collisionPosMaterial; - -init(); - -async function init() { - const { innerWidth, innerHeight } = window; - - camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 0.1, 100); - camera.position.set(20, 2, 20); - camera.layers.enable(2); - camera.lookAt(0, 40, 0); - - scene = new THREE.Scene(); - scene.fog = new THREE.Fog(0x0f3c37, 5, 40); - - const dirLight = new THREE.DirectionalLight(0xf9ff9b, 9); - dirLight.position.set(10, 10, 0); - scene.add(dirLight); - - scene.add(new THREE.HemisphereLight(0x0f3c37, 0x080d10, 100)); - - // - - collisionCamera = new THREE.OrthographicCamera(-50, 50, 50, -50, 0.1, 50); - collisionCamera.position.y = 50; - collisionCamera.lookAt(0, 0, 0); - collisionCamera.layers.enable(1); - - collisionPosRT = new THREE.RenderTarget(1024, 1024); - collisionPosRT.texture.type = THREE.HalfFloatType; - collisionPosRT.texture.magFilter = THREE.NearestFilter; - collisionPosRT.texture.minFilter = THREE.NearestFilter; - collisionPosRT.texture.generateMipmaps = false; - - collisionPosMaterial = new THREE.MeshBasicNodeMaterial(); - collisionPosMaterial.fog = false; - collisionPosMaterial.toneMapped = false; - collisionPosMaterial.colorNode = positionWorld.y; - - // - - const positionBuffer = instancedArray(maxParticleCount, 'vec3'); - const scaleBuffer = instancedArray(maxParticleCount, 'vec3'); - const staticPositionBuffer = instancedArray(maxParticleCount, 'vec3'); - const dataBuffer = instancedArray(maxParticleCount, 'vec4'); - - // compute - - const randUint = () => uint(Math.random() * 0xffffff); - - const computeInit = Fn(() => { - const position = positionBuffer.element(instanceIndex); - const scale = scaleBuffer.element(instanceIndex); - const particleData = dataBuffer.element(instanceIndex); - - const randX = hash(instanceIndex); - const randY = hash(instanceIndex.add(randUint())); - const randZ = hash(instanceIndex.add(randUint())); - - position.x = randX.mul(100).add(-50); - position.y = randY.mul(500).add(3); - position.z = randZ.mul(100).add(-50); - - scale.xyz = hash(instanceIndex.add(Math.random())).mul(0.8).add(0.2); - - staticPositionBuffer.element(instanceIndex).assign(vec3(1000, 10000, 1000)); - - particleData.y = randY.mul(-0.1).add(-0.02); - - particleData.x = position.x; - particleData.z = position.z; - particleData.w = randX; - })() - .compute(maxParticleCount) - .setName('Init Particles'); - - // - - const surfaceOffset = 0.2; - const speed = 0.4; - - const computeUpdate = Fn(() => { - const getCoord = pos => pos.add(50).div(100); - - const position = positionBuffer.element(instanceIndex); - const scale = scaleBuffer.element(instanceIndex); - const particleData = dataBuffer.element(instanceIndex); - - const velocity = particleData.y; - const random = particleData.w; - - const rippleOnSurface = texture(collisionPosRT.texture, getCoord(position.xz)).toInspector( - 'Collision Test', - () => { - return texture(collisionPosRT.texture).y; // .div( collisionCamera.position.y ); - }, - ); - - const rippleFloorArea = rippleOnSurface.y.add(scale.x.mul(surfaceOffset)); - - If(position.y.greaterThan(rippleFloorArea), () => { - position.x = particleData.x.add(time.mul(random.mul(random)).mul(speed).sin().mul(3)); - position.z = particleData.z.add(time.mul(random).mul(speed).cos().mul(random.mul(10))); - - position.y = position.y.add(velocity); - }).Else(() => { - staticPositionBuffer.element(instanceIndex).assign(position); - }); - }); - - computeParticles = computeUpdate().compute(maxParticleCount); - computeParticles.name = 'Update Particles'; - - // rain - - const geometry = new THREE.SphereGeometry(surfaceOffset, 5, 5); - - function particle(staticParticles) { - const posBuffer = staticParticles ? staticPositionBuffer : positionBuffer; - const layer = staticParticles ? 1 : 2; - - const staticMaterial = new THREE.MeshStandardNodeMaterial({ - color: 0xeeeeee, - roughness: 0.9, - metalness: 0, - }); - - staticMaterial.positionNode = positionLocal.mul(scaleBuffer.toAttribute()).add(posBuffer.toAttribute()); - - const rainParticles = new THREE.Mesh(geometry, staticMaterial); - rainParticles.count = maxParticleCount; - rainParticles.layers.disableAll(); - rainParticles.layers.enable(layer); - - return rainParticles; - } - - const dynamicParticles = particle(); - const staticParticles = particle(true); - - scene.add(dynamicParticles); - scene.add(staticParticles); - - // floor geometry - - const floorGeometry = new THREE.PlaneGeometry(100, 100); - floorGeometry.rotateX(-Math.PI / 2); - - const plane = new THREE.Mesh( - floorGeometry, - new THREE.MeshStandardMaterial({ - color: 0x0c1e1e, - roughness: 0.5, - metalness: 0, - transparent: true, - }), - ); - - plane.material.opacityNode = positionLocal.xz.mul(0.05).distance(0).saturate().oneMinus(); - - scene.add(plane); - - // tree - - function tree(count = 8) { - const coneMaterial = new THREE.MeshStandardNodeMaterial({ - color: 0x0d492c, - roughness: 0.6, - metalness: 0, - }); - - const object = new THREE.Group(); - - for (let i = 0; i < count; i++) { - const radius = 1 + i; - - const coneGeometry = new THREE.ConeGeometry(radius * 0.95, radius * 1.25, 32); - - const cone = new THREE.Mesh(coneGeometry, coneMaterial); - cone.position.y = (count - i) * 1.5 + count * 0.6; - object.add(cone); - } - - const geometry = new THREE.CylinderGeometry(1, 1, count, 32); - const cone = new THREE.Mesh(geometry, coneMaterial); - cone.position.y = count / 2; - object.add(cone); - - return object; - } - - const teapotTree = new THREE.Mesh( - new TeapotGeometry(0.5, 18), - new THREE.MeshBasicNodeMaterial({ - color: 0xfcfb9e, - }), - ); - - teapotTree.name = 'Teapot Pass'; - teapotTree.position.y = 18; - - scene.add(tree()); - scene.add(teapotTree); - - // - - scene.backgroundNode = screenUV.distance(0.5).mul(2).mix(color(0x0f4140), color(0x060a0d)); - - // - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - await renderer.init(); - - // - - controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, 10, 0); - controls.minDistance = 25; - controls.maxDistance = 35; - controls.maxPolarAngle = Math.PI / 1.7; - controls.autoRotate = true; - controls.autoRotateSpeed = -0.7; - controls.update(); - - // post processing - - const scenePass = pass(scene, camera); - const scenePassColor = scenePass.getTextureNode(); - const vignette = screenUV.distance(0.5).mul(1.35).clamp().oneMinus(); - - const teapotTreePass = pass(teapotTree, camera).getTextureNode(); - const teapotTreePassBlurred = gaussianBlur(teapotTreePass, vec2(1), 6); - teapotTreePassBlurred.resolutionScale = 0.2; - - const scenePassColorBlurred = gaussianBlur(scenePassColor); - scenePassColorBlurred.resolutionScale = 0.5; - scenePassColorBlurred.directionNode = vec2(1); - - // compose - - let totalPass = scenePass.toInspector('Scene'); - totalPass = totalPass.add(scenePassColorBlurred.mul(0.1)); - totalPass = totalPass.mul(vignette); - totalPass = totalPass.add(teapotTreePass.mul(10).add(teapotTreePassBlurred).toInspector('Teapot Blur')); - - renderPipeline = new THREE.RenderPipeline(renderer); - renderPipeline.outputNode = totalPass; - - // - - renderer.compute(computeInit); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - const { innerWidth, innerHeight } = window; - - camera.aspect = innerWidth / innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(innerWidth, innerHeight); -} - -function animate() { - controls.update(); - - // position - - scene.name = 'Collider Position'; - scene.overrideMaterial = collisionPosMaterial; - renderer.setRenderTarget(collisionPosRT); - renderer.render(scene, collisionCamera); - - // compute - - renderer.compute(computeParticles); - - // result - - scene.name = 'Scene'; - scene.overrideMaterial = null; - renderer.setRenderTarget(null); - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_compute_points.ts b/examples-testing/examples/webgpu_compute_points.ts deleted file mode 100644 index eab1d9f61..000000000 --- a/examples-testing/examples/webgpu_compute_points.ts +++ /dev/null @@ -1,124 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { Fn, uniform, instancedArray, float, vec2, color, instanceIndex } from 'three/tsl'; - -let camera, scene, renderer; -let computeNode; - -const pointerVector = new THREE.Vector2(-10.0, -10.0); // Out of bounds first -const scaleVector = new THREE.Vector2(1, 1); - -init(); - -async function init() { - camera = new THREE.OrthographicCamera(-1.0, 1.0, 1.0, -1.0, 0, 1); - camera.position.z = 1; - - scene = new THREE.Scene(); - - // initialize particles - - const particlesCount = 300000; - - const particleArray = instancedArray(particlesCount, 'vec2'); - const velocityArray = instancedArray(particlesCount, 'vec2'); - - // create function - - const computeShaderFn = Fn(() => { - const particle = particleArray.element(instanceIndex); - const velocity = velocityArray.element(instanceIndex); - - const pointer = uniform(pointerVector); - const limit = uniform(scaleVector); - - const position = particle.add(velocity).toVar(); - - velocity.x = position.x.abs().greaterThanEqual(limit.x).select(velocity.x.negate(), velocity.x); - velocity.y = position.y.abs().greaterThanEqual(limit.y).select(velocity.y.negate(), velocity.y); - - position.assign(position.min(limit).max(limit.negate())); - - const pointerSize = 0.1; - const distanceFromPointer = pointer.sub(position).length(); - - particle.assign(distanceFromPointer.lessThanEqual(pointerSize).select(vec2(), position)); - }); - - // compute - - computeNode = computeShaderFn().compute(particlesCount).setName('Update Particles'); - computeNode.onInit(({ renderer }) => { - const precomputeShaderNode = Fn(() => { - const particleIndex = float(instanceIndex); - - const randomAngle = particleIndex.mul(0.005).mul(Math.PI * 2); - const randomSpeed = particleIndex.mul(0.00000001).add(0.0000001); - - const velX = randomAngle.sin().mul(randomSpeed); - const velY = randomAngle.cos().mul(randomSpeed); - - const velocity = velocityArray.element(instanceIndex); - - velocity.xy = vec2(velX, velY); - }); - - renderer.compute(precomputeShaderNode().compute(particlesCount)); - }); - - // use a compute shader to animate the point cloud's vertex data. - - const pointsGeometry = new THREE.BufferGeometry(); - pointsGeometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(3), 3)); // single vertex ( not triangle ) - pointsGeometry.drawRange.count = 1; // force render points as instances ( not triangle ) - - const pointsMaterial = new THREE.PointsNodeMaterial(); - pointsMaterial.colorNode = particleArray.element(instanceIndex).add(color(0xffffff)); - pointsMaterial.positionNode = particleArray.element(instanceIndex); - - const mesh = new THREE.Points(pointsGeometry, pointsMaterial); - mesh.count = particlesCount; - scene.add(mesh); - - renderer = new THREE.WebGPURenderer({ antialias: true, requiredLimits: { maxStorageBuffersInVertexStage: 1 } }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - await renderer.init(); - - window.addEventListener('resize', onWindowResize); - window.addEventListener('mousemove', onMouseMove); - - // gui - - const gui = renderer.inspector.createParameters('Settings'); - - gui.add(scaleVector, 'x', 0, 1, 0.01); - gui.add(scaleVector, 'y', 0, 1, 0.01); -} - -function onWindowResize() { - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function onMouseMove(event) { - const x = event.clientX; - const y = event.clientY; - - const width = window.innerWidth; - const height = window.innerHeight; - - pointerVector.set((x / width - 0.5) * 2.0, (-y / height + 0.5) * 2.0); -} - -function animate() { - renderer.compute(computeNode); - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_compute_sort_bitonic.ts b/examples-testing/examples/webgpu_compute_sort_bitonic.ts deleted file mode 100644 index 9a37a9ece..000000000 --- a/examples-testing/examples/webgpu_compute_sort_bitonic.ts +++ /dev/null @@ -1,457 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { storage, If, vec3, not, uniform, uv, uint, Fn, vec2, abs, int, uvec2, floor, instanceIndex } from 'three/tsl'; - -import { BitonicSort, getBitonicDisperseIndices, getBitonicFlipIndices } from 'three/addons/gpgpu/BitonicSort.js'; - -import WebGPU from 'three/addons/capabilities/WebGPU.js'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -const StepType = { - NONE: 0, - // Swap values within workgroup local values - SWAP_LOCAL: 1, - DISPERSE_LOCAL: 2, - // Swap values within global data buffer. - FLIP_GLOBAL: 3, - DISPERSE_GLOBAL: 4, -}; - -const timestamps = { - local_swap: document.getElementById('local_swap'), - global_swap: document.getElementById('global_swap'), -}; - -const localColors = ['rgb(203, 64, 203)', 'rgb(0, 215, 215)']; -const globalColors = ['rgb(1, 150, 1)', 'red']; - -// Total number of elements and the dimensions of the display grid. -const size = 16384; -const gridDim = Math.sqrt(size); - -const getNumSteps = () => { - const n = Math.log2(size); - return (n * (n + 1)) / 2; -}; - -// Total number of steps in a bitonic sort with 'size' elements. -const MAX_STEPS = getNumSteps(); - -const effectController = { - // Sqr root of 16834 - gridWidth: uniform(gridDim), - gridHeight: uniform(gridDim), - highlight: uniform(1), - stepBitonic: true, - 'Display Mode': 'Swap Zone Highlight', -}; - -const gui = new GUI(); -gui.add(effectController, 'Display Mode', ['Elements', 'Swap Zone Highlight']).onChange(() => { - if (effectController['Display Mode'] === 'Elements') { - effectController.highlight.value = 0; - } else { - effectController.highlight.value = 1; - } -}); - -if (WebGPU.isAvailable() === false) { - document.body.appendChild(WebGPU.getErrorMessage()); - - throw new Error('No WebGPU support'); -} - -// Display utilities - -const getElementIndex = Fn( - ([uvNode, gridWidth, gridHeight]) => { - const newUV = uvNode.mul(vec2(gridWidth, gridHeight)); - const pixel = uvec2(uint(floor(newUV.x)), uint(floor(newUV.y))); - const elementIndex = uint(gridWidth).mul(pixel.y).add(pixel.x); - - return elementIndex; - }, - { - uvNode: 'vec2', - gridWidth: 'uint', - gridHeight: 'uint', - return: 'uint', - }, -); - -const getColor = Fn( - ([colorChanger, gridWidth, gridHeight]) => { - const subtracter = colorChanger.div(gridWidth.mul(gridHeight)); - return vec3(subtracter.oneMinus()).toVar(); - }, - { - colorChanger: 'float', - gridWidth: 'float', - gridHeight: 'float', - return: 'vec3', - }, -); - -const randomizeDataArray = array => { - let currentIndex = array.length; - while (currentIndex !== 0) { - const randomIndex = Math.floor(Math.random() * currentIndex); - currentIndex -= 1; - [array[currentIndex], array[randomIndex]] = [array[randomIndex], array[currentIndex]]; - } -}; - -const windowResizeCallback = (renderer, scene, camera) => { - renderer.setSize(window.innerWidth / 2, window.innerHeight); - const aspect = window.innerWidth / 2 / window.innerHeight; - const frustumHeight = camera.top - camera.bottom; - camera.left = (-frustumHeight * aspect) / 2; - camera.right = (frustumHeight * aspect) / 2; - camera.updateProjectionMatrix(); - renderer.render(scene, camera); -}; - -const constructInnerHTML = (isGlobal, colorsArr) => { - return ` - - Compute ${isGlobal ? 'Global' : 'Local'}: -
- ${isGlobal ? 'Global Swaps' : 'Local Swaps'} Compare Region  -
-  to Region  -
-
`; -}; - -const createDisplayMesh = (elementsStorage, algoStorage = null, blockHeightStorage = null) => { - const material = new THREE.MeshBasicNodeMaterial({ color: 0x00ff00 }); - - const display = Fn(() => { - const { gridWidth, gridHeight, highlight } = effectController; - - const elementIndex = getElementIndex(uv(), gridWidth, gridHeight); - const color = getColor(elementsStorage.element(elementIndex), gridWidth, gridHeight).toVar(); - - if (algoStorage !== null && blockHeightStorage !== null) { - If(highlight.equal(1).and(not(algoStorage.element(0).equal(StepType.NONE))), () => { - const boolCheck = int( - elementIndex.mod(blockHeightStorage.element(0)).lessThan(blockHeightStorage.element(0).div(2)), - ); - color.z.assign(algoStorage.element(0).lessThanEqual(StepType.DISPERSE_LOCAL)); - color.x.mulAssign(boolCheck); - color.y.mulAssign(abs(boolCheck.sub(1))); - }); - } - - return color; - }); - - material.colorNode = display(); - const plane = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), material); - return plane; -}; - -const createDisplayMesh2 = (elementsStorage, infoStorage) => { - const material = new THREE.MeshBasicNodeMaterial({ color: 0x00ff00 }); - - const display = Fn(() => { - const { gridWidth, gridHeight, highlight } = effectController; - - const elementIndex = getElementIndex(uv(), gridWidth, gridHeight); - const color = getColor(elementsStorage.element(elementIndex), gridWidth, gridHeight).toVar(); - - If(highlight.equal(1).and(not(infoStorage.element(0).equal(StepType.SWAP_LOCAL))), () => { - const boolCheck = int(elementIndex.mod(infoStorage.element(1)).lessThan(infoStorage.element(1).div(2))); - color.z.assign(infoStorage.element(0).lessThanEqual(StepType.DISPERSE_LOCAL)); - color.x.mulAssign(boolCheck); - color.y.mulAssign(abs(boolCheck.sub(1))); - }); - - return color; - }); - - material.colorNode = display(); - const plane = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), material); - return plane; -}; - -const setupDomElement = renderer => { - document.body.appendChild(renderer.domElement); - renderer.domElement.style.position = 'absolute'; - renderer.domElement.style.top = '0'; - renderer.domElement.style.left = '0'; - renderer.domElement.style.width = '50%'; - renderer.domElement.style.height = '100%'; -}; - -async function initBitonicSort() { - let currentStep = 0; - - const aspect = window.innerWidth / 2 / window.innerHeight; - const camera = new THREE.OrthographicCamera(-aspect, aspect, 1, -1, 0, 2); - camera.position.z = 1; - - const scene = new THREE.Scene(); - - const array = new Uint32Array( - Array.from({ length: size }, (_, i) => { - return i; - }), - ); - - randomizeDataArray(array); - - const currentElementsBuffer = new THREE.StorageInstancedBufferAttribute(array, 1); - const currentElementsStorage = storage(currentElementsBuffer, 'uint', size).setPBO(true).setName('Elements'); - const randomizedElementsBuffer = new THREE.StorageInstancedBufferAttribute(size, 1); - const randomizedElementsStorage = storage(randomizedElementsBuffer, 'uint', size) - .setPBO(true) - .setName('RandomizedElements'); - - const computeInitFn = Fn(() => { - randomizedElementsStorage.element(instanceIndex).assign(currentElementsStorage.element(instanceIndex)); - }); - - const computeResetBuffersFn = Fn(() => { - currentElementsStorage.element(instanceIndex).assign(randomizedElementsStorage.element(instanceIndex)); - }); - - const renderer = new THREE.WebGPURenderer({ antialias: false }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth / 2, window.innerHeight); - - await renderer.init(); - - const animate = () => { - renderer.render(scene, camera); - }; - - renderer.setAnimationLoop(animate); - setupDomElement(renderer); - scene.background = new THREE.Color(0x313131); - - const bitonicSortModule = new BitonicSort(renderer, currentElementsStorage, { - workgroupSize: 64, - }); - - scene.add(createDisplayMesh2(currentElementsStorage, bitonicSortModule.infoStorage)); - - // Initialize each value in the elements buffer. - const computeInit = computeInitFn().compute(size); - const computeReset = computeResetBuffersFn().compute(size); - - renderer.compute(computeInit); - - const stepAnimation = async function () { - renderer.info.reset(); - - if (currentStep < bitonicSortModule.stepCount) { - bitonicSortModule.computeStep(renderer); - - currentStep++; - } else { - renderer.compute(computeReset); - - currentStep = 0; - } - - timestamps['local_swap'].innerHTML = constructInnerHTML(false, localColors); - - if (currentStep === bitonicSortModule.stepCount) { - setTimeout(stepAnimation, 1000); - } else { - setTimeout(stepAnimation, 100); - } - }; - - stepAnimation(); - - window.addEventListener('resize', onWindowResize); - - function onWindowResize() { - windowResizeCallback(renderer, scene, camera); - } -} - -initBitonicSort(); - -// Global Swaps Only -initGlobalSwapOnly(); - -// When forceGlobalSwap is true, force all valid local swaps to be global swaps. -async function initGlobalSwapOnly() { - let currentStep = 0; - - const aspect = window.innerWidth / 2 / window.innerHeight; - const camera = new THREE.OrthographicCamera(-aspect, aspect, 1, -1, 0, 2); - camera.position.z = 1; - - const scene = new THREE.Scene(); - - const infoArray = new Uint32Array([3, 2, 2]); - const infoBuffer = new THREE.StorageInstancedBufferAttribute(infoArray, 1); - const infoStorage = storage(infoBuffer, 'uint', infoBuffer.count).setPBO(true).setName('TheInfo'); - - const nextBlockHeightBuffer = new THREE.StorageInstancedBufferAttribute(new Uint32Array(1).fill(2), 1); - const nextBlockHeightStorage = storage(nextBlockHeightBuffer, 'uint', nextBlockHeightBuffer.count) - .setPBO(true) - .setName('NextBlockHeight'); - const nextBlockHeightRead = storage(nextBlockHeightBuffer, 'uint', nextBlockHeightBuffer.count) - .setPBO(true) - .setName('NextBlockHeight') - .toReadOnly(); - - const array = new Uint32Array( - Array.from({ length: size }, (_, i) => { - return i; - }), - ); - - randomizeDataArray(array); - - const currentElementsBuffer = new THREE.StorageInstancedBufferAttribute(array, 1); - const currentElementsStorage = storage(currentElementsBuffer, 'uint', size).setPBO(true).setName('Elements'); - const tempBuffer = new THREE.StorageInstancedBufferAttribute(array, 1); - const tempStorage = storage(tempBuffer, 'uint', size).setPBO(true).setName('Temp'); - const randomizedElementsBuffer = new THREE.StorageInstancedBufferAttribute(size, 1); - const randomizedElementsStorage = storage(randomizedElementsBuffer, 'uint', size) - .setPBO(true) - .setName('RandomizedElements'); - - // Swap the elements in local storage - const globalCompareAndSwap = (idxBefore, idxAfter) => { - // If the later element is less than the current element - If(currentElementsStorage.element(idxAfter).lessThan(currentElementsStorage.element(idxBefore)), () => { - // Apply the swapped values to temporary storage. - tempStorage.element(idxBefore).assign(currentElementsStorage.element(idxAfter)); - tempStorage.element(idxAfter).assign(currentElementsStorage.element(idxBefore)); - }).Else(() => { - // Otherwise apply the existing values to temporary storage. - tempStorage.element(idxBefore).assign(currentElementsStorage.element(idxBefore)); - tempStorage.element(idxAfter).assign(currentElementsStorage.element(idxAfter)); - }); - }; - - const computeInitFn = Fn(() => { - randomizedElementsStorage.element(instanceIndex).assign(currentElementsStorage.element(instanceIndex)); - }); - - const computeBitonicStepFn = Fn(() => { - const nextBlockHeight = nextBlockHeightStorage.element(0).toVar(); - const nextAlgo = infoStorage.element(0).toVar(); - - // TODO: Convert to switch block. - If(nextAlgo.equal(uint(StepType.FLIP_GLOBAL)), () => { - const idx = getBitonicFlipIndices(instanceIndex, nextBlockHeight); - globalCompareAndSwap(idx.x, idx.y); - }).ElseIf(nextAlgo.equal(uint(StepType.DISPERSE_GLOBAL)), () => { - const idx = getBitonicDisperseIndices(instanceIndex, nextBlockHeight); - globalCompareAndSwap(idx.x, idx.y); - }); - - // Since this algorithm is global only, we execute an additional compute step to sync the current buffer with the output buffer. - }); - - const computeSetAlgoFn = Fn(() => { - const nextBlockHeight = nextBlockHeightStorage.element(0).toVar(); - const nextAlgo = infoStorage.element(0); - const highestBlockHeight = infoStorage.element(2).toVar(); - - nextBlockHeight.divAssign(2); - - If(nextBlockHeight.equal(1), () => { - highestBlockHeight.mulAssign(2); - - If(highestBlockHeight.equal(size * 2), () => { - nextAlgo.assign(StepType.NONE); - nextBlockHeight.assign(0); - }).Else(() => { - nextAlgo.assign(StepType.FLIP_GLOBAL); - nextBlockHeight.assign(highestBlockHeight); - }); - }).Else(() => { - nextAlgo.assign(StepType.DISPERSE_GLOBAL); - }); - - nextBlockHeightStorage.element(0).assign(nextBlockHeight); - infoStorage.element(2).assign(highestBlockHeight); - }); - - const computeAlignCurrentFn = Fn(() => { - currentElementsStorage.element(instanceIndex).assign(tempStorage.element(instanceIndex)); - }); - - const computeResetBuffersFn = Fn(() => { - currentElementsStorage.element(instanceIndex).assign(randomizedElementsStorage.element(instanceIndex)); - }); - - const computeResetAlgoFn = Fn(() => { - infoStorage.element(0).assign(StepType.FLIP_GLOBAL); - nextBlockHeightStorage.element(0).assign(2); - infoStorage.element(2).assign(2); - }); - - // Initialize each value in the elements buffer. - const computeInit = computeInitFn().compute(size); - // Swap a pair of elements in the elements buffer. - const computeBitonicStep = computeBitonicStepFn().compute(size / 2); - // Set the conditions for the next swap. - const computeSetAlgo = computeSetAlgoFn().compute(1); - // Align the current buffer with the temp buffer if the previous sort was executed in a global scope. - const computeAlignCurrent = computeAlignCurrentFn().compute(size); - // Reset the buffers and algorithm information after a full bitonic sort has been completed. - const computeResetBuffers = computeResetBuffersFn().compute(size); - const computeResetAlgo = computeResetAlgoFn().compute(1); - - scene.add(createDisplayMesh(currentElementsStorage, infoStorage, nextBlockHeightRead)); - - const renderer = new THREE.WebGPURenderer({ antialias: false }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth / 2, window.innerHeight); - - await renderer.init(); - - const animate = () => { - renderer.render(scene, camera); - }; - - renderer.setAnimationLoop(animate); - setupDomElement(renderer); - renderer.domElement.style.left = '50%'; - scene.background = new THREE.Color(0x212121); - - renderer.compute(computeInit); - - const stepAnimation = async function () { - if (currentStep !== MAX_STEPS) { - renderer.compute(computeBitonicStep); - - renderer.compute(computeAlignCurrent); - - renderer.compute(computeSetAlgo); - - currentStep++; - } else { - renderer.compute(computeResetBuffers); - renderer.compute(computeResetAlgo); - - currentStep = 0; - } - - timestamps['global_swap'].innerHTML = constructInnerHTML(true, globalColors); - - if (currentStep === MAX_STEPS) { - setTimeout(stepAnimation, 1000); - } else { - setTimeout(stepAnimation, 100); - } - }; - - stepAnimation(); - - window.addEventListener('resize', onWindowResize); - - function onWindowResize() { - windowResizeCallback(renderer, scene, camera); - } -} diff --git a/examples-testing/examples/webgpu_compute_texture.ts b/examples-testing/examples/webgpu_compute_texture.ts deleted file mode 100644 index 43d162ab5..000000000 --- a/examples-testing/examples/webgpu_compute_texture.ts +++ /dev/null @@ -1,96 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { texture, textureStore, Fn, instanceIndex, float, uvec2, vec4 } from 'three/tsl'; - -import WebGPU from 'three/addons/capabilities/WebGPU.js'; - -let camera, scene, renderer; - -init().then(render); - -async function init() { - if (WebGPU.isAvailable() === false) { - document.body.appendChild(WebGPU.getErrorMessage()); - - throw new Error('No WebGPU support'); - } - - const aspect = window.innerWidth / window.innerHeight; - camera = new THREE.OrthographicCamera(-aspect, aspect, 1, -1, 0, 2); - camera.position.z = 1; - - scene = new THREE.Scene(); - - // texture - - const width = 512, - height = 512; - - const storageTexture = new THREE.StorageTexture(width, height); - //storageTexture.minFilter = THREE.LinearMipMapLinearFilter; - - // create function - - const computeTexture = Fn(({ storageTexture }) => { - const posX = instanceIndex.mod(width); - const posY = instanceIndex.div(width); - const indexUV = uvec2(posX, posY); - - // https://www.shadertoy.com/view/Xst3zN - - const x = float(posX).div(50.0); - const y = float(posY).div(50.0); - - const v1 = x.sin(); - const v2 = y.sin(); - const v3 = x.add(y).sin(); - const v4 = x.mul(x).add(y.mul(y)).sqrt().add(5.0).sin(); - const v = v1.add(v2, v3, v4); - - const r = v.sin(); - const g = v.add(Math.PI).sin(); - const b = v.add(Math.PI).sub(0.5).sin(); - - textureStore(storageTexture, indexUV, vec4(r, g, b, 1)).toWriteOnly(); - }); - - // compute - - const computeNode = computeTexture({ storageTexture }).compute(width * height); - - const material = new THREE.MeshBasicNodeMaterial({ color: 0x00ff00 }); - material.colorNode = texture(storageTexture); - - const plane = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), material); - scene.add(plane); - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - document.body.appendChild(renderer.domElement); - - await renderer.init(); - - // compute texture - renderer.compute(computeNode); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - renderer.setSize(window.innerWidth, window.innerHeight); - - const aspect = window.innerWidth / window.innerHeight; - - const frustumHeight = camera.top - camera.bottom; - - camera.left = (-frustumHeight * aspect) / 2; - camera.right = (frustumHeight * aspect) / 2; - - camera.updateProjectionMatrix(); - - render(); -} - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_compute_texture_3d.ts b/examples-testing/examples/webgpu_compute_texture_3d.ts deleted file mode 100644 index ccdfba0be..000000000 --- a/examples-testing/examples/webgpu_compute_texture_3d.ts +++ /dev/null @@ -1,193 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { - time, - mx_noise_vec3, - instanceIndex, - textureStore, - float, - vec3, - vec4, - If, - Break, - Fn, - smoothstep, - texture3D, - uniform, -} from 'three/tsl'; - -import { RaymarchingBox } from 'three/addons/tsl/utils/Raymarching.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import WebGPU from 'three/addons/capabilities/WebGPU.js'; - -let renderer, scene, camera; -let mesh; -let computeNode; - -if (WebGPU.isAvailable() === false) { - document.body.appendChild(WebGPU.getErrorMessage()); - - throw new Error('No WebGPU support'); -} - -init(); - -async function init() { - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - await renderer.init(); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(0, 1, 1.5); - - new OrbitControls(camera, renderer.domElement); - - // Sky - - const canvas = document.createElement('canvas'); - canvas.width = 1; - canvas.height = 32; - - const context = canvas.getContext('2d'); - const gradient = context.createLinearGradient(0, 0, 0, 32); - gradient.addColorStop(0.0, '#014a84'); - gradient.addColorStop(0.5, '#0561a0'); - gradient.addColorStop(1.0, '#437ab6'); - context.fillStyle = gradient; - context.fillRect(0, 0, 1, 32); - - const skyMap = new THREE.CanvasTexture(canvas); - skyMap.colorSpace = THREE.SRGBColorSpace; - - const sky = new THREE.Mesh( - new THREE.SphereGeometry(10), - new THREE.MeshBasicNodeMaterial({ map: skyMap, side: THREE.BackSide }), - ); - scene.add(sky); - - // Texture - - const size = 200; - - const computeCloud = Fn(({ storageTexture }) => { - const scale = float(0.05); - const id = instanceIndex; - - const x = id.mod(size); - const y = id.div(size).mod(size); - const z = id.div(size * size); - - const coord3d = vec3(x, y, z); - const centered = coord3d.sub(size / 2).div(size); - const d = float(1.0).sub(centered.length()); - - const noiseCoord = coord3d.mul(scale.div(1.5)).add(time); - - const noise = mx_noise_vec3(noiseCoord).toConst('noise'); - - const data = noise.mul(d).mul(d).toConst('data'); - - textureStore(storageTexture, vec3(x, y, z), vec4(vec3(data.x), 1.0)); - }); - - const storageTexture = new THREE.Storage3DTexture(size, size, size); - storageTexture.generateMipmaps = false; - storageTexture.name = 'cloud'; - - computeNode = computeCloud({ storageTexture }) - .compute(size * size * size) - .setName('computeCloud'); - - // Shader - - const transparentRaymarchingTexture = Fn( - ({ texture, range = float(0.14), threshold = float(0.08), opacity = float(0.18), steps = float(100) }) => { - const finalColor = vec4(0).toVar(); - - RaymarchingBox(steps, ({ positionRay }) => { - const mapValue = float(texture.sample(positionRay.add(0.5)).r).toVar(); - - mapValue.assign(smoothstep(threshold.sub(range), threshold.add(range), mapValue).mul(opacity)); - - const shading = texture - .sample(positionRay.add(vec3(-0.01))) - .r.sub(texture.sample(positionRay.add(vec3(0.01))).r); - - const col = shading.mul(4.0).add(positionRay.x.add(positionRay.y).mul(0.5)).add(0.3); - - finalColor.rgb.addAssign(finalColor.a.oneMinus().mul(mapValue).mul(col)); - - finalColor.a.addAssign(finalColor.a.oneMinus().mul(mapValue)); - - If(finalColor.a.greaterThanEqual(0.95), () => { - Break(); - }); - }); - - return finalColor; - }, - ); - - // Material - - const baseColor = uniform(new THREE.Color(0x798aa0)); - const range = uniform(0.1); - const threshold = uniform(0.08); - const opacity = uniform(0.08); - const steps = uniform(100); - - const cloud3d = transparentRaymarchingTexture({ - texture: texture3D(storageTexture, null, 0), - range, - threshold, - opacity, - steps, - }); - - const finalCloud = cloud3d.setRGB(cloud3d.rgb.add(baseColor)); - - const material = new THREE.NodeMaterial(); - material.colorNode = finalCloud; - material.side = THREE.BackSide; - material.transparent = true; - material.name = 'transparentRaymarchingMaterial'; - - mesh = new THREE.Mesh(new THREE.BoxGeometry(10, 10, 10), material); - scene.add(mesh); - - mesh.rotation.y = Math.PI / 2; - - // - - renderer.compute(computeNode); - - const gui = renderer.inspector.createParameters('Settings'); - gui.add(threshold, 'value', 0, 1, 0.01).name('threshold'); - gui.add(opacity, 'value', 0, 1, 0.01).name('opacity'); - gui.add(range, 'value', 0, 1, 0.01).name('range'); - gui.add(steps, 'value', 0, 200, 1).name('steps'); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - renderer.compute(computeNode); - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_compute_texture_pingpong.ts b/examples-testing/examples/webgpu_compute_texture_pingpong.ts deleted file mode 100644 index 5c411fa69..000000000 --- a/examples-testing/examples/webgpu_compute_texture_pingpong.ts +++ /dev/null @@ -1,170 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { - storageTexture, - textureStore, - Fn, - instanceIndex, - uniform, - float, - vec2, - vec4, - uvec2, - ivec2, - int, - NodeAccess, -} from 'three/tsl'; - -import WebGPU from 'three/addons/capabilities/WebGPU.js'; - -let camera, scene, renderer; -let computeInitNode, computeToPing, computeToPong; -let pingTexture, pongTexture; -let material; -let phase = true; -let lastUpdate = -1; - -const width = 512, - height = 512; - -const seed = uniform(new THREE.Vector2()); - -init(); - -async function init() { - if (WebGPU.isAvailable() === false) { - document.body.appendChild(WebGPU.getErrorMessage()); - - throw new Error('No WebGPU support'); - } - - const aspect = window.innerWidth / window.innerHeight; - camera = new THREE.OrthographicCamera(-aspect, aspect, 1, -1, 0, 2); - camera.position.z = 1; - - scene = new THREE.Scene(); - - // texture - - const hdr = true; - - pingTexture = new THREE.StorageTexture(width, height); - pongTexture = new THREE.StorageTexture(width, height); - - if (hdr) { - pingTexture.type = THREE.HalfFloatType; - pongTexture.type = THREE.HalfFloatType; - } - - const rand2 = Fn(([n]) => { - return n.dot(vec2(12.9898, 4.1414)).sin().mul(43758.5453).fract(); - }); - - // Create storage texture nodes with proper access - const writePing = storageTexture(pingTexture).setAccess(NodeAccess.WRITE_ONLY); - const readPing = storageTexture(pingTexture).setAccess(NodeAccess.READ_ONLY); - const writePong = storageTexture(pongTexture).setAccess(NodeAccess.WRITE_ONLY); - const readPong = storageTexture(pongTexture).setAccess(NodeAccess.READ_ONLY); - - const computeInit = Fn(() => { - const posX = instanceIndex.mod(width); - const posY = instanceIndex.div(width); - const indexUV = uvec2(posX, posY); - const uv = vec2(float(posX).div(width), float(posY).div(height)); - - const r = rand2(uv.add(seed.mul(100))).sub(rand2(uv.add(seed.mul(300)))); - const g = rand2(uv.add(seed.mul(200))).sub(rand2(uv.add(seed.mul(300)))); - const b = rand2(uv.add(seed.mul(200))).sub(rand2(uv.add(seed.mul(100)))); - - textureStore(writePing, indexUV, vec4(r, g, b, 1)); - }); - - computeInitNode = computeInit().compute(width * height); - - // compute ping-pong: blur function using .load() for textureLoad - const blur = Fn(([readTex, uv]) => { - const c0 = readTex.load(uv.add(ivec2(-1, 1))); - const c1 = readTex.load(uv.add(ivec2(-1, -1))); - const c2 = readTex.load(uv.add(ivec2(0, 0))); - const c3 = readTex.load(uv.add(ivec2(1, -1))); - const c4 = readTex.load(uv.add(ivec2(1, 1))); - - return c0.add(c1).add(c2).add(c3).add(c4).div(5.0); - }); - - // compute loop: read from one texture, blur, write to another - const computePingPong = Fn(([readTex, writeTex]) => { - const posX = instanceIndex.mod(width); - const posY = instanceIndex.div(width); - const indexUV = ivec2(int(posX), int(posY)); - - const color = blur(readTex, indexUV); - - textureStore(writeTex, indexUV, vec4(color.rgb.mul(1.05), 1)); - }); - - computeToPong = computePingPong(readPing, writePong).compute(width * height); - computeToPing = computePingPong(readPong, writePing).compute(width * height); - - // - - material = new THREE.MeshBasicMaterial({ color: 0xffffff, map: pongTexture }); - - const plane = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), material); - scene.add(plane); - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(render); - document.body.appendChild(renderer.domElement); - - await renderer.init(); - - window.addEventListener('resize', onWindowResize); - - // compute init - - renderer.compute(computeInitNode); -} - -function onWindowResize() { - renderer.setSize(window.innerWidth, window.innerHeight); - - const aspect = window.innerWidth / window.innerHeight; - - const frustumHeight = camera.top - camera.bottom; - - camera.left = (-frustumHeight * aspect) / 2; - camera.right = (frustumHeight * aspect) / 2; - - camera.updateProjectionMatrix(); -} - -function render() { - const time = performance.now(); - const seconds = Math.floor(time / 1000); - - // reset every second - - if (phase && seconds !== lastUpdate) { - seed.value.set(Math.random(), Math.random()); - - renderer.compute(computeInitNode); - - lastUpdate = seconds; - } - - // compute step - - renderer.compute(phase ? computeToPong : computeToPing); - - material.map = phase ? pongTexture : pingTexture; - - phase = !phase; - - // render step - - // update material texture node - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_cubemap_adjustments.ts b/examples-testing/examples/webgpu_cubemap_adjustments.ts deleted file mode 100644 index 2e886d824..000000000 --- a/examples-testing/examples/webgpu_cubemap_adjustments.ts +++ /dev/null @@ -1,165 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { - uniform, - mix, - pmremTexture, - reference, - positionLocal, - hue, - saturation, - positionWorld, - normalWorld, - positionWorldDirection, - reflectVector, -} from 'three/tsl'; - -import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer; - -init(); - -async function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - const initialDistance = 2; - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); - camera.position.set(-1.8 * initialDistance, 0.6 * initialDistance, 2.7 * initialDistance); - - scene = new THREE.Scene(); - - // HDR textures - - const hdr1Texture = await new HDRLoader().loadAsync('./textures/equirectangular/pedestrian_overpass_1k.hdr'); - - hdr1Texture.mapping = THREE.EquirectangularReflectionMapping; - hdr1Texture.generateMipmaps = true; - hdr1Texture.minFilter = THREE.LinearMipmapLinearFilter; - - const hdr2Texture = await new HDRLoader().loadAsync('./textures/equirectangular/752-hdri-skies-com_1k.hdr'); - - hdr2Texture.mapping = THREE.EquirectangularReflectionMapping; - hdr2Texture.generateMipmaps = true; - hdr2Texture.minFilter = THREE.LinearMipmapLinearFilter; - - // nodes and environment - - const adjustments = { - mix: 0, - procedural: 0, - intensity: 1, - hue: 0, - saturation: 1, - }; - - const mixNode = reference('mix', 'float', adjustments); - const proceduralNode = reference('procedural', 'float', adjustments); - const intensityNode = reference('intensity', 'float', adjustments); - const hueNode = reference('hue', 'float', adjustments); - const saturationNode = reference('saturation', 'float', adjustments); - - const rotateY1Matrix = new THREE.Matrix4(); - const rotateY2Matrix = new THREE.Matrix4(); - - const getEnvironmentNode = (reflectNode, positionNode) => { - const custom1UV = reflectNode.xyz.mul(uniform(rotateY1Matrix)); - const custom2UV = reflectNode.xyz.mul(uniform(rotateY2Matrix)); - const mixCubeMaps = mix( - pmremTexture(hdr1Texture, custom1UV), - pmremTexture(hdr2Texture, custom2UV), - positionNode.y.add(mixNode).clamp(), - ); - - const proceduralEnv = mix(mixCubeMaps, normalWorld, proceduralNode); - - const intensityFilter = proceduralEnv.mul(intensityNode); - const hueFilter = hue(intensityFilter, hueNode); - return saturation(hueFilter, saturationNode); - }; - - const blurNode = uniform(0); - - scene.environmentNode = getEnvironmentNode(reflectVector, positionWorld); - - scene.backgroundNode = getEnvironmentNode(positionWorldDirection, positionLocal).context({ - getTextureLevel: () => blurNode, - }); - - // scene objects - - const loader = new GLTFLoader().setPath('models/gltf/DamagedHelmet/glTF/'); - const gltf = await loader.loadAsync('DamagedHelmet.gltf'); - - scene.add(gltf.scene); - - const sphereGeometry = new THREE.SphereGeometry(0.5, 64, 32); - - const sphereRightView = new THREE.Mesh( - sphereGeometry, - new THREE.MeshStandardMaterial({ roughness: 0, metalness: 1 }), - ); - sphereRightView.position.x += 2; - - const sphereLeftView = new THREE.Mesh( - sphereGeometry, - new THREE.MeshStandardMaterial({ roughness: 1, metalness: 1 }), - ); - sphereLeftView.position.x -= 2; - - scene.add(sphereLeftView); - scene.add(sphereRightView); - - // renderer and controls - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.toneMapping = THREE.LinearToneMapping; - renderer.inspector = new Inspector(); - renderer.setAnimationLoop(render); - container.appendChild(renderer.domElement); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 2; - controls.maxDistance = 10; - - window.addEventListener('resize', onWindowResize); - - // gui - - const gui = renderer.inspector.createParameters('Settings'); - gui.add(adjustments, 'mix', -1, 2, 0.01); - gui.add({ blurBackground: blurNode.value }, 'blurBackground', 0, 1, 0.01).onChange(value => { - blurNode.value = value; - }); - gui.add({ offsetHDR1: 0 }, 'offsetHDR1', 0, Math.PI * 2, 0.01).onChange(value => { - rotateY1Matrix.makeRotationY(value); - }); - gui.add({ offsetHDR2: 0 }, 'offsetHDR2', 0, Math.PI * 2, 0.01).onChange(value => { - rotateY2Matrix.makeRotationY(value); - }); - gui.add(adjustments, 'procedural', 0, 1, 0.01); - gui.add(adjustments, 'intensity', 0, 5, 0.01); - gui.add(adjustments, 'hue', 0, Math.PI * 2, 0.01); - gui.add(adjustments, 'saturation', 0, 2, 0.01); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_cubemap_dynamic.ts b/examples-testing/examples/webgpu_cubemap_dynamic.ts deleted file mode 100644 index 244b85935..000000000 --- a/examples-testing/examples/webgpu_cubemap_dynamic.ts +++ /dev/null @@ -1,130 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { HDRCubeTextureLoader } from 'three/addons/loaders/HDRCubeTextureLoader.js'; -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer; -let cube, sphere, torus, material; - -let cubeCamera, cubeRenderTarget; - -let controls; - -init(); - -async function init() { - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.z = 75; - - scene = new THREE.Scene(); - - const uvTexture = new THREE.TextureLoader().load('./textures/uv_grid_opengl.jpg'); - - const hdrUrls = ['px.hdr', 'nx.hdr', 'py.hdr', 'ny.hdr', 'pz.hdr', 'nz.hdr']; - const texture = await new HDRCubeTextureLoader().setPath('./textures/cube/pisaHDR/').loadAsync(hdrUrls); - - texture.name = 'pisaHDR'; - texture.minFilter = THREE.LinearMipmapLinearFilter; - texture.magFilter = THREE.LinearFilter; - - scene.background = texture; - scene.environment = texture; - - // - - cubeRenderTarget = new THREE.CubeRenderTarget(256); - cubeRenderTarget.texture.type = THREE.HalfFloatType; - cubeRenderTarget.texture.minFilter = THREE.LinearMipmapLinearFilter; - cubeRenderTarget.texture.magFilter = THREE.LinearFilter; - cubeRenderTarget.texture.generateMipmaps = true; - - cubeCamera = new THREE.CubeCamera(1, 1000, cubeRenderTarget); - - // - - material = new THREE.MeshStandardNodeMaterial({ - envMap: cubeRenderTarget.texture, - roughness: 0.05, - metalness: 1, - }); - - sphere = new THREE.Mesh(new THREE.IcosahedronGeometry(15, 8), material); - scene.add(sphere); - - const material1 = new THREE.MeshStandardNodeMaterial({ - map: uvTexture, - roughness: 0.1, - metalness: 0, - }); - - const material2 = new THREE.MeshStandardNodeMaterial({ - map: uvTexture, - roughness: 0.1, - metalness: 0, - envMap: texture, - }); - - cube = new THREE.Mesh(new THREE.BoxGeometry(15, 15, 15), material1); - scene.add(cube); - - torus = new THREE.Mesh(new THREE.TorusKnotGeometry(8, 3, 128, 16), material2); - scene.add(torus); - - // - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animation); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - window.addEventListener('resize', onWindowResized); - - controls = new OrbitControls(camera, renderer.domElement); - controls.autoRotate = true; - - const gui = renderer.inspector.createParameters('Settings'); - gui.add(material, 'roughness', 0, 1); - gui.add(material, 'metalness', 0, 1); - gui.add(renderer, 'toneMappingExposure', 0, 2).name('exposure'); - gui.add(scene, 'environmentIntensity', 0, 1); - gui.add(material2, 'envMapIntensity', 0, 1); -} - -function onWindowResized() { - renderer.setSize(window.innerWidth, window.innerHeight); - - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); -} - -function animation(msTime) { - const time = msTime / 1000; - - cube.position.x = Math.cos(time) * 30; - cube.position.y = Math.sin(time) * 30; - cube.position.z = Math.sin(time) * 30; - - cube.rotation.x += 0.02; - cube.rotation.y += 0.03; - - torus.position.x = Math.cos(time + 10) * 30; - torus.position.y = Math.sin(time + 10) * 30; - torus.position.z = Math.sin(time + 10) * 30; - - torus.rotation.x += 0.02; - torus.rotation.y += 0.03; - - material.visible = false; - - cubeCamera.update(renderer, scene); - - material.visible = true; - - controls.update(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_cubemap_mix.ts b/examples-testing/examples/webgpu_cubemap_mix.ts deleted file mode 100644 index 6fbdb55ae..000000000 --- a/examples-testing/examples/webgpu_cubemap_mix.ts +++ /dev/null @@ -1,78 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { mix, oscSine, time, pmremTexture, float } from 'three/tsl'; - -import { HDRCubeTextureLoader } from 'three/addons/loaders/HDRCubeTextureLoader.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer; - -init(); - -async function init() { - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); - camera.position.set(-1.8, 0.6, 2.7); - - scene = new THREE.Scene(); - - const hdrUrls = ['px.hdr', 'nx.hdr', 'py.hdr', 'ny.hdr', 'pz.hdr', 'nz.hdr']; - const cube1Texture = new HDRCubeTextureLoader().setPath('./textures/cube/pisaHDR/').load(hdrUrls); - - cube1Texture.generateMipmaps = true; - cube1Texture.minFilter = THREE.LinearMipmapLinearFilter; - - const cube2Urls = [ - 'dark-s_px.jpg', - 'dark-s_nx.jpg', - 'dark-s_py.jpg', - 'dark-s_ny.jpg', - 'dark-s_pz.jpg', - 'dark-s_nz.jpg', - ]; - const cube2Texture = await new THREE.CubeTextureLoader().setPath('./textures/cube/MilkyWay/').loadAsync(cube2Urls); - - cube2Texture.generateMipmaps = true; - cube2Texture.minFilter = THREE.LinearMipmapLinearFilter; - - scene.environmentNode = mix(pmremTexture(cube2Texture), pmremTexture(cube1Texture), oscSine(time.mul(0.1))); - - scene.backgroundNode = scene.environmentNode.context({ - getTextureLevel: () => float(0.5), - }); - - const loader = new GLTFLoader().setPath('models/gltf/DamagedHelmet/glTF/'); - const gltf = await loader.loadAsync('DamagedHelmet.gltf'); - - scene.add(gltf.scene); - - renderer = new THREE.WebGPURenderer({ antialias: true }); - - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.toneMapping = THREE.LinearToneMapping; - renderer.inspector = new Inspector(); - renderer.setAnimationLoop(render); - document.body.appendChild(renderer.domElement); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 2; - controls.maxDistance = 10; - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_custom_fog.ts b/examples-testing/examples/webgpu_custom_fog.ts deleted file mode 100644 index bcfcb0fd3..000000000 --- a/examples-testing/examples/webgpu_custom_fog.ts +++ /dev/null @@ -1,135 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { color, fog, float, positionWorld, triNoise3D, positionView, normalWorld, uniform } from 'three/tsl'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer; -let controls; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 600); - camera.position.set(30, 15, 30); - - scene = new THREE.Scene(); - - // custom fog - - const skyColorValue = 0xf0f5f5; - const groundColorValue = 0xd0dee7; - - const skyColor = color(skyColorValue); - const groundColor = color(groundColorValue); - - const fogNoiseDistance = positionView.z.negate().smoothstep(0, camera.far - 300); - - const distance = fogNoiseDistance.mul(20).max(4); - const alpha = 0.98; - const groundFogArea = float(distance).sub(positionWorld.y).div(distance).pow(3).saturate().mul(alpha); - - // a alternative way to create a TimerNode - const timer = uniform(0).onFrameUpdate(frame => frame.time); - - const fogNoiseA = triNoise3D(positionWorld.mul(0.005), 0.2, timer); - const fogNoiseB = triNoise3D(positionWorld.mul(0.01), 0.2, timer.mul(1.2)); - - const fogNoise = fogNoiseA.add(fogNoiseB).mul(groundColor); - - // apply custom fog - - scene.fogNode = fog(fogNoiseDistance.oneMinus().mix(groundColor, fogNoise), groundFogArea); - scene.backgroundNode = normalWorld.y.max(0).mix(groundColor, skyColor); - - // builds - - const buildWindows = positionWorld.y - .mul(10) - .floor() - .mod(4) - .sign() - .mix(color(0x000066).add(fogNoiseDistance), color(0xffffff)); - - const buildGeometry = new THREE.BoxGeometry(1, 1, 1); - const buildMaterial = new THREE.MeshPhongNodeMaterial({ - colorNode: buildWindows, - }); - - const buildMesh = new THREE.InstancedMesh(buildGeometry, buildMaterial, 4000); - scene.add(buildMesh); - - const dummy = new THREE.Object3D(); - const center = new THREE.Vector3(); - - for (let i = 0; i < buildMesh.count; i++) { - const scaleY = Math.random() * 7 + 0.5; - - dummy.position.x = Math.random() * 600 - 300; - dummy.position.z = Math.random() * 600 - 300; - - const distance = Math.max(dummy.position.distanceTo(center) * 0.012, 1); - - dummy.position.y = 0.5 * scaleY * distance; - - dummy.scale.x = dummy.scale.z = Math.random() * 3 + 0.5; - dummy.scale.y = scaleY * distance; - - dummy.updateMatrix(); - - buildMesh.setMatrixAt(i, dummy.matrix); - } - - // lights - - scene.add(new THREE.HemisphereLight(skyColorValue, groundColorValue, 0.5)); - - // geometry - - const planeGeometry = new THREE.PlaneGeometry(200, 200); - const planeMaterial = new THREE.MeshPhongMaterial({ - color: 0x999999, - }); - - const ground = new THREE.Mesh(planeGeometry, planeMaterial); - ground.rotation.x = -Math.PI / 2; - ground.scale.multiplyScalar(3); - ground.castShadow = true; - ground.receiveShadow = true; - scene.add(ground); - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // controls - - controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, 2, 0); - controls.minDistance = 7; - controls.maxDistance = 100; - controls.maxPolarAngle = Math.PI / 2; - controls.autoRotate = true; - controls.autoRotateSpeed = 0.1; - controls.update(); - - window.addEventListener('resize', resize); -} - -function resize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - controls.update(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_custom_fog_background.ts b/examples-testing/examples/webgpu_custom_fog_background.ts deleted file mode 100644 index a9b619345..000000000 --- a/examples-testing/examples/webgpu_custom_fog_background.ts +++ /dev/null @@ -1,86 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { pass, color, rangeFogFactor } from 'three/tsl'; - -import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -let camera, scene, renderer; -let renderPipeline; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); - camera.position.set(-1.8, 0.6, 2.7); - - scene = new THREE.Scene(); - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.toneMapping = THREE.NoToneMapping; // apply tone mapping in post processing, instead - document.body.appendChild(renderer.domElement); - - // post processing - - // render scene pass - const scenePass = pass(scene, camera); - const scenePassViewZ = scenePass.getViewZNode(); - - // fog color - const fogColor = color(0x4080cc); // in sRGB color space - - // get fog factor from the scene pass context - // equivalent to: scene.fog = new THREE.Fog( 0x4080cc, 2.7, 4 ); - const fogFactor = rangeFogFactor(2.7, 4).context({ getViewZ: () => scenePassViewZ }); - - // tone-mapped scene pass - const scenePassTM = scenePass.toneMapping(THREE.ACESFilmicToneMapping, 1); - - // mix fog using fog factor and fog color - const compose = fogFactor.mix(scenePassTM, fogColor); - - renderPipeline = new THREE.RenderPipeline(renderer); - renderPipeline.outputColorTransform = true; // no tone mapping will be applied, only the default color space transform - renderPipeline.outputNode = compose; - - // - - new UltraHDRLoader().setPath('textures/equirectangular/').load('royal_esplanade_2k.hdr.jpg', function (texture) { - texture.mapping = THREE.EquirectangularReflectionMapping; - scene.environment = texture; - - // model - - const loader = new GLTFLoader().setPath('models/gltf/DamagedHelmet/glTF/'); - loader.load('DamagedHelmet.gltf', function (gltf) { - scene.add(gltf.scene); - }); - }); - - // - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 2; - controls.maxDistance = 5; - controls.target.set(0, -0.1, -0.2); - controls.update(); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_custom_fog_scattering.ts b/examples-testing/examples/webgpu_custom_fog_scattering.ts deleted file mode 100644 index 7f14133fd..000000000 --- a/examples-testing/examples/webgpu_custom_fog_scattering.ts +++ /dev/null @@ -1,161 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { positionWorld, densityFogFactor, pass, vec2, uniform, mix, color } from 'three/tsl'; -import { gaussianBlur } from 'three/addons/tsl/display/GaussianBlurNode.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer, renderPipeline, controls; - -const params = { - scatteringEnabled: true, -}; - -init(); - -async function init() { - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 600); - camera.position.set(30, 10, 30); - - scene = new THREE.Scene(); - - // fog - - scene.fog = new THREE.FogExp2(0xcccccc, 0.014); - scene.background = new THREE.Color(0xcccccc); - - // builds - - const buildWindows = positionWorld.y.mul(10).floor().mod(4).sign().mix(color(0x000066), color(0xffffff)); - - const buildGeometry = new THREE.BoxGeometry(1, 1, 1); - const buildMaterial = new THREE.MeshPhongNodeMaterial({ - colorNode: buildWindows, - }); - - const buildMesh = new THREE.InstancedMesh(buildGeometry, buildMaterial, 4000); - scene.add(buildMesh); - - const dummy = new THREE.Object3D(); - const center = new THREE.Vector3(); - - for (let i = 0; i < buildMesh.count; i++) { - const scaleY = Math.random() * 7 + 0.5; - - dummy.position.x = Math.random() * 600 - 300; - dummy.position.z = Math.random() * 600 - 300; - - const distance = Math.max(dummy.position.distanceTo(center) * 0.012, 1); - - dummy.position.y = 0.5 * scaleY * distance; - - dummy.scale.x = dummy.scale.z = Math.random() * 3 + 0.5; - dummy.scale.y = scaleY * distance; - - dummy.updateMatrix(); - - buildMesh.setMatrixAt(i, dummy.matrix); - } - - // lights - - scene.add(new THREE.HemisphereLight(0xf0f5f5, 0xd0dee7, 0.5)); - - // geometry - - const planeGeometry = new THREE.PlaneGeometry(200, 200); - const planeMaterial = new THREE.MeshPhongMaterial({ - color: 0x999999, - }); - - const ground = new THREE.Mesh(planeGeometry, planeMaterial); - ground.rotation.x = -Math.PI / 2; - ground.scale.multiplyScalar(3); - ground.castShadow = true; - ground.receiveShadow = true; - scene.add(ground); - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // controls - - controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, 2, 0); - controls.minDistance = 7; - controls.maxDistance = 100; - controls.maxPolarAngle = Math.PI / 2; - controls.enableDamping = true; - controls.update(); - - // - - renderPipeline = new THREE.RenderPipeline(renderer); - - // uniforms - - const density = uniform(0.014); - const scattering = uniform(2); - - // scene pass - - const scenePass = pass(scene, camera); - - const scenePassColor = scenePass.getTextureNode('output'); - const scenePassViewZ = scenePass.getViewZNode(); - - // blur pass (always downsampled to improve performance) - - const sceneColorBlurred = gaussianBlur(scenePassColor, vec2(scattering), 4, { resolutionScale: 0.5 }); - - // composite - - const fogFactor = densityFogFactor(density).context({ getViewZ: () => scenePassViewZ }); - - const compositeNode = mix(scenePassColor, sceneColorBlurred, fogFactor); - - renderPipeline.outputNode = compositeNode; - - // gui - - const gui = renderer.inspector.createParameters('Settings'); - gui.add(density, 'value', 0.005, 0.03) - .step(0.0001) - .name('fog density') - .onChange(value => { - scene.fog.density = value; - }); - gui.add(scattering, 'value', 0, 5).name('scattering factor'); - gui.add(params, 'scatteringEnabled') - .name('enable scattering') - .onChange(value => { - if (value === true) { - renderPipeline.outputNode = compositeNode; - } else { - renderPipeline.outputNode = scenePassColor; - } - - renderPipeline.needsUpdate = true; - }); - - window.addEventListener('resize', resize); -} - -function resize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - controls.update(); - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_display_stereo.ts b/examples-testing/examples/webgpu_display_stereo.ts deleted file mode 100644 index 7f1ade726..000000000 --- a/examples-testing/examples/webgpu_display_stereo.ts +++ /dev/null @@ -1,191 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { stereoPass } from 'three/addons/tsl/display/StereoPassNode.js'; -import { anaglyphPass, AnaglyphAlgorithm, AnaglyphColorMode } from 'three/addons/tsl/display/AnaglyphPassNode.js'; -import { parallaxBarrierPass } from 'three/addons/tsl/display/ParallaxBarrierPassNode.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer, renderPipeline; - -let stereo, anaglyph, parallaxBarrier; - -let mesh, dummy, timer; - -let anaglyphFolder; - -const position = new THREE.Vector3(); - -const params = { - effect: 'stereo', - eyeSep: 0.064, - planeDistance: 3, - anaglyphAlgorithm: 'dubois', - anaglyphColorMode: 'redCyan', -}; - -const effects = { Stereo: 'stereo', Anaglyph: 'anaglyph', ParallaxBarrier: 'parallaxBarrier' }; - -const anaglyphAlgorithms = { - True: AnaglyphAlgorithm.TRUE, - Grey: AnaglyphAlgorithm.GREY, - Colour: AnaglyphAlgorithm.COLOUR, - 'Half-Colour': AnaglyphAlgorithm.HALF_COLOUR, - Dubois: AnaglyphAlgorithm.DUBOIS, - Optimised: AnaglyphAlgorithm.OPTIMISED, - Compromise: AnaglyphAlgorithm.COMPROMISE, -}; - -const anaglyphColorModes = { - 'Red / Cyan': AnaglyphColorMode.RED_CYAN, - 'Magenta / Cyan': AnaglyphColorMode.MAGENTA_CYAN, - 'Magenta / Green': AnaglyphColorMode.MAGENTA_GREEN, -}; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.z = 3; - - scene = new THREE.Scene(); - scene.background = new THREE.CubeTextureLoader() - .setPath('textures/cube/Park3Med/') - .load(['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg']); - - timer = new THREE.Timer(); - timer.connect(document); - - const geometry = new THREE.SphereGeometry(0.1, 32, 16); - - const textureCube = new THREE.CubeTextureLoader() - .setPath('textures/cube/Park3Med/') - .load(['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg']); - - const material = new THREE.MeshBasicMaterial({ color: 0xffffff, envMap: textureCube }); - - mesh = new THREE.InstancedMesh(geometry, material, 500); - mesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage); - - dummy = new THREE.Mesh(); - - for (let i = 0; i < 500; i++) { - dummy.position.x = Math.random() * 10 - 5; - dummy.position.y = Math.random() * 10 - 5; - dummy.position.z = Math.random() * 10 - 5; - dummy.scale.x = dummy.scale.y = dummy.scale.z = Math.random() * 3 + 1; - - dummy.updateMatrix(); - - mesh.setMatrixAt(i, dummy.matrix); - } - - scene.add(mesh); - - // - - renderer = new THREE.WebGPURenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - renderPipeline = new THREE.RenderPipeline(renderer); - stereo = stereoPass(scene, camera); - anaglyph = anaglyphPass(scene, camera); - parallaxBarrier = parallaxBarrierPass(scene, camera); - - // Configure anaglyph for physically-correct stereo with zero parallax at scene center - anaglyph.eyeSep = params.eyeSep; - anaglyph.planeDistance = params.planeDistance; - - renderPipeline.outputNode = stereo; - - const gui = renderer.inspector.createParameters('Stereo Settings'); - gui.add(params, 'effect', effects).onChange(update); - gui.add(params, 'eyeSep', 0.001, 0.15, 0.001).onChange(function (value) { - stereo.stereo.eyeSep = value; - anaglyph.eyeSep = value; // Anaglyph has direct eyeSep property - parallaxBarrier.stereo.eyeSep = value; - }); - - // Anaglyph-specific settings folder - anaglyphFolder = gui.addFolder('Anaglyph Options'); - anaglyphFolder - .add(params, 'anaglyphAlgorithm', anaglyphAlgorithms) - .name('Algorithm') - .onChange(function (value) { - anaglyph.algorithm = value; - }); - anaglyphFolder - .add(params, 'anaglyphColorMode', anaglyphColorModes) - .name('Color Mode') - .onChange(function (value) { - anaglyph.colorMode = value; - }); - anaglyphFolder - .add(params, 'planeDistance', 0.5, 10, 0.1) - .name('Plane Distance') - .onChange(function (value) { - anaglyph.planeDistance = value; - }); - anaglyphFolder.paramList.domElement.style.display = 'none'; - - window.addEventListener('resize', onWindowResize); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 1; - controls.maxDistance = 25; -} - -function update(value) { - if (value === 'stereo') { - renderPipeline.outputNode = stereo; - anaglyphFolder.paramList.domElement.style.display = 'none'; - } else if (value === 'anaglyph') { - renderPipeline.outputNode = anaglyph; - anaglyphFolder.paramList.domElement.style.display = ''; - } else if (value === 'parallaxBarrier') { - renderPipeline.outputNode = parallaxBarrier; - anaglyphFolder.paramList.domElement.style.display = 'none'; - } - - renderPipeline.needsUpdate = true; -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function extractPosition(matrix, position) { - position.x = matrix.elements[12]; - position.y = matrix.elements[13]; - position.z = matrix.elements[14]; -} - -function animate() { - timer.update(); - - const elapsedTime = timer.getElapsed() * 0.1; - - for (let i = 0; i < mesh.count; i++) { - mesh.getMatrixAt(i, dummy.matrix); - - extractPosition(dummy.matrix, position); - - position.x = 5 * Math.cos(elapsedTime + i); - position.y = 5 * Math.sin(elapsedTime + i * 1.1); - - dummy.matrix.setPosition(position); - - mesh.setMatrixAt(i, dummy.matrix); - - mesh.instanceMatrix.needsUpdate = true; - } - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_equirectangular.ts b/examples-testing/examples/webgpu_equirectangular.ts deleted file mode 100644 index 9e159e465..000000000 --- a/examples-testing/examples/webgpu_equirectangular.ts +++ /dev/null @@ -1,57 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { texture, equirectUV } from 'three/tsl'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer; -let controls; - -init(); - -function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); - camera.position.set(1, 0, 0); - - const equirectTexture = new THREE.TextureLoader().load('textures/2294472375_24a3b8ef46_o.jpg'); - equirectTexture.colorSpace = THREE.SRGBColorSpace; - - scene = new THREE.Scene(); - scene.backgroundNode = texture(equirectTexture, equirectUV(), 0); - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - container.appendChild(renderer.domElement); - - controls = new OrbitControls(camera, renderer.domElement); - controls.autoRotate = true; - controls.rotateSpeed = -0.125; // negative, to track mouse pointer - controls.autoRotateSpeed = 1.0; - - // GUI - - const gui = renderer.inspector.createParameters('Settings'); - - gui.add(scene, 'backgroundIntensity', 0, 1).name('background intensity'); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - controls.update(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_fog_height.ts b/examples-testing/examples/webgpu_fog_height.ts deleted file mode 100644 index 65bda9cd8..000000000 --- a/examples-testing/examples/webgpu_fog_height.ts +++ /dev/null @@ -1,99 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { exponentialHeightFogFactor, uniform, fog, color } from 'three/tsl'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer; -let controls; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 600); - camera.position.set(20, 10, 25); - - scene = new THREE.Scene(); - - // height fog - - const density = uniform(0.04); - const height = uniform(2); - - const fogFactor = exponentialHeightFogFactor(density, height); - - scene.fogNode = fog(color(0xffdfc1), fogFactor); - scene.backgroundNode = color(0xffdfc1); - - // meshes - - const geometry = new THREE.BoxGeometry(1, 25, 1); - const material = new THREE.MeshPhongNodeMaterial({ color: 0xcd959a }); - - const mesh = new THREE.InstancedMesh(geometry, material, 100); - mesh.position.y = -10; - scene.add(mesh); - - const dummy = new THREE.Object3D(); - - let index = 0; - - for (let i = 0; i < 10; i++) { - for (let j = 0; j < 10; j++) { - dummy.position.x = -18 + i * 4; - dummy.position.z = -18 + j * 4; - - dummy.updateMatrix(); - - mesh.setMatrixAt(index++, dummy.matrix); - } - } - - // lights - - const directionalLight = new THREE.DirectionalLight(0xffc0cb, 2); - directionalLight.position.set(-10, 10, 10); - scene.add(directionalLight); - - const ambientLight = new THREE.AmbientLight(0xcccccc); - scene.add(ambientLight); - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // gui - - const gui = renderer.inspector.createParameters('Fog Settings'); - - gui.add(density, 'value', 0.001, 0.1).step(0.0001).name('Density'); - gui.add(height, 'value', -5, 5).name('Height'); - - // controls - - controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 7; - controls.maxDistance = 100; - controls.maxPolarAngle = Math.PI / 2; - controls.enableDamping = true; - - window.addEventListener('resize', resize); -} - -function resize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - controls.update(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_hdr.ts b/examples-testing/examples/webgpu_hdr.ts deleted file mode 100644 index b96ba5f42..000000000 --- a/examples-testing/examples/webgpu_hdr.ts +++ /dev/null @@ -1,124 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { pass, uv, uniform } from 'three/tsl'; -import WebGPU from 'three/addons/capabilities/WebGPU.js'; -import { afterImage } from 'three/addons/tsl/display/AfterImageNode.js'; -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { ExtendedSRGBColorSpace, ExtendedSRGBColorSpaceImpl } from 'three/addons/math/ColorSpaces.js'; - -const params = { - intensity: uniform(4.0, 'float').setName('intensity'), - hardness: uniform(0.4, 'float').setName('hardness'), - radius: uniform(0.5, 'float').setName('radius'), - afterImageDecay: uniform(0.985, 'float').setName('afterImageDecay'), -}; - -const hdrMediaQuery = window.matchMedia('(dynamic-range: high)'); - -function updateHDRWarning() { - const displayIsHDR = hdrMediaQuery.matches; - document.querySelector('#no-hdr').style.display = displayIsHDR ? 'none' : ''; -} - -hdrMediaQuery.addEventListener('change', updateHDRWarning); -updateHDRWarning(); - -if (WebGPU.isAvailable() === false) { - document.body.appendChild(WebGPU.getErrorMessage()); - throw new Error('No WebGPU support'); -} - -// Enable Extended sRGB output color space for HDR presentation -THREE.ColorManagement.define({ [ExtendedSRGBColorSpace]: ExtendedSRGBColorSpaceImpl }); - -// Renderer (HalfFloat output + Extended sRGB) -const renderer = new THREE.WebGPURenderer({ - antialias: true, - outputType: THREE.HalfFloatType, -}); - -renderer.outputColorSpace = ExtendedSRGBColorSpace; -renderer.setPixelRatio(window.devicePixelRatio); -renderer.setSize(window.innerWidth, window.innerHeight); -renderer.inspector = new Inspector(); -document.body.appendChild(renderer.domElement); - -const camera = new THREE.OrthographicCamera(0, window.innerWidth, window.innerHeight, 0, 1, 2); -camera.position.z = 1; - -// Brush scene (rendered into drawTarget) -const brushScene = new THREE.Scene(); - -brushScene.background = new THREE.Color(0xffffff); -const brushMat = new THREE.MeshBasicNodeMaterial(); -brushMat.transparent = true; -brushMat.depthTest = false; -brushMat.depthWrite = false; -brushMat.blending = THREE.AdditiveBlending; // additive to build HDR energy - -const renderPipeline = new THREE.RenderPipeline(renderer); -const brushPass = pass(brushScene, camera, { type: THREE.HalfFloatType }); -brushPass.renderTarget.texture.colorSpace = ExtendedSRGBColorSpace; - -renderPipeline.outputNode = afterImage(brushPass, params.afterImageDecay); - -// HDR brush uniforms -const uColor = params.intensity; -const uHard = params.hardness; -const uRadius = params.radius; - -// Radial falloff in TSL -const d = uv().sub(0.5).length(); -const t = d.div(uRadius); -const a = t.clamp().oneMinus().pow(uHard.mul(8.0).add(1.0)); - -brushMat.colorNode = uColor.mul(a); -brushMat.opacityNode = a; // premultiplied style with additive blending - -const brushMesh = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), brushMat); -brushMesh.scale.set(300, 300, 1); // ~300px default brush size -brushScene.add(brushMesh); - -function onPointerMove(e) { - const rect = renderer.domElement.getBoundingClientRect(); - const x = e.clientX - rect.left; - const y = e.clientY - rect.top; - - // camera has origin at bottom-left (0,0) - brushMesh.position.set(x, window.innerHeight - y, 0); -} - -window.addEventListener('pointermove', onPointerMove, { passive: false }); - -// Prevent mobile scroll on touch -renderer.domElement.addEventListener('touchstart', e => e.preventDefault(), { passive: false }); -renderer.domElement.addEventListener('touchmove', e => e.preventDefault(), { passive: false }); -renderer.domElement.addEventListener('touchend', e => e.preventDefault(), { passive: false }); - -// GUI setup -const gui = renderer.inspector.createParameters('Settings'); - -const colorFolder = gui.addFolder('HDR'); -colorFolder.add(params.intensity, 'value', 0, 10, 0.1).name('Intensity'); - -const brushFolder = gui.addFolder('Brush Settings'); -brushFolder.add(params.hardness, 'value', 0, 0.99, 0.01).name('Hardness'); -brushFolder.add(params.radius, 'value', 0.1, 2.0, 0.01).name('Radius'); - -const effectFolder = gui.addFolder('Effects'); -effectFolder.add(params.afterImageDecay, 'value', 0.9, 0.999, 0.001).name('After Image Decay'); - -// Resize handling -function onResize() { - renderer.setSize(window.innerWidth, window.innerHeight); - camera.right = window.innerWidth; - camera.top = window.innerHeight; - camera.updateProjectionMatrix(); -} - -window.addEventListener('resize', onResize); - -// Main loop -renderer.setAnimationLoop(async () => { - renderPipeline.render(); -}); diff --git a/examples-testing/examples/webgpu_instance_mesh.ts b/examples-testing/examples/webgpu_instance_mesh.ts deleted file mode 100644 index 0abd2f26c..000000000 --- a/examples-testing/examples/webgpu_instance_mesh.ts +++ /dev/null @@ -1,98 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { mix, range, normalWorld, oscSine, time } from 'three/tsl'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer; - -let mesh; -const amount = parseInt(window.location.search.slice(1)) || 10; -const count = Math.pow(amount, 3); -const dummy = new THREE.Object3D(); - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(amount * 0.9, amount * 0.9, amount * 0.9); - camera.lookAt(0, 0, 0); - - scene = new THREE.Scene(); - - const material = new THREE.MeshBasicMaterial(); - - // random colors between instances from 0x000000 to 0xFFFFFF - const randomColors = range(new THREE.Color(0x000000), new THREE.Color(0xffffff)); - - material.colorNode = mix(normalWorld, randomColors, oscSine(time.mul(0.1))); - - const loader = new THREE.BufferGeometryLoader(); - loader.load('models/json/suzanne_buffergeometry.json', function (geometry) { - geometry.computeVertexNormals(); - geometry.scale(0.5, 0.5, 0.5); - - mesh = new THREE.InstancedMesh(geometry, material, count); - mesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage); - - scene.add(mesh); - - // - - const gui = renderer.inspector.createParameters('Settings'); - gui.add(mesh, 'count', 1, count, 1).name('instance count'); - }); - - // - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - render(); -} - -async function render() { - if (mesh) { - const time = Date.now() * 0.001; - - mesh.rotation.x = Math.sin(time / 4); - mesh.rotation.y = Math.sin(time / 2); - - let i = 0; - const offset = (amount - 1) / 2; - - for (let x = 0; x < amount; x++) { - for (let y = 0; y < amount; y++) { - for (let z = 0; z < amount; z++) { - dummy.position.set(offset - x, offset - y, offset - z); - dummy.rotation.y = Math.sin(x / 4 + time) + Math.sin(y / 4 + time) + Math.sin(z / 4 + time); - dummy.rotation.z = dummy.rotation.y * 2; - - dummy.updateMatrix(); - - mesh.setMatrixAt(i++, dummy.matrix); - } - } - } - } - - await renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_instance_path.ts b/examples-testing/examples/webgpu_instance_path.ts deleted file mode 100644 index a5bce3225..000000000 --- a/examples-testing/examples/webgpu_instance_path.ts +++ /dev/null @@ -1,147 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { - abs, - add, - instancedBufferAttribute, - positionLocal, - mod, - time, - sin, - vec3, - select, - float, - screenUV, - color, -} from 'three/tsl'; - -let camera, scene, renderer, controls; - -const count = 1000; - -init(); - -async function init() { - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.01, 100); - camera.position.z = 15; - - scene = new THREE.Scene(); - scene.backgroundNode = screenUV.distance(0.5).remap(0, 0.65).mix(color(0x94254c), color(0x000000)); - - // generate a path representing a heart shape - - const x = 0, - y = 0; - - const path = new THREE.Path() - .moveTo(x - 2.5, y - 2.5) - .bezierCurveTo(x - 2.5, y - 2.5, x - 2, y, x, y) - .bezierCurveTo(x + 3, y, x + 3, y - 3.5, x + 3, y - 3.5) - .bezierCurveTo(x + 3, y - 5.5, x + 1, y - 7.7, x - 2.5, y - 9.5) - .bezierCurveTo(x - 6, y - 7.7, x - 8, y - 5.5, x - 8, y - 3.5) - .bezierCurveTo(x - 8, y - 3.5, x - 8, y, x - 5, y) - .bezierCurveTo(x - 3.5, y, x - 2.5, y - 2.5, x - 2.5, y - 2.5); - - // generate instanced ico-spheres along the path - - const geometry = new THREE.IcosahedronGeometry(0.1); - const material = new THREE.MeshStandardNodeMaterial(); - - const mesh = new THREE.Mesh(geometry, material); - mesh.position.set(2.5, 5, 0); - mesh.count = count; - mesh.frustumCulled = false; - - scene.add(mesh); - - // instance data - - const v = new THREE.Vector3(); - const c = new THREE.Color(); - - const positions = []; - const times = []; - const seeds = []; - const colors = []; - - for (let i = 0; i < count; i++) { - const t = i / count; - path.getPointAt(t, v); - - v.x += 0.5 - Math.random(); - v.y += 0.5 - Math.random(); - v.z = 0.5 - Math.random(); - - positions.push(v.x, v.y, v.z); - times.push(t); - seeds.push(Math.random()); - - c.setHSL(0.75 + Math.random() * 0.25, 1, 0.4); - - colors.push(c.r, c.g, c.b); - } - - const positionAttribute = new THREE.InstancedBufferAttribute(new Float32Array(positions), 3); - const colorAttribute = new THREE.InstancedBufferAttribute(new Float32Array(colors), 3); - const timeAttribute = new THREE.InstancedBufferAttribute(new Float32Array(times), 1); - const seedAttribute = new THREE.InstancedBufferAttribute(new Float32Array(seeds), 1); - - // TSL - - const instancePosition = instancedBufferAttribute(positionAttribute); - const instanceColor = instancedBufferAttribute(colorAttribute); - const instanceSeed = instancedBufferAttribute(seedAttribute); - const instanceTime = instancedBufferAttribute(timeAttribute); - - const localTime = instanceTime.add(time); - const modTime = mod(time.mul(0.4), 1); - - const s0 = sin(localTime.add(instanceSeed)).mul(0.25); - - const dist = abs(instanceTime.sub(modTime)).toConst(); // modTime and instanceTime are in the range [0,1] - const wrapDist = select(dist.greaterThan(0.5), dist.oneMinus(), dist).toConst(); // the normalized distance should wrap around 0/1 - const s1 = select(wrapDist.greaterThan(0.1), float(1), wrapDist.remap(0, 0.1, 3, 1)); // compute a scale in a range around the current interpolated value - - const offset = vec3(instancePosition.x, instancePosition.y.add(s0), instancePosition.z).toConst('offset'); - material.positionNode = add(positionLocal.mul(s1), offset); - material.colorNode = instanceColor; - - // - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.toneMapping = THREE.NeutralToneMapping; - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - await renderer.init(); - - const pmremGenerator = new THREE.PMREMGenerator(renderer); - scene.environment = pmremGenerator.fromScene(new RoomEnvironment(), 0.04).texture; - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - controls.update(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_instance_points.ts b/examples-testing/examples/webgpu_instance_points.ts deleted file mode 100644 index 409129373..000000000 --- a/examples-testing/examples/webgpu_instance_points.ts +++ /dev/null @@ -1,203 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { - color, - storage, - Fn, - instancedBufferAttribute, - instanceIndex, - sin, - time, - float, - uniform, - shapeCircle, - mix, - vec3, -} from 'three/tsl'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import * as GeometryUtils from 'three/addons/utils/GeometryUtils.js'; - -let renderer, scene, camera, camera2, controls, backgroundNode; -let material; -let effectController; - -// viewport -let insetWidth; -let insetHeight; - -// compute -let computeSize; - -init(); - -async function init() { - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(-40, 0, 60); - - camera2 = new THREE.PerspectiveCamera(40, 1, 1, 1000); - camera2.position.copy(camera.position); - - backgroundNode = color(0x222222); - - effectController = { - pulseSpeed: uniform(6), - minWidth: uniform(6), - maxWidth: uniform(20), - }; - - // Position and THREE.Color Data - - const points = GeometryUtils.hilbert3D(new THREE.Vector3(0, 0, 0), 20.0, 1, 0, 1, 2, 3, 4, 5, 6, 7); - - const spline = new THREE.CatmullRomCurve3(points); - const divisions = Math.round(4 * points.length); - const point = new THREE.Vector3(); - const pointColor = new THREE.Color(); - - const positions = []; - const colors = []; - const sizes = new Float32Array(divisions); - - for (let i = 0, l = divisions; i < l; i++) { - const t = i / l; - - spline.getPoint(t, point); - positions.push(point.x, point.y, point.z); - - pointColor.setHSL(t, 1.0, 0.5, THREE.SRGBColorSpace); - colors.push(pointColor.r, pointColor.g, pointColor.b); - - sizes[i] = 10.0; - } - - // Instanced Points - - const positionAttribute = new THREE.InstancedBufferAttribute(new Float32Array(positions), 3); - const colorsAttribute = new THREE.InstancedBufferAttribute(new Float32Array(colors), 3); - - const instanceSizeBufferAttribute = new THREE.StorageInstancedBufferAttribute(sizes, 1); - const instanceSizeStorage = storage(instanceSizeBufferAttribute, 'float', instanceSizeBufferAttribute.count); - - computeSize = Fn(() => { - const { pulseSpeed, minWidth, maxWidth } = effectController; - - const relativeTime = time.add(float(instanceIndex)); - - const sizeFactor = sin(relativeTime.mul(pulseSpeed)).add(1).div(2); - - instanceSizeStorage.element(instanceIndex).assign(sizeFactor.mul(maxWidth.sub(minWidth)).add(minWidth)); - })().compute(divisions); - - // Material / Sprites - - const attributeRange = instancedBufferAttribute(instanceSizeBufferAttribute); - const pointColors = mix( - vec3(0.0), - instancedBufferAttribute(colorsAttribute), - attributeRange.div(float(effectController.maxWidth)), - ); - - material = new THREE.PointsNodeMaterial({ - colorNode: pointColors, - opacityNode: shapeCircle(), - positionNode: instancedBufferAttribute(positionAttribute), - // rotationNode: time, - sizeNode: instancedBufferAttribute(instanceSizeBufferAttribute), - // size: 40, // in pixels units - vertexColors: true, - sizeAttenuation: false, - alphaToCoverage: true, - }); - - const instancedPoints = new THREE.Sprite(material); - instancedPoints.count = divisions; - scene.add(instancedPoints); - - // Renderer / Controls - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - controls.minDistance = 10; - controls.maxDistance = 500; - - window.addEventListener('resize', onWindowResize); - onWindowResize(); - - // GUI - - const gui = renderer.inspector.createParameters('Settings'); - - gui.add(material, 'alphaToCoverage'); - - gui.add(effectController.minWidth, 'value', 1, 30, 1).name('minWidth'); - gui.add(effectController.maxWidth, 'value', 2, 30, 1).name('maxWidth'); - gui.add(effectController.pulseSpeed, 'value', 1, 20, 0.1).name('pulseSpeed'); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - insetWidth = window.innerHeight / 4; // square - insetHeight = window.innerHeight / 4; - - camera2.aspect = insetWidth / insetHeight; - camera2.updateProjectionMatrix(); -} - -function animate() { - // compute - - renderer.compute(computeSize); - - // main scene - - renderer.setViewport(0, 0, window.innerWidth, window.innerHeight); - - controls.update(); - - renderer.autoClear = true; - - scene.backgroundNode = null; - - renderer.render(scene, camera); - - // inset scene - - const posY = window.innerHeight - insetHeight - 20; - - renderer.clearDepth(); // important! - - renderer.setScissorTest(true); - - renderer.setScissor(20, posY, insetWidth, insetHeight); - - renderer.setViewport(20, posY, insetWidth, insetHeight); - - camera2.position.copy(camera.position); - - camera2.quaternion.copy(camera.quaternion); - - renderer.autoClear = false; - - scene.backgroundNode = backgroundNode; - - renderer.render(scene, camera2); - - renderer.setScissorTest(false); -} - -// diff --git a/examples-testing/examples/webgpu_instancing_morph.ts b/examples-testing/examples/webgpu_instancing_morph.ts deleted file mode 100644 index f8f5c5fe5..000000000 --- a/examples-testing/examples/webgpu_instancing_morph.ts +++ /dev/null @@ -1,145 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer, mesh, mixer, dummy; - -const offset = 5000; - -const timeOffsets = new Float32Array(1024); - -for (let i = 0; i < 1024; i++) { - timeOffsets[i] = Math.random() * 3; -} - -const timer = new THREE.Timer(); -timer.connect(document); - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 100, 10000); - - scene = new THREE.Scene(); - - scene.background = new THREE.Color(0x99ddff); - - scene.fog = new THREE.Fog(0x99ddff, 5000, 10000); - - // - - const light = new THREE.DirectionalLight(0xffffff, 1); - - light.position.set(200, 1000, 50); - - light.shadow.mapSize.width = 2048; - light.shadow.mapSize.height = 2048; - light.castShadow = true; - - light.shadow.camera.left = -5000; - light.shadow.camera.right = 5000; - light.shadow.camera.top = 5000; - light.shadow.camera.bottom = -5000; - light.shadow.camera.far = 2000; - - light.shadow.camera.updateProjectionMatrix(); - - scene.add(light); - - const hemi = new THREE.HemisphereLight(0x99ddff, 0x669933, 1 / 3); - - scene.add(hemi); - - const ground = new THREE.Mesh( - new THREE.PlaneGeometry(1000000, 1000000), - new THREE.MeshStandardMaterial({ color: 0x669933 }), - ); - - ground.rotation.x = -Math.PI / 2; - - ground.receiveShadow = true; - - scene.add(ground); - - const loader = new GLTFLoader(); - - loader.load('models/gltf/Horse.glb', function (glb) { - dummy = glb.scene.children[0]; - - mesh = new THREE.InstancedMesh( - dummy.geometry, - new THREE.MeshStandardNodeMaterial({ - flatShading: true, - }), - 1024, - ); - - mesh.castShadow = true; - - for (let x = 0, i = 0; x < 32; x++) { - for (let y = 0; y < 32; y++) { - dummy.position.set(offset - 300 * x + 200 * Math.random(), 0, offset - 300 * y); - - dummy.updateMatrix(); - - mesh.setMatrixAt(i, dummy.matrix); - - mesh.setColorAt(i, new THREE.Color(`hsl(${Math.random() * 360}, 50%, 66%)`)); - - i++; - } - } - - scene.add(mesh); - - mixer = new THREE.AnimationMixer(glb.scene); - - const action = mixer.clipAction(glb.animations[0]); - - action.play(); - }); - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setAnimationLoop(animate); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.shadowMap.enabled = true; - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - timer.update(); - - const time = timer.getElapsed(); - - const r = 3000; - camera.position.set(Math.sin(time / 10) * r, 1500 + 1000 * Math.cos(time / 5), Math.cos(time / 10) * r); - camera.lookAt(0, 0, 0); - - if (mesh) { - for (let i = 0; i < 1024; i++) { - mixer.setTime(time + timeOffsets[i]); - - mesh.setMorphAt(i, dummy); - } - - mesh.morphTexture.needsUpdate = true; - } - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_layers.ts b/examples-testing/examples/webgpu_layers.ts deleted file mode 100644 index c1db7d60c..000000000 --- a/examples-testing/examples/webgpu_layers.ts +++ /dev/null @@ -1,144 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { positionLocal, time, mod, instancedBufferAttribute, rotate, screenUV, color, vec2 } from 'three/tsl'; - -let camera, scene, renderer; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); - camera.layers.enable(0); // enabled by default - camera.layers.enable(1); - camera.layers.enable(2); - - camera.position.z = 10; - - scene = new THREE.Scene(); - - const horizontalEffect = screenUV.x.mix(color(0xf996ae), color(0xf6f0a3)); - const lightEffect = screenUV.distance(vec2(0.5, 1.0)).oneMinus().mul(color(0xd9b6fd)); - - scene.backgroundNode = horizontalEffect.add(lightEffect); - - const sprite = new THREE.TextureLoader().load('textures/sprites/blossom.png'); - sprite.colorSpace = THREE.SRGBColorSpace; - - const count = 2500; - - const geometry = new THREE.PlaneGeometry(0.25, 0.25); - - const colors = [0xd70654, 0xffd95f, 0xb8d576]; - - for (let i = 0; i < 3; i++) { - const particles = new THREE.Mesh(geometry, getMaterial(count, colors[i], sprite)); - particles.layers.set(i); - particles.count = count; - scene.add(particles); - } - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // GUI - - const layers = { - Red: true, - Yellow: true, - Green: true, - }; - - const gui = renderer.inspector.createParameters('Layers'); - - gui.add(layers, 'Red').onChange(() => { - camera.layers.toggle(0); - }); - - gui.add(layers, 'Yellow').onChange(() => { - camera.layers.toggle(1); - }); - - gui.add(layers, 'Green').onChange(() => { - camera.layers.toggle(2); - }); - - // - - window.addEventListener('resize', onWindowResize); -} - -function getMaterial(count, color, sprite) { - // instance data - - const positions = []; - const rotations = []; - const directions = []; - const timeOffsets = []; - - const v = new THREE.Vector3(); - - for (let i = 0; i < count; i++) { - positions.push( - THREE.MathUtils.randFloat(-25, -20), - THREE.MathUtils.randFloat(-10, 50), - THREE.MathUtils.randFloat(-5, 5), - ); - - v.set(THREE.MathUtils.randFloat(0.7, 0.9), THREE.MathUtils.randFloat(-0.3, -0.15), 0).normalize(); - - rotations.push(Math.random(), Math.random(), Math.random()); - - directions.push(v.x, v.y, v.z); - - timeOffsets.push(i / count); - } - - const positionAttribute = new THREE.InstancedBufferAttribute(new Float32Array(positions), 3); - const rotationAttribute = new THREE.InstancedBufferAttribute(new Float32Array(rotations), 3); - const directionAttribute = new THREE.InstancedBufferAttribute(new Float32Array(directions), 3); - const timeAttribute = new THREE.InstancedBufferAttribute(new Float32Array(timeOffsets), 1); - - // material - - const material = new THREE.MeshBasicNodeMaterial({ - color: color, - map: sprite, - alphaMap: sprite, - alphaTest: 0.1, - side: THREE.DoubleSide, - forceSinglePass: true, - }); - - // TSL - - const instancePosition = instancedBufferAttribute(positionAttribute); - const instanceDirection = instancedBufferAttribute(directionAttribute); - const instanceRotation = instancedBufferAttribute(rotationAttribute); - - const localTime = instancedBufferAttribute(timeAttribute).add(time.mul(0.02)); - const modTime = mod(localTime, 1.0); - - const rotatedPosition = rotate(positionLocal, instanceRotation.mul(modTime.mul(20))); - material.positionNode = rotatedPosition.add(instancePosition).add(instanceDirection.mul(modTime.mul(50))); - - return material; -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_lensflares.ts b/examples-testing/examples/webgpu_lensflares.ts deleted file mode 100644 index ab4e49677..000000000 --- a/examples-testing/examples/webgpu_lensflares.ts +++ /dev/null @@ -1,137 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { FlyControls } from 'three/addons/controls/FlyControls.js'; -import { LensflareMesh, LensflareElement } from 'three/addons/objects/LensflareMesh.js'; -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let container; - -let camera, scene, renderer; -let controls; - -const timer = new THREE.Timer(); -timer.connect(document); - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - // camera - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 15000); - camera.position.z = 250; - - // scene - - scene = new THREE.Scene(); - scene.background = new THREE.Color().setHSL(0.51, 0.4, 0.01, THREE.SRGBColorSpace); - scene.fog = new THREE.Fog(scene.background, 3500, 15000); - - // world - - const s = 250; - - const geometry = new THREE.BoxGeometry(s, s, s); - const material = new THREE.MeshPhongNodeMaterial({ color: 0xffffff, specular: 0xffffff, shininess: 50 }); - - for (let i = 0; i < 3000; i++) { - const mesh = new THREE.Mesh(geometry, material); - - mesh.position.x = 8000 * (2.0 * Math.random() - 1.0); - mesh.position.y = 8000 * (2.0 * Math.random() - 1.0); - mesh.position.z = 8000 * (2.0 * Math.random() - 1.0); - - mesh.rotation.x = Math.random() * Math.PI; - mesh.rotation.y = Math.random() * Math.PI; - mesh.rotation.z = Math.random() * Math.PI; - - mesh.matrixAutoUpdate = false; - mesh.updateMatrix(); - - scene.add(mesh); - } - - // lights - - const dirLight = new THREE.DirectionalLight(0xffffff, 0.15); - dirLight.position.set(0, -1, 0).normalize(); - dirLight.color.setHSL(0.1, 0.7, 0.5); - scene.add(dirLight); - - // lensflares - const textureLoader = new THREE.TextureLoader(); - - const textureFlare0 = textureLoader.load('textures/lensflare/lensflare0.png'); - const textureFlare3 = textureLoader.load('textures/lensflare/lensflare3.png'); - - textureFlare0.colorSpace = THREE.SRGBColorSpace; - textureFlare3.colorSpace = THREE.SRGBColorSpace; - - addLight(0.55, 0.95, 0.6, 5000, 0, -1000); - addLight(0.1, 0.85, 0.65, 0, 0, -1000); - addLight(0.995, 0.5, 0.95, 5000, 5000, -1000); - - function addLight(h, s, l, x, y, z) { - const light = new THREE.PointLight(0xffffff, 1.5, 2000, 0); - light.color.setHSL(h, s, l); - light.position.set(x, y, z); - scene.add(light); - - const lensflare = new LensflareMesh(); - lensflare.addElement(new LensflareElement(textureFlare0, 700, 0, light.color)); - lensflare.addElement(new LensflareElement(textureFlare3, 60, 0.6)); - lensflare.addElement(new LensflareElement(textureFlare3, 70, 0.7)); - lensflare.addElement(new LensflareElement(textureFlare3, 120, 0.9)); - lensflare.addElement(new LensflareElement(textureFlare3, 70, 1)); - light.add(lensflare); - } - - // renderer - - renderer = new THREE.WebGPURenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - container.appendChild(renderer.domElement); - - // - - controls = new FlyControls(camera, renderer.domElement); - - controls.movementSpeed = 2500; - controls.domElement = container; - controls.rollSpeed = Math.PI / 6; - controls.autoForward = false; - controls.dragToLook = false; - - // events - - window.addEventListener('resize', onWindowResize); -} - -// - -function onWindowResize() { - renderer.setSize(window.innerWidth, window.innerHeight); - - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); -} - -// - -function animate() { - timer.update(); - - render(); -} - -function render() { - const delta = timer.getDelta(); - - controls.update(delta); - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_lightprobe.ts b/examples-testing/examples/webgpu_lightprobe.ts deleted file mode 100644 index 3e90b6f66..000000000 --- a/examples-testing/examples/webgpu_lightprobe.ts +++ /dev/null @@ -1,136 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { LightProbeGenerator } from 'three/addons/lights/LightProbeGenerator.js'; - -import { LightProbeHelper } from 'three/addons/helpers/LightProbeHelperGPU.js'; - -let mesh, renderer, scene, camera; - -let gui; - -let lightProbe; -let directionalLight; - -// linear color space -const API = { - lightProbeIntensity: 1.0, - directionalLightIntensity: 0.6, - envMapIntensity: 1, -}; - -init(); - -function init() { - // renderer - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // tone mapping - renderer.toneMapping = THREE.NoToneMapping; - - // scene - scene = new THREE.Scene(); - - // camera - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(0, 0, 30); - - // controls - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 10; - controls.maxDistance = 50; - controls.enablePan = false; - - // probe - lightProbe = new THREE.LightProbe(); - scene.add(lightProbe); - - // light - directionalLight = new THREE.DirectionalLight(0xffffff, API.directionalLightIntensity); - directionalLight.position.set(10, 10, 10); - scene.add(directionalLight); - - // envmap - const genCubeUrls = function (prefix, postfix) { - return [ - prefix + 'px' + postfix, - prefix + 'nx' + postfix, - prefix + 'py' + postfix, - prefix + 'ny' + postfix, - prefix + 'pz' + postfix, - prefix + 'nz' + postfix, - ]; - }; - - const urls = genCubeUrls('textures/cube/pisa/', '.png'); - - new THREE.CubeTextureLoader().load(urls, function (cubeTexture) { - scene.background = cubeTexture; - - lightProbe.copy(LightProbeGenerator.fromCubeTexture(cubeTexture)); - lightProbe.intensity = API.lightProbeIntensity; - lightProbe.position.set(-10, 0, 0); // position not used in scene lighting calculations (helper honors the position, however) - - const geometry = new THREE.SphereGeometry(5, 64, 32); - //const geometry = new THREE.TorusKnotGeometry( 4, 1.5, 256, 32, 2, 3 ); - - const material = new THREE.MeshStandardMaterial({ - color: 0xffffff, - metalness: 0, - roughness: 0, - envMap: cubeTexture, - envMapIntensity: API.envMapIntensity, - }); - - // mesh - mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - // helper - const helper = new LightProbeHelper(lightProbe, 1); - scene.add(helper); - }); - - // gui - gui = renderer.inspector.createParameters('Intensity'); - - gui.add(API, 'lightProbeIntensity', 0, 1, 0.02) - .name('light probe') - .onChange(function () { - lightProbe.intensity = API.lightProbeIntensity; - }); - - gui.add(API, 'directionalLightIntensity', 0, 1, 0.02) - .name('directional light') - .onChange(function () { - directionalLight.intensity = API.directionalLightIntensity; - }); - - gui.add(API, 'envMapIntensity', 0, 1, 0.02) - .name('envMap') - .onChange(function () { - mesh.material.envMapIntensity = API.envMapIntensity; - }); - - // listener - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - renderer.setSize(window.innerWidth, window.innerHeight); - - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); -} - -function animate() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_lightprobe_cubecamera.ts b/examples-testing/examples/webgpu_lightprobe_cubecamera.ts deleted file mode 100644 index 60fe6ccc9..000000000 --- a/examples-testing/examples/webgpu_lightprobe_cubecamera.ts +++ /dev/null @@ -1,90 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { LightProbeHelper } from 'three/addons/helpers/LightProbeHelperGPU.js'; -import { LightProbeGenerator } from 'three/addons/lights/LightProbeGenerator.js'; - -let renderer, scene, camera, cubeCamera; - -let lightProbe; - -init(); - -function init() { - // renderer - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // scene - scene = new THREE.Scene(); - - // camera - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(0, 0, 30); - - const cubeRenderTarget = new THREE.CubeRenderTarget(256); - - cubeCamera = new THREE.CubeCamera(1, 1000, cubeRenderTarget); - - // controls - const controls = new OrbitControls(camera, renderer.domElement); - controls.addEventListener('change', render); - controls.minDistance = 10; - controls.maxDistance = 50; - controls.enablePan = false; - - // probe - lightProbe = new THREE.LightProbe(); - scene.add(lightProbe); - - // envmap - const genCubeUrls = function (prefix, postfix) { - return [ - prefix + 'px' + postfix, - prefix + 'nx' + postfix, - prefix + 'py' + postfix, - prefix + 'ny' + postfix, - prefix + 'pz' + postfix, - prefix + 'nz' + postfix, - ]; - }; - - const urls = genCubeUrls('textures/cube/pisa/', '.png'); - - new THREE.CubeTextureLoader().load(urls, async function (cubeTexture) { - scene.background = cubeTexture; - - await renderer.init(); - - cubeCamera.update(renderer, scene); - - const probe = await LightProbeGenerator.fromCubeRenderTarget(renderer, cubeRenderTarget); - - lightProbe.copy(probe); - - scene.add(new LightProbeHelper(lightProbe, 5)); - - render(); - }); - - // listener - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - renderer.setSize(window.innerWidth, window.innerHeight); - - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - render(); -} - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_lights_dynamic.ts b/examples-testing/examples/webgpu_lights_dynamic.ts deleted file mode 100644 index b08c5902f..000000000 --- a/examples-testing/examples/webgpu_lights_dynamic.ts +++ /dev/null @@ -1,242 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { DynamicLighting } from 'three/addons/lighting/DynamicLighting.js'; -import { Inspector } from 'three/addons/inspector/Inspector.js'; -import Stats from 'three/addons/libs/stats.module.js'; - -let camera, scene, renderer, controls, timer, stats; - -const pointLights = []; -let autoAddInterval = null; - -const params = { - dynamic: true, - autoAdd: false, - lightCount: 2, - addLight() { - addLight(); - }, - removeLight() { - removeLight(); - }, - removeAll() { - removeAllLights(); - }, -}; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 200); - camera.position.set(0, 15, 30); - - scene = new THREE.Scene(); - - timer = new THREE.Timer(); - timer.connect(document); - - // Stats - - stats = new Stats(); - document.body.appendChild(stats.dom); - - // Floor - - const floorGeometry = new THREE.PlaneGeometry(120, 120); - const floorMaterial = new THREE.MeshStandardMaterial({ color: 0x444444, roughness: 0.8 }); - const floor = new THREE.Mesh(floorGeometry, floorMaterial); - floor.rotation.x = -Math.PI / 2; - scene.add(floor); - - // Shared geometries - - const sphereGeometry = new THREE.SphereGeometry(0.8, 128, 128); - const boxGeometry = new THREE.BoxGeometry(1.2, 1.2, 1.2, 64, 64, 64); - const torusGeometry = new THREE.TorusGeometry(0.6, 0.25, 128, 128); - const cylinderGeometry = new THREE.CylinderGeometry(0.5, 0.5, 1.4, 128, 64); - const coneGeometry = new THREE.ConeGeometry(0.6, 1.4, 128, 64); - - const geometries = [sphereGeometry, boxGeometry, torusGeometry, cylinderGeometry, coneGeometry]; - - // 100 meshes — first 50 with unique PBR materials, remaining 50 reuse them - - const uniqueMaterials = []; - - for (let i = 0; i < 50; i++) { - const material = new THREE.MeshStandardMaterial({ - color: new THREE.Color().setHSL(i / 50, 0.6 + Math.random() * 0.4, 0.35 + Math.random() * 0.3), - roughness: Math.random(), - metalness: Math.random(), - }); - material.name = 'Standard_' + i; - - uniqueMaterials.push(material); - } - - const meshesPerRing = 10; - - for (let i = 0; i < 100; i++) { - const material = i < 50 ? uniqueMaterials[i] : uniqueMaterials[i - 50]; - const geometry = geometries[i % geometries.length]; - const mesh = new THREE.Mesh(geometry, material); - - const ring = Math.floor(i / meshesPerRing); - const indexInRing = i % meshesPerRing; - const angle = (indexInRing / meshesPerRing) * Math.PI * 2 + ring * 0.3; - const radius = 6 + ring * 4; - - mesh.position.set(Math.cos(angle) * radius, 0.7 + Math.random() * 2, Math.sin(angle) * radius); - - mesh.rotation.set(Math.random() * Math.PI, Math.random() * Math.PI, 0); - - scene.add(mesh); - } - - // Center sphere - - const centerMaterial = new THREE.MeshStandardMaterial({ color: 0xffffff, roughness: 0.1, metalness: 0.9 }); - const centerSphere = new THREE.Mesh(new THREE.SphereGeometry(2, 128, 128), centerMaterial); - centerSphere.position.y = 2; - scene.add(centerSphere); - - // Ambient light - - scene.add(new THREE.AmbientLight(0x404040, 0.5)); - - // Start with a couple point lights - - addLight(); - addLight(); - - // Renderer - - createRenderer(); - - // Inspector GUI - - window.addEventListener('resize', onWindowResize); -} - -function createRenderer() { - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - if (params.dynamic) renderer.lighting = new DynamicLighting(); - - // Inspector GUI - - const gui = renderer.inspector.createParameters('Dynamic Lights'); - - gui.add(params, 'dynamic') - .name('dynamic mode') - .onChange(() => { - renderer.dispose(); - document.body.removeChild(renderer.domElement); - clearInterval(autoAddInterval); - autoAddInterval = null; - params.autoAdd = false; - createRenderer(); - }); - - gui.add(params, 'autoAdd') - .name('auto-add lights') - .onChange(value => { - if (value) { - autoAddInterval = setInterval(() => { - addLight(); - }, 500); - } else { - clearInterval(autoAddInterval); - autoAddInterval = null; - } - }); - - gui.add(params, 'addLight').name('add light'); - gui.add(params, 'removeLight').name('remove light'); - gui.add(params, 'removeAll').name('remove all lights'); - gui.add(params, 'lightCount').name('point lights').listen(); - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - controls.target.set(0, 2, 0); - controls.update(); -} - -function addLight() { - const color = new THREE.Color().setHSL(Math.random(), 0.8, 0.5); - const light = new THREE.PointLight(color, 1000); - - const angle = Math.random() * Math.PI * 2; - const radius = 5 + Math.random() * 20; - light.position.set(Math.cos(angle) * radius, 1 + Math.random() * 6, Math.sin(angle) * radius); - - light.userData.angle = angle; - light.userData.radius = radius; - light.userData.speed = 0.2 + Math.random() * 0.8; - light.userData.baseY = light.position.y; - - // Visual indicator - const sphere = new THREE.Mesh(new THREE.SphereGeometry(0.15, 8, 8), new THREE.MeshBasicMaterial({ color: color })); - light.add(sphere); - - scene.add(light); - pointLights.push(light); - - params.lightCount = pointLights.length; -} - -function removeLight() { - if (pointLights.length === 0) return; - - const light = pointLights.pop(); - scene.remove(light); - light.dispose(); - - params.lightCount = pointLights.length; -} - -function removeAllLights() { - while (pointLights.length > 0) { - const light = pointLights.pop(); - scene.remove(light); - light.dispose(); - } - - params.lightCount = 0; -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - const time = timer.getElapsed(); - - controls.update(); - - // Animate lights - - for (let i = 0; i < pointLights.length; i++) { - const light = pointLights[i]; - const d = light.userData; - const t = time * d.speed + d.angle; - - light.position.x = Math.cos(t) * d.radius; - light.position.z = Math.sin(t) * d.radius; - light.position.y = d.baseY + Math.sin(t * 2) * 0.5; - } - - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/webgpu_lights_ies_spotlight.ts b/examples-testing/examples/webgpu_lights_ies_spotlight.ts deleted file mode 100644 index edfc51efa..000000000 --- a/examples-testing/examples/webgpu_lights_ies_spotlight.ts +++ /dev/null @@ -1,168 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { OrbitControls } from './jsm/controls/OrbitControls.js'; - -import { IESLoader } from 'three/addons/loaders/IESLoader.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let renderer, scene, camera; -let lights; - -async function init() { - const iesLoader = new IESLoader().setPath('./ies/'); - //iesLoader.type = THREE.UnsignedByteType; // LDR - - const [iesTexture1, iesTexture2, iesTexture3, iesTexture4] = await Promise.all([ - iesLoader.loadAsync('007cfb11e343e2f42e3b476be4ab684e.ies'), - iesLoader.loadAsync('06b4cfdc8805709e767b5e2e904be8ad.ies'), - iesLoader.loadAsync('02a7562c650498ebb301153dbbf59207.ies'), - iesLoader.loadAsync('1a936937a49c63374e6d4fbed9252b29.ies'), - ]); - - // - - scene = new THREE.Scene(); - - // - - const spotLight = new THREE.IESSpotLight(0xff0000, 500); - spotLight.position.set(6.5, 3, 6.5); - spotLight.angle = Math.PI / 8; - spotLight.penumbra = 0.7; - spotLight.distance = 20; - spotLight.castShadow = true; - spotLight.iesMap = iesTexture1; - spotLight.userData.helper = new THREE.SpotLightHelper(spotLight); - scene.add(spotLight); - scene.add(spotLight.target); - scene.add(spotLight.userData.helper); - - // - - const spotLight2 = new THREE.IESSpotLight(0x00ff00, 500); - spotLight2.position.set(-6.5, 3, 6.5); - spotLight2.angle = Math.PI / 8; - spotLight2.penumbra = 0.7; - spotLight2.distance = 20; - spotLight2.castShadow = true; - spotLight2.iesMap = iesTexture2; - spotLight2.userData.helper = new THREE.SpotLightHelper(spotLight2); - scene.add(spotLight2); - scene.add(spotLight2.target); - scene.add(spotLight2.userData.helper); - - // - - const spotLight3 = new THREE.IESSpotLight(0x0000ff, 500); - spotLight3.position.set(-6.5, 3, -6.5); - spotLight3.angle = Math.PI / 8; - spotLight3.penumbra = 0.7; - spotLight3.distance = 20; - spotLight3.castShadow = true; - spotLight3.iesMap = iesTexture3; - spotLight3.userData.helper = new THREE.SpotLightHelper(spotLight3); - scene.add(spotLight3); - scene.add(spotLight3.target); - scene.add(spotLight3.userData.helper); - - // - - const spotLight4 = new THREE.IESSpotLight(0xffffff, 500); - spotLight4.position.set(6.5, 3, -6.5); - spotLight4.angle = Math.PI / 8; - spotLight4.penumbra = 0.7; - spotLight4.distance = 20; - spotLight4.castShadow = true; - spotLight4.iesMap = iesTexture4; - spotLight4.userData.helper = new THREE.SpotLightHelper(spotLight4); - scene.add(spotLight4); - scene.add(spotLight4.target); - scene.add(spotLight4.userData.helper); - - // - - lights = [spotLight, spotLight2, spotLight3, spotLight4]; - - // - - const material = new THREE.MeshPhongMaterial({ color: 0x999999 /*, dithering: true*/ }); - - const geometry = new THREE.PlaneGeometry(200, 200); - - const mesh = new THREE.Mesh(geometry, material); - mesh.rotation.x = -Math.PI * 0.5; - mesh.receiveShadow = true; - scene.add(mesh); - - const geometry2 = new THREE.BoxGeometry(2, 2, 2); - //const geometry2 = new THREE.IcosahedronGeometry( 1, 5 ); - - const mesh2 = new THREE.Mesh(geometry2, material); - mesh2.position.y = 1; - mesh2.castShadow = true; - scene.add(mesh2); - - // - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(render); - renderer.shadowMap.enabled = true; - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(16, 4, 1); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 2; - controls.maxDistance = 50; - controls.enablePan = false; - - // - - function setHelperVisible(value) { - for (let i = 0; i < lights.length; i++) { - lights[i].userData.helper.visible = value; - } - } - - setHelperVisible(false); - - // - - const gui = renderer.inspector.createParameters('Settings'); - gui.add({ helper: false }, 'helper').onChange(v => setHelperVisible(v)); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function render(time) { - time = time / 1000; - - for (let i = 0; i < lights.length; i++) { - const t = (Math.sin((time + i) * (Math.PI / 2)) + 1) / 2; - - const x = THREE.MathUtils.lerp(lights[i].position.x, 0, t); - const z = THREE.MathUtils.lerp(lights[i].position.z, 0, t); - - lights[i].target.position.x = x; - lights[i].target.position.z = z; - if (lights[i].userData.helper) lights[i].userData.helper.update(); - } - - renderer.render(scene, camera); -} - -init(); diff --git a/examples-testing/examples/webgpu_lights_phong.ts b/examples-testing/examples/webgpu_lights_phong.ts deleted file mode 100644 index c2cdf4393..000000000 --- a/examples-testing/examples/webgpu_lights_phong.ts +++ /dev/null @@ -1,136 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { color, fog, rangeFogFactor, checker, uv, mix, texture, lights, normalMap } from 'three/tsl'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { TeapotGeometry } from 'three/addons/geometries/TeapotGeometry.js'; - -let camera, scene, renderer, light1, light2, light3, light4, controls; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.01, 100); - camera.position.z = 7; - - scene = new THREE.Scene(); - scene.fogNode = fog(color(0xff00ff), rangeFogFactor(12, 30)); - - const sphereGeometry = new THREE.SphereGeometry(0.1, 16, 8); - - // textures - - const textureLoader = new THREE.TextureLoader(); - - const normalMapTexture = textureLoader.load('./textures/water/Water_1_M_Normal.jpg'); - normalMapTexture.wrapS = THREE.RepeatWrapping; - normalMapTexture.wrapT = THREE.RepeatWrapping; - - const alphaTexture = textureLoader.load('./textures/roughness_map.jpg'); - alphaTexture.wrapS = THREE.RepeatWrapping; - alphaTexture.wrapT = THREE.RepeatWrapping; - - // lights - - const addLight = (hexColor, power = 1700, distance = 100) => { - const material = new THREE.MeshPhongNodeMaterial(); - material.colorNode = color(hexColor); - material.lights = false; - - const mesh = new THREE.Mesh(sphereGeometry, material); - - const light = new THREE.PointLight(hexColor, 1, distance); - light.power = power; - light.add(mesh); - - scene.add(light); - - return light; - }; - - light1 = addLight(0x0040ff); - light2 = addLight(0xffffff); - light3 = addLight(0x80ff80); - light4 = addLight(0xffaa00); - - // light nodes ( selective lights ) - - const blueLightsNode = lights([light1]); - const whiteLightsNode = lights([light2]); - - // models - - const geometryTeapot = new TeapotGeometry(0.8, 18); - - const leftObject = new THREE.Mesh(geometryTeapot, new THREE.MeshPhongNodeMaterial({ color: 0x555555 })); - leftObject.material.lightsNode = blueLightsNode; - leftObject.material.specularNode = texture(alphaTexture); - leftObject.position.x = -3; - scene.add(leftObject); - - const centerObject = new THREE.Mesh(geometryTeapot, new THREE.MeshPhongNodeMaterial({ color: 0x555555 })); - centerObject.material.normalNode = normalMap(texture(normalMapTexture)); - centerObject.material.shininess = 80; - scene.add(centerObject); - - const rightObject = new THREE.Mesh(geometryTeapot, new THREE.MeshPhongNodeMaterial({ color: 0x555555 })); - rightObject.material.lightsNode = whiteLightsNode; - //rightObject.material.specular.setHex( 0xFF00FF ); - rightObject.material.specularNode = mix(color(0x0000ff), color(0xff0000), checker(uv().mul(5))); - rightObject.material.shininess = 90; - rightObject.position.x = 3; - scene.add(rightObject); - - leftObject.rotation.y = centerObject.rotation.y = rightObject.rotation.y = Math.PI * -0.5; - leftObject.position.y = centerObject.position.y = rightObject.position.y = -1; - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // controls - - controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 3; - controls.maxDistance = 25; - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - const time = performance.now() / 1000; - const lightTime = time * 0.5; - - light1.position.x = Math.sin(lightTime * 0.7) * 3; - light1.position.y = Math.cos(lightTime * 0.5) * 4; - light1.position.z = Math.cos(lightTime * 0.3) * 3; - - light2.position.x = Math.cos(lightTime * 0.3) * 3; - light2.position.y = Math.sin(lightTime * 0.5) * 4; - light2.position.z = Math.sin(lightTime * 0.7) * 3; - - light3.position.x = Math.sin(lightTime * 0.7) * 3; - light3.position.y = Math.cos(lightTime * 0.3) * 4; - light3.position.z = Math.sin(lightTime * 0.5) * 3; - - light4.position.x = Math.sin(lightTime * 0.3) * 3; - light4.position.y = Math.cos(lightTime * 0.7) * 4; - light4.position.z = Math.sin(lightTime * 0.5) * 3; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_lights_physical.ts b/examples-testing/examples/webgpu_lights_physical.ts deleted file mode 100644 index 43d7ce723..000000000 --- a/examples-testing/examples/webgpu_lights_physical.ts +++ /dev/null @@ -1,236 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let camera, scene, renderer, bulbLight, bulbMat, hemiLight; -let ballMat, cubeMat, floorMat; - -let previousShadowMap = false; - -// ref for lumens: http://www.power-sure.com/lumens.htm -const bulbLuminousPowers = { - '110000 lm (1000W)': 110000, - '3500 lm (300W)': 3500, - '1700 lm (100W)': 1700, - '800 lm (60W)': 800, - '400 lm (40W)': 400, - '180 lm (25W)': 180, - '20 lm (4W)': 20, - Off: 0, -}; - -// ref for solar irradiances: https://en.wikipedia.org/wiki/Lux -const hemiLuminousIrradiances = { - '0.0001 lx (Moonless Night)': 0.0001, - '0.002 lx (Night Airglow)': 0.002, - '0.5 lx (Full Moon)': 0.5, - '3.4 lx (City Twilight)': 3.4, - '50 lx (Living Room)': 50, - '100 lx (Very Overcast)': 100, - '350 lx (Office Room)': 350, - '400 lx (Sunrise/Sunset)': 400, - '1000 lx (Overcast)': 1000, - '18000 lx (Daylight)': 18000, - '50000 lx (Direct Sun)': 50000, -}; - -const params = { - shadows: true, - exposure: 0.68, - bulbPower: Object.keys(bulbLuminousPowers)[4], - hemiIrradiance: Object.keys(hemiLuminousIrradiances)[0], -}; - -init(); - -function init() { - const container = document.getElementById('container'); - - // - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.x = -4; - camera.position.z = 4; - camera.position.y = 2; - - scene = new THREE.Scene(); - - const bulbGeometry = new THREE.SphereGeometry(0.02, 16, 8); - bulbLight = new THREE.PointLight(0xffee88, 1, 100, 2); - - bulbMat = new THREE.MeshStandardMaterial({ - emissive: 0xffffee, - emissiveIntensity: 1, - color: 0x000000, - }); - bulbLight.add(new THREE.Mesh(bulbGeometry, bulbMat)); - bulbLight.position.set(0, 2, 0); - bulbLight.castShadow = true; - scene.add(bulbLight); - - hemiLight = new THREE.HemisphereLight(0xddeeff, 0x0f0e0d, 0.02); - scene.add(hemiLight); - - floorMat = new THREE.MeshStandardMaterial({ - roughness: 0.8, - color: 0xffffff, - metalness: 0.2, - bumpScale: 1, - }); - const textureLoader = new THREE.TextureLoader(); - textureLoader.load('textures/hardwood2_diffuse.jpg', function (map) { - map.wrapS = THREE.RepeatWrapping; - map.wrapT = THREE.RepeatWrapping; - map.anisotropy = 4; - map.repeat.set(10, 24); - map.colorSpace = THREE.SRGBColorSpace; - floorMat.map = map; - floorMat.needsUpdate = true; - }); - textureLoader.load('textures/hardwood2_bump.jpg', function (map) { - map.wrapS = THREE.RepeatWrapping; - map.wrapT = THREE.RepeatWrapping; - map.anisotropy = 4; - map.repeat.set(10, 24); - floorMat.bumpMap = map; - floorMat.needsUpdate = true; - }); - textureLoader.load('textures/hardwood2_roughness.jpg', function (map) { - map.wrapS = THREE.RepeatWrapping; - map.wrapT = THREE.RepeatWrapping; - map.anisotropy = 4; - map.repeat.set(10, 24); - floorMat.roughnessMap = map; - floorMat.needsUpdate = true; - }); - - cubeMat = new THREE.MeshStandardMaterial({ - roughness: 0.7, - color: 0xffffff, - bumpScale: 1, - metalness: 0.2, - }); - textureLoader.load('textures/brick_diffuse.jpg', function (map) { - map.wrapS = THREE.RepeatWrapping; - map.wrapT = THREE.RepeatWrapping; - map.anisotropy = 4; - map.repeat.set(1, 1); - map.colorSpace = THREE.SRGBColorSpace; - cubeMat.map = map; - cubeMat.needsUpdate = true; - }); - textureLoader.load('textures/brick_bump.jpg', function (map) { - map.wrapS = THREE.RepeatWrapping; - map.wrapT = THREE.RepeatWrapping; - map.anisotropy = 4; - map.repeat.set(1, 1); - cubeMat.bumpMap = map; - cubeMat.needsUpdate = true; - }); - - ballMat = new THREE.MeshStandardMaterial({ - color: 0xffffff, - roughness: 0.5, - metalness: 1.0, - }); - textureLoader.load('textures/planets/earth_atmos_2048.jpg', function (map) { - map.anisotropy = 4; - map.colorSpace = THREE.SRGBColorSpace; - ballMat.map = map; - ballMat.needsUpdate = true; - }); - textureLoader.load('textures/planets/earth_specular_2048.jpg', function (map) { - map.anisotropy = 4; - map.colorSpace = THREE.SRGBColorSpace; - ballMat.metalnessMap = map; - ballMat.needsUpdate = true; - }); - - const floorGeometry = new THREE.PlaneGeometry(20, 20); - const floorMesh = new THREE.Mesh(floorGeometry, floorMat); - floorMesh.receiveShadow = true; - floorMesh.rotation.x = -Math.PI / 2.0; - scene.add(floorMesh); - - const ballGeometry = new THREE.SphereGeometry(0.25, 32, 32); - const ballMesh = new THREE.Mesh(ballGeometry, ballMat); - ballMesh.position.set(1, 0.25, 1); - ballMesh.rotation.y = Math.PI; - ballMesh.castShadow = true; - scene.add(ballMesh); - - const boxGeometry = new THREE.BoxGeometry(0.5, 0.5, 0.5); - const boxMesh = new THREE.Mesh(boxGeometry, cubeMat); - boxMesh.position.set(-0.5, 0.25, -1); - boxMesh.castShadow = true; - scene.add(boxMesh); - - const boxMesh2 = new THREE.Mesh(boxGeometry, cubeMat); - boxMesh2.position.set(0, 0.25, -5); - boxMesh2.castShadow = true; - scene.add(boxMesh2); - - const boxMesh3 = new THREE.Mesh(boxGeometry, cubeMat); - boxMesh3.position.set(7, 0.25, 0); - boxMesh3.castShadow = true; - scene.add(boxMesh3); - - // - - renderer = new THREE.WebGPURenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.shadowMap.enabled = true; - renderer.toneMapping = THREE.ReinhardToneMapping; - renderer.inspector = new Inspector(); - container.appendChild(renderer.domElement); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 1; - controls.maxDistance = 20; - - window.addEventListener('resize', onWindowResize); - - // - - const gui = renderer.inspector.createParameters('Settings'); - gui.add(params, 'hemiIrradiance', Object.keys(hemiLuminousIrradiances)); - gui.add(params, 'bulbPower', Object.keys(bulbLuminousPowers)); - gui.add(params, 'exposure', 0, 1); - gui.add(params, 'shadows'); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - renderer.toneMappingExposure = Math.pow(params.exposure, 5.0); // to allow for very bright scenes. - renderer.shadowMap.enabled = params.shadows; - bulbLight.castShadow = params.shadows; - - if (params.shadows !== previousShadowMap) { - ballMat.needsUpdate = true; - cubeMat.needsUpdate = true; - floorMat.needsUpdate = true; - previousShadowMap = params.shadows; - } - - bulbLight.power = bulbLuminousPowers[params.bulbPower]; - bulbMat.emissiveIntensity = bulbLight.intensity / Math.pow(0.02, 2.0); // convert from intensity to irradiance at bulb surface - - hemiLight.intensity = hemiLuminousIrradiances[params.hemiIrradiance]; - const time = Date.now() * 0.0005; - - bulbLight.position.y = Math.cos(time) * 0.75 + 1.25; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_lights_pointlights.ts b/examples-testing/examples/webgpu_lights_pointlights.ts deleted file mode 100644 index e00d75b21..000000000 --- a/examples-testing/examples/webgpu_lights_pointlights.ts +++ /dev/null @@ -1,189 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { OBJLoader } from 'three/addons/loaders/OBJLoader.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { - abs, - attribute, - distance, - float, - max, - modelWorldMatrixInverse, - positionLocal, - sin, - time, - uniform, -} from 'three/tsl'; - -let camera, scene, timer, renderer, controls; - -let light1, light2; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.z = 100; - - scene = new THREE.Scene(); - - timer = new THREE.Timer(); - timer.connect(document); - - // model - - const loader = new OBJLoader(); - loader.load('models/obj/walt/WaltHead.obj', function (obj) { - const mesh = obj.children[0]; - mesh.geometry = createGeometry(mesh.geometry); - mesh.material = createMaterial(); - - mesh.scale.multiplyScalar(0.8); - mesh.position.y = -30; - scene.add(mesh); - }); - - const sphere = new THREE.SphereGeometry(0.5, 16, 8); - - // lights - - light1 = new THREE.PointLight(0xff0040, 2000); - light1.add(new THREE.Mesh(sphere, new THREE.MeshBasicMaterial({ color: 0xff0040 }))); - scene.add(light1); - - light2 = new THREE.PointLight(0x0040ff, 2000); - light2.add(new THREE.Mesh(sphere, new THREE.MeshBasicMaterial({ color: 0x0040ff }))); - scene.add(light2); - - scene.add(new THREE.AmbientLight(0xaaaaaa, 0.1)); - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - const time = timer.getElapsed() * 0.5; - - controls.update(); - - light1.position.x = Math.sin(time) * 20; - light1.position.y = Math.cos(time * 0.75) * -30; - light1.position.z = Math.cos(time * 0.5) * 20; - - light2.position.x = Math.cos(time * 0.5) * 20; - light2.position.y = Math.sin(time * 0.75) * -30; - light2.position.z = Math.sin(time) * 20; - - renderer.render(scene, camera); -} - -// helpers - -function createMaterial() { - const material = new THREE.MeshPhongNodeMaterial(); - - const seedAttribute = attribute('seed'); - const displaceNormalAttribute = attribute('displaceNormal'); - - const localTime = attribute('time').add(time); - - const effector1 = uniform(light1.position).toVar(); - const effector2 = uniform(light2.position).toVar(); - - const distance1 = distance(positionLocal, modelWorldMatrixInverse.mul(effector1)); - const distance2 = distance(positionLocal, modelWorldMatrixInverse.mul(effector2)); - - const invDistance1 = max(0.0, float(20.0).sub(distance1)).div(2.0); - const invDistance2 = max(0.0, float(20.0).sub(distance2)).div(2.0); - - const s = abs(sin(localTime.mul(2).add(seedAttribute)).mul(0.5)) - .add(invDistance1) - .add(invDistance2); - - material.positionNode = positionLocal.add(displaceNormalAttribute.mul(s)); - - return material; -} - -function createGeometry(geometry) { - const positionAttribute = geometry.getAttribute('position'); - - const v0 = new THREE.Vector3(); - const v1 = new THREE.Vector3(); - const v2 = new THREE.Vector3(); - const v3 = new THREE.Vector3(); - const n = new THREE.Vector3(); - - const plane = new THREE.Plane(); - - const vertices = []; - const times = []; - const seeds = []; - const displaceNormal = []; - - for (let i = 0; i < positionAttribute.count; i += 3) { - v0.fromBufferAttribute(positionAttribute, i); - v1.fromBufferAttribute(positionAttribute, i + 1); - v2.fromBufferAttribute(positionAttribute, i + 2); - - plane.setFromCoplanarPoints(v0, v1, v2); - - v3.copy(v0).add(v1).add(v2).divideScalar(3); // compute center - v3.add(n.copy(plane.normal).multiplyScalar(-1)); // displace center inwards - - // generate tetrahedron for each triangle - - vertices.push(v0.x, v0.y, v0.z, v1.x, v1.y, v1.z, v2.x, v2.y, v2.z); - vertices.push(v3.x, v3.y, v3.z, v1.x, v1.y, v1.z, v0.x, v0.y, v0.z); - vertices.push(v3.x, v3.y, v3.z, v2.x, v2.y, v2.z, v1.x, v1.y, v1.z); - vertices.push(v3.x, v3.y, v3.z, v0.x, v0.y, v0.z, v2.x, v2.y, v2.z); - - const t = Math.random(); - const s = Math.random(); - n.copy(plane.normal); - - times.push(t, t, t); - times.push(t, t, t); - times.push(t, t, t); - times.push(t, t, t); - seeds.push(s, s, s); - seeds.push(s, s, s); - seeds.push(s, s, s); - seeds.push(s, s, s); - - displaceNormal.push(n.x, n.y, n.z, n.x, n.y, n.z, n.x, n.y, n.z); - displaceNormal.push(n.x, n.y, n.z, n.x, n.y, n.z, n.x, n.y, n.z); - displaceNormal.push(n.x, n.y, n.z, n.x, n.y, n.z, n.x, n.y, n.z); - displaceNormal.push(n.x, n.y, n.z, n.x, n.y, n.z, n.x, n.y, n.z); - } - - const newGeometry = new THREE.BufferGeometry(); - newGeometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); - newGeometry.setAttribute('time', new THREE.Float32BufferAttribute(times, 1)); - newGeometry.setAttribute('seed', new THREE.Float32BufferAttribute(seeds, 1)); - newGeometry.setAttribute('displaceNormal', new THREE.Float32BufferAttribute(displaceNormal, 3)); - - newGeometry.computeVertexNormals(); - - return newGeometry; -} diff --git a/examples-testing/examples/webgpu_lights_rectarealight.ts b/examples-testing/examples/webgpu_lights_rectarealight.ts deleted file mode 100644 index 75e2cc442..000000000 --- a/examples-testing/examples/webgpu_lights_rectarealight.ts +++ /dev/null @@ -1,87 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { RectAreaLightHelper } from 'three/addons/helpers/RectAreaLightHelper.js'; -import { RectAreaLightTexturesLib } from 'three/addons/lights/RectAreaLightTexturesLib.js'; - -import { checker, uv } from 'three/tsl'; - -let renderer, scene, camera; -let rectLight1, rectLight2, rectLight3; -let timer; - -init(); - -function init() { - THREE.RectAreaLightNode.setLTC(RectAreaLightTexturesLib.init()); - - timer = new THREE.Timer(); - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animation); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(0, 5, -15); - - scene = new THREE.Scene(); - - rectLight1 = new THREE.RectAreaLight(0xff0000, 5, 4, 10); - rectLight1.position.set(-5, 6, 5); - scene.add(rectLight1); - - rectLight2 = new THREE.RectAreaLight(0x00ff00, 5, 4, 10); - rectLight2.position.set(0, 6, 5); - scene.add(rectLight2); - - rectLight3 = new THREE.RectAreaLight(0x0000ff, 5, 4, 10); - rectLight3.position.set(5, 6, 5); - scene.add(rectLight3); - - scene.add(new RectAreaLightHelper(rectLight1)); - scene.add(new RectAreaLightHelper(rectLight2)); - scene.add(new RectAreaLightHelper(rectLight3)); - - const geoFloor = new THREE.BoxGeometry(2000, 0.1, 2000); - const matStdFloor = new THREE.MeshStandardMaterial({ color: 0x444444 }); - matStdFloor.roughnessNode = checker(uv().mul(400)); - const mshStdFloor = new THREE.Mesh(geoFloor, matStdFloor); - scene.add(mshStdFloor); - - const geoKnot = new THREE.TorusKnotGeometry(1.5, 0.5, 200, 16); - const matKnot = new THREE.MeshStandardMaterial({ color: 0xffffff, roughness: 0, metalness: 0 }); - const meshKnot = new THREE.Mesh(geoKnot, matKnot); - meshKnot.position.set(0, 5.5, 0); - scene.add(meshKnot); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.target.copy(meshKnot.position); - controls.update(); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - renderer.setSize(window.innerWidth, window.innerHeight); - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); -} - -function animation() { - timer.update(); - - const delta = timer.getDelta(); - - rectLight1.rotation.y += -delta; - rectLight2.rotation.y += delta * 0.5; - rectLight3.rotation.y += delta; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_lights_selective.ts b/examples-testing/examples/webgpu_lights_selective.ts deleted file mode 100644 index d00048347..000000000 --- a/examples-testing/examples/webgpu_lights_selective.ts +++ /dev/null @@ -1,150 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { fog, rangeFogFactor, color, lights, texture, normalMap } from 'three/tsl'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { TeapotGeometry } from 'three/addons/geometries/TeapotGeometry.js'; - -let camera, scene, renderer, light1, light2, light3, light4, controls; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.01, 100); - camera.position.z = 7; - - scene = new THREE.Scene(); - scene.fogNode = fog(color(0xff00ff), rangeFogFactor(12, 30)); - - const sphereGeometry = new THREE.SphereGeometry(0.1, 16, 8); - - // textures - - const textureLoader = new THREE.TextureLoader(); - - const normalMapTexture = textureLoader.load('./textures/water/Water_1_M_Normal.jpg'); - normalMapTexture.wrapS = THREE.RepeatWrapping; - normalMapTexture.wrapT = THREE.RepeatWrapping; - - const alphaTexture = textureLoader.load('./textures/roughness_map.jpg'); - alphaTexture.wrapS = THREE.RepeatWrapping; - alphaTexture.wrapT = THREE.RepeatWrapping; - - // lights - - const addLight = (hexColor, power = 1700, distance = 100) => { - const material = new THREE.MeshStandardNodeMaterial(); - material.colorNode = color(hexColor); - material.lights = false; - - const mesh = new THREE.Mesh(sphereGeometry, material); - - const light = new THREE.PointLight(hexColor, 1, distance); - light.power = power; - light.add(mesh); - - scene.add(light); - - return light; - }; - - light1 = addLight(0xff0040); - light2 = addLight(0x0040ff); - light3 = addLight(0x80ff80); - light4 = addLight(0xffaa00); - - // light nodes ( selective lights ) - - const redLightsNode = lights([light1]); - const blueLightsNode = lights([light2]); - - // models - - const geometryTeapot = new TeapotGeometry(0.8, 18); - - const leftObject = new THREE.Mesh(geometryTeapot, new THREE.MeshStandardNodeMaterial({ color: 0x555555 })); - leftObject.material.lightsNode = redLightsNode; - leftObject.material.roughnessNode = texture(alphaTexture); - leftObject.material.metalness = 0; - leftObject.position.x = -3; - scene.add(leftObject); - - const centerObject = new THREE.Mesh(geometryTeapot, new THREE.MeshStandardNodeMaterial({ color: 0x555555 })); - centerObject.material.normalNode = normalMap(texture(normalMapTexture)); - centerObject.material.metalness = 0.5; - centerObject.material.roughness = 0.5; - scene.add(centerObject); - - const rightObject = new THREE.Mesh(geometryTeapot, new THREE.MeshStandardNodeMaterial({ color: 0x555555 })); - rightObject.material.lightsNode = blueLightsNode; - rightObject.material.metalnessNode = texture(alphaTexture); - rightObject.position.x = 3; - scene.add(rightObject); - - leftObject.rotation.y = centerObject.rotation.y = rightObject.rotation.y = Math.PI * -0.5; - leftObject.position.y = centerObject.position.y = rightObject.position.y = -1; - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // controls - - controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 3; - controls.maxDistance = 25; - - // events - - window.addEventListener('resize', onWindowResize); - - // gui - - const gui = renderer.inspector.createParameters('Material'); - - gui.add(centerObject.material, 'roughness', 0, 1, 0.01); - gui.add(centerObject.material, 'metalness', 0, 1, 0.01); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - const time = performance.now() / 1000; - const lightTime = time * 0.5; - - light1.position.x = Math.sin(lightTime * 0.7) * 3; - light1.position.y = Math.cos(lightTime * 0.5) * 4; - light1.position.z = Math.cos(lightTime * 0.3) * 3; - - light2.position.x = Math.cos(lightTime * 0.3) * 3; - light2.position.y = Math.sin(lightTime * 0.5) * 4; - light2.position.z = Math.sin(lightTime * 0.7) * 3; - - light3.position.x = Math.sin(lightTime * 0.7) * 3; - light3.position.y = Math.cos(lightTime * 0.3) * 4; - light3.position.z = Math.sin(lightTime * 0.5) * 3; - - light4.position.x = Math.sin(lightTime * 0.3) * 3; - light4.position.y = Math.cos(lightTime * 0.7) * 4; - light4.position.z = Math.sin(lightTime * 0.5) * 3; - /* - @TODO: Used to test scene light change ( currently unavailable ) - - if ( time > 2.0 && light1.parent === null ) scene.add( light1 ); - if ( time > 2.5 && light2.parent === null ) scene.add( light2 ); - if ( time > 3.0 && light3.parent === null ) scene.add( light3 ); - if ( time > 3.5 && light4.parent === null ) scene.add( light4 ); - */ - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_lights_spotlight.ts b/examples-testing/examples/webgpu_lights_spotlight.ts deleted file mode 100644 index 85e320800..000000000 --- a/examples-testing/examples/webgpu_lights_spotlight.ts +++ /dev/null @@ -1,201 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { PLYLoader } from 'three/addons/loaders/PLYLoader.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let renderer, scene, camera; - -let spotLight; - -init(); - -function init() { - // Renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - renderer.inspector = new Inspector(); - - renderer.toneMapping = THREE.NeutralToneMapping; - renderer.toneMappingExposure = 1; - - renderer.shadowMap.enabled = true; - renderer.shadowMap.type = THREE.PCFSoftShadowMap; - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(7, 4, 1); - - // Controls - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 2; - controls.maxDistance = 10; - controls.maxPolarAngle = Math.PI / 2; - controls.target.set(0, 1, 0); - controls.update(); - - // Textures - - const loader = new THREE.TextureLoader().setPath('textures/'); - const filenames = ['disturb.jpg', 'colors.png', 'uv_grid_opengl.jpg']; - - const textures = { none: null }; - - for (let i = 0; i < filenames.length; i++) { - const filename = filenames[i]; - - const texture = loader.load(filename); - texture.minFilter = THREE.LinearFilter; - texture.magFilter = THREE.LinearFilter; - texture.generateMipmaps = false; - texture.colorSpace = THREE.SRGBColorSpace; - - textures[filename] = texture; - } - - // Lights - - const ambient = new THREE.HemisphereLight(0xffffff, 0x8d8d8d, 0.25); - scene.add(ambient); - - spotLight = new THREE.SpotLight(0xffffff, 100); - spotLight.name = 'spotLight'; - spotLight.map = textures['disturb.jpg']; - spotLight.position.set(2.5, 5, 2.5); - spotLight.angle = Math.PI / 6; - spotLight.penumbra = 1; - spotLight.decay = 2; - spotLight.distance = 0; - - spotLight.castShadow = true; - spotLight.shadow.mapSize.width = 1024; - spotLight.shadow.mapSize.height = 1024; - spotLight.shadow.camera.near = 2; - spotLight.shadow.camera.far = 10; - spotLight.shadow.focus = 1; - spotLight.shadow.intensity = 1; - scene.add(spotLight); - - spotLight.lightHelper = new THREE.SpotLightHelper(spotLight); - spotLight.lightHelper.visible = false; - scene.add(spotLight.lightHelper); - - spotLight.shadowCameraHelper = new THREE.CameraHelper(spotLight.shadow.camera); // colored lines - spotLight.shadowCameraHelper.visible = false; - scene.add(spotLight.shadowCameraHelper); - - // - - const geometry = new THREE.PlaneGeometry(10, 10); - const material = new THREE.MeshLambertMaterial({ color: 0xbcbcbc }); - - const mesh = new THREE.Mesh(geometry, material); - mesh.position.set(0, -1, 0); - mesh.rotation.x = -Math.PI / 2; - mesh.receiveShadow = true; - scene.add(mesh); - - // Model - - new PLYLoader().load('models/ply/binary/Lucy100k.ply', function (geometry) { - geometry.scale(0.0024, 0.0024, 0.0024); - geometry.computeVertexNormals(); - - const material = new THREE.MeshLambertMaterial(); - - const mesh = new THREE.Mesh(geometry, material); - mesh.rotation.y = -Math.PI / 2; - mesh.position.y = 0.8; - mesh.castShadow = true; - mesh.receiveShadow = true; - scene.add(mesh); - }); - - // - - window.addEventListener('resize', onWindowResize); - - // GUI - - const gui = renderer.inspector.createParameters('Settings'); - - const params = { - map: textures['disturb.jpg'], - color: spotLight.color.getHex(), - intensity: spotLight.intensity, - distance: spotLight.distance, - angle: spotLight.angle, - penumbra: spotLight.penumbra, - decay: spotLight.decay, - focus: spotLight.shadow.focus, - shadowIntensity: spotLight.shadow.intensity, - helpers: false, - }; - - gui.add(params, 'map', textures).onChange(function (val) { - spotLight.map = val; - }); - - gui.addColor(params, 'color').onChange(function (val) { - spotLight.color.setHex(val); - }); - - gui.add(params, 'intensity', 0, 500).onChange(function (val) { - spotLight.intensity = val; - }); - - gui.add(params, 'distance', 0, 20).onChange(function (val) { - spotLight.distance = val; - }); - - gui.add(params, 'angle', 0, Math.PI / 3).onChange(function (val) { - spotLight.angle = val; - }); - - gui.add(params, 'penumbra', 0, 1).onChange(function (val) { - spotLight.penumbra = val; - }); - - gui.add(params, 'decay', 1, 2).onChange(function (val) { - spotLight.decay = val; - }); - - gui.add(params, 'focus', 0, 1).onChange(function (val) { - spotLight.shadow.focus = val; - }); - - gui.add(params, 'shadowIntensity', 0, 1).onChange(function (val) { - spotLight.shadow.intensity = val; - }); - - gui.add(params, 'helpers').onChange(function (val) { - spotLight.lightHelper.visible = val; - spotLight.shadowCameraHelper.visible = val; - }); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - const time = performance.now() / 3000; - - spotLight.position.x = Math.cos(time) * 2.5; - spotLight.position.z = Math.sin(time) * 2.5; - - spotLight.lightHelper.update(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_lights_tiled.ts b/examples-testing/examples/webgpu_lights_tiled.ts deleted file mode 100644 index e575ee344..000000000 --- a/examples-testing/examples/webgpu_lights_tiled.ts +++ /dev/null @@ -1,180 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { texture, uv, pass, normalMap, uniform } from 'three/tsl'; -import { bloom } from 'three/addons/tsl/display/BloomNode.js'; - -import { TiledLighting } from 'three/addons/lighting/TiledLighting.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import WebGPU from 'three/addons/capabilities/WebGPU.js'; - -let camera, scene, renderer, lights, lightDummy, controls, compose, tileInfluence, lighting, count, renderPipeline; - -init(); - -function init() { - if (WebGPU.isAvailable() === false) { - document.body.appendChild(WebGPU.getErrorMessage()); - - throw new Error('No WebGPU support'); - } - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 600); - camera.position.z = 200; - camera.position.y = 30; - - scene = new THREE.Scene(); - scene.fog = new THREE.Fog(0x111111, 300, 500); - scene.background = new THREE.Color(0x111111); - - count = 1000; - - const material = new THREE.MeshBasicMaterial(); - - lightDummy = new THREE.InstancedMesh(new THREE.SphereGeometry(0.1, 16, 8), material, count); - lightDummy.instanceMatrix.setUsage(THREE.DynamicDrawUsage); - scene.add(lightDummy); - - // lights - - lights = new THREE.Group(); - scene.add(lights); - - const addLight = (hexColor, power = 10, distance = 3) => { - const light = new THREE.PointLight(hexColor, 1, distance); - light.position.set(Math.random() * 300 - 150, 1, Math.random() * 300 - 150); - light.power = power; - light.userData.fixedPosition = light.position.clone(); - lights.add(light); - - return light; - }; - - const color = new THREE.Color(); - - for (let i = 0; i < count; i++) { - const hex = Math.random() * 0xffffff + 0x666666; - - lightDummy.setColorAt(i, color.setHex(hex)); - - addLight(hex); - } - - // - - const lightAmbient = new THREE.AmbientLight(0xffffff, 0.1); - scene.add(lightAmbient); - - // textures - - const textureLoader = new THREE.TextureLoader(); - - const floorColor = textureLoader.load('textures/floors/FloorsCheckerboard_S_Diffuse.jpg'); - floorColor.wrapS = THREE.RepeatWrapping; - floorColor.wrapT = THREE.RepeatWrapping; - floorColor.colorSpace = THREE.SRGBColorSpace; - - const floorNormal = textureLoader.load('textures/floors/FloorsCheckerboard_S_Normal.jpg'); - floorNormal.wrapS = THREE.RepeatWrapping; - floorNormal.wrapT = THREE.RepeatWrapping; - - const uvTile = uv().mul(50); - - const planeGeometry = new THREE.PlaneGeometry(1000, 1000); - const planeMaterial = new THREE.MeshPhongNodeMaterial({ - colorNode: texture(floorColor, uvTile), - normalNode: normalMap(texture(floorNormal, uvTile)), - }); - - const ground = new THREE.Mesh(planeGeometry, planeMaterial); - ground.rotation.x = -Math.PI / 2; - ground.position.y = 0; - ground.castShadow = true; - ground.receiveShadow = true; - scene.add(ground); - - // renderer - - lighting = new TiledLighting(); // ( maxLights = 1024, tileSize = 32 ) - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.lighting = lighting; // set lighting system - renderer.toneMapping = THREE.NeutralToneMapping; - renderer.toneMappingExposure = 5; - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // controls - - controls = new OrbitControls(camera, renderer.domElement); - controls.maxDistance = 400; - - // events - - window.addEventListener('resize', onWindowResize); - - // post processing - - const scenePass = pass(scene, camera); - const bloomPass = bloom(scenePass, 3, 0.9, 0.2); - - // compose - - compose = scenePass.add(bloomPass); - tileInfluence = uniform(0); - - renderPipeline = new THREE.RenderPipeline(renderer); - - updatePostProcessing(); - - // gui - - const gui = renderer.inspector.createParameters('Settings'); - gui.add(tileInfluence, 'value', 0, 1).name('tile indexes debug'); -} - -function updatePostProcessing() { - // tile indexes debug, needs to be updated every time the renderer size changes - - const debugBlockIndexes = lighting - .getNode(scene) - .setSize(window.innerWidth * window.devicePixelRatio, window.innerHeight * window.devicePixelRatio) - .getBlock() - .toColor() - .div(count * 2); - - renderPipeline.outputNode = compose.add(debugBlockIndexes.mul(tileInfluence)); - renderPipeline.needsUpdate = true; -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - updatePostProcessing(); -} - -function animate() { - const time = performance.now() / 1000; - - for (let i = 0; i < lights.children.length; i++) { - const light = lights.children[i]; - const lightTime = time * 0.5 + light.id; - - light.position.copy(light.userData.fixedPosition); - light.position.x += Math.sin(lightTime * 0.7) * 3; - light.position.y += Math.cos(lightTime * 0.5) * 0.5; - light.position.z += Math.cos(lightTime * 0.3) * 3; - - lightDummy.setMatrixAt(i, light.matrixWorld); - } - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_lines_fat.ts b/examples-testing/examples/webgpu_lines_fat.ts deleted file mode 100644 index 96417888c..000000000 --- a/examples-testing/examples/webgpu_lines_fat.ts +++ /dev/null @@ -1,255 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { color } from 'three/tsl'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { Line2 } from 'three/addons/lines/webgpu/Line2.js'; -import { LineGeometry } from 'three/addons/lines/LineGeometry.js'; -import * as GeometryUtils from 'three/addons/utils/GeometryUtils.js'; - -let line, renderer, scene, camera, camera2, controls, backgroundNode; -let line1; -let matLine, matLineBasic, matLineDashed; -let stats; -let gui; - -// viewport -let insetWidth; -let insetHeight; - -init(); - -function init() { - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setClearColor(0x000000, 0.0); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(-40, 0, 60); - - camera2 = new THREE.PerspectiveCamera(40, 1, 1, 1000); - camera2.position.copy(camera.position); - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - controls.minDistance = 10; - controls.maxDistance = 500; - - backgroundNode = color(0x222222); - - // Position and THREE.Color Data - - const positions = []; - const colors = []; - - const points = GeometryUtils.hilbert3D(new THREE.Vector3(0, 0, 0), 20.0, 1, 0, 1, 2, 3, 4, 5, 6, 7); - - const spline = new THREE.CatmullRomCurve3(points); - const divisions = Math.round(12 * points.length); - const point = new THREE.Vector3(); - const lineColor = new THREE.Color(); - - for (let i = 0, l = divisions; i < l; i++) { - const t = i / l; - - spline.getPoint(t, point); - positions.push(point.x, point.y, point.z); - - lineColor.setHSL(t, 1.0, 0.5, THREE.SRGBColorSpace); - colors.push(lineColor.r, lineColor.g, lineColor.b); - } - - // Line2 ( LineGeometry, LineMaterial ) - - const geometry = new LineGeometry(); - geometry.setPositions(positions); - geometry.setColors(colors); - - matLine = new THREE.Line2NodeMaterial({ - color: 0xffffff, - linewidth: 5, // in world units with size attenuation, pixels otherwise - vertexColors: true, - dashed: false, - alphaToCoverage: true, - }); - - line = new Line2(geometry, matLine); - line.computeLineDistances(); - line.scale.set(1, 1, 1); - scene.add(line); - - const geo = new THREE.BufferGeometry(); - geo.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)); - geo.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); - - matLineBasic = new THREE.LineBasicNodeMaterial({ vertexColors: true }); - matLineDashed = new THREE.LineDashedNodeMaterial({ vertexColors: true, scale: 2, dashSize: 1, gapSize: 1 }); - - line1 = new THREE.Line(geo, matLineBasic); - line1.computeLineDistances(); - line1.visible = false; - scene.add(line1); - - // - - window.addEventListener('resize', onWindowResize); - onWindowResize(); - - stats = new Stats(); - document.body.appendChild(stats.dom); - - initGui(); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - insetWidth = window.innerHeight / 4; // square - insetHeight = window.innerHeight / 4; - - camera2.aspect = insetWidth / insetHeight; - camera2.updateProjectionMatrix(); -} - -function animate() { - stats.update(); - - // main scene - - renderer.setClearColor(0x000000, 0); - - renderer.setViewport(0, 0, window.innerWidth, window.innerHeight); - - controls.update(); - - renderer.autoClear = true; - - scene.backgroundNode = null; - renderer.render(scene, camera); - - // inset scene - - const posY = window.innerHeight - insetHeight - 20; - - renderer.clearDepth(); // important! - - renderer.setScissorTest(true); - - renderer.setScissor(20, posY, insetWidth, insetHeight); - - renderer.setViewport(20, posY, insetWidth, insetHeight); - - camera2.position.copy(camera.position); - camera2.quaternion.copy(camera.quaternion); - - renderer.autoClear = false; - - scene.backgroundNode = backgroundNode; - renderer.render(scene, camera2); - - renderer.setScissorTest(false); -} - -// - -function initGui() { - gui = new GUI(); - - const param = { - 'line type': 0, - 'world units': false, - width: 5, - alphaToCoverage: true, - dashed: false, - 'dash offset': 0, - 'dash scale': 1, - 'dash / gap': 1, - }; - - gui.add(param, 'line type', { LineGeometry: 0, '"line-strip"': 1 }).onChange(function (val) { - switch (val) { - case 0: - line.visible = true; - - line1.visible = false; - - break; - - case 1: - line.visible = false; - - line1.visible = true; - - break; - } - }); - - gui.add(param, 'world units').onChange(function (val) { - matLine.worldUnits = val; - matLine.needsUpdate = true; - }); - - gui.add(param, 'width', 1, 10).onChange(function (val) { - matLine.linewidth = val; - }); - - gui.add(param, 'alphaToCoverage').onChange(function (val) { - matLine.alphaToCoverage = val; - }); - - gui.add(param, 'dashed').onChange(function (val) { - matLine.dashed = val; - line1.material = val ? matLineDashed : matLineBasic; - }); - - gui.add(param, 'dash scale', 0.5, 2, 0.1).onChange(function (val) { - matLine.scale = val; - matLineDashed.scale = val; - }); - - gui.add(param, 'dash offset', 0, 5, 0.1).onChange(function (val) { - matLine.dashOffset = val; - matLineDashed.dashOffset = val; - }); - - gui.add(param, 'dash / gap', { '2 : 1': 0, '1 : 1': 1, '1 : 2': 2 }).onChange(function (val) { - switch (val) { - case 0: - matLine.dashSize = 2; - matLine.gapSize = 1; - - matLineDashed.dashSize = 2; - matLineDashed.gapSize = 1; - - break; - - case 1: - matLine.dashSize = 1; - matLine.gapSize = 1; - - matLineDashed.dashSize = 1; - matLineDashed.gapSize = 1; - - break; - - case 2: - matLine.dashSize = 1; - matLine.gapSize = 2; - - matLineDashed.dashSize = 1; - matLineDashed.gapSize = 2; - - break; - } - }); -} diff --git a/examples-testing/examples/webgpu_lines_fat_raycasting.ts b/examples-testing/examples/webgpu_lines_fat_raycasting.ts deleted file mode 100644 index 4280b580c..000000000 --- a/examples-testing/examples/webgpu_lines_fat_raycasting.ts +++ /dev/null @@ -1,286 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { LineSegments2 } from 'three/addons/lines/webgpu/LineSegments2.js'; -import { LineSegmentsGeometry } from 'three/addons/lines/LineSegmentsGeometry.js'; -import { Line2 } from 'three/addons/lines/webgpu/Line2.js'; -import { LineGeometry } from 'three/addons/lines/LineGeometry.js'; - -// - -let line, thresholdLine, segments, thresholdSegments; -let renderer, scene, camera, controls; -let sphereInter, sphereOnLine; -let gui; -let timer; - -const color = new THREE.Color(); - -const pointer = new THREE.Vector2(Infinity, Infinity); - -const raycaster = new THREE.Raycaster(); - -raycaster.params.Line2 = {}; -raycaster.params.Line2.threshold = 0; - -const matLine = new THREE.Line2NodeMaterial({ - color: 0xffffff, - linewidth: 1, // in world units with size attenuation, pixels otherwise - worldUnits: true, - vertexColors: true, - - alphaToCoverage: true, -}); - -const matThresholdLine = new THREE.Line2NodeMaterial({ - color: 0xffffff, - linewidth: matLine.linewidth, // in world units with size attenuation, pixels otherwise - worldUnits: true, - // vertexColors: true, - transparent: true, - opacity: 0.2, - depthTest: false, - visible: false, -}); - -const params = { - 'line type': 1, - 'world units': matLine.worldUnits, - 'visualize threshold': matThresholdLine.visible, - width: matLine.linewidth, - alphaToCoverage: matLine.alphaToCoverage, - threshold: raycaster.params.Line2.threshold, - translation: 0, - animate: true, -}; - -init(); - -function init() { - timer = new THREE.Timer(); - timer.connect(document); - - renderer = new THREE.WebGPURenderer({ antialias: true, alpha: true, trackTimestamp: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setClearColor(0x000000, 0.0); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(-40, 0, 60); - - controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 10; - controls.maxDistance = 500; - - const sphereGeometry = new THREE.SphereGeometry(0.25, 8, 4); - const sphereInterMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000, depthTest: false }); - const sphereOnLineMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00, depthTest: false }); - - sphereInter = new THREE.Mesh(sphereGeometry, sphereInterMaterial); - sphereOnLine = new THREE.Mesh(sphereGeometry, sphereOnLineMaterial); - sphereInter.visible = false; - sphereOnLine.visible = false; - sphereInter.renderOrder = 10; - sphereOnLine.renderOrder = 10; - scene.add(sphereInter); - scene.add(sphereOnLine); - - // Position and THREE.Color Data - - const positions = []; - const colors = []; - const points = []; - for (let i = -50; i < 50; i++) { - const t = i / 3; - points.push(new THREE.Vector3(t * Math.sin(2 * t), t, t * Math.cos(2 * t))); - } - - const spline = new THREE.CatmullRomCurve3(points); - const divisions = Math.round(3 * points.length); - const point = new THREE.Vector3(); - const color = new THREE.Color(); - - for (let i = 0, l = divisions; i < l; i++) { - const t = i / l; - - spline.getPoint(t, point); - positions.push(point.x, point.y, point.z); - - color.setHSL(t, 1.0, 0.5, THREE.SRGBColorSpace); - colors.push(color.r, color.g, color.b); - } - - const lineGeometry = new LineGeometry(); - lineGeometry.setPositions(positions); - lineGeometry.setColors(colors); - - const segmentsGeometry = new LineSegmentsGeometry(); - segmentsGeometry.setPositions(positions); - segmentsGeometry.setColors(colors); - - segments = new LineSegments2(segmentsGeometry, matLine); - segments.computeLineDistances(); - segments.scale.set(1, 1, 1); - scene.add(segments); - - thresholdSegments = new LineSegments2(segmentsGeometry, matThresholdLine); - thresholdSegments.computeLineDistances(); - thresholdSegments.scale.set(1, 1, 1); - scene.add(thresholdSegments); - - line = new Line2(lineGeometry, matLine); - line.computeLineDistances(); - line.scale.set(1, 1, 1); - scene.add(line); - - thresholdLine = new Line2(lineGeometry, matThresholdLine); - thresholdLine.computeLineDistances(); - thresholdLine.scale.set(1, 1, 1); - scene.add(thresholdLine); - - const geo = new THREE.BufferGeometry(); - geo.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)); - geo.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); - - // - - switchLine(params['line type']); - - // - - document.addEventListener('pointermove', onPointerMove); - window.addEventListener('resize', onWindowResize); - onWindowResize(); - - initGui(); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function onPointerMove(event) { - pointer.x = (event.clientX / window.innerWidth) * 2 - 1; - pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; -} - -async function animate() { - timer.update(); - - const delta = timer.getDelta(); - - const obj = line.visible ? line : segments; - thresholdLine.position.copy(line.position); - thresholdLine.quaternion.copy(line.quaternion); - thresholdSegments.position.copy(segments.position); - thresholdSegments.quaternion.copy(segments.quaternion); - - if (params.animate) { - line.rotation.y += delta * 0.1; - - segments.rotation.y = line.rotation.y; - } - - raycaster.setFromCamera(pointer, camera); - - const intersects = raycaster.intersectObject(obj); - - if (intersects.length > 0) { - sphereInter.visible = true; - sphereOnLine.visible = true; - - sphereInter.position.copy(intersects[0].point); - sphereOnLine.position.copy(intersects[0].pointOnLine); - - const index = intersects[0].faceIndex; - const colors = obj.geometry.getAttribute('instanceColorStart'); - - color.fromBufferAttribute(colors, index); - - sphereInter.material.color.copy(color).offsetHSL(0.3, 0, 0); - sphereOnLine.material.color.copy(color).offsetHSL(0.7, 0, 0); - - renderer.domElement.style.cursor = 'crosshair'; - } else { - sphereInter.visible = false; - sphereOnLine.visible = false; - renderer.domElement.style.cursor = ''; - } - - renderer.render(scene, camera); -} - -// - -function switchLine(val) { - switch (val) { - case 0: - line.visible = true; - thresholdLine.visible = true; - - segments.visible = false; - thresholdSegments.visible = false; - - break; - - case 1: - line.visible = false; - thresholdLine.visible = false; - - segments.visible = true; - thresholdSegments.visible = true; - - break; - } -} - -function initGui() { - gui = renderer.inspector.createParameters('Settings'); - - gui.add(params, 'line type', { LineGeometry: 0, LineSegmentsGeometry: 1 }).onChange(function (val) { - switchLine(val); - }); - - gui.add(params, 'world units').onChange(function (val) { - matLine.worldUnits = val; - matLine.needsUpdate = true; - - matThresholdLine.worldUnits = val; - matThresholdLine.needsUpdate = true; - }); - - gui.add(params, 'visualize threshold').onChange(function (val) { - matThresholdLine.visible = val; - }); - - gui.add(params, 'width', 1, 10).onChange(function (val) { - matLine.linewidth = val; - matThresholdLine.linewidth = matLine.linewidth + raycaster.params.Line2.threshold; - }); - - gui.add(params, 'alphaToCoverage').onChange(function (val) { - matLine.alphaToCoverage = val; - }); - - gui.add(params, 'threshold', 0, 10).onChange(function (val) { - raycaster.params.Line2.threshold = val; - matThresholdLine.linewidth = matLine.linewidth + raycaster.params.Line2.threshold; - }); - - gui.add(params, 'translation', 0, 10).onChange(function (val) { - line.position.x = val; - segments.position.x = val; - }); - - gui.add(params, 'animate'); -} diff --git a/examples-testing/examples/webgpu_lines_fat_wireframe.ts b/examples-testing/examples/webgpu_lines_fat_wireframe.ts deleted file mode 100644 index 9ab5c44c0..000000000 --- a/examples-testing/examples/webgpu_lines_fat_wireframe.ts +++ /dev/null @@ -1,210 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { color } from 'three/tsl'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { Wireframe } from 'three/addons/lines/webgpu/Wireframe.js'; -import { WireframeGeometry2 } from 'three/addons/lines/WireframeGeometry2.js'; - -let wireframe, renderer, scene, camera, camera2, controls, backgroundNode; -let wireframe1; -let matLine, matLineBasic, matLineDashed; -let gui; - -// viewport -let insetWidth; -let insetHeight; - -init(); - -function init() { - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setClearColor(0x000000, 0.0); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(-50, 0, 50); - - camera2 = new THREE.PerspectiveCamera(40, 1, 1, 1000); - camera2.position.copy(camera.position); - - controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 10; - controls.maxDistance = 500; - - backgroundNode = color(0x222222); - - // Wireframe ( WireframeGeometry2, Line2NodeMaterial ) - - let geo = new THREE.IcosahedronGeometry(20, 1); - - const geometry = new WireframeGeometry2(geo); - - matLine = new THREE.Line2NodeMaterial({ - color: 0x4080ff, - linewidth: 5, // in world units with size attenuation, pixels otherwise - dashed: false, - }); - - wireframe = new Wireframe(geometry, matLine); - wireframe.computeLineDistances(); - wireframe.scale.set(1, 1, 1); - scene.add(wireframe); - - // Line ( THREE.WireframeGeometry, THREE.LineBasicMaterial ) - rendered with gl.LINE - - geo = new THREE.WireframeGeometry(geo); - - matLineBasic = new THREE.LineBasicMaterial({ color: 0x4080ff }); - matLineDashed = new THREE.LineDashedMaterial({ scale: 2, dashSize: 1, gapSize: 1 }); - - wireframe1 = new THREE.LineSegments(geo, matLineBasic); - wireframe1.computeLineDistances(); - wireframe1.visible = false; - scene.add(wireframe1); - - // - - window.addEventListener('resize', onWindowResize); - onWindowResize(); - - initGui(); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - insetWidth = window.innerHeight / 4; // square - insetHeight = window.innerHeight / 4; - - camera2.aspect = insetWidth / insetHeight; - camera2.updateProjectionMatrix(); -} - -function animate() { - // main scene - - renderer.setClearColor(0x000000, 0); - - renderer.setViewport(0, 0, window.innerWidth, window.innerHeight); - - renderer.autoClear = true; - - scene.backgroundNode = null; - - scene.name = 'Scene'; - - renderer.render(scene, camera); - - // inset scene - - const posY = window.innerHeight - insetHeight - 20; - - renderer.clearDepth(); // important! - - renderer.setScissorTest(true); - - renderer.setScissor(20, posY, insetWidth, insetHeight); - - renderer.setViewport(20, posY, insetWidth, insetHeight); - - camera2.position.copy(camera.position); - camera2.quaternion.copy(camera.quaternion); - - renderer.autoClear = false; - - scene.backgroundNode = backgroundNode; - - scene.name = 'Scene [ Scissor ]'; - - renderer.render(scene, camera2); - - renderer.setScissorTest(false); -} - -// - -function initGui() { - gui = renderer.inspector.createParameters('Settings'); - - const param = { - 'line type': 0, - 'width (px)': 5, - dashed: false, - 'dash scale': 1, - 'dash / gap': 1, - }; - - gui.add(param, 'line type', { LineGeometry: 0, 'gl.LINE': 1 }).onChange(function (val) { - switch (val) { - case 0: - wireframe.visible = true; - - wireframe1.visible = false; - - break; - - case 1: - wireframe.visible = false; - - wireframe1.visible = true; - - break; - } - }); - - gui.add(param, 'width (px)', 1, 10).onChange(function (val) { - matLine.linewidth = val; - }); - - gui.add(param, 'dashed').onChange(function (val) { - matLine.dashed = val; - wireframe1.material = val ? matLineDashed : matLineBasic; - }); - - gui.add(param, 'dash scale', 0.5, 1, 0.1).onChange(function (val) { - matLine.scale = val; - matLineDashed.scale = val; - }); - - gui.add(param, 'dash / gap', { '2 : 1': 0, '1 : 1': 1, '1 : 2': 2 }).onChange(function (val) { - switch (val) { - case 0: - matLine.dashSize = 2; - matLine.gapSize = 1; - - matLineDashed.dashSize = 2; - matLineDashed.gapSize = 1; - - break; - - case 1: - matLine.dashSize = 1; - matLine.gapSize = 1; - - matLineDashed.dashSize = 1; - matLineDashed.gapSize = 1; - - break; - - case 2: - matLine.dashSize = 1; - matLine.gapSize = 2; - - matLineDashed.dashSize = 1; - matLineDashed.gapSize = 2; - - break; - } - }); -} diff --git a/examples-testing/examples/webgpu_loader_gltf.ts b/examples-testing/examples/webgpu_loader_gltf.ts deleted file mode 100644 index 6380bd903..000000000 --- a/examples-testing/examples/webgpu_loader_gltf.ts +++ /dev/null @@ -1,183 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer, controls; -let currentModel, mixer; -let currentLoadId = 0; - -const timer = new THREE.Timer(); - -init().then(render); - -async function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); - camera.position.set(-1.8, 0.6, 2.7); - - scene = new THREE.Scene(); - - new UltraHDRLoader().setPath('textures/equirectangular/').load('royal_esplanade_2k.hdr.jpg', function (texture) { - texture.mapping = THREE.EquirectangularReflectionMapping; - //texture.minFilter = THREE.LinearMipmapLinearFilter; - //texture.generateMipmaps = true; - - scene.background = texture; - scene.environment = texture; - - // model - - fetch('https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Assets/main/Models/model-index.json') - .then(response => response.json()) - .then(models => { - const gui = renderer.inspector.createParameters('Model'); - const modelNames = models.map(m => m.name); - const params = { model: 'DamagedHelmet' }; - - if (!modelNames.includes(params.model) && modelNames.length > 0) { - params.model = modelNames[0]; - } - - gui.add(params, 'model', modelNames).onChange(name => { - const modelInfo = models.find(m => m.name === name); - loadModel(modelInfo); - }); - - gui.add(scene, 'backgroundBlurriness', 0, 1); - - const initialModel = models.find(m => m.name === params.model); - if (initialModel) loadModel(initialModel); - }); - }); - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(render); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.inspector = new Inspector(); - container.appendChild(renderer.domElement); - - await renderer.init(); - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - controls.minDistance = 2; - controls.maxDistance = 10; - controls.target.set(0, 0, -0.2); - controls.update(); - - window.addEventListener('resize', onWindowResize); -} - -function loadModel(modelInfo) { - const variants = modelInfo.variants; - const variant = variants['glTF-Binary'] || variants['glTF']; - const url = `https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Assets/main/Models/${modelInfo.name}/${variant.endsWith('.glb') ? 'glTF-Binary' : 'glTF'}/${variant}`; - - if (currentModel) { - currentModel.traverse(model => { - if (model.isMesh) { - model.geometry.dispose(); - model.material.dispose(); - - for (const value of Object.values(model.material)) { - if (value && value.isTexture) { - value.dispose(); - } - } - } - }); - - scene.remove(currentModel); - currentModel = null; - } - - if (mixer) { - mixer.stopAllAction(); - mixer = null; - } - - const loadId = ++currentLoadId; - - const loader = new GLTFLoader(); - loader.load(url, async function (gltf) { - if (loadId !== currentLoadId) return; - - currentModel = gltf.scene; - - // wait until the model can be added to the scene without blocking due to shader compilation - - await renderer.compileAsync(currentModel, camera, scene); - - if (loadId !== currentLoadId) return; - - scene.add(currentModel); - - fitCameraToSelection(camera, controls, currentModel); - - // animations - - if (gltf.animations.length > 0) { - mixer = new THREE.AnimationMixer(currentModel); - - for (const animation of gltf.animations) { - mixer.clipAction(animation).play(); - } - } - }); -} - -function fitCameraToSelection(camera, controls, selection, fitOffset = 1.3) { - const box = new THREE.Box3(); - box.setFromObject(selection); - - const size = box.getSize(new THREE.Vector3()); - const center = box.getCenter(new THREE.Vector3()); - - const maxSize = Math.max(size.x, size.y, size.z); - const fitHeightDistance = maxSize / (2 * Math.atan((Math.PI * camera.fov) / 360)); - // const fitWidthDistance = fitHeightDistance / camera.aspect; - // const distance = fitOffset * Math.max( fitHeightDistance, fitWidthDistance ); - const distance = fitOffset * fitHeightDistance; - - const direction = controls.target.clone().sub(camera.position).normalize().multiplyScalar(distance); - - controls.maxDistance = distance * 10; - controls.minDistance = distance / 10; - controls.target.copy(center); - - camera.near = distance / 100; - camera.far = distance * 100; - camera.updateProjectionMatrix(); - - camera.position.copy(controls.target).sub(direction); - - controls.update(); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function render() { - timer.update(); - - controls.update(); - - if (mixer) mixer.update(timer.getDelta()); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_loader_gltf_anisotropy.ts b/examples-testing/examples/webgpu_loader_gltf_anisotropy.ts deleted file mode 100644 index a3aa95e15..000000000 --- a/examples-testing/examples/webgpu_loader_gltf_anisotropy.ts +++ /dev/null @@ -1,64 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; - -let renderer, scene, camera, controls; - -init(); - -async function init() { - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = 1.35; - document.body.appendChild(renderer.domElement); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.01, 10); - camera.position.set(-0.35, -0.2, 0.35); - - controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, -0.08, 0.11); - controls.minDistance = 0.1; - controls.maxDistance = 2; - controls.update(); - - const hdrLoader = new UltraHDRLoader().setPath('textures/equirectangular/'); - const gltfLoader = new GLTFLoader().setPath('models/gltf/'); - - const [texture, gltf] = await Promise.all([ - hdrLoader.loadAsync('royal_esplanade_2k.hdr.jpg'), - gltfLoader.loadAsync('AnisotropyBarnLamp.glb'), - ]); - - // environment - - texture.mapping = THREE.EquirectangularReflectionMapping; - - scene.background = texture; - scene.backgroundBlurriness = 0.5; - scene.environment = texture; - - // model - - scene.add(gltf.scene); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_loader_gltf_compressed.ts b/examples-testing/examples/webgpu_loader_gltf_compressed.ts deleted file mode 100644 index 31d5d0d1a..000000000 --- a/examples-testing/examples/webgpu_loader_gltf_compressed.ts +++ /dev/null @@ -1,69 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js'; -import { MeshoptDecoder } from 'three/addons/libs/meshopt_decoder.module.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let camera, scene, renderer; - -init(); - -async function init() { - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 20); - camera.position.set(2, 2, 2); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xeeeeee); - - //lights - - const light = new THREE.PointLight(0xffffff); - light.power = 1300; - camera.add(light); - scene.add(camera); - - //renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.toneMapping = THREE.ReinhardToneMapping; - renderer.toneMappingExposure = 1; - document.body.appendChild(renderer.domElement); - - await renderer.init(); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 3; - controls.maxDistance = 6; - controls.update(); - - const ktx2Loader = await new KTX2Loader().setTranscoderPath('jsm/libs/basis/').detectSupport(renderer); - - const loader = new GLTFLoader(); - loader.setKTX2Loader(ktx2Loader); - loader.setMeshoptDecoder(MeshoptDecoder); - loader.load('models/gltf/coffeemat.glb', function (gltf) { - const gltfScene = gltf.scene; - gltfScene.position.y = -0.8; - gltfScene.scale.setScalar(0.01); - - scene.add(gltfScene); - }); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_loader_gltf_dispersion.ts b/examples-testing/examples/webgpu_loader_gltf_dispersion.ts deleted file mode 100644 index a2e815ddb..000000000 --- a/examples-testing/examples/webgpu_loader_gltf_dispersion.ts +++ /dev/null @@ -1,63 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; - -let camera, scene, renderer; - -init(); - -async function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.01, 5); - camera.position.set(0.1, 0.05, 0.15); - - scene = new THREE.Scene(); - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(render); - renderer.toneMapping = THREE.ReinhardToneMapping; // TODO: Add THREE.NeutralToneMapping; - renderer.toneMappingExposure = 1; - container.appendChild(renderer.domElement); - - const hdrTexture = await new HDRLoader() - .setPath('textures/equirectangular/') - .loadAsync('pedestrian_overpass_1k.hdr'); - hdrTexture.mapping = THREE.EquirectangularReflectionMapping; - - scene = new THREE.Scene(); - scene.backgroundBlurriness = 0.5; - scene.environment = hdrTexture; - scene.background = hdrTexture; - - const loader = new GLTFLoader(); - const gltf = await loader.loadAsync('models/gltf/DispersionTest.glb'); - - scene.add(gltf.scene); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 0.1; - controls.maxDistance = 10; - controls.target.set(0, 0, 0); - controls.update(); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_loader_gltf_iridescence.ts b/examples-testing/examples/webgpu_loader_gltf_iridescence.ts deleted file mode 100644 index 8007d5e65..000000000 --- a/examples-testing/examples/webgpu_loader_gltf_iridescence.ts +++ /dev/null @@ -1,70 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; - -let renderer, scene, camera, controls; - -init().catch(function (err) { - console.error(err); -}); - -async function init() { - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setAnimationLoop(render); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - document.body.appendChild(renderer.domElement); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.05, 20); - camera.position.set(0.35, 0.05, 0.35); - - controls = new OrbitControls(camera, renderer.domElement); - controls.autoRotate = true; - controls.autoRotateSpeed = -0.5; - controls.target.set(0, 0.2, 0); - controls.update(); - - const hdrLoader = new HDRLoader().setPath('textures/equirectangular/'); - - const gltfLoader = new GLTFLoader().setPath('models/gltf/'); - - const [texture, gltf] = await Promise.all([ - hdrLoader.loadAsync('venice_sunset_1k.hdr'), - gltfLoader.loadAsync('IridescenceLamp.glb'), - ]); - - // environment - - texture.mapping = THREE.EquirectangularReflectionMapping; - - scene.background = texture; - scene.environment = texture; - - // model - - scene.add(gltf.scene); - - render(); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - render(); -} - -function render() { - controls.update(); - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_loader_gltf_sheen.ts b/examples-testing/examples/webgpu_loader_gltf_sheen.ts deleted file mode 100644 index 442edc7fb..000000000 --- a/examples-testing/examples/webgpu_loader_gltf_sheen.ts +++ /dev/null @@ -1,81 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer, controls; - -init(); - -function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 20); - camera.position.set(-0.75, 0.7, 1.25); - - scene = new THREE.Scene(); - //scene.add( new THREE.DirectionalLight( 0xffffff, 2 ) ); - - // model - - new GLTFLoader().setPath('models/gltf/').load('SheenChair.glb', function (gltf) { - scene.add(gltf.scene); - - const object = gltf.scene.getObjectByName('SheenChair_fabric'); - - const gui = renderer.inspector.createParameters('SheenChair_fabric'); - - gui.add(object.material, 'sheen', 0, 1); - }); - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = 1; - renderer.inspector = new Inspector(); - container.appendChild(renderer.domElement); - - scene.background = new THREE.Color(0xaaaaaa); - - new UltraHDRLoader().setPath('textures/equirectangular/').load('royal_esplanade_2k.hdr.jpg', function (texture) { - texture.mapping = THREE.EquirectangularReflectionMapping; - - scene.background = texture; - //scene.backgroundBlurriness = 1; // @TODO: Needs PMREM - scene.environment = texture; - }); - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - controls.minDistance = 1; - controls.maxDistance = 10; - controls.target.set(0, 0.35, 0); - controls.update(); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - controls.update(); // required if damping enabled - - render(); -} - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_loader_gltf_transmission.ts b/examples-testing/examples/webgpu_loader_gltf_transmission.ts deleted file mode 100644 index 45b86e772..000000000 --- a/examples-testing/examples/webgpu_loader_gltf_transmission.ts +++ /dev/null @@ -1,83 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; - -import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; - -let camera, scene, renderer, controls, timer, mixer; - -init(); - -function init() { - timer = new THREE.Timer(); - timer.connect(document); - - const container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); - camera.position.set(0, 0.4, 0.7); - - scene = new THREE.Scene(); - - new UltraHDRLoader().setPath('textures/equirectangular/').load('royal_esplanade_2k.hdr.jpg', function (texture) { - texture.mapping = THREE.EquirectangularReflectionMapping; - - scene.background = texture; - scene.backgroundBlurriness = 0.35; - - scene.environment = texture; - - // model - - new GLTFLoader() - .setPath('models/gltf/') - .setDRACOLoader(new DRACOLoader().setDecoderPath('jsm/libs/draco/gltf/')) - .load('IridescentDishWithOlives.glb', function (gltf) { - mixer = new THREE.AnimationMixer(gltf.scene); - mixer.clipAction(gltf.animations[0]).play(); - - scene.add(gltf.scene); - }); - }); - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setAnimationLoop(render); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = 1; - container.appendChild(renderer.domElement); - - controls = new OrbitControls(camera, renderer.domElement); - controls.autoRotate = true; - controls.autoRotateSpeed = -0.75; - controls.enableDamping = true; - controls.minDistance = 0.5; - controls.maxDistance = 1; - controls.target.set(0, 0.1, 0); - controls.update(); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function render() { - timer.update(); - - if (mixer) mixer.update(timer.getDelta()); - - controls.update(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_loader_materialx.ts b/examples-testing/examples/webgpu_loader_materialx.ts deleted file mode 100644 index bb84eb693..000000000 --- a/examples-testing/examples/webgpu_loader_materialx.ts +++ /dev/null @@ -1,247 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { Fn, abs, fract, fwidth, length, max, saturate, smoothstep, vec4, positionWorld, float } from 'three/tsl'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -import { MaterialXLoader } from 'three/addons/loaders/MaterialXLoader.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -const SAMPLE_PATH = - 'https://raw.githubusercontent.com/materialx/MaterialX/main/resources/Materials/Examples/StandardSurface/'; -const LOCAL_SAMPLE_PATH = 'materialx/'; -const samples = [ - 'standard_surface_brass_tiled.mtlx', - 'standard_surface_brick_procedural.mtlx', - 'standard_surface_carpaint.mtlx', - //'standard_surface_chess_set.mtlx', - 'standard_surface_chrome.mtlx', - 'standard_surface_copper.mtlx', - //'standard_surface_default.mtlx', - //'standard_surface_glass.mtlx', - //'standard_surface_glass_tinted.mtlx', - 'standard_surface_gold.mtlx', - //'standard_surface_greysphere.mtlx', - //'standard_surface_greysphere_calibration.mtlx', - 'standard_surface_jade.mtlx', - //'standard_surface_look_brass_tiled.mtlx', - //'standard_surface_look_wood_tiled.mtlx', - 'standard_surface_marble_solid.mtlx', - 'standard_surface_metal_brushed.mtlx', - 'standard_surface_plastic.mtlx', - //'standard_surface_thin_film.mtlx', - 'standard_surface_velvet.mtlx', - 'standard_surface_wood_tiled.mtlx', -]; - -const localSamples = [ - 'heightnormal.mtlx', - 'conditional_if_float.mtlx', - 'image_transform.mtlx', - 'color3_vec3_cm_test.mtlx', - 'rotate2d_test.mtlx', - 'rotate3d_test.mtlx', - 'heighttonormal_normal_input.mtlx', - 'roughness_test.mtlx', - 'opacity_test.mtlx', - 'opacity_only_test.mtlx', - 'specular_test.mtlx', - 'ior_test.mtlx', - 'combined_test.mtlx', - 'texture_opacity_test.mtlx', - 'transmission_test.mtlx', - 'transmission_only_test.mtlx', - 'transmission_rough.mtlx', - 'thin_film_rainbow_test.mtlx', - 'thin_film_ior_clamp_test.mtlx', - 'sheen_test.mtlx', -]; - -let camera, scene, renderer; -let controls, prefab; -const models = []; - -init(); - -function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.toneMapping = THREE.LinearToneMapping; - renderer.toneMappingExposure = 0.5; - renderer.inspector = new Inspector(); - renderer.setAnimationLoop(render); - container.appendChild(renderer.domElement); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 200); - camera.position.set(10, 10, 20); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xffffff); - - // Ground plane - - const material = new THREE.MeshBasicNodeMaterial(); - - const grid = Fn(([coord, lineWidth = float(0.01), dotSize = float(0.03)]) => { - // https://bgolus.medium.com/the-best-darn-grid-shader-yet-727f9278b9d8 - - const g = fract(coord); - const fw = fwidth(coord); - const gx = abs(g.x.sub(0.5)); - const gy = abs(g.y.sub(0.5)); - - const lineX = saturate(lineWidth.sub(gx).div(fw.x).add(0.5)); - const lineY = saturate(lineWidth.sub(gy).div(fw.y).add(0.5)); - const lines = max(lineX, lineY); - - const squareDist = max(gx, gy); - const aa = max(fw.x, fw.y); - const dots = smoothstep(dotSize.add(aa), dotSize.sub(aa), squareDist); - - return max(dots, lines); - }); - - const fade = Fn(([radius = float(10.0), falloff = float(1.0)]) => { - return smoothstep(radius, radius.sub(falloff), length(positionWorld)); - }); - - const gridColor = vec4(0.5, 0.5, 0.5, 1.0); - const baseColor = vec4(1.0, 1.0, 1.0, 0.0); - - material.colorNode = grid(positionWorld.xz, 0.007, 0.03).mix(baseColor, gridColor).mul(fade(30.0, 20.0)); - material.transparent = true; - - const plane = new THREE.Mesh(new THREE.CircleGeometry(40), material); - plane.rotation.x = -Math.PI / 2; - plane.renderOrder = -1; - scene.add(plane); - - // - - controls = new OrbitControls(camera); - controls.connect(renderer.domElement); - controls.enableDamping = true; - controls.minDistance = 2; - controls.maxDistance = 40; - - // - - new HDRLoader().setPath('textures/equirectangular/').load('san_giuseppe_bridge_2k.hdr', async texture => { - texture.mapping = THREE.EquirectangularReflectionMapping; - - scene.environment = texture; - - prefab = (await new GLTFLoader().loadAsync('./models/gltf/ShaderBall.glb')).scene; - - for (const sample of samples) { - await addSample(sample, SAMPLE_PATH); - } - - for (const sample of localSamples) { - await addSample(sample, LOCAL_SAMPLE_PATH); - } - - addGUI(); - }); - - window.addEventListener('resize', onWindowResize); -} - -function updateModelsAlign() { - const COLUMN_COUNT = 6; - const DIST_X = 3; - const DIST_Z = 3; - - const lineCount = Math.floor(models.length / COLUMN_COUNT) - 1.5; - - const offsetX = DIST_X * (COLUMN_COUNT - 1) * -0.5; - const offsetZ = DIST_Z * lineCount * 0.5; - - for (let i = 0; i < models.length; i++) { - const model = models[i]; - - model.position.x = (i % COLUMN_COUNT) * DIST_X + offsetX; - model.position.z = Math.floor(i / COLUMN_COUNT) * -DIST_Z + offsetZ; - } -} - -async function addSample(sample, path) { - const model = prefab.clone(); - - models.push(model); - - scene.add(model); - - updateModelsAlign(); - - // - - const material = await new MaterialXLoader() - .setPath(path) - .loadAsync(sample) - .then(({ materials }) => Object.values(materials).pop()); - - const calibrationMesh = model.getObjectByName('Calibration_Mesh'); - calibrationMesh.material = material; - - const previewMesh = model.getObjectByName('Preview_Mesh'); - previewMesh.material = material; - - if (material.transparent) { - calibrationMesh.renderOrder = 1; - previewMesh.renderOrder = 2; - } - - await renderer.compileAsync(model, camera, scene); -} - -function addGUI() { - const gui = renderer.inspector.createParameters('MaterialX Loader'); - - const API = { - showCalibrationMesh: true, - showPreviewMesh: true, - }; - - gui.add(API, 'showCalibrationMesh') - .name('Calibration Mesh') - .onChange(function (value) { - setVisibility('Calibration_Mesh', value); - }); - - gui.add(API, 'showPreviewMesh') - .name('Preview Mesh') - .onChange(function (value) { - setVisibility('Preview_Mesh', value); - }); -} - -function setVisibility(name, visible) { - scene.traverse(function (node) { - if (node.isMesh) { - if (node.name == name) node.visible = visible; - } - }); -} - -// - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function render() { - controls.update(); - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_loader_texture_ktx2.ts b/examples-testing/examples/webgpu_loader_texture_ktx2.ts deleted file mode 100644 index 1daf0f032..000000000 --- a/examples-testing/examples/webgpu_loader_texture_ktx2.ts +++ /dev/null @@ -1,189 +0,0 @@ -import * as THREE from 'three'; - -import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js'; - -let canvas, renderer; - -const scenes = []; - -const sections = [ - { - title: 'Uncompressed', - description: - 'Uncompressed formats (rgba8, rgba16, rgba32) load as THREE.DataTexture objects.' + - ' Lossless, easy to read/write, uncompressed on GPU, optionally compressed over the network.', - textures: [ - { path: '2d_rgba8.ktx2' }, - { path: '2d_rgba8_linear.ktx2' }, - { path: '2d_rgba16_linear.ktx2' }, - { path: '2d_rgba32_linear.ktx2' }, - { path: '2d_rgb9e5_linear.ktx2' }, - { path: '2d_r11g11b10_linear.ktx2' }, - ], - }, - { - title: 'Compressed', - description: - 'Compressed formats (ASTC, BCn, ...) load as THREE.CompressedTexture objects,' + - ' reducing memory cost. Requires native support on the device GPU: no single compressed' + - ' format is supported on every device.', - textures: [ - { path: '2d_astc4x4.ktx2' }, - { path: '2d_etc1.ktx2' }, - { path: '2d_etc2.ktx2' }, - { path: '2d_bc1.ktx2' }, - { path: '2d_bc3.ktx2' }, - { path: '2d_bc4.ktx2' }, - { path: '2d_bc5.ktx2' }, - { path: '2d_bc7.ktx2' }, - ], - }, - - { - title: 'Universal', - description: - 'Basis Universal textures are specialized intermediate formats supporting fast' + - ' runtime transcoding into other GPU texture compression formats. After transcoding,' + - ' universal textures can be used on any device at reduced memory cost.', - textures: [{ path: '2d_etc1s.ktx2' }, { path: '2d_uastc.ktx2' }], - }, -]; - -init(); - -async function init() { - canvas = document.getElementById('c'); - - renderer = new THREE.WebGPURenderer({ canvas, antialias: true, forceWebGL: false }); - renderer.setClearColor(0xffffff, 1); - renderer.setPixelRatio(window.devicePixelRatio); - - await renderer.init(); - - const loader = new KTX2Loader() - .setTranscoderPath('jsm/libs/basis/') - .setPath('textures/ktx2/') - .detectSupport(renderer); - - const geometry = flipY(new THREE.PlaneGeometry(1, 1)); - - const content = document.getElementById('content'); - - for (const section of sections) { - const sectionElement = document.createElement('section'); - - const sectionHeader = document.createElement('h2'); - sectionHeader.textContent = section.title; - sectionElement.appendChild(sectionHeader); - - const sectionDescription = document.createElement('p'); - sectionDescription.className = 'description'; - sectionDescription.textContent = section.description; - sectionElement.appendChild(sectionDescription); - - for (const { path, supported } of section.textures) { - const scene = new THREE.Scene(); - - // make a list item - const element = document.createElement('div'); - element.className = 'list-item'; - - const sceneElement = document.createElement('div'); - element.appendChild(sceneElement); - - const labelElement = document.createElement('div'); - labelElement.innerText = 'file: ' + path; - element.appendChild(labelElement); - - // the element that represents the area we want to render the scene - scene.userData.element = sceneElement; - sectionElement.appendChild(element); - - const camera = new THREE.PerspectiveCamera(50, 1, 1, 10); - camera.position.z = 2; - scene.userData.camera = camera; - - try { - const texture = await loader.loadAsync(supported === false ? 'fail_load.ktx2' : path); - const mesh = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ map: texture })); - - labelElement.innerText += '\ncolorSpace: ' + texture.colorSpace; - - scene.add(mesh); - scenes.push(scene); - } catch (e) { - console.error(`Failed to load ${path}`, e); - } - } - - content.appendChild(sectionElement); - } - - renderer.setAnimationLoop(animate); -} - -function updateSize() { - const width = canvas.clientWidth; - const height = canvas.clientHeight; - - if (canvas.width !== width || canvas.height !== height) { - renderer.setSize(width, height, false); - } -} - -// Rewrite UVs for `flipY=false` textures. - -function flipY(geometry) { - const uv = geometry.attributes.uv; - - for (let i = 0; i < uv.count; i++) { - uv.setY(i, 1 - uv.getY(i)); - } - - return geometry; -} - -function animate() { - updateSize(); - - canvas.style.transform = `translateY(${window.scrollY}px)`; - - renderer.setClearColor(0xffffff); - renderer.setScissorTest(false); - renderer.clear(); - - renderer.setClearColor(0xe0e0e0); - renderer.setScissorTest(true); - - scenes.forEach(function (scene) { - // get the element that is a place holder for where we want to - // draw the scene - const element = scene.userData.element; - - // get its position relative to the page's viewport - const rect = element.getBoundingClientRect(); - - // check if it's offscreen. If so skip it - if ( - rect.top < 0 || - rect.bottom > renderer.domElement.clientHeight || - rect.left < 0 || - rect.right > renderer.domElement.clientWidth - ) { - return; // it's off screen - } - - // set the viewport - const width = rect.right - rect.left; - const height = rect.bottom - rect.top; - const left = rect.left; - const top = rect.top; - - renderer.setViewport(left, top, width, height); - renderer.setScissor(left, top, width, height); - - const camera = scene.userData.camera; - - renderer.render(scene, camera); - }); -} diff --git a/examples-testing/examples/webgpu_materials_alphahash.ts b/examples-testing/examples/webgpu_materials_alphahash.ts deleted file mode 100644 index 047bbe582..000000000 --- a/examples-testing/examples/webgpu_materials_alphahash.ts +++ /dev/null @@ -1,129 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; - -import { ssaaPass } from 'three/addons/tsl/display/SSAAPassNode.js'; - -let camera, scene, renderer, controls, mesh, material, renderPipeline; - -const amount = parseInt(window.location.search.slice(1)) || 3; -const count = Math.pow(amount, 3); - -const color = new THREE.Color(); - -const params = { - alpha: 0.5, - alphaHash: true, -}; - -init(); - -async function init() { - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(amount, amount, amount); - camera.lookAt(0, 0, 0); - - scene = new THREE.Scene(); - - const geometry = new THREE.IcosahedronGeometry(0.5, 3); - - material = new THREE.MeshStandardMaterial({ - color: 0xffffff, - alphaHash: params.alphaHash, - opacity: params.alpha, - }); - - mesh = new THREE.InstancedMesh(geometry, material, count); - - let i = 0; - const offset = (amount - 1) / 2; - - const matrix = new THREE.Matrix4(); - - for (let x = 0; x < amount; x++) { - for (let y = 0; y < amount; y++) { - for (let z = 0; z < amount; z++) { - matrix.setPosition(offset - x, offset - y, offset - z); - - mesh.setMatrixAt(i, matrix); - mesh.setColorAt(i, color.setHex(Math.random() * 0xffffff)); - - i++; - } - } - } - - scene.add(mesh); - - // - - renderer = new THREE.WebGPURenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - await renderer.init(); - - // - - const environment = new RoomEnvironment(); - const pmremGenerator = new THREE.PMREMGenerator(renderer); - - scene.environment = pmremGenerator.fromScene(environment, 0.04).texture; - environment.dispose(); - - // - - // postprocessing - - renderPipeline = new THREE.RenderPipeline(renderer); - const scenePass = ssaaPass(scene, camera); - scenePass.sampleLevel = 3; - - renderPipeline.outputNode = scenePass; - - // - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableZoom = false; - controls.enablePan = false; - - // - - const gui = renderer.inspector.createParameters('Parameters'); - - gui.add(params, 'alpha', 0, 1).onChange(onMaterialUpdate); - gui.add(params, 'alphaHash').onChange(onMaterialUpdate); - - const ssaaFolder = gui.addFolder('SSAA'); - ssaaFolder.add(scenePass, 'sampleLevel', 0, 4, 1); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function onMaterialUpdate() { - material.opacity = params.alpha; - material.alphaHash = params.alphaHash; - material.transparent = !params.alphaHash; - material.depthWrite = params.alphaHash; - - material.needsUpdate = true; -} - -function animate() { - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_materials_arrays.ts b/examples-testing/examples/webgpu_materials_arrays.ts deleted file mode 100644 index e6d15853e..000000000 --- a/examples-testing/examples/webgpu_materials_arrays.ts +++ /dev/null @@ -1,135 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let renderer, scene, camera, controls; -let planeMesh, boxMesh, boxMeshWireframe, planeMeshWireframe; -let materials; - -const api = { - webgpu: true, -}; - -init(!api.webgpu); - -function init(forceWebGL = false) { - if (renderer) { - renderer.dispose(); - controls.dispose(); - document.body.removeChild(renderer.domElement); - } - - // renderer - renderer = new THREE.WebGPURenderer({ - forceWebGL, - antialias: true, - }); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // scene - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x000000); - - // camera - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 100); - camera.position.set(0, 0, 10); - - // controls - controls = new OrbitControls(camera, renderer.domElement); - - // materials - materials = [ - new THREE.MeshBasicMaterial({ color: 0xff1493, side: THREE.DoubleSide }), - new THREE.MeshBasicMaterial({ color: 0x0000ff, side: THREE.DoubleSide }), - new THREE.MeshBasicMaterial({ color: 0x00ff00, side: THREE.DoubleSide }), - ]; - - // plane geometry - const planeGeometry = new THREE.PlaneGeometry(1, 1, 4, 4); - - planeGeometry.clearGroups(); - const numFacesPerRow = 4; // Number of faces in a row (since each face is made of 2 triangles) - - planeGeometry.addGroup(0, 6 * numFacesPerRow, 0); - planeGeometry.addGroup(6 * numFacesPerRow, 6 * numFacesPerRow, 1); - planeGeometry.addGroup(12 * numFacesPerRow, 6 * numFacesPerRow, 2); - - // box geometry - const boxGeometry = new THREE.BoxGeometry(0.75, 0.75, 0.75); - - boxGeometry.clearGroups(); - boxGeometry.addGroup(0, 6, 0); // front face - boxGeometry.addGroup(6, 6, 0); // back face - boxGeometry.addGroup(12, 6, 2); // top face - boxGeometry.addGroup(18, 6, 2); // bottom face - boxGeometry.addGroup(24, 6, 1); // left face - boxGeometry.addGroup(30, 6, 1); // right face - - scene.background = forceWebGL ? new THREE.Color(0x000000) : new THREE.Color(0x222222); - - // meshes - planeMesh = new THREE.Mesh(planeGeometry, materials); - - const materialsWireframe = []; - - for (let index = 0; index < materials.length; index++) { - const material = new THREE.MeshBasicMaterial({ - color: materials[index].color, - side: THREE.DoubleSide, - wireframe: true, - }); - materialsWireframe.push(material); - } - - planeMeshWireframe = new THREE.Mesh(planeGeometry, materialsWireframe); - boxMeshWireframe = new THREE.Mesh(boxGeometry, materialsWireframe); - - boxMesh = new THREE.Mesh(boxGeometry, materials); - - planeMesh.position.set(-1.5, -1, 0); - boxMesh.position.set(1.5, -0.75, 0); - boxMesh.rotation.set(-Math.PI / 8, Math.PI / 4, Math.PI / 4); - - planeMeshWireframe.position.set(-1.5, 1, 0); - boxMeshWireframe.position.set(1.5, 1.25, 0); - boxMeshWireframe.rotation.set(-Math.PI / 8, Math.PI / 4, Math.PI / 4); - - scene.add(planeMesh, planeMeshWireframe); - scene.add(boxMesh, boxMeshWireframe); -} - -function animate() { - boxMesh.rotation.y += 0.005; - boxMesh.rotation.x += 0.005; - boxMeshWireframe.rotation.y += 0.005; - boxMeshWireframe.rotation.x += 0.005; - renderer.render(scene, camera); -} - -// gui - -const gui = renderer.inspector.createParameters('Parameters'); - -gui.add(api, 'webgpu').onChange(() => { - init(!api.webgpu); -}); - -// listeners - -window.addEventListener('resize', onWindowResize); - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); -} diff --git a/examples-testing/examples/webgpu_materials_basic.ts b/examples-testing/examples/webgpu_materials_basic.ts deleted file mode 100644 index 6aca76c97..000000000 --- a/examples-testing/examples/webgpu_materials_basic.ts +++ /dev/null @@ -1,129 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer; - -const spheres = []; - -let mouseX = 0; -let mouseY = 0; - -let windowHalfX = window.innerWidth / 2; -let windowHalfY = window.innerHeight / 2; - -const params = { - color: '#ffffff', - mapping: THREE.CubeReflectionMapping, - refractionRatio: 0.98, - transparent: false, - opacity: 1, -}; - -const mappings = { ReflectionMapping: THREE.CubeReflectionMapping, RefractionMapping: THREE.CubeRefractionMapping }; - -document.addEventListener('mousemove', onDocumentMouseMove); - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.01, 100); - camera.position.z = 3; - - const path = './textures/cube/pisa/'; - const format = '.png'; - const urls = [ - path + 'px' + format, - path + 'nx' + format, - path + 'py' + format, - path + 'ny' + format, - path + 'pz' + format, - path + 'nz' + format, - ]; - - const textureCube = new THREE.CubeTextureLoader().load(urls); - - scene = new THREE.Scene(); - scene.background = textureCube; - - const geometry = new THREE.SphereGeometry(0.1, 32, 16); - const material = new THREE.MeshBasicMaterial({ color: 0xffffff, envMap: textureCube }); - - for (let i = 0; i < 500; i++) { - const mesh = new THREE.Mesh(geometry, material); - - mesh.position.x = Math.random() * 10 - 5; - mesh.position.y = Math.random() * 10 - 5; - mesh.position.z = Math.random() * 10 - 5; - - mesh.scale.x = mesh.scale.y = mesh.scale.z = Math.random() * 3 + 1; - - scene.add(mesh); - - spheres.push(mesh); - } - - // - - renderer = new THREE.WebGPURenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // - - const gui = renderer.inspector.createParameters('Parameters'); - - gui.addColor(params, 'color').onChange(value => material.color.set(value)); - gui.add(params, 'mapping', mappings).onChange(value => { - textureCube.mapping = value; - material.needsUpdate = true; - }); - gui.add(params, 'refractionRatio', 0, 1, 0.01).onChange(value => (material.refractionRatio = value)); - gui.add(params, 'transparent').onChange(value => { - material.transparent = value; - material.needsUpdate = true; - }); - gui.add(params, 'opacity', 0, 1, 0.01).onChange(value => (material.opacity = value)); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - windowHalfX = window.innerWidth / 2; - windowHalfY = window.innerHeight / 2; - - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function onDocumentMouseMove(event) { - mouseX = (event.clientX - windowHalfX) / 100; - mouseY = (event.clientY - windowHalfY) / 100; -} - -// - -function animate() { - const timer = 0.0001 * Date.now(); - - camera.position.x += (mouseX - camera.position.x) * 0.05; - camera.position.y += (-mouseY - camera.position.y) * 0.05; - - camera.lookAt(scene.position); - - for (let i = 0, il = spheres.length; i < il; i++) { - const sphere = spheres[i]; - - sphere.position.x = 5 * Math.cos(timer + i); - sphere.position.y = 5 * Math.sin(timer + i * 1.1); - } - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_materials_cubemap_mipmaps.ts b/examples-testing/examples/webgpu_materials_cubemap_mipmaps.ts deleted file mode 100644 index 6c66f9d35..000000000 --- a/examples-testing/examples/webgpu_materials_cubemap_mipmaps.ts +++ /dev/null @@ -1,119 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let container; - -let camera, scene, renderer; - -init(); - -// load customized cube texture -async function loadCubeTextureWithMipmaps() { - const path = 'textures/cube/angus/'; - const format = '.jpg'; - const mipmaps = []; - const maxLevel = 8; - - async function loadCubeTexture(urls) { - return new Promise(function (resolve) { - new THREE.CubeTextureLoader().load(urls, function (cubeTexture) { - resolve(cubeTexture); - }); - }); - } - - // load mipmaps - const pendings = []; - - for (let level = 0; level <= maxLevel; ++level) { - const urls = []; - - for (let face = 0; face < 6; ++face) { - urls.push(path + 'cube_m0' + level + '_c0' + face + format); - } - - const mipmapLevel = level; - - pendings.push( - loadCubeTexture(urls).then(function (cubeTexture) { - mipmaps[mipmapLevel] = cubeTexture; - }), - ); - } - - await Promise.all(pendings); - - const customizedCubeTexture = mipmaps.shift(); - customizedCubeTexture.mipmaps = mipmaps; - customizedCubeTexture.colorSpace = THREE.SRGBColorSpace; - customizedCubeTexture.minFilter = THREE.LinearMipMapLinearFilter; - customizedCubeTexture.magFilter = THREE.LinearFilter; - customizedCubeTexture.generateMipmaps = false; - customizedCubeTexture.needsUpdate = true; - - return customizedCubeTexture; -} - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000); - camera.position.z = 500; - - scene = new THREE.Scene(); - - loadCubeTextureWithMipmaps().then(function (cubeTexture) { - // model - const sphere = new THREE.SphereGeometry(100, 128, 128); - - // manual mipmaps - let material = new THREE.MeshBasicMaterial({ color: 0xffffff, envMap: cubeTexture }); - material.name = 'manual mipmaps'; - - let mesh = new THREE.Mesh(sphere, material); - mesh.position.set(100, 0, 0); - scene.add(mesh); - - // auto mipmaps - material = material.clone(); - material.name = 'auto mipmaps'; - - const autoCubeTexture = cubeTexture.clone(); - autoCubeTexture.mipmaps = []; - autoCubeTexture.generateMipmaps = true; - autoCubeTexture.needsUpdate = true; - - material.envMap = autoCubeTexture; - - mesh = new THREE.Mesh(sphere, material); - mesh.position.set(-100, 0, 0); - scene.add(mesh); - }); - - //renderer - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - //controls - const controls = new OrbitControls(camera, renderer.domElement); - controls.minPolarAngle = Math.PI / 4; - controls.maxPolarAngle = Math.PI / 1.5; - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_materials_displacementmap.ts b/examples-testing/examples/webgpu_materials_displacementmap.ts deleted file mode 100644 index 17a01f324..000000000 --- a/examples-testing/examples/webgpu_materials_displacementmap.ts +++ /dev/null @@ -1,195 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { OBJLoader } from 'three/addons/loaders/OBJLoader.js'; - -let camera, scene, renderer, controls; - -const settings = { - metalness: 1.0, - roughness: 0.4, - ambientIntensity: 0.2, - aoMapIntensity: 1.0, - envMapIntensity: 1.0, - displacementScale: 2.436143, // from original model - normalScale: 1.0, -}; - -let mesh, material; - -let pointLight, ambientLight; - -const height = 500; // of camera frustum - -let r = 0.0; - -init(); -initGui(); - -// Init gui -function initGui() { - const gui = renderer.inspector.createParameters('settings'); - - gui.add(settings, 'metalness', 0, 1).onChange(function (value) { - material.metalness = value; - }); - - gui.add(settings, 'roughness', 0, 1).onChange(function (value) { - material.roughness = value; - }); - - gui.add(settings, 'aoMapIntensity', 0, 1).onChange(function (value) { - material.aoMapIntensity = value; - }); - - gui.add(settings, 'ambientIntensity', 0, 1).onChange(function (value) { - ambientLight.intensity = value; - }); - - gui.add(settings, 'envMapIntensity', 0, 3).onChange(function (value) { - material.envMapIntensity = value; - }); - - gui.add(settings, 'displacementScale', 0, 3.0).onChange(function (value) { - material.displacementScale = value; - }); - - gui.add(settings, 'normalScale', -1, 1).onChange(function (value) { - material.normalScale.set(1, -1).multiplyScalar(value); - }); -} - -function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - renderer = new THREE.WebGPURenderer(); - renderer.setAnimationLoop(animate); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.inspector = new Inspector(); - container.appendChild(renderer.domElement); - - // - - scene = new THREE.Scene(); - - const aspect = window.innerWidth / window.innerHeight; - camera = new THREE.OrthographicCamera(-height * aspect, height * aspect, height, -height, 1, 10000); - camera.position.z = 1500; - scene.add(camera); - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableZoom = false; - controls.enableDamping = true; - - // lights - - ambientLight = new THREE.AmbientLight(0xffffff, settings.ambientIntensity); - scene.add(ambientLight); - - pointLight = new THREE.PointLight(0xff0000, 1.5, 0, 0); - pointLight.position.z = 2500; - scene.add(pointLight); - - const pointLight2 = new THREE.PointLight(0xff6666, 3, 0, 0); - camera.add(pointLight2); - - const pointLight3 = new THREE.PointLight(0x0000ff, 1.5, 0, 0); - pointLight3.position.x = -1000; - pointLight3.position.z = 1000; - scene.add(pointLight3); - - // env map - - const path = 'textures/cube/SwedishRoyalCastle/'; - const format = '.jpg'; - const urls = [ - path + 'px' + format, - path + 'nx' + format, - path + 'py' + format, - path + 'ny' + format, - path + 'pz' + format, - path + 'nz' + format, - ]; - - const reflectionCube = new THREE.CubeTextureLoader().load(urls); - - // textures - - const textureLoader = new THREE.TextureLoader(); - const normalMap = textureLoader.load('models/obj/ninja/normal.png'); - const aoMap = textureLoader.load('models/obj/ninja/ao.jpg'); - const displacementMap = textureLoader.load('models/obj/ninja/displacement.jpg'); - - // material - - material = new THREE.MeshStandardNodeMaterial({ - color: 0xc1c1c1, - roughness: settings.roughness, - metalness: settings.metalness, - - normalMap: normalMap, - normalScale: new THREE.Vector2(1, -1), // why does the normal map require negation in this case? - - aoMap: aoMap, - aoMapIntensity: 1, - - displacementMap: displacementMap, - displacementScale: settings.displacementScale, - displacementBias: -0.428408, // from original model - - envMap: reflectionCube, - envMapIntensity: settings.envMapIntensity, - - side: THREE.DoubleSide, - }); - - // - - const loader = new OBJLoader(); - loader.load('models/obj/ninja/ninjaHead_Low.obj', function (group) { - const geometry = group.children[0].geometry; - geometry.center(); - - mesh = new THREE.Mesh(geometry, material); - mesh.scale.multiplyScalar(25); - scene.add(mesh); - }); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - const aspect = window.innerWidth / window.innerHeight; - - camera.left = -height * aspect; - camera.right = height * aspect; - camera.top = height; - camera.bottom = -height; - - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - controls.update(); - - render(); -} - -function render() { - pointLight.position.x = 2500 * Math.cos(r); - pointLight.position.z = 2500 * Math.sin(r); - - r += 0.01; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_materials_envmaps.ts b/examples-testing/examples/webgpu_materials_envmaps.ts deleted file mode 100644 index 6862caeed..000000000 --- a/examples-testing/examples/webgpu_materials_envmaps.ts +++ /dev/null @@ -1,132 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let controls, camera, scene, renderer; -let textureEquirec, textureCube; -let sphereMesh, sphereMaterial, params; - -init(); - -function init() { - // CAMERAS - - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(0, 0, 2.5); - - // SCENE - - scene = new THREE.Scene(); - - // Textures - - const loader = new THREE.CubeTextureLoader(); - loader.setPath('textures/cube/Bridge2/'); - - textureCube = loader.load(['posx.jpg', 'negx.jpg', 'posy.jpg', 'negy.jpg', 'posz.jpg', 'negz.jpg']); - - const textureLoader = new THREE.TextureLoader(); - - textureEquirec = textureLoader.load('textures/2294472375_24a3b8ef46_o.jpg'); - textureEquirec.mapping = THREE.EquirectangularReflectionMapping; - textureEquirec.colorSpace = THREE.SRGBColorSpace; - - scene.background = textureCube; - - // - - const geometry = new THREE.IcosahedronGeometry(1, 15); - sphereMaterial = new THREE.MeshBasicMaterial({ envMap: textureCube }); - sphereMesh = new THREE.Mesh(geometry, sphereMaterial); - scene.add(sphereMesh); - - // - - renderer = new THREE.WebGPURenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // - - controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 1.5; - controls.maxDistance = 6; - - // - - params = { - Type: 'Cube', - Refraction: false, - backgroundRotationX: false, - backgroundRotationY: false, - backgroundRotationZ: false, - syncMaterial: false, - }; - - const gui = renderer.inspector.createParameters('Parameters'); - gui.add(params, 'Type', ['Cube', 'Equirectangular']).onChange(function (value) { - if (value === 'Cube') { - scene.background = textureCube; - - sphereMaterial.envMap = textureCube; - sphereMaterial.needsUpdate = true; - } else if (value === 'Equirectangular') { - scene.background = textureEquirec; - - sphereMaterial.envMap = textureEquirec; - sphereMaterial.needsUpdate = true; - } - }); - gui.add(params, 'Refraction').onChange(function (value) { - if (value) { - textureEquirec.mapping = THREE.EquirectangularRefractionMapping; - textureCube.mapping = THREE.CubeRefractionMapping; - } else { - textureEquirec.mapping = THREE.EquirectangularReflectionMapping; - textureCube.mapping = THREE.CubeReflectionMapping; - } - - sphereMaterial.needsUpdate = true; - }); - gui.add(params, 'backgroundRotationX'); - gui.add(params, 'backgroundRotationY'); - gui.add(params, 'backgroundRotationZ'); - gui.add(params, 'syncMaterial'); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - if (params.backgroundRotationX) { - scene.backgroundRotation.x += 0.001; - } - - if (params.backgroundRotationY) { - scene.backgroundRotation.y += 0.001; - } - - if (params.backgroundRotationZ) { - scene.backgroundRotation.z += 0.001; - } - - if (params.syncMaterial) { - sphereMesh.material.envMapRotation.copy(scene.backgroundRotation); - } - - camera.lookAt(scene.position); - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_materials_envmaps_bpcem.ts b/examples-testing/examples/webgpu_materials_envmaps_bpcem.ts deleted file mode 100644 index adc28c989..000000000 --- a/examples-testing/examples/webgpu_materials_envmaps_bpcem.ts +++ /dev/null @@ -1,202 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { - bumpMap, - float, - getParallaxCorrectNormal, - pmremTexture, - reflectVector, - texture, - uniform, - vec3, -} from 'three/tsl'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { RectAreaLightHelper } from 'three/addons/helpers/RectAreaLightHelper.js'; -import { RectAreaLightTexturesLib } from 'three/addons/lights/RectAreaLightTexturesLib.js'; - -let camera, scene, renderer; - -let controls, cubeCamera; - -let groundPlane, wallMat; - -init(); - -async function init() { - THREE.RectAreaLightNode.setLTC(RectAreaLightTexturesLib.init()); - - // scene - - scene = new THREE.Scene(); - - // camera - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000); - camera.position.set(0, 200, -200); - - // cube camera for environment map - - const renderTarget = new THREE.CubeRenderTarget(512); - renderTarget.texture.type = THREE.HalfFloatType; - renderTarget.texture.minFilter = THREE.LinearMipmapLinearFilter; - renderTarget.texture.magFilter = THREE.LinearFilter; - renderTarget.texture.generateMipmaps = true; - renderTarget.texture.mapping = THREE.CubeReflectionMapping; - - cubeCamera = new THREE.CubeCamera(1, 1000, renderTarget); - cubeCamera.position.set(0, -100, 0); - - // ground floor ( with box projected environment mapping ) - - const loader = new THREE.TextureLoader(); - const rMap = loader.load('textures/lava/lavatile.jpg'); - rMap.wrapS = THREE.RepeatWrapping; - rMap.wrapT = THREE.RepeatWrapping; - rMap.repeat.set(2, 1); - - const roughnessUniform = uniform(0.25); - - const defaultMat = new THREE.MeshStandardNodeMaterial(); - defaultMat.envNode = pmremTexture(renderTarget.texture); - defaultMat.roughnessNode = texture(rMap).mul(roughnessUniform); - defaultMat.metalnessNode = float(1); - - const boxProjectedMat = new THREE.MeshStandardNodeMaterial(); - boxProjectedMat.envNode = pmremTexture( - renderTarget.texture, - getParallaxCorrectNormal(reflectVector, vec3(200, 100, 100), vec3(0, -50, 0)), - ); - boxProjectedMat.roughnessNode = texture(rMap).mul(roughnessUniform); - boxProjectedMat.metalnessNode = float(1); - - groundPlane = new THREE.Mesh(new THREE.PlaneGeometry(200, 100, 100), boxProjectedMat); - groundPlane.rotateX(-Math.PI / 2); - groundPlane.position.set(0, -49, 0); - scene.add(groundPlane); - - // walls - - const [diffuseTex, bumpTex] = await Promise.all([ - loader.loadAsync('textures/brick_diffuse.jpg'), - loader.loadAsync('textures/brick_bump.jpg'), - ]); - diffuseTex.colorSpace = THREE.SRGBColorSpace; - - wallMat = new THREE.MeshStandardNodeMaterial(); - - wallMat.colorNode = texture(diffuseTex); - wallMat.normalNode = bumpMap(texture(bumpTex), float(5)); - - const planeGeo = new THREE.PlaneGeometry(100, 100); - - const planeBack1 = new THREE.Mesh(planeGeo, wallMat); - planeBack1.position.z = -50; - planeBack1.position.x = -50; - scene.add(planeBack1); - - const planeBack2 = new THREE.Mesh(planeGeo, wallMat); - planeBack2.position.z = -50; - planeBack2.position.x = 50; - scene.add(planeBack2); - - const planeFront1 = new THREE.Mesh(planeGeo, wallMat); - planeFront1.position.z = 50; - planeFront1.position.x = -50; - planeFront1.rotateY(Math.PI); - scene.add(planeFront1); - - const planeFront2 = new THREE.Mesh(planeGeo, wallMat); - planeFront2.position.z = 50; - planeFront2.position.x = 50; - planeFront2.rotateY(Math.PI); - scene.add(planeFront2); - - const planeRight = new THREE.Mesh(planeGeo, wallMat); - planeRight.position.x = 100; - planeRight.rotateY(-Math.PI / 2); - scene.add(planeRight); - - const planeLeft = new THREE.Mesh(planeGeo, wallMat); - planeLeft.position.x = -100; - planeLeft.rotateY(Math.PI / 2); - scene.add(planeLeft); - - // area lights - - const width = 50; - const height = 50; - const intensity = 5; - - const blueRectLight = new THREE.RectAreaLight(0x9aaeff, intensity, width, height); - blueRectLight.position.set(-99, 5, 0); - blueRectLight.lookAt(0, 5, 0); - scene.add(blueRectLight); - - const blueRectLightHelper = new RectAreaLightHelper(blueRectLight, 0xffffff); - blueRectLight.add(blueRectLightHelper); - - const redRectLight = new THREE.RectAreaLight(0xf3aaaa, intensity, width, height); - redRectLight.position.set(99, 5, 0); - redRectLight.lookAt(0, 5, 0); - scene.add(redRectLight); - - const redRectLightHelper = new RectAreaLightHelper(redRectLight, 0xffffff); - redRectLight.add(redRectLightHelper); - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - await renderer.init(); - - updateCubeMap(); - - window.addEventListener('resize', onWindowResize); - - // controls - - controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, -10, 0); - controls.maxDistance = 400; - controls.minDistance = 10; - controls.update(); - - // gui - - const gui = renderer.inspector.createParameters('Parameters'); - const params = { - 'box projected': true, - }; - gui.add(params, 'box projected').onChange(value => { - groundPlane.material = value ? boxProjectedMat : defaultMat; - }); - gui.add(roughnessUniform, 'value', 0, 1).name('roughness'); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function updateCubeMap() { - groundPlane.visible = false; - - cubeCamera.position.copy(groundPlane.position); - - cubeCamera.update(renderer, scene); - - groundPlane.visible = true; -} - -function animate() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_materials_lightmap.ts b/examples-testing/examples/webgpu_materials_lightmap.ts deleted file mode 100644 index 081de95ac..000000000 --- a/examples-testing/examples/webgpu_materials_lightmap.ts +++ /dev/null @@ -1,105 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { vec4, color, positionLocal, mix } from 'three/tsl'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let container; -let camera, scene, renderer; - -const params = { - intensity: 1, -}; - -init(); - -async function init() { - const { innerWidth, innerHeight } = window; - - container = document.createElement('div'); - document.body.appendChild(container); - - // CAMERA - - camera = new THREE.PerspectiveCamera(40, innerWidth / innerHeight, 1, 10000); - camera.position.set(700, 200, -500); - - // SCENE - - scene = new THREE.Scene(); - - // LIGHTS - - const light = new THREE.DirectionalLight(0xd5deff); - light.position.x = 300; - light.position.y = 250; - light.position.z = -500; - scene.add(light); - - // SKYDOME - - const topColor = new THREE.Color().copy(light.color); - const bottomColor = new THREE.Color(0xffffff); - const offset = 400; - const exponent = 0.6; - - const h = positionLocal.add(offset).normalize().y; - - const skyMat = new THREE.MeshBasicNodeMaterial(); - skyMat.colorNode = vec4(mix(color(bottomColor), color(topColor), h.max(0.0).pow(exponent)), 1.0); - skyMat.side = THREE.BackSide; - - const sky = new THREE.Mesh(new THREE.SphereGeometry(4000, 32, 15), skyMat); - scene.add(sky); - - // MODEL - - const loader = new THREE.ObjectLoader(); - const object = await loader.loadAsync('models/json/lightmap/lightmap.json'); - scene.add(object); - - // RENDERER - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setAnimationLoop(animate); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(innerWidth, innerHeight); - renderer.inspector = new Inspector(); - container.appendChild(renderer.domElement); - - // CONTROLS - - const controls = new OrbitControls(camera, renderer.domElement); - controls.maxPolarAngle = (0.9 * Math.PI) / 2; - controls.enableZoom = false; - - // GUI - - const gui = renderer.inspector.createParameters('Parameters'); - - gui.add(params, 'intensity', 0, 1) - .name('Light Map Intensity') - .onChange(value => { - for (const material of object.material) { - material.lightMapIntensity = value; - } - }); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_materials_matcap.ts b/examples-testing/examples/webgpu_materials_matcap.ts deleted file mode 100644 index 0a841ae27..000000000 --- a/examples-testing/examples/webgpu_materials_matcap.ts +++ /dev/null @@ -1,194 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { EXRLoader } from 'three/addons/loaders/EXRLoader.js'; - -let mesh, renderer, scene, camera; - -const API = { - color: 0xffffff, // sRGB - exposure: 1.0, -}; - -init(); - -function init() { - // renderer - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // tone mapping - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = API.exposure; - - // scene - scene = new THREE.Scene(); - - // camera - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 100); - camera.position.set(0, 0, 13); - - // controls - const controls = new OrbitControls(camera, renderer.domElement); - controls.enableZoom = false; - controls.enablePan = false; - - // matcap - const loaderEXR = new EXRLoader(); - const matcap = loaderEXR.load('textures/matcaps/040full.exr'); - - // normalmap - const loader = new THREE.TextureLoader(); - - const normalmap = loader.load('models/gltf/LeePerrySmith/Infinite-Level_02_Tangent_SmoothUV.jpg'); - - // model - new GLTFLoader().load('models/gltf/LeePerrySmith/LeePerrySmith.glb', function (gltf) { - mesh = gltf.scene.children[0]; - mesh.position.y = -0.25; - - mesh.material = new THREE.MeshMatcapNodeMaterial({ - color: new THREE.Color().setHex(API.color), - matcap: matcap, - normalMap: normalmap, - }); - - scene.add(mesh); - }); - - // gui - const gui = renderer.inspector.createParameters('Parameters'); - - gui.addColor(API, 'color') - .listen() - .onChange(function () { - mesh.material.color.set(API.color); - }); - - gui.add(API, 'exposure', 0, 2) - .listen() - .onChange(function () { - renderer.toneMappingExposure = API.exposure; - }); - - // drag 'n drop - initDragAndDrop(); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - renderer.setSize(window.innerWidth, window.innerHeight); - - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); -} - -function animate() { - renderer.render(scene, camera); -} - -// -// drag and drop anywhere in document -// - -function updateMatcap(texture) { - if (mesh.material.matcap) { - mesh.material.matcap.dispose(); - } - - mesh.material.matcap = texture; - - texture.needsUpdate = true; - - mesh.material.needsUpdate = true; // because the color space can change -} - -function handleJPG(event) { - // PNG, WebP, AVIF, too - - function imgCallback(event) { - const texture = new THREE.Texture(event.target); - - texture.colorSpace = THREE.SRGBColorSpace; - - updateMatcap(texture); - } - - const img = new Image(); - - img.onload = imgCallback; - - img.src = event.target.result; -} - -function handleEXR(event) { - const contents = event.target.result; - - const loader = new EXRLoader(); - - loader.setDataType(THREE.HalfFloatType); - - const texData = loader.parse(contents); - - const texture = new THREE.DataTexture(); - - texture.image.width = texData.width; - texture.image.height = texData.height; - texture.image.data = texData.data; - - texture.format = texData.format; - texture.type = texData.type; - texture.colorSpace = THREE.LinearSRGBColorSpace; - texture.minFilter = THREE.LinearFilter; - texture.magFilter = THREE.LinearFilter; - texture.generateMipmaps = false; - texture.flipY = false; - - updateMatcap(texture); -} - -function loadFile(file) { - const filename = file.name; - const extension = filename.split('.').pop().toLowerCase(); - - if (extension === 'exr') { - const reader = new FileReader(); - - reader.addEventListener('load', function (event) { - handleEXR(event); - }); - - reader.readAsArrayBuffer(file); - } else { - // 'jpg', 'png' - - const reader = new FileReader(); - - reader.addEventListener('load', function (event) { - handleJPG(event); - }); - - reader.readAsDataURL(file); - } -} - -function initDragAndDrop() { - document.addEventListener('dragover', function (event) { - event.preventDefault(); - event.dataTransfer.dropEffect = 'copy'; - }); - - document.addEventListener('drop', function (event) { - event.preventDefault(); - - loadFile(event.dataTransfer.files[0]); - }); -} diff --git a/examples-testing/examples/webgpu_materials_sss.ts b/examples-testing/examples/webgpu_materials_sss.ts deleted file mode 100644 index c76df99dc..000000000 --- a/examples-testing/examples/webgpu_materials_sss.ts +++ /dev/null @@ -1,153 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { texture, uniform, vec3 } from 'three/tsl'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { FBXLoader } from 'three/addons/loaders/FBXLoader.js'; - -let container; -let camera, scene, renderer; -let model; - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 5000); - camera.position.set(0.0, 300, 400 * 4); - - scene = new THREE.Scene(); - - // Lights - - scene.add(new THREE.AmbientLight(0xc1c1c1)); - - const directionalLight = new THREE.DirectionalLight(0xffffff, 0.03); - directionalLight.position.set(0.0, 0.5, 0.5).normalize(); - scene.add(directionalLight); - - const pointLight1 = new THREE.Mesh( - new THREE.SphereGeometry(4, 8, 8), - new THREE.MeshBasicMaterial({ color: 0xc1c1c1 }), - ); - pointLight1.add(new THREE.PointLight(0xc1c1c1, 4.0, 300, 0)); - scene.add(pointLight1); - pointLight1.position.x = 0; - pointLight1.position.y = -50; - pointLight1.position.z = 350; - - const pointLight2 = new THREE.Mesh( - new THREE.SphereGeometry(4, 8, 8), - new THREE.MeshBasicMaterial({ color: 0xc1c100 }), - ); - pointLight2.add(new THREE.PointLight(0xc1c100, 0.75, 500, 0)); - scene.add(pointLight2); - pointLight2.position.x = -100; - pointLight2.position.y = 20; - pointLight2.position.z = -260; - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(render); - container.appendChild(renderer.domElement); - - // - - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.inspector.domElement); - - // - - const controls = new OrbitControls(camera, container); - controls.minDistance = 500; - controls.maxDistance = 3000; - - window.addEventListener('resize', onWindowResize); - - initMaterial(); -} - -function initMaterial() { - const loader = new THREE.TextureLoader(); - const imgTexture = loader.load('models/fbx/white.jpg'); - imgTexture.colorSpace = THREE.SRGBColorSpace; - - const thicknessTexture = loader.load('models/fbx/bunny_thickness.jpg'); - imgTexture.wrapS = imgTexture.wrapT = THREE.RepeatWrapping; - - const material = new THREE.MeshSSSNodeMaterial(); - material.color = new THREE.Color(1.0, 0.2, 0.2); - material.roughness = 0.3; - material.thicknessColorNode = texture(thicknessTexture).mul(vec3(0.5, 0.3, 0.0)); - material.thicknessDistortionNode = uniform(0.1); - material.thicknessAmbientNode = uniform(0.4); - material.thicknessAttenuationNode = uniform(0.8); - material.thicknessPowerNode = uniform(2.0); - material.thicknessScaleNode = uniform(16.0); - - // LOADER - - const loaderFBX = new FBXLoader(); - loaderFBX.load('models/fbx/stanford-bunny.fbx', function (object) { - model = object.children[0]; - model.position.set(0, 0, 10); - model.scale.setScalar(1); - model.material = material; - scene.add(model); - }); - - initGUI(material); -} - -function initGUI(material) { - const gui = renderer.inspector.createParameters('Parameters'); - - const ThicknessControls = function () { - this.distortion = material.thicknessDistortionNode.value; - this.ambient = material.thicknessAmbientNode.value; - this.attenuation = material.thicknessAttenuationNode.value; - this.power = material.thicknessPowerNode.value; - this.scale = material.thicknessScaleNode.value; - }; - - const thicknessControls = new ThicknessControls(); - - gui.add(thicknessControls, 'distortion', 0.01, 1, 0.01).onChange(function () { - material.thicknessDistortionNode.value = thicknessControls.distortion; - }); - - gui.add(thicknessControls, 'ambient', 0.01, 5.0, 0.05).onChange(function () { - material.thicknessAmbientNode.value = thicknessControls.ambient; - }); - - gui.add(thicknessControls, 'attenuation', 0.01, 5.0, 0.05).onChange(function () { - material.thicknessAttenuationNode.value = thicknessControls.attenuation; - }); - - gui.add(thicknessControls, 'power', 0.01, 16.0, 0.1).onChange(function () { - material.thicknessPowerNode.value = thicknessControls.power; - }); - - gui.add(thicknessControls, 'scale', 0.01, 50.0, 0.1).onChange(function () { - material.thicknessScaleNode.value = thicknessControls.scale; - }); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function render() { - if (model) model.rotation.y = performance.now() / 5000; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_materials_texture_manualmipmap.ts b/examples-testing/examples/webgpu_materials_texture_manualmipmap.ts deleted file mode 100644 index 090372729..000000000 --- a/examples-testing/examples/webgpu_materials_texture_manualmipmap.ts +++ /dev/null @@ -1,170 +0,0 @@ -import * as THREE from 'three/webgpu'; - -const SCREEN_WIDTH = window.innerWidth; -const SCREEN_HEIGHT = window.innerHeight; - -let container; - -let camera, scene1, scene2, renderer; - -let mouseX = 0, - mouseY = 0; - -const windowHalfX = window.innerWidth / 2; -const windowHalfY = window.innerHeight / 2; - -init(); - -async function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(35, SCREEN_WIDTH / SCREEN_HEIGHT, 1, 5000); - camera.position.z = 1500; - - scene1 = new THREE.Scene(); - scene1.background = new THREE.Color(0x000000); - scene1.fog = new THREE.Fog(0x000000, 1500, 4000); - - scene2 = new THREE.Scene(); - scene2.background = new THREE.Color(0x000000); - scene2.fog = new THREE.Fog(0x000000, 1500, 4000); - - // GROUND - - const canvas = mipmap(128, '#f00'); - const textureCanvas1 = new THREE.CanvasTexture(canvas); - textureCanvas1.mipmaps[0] = canvas; - textureCanvas1.mipmaps[1] = mipmap(64, '#0f0'); - textureCanvas1.mipmaps[2] = mipmap(32, '#00f'); - textureCanvas1.mipmaps[3] = mipmap(16, '#400'); - textureCanvas1.mipmaps[4] = mipmap(8, '#040'); - textureCanvas1.mipmaps[5] = mipmap(4, '#004'); - textureCanvas1.mipmaps[6] = mipmap(2, '#044'); - textureCanvas1.mipmaps[7] = mipmap(1, '#404'); - textureCanvas1.colorSpace = THREE.SRGBColorSpace; - textureCanvas1.repeat.set(1000, 1000); - textureCanvas1.wrapS = THREE.RepeatWrapping; - textureCanvas1.wrapT = THREE.RepeatWrapping; - - const textureCanvas2 = textureCanvas1.clone(); - textureCanvas2.magFilter = THREE.NearestFilter; - textureCanvas2.minFilter = THREE.NearestMipmapNearestFilter; - - const materialCanvas1 = new THREE.MeshBasicMaterial({ map: textureCanvas1 }); - const materialCanvas2 = new THREE.MeshBasicMaterial({ color: 0xffccaa, map: textureCanvas2 }); - - const geometry = new THREE.PlaneGeometry(100, 100); - - const meshCanvas1 = new THREE.Mesh(geometry, materialCanvas1); - meshCanvas1.rotation.x = -Math.PI / 2; - meshCanvas1.scale.set(1000, 1000, 1000); - - const meshCanvas2 = new THREE.Mesh(geometry, materialCanvas2); - meshCanvas2.rotation.x = -Math.PI / 2; - meshCanvas2.scale.set(1000, 1000, 1000); - - scene1.add(meshCanvas1); - scene2.add(meshCanvas2); - - // PAINTING - - const texturePainting1 = await new THREE.TextureLoader().loadAsync( - 'textures/758px-Canestra_di_frutta_(Caravaggio).jpg', - ); - const texturePainting2 = texturePainting1.clone(); - - texturePainting1.colorSpace = THREE.SRGBColorSpace; - texturePainting2.colorSpace = THREE.SRGBColorSpace; - - texturePainting1.minFilter = texturePainting1.magFilter = THREE.LinearFilter; - texturePainting2.minFilter = texturePainting2.magFilter = THREE.NearestFilter; - - const materialPainting1 = new THREE.MeshBasicMaterial({ color: 0xffffff, map: texturePainting1 }); - const materialPainting2 = new THREE.MeshBasicMaterial({ color: 0xffccaa, map: texturePainting2 }); - - const geometryPainting = new THREE.PlaneGeometry(100, 100); - const mesh1 = new THREE.Mesh(geometryPainting, materialPainting1); - const mesh2 = new THREE.Mesh(geometryPainting, materialPainting2); - - addPainting(scene1, mesh1); - addPainting(scene2, mesh2); - - function addPainting(zscene, zmesh) { - const image = texturePainting1.image; - - zmesh.scale.x = image.width / 100; - zmesh.scale.y = image.height / 100; - - zscene.add(zmesh); - - const meshFrame = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ color: 0x000000 })); - meshFrame.position.z = -10.0; - meshFrame.scale.x = (1.1 * image.width) / 100; - meshFrame.scale.y = (1.1 * image.height) / 100; - zscene.add(meshFrame); - - const meshShadow = new THREE.Mesh( - geometry, - new THREE.MeshBasicMaterial({ color: 0x000000, opacity: 0.75, transparent: true }), - ); - meshShadow.position.y = (-1.1 * image.height) / 2; - meshShadow.position.z = (-1.1 * image.height) / 2; - meshShadow.rotation.x = -Math.PI / 2; - meshShadow.scale.x = (1.1 * image.width) / 100; - meshShadow.scale.y = (1.1 * image.height) / 100; - zscene.add(meshShadow); - - const floorHeight = (-1.117 * image.height) / 2; - meshCanvas1.position.y = meshCanvas2.position.y = floorHeight; - } - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); - renderer.setAnimationLoop(animate); - renderer.autoClear = false; - - renderer.domElement.style.position = 'relative'; - container.appendChild(renderer.domElement); - - document.addEventListener('mousemove', onDocumentMouseMove); -} - -function mipmap(size, color) { - const imageCanvas = document.createElement('canvas'); - const context = imageCanvas.getContext('2d'); - - imageCanvas.width = imageCanvas.height = size; - - context.fillStyle = '#444'; - context.fillRect(0, 0, size, size); - - context.fillStyle = color; - context.fillRect(0, 0, size / 2, size / 2); - context.fillRect(size / 2, size / 2, size / 2, size / 2); - return imageCanvas; -} - -function onDocumentMouseMove(event) { - mouseX = event.clientX - windowHalfX; - mouseY = event.clientY - windowHalfY; -} - -function animate() { - camera.position.x += (mouseX - camera.position.x) * 0.05; - camera.position.y += (-(mouseY - 200) - camera.position.y) * 0.05; - - camera.lookAt(scene1.position); - - renderer.clear(); - renderer.setScissorTest(true); - - renderer.setScissor(0, 0, SCREEN_WIDTH / 2 - 2, SCREEN_HEIGHT); - renderer.render(scene1, camera); - - renderer.setScissor(SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 2 - 2, SCREEN_HEIGHT); - renderer.render(scene2, camera); - - renderer.setScissorTest(false); -} diff --git a/examples-testing/examples/webgpu_materials_toon.ts b/examples-testing/examples/webgpu_materials_toon.ts deleted file mode 100644 index 78cb62ed6..000000000 --- a/examples-testing/examples/webgpu_materials_toon.ts +++ /dev/null @@ -1,149 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { toonOutlinePass } from 'three/tsl'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { FontLoader } from 'three/addons/loaders/FontLoader.js'; -import { TextGeometry } from 'three/addons/geometries/TextGeometry.js'; - -let container; - -let camera, scene, renderer, renderPipeline; -let particleLight; - -const loader = new FontLoader(); -loader.load('fonts/gentilis_regular.typeface.json', function (font) { - init(font); -}); - -function init(font) { - container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 2500); - camera.position.set(0.0, 400, 400 * 3.5); - - // - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x444488); - - // - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(render); - renderer.inspector = new Inspector(); - container.appendChild(renderer.domElement); - - // - - renderPipeline = new THREE.RenderPipeline(renderer); - - renderPipeline.outputNode = toonOutlinePass(scene, camera); - - // Materials - - const cubeWidth = 400; - const numberOfSpheresPerSide = 5; - const sphereRadius = (cubeWidth / numberOfSpheresPerSide) * 0.8 * 0.5; - const stepSize = 1.0 / numberOfSpheresPerSide; - - const geometry = new THREE.SphereGeometry(sphereRadius, 32, 16); - - for (let alpha = 0, alphaIndex = 0; alpha <= 1.0; alpha += stepSize, alphaIndex++) { - const colors = new Uint8Array(alphaIndex + 2); - - for (let c = 0; c <= colors.length; c++) { - colors[c] = (c / colors.length) * 256; - } - - const gradientMap = new THREE.DataTexture(colors, colors.length, 1, THREE.RedFormat); - gradientMap.needsUpdate = true; - - for (let beta = 0; beta <= 1.0; beta += stepSize) { - for (let gamma = 0; gamma <= 1.0; gamma += stepSize) { - // basic monochromatic energy preservation - const diffuseColor = new THREE.Color() - .setHSL(alpha, 0.5, gamma * 0.5 + 0.1) - .multiplyScalar(1 - beta * 0.2); - - const material = new THREE.MeshToonNodeMaterial({ - color: diffuseColor, - gradientMap: gradientMap, - }); - - const mesh = new THREE.Mesh(geometry, material); - - mesh.position.x = alpha * 400 - 200; - mesh.position.y = beta * 400 - 200; - mesh.position.z = gamma * 400 - 200; - - scene.add(mesh); - } - } - } - - function addLabel(name, location) { - const textGeo = new TextGeometry(name, { - font: font, - - size: 20, - depth: 1, - curveSegments: 1, - }); - - const textMaterial = new THREE.MeshBasicNodeMaterial(); - const textMesh = new THREE.Mesh(textGeo, textMaterial); - textMesh.position.copy(location); - scene.add(textMesh); - } - - addLabel('-gradientMap', new THREE.Vector3(-350, 0, 0)); - addLabel('+gradientMap', new THREE.Vector3(350, 0, 0)); - - addLabel('-diffuse', new THREE.Vector3(0, 0, -300)); - addLabel('+diffuse', new THREE.Vector3(0, 0, 300)); - - particleLight = new THREE.Mesh( - new THREE.SphereGeometry(4, 8, 8), - new THREE.MeshBasicNodeMaterial({ color: 0xffffff }), - ); - scene.add(particleLight); - - // Lights - - scene.add(new THREE.AmbientLight(0xc1c1c1, 3)); - - const pointLight = new THREE.PointLight(0xffffff, 2, 800, 0); - particleLight.add(pointLight); - - // - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 200; - controls.maxDistance = 2000; - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function render() { - const timer = Date.now() * 0.00025; - - particleLight.position.x = Math.sin(timer * 7) * 300; - particleLight.position.y = Math.cos(timer * 5) * 400; - particleLight.position.z = Math.cos(timer * 3) * 300; - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_materials_transmission.ts b/examples-testing/examples/webgpu_materials_transmission.ts deleted file mode 100644 index 310d979d5..000000000 --- a/examples-testing/examples/webgpu_materials_transmission.ts +++ /dev/null @@ -1,173 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; - -const params = { - color: 0xffffff, - transmission: 1, - opacity: 1, - metalness: 0, - roughness: 0, - ior: 1.5, - thickness: 0.01, - specularIntensity: 1, - specularColor: 0xffffff, - envMapIntensity: 1, - lightIntensity: 1, - exposure: 1, -}; - -let camera, scene, renderer; - -let mesh; - -const hdrEquirect = new UltraHDRLoader() - .setPath('textures/equirectangular/') - .load('royal_esplanade_2k.hdr.jpg', function () { - hdrEquirect.mapping = THREE.EquirectangularReflectionMapping; - - init(); - }); - -function init() { - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = params.exposure; - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 2000); - camera.position.set(0, 0, 120); - - // - - scene.background = hdrEquirect; - - // - - const geometry = new THREE.SphereGeometry(20, 64, 32); - - const texture = new THREE.CanvasTexture(generateTexture()); - texture.magFilter = THREE.NearestFilter; - texture.wrapT = THREE.RepeatWrapping; - texture.wrapS = THREE.RepeatWrapping; - texture.repeat.set(1, 3.5); - - const material = new THREE.MeshPhysicalMaterial({ - color: params.color, - metalness: params.metalness, - roughness: params.roughness, - ior: params.ior, - alphaMap: texture, - envMap: hdrEquirect, - envMapIntensity: params.envMapIntensity, - transmission: params.transmission, // use material.transmission for glass materials - specularIntensity: params.specularIntensity, - specularColor: params.specularColor, - opacity: params.opacity, - side: THREE.DoubleSide, - transparent: true, - }); - - mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - // - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 10; - controls.maxDistance = 150; - - window.addEventListener('resize', onWindowResize); - - // - - const gui = renderer.inspector.createParameters('Settings'); - - gui.addColor(params, 'color').onChange(function () { - material.color.set(params.color); - }); - - gui.add(params, 'transmission', 0, 1, 0.01).onChange(function () { - material.transmission = params.transmission; - }); - - gui.add(params, 'opacity', 0, 1, 0.01).onChange(function () { - material.opacity = params.opacity; - }); - - gui.add(params, 'metalness', 0, 1, 0.01).onChange(function () { - material.metalness = params.metalness; - }); - - gui.add(params, 'roughness', 0, 1, 0.01).onChange(function () { - material.roughness = params.roughness; - }); - - gui.add(params, 'ior', 1, 2, 0.01).onChange(function () { - material.ior = params.ior; - }); - - gui.add(params, 'thickness', 0, 5, 0.01).onChange(function () { - material.thickness = params.thickness; - }); - - gui.add(params, 'specularIntensity', 0, 1, 0.01) - .name('specular intensity') - .onChange(function () { - material.specularIntensity = params.specularIntensity; - }); - - gui.addColor(params, 'specularColor') - .name('specular color') - .onChange(function () { - material.specularColor.set(params.specularColor); - }); - - gui.add(params, 'envMapIntensity', 0, 1, 0.01) - .name('envMap intensity') - .onChange(function () { - material.envMapIntensity = params.envMapIntensity; - }); - - gui.add(params, 'exposure', 0, 1, 0.01).onChange(function () { - renderer.toneMappingExposure = params.exposure; - }); -} - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); -} - -// - -function generateTexture() { - const canvas = document.createElement('canvas'); - canvas.width = 2; - canvas.height = 2; - - const context = canvas.getContext('2d'); - context.fillStyle = 'white'; - context.fillRect(0, 1, 2, 1); - - return canvas; -} - -function animate() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_materials_video.ts b/examples-testing/examples/webgpu_materials_video.ts deleted file mode 100644 index bc837653f..000000000 --- a/examples-testing/examples/webgpu_materials_video.ts +++ /dev/null @@ -1,185 +0,0 @@ -import * as THREE from 'three/webgpu'; - -let container; - -let camera, scene, renderer; - -let video, texture, material, mesh; - -let mouseX = 0; -let mouseY = 0; - -let windowHalfX = window.innerWidth / 2; -let windowHalfY = window.innerHeight / 2; - -let cube_count; - -const meshes = [], - materials = [], - xgrid = 20, - ygrid = 10; - -const startButton = document.getElementById('startButton'); -startButton.addEventListener('click', function () { - init(); -}); - -function init() { - const overlay = document.getElementById('overlay'); - overlay.remove(); - - container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 10000); - camera.position.z = 500; - - scene = new THREE.Scene(); - - const light = new THREE.DirectionalLight(0xffffff, 7); - light.position.set(0.5, 1, 1).normalize(); - scene.add(light); - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(render); - container.appendChild(renderer.domElement); - - video = document.getElementById('video'); - video.play(); - video.addEventListener('play', function () { - this.currentTime = 3; - }); - - texture = new THREE.VideoTexture(video); - texture.colorSpace = THREE.SRGBColorSpace; - - // - - let i, j, ox, oy, geometry; - - const ux = 1 / xgrid; - const uy = 1 / ygrid; - - const xsize = 480 / xgrid; - const ysize = 204 / ygrid; - - const parameters = { color: 0xffffff, map: texture }; - - cube_count = 0; - - for (i = 0; i < xgrid; i++) { - for (j = 0; j < ygrid; j++) { - ox = i; - oy = j; - - geometry = new THREE.BoxGeometry(xsize, ysize, xsize); - - change_uvs(geometry, ux, uy, ox, oy); - - materials[cube_count] = new THREE.MeshPhongMaterial(parameters); - - material = materials[cube_count]; - - material.hue = i / xgrid; - material.saturation = 1 - j / ygrid; - - material.color.setHSL(material.hue, material.saturation, 0.5); - - mesh = new THREE.Mesh(geometry, material); - - mesh.position.x = (i - xgrid / 2) * xsize; - mesh.position.y = (j - ygrid / 2) * ysize; - mesh.position.z = 0; - - mesh.scale.x = mesh.scale.y = mesh.scale.z = 1; - - scene.add(mesh); - - mesh.dx = 0.001 * (0.5 - Math.random()); - mesh.dy = 0.001 * (0.5 - Math.random()); - - meshes[cube_count] = mesh; - - cube_count += 1; - } - } - - document.addEventListener('mousemove', onDocumentMouseMove); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - windowHalfX = window.innerWidth / 2; - windowHalfY = window.innerHeight / 2; - - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function change_uvs(geometry, unitx, unity, offsetx, offsety) { - const uvs = geometry.attributes.uv.array; - - for (let i = 0; i < uvs.length; i += 2) { - uvs[i] = (uvs[i] + offsetx) * unitx; - uvs[i + 1] = (uvs[i + 1] + offsety) * unity; - } -} - -function onDocumentMouseMove(event) { - mouseX = event.clientX - windowHalfX; - mouseY = (event.clientY - windowHalfY) * 0.3; -} - -// - -let h, - counter = 1; - -function render() { - const time = Date.now() * 0.00005; - - camera.position.x += (mouseX - camera.position.x) * 0.05; - camera.position.y += (-mouseY - camera.position.y) * 0.05; - - camera.lookAt(scene.position); - - for (let i = 0; i < cube_count; i++) { - material = materials[i]; - - h = ((360 * (material.hue + time)) % 360) / 360; - material.color.setHSL(h, material.saturation, 0.5); - } - - if (counter % 1000 > 200) { - for (let i = 0; i < cube_count; i++) { - mesh = meshes[i]; - - mesh.rotation.x += 10 * mesh.dx; - mesh.rotation.y += 10 * mesh.dy; - - mesh.position.x -= 150 * mesh.dx; - mesh.position.y += 150 * mesh.dy; - mesh.position.z += 300 * mesh.dx; - } - } - - if (counter % 1000 === 0) { - for (let i = 0; i < cube_count; i++) { - mesh = meshes[i]; - - mesh.dx *= -1; - mesh.dy *= -1; - } - } - - counter++; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_materialx_noise.ts b/examples-testing/examples/webgpu_materialx_noise.ts deleted file mode 100644 index 229c26a8e..000000000 --- a/examples-testing/examples/webgpu_materialx_noise.ts +++ /dev/null @@ -1,146 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { - normalWorld, - time, - mx_noise_vec3, - mx_worley_noise_vec3, - mx_cell_noise_float, - mx_fractal_noise_vec3, -} from 'three/tsl'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { HDRCubeTextureLoader } from 'three/addons/loaders/HDRCubeTextureLoader.js'; - -let container; - -let camera, scene, renderer; - -let particleLight; -let group; - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.z = 100; - - scene = new THREE.Scene(); - - group = new THREE.Group(); - scene.add(group); - - new HDRCubeTextureLoader() - .setPath('textures/cube/pisaHDR/') - .load(['px.hdr', 'nx.hdr', 'py.hdr', 'ny.hdr', 'pz.hdr', 'nz.hdr'], function (hdrTexture) { - const geometry = new THREE.SphereGeometry(8, 64, 32); - - const customUV = normalWorld.mul(10).add(time); - - // left top - - let material = new THREE.MeshPhysicalNodeMaterial(); - material.colorNode = mx_noise_vec3(customUV); - - let mesh = new THREE.Mesh(geometry, material); - mesh.position.x = -10; - mesh.position.y = 10; - group.add(mesh); - - // right top - - material = new THREE.MeshPhysicalNodeMaterial(); - material.colorNode = mx_cell_noise_float(customUV); - - mesh = new THREE.Mesh(geometry, material); - mesh.position.x = 10; - mesh.position.y = 10; - group.add(mesh); - - // left bottom - - material = new THREE.MeshPhysicalNodeMaterial(); - material.colorNode = mx_worley_noise_vec3(customUV); - - mesh = new THREE.Mesh(geometry, material); - mesh.position.x = -10; - mesh.position.y = -10; - group.add(mesh); - - // right bottom - - material = new THREE.MeshPhysicalNodeMaterial(); - material.colorNode = mx_fractal_noise_vec3(customUV.mul(0.2)); - - mesh = new THREE.Mesh(geometry, material); - mesh.position.x = 10; - mesh.position.y = -10; - group.add(mesh); - - // - - scene.background = hdrTexture; - scene.environment = hdrTexture; - }); - - // LIGHTS - - particleLight = new THREE.Mesh( - new THREE.SphereGeometry(0.4, 8, 8), - new THREE.MeshBasicMaterial({ color: 0xffffff }), - ); - scene.add(particleLight); - - particleLight.add(new THREE.PointLight(0xffffff, 1000)); - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setAnimationLoop(render); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.inspector = new Inspector(); - container.appendChild(renderer.domElement); - - // - - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = 1.25; - - // EVENTS - - new OrbitControls(camera, renderer.domElement); - - window.addEventListener('resize', onWindowResize); -} - -// - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); -} - -// - -function render() { - const timer = Date.now() * 0.00025; - - particleLight.position.x = Math.sin(timer * 7) * 30; - particleLight.position.y = Math.cos(timer * 5) * 40; - particleLight.position.z = Math.cos(timer * 3) * 30; - - for (let i = 0; i < group.children.length; i++) { - const child = group.children[i]; - child.rotation.y += 0.005; - } - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_mesh_batch.ts b/examples-testing/examples/webgpu_mesh_batch.ts deleted file mode 100644 index c20dc4b57..000000000 --- a/examples-testing/examples/webgpu_mesh_batch.ts +++ /dev/null @@ -1,265 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { radixSort } from 'three/addons/utils/SortUtils.js'; - -import { normalView, directionToColor, diffuseColor } from 'three/tsl'; - -let camera, scene, renderer; -let controls; -let gui; -let geometries, mesh, material; -const ids = []; - -const matrix = new THREE.Matrix4(); - -// - -const position = new THREE.Vector3(); -const rotation = new THREE.Euler(); -const quaternion = new THREE.Quaternion(); -const scale = new THREE.Vector3(); - -// - -const MAX_GEOMETRY_COUNT = 20000; - -const api = { - webgpu: true, - count: 512, - dynamic: 16, - - sortObjects: true, - perObjectFrustumCulled: true, - opacity: 1, - useCustomSort: true, - randomizeGeometry: () => { - for (let i = 0; i < api.count; i++) { - mesh.setGeometryIdAt(i, Math.floor(Math.random() * geometries.length)); - } - }, -}; - -init(); - -// - -function randomizeMatrix(matrix) { - position.x = Math.random() * 40 - 20; - position.y = Math.random() * 40 - 20; - position.z = Math.random() * 40 - 20; - - rotation.x = Math.random() * 2 * Math.PI; - rotation.y = Math.random() * 2 * Math.PI; - rotation.z = Math.random() * 2 * Math.PI; - - quaternion.setFromEuler(rotation); - - scale.x = scale.y = scale.z = 0.5 + Math.random() * 0.5; - - return matrix.compose(position, quaternion, scale); -} - -function randomizeRotationSpeed(rotation) { - rotation.x = Math.random() * 0.01; - rotation.y = Math.random() * 0.01; - rotation.z = Math.random() * 0.01; - return rotation; -} - -function initGeometries() { - geometries = [ - new THREE.ConeGeometry(1.0, 2.0), - new THREE.BoxGeometry(2.0, 2.0, 2.0), - new THREE.SphereGeometry(1.0, 16, 8), - ]; -} - -function createMaterial() { - if (!material) { - material = new THREE.MeshBasicNodeMaterial(); - material.outputNode = diffuseColor.mul(directionToColor(normalView).y.add(0.5)); - } - - return material; -} - -function cleanup() { - if (mesh) { - mesh.parent.remove(mesh); - - if (mesh.dispose) { - mesh.dispose(); - } - } -} - -function initMesh() { - cleanup(); - initBatchedMesh(); -} - -function initBatchedMesh() { - const geometryCount = api.count; - const vertexCount = geometries.length * 512; - const indexCount = geometries.length * 1024; - - const euler = new THREE.Euler(); - const matrix = new THREE.Matrix4(); - mesh = new THREE.BatchedMesh(geometryCount, vertexCount, indexCount, createMaterial()); - mesh.userData.rotationSpeeds = []; - - // disable full-object frustum culling since all of the objects can be dynamic. - mesh.frustumCulled = false; - - ids.length = 0; - - const geometryIds = [ - mesh.addGeometry(geometries[0]), - mesh.addGeometry(geometries[1]), - mesh.addGeometry(geometries[2]), - ]; - for (let i = 0; i < api.count; i++) { - const id = mesh.addInstance(geometryIds[i % geometryIds.length]); - mesh.setMatrixAt(id, randomizeMatrix(matrix)); - mesh.setColorAt(id, new THREE.Color(Math.random() * 0xffffff)); - - const rotationMatrix = new THREE.Matrix4(); - rotationMatrix.makeRotationFromEuler(randomizeRotationSpeed(euler)); - mesh.userData.rotationSpeeds.push(rotationMatrix); - - ids.push(id); - } - - scene.add(mesh); -} - -function init(forceWebGL = false) { - if (renderer) { - renderer.dispose(); - controls.dispose(); - document.body.removeChild(renderer.domElement); - } - - // camera - - const aspect = window.innerWidth / window.innerHeight; - - camera = new THREE.PerspectiveCamera(70, aspect, 1, 100); - camera.position.z = 30; - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: true, forceWebGL }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.inspector = new Inspector(); - renderer.setAnimationLoop(animate); - - // scene - - scene = new THREE.Scene(); - scene.background = forceWebGL ? new THREE.Color(0xffc1c1) : new THREE.Color(0xc1c1ff); - - document.body.appendChild(renderer.domElement); - - initGeometries(); - initMesh(); - - // controls - - controls = new OrbitControls(camera, renderer.domElement); - controls.autoRotate = true; - controls.autoRotateSpeed = 1.0; - - // gui - - gui = renderer.inspector.createParameters('Settings'); - gui.add(api, 'webgpu').onChange(() => { - init(!api.webgpu); - }); - gui.add(api, 'count', 1, MAX_GEOMETRY_COUNT, 1).onChange(initMesh); - gui.add(api, 'dynamic', 0, MAX_GEOMETRY_COUNT, 1); - - gui.add(api, 'opacity', 0, 1).onChange(v => { - if (v < 1) { - material.transparent = true; - material.depthWrite = false; - } else { - material.transparent = false; - material.depthWrite = true; - } - - material.opacity = v; - material.needsUpdate = true; - }); - gui.add(api, 'sortObjects'); - gui.add(api, 'perObjectFrustumCulled'); - gui.add(api, 'useCustomSort'); - gui.add(api, 'randomizeGeometry').name('randomize geometry'); - - // listeners - - window.addEventListener('resize', onWindowResize); - - function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); - } - - async function animate() { - animateMeshes(); - - controls.update(); - - if (mesh.isBatchedMesh) { - mesh.sortObjects = api.sortObjects; - mesh.perObjectFrustumCulled = api.perObjectFrustumCulled; - mesh.setCustomSort(api.useCustomSort ? sortFunction : null); - } - - renderer.render(scene, camera); - } - - function animateMeshes() { - const loopNum = Math.min(api.count, api.dynamic); - - for (let i = 0; i < loopNum; i++) { - const rotationMatrix = mesh.userData.rotationSpeeds[i]; - const id = ids[i]; - - mesh.getMatrixAt(id, matrix); - matrix.multiply(rotationMatrix); - mesh.setMatrixAt(id, matrix); - } - } -} - -// - -function sortFunction(list, camera) { - // initialize options - this._options = this._options || { - get: el => el.z, - aux: new Array(this.maxInstanceCount), - }; - - const options = this._options; - options.reversed = this.material.transparent; - - // convert depth to unsigned 32 bit range - const factor = (2 ** 32 - 1) / camera.far; // UINT32_MAX / max_depth - for (let i = 0, l = list.length; i < l; i++) { - list[i].z *= factor; - } - - // perform a fast-sort using the hybrid radix sort function - radixSort(list, options); -} diff --git a/examples-testing/examples/webgpu_mirror.ts b/examples-testing/examples/webgpu_mirror.ts deleted file mode 100644 index 56b226193..000000000 --- a/examples-testing/examples/webgpu_mirror.ts +++ /dev/null @@ -1,194 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { reflector, uv, texture, color } from 'three/tsl'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer; - -let cameraControls; - -let sphereGroup, smallSphere; - -init(); - -function init() { - // scene - scene = new THREE.Scene(); - - // camera - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 500); - camera.position.set(0, 75, 160); - - // - - let geometry, material; - - // - - sphereGroup = new THREE.Object3D(); - scene.add(sphereGroup); - - geometry = new THREE.CylinderGeometry(0.1, 15 * Math.cos((Math.PI / 180) * 30), 0.1, 24, 1); - material = new THREE.MeshPhongMaterial({ color: 0xffffff, emissive: 0x8d8d8d }); - const sphereCap = new THREE.Mesh(geometry, material); - sphereCap.position.y = -15 * Math.sin((Math.PI / 180) * 30) - 0.05; - sphereCap.rotateX(-Math.PI); - - geometry = new THREE.SphereGeometry(15, 24, 24, Math.PI / 2, Math.PI * 2, 0, (Math.PI / 180) * 120); - const halfSphere = new THREE.Mesh(geometry, material); - halfSphere.add(sphereCap); - halfSphere.rotateX((-Math.PI / 180) * 135); - halfSphere.rotateZ((-Math.PI / 180) * 20); - halfSphere.position.y = 7.5 + 15 * Math.sin((Math.PI / 180) * 30); - - sphereGroup.add(halfSphere); - - geometry = new THREE.IcosahedronGeometry(5, 0); - material = new THREE.MeshPhongMaterial({ color: 0xffffff, emissive: 0x7b7b7b, flatShading: true }); - smallSphere = new THREE.Mesh(geometry, material); - scene.add(smallSphere); - - // textures - - const textureLoader = new THREE.TextureLoader(); - - const floorNormal = textureLoader.load('textures/floors/FloorsCheckerboard_S_Normal.jpg'); - floorNormal.wrapS = THREE.RepeatWrapping; - floorNormal.wrapT = THREE.RepeatWrapping; - - const decalDiffuse = textureLoader.load('textures/decal/decal-diffuse.png'); - decalDiffuse.colorSpace = THREE.SRGBColorSpace; - - const decalNormal = textureLoader.load('textures/decal/decal-normal.jpg'); - - // reflectors / mirrors - - const groundReflector = reflector().toInspector('Ground Reflector'); - const verticalReflector = reflector().toInspector('Vertical Reflector'); - - const groundNormalScale = -0.08; - const verticalNormalScale = 0.1; - - const groundUVOffset = texture(decalNormal).xy.mul(2).sub(1).mul(groundNormalScale); - const verticalUVOffset = texture(floorNormal, uv().mul(5)).xy.mul(2).sub(1).mul(verticalNormalScale); - - groundReflector.uvNode = groundReflector.uvNode.add(groundUVOffset); - verticalReflector.uvNode = verticalReflector.uvNode.add(verticalUVOffset); - - const groundNode = texture(decalDiffuse).a.mix(color(0xffffff), groundReflector); - const verticalNode = color(0x0000ff).mul(0.1).add(verticalReflector); - - // walls - - const planeGeo = new THREE.PlaneGeometry(100.1, 100.1); - - // - - const planeBottom = new THREE.Mesh( - planeGeo, - new THREE.MeshPhongNodeMaterial({ - colorNode: groundNode, - }), - ); - planeBottom.rotateX(-Math.PI / 2); - planeBottom.add(groundReflector.target); - scene.add(planeBottom); - - const planeBack = new THREE.Mesh( - planeGeo, - new THREE.MeshPhongNodeMaterial({ - colorNode: verticalNode, - }), - ); - planeBack.position.z = -50; - planeBack.position.y = 50; - planeBack.add(verticalReflector.target); - scene.add(planeBack); - - // - - const planeTop = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xffffff })); - planeTop.position.y = 100; - planeTop.rotateX(Math.PI / 2); - scene.add(planeTop); - - const planeFront = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0x7f7fff })); - planeFront.position.z = 50; - planeFront.position.y = 50; - planeFront.rotateY(Math.PI); - scene.add(planeFront); - - const planeRight = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0x00ff00 })); - planeRight.position.x = 50; - planeRight.position.y = 50; - planeRight.rotateY(-Math.PI / 2); - scene.add(planeRight); - - const planeLeft = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xff0000 })); - planeLeft.position.x = -50; - planeLeft.position.y = 50; - planeLeft.rotateY(Math.PI / 2); - scene.add(planeLeft); - - // lights - - const mainLight = new THREE.PointLight(0xe7e7e7, 2.5, 250, 0); - mainLight.position.y = 60; - scene.add(mainLight); - - const greenLight = new THREE.PointLight(0x00ff00, 0.5, 1000, 0); - greenLight.position.set(550, 50, 0); - scene.add(greenLight); - - const redLight = new THREE.PointLight(0xff0000, 0.5, 1000, 0); - redLight.position.set(-550, 50, 0); - scene.add(redLight); - - const blueLight = new THREE.PointLight(0xbbbbfe, 0.5, 1000, 0); - blueLight.position.set(0, 50, 550); - scene.add(blueLight); - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // controls - - cameraControls = new OrbitControls(camera, renderer.domElement); - cameraControls.target.set(0, 40, 0); - cameraControls.maxDistance = 400; - cameraControls.minDistance = 10; - cameraControls.update(); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - const timer = Date.now() * 0.01; - - sphereGroup.rotation.y -= 0.002; - - smallSphere.position.set( - Math.cos(timer * 0.1) * 30, - Math.abs(Math.cos(timer * 0.2)) * 20 + 5, - Math.sin(timer * 0.1) * 30, - ); - smallSphere.rotation.y = Math.PI / 2 - timer * 0.1; - smallSphere.rotation.z = timer * 0.8; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_modifier_curve.ts b/examples-testing/examples/webgpu_modifier_curve.ts deleted file mode 100644 index c465ddc57..000000000 --- a/examples-testing/examples/webgpu_modifier_curve.ts +++ /dev/null @@ -1,154 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { TransformControls } from 'three/addons/controls/TransformControls.js'; - -import { Flow } from 'three/addons/modifiers/CurveModifierGPU.js'; -import { FontLoader } from 'three/addons/loaders/FontLoader.js'; -import { TextGeometry } from 'three/addons/geometries/TextGeometry.js'; - -const ACTION_SELECT = 1, - ACTION_NONE = 0; -const curveHandles = []; -const mouse = new THREE.Vector2(); - -let scene, - camera, - renderer, - rayCaster, - control, - flow, - action = ACTION_NONE; - -init(); - -function init() { - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(2, 2, 4); - camera.lookAt(scene.position); - - const initialPoints = [ - { x: 1, y: 0, z: -1 }, - { x: 1, y: 0, z: 1 }, - { x: -1, y: 0, z: 1 }, - { x: -1, y: 0, z: -1 }, - ]; - - const boxGeometry = new THREE.BoxGeometry(0.1, 0.1, 0.1); - const boxMaterial = new THREE.MeshBasicNodeMaterial(); - - for (const handlePos of initialPoints) { - const handle = new THREE.Mesh(boxGeometry, boxMaterial); - handle.position.copy(handlePos); - curveHandles.push(handle); - scene.add(handle); - } - - const curve = new THREE.CatmullRomCurve3(curveHandles.map(handle => handle.position)); - curve.curveType = 'centripetal'; - curve.closed = true; - - const points = curve.getPoints(50); - const line = new THREE.Line( - new THREE.BufferGeometry().setFromPoints(points), - new THREE.LineBasicMaterial({ color: 0x00ff00 }), - ); - - scene.add(line); - - // - - const light = new THREE.DirectionalLight(0xffaa33, 3); - light.position.set(-10, 10, 10); - scene.add(light); - - const light2 = new THREE.AmbientLight(0x003973, 3); - scene.add(light2); - - // - - const loader = new FontLoader(); - loader.load('fonts/helvetiker_regular.typeface.json', function (font) { - const geometry = new TextGeometry('Hello three.js!', { - font: font, - size: 0.2, - depth: 0.05, - curveSegments: 12, - bevelEnabled: true, - bevelThickness: 0.02, - bevelSize: 0.01, - bevelOffset: 0, - bevelSegments: 5, - }); - - geometry.rotateX(Math.PI); - - const material = new THREE.MeshStandardNodeMaterial({ - color: 0x99ffff, - }); - - const objectToCurve = new THREE.Mesh(geometry, material); - - flow = new Flow(objectToCurve); - flow.updateCurve(0, curve); - scene.add(flow.object3D); - }); - - // - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - renderer.domElement.addEventListener('pointerdown', onPointerDown); - - rayCaster = new THREE.Raycaster(); - control = new TransformControls(camera, renderer.domElement); - control.addEventListener('dragging-changed', function (event) { - if (!event.value) { - const points = curve.getPoints(50); - line.geometry.setFromPoints(points); - flow.updateCurve(0, curve); - } - }); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function onPointerDown(event) { - action = ACTION_SELECT; - mouse.x = (event.clientX / window.innerWidth) * 2 - 1; - mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; -} - -function animate() { - if (action === ACTION_SELECT) { - rayCaster.setFromCamera(mouse, camera); - action = ACTION_NONE; - const intersects = rayCaster.intersectObjects(curveHandles, false); - if (intersects.length) { - const target = intersects[0].object; - control.attach(target); - scene.add(control.getHelper()); - } - } - - if (flow) { - flow.moveAlongCurve(0.001); - } - - render(); -} - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_morphtargets.ts b/examples-testing/examples/webgpu_morphtargets.ts deleted file mode 100644 index cde3ae83b..000000000 --- a/examples-testing/examples/webgpu_morphtargets.ts +++ /dev/null @@ -1,119 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let container, camera, scene, renderer, mesh; - -init(); - -function init() { - container = document.getElementById('container'); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x8fbcd4); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 20); - camera.position.z = 10; - scene.add(camera); - - scene.add(new THREE.AmbientLight(0x8fbcd4, 1.5)); - - const pointLight = new THREE.PointLight(0xffffff, 200); - camera.add(pointLight); - - const geometry = createGeometry(); - - const material = new THREE.MeshPhongMaterial({ - color: 0xff0000, - flatShading: true, - }); - - mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.inspector = new Inspector(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(function () { - renderer.render(scene, camera); - }); - container.appendChild(renderer.domElement); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.enableZoom = false; - - window.addEventListener('resize', onWindowResize); - - initGUI(); -} - -function createGeometry() { - const geometry = new THREE.BoxGeometry(2, 2, 2, 32, 32, 32); - - // create an empty array to hold targets for the attribute we want to morph - // morphing positions and normals is supported - geometry.morphAttributes.position = []; - - // the original positions of the cube's vertices - const positionAttribute = geometry.attributes.position; - - // for the first morph target we'll move the cube's vertices onto the surface of a sphere - const spherePositions = []; - - // for the second morph target, we'll twist the cubes vertices - const twistPositions = []; - const direction = new THREE.Vector3(1, 0, 0); - const vertex = new THREE.Vector3(); - - for (let i = 0; i < positionAttribute.count; i++) { - const x = positionAttribute.getX(i); - const y = positionAttribute.getY(i); - const z = positionAttribute.getZ(i); - - spherePositions.push( - x * Math.sqrt(1 - (y * y) / 2 - (z * z) / 2 + (y * y * z * z) / 3), - y * Math.sqrt(1 - (z * z) / 2 - (x * x) / 2 + (z * z * x * x) / 3), - z * Math.sqrt(1 - (x * x) / 2 - (y * y) / 2 + (x * x * y * y) / 3), - ); - - // stretch along the x-axis so we can see the twist better - vertex.set(x * 2, y, z); - - vertex.applyAxisAngle(direction, (Math.PI * x) / 2).toArray(twistPositions, twistPositions.length); - } - - // add the spherical positions as the first morph target - geometry.morphAttributes.position[0] = new THREE.Float32BufferAttribute(spherePositions, 3); - - // add the twisted positions as the second morph target - geometry.morphAttributes.position[1] = new THREE.Float32BufferAttribute(twistPositions, 3); - - return geometry; -} - -function initGUI() { - // Set up dat.GUI to control targets - const params = { - Spherify: 0, - Twist: 0, - }; - - const gui = renderer.inspector.createParameters('Morph Targets'); - - gui.add(params, 'Spherify', 0, 1, 0.01).onChange(function (value) { - mesh.morphTargetInfluences[0] = value; - }); - gui.add(params, 'Twist', 0, 1, 0.01).onChange(function (value) { - mesh.morphTargetInfluences[1] = value; - }); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} diff --git a/examples-testing/examples/webgpu_morphtargets_face.ts b/examples-testing/examples/webgpu_morphtargets_face.ts deleted file mode 100644 index 91120dfb5..000000000 --- a/examples-testing/examples/webgpu_morphtargets_face.ts +++ /dev/null @@ -1,98 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js'; -import { MeshoptDecoder } from 'three/addons/libs/meshopt_decoder.module.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -init(); - -async function init() { - let mixer; - - const timer = new THREE.Timer(); - timer.connect(document); - - const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 20); - camera.position.set(-1.8, 0.8, 3); - - const scene = new THREE.Scene(); - - const renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - await renderer.init(); - - const environment = new RoomEnvironment(); - const pmremGenerator = new THREE.PMREMGenerator(renderer); - - scene.background = new THREE.Color(0x666666); - scene.environment = pmremGenerator.fromScene(environment, 0.04).texture; - - const ktx2Loader = await new KTX2Loader().setTranscoderPath('jsm/libs/basis/').detectSupport(renderer); - - new GLTFLoader() - .setKTX2Loader(ktx2Loader) - .setMeshoptDecoder(MeshoptDecoder) - .load('models/gltf/facecap.glb', gltf => { - const mesh = gltf.scene.children[0]; - - scene.add(mesh); - - mixer = new THREE.AnimationMixer(mesh); - - mixer.clipAction(gltf.animations[0]).play(); - - // GUI - - const head = mesh.getObjectByName('mesh_2'); - const influences = head.morphTargetInfluences; - - const gui = renderer.inspector.createParameters('Morph Targets'); - - for (const [key, value] of Object.entries(head.morphTargetDictionary)) { - gui.add(influences, value, 0, 1, 0.01).name(key.replace('blendShape1.', '')).listen(); - } - }); - - scene.background = new THREE.Color(0x666666); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - controls.minDistance = 2.5; - controls.maxDistance = 5; - controls.minAzimuthAngle = -Math.PI / 2; - controls.maxAzimuthAngle = Math.PI / 2; - controls.maxPolarAngle = Math.PI / 1.8; - controls.target.set(0, 0.15, -0.2); - - function animate() { - timer.update(); - - const delta = timer.getDelta(); - - if (mixer) { - mixer.update(delta); - } - - renderer.render(scene, camera); - - controls.update(); - } - - window.addEventListener('resize', () => { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - }); -} diff --git a/examples-testing/examples/webgpu_mrt.ts b/examples-testing/examples/webgpu_mrt.ts deleted file mode 100644 index 2b41adc9c..000000000 --- a/examples-testing/examples/webgpu_mrt.ts +++ /dev/null @@ -1,123 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { - output, - normalView, - pass, - step, - diffuseColor, - emissive, - directionToColor, - screenUV, - mix, - mrt, - Fn, -} from 'three/tsl'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -let camera, scene, renderer; -let renderPipeline; - -init(); - -function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - // scene - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); - camera.position.set(-1.8, 0.6, 2.7); - - scene = new THREE.Scene(); - - new UltraHDRLoader().setPath('textures/equirectangular/').load('royal_esplanade_2k.hdr.jpg', function (texture) { - texture.mapping = THREE.EquirectangularReflectionMapping; - - scene.background = texture; - scene.environment = texture; - - // model - - const loader = new GLTFLoader().setPath('models/gltf/DamagedHelmet/glTF/'); - loader.load('DamagedHelmet.gltf', function (gltf) { - scene.add(gltf.scene); - }); - }); - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: true, requiredLimits: { maxColorAttachments: 5 } }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(render); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.inspector = new Inspector(); - container.appendChild(renderer.domElement); - - // post processing - - const scenePass = pass(scene, camera, { minFilter: THREE.NearestFilter, magFilter: THREE.NearestFilter }); - scenePass.setMRT( - mrt({ - output: output, - normal: directionToColor(normalView), - diffuse: diffuseColor, - emissive: emissive, - }), - ); - - // optimize textures - - const normalTexture = scenePass.getTexture('normal'); - const diffuseTexture = scenePass.getTexture('diffuse'); - const emissiveTexture = scenePass.getTexture('emissive'); - - normalTexture.type = diffuseTexture.type = emissiveTexture.type = THREE.UnsignedByteType; - - // post processing - mrt - - renderPipeline = new THREE.RenderPipeline(renderer); - renderPipeline.outputColorTransform = false; - renderPipeline.outputNode = Fn(() => { - const output = scenePass.getTextureNode('output'); // output name is optional here - const normal = scenePass.getTextureNode('normal'); - const diffuse = scenePass.getTextureNode('diffuse'); - const emissive = scenePass.getTextureNode('emissive'); - - const out = mix(output.renderOutput(), output, step(0.2, screenUV.x)); - const nor = mix(out, normal, step(0.4, screenUV.x)); - const emi = mix(nor, emissive, step(0.6, screenUV.x)); - const dif = mix(emi, diffuse, step(0.8, screenUV.x)); - - return dif; - })(); - - // controls - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 2; - controls.maxDistance = 10; - controls.target.set(0, 0, -0.2); - controls.update(); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function render() { - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_mrt_mask.ts b/examples-testing/examples/webgpu_mrt_mask.ts deleted file mode 100644 index 2bb80b84e..000000000 --- a/examples-testing/examples/webgpu_mrt_mask.ts +++ /dev/null @@ -1,132 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { color, screenUV, mrt, output, pass, vec4 } from 'three/tsl'; -import { gaussianBlur } from 'three/addons/tsl/display/GaussianBlurNode.js'; - -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let camera, scene, renderer; -let renderPipeline; -let spheres, - rotate = true; -let mixer, timer; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.01, 100); - camera.position.set(1, 2, 3); - - scene = new THREE.Scene(); - scene.backgroundNode = screenUV.y.mix(color(0x66bbff), color(0x4466ff)).mul(0.05); - camera.lookAt(0, 1, 0); - - timer = new THREE.Timer(); - timer.connect(document); - - // lights - - const light = new THREE.SpotLight(0xffffff, 1); - light.power = 2000; - camera.add(light); - scene.add(camera); - - const loader = new GLTFLoader(); - loader.load('models/gltf/Michelle.glb', function (gltf) { - const object = gltf.scene; - mixer = new THREE.AnimationMixer(object); - - const material = object.children[0].children[0].material; - - // add glow effect - material.mrtNode = mrt({ mask: output.add(1) }); - - const action = mixer.clipAction(gltf.animations[0]); - action.play(); - - scene.add(object); - }); - - // spheres - - const geometry = new THREE.SphereGeometry(0.3, 32, 16); - - spheres = new THREE.Group(); - scene.add(spheres); - - function addSphere(color, mrtNode = null) { - const distance = 1; - const id = spheres.children.length; - const rotation = THREE.MathUtils.degToRad(id * 90); - - const material = new THREE.MeshStandardNodeMaterial({ color }); - material.mrtNode = mrtNode; - - const mesh = new THREE.Mesh(geometry, material); - mesh.position.set(Math.cos(rotation) * distance, 1, Math.sin(rotation) * distance); - - spheres.add(mesh); - } - - addSphere(0x0000ff, mrt({ mask: output })); - addSphere(0x00ff00); - addSphere(0xff0000); - addSphere(0x00ffff); - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.toneMapping = THREE.NeutralToneMapping; - renderer.toneMappingExposure = 0.4; - document.body.appendChild(renderer.domElement); - - // post processing - - const scenePass = pass(scene, camera); - scenePass.setMRT( - mrt({ - output: output.renderOutput(), - mask: vec4(0), // empty as default, custom materials can set this - }), - ); - - const colorPass = scenePass.getTextureNode(); - const maskPass = scenePass.getTextureNode('mask'); - - renderPipeline = new THREE.RenderPipeline(renderer); - renderPipeline.outputColorTransform = false; - renderPipeline.outputNode = colorPass.add(gaussianBlur(maskPass, 1, 20).mul(0.3)).renderOutput(); - - // controls - - const controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, 1, 0); - controls.addEventListener('start', () => (rotate = false)); - controls.addEventListener('end', () => (rotate = true)); - controls.update(); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - const delta = timer.getDelta(); - - if (mixer) mixer.update(delta); - - if (rotate) spheres.rotation.y += delta * 0.5; - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_multiple_canvas.ts b/examples-testing/examples/webgpu_multiple_canvas.ts deleted file mode 100644 index db55fe165..000000000 --- a/examples-testing/examples/webgpu_multiple_canvas.ts +++ /dev/null @@ -1,111 +0,0 @@ -import * as THREE from 'three'; -import { color } from 'three/tsl'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import WebGPU from 'three/addons/capabilities/WebGPU.js'; - -// - -if (WebGPU.isAvailable() === false) { - document.body.appendChild(WebGPU.getErrorMessage()); - - throw new Error('No WebGPU support'); -} - -// - -let renderer; - -const scenes = []; - -init(); - -function init() { - const geometries = [ - new THREE.BoxGeometry(1, 1, 1), - new THREE.SphereGeometry(0.5, 12, 8), - new THREE.DodecahedronGeometry(0.5), - new THREE.CylinderGeometry(0.5, 0.5, 1, 12), - ]; - - const content = document.getElementById('content'); - - for (let i = 0; i < 40; i++) { - const scene = new THREE.Scene(); - scene.backgroundNode = color(0xeeeeee); - - // make a list item - const element = document.createElement('div'); - element.className = 'list-item'; - - const sceneCanvas = document.createElement('canvas'); - element.appendChild(sceneCanvas); - - const descriptionElement = document.createElement('div'); - descriptionElement.innerText = 'Scene ' + (i + 1); - element.appendChild(descriptionElement); - - const canvasTarget = new THREE.CanvasTarget(sceneCanvas); - canvasTarget.setPixelRatio(window.devicePixelRatio); - canvasTarget.setSize(200, 200); - - // the element that represents the area we want to render the scene - scene.userData.canvasTarget = canvasTarget; - content.appendChild(element); - - const camera = new THREE.PerspectiveCamera(50, 1, 1, 10); - camera.position.z = 2; - scene.userData.camera = camera; - - const controls = new OrbitControls(scene.userData.camera, scene.userData.canvasTarget.domElement); - controls.minDistance = 2; - controls.maxDistance = 5; - controls.enablePan = false; - controls.enableZoom = false; - scene.userData.controls = controls; - - // add one random mesh to each scene - const geometry = geometries[(geometries.length * Math.random()) | 0]; - - const material = new THREE.MeshStandardMaterial({ - color: new THREE.Color().setHSL(Math.random(), 1, 0.75, THREE.SRGBColorSpace), - roughness: 0.5, - metalness: 0, - flatShading: true, - }); - - scene.add(new THREE.Mesh(geometry, material)); - - scene.add(new THREE.HemisphereLight(0xaaaaaa, 0x444444, 3)); - - const light = new THREE.DirectionalLight(0xffffff, 1.5); - light.position.set(1, 1, 1); - scene.add(light); - - scenes.push(scene); - } - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setClearColor(0xffffff, 1); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setAnimationLoop(animate); -} - -function animate() { - scenes.forEach(function (scene) { - // so something moves - //scene.children[ 0 ].rotation.y = Date.now() * 0.001; - - // get the canvas and camera for this scene - const { canvasTarget, camera } = scene.userData; - - //camera.aspect = width / height; // not changing in this example - //camera.updateProjectionMatrix(); - - //scene.userData.controls.update(); - - renderer.setCanvasTarget(canvasTarget); - renderer.render(scene, camera); - }); -} diff --git a/examples-testing/examples/webgpu_multiple_elements.ts b/examples-testing/examples/webgpu_multiple_elements.ts deleted file mode 100644 index 65bd3e75d..000000000 --- a/examples-testing/examples/webgpu_multiple_elements.ts +++ /dev/null @@ -1,142 +0,0 @@ -import * as THREE from 'three'; -import { color } from 'three/tsl'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let canvas, renderer; - -const scenes = []; - -init(); - -function init() { - canvas = document.getElementById('c'); - - const geometries = [ - new THREE.BoxGeometry(1, 1, 1), - new THREE.SphereGeometry(0.5, 12, 8), - new THREE.DodecahedronGeometry(0.5), - new THREE.CylinderGeometry(0.5, 0.5, 1, 12), - ]; - - const content = document.getElementById('content'); - - for (let i = 0; i < 40; i++) { - const scene = new THREE.Scene(); - scene.backgroundNode = color(0xeeeeee); - - // make a list item - const element = document.createElement('div'); - element.className = 'list-item'; - - const sceneElement = document.createElement('div'); - element.appendChild(sceneElement); - - const descriptionElement = document.createElement('div'); - descriptionElement.innerText = 'Scene ' + (i + 1); - element.appendChild(descriptionElement); - - // the element that represents the area we want to render the scene - scene.userData.element = sceneElement; - content.appendChild(element); - - const camera = new THREE.PerspectiveCamera(50, 1, 1, 10); - camera.position.z = 2; - scene.userData.camera = camera; - - const controls = new OrbitControls(scene.userData.camera, scene.userData.element); - controls.minDistance = 2; - controls.maxDistance = 5; - controls.enablePan = false; - controls.enableZoom = false; - scene.userData.controls = controls; - - // add one random mesh to each scene - const geometry = geometries[(geometries.length * Math.random()) | 0]; - - const material = new THREE.MeshStandardMaterial({ - color: new THREE.Color().setHSL(Math.random(), 1, 0.75, THREE.SRGBColorSpace), - roughness: 0.5, - metalness: 0, - flatShading: true, - }); - - scene.add(new THREE.Mesh(geometry, material)); - - scene.add(new THREE.HemisphereLight(0xaaaaaa, 0x444444, 3)); - - const light = new THREE.DirectionalLight(0xffffff, 1.5); - light.position.set(1, 1, 1); - scene.add(light); - - scenes.push(scene); - } - - renderer = new THREE.WebGPURenderer({ canvas: canvas, antialias: true }); - renderer.setClearColor(0xffffff, 1); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setAnimationLoop(animate); -} - -function updateSize() { - const width = canvas.clientWidth; - const height = canvas.clientHeight; - - if (canvas.width !== width || canvas.height !== height) { - renderer.setSize(width, height, false); - } -} - -function animate() { - updateSize(); - - canvas.style.transform = `translateY(${window.scrollY}px)`; - - renderer.setClearColor(0xffffff); - renderer.setScissorTest(false); - renderer.setViewport(0, 0, canvas.width, canvas.height); - renderer.clear(); - - //renderer.setClearColor( 0xe0e0e0 ); - renderer.setScissorTest(true); - - scenes.forEach(function (scene) { - // so something moves - scene.children[0].rotation.y = Date.now() * 0.001; - - // get the element that is a place holder for where we want to - // draw the scene - const element = scene.userData.element; - - // get its position relative to the page's viewport - const rect = element.getBoundingClientRect(); - - // check if it's offscreen. If so skip it - if ( - rect.bottom < 0 || - rect.top > renderer.domElement.clientHeight || - rect.right < 0 || - rect.left > renderer.domElement.clientWidth - ) { - return; // it's off screen - } - - // set the viewport - const width = rect.right - rect.left; - const height = rect.bottom - rect.top; - const left = rect.left; - const top = rect.top; - - renderer.setViewport(left, top, width, height); - renderer.setScissor(left, top, width, height); - - const camera = scene.userData.camera; - - //camera.aspect = width / height; // not changing in this example - //camera.updateProjectionMatrix(); - - //scene.userData.controls.update(); - - renderer.render(scene, camera); - }); -} diff --git a/examples-testing/examples/webgpu_multiple_rendertargets.ts b/examples-testing/examples/webgpu_multiple_rendertargets.ts deleted file mode 100644 index 2085d3c63..000000000 --- a/examples-testing/examples/webgpu_multiple_rendertargets.ts +++ /dev/null @@ -1,97 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { mix, vec2, step, texture, uv, screenUV, normalWorld, output, mrt } from 'three/tsl'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let camera, scene, renderer, torus; -let renderPipeline, renderTarget; - -init(); - -function init() { - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(render); - document.body.appendChild(renderer.domElement); - - // Create a multi render target with Float buffers - - renderTarget = new THREE.RenderTarget( - window.innerWidth * window.devicePixelRatio, - window.innerHeight * window.devicePixelRatio, - { count: 2, minFilter: THREE.NearestFilter, magFilter: THREE.NearestFilter }, - ); - - // Name our G-Buffer attachments for debugging - - renderTarget.textures[0].name = 'output'; - renderTarget.textures[1].name = 'normal'; - - // Scene - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x222222); - - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 50); - camera.position.z = 4; - - const loader = new THREE.TextureLoader(); - - const diffuse = loader.load('textures/hardwood2_diffuse.jpg'); - diffuse.colorSpace = THREE.SRGBColorSpace; - diffuse.wrapS = THREE.RepeatWrapping; - diffuse.wrapT = THREE.RepeatWrapping; - - const torusMaterial = new THREE.NodeMaterial(); - torusMaterial.colorNode = texture(diffuse, uv().mul(vec2(10, 4))); - - torus = new THREE.Mesh(new THREE.TorusKnotGeometry(1, 0.3, 128, 32), torusMaterial); - scene.add(torus); - - // MRT - - renderer.setMRT( - mrt({ - output: output, - normal: normalWorld, - }), - ); - - // Post Processing - - renderPipeline = new THREE.RenderPipeline(renderer); - renderPipeline.outputNode = mix( - texture(renderTarget.textures[0]), - texture(renderTarget.textures[1]), - step(0.5, screenUV.x), - ); - - // Controls - - new OrbitControls(camera, renderer.domElement); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - const dpr = renderer.getPixelRatio(); - renderTarget.setSize(window.innerWidth * dpr, window.innerHeight * dpr); -} - -function render(time) { - torus.rotation.y = (time / 1000) * 0.4; - - // render scene into target - renderer.setRenderTarget(renderTarget); - renderer.render(scene, camera); - - // render post FX - renderer.setRenderTarget(null); - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_multiple_rendertargets_readback.ts b/examples-testing/examples/webgpu_multiple_rendertargets_readback.ts deleted file mode 100644 index 2232ab182..000000000 --- a/examples-testing/examples/webgpu_multiple_rendertargets_readback.ts +++ /dev/null @@ -1,161 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { mix, step, texture, screenUV, mrt, output, normalWorld, uv, vec2 } from 'three/tsl'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer, torus; -let quadMesh, sceneMRT, renderTarget, readbackTarget, material, readbackMaterial, pixelBuffer, pixelBufferTexture; - -const options = { - selection: 'mrt', -}; - -init(); - -function init() { - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(render); - document.body.appendChild(renderer.domElement); - - // - - renderer.inspector = new Inspector(); - - // - - const gui = renderer.inspector.createParameters('Settings'); - gui.add(options, 'selection', ['mrt', 'diffuse', 'normal']); - - // Create a multi render target with Float buffers - - renderTarget = new THREE.RenderTarget( - window.innerWidth * window.devicePixelRatio, - window.innerHeight * window.devicePixelRatio, - { count: 2, minFilter: THREE.NearestFilter, magFilter: THREE.NearestFilter }, - ); - - // Name our G-Buffer attachments for debugging - - renderTarget.textures[0].name = 'output'; - renderTarget.textures[1].name = 'normal'; - - // Init readback render target, readback data texture, readback material - // Be careful with the size! 512 is already big. Reading data back from the GPU is computationally intensive - - const size = 512; - - readbackTarget = new THREE.RenderTarget(size, size, { count: 2 }); - - pixelBuffer = new Uint8Array(size ** 2 * 4).fill(0); - pixelBufferTexture = new THREE.DataTexture(pixelBuffer, size, size); - pixelBufferTexture.type = THREE.UnsignedByteType; - pixelBufferTexture.format = THREE.RGBAFormat; - - readbackMaterial = new THREE.MeshBasicNodeMaterial(); - readbackMaterial.colorNode = texture(pixelBufferTexture); - - // MRT - - sceneMRT = mrt({ - output: output, - normal: normalWorld, - }); - - // Scene - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x222222); - - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 50); - camera.position.z = 4; - - const loader = new THREE.TextureLoader(); - - const diffuse = loader.load('textures/hardwood2_diffuse.jpg'); - diffuse.colorSpace = THREE.SRGBColorSpace; - diffuse.wrapS = THREE.RepeatWrapping; - diffuse.wrapT = THREE.RepeatWrapping; - - const torusMaterial = new THREE.NodeMaterial(); - torusMaterial.colorNode = texture(diffuse, uv().mul(vec2(10, 4))); - - torus = new THREE.Mesh(new THREE.TorusKnotGeometry(1, 0.3, 128, 32), torusMaterial); - scene.add(torus); - - // Output - - material = new THREE.NodeMaterial(); - material.colorNode = mix( - texture(renderTarget.textures[0]), - texture(renderTarget.textures[1]), - step(0.5, screenUV.x), - ); - - quadMesh = new THREE.QuadMesh(material); - - // Controls - - new OrbitControls(camera, renderer.domElement); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - - const dpr = renderer.getPixelRatio(); - renderTarget.setSize(window.innerWidth * dpr, window.innerHeight * dpr); -} - -async function render(time) { - const selection = options.selection; - - torus.rotation.y = (time / 1000) * 0.4; - - const isReadback = selection !== 'mrt'; - - // render scene into target - renderer.setMRT(sceneMRT); - renderer.setRenderTarget(isReadback ? readbackTarget : renderTarget); - renderer.render(scene, camera); - - // render post FX - renderer.setMRT(null); - renderer.setRenderTarget(null); - - if (isReadback) { - quadMesh.material = readbackMaterial; - - await readback(); - } else { - quadMesh.material = material; - } - - quadMesh.render(renderer); -} - -async function readback() { - const width = readbackTarget.width; - const height = readbackTarget.height; - - const selection = options.selection; - - if (selection === 'diffuse') { - pixelBuffer = await renderer.readRenderTargetPixelsAsync(readbackTarget, 0, 0, width, height, 0); // zero is optional - - pixelBufferTexture.image.data = pixelBuffer; - pixelBufferTexture.needsUpdate = true; - } else if (selection === 'normal') { - pixelBuffer = await renderer.readRenderTargetPixelsAsync(readbackTarget, 0, 0, width, height, 1); - - pixelBufferTexture.image.data = pixelBuffer; - pixelBufferTexture.needsUpdate = true; - } -} diff --git a/examples-testing/examples/webgpu_multisampled_renderbuffers.ts b/examples-testing/examples/webgpu_multisampled_renderbuffers.ts deleted file mode 100644 index 05098c8d4..000000000 --- a/examples-testing/examples/webgpu_multisampled_renderbuffers.ts +++ /dev/null @@ -1,129 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { texture } from 'three/tsl'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer; -const mouse = new THREE.Vector2(); - -let quadMesh, renderTarget; - -let box, box2; - -const dpr = 1; - -const params = { - animated: true, - samples: 4, -}; - -const mat4 = new THREE.Matrix4(); - -const count = 50; -const fullRadius = 20; // Radius of the sphere -const halfRadius = 10; // Radius of the sphere -const positions = new Array(count).fill().map((_, i) => { - const radius = i % 2 === 0 ? fullRadius : halfRadius; - - const phi = Math.acos(2 * Math.random() - 1) - Math.PI / 2; // phi: latitude, range -π/2 to π/2 - const theta = 2 * Math.PI * Math.random(); // theta: longitude, range 0 to 2π - - return new THREE.Vector3( - radius * Math.cos(phi) * Math.cos(theta), // x - radius * Math.sin(phi), // y - radius * Math.cos(phi) * Math.sin(theta), // z - ); -}); - -init(); -initGUI(); - -function initGUI() { - const gui = renderer.inspector.createParameters('Settings'); - gui.add(params, 'samples', 0, 4, 1); - gui.add(params, 'animated'); -} - -function init() { - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 50); - camera.position.z = 3; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x111111); - - // textured mesh - - const geometryBox = new THREE.BoxGeometry(7, 7, 7, 12, 12, 12); - const materialBox = new THREE.MeshBasicNodeMaterial(); - const materialBoxInner = new THREE.MeshBasicNodeMaterial({ color: 0xff0000 }); - - materialBox.wireframe = true; - - // - - box = new THREE.InstancedMesh(geometryBox, materialBox, count); - box2 = new THREE.InstancedMesh(geometryBox, materialBoxInner, count); - - for (let i = 0; i < count; i++) { - box.setMatrixAt(i, mat4.identity().setPosition(positions[i])); - box2.setMatrixAt(i, mat4.multiplyScalar(0.996).setPosition(positions[i])); - } - - scene.add(box, box2); - - // - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.inspector = new Inspector(); - renderer.setPixelRatio(dpr); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - renderTarget = new THREE.RenderTarget(window.innerWidth * dpr, window.innerHeight * dpr, { - samples: params.samples, - depthBuffer: true, - }); - - window.addEventListener('mousemove', onWindowMouseMove); - window.addEventListener('resize', onWindowResize); - - // FX - - // modulate the final color based on the mouse position - - const materialFX = new THREE.MeshBasicNodeMaterial(); - materialFX.colorNode = texture(renderTarget.texture).rgb; - - quadMesh = new THREE.QuadMesh(materialFX); -} - -function onWindowMouseMove(e) { - mouse.x = e.offsetX / window.innerWidth; - mouse.y = e.offsetY / window.innerHeight; -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - renderTarget.setSize(window.innerWidth * dpr, window.innerHeight * dpr); -} - -function animate() { - if (params.animated) { - box.rotation.x += 0.001; - box.rotation.y += 0.002; - box2.rotation.x += 0.001; - box2.rotation.y += 0.002; - } - - renderTarget.samples = params.samples; - - renderer.setRenderTarget(renderTarget); - renderer.render(scene, camera); - - renderer.setRenderTarget(null); - quadMesh.render(renderer); -} diff --git a/examples-testing/examples/webgpu_occlusion.ts b/examples-testing/examples/webgpu_occlusion.ts deleted file mode 100644 index 376dc987d..000000000 --- a/examples-testing/examples/webgpu_occlusion.ts +++ /dev/null @@ -1,97 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { uniform } from 'three/tsl'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let camera, scene, renderer, controls; - -class OcclusionNode extends THREE.Node { - constructor(testObject, normalColor, occludedColor) { - super('vec3'); - - this.updateType = THREE.NodeUpdateType.OBJECT; - - this.uniformNode = uniform(new THREE.Color()); - - this.testObject = testObject; - this.normalColor = normalColor; - this.occludedColor = occludedColor; - } - - async update(frame) { - const isOccluded = frame.renderer.isOccluded(this.testObject); - - this.uniformNode.value.copy(isOccluded ? this.occludedColor : this.normalColor); - } - - setup(/* builder */) { - return this.uniformNode; - } -} - -init(); - -async function init() { - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.01, 100); - camera.position.z = 7; - - scene = new THREE.Scene(); - - // lights - - const ambientLight = new THREE.AmbientLight(0xb0b0b0); - - const light = new THREE.DirectionalLight(0xffffff, 1.0); - light.position.set(0.32, 0.39, 0.7); - - scene.add(ambientLight); - scene.add(light); - - // models - - const planeGeometry = new THREE.PlaneGeometry(2, 2); - const sphereGeometry = new THREE.SphereGeometry(0.5); - - const plane = new THREE.Mesh( - planeGeometry, - new THREE.MeshPhongNodeMaterial({ color: 0x00ff00, side: THREE.DoubleSide }), - ); - const sphere = new THREE.Mesh(sphereGeometry, new THREE.MeshPhongNodeMaterial({ color: 0xffff00 })); - - const instanceUniform = new OcclusionNode(sphere, new THREE.Color(0x0000ff), new THREE.Color(0x00ff00)); - - plane.material.colorNode = instanceUniform; - - sphere.position.z = -1; - sphere.occlusionTest = true; - - scene.add(plane); - scene.add(sphere); - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(render); - document.body.appendChild(renderer.domElement); - - // controls - - controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 3; - controls.maxDistance = 25; - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_ocean.ts b/examples-testing/examples/webgpu_ocean.ts deleted file mode 100644 index a7939461e..000000000 --- a/examples-testing/examples/webgpu_ocean.ts +++ /dev/null @@ -1,181 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { pass } from 'three/tsl'; -import { bloom } from 'three/addons/tsl/display/BloomNode.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { WaterMesh } from 'three/addons/objects/WaterMesh.js'; -import { SkyMesh } from 'three/addons/objects/SkyMesh.js'; - -let container; -let camera, scene, renderer, renderPipeline; -let controls, water, sun, sky, mesh, bloomPass; - -init(); - -function init() { - container = document.getElementById('container'); - - // - - renderer = new THREE.WebGPURenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(render); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = 0.1; - renderer.inspector = new Inspector(); - container.appendChild(renderer.domElement); - - // - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight, 1, 20000); - camera.position.set(30, 30, 100); - - // Post-processing - - renderPipeline = new THREE.RenderPipeline(renderer); - - const scenePass = pass(scene, camera); - const scenePassColor = scenePass.getTextureNode('output'); - - bloomPass = bloom(scenePassColor); - bloomPass.threshold.value = 0; - bloomPass.strength.value = 0.1; - bloomPass.radius.value = 0; - - renderPipeline.outputNode = scenePassColor.add(bloomPass); - - // - - sun = new THREE.Vector3(); - - // Water - - const waterGeometry = new THREE.PlaneGeometry(10000, 10000); - const loader = new THREE.TextureLoader(); - const waterNormals = loader.load('textures/waternormals.jpg'); - waterNormals.wrapS = waterNormals.wrapT = THREE.RepeatWrapping; - - water = new WaterMesh(waterGeometry, { - waterNormals: waterNormals, - sunDirection: new THREE.Vector3(), - sunColor: 0xffffff, - waterColor: 0x001e0f, - distortionScale: 3.7, - }); - - water.rotation.x = -Math.PI / 2; - - scene.add(water); - - // Skybox - - sky = new SkyMesh(); - sky.scale.setScalar(10000); - scene.add(sky); - - sky.turbidity.value = 10; - sky.rayleigh.value = 2; - sky.mieCoefficient.value = 0.005; - sky.mieDirectionalG.value = 0.8; - sky.cloudCoverage.value = 0.4; - sky.cloudDensity.value = 0.5; - sky.cloudElevation.value = 0.5; - - const parameters = { - elevation: 2, - azimuth: 180, - exposure: 0.1, - }; - - const pmremGenerator = new THREE.PMREMGenerator(renderer); - const sceneEnv = new THREE.Scene(); - - let renderTarget; - - function updateSun() { - const phi = THREE.MathUtils.degToRad(90 - parameters.elevation); - const theta = THREE.MathUtils.degToRad(parameters.azimuth); - - sun.setFromSphericalCoords(1, phi, theta); - - sky.sunPosition.value.copy(sun); - water.sunDirection.value.copy(sun).normalize(); - - if (renderTarget !== undefined) renderTarget.dispose(); - - sceneEnv.add(sky); - renderTarget = pmremGenerator.fromScene(sceneEnv); - scene.add(sky); - - scene.environment = renderTarget.texture; - } - - renderer.init().then(updateSun); - - // - - const geometry = new THREE.BoxGeometry(30, 30, 30); - const material = new THREE.MeshStandardMaterial({ roughness: 0 }); - - mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - // - - controls = new OrbitControls(camera, renderer.domElement); - controls.maxPolarAngle = Math.PI * 0.495; - controls.target.set(0, 10, 0); - controls.minDistance = 40.0; - controls.maxDistance = 200.0; - controls.update(); - - // GUI - - const gui = renderer.inspector.createParameters('Settings'); - - const folderSky = gui.addFolder('Sky'); - folderSky.add(parameters, 'elevation', 0, 90, 0.1).onChange(updateSun); - folderSky.add(parameters, 'azimuth', -180, 180, 0.1).onChange(updateSun); - folderSky.add(parameters, 'exposure', 0, 1, 0.0001).onChange(function (value) { - renderer.toneMappingExposure = value; - }); - - const folderWater = gui.addFolder('Water'); - folderWater.add(water.distortionScale, 'value', 0, 8, 0.1).name('distortionScale'); - folderWater.add(water.size, 'value', 0.1, 10, 0.1).name('size'); - - const folderBloom = gui.addFolder('Bloom'); - folderBloom.add(bloomPass.strength, 'value', 0, 3, 0.01).name('strength'); - folderBloom.add(bloomPass.radius, 'value', 0, 1, 0.01).name('radius'); - - const folderClouds = gui.addFolder('Clouds'); - folderClouds.add(sky.cloudCoverage, 'value', 0, 1, 0.01).name('coverage'); - folderClouds.add(sky.cloudDensity, 'value', 0, 1, 0.01).name('density'); - folderClouds.add(sky.cloudElevation, 'value', 0, 1, 0.01).name('elevation'); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function render() { - const time = performance.now() * 0.001; - - mesh.position.y = Math.sin(time) * 20 + 5; - mesh.rotation.x = time * 0.5; - mesh.rotation.z = time * 0.51; - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_parallax_uv.ts b/examples-testing/examples/webgpu_parallax_uv.ts deleted file mode 100644 index b6282d89f..000000000 --- a/examples-testing/examples/webgpu_parallax_uv.ts +++ /dev/null @@ -1,135 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { texture, parallaxUV, blendOverlay, uv, normalMap, uniform } from 'three/tsl'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer; - -let controls; - -init(); - -async function init() { - // scene - - scene = new THREE.Scene(); - - // camera - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(15, 7, 15); - - // environment - - const environmentTexture = await new HDRLoader().loadAsync('./textures/equirectangular/752-hdri-skies-com_1k.hdr'); - - environmentTexture.mapping = THREE.EquirectangularReflectionMapping; - - scene.environment = environmentTexture; - scene.background = environmentTexture; - scene.backgroundBlurriness = 0.4; - - // textures - - const loader = new THREE.TextureLoader(); - - const topTexture = await loader.loadAsync('textures/ambientcg/Ice002_1K-JPG_Color.jpg'); - topTexture.colorSpace = THREE.SRGBColorSpace; - topTexture.wrapS = THREE.RepeatWrapping; - topTexture.wrapT = THREE.RepeatWrapping; - - const roughnessTexture = await loader.loadAsync('textures/ambientcg/Ice002_1K-JPG_Roughness.jpg'); - roughnessTexture.colorSpace = THREE.NoColorSpace; - roughnessTexture.wrapS = THREE.RepeatWrapping; - roughnessTexture.wrapT = THREE.RepeatWrapping; - - const normalTexture = await loader.loadAsync('textures/ambientcg/Ice002_1K-JPG_NormalGL.jpg'); - normalTexture.colorSpace = THREE.NoColorSpace; - normalTexture.wrapS = THREE.RepeatWrapping; - normalTexture.wrapT = THREE.RepeatWrapping; - - const displaceTexture = await loader.loadAsync('textures/ambientcg/Ice002_1K-JPG_Displacement.jpg'); - displaceTexture.colorSpace = THREE.NoColorSpace; - displaceTexture.wrapS = THREE.RepeatWrapping; - displaceTexture.wrapT = THREE.RepeatWrapping; - - // - - const bottomTexture = await loader.loadAsync('textures/ambientcg/Ice003_1K-JPG_Color.jpg'); - bottomTexture.colorSpace = THREE.SRGBColorSpace; - bottomTexture.wrapS = THREE.RepeatWrapping; - bottomTexture.wrapT = THREE.RepeatWrapping; - - // parallax effect - - const scaleUV = uniform(3); - const scaledUV = uv().mul(scaleUV); - - const parallaxScale = uniform(0.5); // parallax scale - const offsetUV = texture(displaceTexture, scaledUV).mul(parallaxScale); - - const parallaxUVOffset = parallaxUV(scaledUV, offsetUV); - const parallaxResult = texture(bottomTexture, parallaxUVOffset); - - const iceNode = blendOverlay(texture(topTexture, scaledUV), parallaxResult); - - // material - - const material = new THREE.MeshStandardNodeMaterial(); - material.colorNode = iceNode.mul(5); // increase the color intensity to 5 ( contrast ) - material.roughnessNode = texture(roughnessTexture, scaledUV); - material.normalNode = normalMap(texture(normalTexture, scaledUV)); - material.metalness = 0; - - const geometry = new THREE.CircleGeometry(25, 64); - - const ground = new THREE.Mesh(geometry, material); - ground.rotateX(-Math.PI / 2); - scene.add(ground); - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.toneMapping = THREE.ReinhardToneMapping; - renderer.toneMappingExposure = 6; - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // gui - - const gui = renderer.inspector.createParameters('Scene'); - gui.add(scene, 'backgroundBlurriness', 0, 1).name('Background Blurriness'); - gui.add(parallaxScale, 'value', 0.2, 0.5).name('Parallax Scale'); - gui.add(scaleUV, 'value', 1, 5).name('UV Scale'); - - // controls - - controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, 0, 0); - controls.maxDistance = 40; - controls.minDistance = 10; - controls.autoRotate = true; - controls.autoRotateSpeed = -1; - controls.update(); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - controls.update(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_particles.ts b/examples-testing/examples/webgpu_particles.ts deleted file mode 100644 index 9e81f0761..000000000 --- a/examples-testing/examples/webgpu_particles.ts +++ /dev/null @@ -1,151 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { range, texture, mix, uv, color, rotateUV, positionLocal, time, uniform } from 'three/tsl'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import WebGPU from 'three/addons/capabilities/WebGPU.js'; - -let camera, scene, renderer; -let controls; - -init(); - -function init() { - if (WebGPU.isAvailable() === false) { - document.body.appendChild(WebGPU.getErrorMessage()); - - throw new Error('No WebGPU support'); - } - - const { innerWidth, innerHeight } = window; - - camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 5000); - camera.position.set(1300, 500, 0); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x333333); - - // textures - - const textureLoader = new THREE.TextureLoader(); - const map = textureLoader.load('textures/opengameart/smoke1.png'); - - // create nodes - - const lifeRange = range(0.1, 1); - const offsetRange = range(new THREE.Vector3(-2, 3, -2), new THREE.Vector3(2, 5, 2)); - - const speed = uniform(0.2); - const scaledTime = time.add(5).mul(speed); - - const lifeTime = scaledTime.mul(lifeRange).mod(1); - const scaleRange = range(0.3, 2); - const rotateRange = range(0.1, 4); - - const life = lifeTime.div(lifeRange); - - const fakeLightEffect = positionLocal.y.oneMinus().max(0.2); - - const textureNode = texture(map, rotateUV(uv(), scaledTime.mul(rotateRange))); - - const opacityNode = textureNode.a.mul(life.oneMinus()); - - const smokeColor = mix(color(0x2c1501), color(0x222222), positionLocal.y.mul(3).clamp()); - - // create particles - - const smokeNodeMaterial = new THREE.SpriteNodeMaterial(); - smokeNodeMaterial.colorNode = mix(color(0xf27d0c), smokeColor, life.mul(2.5).min(1)).mul(fakeLightEffect); - smokeNodeMaterial.opacityNode = opacityNode; - smokeNodeMaterial.positionNode = offsetRange.mul(lifeTime); - smokeNodeMaterial.scaleNode = scaleRange.mul(lifeTime.max(0.3)); - smokeNodeMaterial.depthWrite = false; - - const smokeInstancedSprite = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), smokeNodeMaterial); - smokeInstancedSprite.scale.setScalar(400); - smokeInstancedSprite.count = 2000; - scene.add(smokeInstancedSprite); - - // - - const fireGeometry = new THREE.PlaneGeometry(1, 1); - const fireCount = 1000; - - const fireNodeMaterial = new THREE.SpriteNodeMaterial(); - fireNodeMaterial.colorNode = mix(color(0xb72f17), color(0xb72f17), life); - fireNodeMaterial.positionNode = range(new THREE.Vector3(-1, 1, -1), new THREE.Vector3(1, 2, 1)).mul(lifeTime); - fireNodeMaterial.scaleNode = smokeNodeMaterial.scaleNode; - fireNodeMaterial.opacityNode = opacityNode.mul(0.5); - fireNodeMaterial.blending = THREE.AdditiveBlending; - fireNodeMaterial.transparent = true; - fireNodeMaterial.depthWrite = false; - - const fireInstancedSprite = new THREE.Mesh(fireGeometry, fireNodeMaterial); - fireInstancedSprite.scale.setScalar(400); - fireInstancedSprite.count = fireCount; - fireInstancedSprite.position.y = -100; - fireInstancedSprite.renderOrder = 1; - scene.add(fireInstancedSprite); - - // indirect draw ( optional ) - // each indirect draw call is 5 uint32 values for indexes ( different structure for non-indexed draw calls using 4 uint32 values ) - - const indexCount = fireGeometry.index.array.length; - - const uint32 = new Uint32Array(5); - uint32[0] = indexCount; // indexCount - uint32[1] = fireCount; // instanceCount - uint32[2] = 0; // firstIndex - uint32[3] = 0; // baseVertex - uint32[4] = 0; // firstInstance - - const indirectAttribute = new THREE.IndirectStorageBufferAttribute(uint32, 5); - fireGeometry.setIndirect(indirectAttribute); - - // - - const helper = new THREE.GridHelper(3000, 40, 0x444444, 0x444444); - helper.position.y = -75; - scene.add(helper); - - // - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(render); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // - - controls = new OrbitControls(camera, renderer.domElement); - controls.maxDistance = 2700; - controls.target.set(0, 500, 0); - controls.update(); - - // - - window.addEventListener('resize', onWindowResize); - - // gui - - const gui = renderer.inspector.createParameters('Settings'); - - gui.add(speed, 'value', 0, 1, 0.01).name('speed'); -} - -function onWindowResize() { - const { innerWidth, innerHeight } = window; - - camera.aspect = innerWidth / innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(innerWidth, innerHeight); -} - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_performance.ts b/examples-testing/examples/webgpu_performance.ts deleted file mode 100644 index fb4ed1556..000000000 --- a/examples-testing/examples/webgpu_performance.ts +++ /dev/null @@ -1,101 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; - -import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; - -let camera, scene, renderer; -let model; - -const options = { static: true }; - -init(); - -function setStatic(object, value) { - object.traverse(child => { - if (child.isMesh) { - child.static = value; - } - }); -} - -function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(60, 60, 60); - - scene = new THREE.Scene(); - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = 1; - renderer.inspector = new Inspector(); - renderer.setAnimationLoop(render); - container.appendChild(renderer.domElement); - - // - - new UltraHDRLoader().setPath('textures/equirectangular/').load('royal_esplanade_2k.hdr.jpg', function (texture) { - texture.mapping = THREE.EquirectangularReflectionMapping; - - scene.environment = texture; - - // model - - const dracoLoader = new DRACOLoader(); - dracoLoader.setDecoderPath('jsm/libs/draco/gltf/'); - - const loader = new GLTFLoader().setPath('models/gltf/'); - loader.setDRACOLoader(dracoLoader); - - loader.load('dungeon_warkarma.glb', async function (gltf) { - model = gltf.scene; - - // wait until the model can be added to the scene without blocking due to shader compilation - - await renderer.compileAsync(model, camera, scene); - - scene.add(model); - - // - - setStatic(model, options.static); - }); - }); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 2; - controls.maxDistance = 60; - controls.target.set(0, 0, -0.2); - controls.update(); - - // gui - - const gui = renderer.inspector.createParameters('Settings'); - gui.add(options, 'static').onChange(() => { - setStatic(model, options.static); - }); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_performance_renderbundle.ts b/examples-testing/examples/webgpu_performance_renderbundle.ts deleted file mode 100644 index 3b20847b8..000000000 --- a/examples-testing/examples/webgpu_performance_renderbundle.ts +++ /dev/null @@ -1,197 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let camera, scene, renderer; -let controls; -let gui; -let geometries, group; - -// - -const position = new THREE.Vector3(); -const rotation = new THREE.Euler(); -const quaternion = new THREE.Quaternion(); -const scale = new THREE.Vector3(); - -// - -const api = { - webgpu: true, - renderBundle: true, - count: 4000, - opacity: 1, - dynamic: false, -}; - -init(); - -// - -function randomizeMatrix(matrix) { - position.x = Math.random() * 80 - 40; - position.y = Math.random() * 80 - 40; - position.z = Math.random() * 80 - 40; - - rotation.x = Math.random() * 2 * Math.PI; - rotation.y = Math.random() * 2 * Math.PI; - rotation.z = Math.random() * 2 * Math.PI; - - quaternion.setFromEuler(rotation); - - scale.x = scale.y = scale.z = 0.35 + Math.random() * 0.5; - - return matrix.compose(position, quaternion, scale); -} - -function randomizeRotationSpeed(rotation) { - rotation.x = Math.random() * 0.05; - rotation.y = Math.random() * 0.05; - rotation.z = Math.random() * 0.05; - return rotation; -} - -function initGeometries() { - geometries = [ - new THREE.ConeGeometry(1.0, 2.0, 3, 1), - new THREE.BoxGeometry(2.0, 2.0, 2.0), - new THREE.PlaneGeometry(2.0, 2, 1, 1), - new THREE.CapsuleGeometry(), - new THREE.CircleGeometry(1.0, 3), - new THREE.CylinderGeometry(1.0, 1.0, 2.0, 3, 1), - new THREE.DodecahedronGeometry(1.0, 0), - new THREE.IcosahedronGeometry(1.0, 0), - new THREE.OctahedronGeometry(1.0, 0), - new THREE.PolyhedronGeometry([0, 0, 0], [0, 0, 0], 1, 0), - new THREE.RingGeometry(1.0, 1.5, 3), - new THREE.SphereGeometry(1.0, 3, 2), - new THREE.TetrahedronGeometry(1.0, 0), - new THREE.TorusGeometry(1.0, 0.5, 3, 3), - new THREE.TorusKnotGeometry(1.0, 0.5, 20, 3, 1, 1), - ]; -} - -function initMesh(count) { - initRegularMesh(count); -} - -function initRegularMesh(count) { - group = api.renderBundle ? new THREE.BundleGroup() : new THREE.Group(); - - for (let i = 0; i < count; i++) { - const material = new THREE.MeshToonNodeMaterial({ - color: new THREE.Color(Math.random() * 0xffffff), - side: THREE.DoubleSide, - }); - - const child = new THREE.Mesh(geometries[i % geometries.length], material); - randomizeMatrix(child.matrix); - child.matrix.decompose(child.position, child.quaternion, child.scale); - child.userData.rotationSpeed = randomizeRotationSpeed(new THREE.Euler()); - child.frustumCulled = false; - group.add(child); - } - - scene.add(group); -} - -function init() { - const searchParams = new URLSearchParams(window.location.search); - api.webgpu = searchParams.get('backend') !== 'webgl'; - api.renderBundle = searchParams.get('renderBundle') !== 'false'; - api.count = parseFloat(searchParams.get('count') || 4000); - - const count = api.count; - - // camera - - const aspect = window.innerWidth / window.innerHeight; - - camera = new THREE.PerspectiveCamera(70, aspect, 1, 100); - camera.position.z = 50; - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: true, forceWebGL: !api.webgpu }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.inspector = new Inspector(); - renderer.setAnimationLoop(animate); - - // scene - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xc1c1c1); - - const light = new THREE.DirectionalLight(0xffffff, 3.4); - scene.add(light); - - document.body.appendChild(renderer.domElement); - - initGeometries(); - initMesh(count); - - controls = new OrbitControls(camera, renderer.domElement); - controls.autoRotate = true; - controls.autoRotateSpeed = 1.0; - - // gui - - gui = renderer.inspector.createParameters('Settings'); - gui.add(api, 'renderBundle').name('render bundle').onChange(reload); - - gui.add(api, 'webgpu').onChange(reload); - - gui.add(api, 'dynamic').onChange(() => { - group.static = !group.static; - }); - - // listeners - - window.addEventListener('resize', onWindowResize); - - function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); - group.needsUpdate = true; - } - - function reload() { - const backendParam = 'backend=' + (api.webgpu ? 'webgpu' : 'webgl'); - const renderBundleParam = '&renderBundle=' + (api.renderBundle ? 'true' : 'false'); - const countParam = '&count=' + api.count; - - location.href = location.pathname + '?' + backendParam + renderBundleParam + countParam; // relative redirect with parameters - } - - function animate() { - animateMeshes(); - - controls.update(); - - renderer.render(scene, camera); - } - - function animateMeshes() { - const count = api.count; - const loopNum = api.dynamic ? count : 0; - - for (let i = 0; i < loopNum; i++) { - const child = group.children[i]; - const rotationSpeed = child.userData.rotationSpeed; - - child.rotation.set( - child.rotation.x + rotationSpeed.x, - child.rotation.y + rotationSpeed.y, - child.rotation.z + rotationSpeed.z, - ); - } - } -} diff --git a/examples-testing/examples/webgpu_pmrem_cubemap.ts b/examples-testing/examples/webgpu_pmrem_cubemap.ts deleted file mode 100644 index bd751ba33..000000000 --- a/examples-testing/examples/webgpu_pmrem_cubemap.ts +++ /dev/null @@ -1,75 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { normalWorldGeometry, uniform, pmremTexture } from 'three/tsl'; - -import { HDRCubeTextureLoader } from 'three/addons/loaders/HDRCubeTextureLoader.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let camera, scene, renderer; - -init(); - -async function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); - camera.position.set(0, 0, 8); - - scene = new THREE.Scene(); - - const forceWebGL = false; - - renderer = new THREE.WebGPURenderer({ antialias: true, forceWebGL }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(render); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - - await renderer.init(); - - container.appendChild(renderer.domElement); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 2; - controls.maxDistance = 10; - controls.update(); - - new HDRCubeTextureLoader() - .setPath('./textures/cube/pisaHDR/') - .load(['px.hdr', 'nx.hdr', 'py.hdr', 'ny.hdr', 'pz.hdr', 'nz.hdr'], function (map) { - scene.backgroundNode = pmremTexture(map, normalWorldGeometry, uniform(0.5)); - - const geometry = new THREE.SphereGeometry(0.4, 64, 64); - - for (let i = 0; i < 6; i++) { - for (let j = 0; j < 5; j++) { - const material = new THREE.MeshPhysicalNodeMaterial({ - roughness: i / 5, - metalness: j / 4, - envMap: map, - }); - - const mesh = new THREE.Mesh(geometry, material); - mesh.position.x = i - 2.5; - mesh.position.y = j - 2; - scene.add(mesh); - } - } - }); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_pmrem_equirectangular.ts b/examples-testing/examples/webgpu_pmrem_equirectangular.ts deleted file mode 100644 index 76f8326d3..000000000 --- a/examples-testing/examples/webgpu_pmrem_equirectangular.ts +++ /dev/null @@ -1,75 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { normalWorldGeometry, uniform, pmremTexture } from 'three/tsl'; - -import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let camera, scene, renderer; - -init(); - -async function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); - camera.position.set(0, 0, 8); - - scene = new THREE.Scene(); - - const forceWebGL = false; - - renderer = new THREE.WebGPURenderer({ antialias: true, forceWebGL }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(render); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - - await renderer.init(); - - container.appendChild(renderer.domElement); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 2; - controls.maxDistance = 10; - controls.update(); - - new UltraHDRLoader().setPath('textures/equirectangular/').load('royal_esplanade_2k.hdr.jpg', function (map) { - map.mapping = THREE.EquirectangularReflectionMapping; - - scene.backgroundNode = pmremTexture(map, normalWorldGeometry, uniform(0.5)); - - const geometry = new THREE.SphereGeometry(0.4, 64, 64); - - for (let i = 0; i < 6; i++) { - for (let j = 0; j < 5; j++) { - const material = new THREE.MeshPhysicalNodeMaterial({ - roughness: i / 5, - metalness: j / 4, - envMap: map, - }); - - const mesh = new THREE.Mesh(geometry, material); - mesh.position.x = i - 2.5; - mesh.position.y = j - 2; - scene.add(mesh); - } - } - }); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_pmrem_scene.ts b/examples-testing/examples/webgpu_pmrem_scene.ts deleted file mode 100644 index deab005fc..000000000 --- a/examples-testing/examples/webgpu_pmrem_scene.ts +++ /dev/null @@ -1,106 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { normalWorld, uniform, pmremTexture } from 'three/tsl'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer; - -init(); - -async function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); - camera.position.set(-1.8, 0.6, 2.7); - - scene = new THREE.Scene(); - - const forceWebGL = false; - - renderer = new THREE.WebGPURenderer({ antialias: true, forceWebGL }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(render); - renderer.inspector = new Inspector(); - container.appendChild(renderer.domElement); - - await renderer.init(); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.addEventListener('change', render); // use if there is no animation loop - controls.minDistance = 2; - controls.maxDistance = 10; - controls.update(); - - // - const loader = new THREE.CubeTextureLoader().setPath('./textures/cube/Park3Med/'); - - scene.background = await loader.loadAsync(['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg']); - - let model; - - model = new THREE.Mesh(new THREE.SphereGeometry(0.2, 64, 64), new THREE.MeshBasicMaterial({ color: 0x0000ff })); - model.position.z -= 1; - scene.add(model); - - model = new THREE.Mesh(new THREE.SphereGeometry(0.2, 64, 64), new THREE.MeshBasicMaterial({ color: 0xff0000 })); - model.position.z += 1; - scene.add(model); - - model = new THREE.Mesh(new THREE.SphereGeometry(0.2, 64, 64), new THREE.MeshBasicMaterial({ color: 0xff00ff })); - model.position.x += 1; - scene.add(model); - - model = new THREE.Mesh(new THREE.SphereGeometry(0.2, 64, 64), new THREE.MeshBasicMaterial({ color: 0x00ffff })); - model.position.x -= 1; - scene.add(model); - - model = new THREE.Mesh(new THREE.SphereGeometry(0.2, 64, 64), new THREE.MeshBasicMaterial({ color: 0xffff00 })); - model.position.y -= 1; - scene.add(model); - - model = new THREE.Mesh(new THREE.SphereGeometry(0.2, 64, 64), new THREE.MeshBasicMaterial({ color: 0x00ff00 })); - model.position.y += 1; - scene.add(model); - - //while ( scene.children.length > 0 ) scene.remove( scene.children[ 0 ] ); - - const sceneRT = new THREE.PMREMGenerator(renderer).fromScene(scene); - - // - - const pmremRoughness = uniform(0.5); - const pmremNode = pmremTexture(sceneRT.texture, normalWorld, pmremRoughness); - - scene.add( - new THREE.Mesh( - new THREE.SphereGeometry(0.5, 64, 64), - new THREE.MeshBasicNodeMaterial({ colorNode: pmremNode }), - ), - ); - - // gui - - const gui = renderer.inspector.createParameters('Settings'); - gui.add(pmremRoughness, 'value', 0, 1, 0.001) - .name('roughness') - .onChange(() => render()); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_pmrem_test.ts b/examples-testing/examples/webgpu_pmrem_test.ts deleted file mode 100644 index 3e1ea3ef9..000000000 --- a/examples-testing/examples/webgpu_pmrem_test.ts +++ /dev/null @@ -1,138 +0,0 @@ -import * as THREE from 'three'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let scene, camera, controls, renderer; - -async function init() { - const width = window.innerWidth; - const height = window.innerHeight; - const aspect = width / height; - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(width, height); - renderer.setAnimationLoop(render); - renderer.inspector = new Inspector(); - - // tonemapping - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = 1; - - document.body.appendChild(renderer.domElement); - - window.addEventListener('resize', onWindowResize); - - await renderer.init(); - - // scene - - scene = new THREE.Scene(); - - // camera - - camera = new THREE.PerspectiveCamera(40, aspect, 1, 30); - updateCamera(); - camera.position.set(0, 0, 16); - - // controls - - controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 4; - controls.maxDistance = 20; - - // light - - const directionalLight = new THREE.DirectionalLight(0xffffff, 0); // set intensity to 0 to start - const x = 597; - const y = 213; - const theta = ((x + 0.5) * Math.PI) / 512; - const phi = ((y + 0.5) * Math.PI) / 512; - - directionalLight.position.setFromSphericalCoords(100, -phi, Math.PI / 2 - theta); - - scene.add(directionalLight); - // scene.add( new THREE.DirectionalLightHelper( directionalLight ) ); - - // The spot1Lux HDR environment map is expressed in nits (lux / sr). The directional light has units of lux, - // so to match a 1 lux light, we set a single pixel with a value equal to 1 divided by the solid - // angle of the pixel in steradians. This image is 1024 x 512, - // so the value is 1 / ( sin( phi ) * ( pi / 512 ) ^ 2 ) = 27,490 nits. - - const gui = renderer.inspector.createParameters('Settings'); - gui.add({ enabled: true }, 'enabled') - .name('PMREM') - .onChange(value => { - directionalLight.intensity = value ? 0 : 1; - - scene.traverse(function (child) { - if (child.isMesh) { - child.material.envMapIntensity = 1 - directionalLight.intensity; - } - }); - }); -} - -function createObjects() { - let radianceMap = null; - new HDRLoader() - // .setDataType( THREE.FloatType ) - .setPath('textures/equirectangular/') - .load('spot1Lux.hdr', function (texture) { - radianceMap = pmremGenerator.fromEquirectangular(texture).texture; - pmremGenerator.dispose(); - - scene.background = radianceMap; - - const geometry = new THREE.SphereGeometry(0.4, 32, 32); - - for (let x = 0; x <= 10; x++) { - for (let y = 0; y <= 2; y++) { - const material = new THREE.MeshPhysicalMaterial({ - roughness: x / 10, - metalness: y < 1 ? 1 : 0, - color: y < 2 ? 0xffffff : 0x000000, - envMap: radianceMap, - envMapIntensity: 1, - }); - - const mesh = new THREE.Mesh(geometry, material); - mesh.position.x = x - 5; - mesh.position.y = 1 - y; - scene.add(mesh); - } - } - }); - - const pmremGenerator = new THREE.PMREMGenerator(renderer); - pmremGenerator.compileEquirectangularShader(); -} - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - updateCamera(); - - renderer.setSize(width, height); -} - -function updateCamera() { - const horizontalFoV = 40; - const verticalFoV = - (2 * Math.atan(Math.tan(((horizontalFoV / 2) * Math.PI) / 180) / camera.aspect) * 180) / Math.PI; - camera.fov = verticalFoV; - camera.updateProjectionMatrix(); -} - -function render() { - renderer.render(scene, camera); -} - -Promise.resolve().then(init).then(createObjects); diff --git a/examples-testing/examples/webgpu_portal.ts b/examples-testing/examples/webgpu_portal.ts deleted file mode 100644 index df2e400f3..000000000 --- a/examples-testing/examples/webgpu_portal.ts +++ /dev/null @@ -1,156 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { - pass, - color, - mx_worley_noise_float, - time, - screenUV, - vec2, - uv, - normalWorld, - mx_fractal_noise_vec3, -} from 'three/tsl'; - -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, sceneMain, scenePortal, renderer; -let timer; - -const mixers = []; - -init(); - -function init() { - // - - sceneMain = new THREE.Scene(); - sceneMain.background = new THREE.Color(0x222222); - sceneMain.backgroundNode = normalWorld.y.mix(color(0x0066ff), color(0xff0066)); - - scenePortal = new THREE.Scene(); - scenePortal.backgroundNode = mx_worley_noise_float(normalWorld.mul(20).add(vec2(0, time.oneMinus()))).mul( - color(0x0066ff), - ); - scenePortal.name = 'Portal Scene'; - - // - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.01, 30); - camera.position.set(2.5, 1, 3); - camera.position.multiplyScalar(0.8); - camera.lookAt(0, 1, 0); - - timer = new THREE.Timer(); - timer.connect(document); - - // lights - - const light = new THREE.PointLight(0xffffff, 1); - light.position.set(0, 1, 5); - light.power = 17000; - - sceneMain.add(new THREE.HemisphereLight(0xff0066, 0x0066ff, 7)); - sceneMain.add(light); - scenePortal.add(light.clone()); - - // models - - const loader = new GLTFLoader(); - loader.load('models/gltf/Xbot.glb', function (gltf) { - const createModel = (colorNode = null) => { - let object; - - if (mixers.length === 0) { - object = gltf.scene; - } else { - object = gltf.scene.clone(); - - const children = object.children[0].children; - - const applyFX = index => { - children[index].material = children[index].material.clone(); - children[index].material.colorNode = colorNode; - children[index].material.wireframe = true; - }; - - applyFX(0); - applyFX(1); - } - - const mixer = new THREE.AnimationMixer(object); - - const action = mixer.clipAction(gltf.animations[6]); - action.play(); - - mixers.push(mixer); - - return object; - }; - - const colorNode = mx_fractal_noise_vec3(uv().mul(20).add(time)); - - const modelMain = createModel(); - const modelPortal = createModel(colorNode); - - // model portal - - sceneMain.add(modelMain); - scenePortal.add(modelPortal); - }); - - // portal - - const geometry = new THREE.PlaneGeometry(1.7, 2); - - const material = new THREE.MeshBasicNodeMaterial(); - material.colorNode = pass(scenePortal, camera).context({ getUV: () => screenUV }); - material.opacityNode = uv().distance(0.5).remapClamp(0.3, 0.5).oneMinus(); - material.side = THREE.DoubleSide; - material.transparent = true; - - const plane = new THREE.Mesh(geometry, material); - plane.position.set(0, 1, 0.8); - sceneMain.add(plane); - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.toneMapping = THREE.LinearToneMapping; - renderer.toneMappingExposure = 0.15; - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // - - const controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, 1, 0); - controls.update(); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - const delta = timer.getDelta(); - - for (const mixer of mixers) { - mixer.update(delta); - } - - renderer.render(sceneMain, camera); -} diff --git a/examples-testing/examples/webgpu_postprocessing.ts b/examples-testing/examples/webgpu_postprocessing.ts deleted file mode 100644 index d554a8835..000000000 --- a/examples-testing/examples/webgpu_postprocessing.ts +++ /dev/null @@ -1,81 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { pass } from 'three/tsl'; -import { dotScreen } from 'three/addons/tsl/display/DotScreenNode.js'; -import { rgbShift } from 'three/addons/tsl/display/RGBShiftNode.js'; -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, renderer, renderPipeline; -let object; - -init(); - -function init() { - renderer = new THREE.WebGPURenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // - - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.z = 400; - - const scene = new THREE.Scene(); - scene.fog = new THREE.Fog(0x000000, 1, 1000); - - object = new THREE.Object3D(); - scene.add(object); - - const geometry = new THREE.SphereGeometry(1, 4, 4); - const material = new THREE.MeshPhongMaterial({ color: 0xffffff, flatShading: true }); - - for (let i = 0; i < 100; i++) { - const mesh = new THREE.Mesh(geometry, material); - mesh.position.set(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5).normalize(); - mesh.position.multiplyScalar(Math.random() * 400); - mesh.rotation.set(Math.random() * 2, Math.random() * 2, Math.random() * 2); - mesh.scale.x = mesh.scale.y = mesh.scale.z = Math.random() * 50; - object.add(mesh); - } - - scene.add(new THREE.AmbientLight(0xcccccc)); - - const light = new THREE.DirectionalLight(0xffffff, 3); - light.position.set(1, 1, 1); - scene.add(light); - - // postprocessing - - renderPipeline = new THREE.RenderPipeline(renderer); - - const scenePass = pass(scene, camera); - const scenePassColor = scenePass.getTextureNode().toInspector('Scene Color'); - - const dotScreenPass = dotScreen(scenePassColor); - dotScreenPass.scale.value = 0.3; - - const rgbShiftPass = rgbShift(dotScreenPass); - rgbShiftPass.amount.value = 0.001; - - renderPipeline.outputNode = rgbShiftPass; - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - object.rotation.x += 0.005; - object.rotation.y += 0.01; - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_postprocessing_3dlut.ts b/examples-testing/examples/webgpu_postprocessing_3dlut.ts deleted file mode 100644 index 867cc62f7..000000000 --- a/examples-testing/examples/webgpu_postprocessing_3dlut.ts +++ /dev/null @@ -1,228 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { - mix, - mul, - oneMinus, - positionLocal, - smoothstep, - texture, - time, - rotateUV, - Fn, - uv, - vec2, - vec3, - vec4, - pass, - texture3D, - uniform, - renderOutput, -} from 'three/tsl'; -import { lut3D } from 'three/addons/tsl/display/Lut3DNode.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { LUTCubeLoader } from 'three/addons/loaders/LUTCubeLoader.js'; -import { LUT3dlLoader } from 'three/addons/loaders/LUT3dlLoader.js'; -import { LUTImageLoader } from 'three/addons/loaders/LUTImageLoader.js'; -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -const params = { - lut: 'Bourbon 64.CUBE', - intensity: 1, -}; - -const lutMap = { - 'Bourbon 64.CUBE': null, - 'Chemical 168.CUBE': null, - 'Clayton 33.CUBE': null, - 'Cubicle 99.CUBE': null, - 'Remy 24.CUBE': null, - 'Presetpro-Cinematic.3dl': null, - NeutralLUT: null, - 'B&WLUT': null, - NightLUT: null, -}; - -let camera, scene, renderer, renderPipeline, controls, lutPass; - -init(); - -async function init() { - camera = new THREE.PerspectiveCamera(25, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(8, 10, 12); - - scene = new THREE.Scene(); - - // Loaders - - const gltfLoader = new GLTFLoader(); - const textureLoader = new THREE.TextureLoader(); - - // LUTs - - const lutCubeLoader = new LUTCubeLoader(); - const lutImageLoader = new LUTImageLoader(); - const lut3dlLoader = new LUT3dlLoader(); - - for (const name in lutMap) { - if (/\.CUBE$/i.test(name)) { - lutMap[name] = lutCubeLoader.loadAsync('luts/' + name); - } else if (/\LUT$/i.test(name)) { - lutMap[name] = lutImageLoader.loadAsync(`luts/${name}.png`); - } else { - lutMap[name] = lut3dlLoader.loadAsync('luts/' + name); - } - } - - const pendings = Object.values(lutMap); - await Promise.all(pendings); - - for (const name in lutMap) { - lutMap[name] = await lutMap[name]; - } - - // baked model - - gltfLoader.load('./models/gltf/coffeeMug.glb', gltf => { - gltf.scene.getObjectByName('baked').material.map.anisotropy = 8; - scene.add(gltf.scene); - }); - - // geometry - - const smokeGeometry = new THREE.PlaneGeometry(1, 1, 16, 64); - smokeGeometry.translate(0, 0.5, 0); - smokeGeometry.scale(1.5, 6, 1.5); - - // texture - - const noiseTexture = textureLoader.load('./textures/noises/perlin/128x128.png'); - noiseTexture.wrapS = THREE.RepeatWrapping; - noiseTexture.wrapT = THREE.RepeatWrapping; - - // material - - const smokeMaterial = new THREE.MeshBasicNodeMaterial({ - transparent: true, - side: THREE.DoubleSide, - depthWrite: false, - }); - - // position - - smokeMaterial.positionNode = Fn(() => { - // twist - - const twistNoiseUv = vec2(0.5, uv().y.mul(0.2).sub(time.mul(0.005)).mod(1)); - const twist = texture(noiseTexture, twistNoiseUv).r.mul(10); - positionLocal.xz.assign(rotateUV(positionLocal.xz, twist, vec2(0))); - - // wind - - const windOffset = vec2( - texture(noiseTexture, vec2(0.25, time.mul(0.01)).mod(1)).r.sub(0.5), - texture(noiseTexture, vec2(0.75, time.mul(0.01)).mod(1)).r.sub(0.5), - ).mul(uv().y.pow(2).mul(10)); - positionLocal.addAssign(windOffset); - - return positionLocal; - })(); - - // color - - smokeMaterial.colorNode = Fn(() => { - // alpha - - const alphaNoiseUv = uv() - .mul(vec2(0.5, 0.3)) - .add(vec2(0, time.mul(0.03).negate())); - const alpha = mul( - // pattern - texture(noiseTexture, alphaNoiseUv).r.smoothstep(0.4, 1), - - // edges fade - smoothstep(0, 0.1, uv().x), - smoothstep(0, 0.1, oneMinus(uv().x)), - smoothstep(0, 0.1, uv().y), - smoothstep(0, 0.1, oneMinus(uv().y)), - ); - - // color - - const finalColor = mix(vec3(0.6, 0.3, 0.2), vec3(1, 1, 1), alpha.pow(3)); - - return vec4(finalColor, alpha); - })(); - - // mesh - - const smoke = new THREE.Mesh(smokeGeometry, smokeMaterial); - smoke.position.y = 1.83; - scene.add(smoke); - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // post processing - - renderPipeline = new THREE.RenderPipeline(renderer); - - // ignore default output color transform ( toneMapping and outputColorSpace ) - // use renderOutput() for control the sequence - - renderPipeline.outputColorTransform = false; - - // scene pass - - const scenePass = pass(scene, camera); - const outputPass = renderOutput(scenePass); - - const lut = lutMap[params.lut]; - lutPass = lut3D(outputPass, texture3D(lut.texture3D), lut.texture3D.image.width, uniform(1)); - - renderPipeline.outputNode = lutPass; - - // controls - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - controls.minDistance = 0.1; - controls.maxDistance = 50; - controls.target.y = 3; - - // gui - - const gui = renderer.inspector.createParameters('Settings'); - gui.add(params, 'lut', Object.keys(lutMap)); - gui.add(params, 'intensity', 0, 1); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -async function animate() { - controls.update(); - - lutPass.intensityNode.value = params.intensity; - - if (lutMap[params.lut]) { - const lut = lutMap[params.lut]; - lutPass.lutNode.value = lut.texture3D; - lutPass.size.value = lut.texture3D.image.width; - } - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_postprocessing_afterimage.ts b/examples-testing/examples/webgpu_postprocessing_afterimage.ts deleted file mode 100644 index 2df8212f5..000000000 --- a/examples-testing/examples/webgpu_postprocessing_afterimage.ts +++ /dev/null @@ -1,151 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { - instancedBufferAttribute, - uniform, - mod, - pass, - texture, - float, - time, - vec2, - vec3, - vec4, - sin, - cos, -} from 'three/tsl'; -import { afterImage } from 'three/addons/tsl/display/AfterImageNode.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer, particles; -let renderPipeline, afterImagePass, scenePass; - -const params = { - damp: uniform(0.8, 'float').setName('damp'), - enabled: true, -}; - -init(); - -function init() { - renderer = new THREE.WebGPURenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 10000); - camera.position.z = 1000; - - scene = new THREE.Scene(); - - const sprite = new THREE.TextureLoader().load('textures/sprites/circle.png'); - - // geometry - - const radius = 600; - const count = 50000; - - const vertex = new THREE.Vector3(); - const color = new THREE.Color(); - - const colors = []; - const vertices = []; - const timeOffsets = []; - - for (var i = 0; i < count; i++) { - getRandomPointOnSphere(radius, vertex); - vertices.push(vertex.x, vertex.y, vertex.z); - - color.setHSL(i / count, 0.7, 0.7, THREE.SRGBColorSpace); - colors.push(color.r, color.g, color.b); - - timeOffsets.push(i / count); - } - - const positionAttribute = new THREE.InstancedBufferAttribute(new Float32Array(vertices), 3); - const colorAttribute = new THREE.InstancedBufferAttribute(new Float32Array(colors), 3); - const timeAttribute = new THREE.InstancedBufferAttribute(new Float32Array(timeOffsets), 1); - - // material and TSL - - const material = new THREE.SpriteNodeMaterial({ blending: THREE.AdditiveBlending, depthWrite: false }); - - const localTime = instancedBufferAttribute(timeAttribute).add(time.mul(0.1)); - const modTime = mod(localTime, 1.0); - const accTime = modTime.mul(modTime); - - const angle = accTime.mul(40.0); - const pulse = vec2(sin(angle).mul(20.0), cos(angle).mul(20.0)); - const pos = instancedBufferAttribute(positionAttribute); - - const animated = vec3( - pos.x.mul(accTime).add(pulse.x), - pos.y.mul(accTime).add(pulse.y), - pos.z.mul(accTime).mul(1.75), - ); - const fAlpha = modTime.oneMinus().mul(2.0); - - material.colorNode = texture(sprite).mul(vec4(instancedBufferAttribute(colorAttribute), fAlpha)); - material.positionNode = animated; - material.scaleNode = float(2); - - particles = new THREE.Sprite(material); - particles.count = count; - scene.add(particles); - - // postprocessing - - renderPipeline = new THREE.RenderPipeline(renderer); - - scenePass = pass(scene, camera); - - afterImagePass = afterImage(scenePass, params.damp); - - renderPipeline.outputNode = afterImagePass; - - // - - const gui = renderer.inspector.createParameters('Settings'); - gui.add(afterImagePass.damp, 'value', 0.25, 1); - gui.add(params, 'enabled').onChange(updatePassChain); - - window.addEventListener('resize', onWindowResize); -} - -function updatePassChain() { - if (params.enabled === true) { - renderPipeline.outputNode = afterImagePass; - } else { - renderPipeline.outputNode = scenePass; - } - - renderPipeline.needsUpdate = true; -} - -function getRandomPointOnSphere(r, v) { - const angle = Math.random() * Math.PI * 2; - const u = Math.random() * 2 - 1; - - v.set( - Math.cos(angle) * Math.sqrt(1 - Math.pow(u, 2)) * r, - Math.sin(angle) * Math.sqrt(1 - Math.pow(u, 2)) * r, - u * r, - ); - - return v; -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate(time) { - particles.rotation.z = time * 0.001; - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_postprocessing_anamorphic.ts b/examples-testing/examples/webgpu_postprocessing_anamorphic.ts deleted file mode 100644 index 1cdb6893c..000000000 --- a/examples-testing/examples/webgpu_postprocessing_anamorphic.ts +++ /dev/null @@ -1,104 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { pass, cubeTexture, screenUV, grayscale, uniform } from 'three/tsl'; -import { anamorphic } from 'three/addons/tsl/display/AnamorphicNode.js'; - -import { HDRCubeTextureLoader } from 'three/addons/loaders/HDRCubeTextureLoader.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer; -let renderPipeline; - -const params = { - resolutionScale: 0.2, -}; - -init(); - -async function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); - camera.position.set(-1.8, -0.6, 2.7); - - scene = new THREE.Scene(); - - const hdrUrls = ['px.hdr', 'nx.hdr', 'py.hdr', 'ny.hdr', 'pz.hdr', 'nz.hdr']; - const cube1Texture = await new HDRCubeTextureLoader().setPath('./textures/cube/pisaHDR/').loadAsync(hdrUrls); - - scene.environment = cube1Texture; - scene.backgroundNode = grayscale( - cubeTexture(cube1Texture).mul(screenUV.distance(0.5).oneMinus().remapClamp(0.1, 4)), - ); - - const loader = new GLTFLoader().setPath('models/gltf/DamagedHelmet/glTF/'); - loader.load('DamagedHelmet.gltf', function (gltf) { - scene.add(gltf.scene); - }); - - renderer = new THREE.WebGPURenderer({ antialias: true }); - - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.toneMapping = THREE.LinearToneMapping; - renderer.toneMappingExposure = 1; - renderer.inspector = new Inspector(); - renderer.setAnimationLoop(render); - container.appendChild(renderer.domElement); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 2; - controls.maxDistance = 10; - - // post-processing - - const scenePass = pass(scene, camera); - - const threshold = uniform(1.4); - const scaleNode = uniform(5); - const intensity = uniform(1); - const samples = 64; - - const anamorphicPass = anamorphic( - scenePass.getTextureNode().toInspector('Color'), - threshold, - scaleNode, - samples, - ).toInspector('Anamorphic'); - anamorphicPass.resolutionScale = params.resolutionScale; // 1 = full resolution - - renderPipeline = new THREE.RenderPipeline(renderer); - renderPipeline.outputNode = scenePass.add(anamorphicPass.mul(intensity)); - //renderPipeline.outputNode = scenePass.add( anamorphicPass.getTextureNode().gaussianBlur() ); - - // gui - - const gui = renderer.inspector.createParameters('Settings'); - gui.add(intensity, 'value', 0, 4, 0.1).name('intensity'); - gui.add(threshold, 'value', 0.8, 3, 0.001).name('threshold'); - gui.add(scaleNode, 'value', 1, 10, 0.1).name('scale'); - gui.add(params, 'resolutionScale', 0.1, 1, 0.1) - .name('resolution scale') - .onChange(value => (anamorphicPass.resolutionScale = value)); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function render() { - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_postprocessing_ao.ts b/examples-testing/examples/webgpu_postprocessing_ao.ts deleted file mode 100644 index 76046bee8..000000000 --- a/examples-testing/examples/webgpu_postprocessing_ao.ts +++ /dev/null @@ -1,210 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { - sample, - pass, - mrt, - screenUV, - normalView, - velocity, - vec3, - vec4, - directionToColor, - colorToDirection, - colorSpaceToWorking, - builtinAOContext, -} from 'three/tsl'; -import { ao } from 'three/addons/tsl/display/GTAONode.js'; -import { traa } from 'three/addons/tsl/display/TRAANode.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; -import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer, renderPipeline, controls; - -let aoPass, traaPass, transparentMesh; - -const params = { - samples: 16, - distanceExponent: 1, - distanceFallOff: 1, - radius: 0.25, - scale: 1, - thickness: 1, - aoOnly: false, - transparentOpacity: 0.3, -}; - -init(); - -async function init() { - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 50); - camera.position.set(1, 1.3, 5); - - scene = new THREE.Scene(); - - renderer = new THREE.WebGPURenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - await renderer.init(); - - // controls - - controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, 0.5, -1); - controls.update(); - controls.enablePan = false; - controls.enableDamping = true; - controls.minDistance = 2; - controls.maxDistance = 8; - - // environment - - const environment = new RoomEnvironment(); - const pmremGenerator = new THREE.PMREMGenerator(renderer); - - scene.background = new THREE.Color(0x666666); - scene.environment = pmremGenerator.fromScene(environment, 0.04).texture; - environment.dispose(); - pmremGenerator.dispose(); - - // post-processing - - renderPipeline = new THREE.RenderPipeline(renderer); - - // pre-pass - - const prePass = pass(scene, camera).toInspector('Normal', inspectNode => - colorSpaceToWorking(inspectNode, THREE.SRGBColorSpace), - ); - prePass.name = 'Pre-Pass'; - prePass.transparent = false; - - prePass.setMRT( - mrt({ - output: directionToColor(normalView), - velocity: velocity, - }), - ); - - const prePassNormal = sample(uv => { - return colorToDirection(prePass.getTextureNode().sample(uv)); - }); - - const prePassDepth = prePass.getTextureNode('depth').toInspector('Depth', () => prePass.getLinearDepthNode()); - const prePassVelocity = prePass.getTextureNode('velocity').toInspector('Velocity'); - - // pre-pass - bandwidth optimization - - const normalTexture = prePass.getTexture('output'); - normalTexture.type = THREE.UnsignedByteType; - - // scene pass - - const scenePass = pass(scene, camera).toInspector('Color'); - - // ao - - aoPass = ao(prePassDepth, prePassNormal, camera).toInspector('GTAO', inspectNode => inspectNode.r); - aoPass.resolutionScale = 0.5; // running AO in half resolution is often sufficient - aoPass.useTemporalFiltering = true; - - const aoPassOutput = aoPass.getTextureNode(); - - // scene context - - scenePass.contextNode = builtinAOContext(aoPassOutput.sample(screenUV).r); - - // final output + traa - - traaPass = traa(scenePass, prePassDepth, prePassVelocity, camera); - traaPass.useSubpixelCorrection = false; - - renderPipeline.outputNode = traaPass; - - // models - - const dracoLoader = new DRACOLoader(); - dracoLoader.setDecoderPath('jsm/libs/draco/'); - dracoLoader.setDecoderConfig({ type: 'js' }); - const loader = new GLTFLoader(); - loader.setDRACOLoader(dracoLoader); - loader.setPath('models/gltf/'); - - const gltf = await loader.loadAsync('minimalistic_modern_bedroom.glb'); - - const model = gltf.scene; - model.position.set(0, 1, 0); - scene.add(model); - - // - - transparentMesh = new THREE.Mesh( - new THREE.PlaneGeometry(1.8, 2), - new THREE.MeshStandardNodeMaterial({ transparent: true, opacity: params.transparentOpacity }), - ); - transparentMesh.position.z = 0; - transparentMesh.position.y = 0.5; - transparentMesh.visible = false; - scene.add(transparentMesh); - - // events - - window.addEventListener('resize', onWindowResize); - - // - - const gui = renderer.inspector.createParameters('Settings'); - gui.add(params, 'samples', 4, 32, 1).onChange(updateParameters); - gui.add(params, 'distanceExponent', 1, 2).onChange(updateParameters); - gui.add(params, 'distanceFallOff', 0.01, 1).onChange(updateParameters); - gui.add(params, 'radius', 0.1, 1).onChange(updateParameters); - gui.add(params, 'scale', 0.01, 2).onChange(updateParameters); - gui.add(params, 'thickness', 0.01, 2).onChange(updateParameters); - gui.add(aoPass, 'useTemporalFiltering').name('temporal filtering'); - gui.add(transparentMesh, 'visible').name('show transparent mesh'); - gui.add(params, 'transparentOpacity', 0, 1, 0.01).name('transparent opacity').onChange(updateParameters); - gui.add(params, 'aoOnly').onChange(value => { - if (value === true) { - renderPipeline.outputNode = vec4(vec3(aoPass.r), 1); - } else { - renderPipeline.outputNode = traaPass; - } - - renderPipeline.needsUpdate = true; - }); -} - -function updateParameters() { - aoPass.samples.value = params.samples; - aoPass.distanceExponent.value = params.distanceExponent; - aoPass.distanceFallOff.value = params.distanceFallOff; - aoPass.radius.value = params.radius; - aoPass.scale.value = params.scale; - aoPass.thickness.value = params.thickness; - - transparentMesh.material.opacity = params.transparentOpacity; -} - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); -} - -function animate() { - controls.update(); - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_postprocessing_bloom.ts b/examples-testing/examples/webgpu_postprocessing_bloom.ts deleted file mode 100644 index 3197d680d..000000000 --- a/examples-testing/examples/webgpu_postprocessing_bloom.ts +++ /dev/null @@ -1,120 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { pass } from 'three/tsl'; -import { bloom } from 'three/addons/tsl/display/BloomNode.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -let camera; -let renderPipeline, renderer, mixer, timer; - -const params = { - threshold: 0, - strength: 1, - radius: 0, - exposure: 1, -}; - -init(); - -async function init() { - timer = new THREE.Timer(); - timer.connect(document); - - const scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 100); - camera.position.set(-5, 2.5, -3.5); - scene.add(camera); - - scene.add(new THREE.AmbientLight(0xcccccc)); - - const pointLight = new THREE.PointLight(0xffffff, 100); - camera.add(pointLight); - - const loader = new GLTFLoader(); - const gltf = await loader.loadAsync('models/gltf/PrimaryIonDrive.glb'); - - const model = gltf.scene; - scene.add(model); - - mixer = new THREE.AnimationMixer(model); - const clip = gltf.animations[0]; - mixer.clipAction(clip.optimize()).play(); - - // - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.toneMapping = THREE.ReinhardToneMapping; - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // - - renderPipeline = new THREE.RenderPipeline(renderer); - - const scenePass = pass(scene, camera); - const scenePassColor = scenePass.getTextureNode('output').toInspector('Color'); - - const bloomPass = bloom(scenePassColor).toInspector('Bloom'); - - renderPipeline.outputNode = scenePassColor.add(bloomPass); - - // - - const controls = new OrbitControls(camera, renderer.domElement); - controls.maxPolarAngle = Math.PI * 0.5; - controls.minDistance = 3; - controls.maxDistance = 8; - - // - - const gui = renderer.inspector.createParameters('Settings'); - - const bloomFolder = gui.addFolder('bloom'); - - bloomFolder.add(params, 'threshold', 0.0, 1.0).onChange(function (value) { - bloomPass.threshold.value = value; - }); - - bloomFolder.add(params, 'strength', 0.0, 3.0).onChange(function (value) { - bloomPass.strength.value = value; - }); - - gui.add(params, 'radius', 0.0, 1.0, 0.01).onChange(function (value) { - bloomPass.radius.value = value; - }); - - const toneMappingFolder = gui.addFolder('tone mapping'); - - toneMappingFolder.add(params, 'exposure', 0.1, 2).onChange(function (value) { - renderer.toneMappingExposure = Math.pow(value, 4.0); - }); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); -} - -function animate() { - timer.update(); - - const delta = timer.getDelta(); - - mixer.update(delta); - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_postprocessing_bloom_emissive.ts b/examples-testing/examples/webgpu_postprocessing_bloom_emissive.ts deleted file mode 100644 index faa5b67fb..000000000 --- a/examples-testing/examples/webgpu_postprocessing_bloom_emissive.ts +++ /dev/null @@ -1,115 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { pass, mrt, output, emissive, vec4 } from 'three/tsl'; -import { bloom } from 'three/addons/tsl/display/BloomNode.js'; - -import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer; -let renderPipeline; - -init(); - -function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - // - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20); - camera.position.set(-1.8, 0.6, 2.7); - - scene = new THREE.Scene(); - - new HDRLoader().setPath('textures/equirectangular/').load('moonless_golf_1k.hdr', function (texture) { - texture.mapping = THREE.EquirectangularReflectionMapping; - - scene.background = texture; - scene.environment = texture; - - // model - - const loader = new GLTFLoader().setPath('models/gltf/DamagedHelmet/glTF/'); - loader.load('DamagedHelmet.gltf', function (gltf) { - scene.add(gltf.scene); - }); - }); - - // - - renderer = new THREE.WebGPURenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(render); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.inspector = new Inspector(); - container.appendChild(renderer.domElement); - - // - - const scenePass = pass(scene, camera); - - // set up MRT with emissive - - const mrtNode = mrt({ - output: output, - emissive: vec4(emissive, output.a), - }); - - mrtNode.setBlendMode('emissive', new THREE.BlendMode(THREE.NormalBlending)); - - scenePass.setMRT(mrtNode); - - // optimize the bandwidth - - const emissiveTexture = scenePass.getTexture('emissive'); - emissiveTexture.type = THREE.UnsignedByteType; - - // - - const outputPass = scenePass.getTextureNode().toInspector('Color'); - const emissivePass = scenePass.getTextureNode('emissive').toInspector('Emissive'); - - const bloomPass = bloom(emissivePass, 2.5, 0.5); - - renderPipeline = new THREE.RenderPipeline(renderer); - renderPipeline.outputNode = outputPass.add(bloomPass); - - // - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 2; - controls.maxDistance = 10; - controls.target.set(0, 0, -0.2); - controls.update(); - - window.addEventListener('resize', onWindowResize); - - // - - const gui = renderer.inspector.createParameters('Settings'); - - const bloomFolder = gui.addFolder('Bloom'); - bloomFolder.add(bloomPass.strength, 'value', 0.0, 5.0).name('strength'); - bloomFolder.add(bloomPass.radius, 'value', 0.0, 1.0).name('radius'); - - const toneMappingFolder = gui.addFolder('Tone Mapping'); - toneMappingFolder.add(renderer, 'toneMappingExposure', 0.1, 2).name('exposure'); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function render() { - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_postprocessing_bloom_selective.ts b/examples-testing/examples/webgpu_postprocessing_bloom_selective.ts deleted file mode 100644 index b8f2427d4..000000000 --- a/examples-testing/examples/webgpu_postprocessing_bloom_selective.ts +++ /dev/null @@ -1,124 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { pass, mrt, output, float, uniform } from 'three/tsl'; -import { bloom } from 'three/addons/tsl/display/BloomNode.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -// scene - -const scene = new THREE.Scene(); - -const camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 200); -camera.position.set(0, 0, 20); -camera.lookAt(0, 0, 0); - -const geometry = new THREE.IcosahedronGeometry(1, 15); - -for (let i = 0; i < 50; i++) { - const color = new THREE.Color(); - color.setHSL(Math.random(), 0.7, Math.random() * 0.2 + 0.05); - - const bloomIntensity = Math.random() > 0.5 ? 1 : 0; - - const material = new THREE.MeshBasicNodeMaterial({ color: color }); - material.mrtNode = mrt({ - bloomIntensity: uniform(bloomIntensity), - }); - - const sphere = new THREE.Mesh(geometry, material); - sphere.position.x = Math.random() * 10 - 5; - sphere.position.y = Math.random() * 10 - 5; - sphere.position.z = Math.random() * 10 - 5; - sphere.position.normalize().multiplyScalar(Math.random() * 4.0 + 2.0); - sphere.scale.setScalar(Math.random() * Math.random() + 0.5); - scene.add(sphere); -} - -// renderer - -const renderer = new THREE.WebGPURenderer(); -renderer.setPixelRatio(window.devicePixelRatio); -renderer.setSize(window.innerWidth, window.innerHeight); -renderer.setAnimationLoop(animate); -renderer.inspector = new Inspector(); -renderer.toneMapping = THREE.NeutralToneMapping; -document.body.appendChild(renderer.domElement); - -// post processing - -const scenePass = pass(scene, camera); -scenePass.setMRT( - mrt({ - output, - bloomIntensity: float(0), // default bloom intensity - }), -); - -const outputPass = scenePass.getTextureNode().toInspector('Color'); -const bloomIntensityPass = scenePass.getTextureNode('bloomIntensity').toInspector('Bloom Intensity'); - -const bloomPass = bloom(outputPass.mul(bloomIntensityPass)); - -const renderPipeline = new THREE.RenderPipeline(renderer); -renderPipeline.outputColorTransform = false; -renderPipeline.outputNode = outputPass.add(bloomPass).renderOutput(); - -// controls - -const controls = new OrbitControls(camera, renderer.domElement); -controls.maxPolarAngle = Math.PI * 0.5; -controls.minDistance = 1; -controls.maxDistance = 100; - -// raycaster - -const raycaster = new THREE.Raycaster(); -const mouse = new THREE.Vector2(); - -window.addEventListener('pointerdown', event => { - mouse.x = (event.clientX / window.innerWidth) * 2 - 1; - mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; - - raycaster.setFromCamera(mouse, camera); - - const intersects = raycaster.intersectObjects(scene.children, false); - - if (intersects.length > 0) { - const material = intersects[0].object.material; - - const bloomIntensity = material.mrtNode.get('bloomIntensity'); - bloomIntensity.value = bloomIntensity.value === 0 ? 1 : 0; - } -}); - -// gui - -const gui = renderer.inspector.createParameters('Settings'); - -const bloomFolder = gui.addFolder('Bloom'); -bloomFolder.add(bloomPass.threshold, 'value', 0.0, 1.0).name('threshold'); -bloomFolder.add(bloomPass.strength, 'value', 0.0, 3).name('strength'); -bloomFolder.add(bloomPass.radius, 'value', 0.0, 1.0).name('radius'); - -const toneMappingFolder = gui.addFolder('Tone Mapping'); -toneMappingFolder.add(renderer, 'toneMappingExposure', 0.1, 3).name('exposure'); - -// events - -window.onresize = function () { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); -}; - -// animate - -function animate() { - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_postprocessing_ca.ts b/examples-testing/examples/webgpu_postprocessing_ca.ts deleted file mode 100644 index 48b549050..000000000 --- a/examples-testing/examples/webgpu_postprocessing_ca.ts +++ /dev/null @@ -1,266 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { pass, renderOutput, uniform } from 'three/tsl'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; -import { chromaticAberration } from 'three/addons/tsl/display/ChromaticAberrationNode.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -const params = { - enabled: true, - animated: true, - strength: 1.5, - center: new THREE.Vector2(0.5, 0.5), - scale: 1.2, - autoRotate: true, - cameraDistance: 40, -}; - -let camera, scene, renderer, timer, mainGroup; -let controls, renderPipeline; - -init(); - -async function init() { - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - await renderer.init(); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 200); - camera.position.set(0, 15, params.cameraDistance); - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - controls.dampingFactor = 0.1; - controls.autoRotate = true; - controls.autoRotateSpeed = -0.1; - controls.target.set(0, 0.5, 0); - controls.update(); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x0a0a0a); - - const pmremGenerator = new THREE.PMREMGenerator(renderer); - scene.environment = pmremGenerator.fromScene(new RoomEnvironment(), 0.04).texture; - - timer = new THREE.Timer(); - timer.connect(document); - - // Create main group - mainGroup = new THREE.Group(); - scene.add(mainGroup); - - // Create shapes - createShapes(); - - // Add a grid for reference - const gridHelper = new THREE.GridHelper(40, 20, 0x444444, 0x222222); - gridHelper.position.y = -10; - scene.add(gridHelper); - - // post processing - renderPipeline = new THREE.RenderPipeline(renderer); - renderPipeline.outputColorTransform = false; - - // scene pass - const scenePass = pass(scene, camera); - const outputPass = renderOutput(scenePass); - - // Create uniform nodes for the static version that can be updated - const staticStrength = uniform(params.strength); - const staticCenter = uniform(new THREE.Vector2(params.center.x, params.center.y)); - const staticScale = uniform(params.scale); - - // With static values (using uniform nodes) - const caPass = chromaticAberration(outputPass, staticStrength, staticCenter, staticScale); - - // Set initial output based on params - renderPipeline.outputNode = params.enabled ? caPass : outputPass; - - window.addEventListener('resize', onWindowResize); - - // GUI - - const gui = renderer.inspector.createParameters('Settings'); - - gui.add(params, 'enabled').onChange(value => { - renderPipeline.outputNode = value ? caPass : outputPass; - renderPipeline.needsUpdate = true; - }); - - const staticFolder = gui.addFolder('Static Parameters'); - - staticFolder.add(staticStrength, 'value', 0, 3).name('Strength'); - staticFolder.add(staticCenter.value, 'x', -1, 1).name('Center X'); - staticFolder.add(staticCenter.value, 'y', -1, 1).name('Center Y'); - staticFolder.add(staticScale, 'value', 0.5, 2).name('Scale'); - - const animationFolder = gui.addFolder('Animation'); - animationFolder.add(params, 'animated'); - animationFolder.add(params, 'autoRotate').onChange(value => { - controls.autoRotate = value; - }); -} - -function createShapes() { - const shapes = []; - const materials = []; - - // Define colors for different materials - const colors = [ - 0xff0000, // Red - 0x00ff00, // Green - 0x0000ff, // Blue - 0xffff00, // Yellow - 0xff00ff, // Magenta - 0x00ffff, // Cyan - 0xffffff, // White - 0xff8800, // Orange - ]; - - // Create materials - colors.forEach(color => { - materials.push( - new THREE.MeshStandardMaterial({ - color: color, - roughness: 0.2, - metalness: 0.8, - }), - ); - }); - - // Create geometries - const geometries = [ - new THREE.BoxGeometry(3, 3, 3), - new THREE.SphereGeometry(2, 32, 16), - new THREE.ConeGeometry(2, 4, 8), - new THREE.CylinderGeometry(1.5, 1.5, 4, 8), - new THREE.TorusGeometry(2, 0.8, 8, 16), - new THREE.OctahedronGeometry(2.5), - new THREE.IcosahedronGeometry(2.5), - new THREE.TorusKnotGeometry(1.5, 0.5, 64, 8), - ]; - - // Create central showcase - const centralGroup = new THREE.Group(); - - // Large central torus - const centralTorus = new THREE.Mesh( - new THREE.TorusGeometry(5, 1.5, 16, 32), - new THREE.MeshStandardMaterial({ - color: 0xffffff, - roughness: 0.1, - metalness: 1, - emissive: 0x222222, - }), - ); - centralGroup.add(centralTorus); - - // Inner rotating shapes - for (let i = 0; i < 6; i++) { - const angle = (i / 6) * Math.PI * 2; - const radius = 3; - - const mesh = new THREE.Mesh(geometries[i % geometries.length], materials[i % materials.length]); - - mesh.position.set(Math.cos(angle) * radius, 0, Math.sin(angle) * radius); - - mesh.scale.setScalar(0.5); - centralGroup.add(mesh); - shapes.push(mesh); - } - - mainGroup.add(centralGroup); - shapes.push(centralGroup); - - // Create outer ring of shapes - const numShapes = 12; - const outerRadius = 15; - - for (let i = 0; i < numShapes; i++) { - const angle = (i / numShapes) * Math.PI * 2; - const shapesGroup = new THREE.Group(); - - const geometry = geometries[i % geometries.length]; - const material = materials[i % materials.length]; - - const mesh = new THREE.Mesh(geometry, material); - mesh.castShadow = true; - mesh.receiveShadow = true; - - shapesGroup.add(mesh); - shapesGroup.position.set(Math.cos(angle) * outerRadius, Math.sin(i * 0.5) * 2, Math.sin(angle) * outerRadius); - - mainGroup.add(shapesGroup); - shapes.push(shapesGroup); - } - - // Add floating particles - const particlesGeometry = new THREE.BufferGeometry(); - const particlesCount = 200; - const positions = new Float32Array(particlesCount * 3); - - for (let i = 0; i < particlesCount * 3; i += 3) { - const radius = 25 + Math.random() * 10; - const theta = Math.random() * Math.PI * 2; - const phi = Math.random() * Math.PI; - - positions[i] = radius * Math.sin(phi) * Math.cos(theta); - positions[i + 1] = radius * Math.cos(phi); - positions[i + 2] = radius * Math.sin(phi) * Math.sin(theta); - } - - particlesGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); - - const particlesMaterial = new THREE.PointsMaterial({ - color: 0xffffff, - size: 0.5, - sizeAttenuation: true, - }); - - const particles = new THREE.Points(particlesGeometry, particlesMaterial); - mainGroup.add(particles); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - const time = timer.getElapsed(); - - controls.update(); - - if (params.animated) { - // Animate individual shapes - mainGroup.children.forEach((child, index) => { - if (child.children.length > 0) { - // Central group - child.rotation.y = time * 0.5; - child.children.forEach((subChild, subIndex) => { - if (subChild.geometry) { - subChild.rotation.x = time * (1 + subIndex * 0.1); - subChild.rotation.z = time * (1 - subIndex * 0.1); - } - }); - } else if (child.type === 'Group') { - // Outer shapes - child.rotation.x = time * 0.5 + index; - child.rotation.y = time * 0.3 + index; - child.position.y = Math.sin(time + index) * 2; - } - }); - } - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_postprocessing_difference.ts b/examples-testing/examples/webgpu_postprocessing_difference.ts deleted file mode 100644 index 91cc4cbf5..000000000 --- a/examples-testing/examples/webgpu_postprocessing_difference.ts +++ /dev/null @@ -1,93 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { pass, luminance, saturation } from 'three/tsl'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -const params = { - speed: 0, -}; - -let camera, renderer, renderPipeline; -let timer, mesh, controls; - -init(); - -function init() { - renderer = new THREE.WebGPURenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - renderer.toneMapping = THREE.NeutralToneMapping; - document.body.appendChild(renderer.domElement); - - // - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 100); - camera.position.set(1, 2, 3); - - const scene = new THREE.Scene(); - scene.fog = new THREE.Fog(0x0487e2, 7, 25); - scene.background = new THREE.Color(0x0487e2); - - timer = new THREE.Timer(); - timer.connect(document); - - const texture = new THREE.TextureLoader().load('textures/crate.gif'); - texture.colorSpace = THREE.SRGBColorSpace; - - const geometry = new THREE.BoxGeometry(); - const material = new THREE.MeshBasicMaterial({ map: texture }); - - mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - // post processing - - renderPipeline = new THREE.RenderPipeline(renderer); - - const scenePass = pass(scene, camera); - - const currentTexture = scenePass.getTextureNode(); - const previousTexture = scenePass.getPreviousTextureNode(); - - const frameDiff = previousTexture.sub(currentTexture).abs(); - - const saturationAmount = luminance(frameDiff).mul(1000).clamp(0, 3); - - renderPipeline.outputNode = saturation(currentTexture, saturationAmount); - - // - - controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 2; - controls.maxDistance = 10; - controls.enableDamping = true; - controls.dampingFactor = 0.01; - - window.addEventListener('resize', onWindowResize); - - // - - const gui = renderer.inspector.createParameters('Settings'); - gui.add(params, 'speed', 0, 2); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - controls.update(); - - mesh.rotation.y += timer.getDelta() * 5 * params.speed; - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_postprocessing_dof.ts b/examples-testing/examples/webgpu_postprocessing_dof.ts deleted file mode 100644 index 862c2802b..000000000 --- a/examples-testing/examples/webgpu_postprocessing_dof.ts +++ /dev/null @@ -1,132 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { cubeTexture, positionWorld, oscSine, time, pass, uniform } from 'three/tsl'; -import { dof } from 'three/addons/tsl/display/DepthOfFieldNode.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -// - -let camera, scene, renderer, mesh, controls; - -let width = window.innerWidth; -let height = window.innerHeight; - -let renderPipeline; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(70, width / height, 1, 3500); - camera.position.z = 200; - - scene = new THREE.Scene(); - - const path = 'textures/cube/SwedishRoyalCastle/'; - const format = '.jpg'; - const urls = [ - path + 'px' + format, - path + 'nx' + format, - path + 'py' + format, - path + 'ny' + format, - path + 'pz' + format, - path + 'nz' + format, - ]; - - const xgrid = 14, - ygrid = 9, - zgrid = 14; - const count = xgrid * ygrid * zgrid; - - const textureCube = new THREE.CubeTextureLoader().load(urls); - const cubeTextureNode = cubeTexture(textureCube); - const oscPos = oscSine(positionWorld.div(1000 /* scene distance */).add(time.mul(0.2))); - - const geometry = new THREE.SphereGeometry(60, 20, 10); - const material = new THREE.MeshBasicNodeMaterial(); - material.colorNode = cubeTextureNode.mul(oscPos); - - mesh = new THREE.InstancedMesh(geometry, material, count); - scene.add(mesh); - - const matrix = new THREE.Matrix4(); - - let index = 0; - - for (let i = 0; i < xgrid; i++) { - for (let j = 0; j < ygrid; j++) { - for (let k = 0; k < zgrid; k++) { - const x = 200 * (i - xgrid / 2); - const y = 200 * (j - ygrid / 2); - const z = 200 * (k - zgrid / 2); - - mesh.setMatrixAt(index, matrix.identity().setPosition(x, y, z)); - index++; - } - } - } - - // renderer - - renderer = new THREE.WebGPURenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - const effectController = { - focusDistance: uniform(500), - focalLength: uniform(200), - bokehScale: uniform(10), - }; - - // post processing - - renderPipeline = new THREE.RenderPipeline(renderer); - - const scenePass = pass(scene, camera); - - const scenePassColor = scenePass.getTextureNode().toInspector('Color'); - const scenePassViewZ = scenePass.getViewZNode(); - - const dofPass = dof( - scenePassColor, - scenePassViewZ, - effectController.focusDistance, - effectController.focalLength, - effectController.bokehScale, - ); - - renderPipeline.outputNode = dofPass; - - // controls - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - - window.addEventListener('resize', onWindowResize); - - // gui - - const gui = renderer.inspector.createParameters('Settings'); - gui.add(effectController.focusDistance, 'value', 10.0, 3000.0).name('focus distance'); - gui.add(effectController.focalLength, 'value', 50, 750).name('focal length'); - gui.add(effectController.bokehScale, 'value', 1, 20).name('bokeh scale'); -} - -function onWindowResize() { - width = window.innerWidth; - height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); -} - -function animate() { - controls.update(); - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_postprocessing_dof_basic.ts b/examples-testing/examples/webgpu_postprocessing_dof_basic.ts deleted file mode 100644 index 3ad295247..000000000 --- a/examples-testing/examples/webgpu_postprocessing_dof_basic.ts +++ /dev/null @@ -1,154 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { mix, pass, renderOutput, smoothstep, uniform, vec3 } from 'three/tsl'; -import { boxBlur } from 'three/addons/tsl/display/boxBlur.js'; -import { fxaa } from 'three/addons/tsl/display/FXAANode.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; -import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; -import TWEEN from 'three/addons/libs/tween.module.js'; - -let camera, controls, scene, timer, renderer, model, mixer, raycaster, renderPipeline; - -const pointerCoords = new THREE.Vector2(); -const focusPoint = new THREE.Vector3(1, 1.75, -0.4); -const focusPointView = uniform(vec3()); - -init(); - -async function init() { - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(-6, 5, 6); - - controls = new OrbitControls(camera); - controls.target.set(0, 2, 0); - controls.enableDamping = true; - controls.update(); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x90d5ff); - - raycaster = new THREE.Raycaster(); - - timer = new THREE.Timer(); - - const dracoLoader = new DRACOLoader(); - dracoLoader.setDecoderPath('jsm/libs/draco/gltf/'); - - const loader = new GLTFLoader(); - loader.setDRACOLoader(dracoLoader); - const gltf = await loader.loadAsync('models/gltf/bath_day.glb'); - - model = gltf.scene; - scene.add(model); - - mixer = new THREE.AnimationMixer(model); - const action = mixer.clipAction(gltf.animations[0]); - action.play(); - - // - - const hdrLoader = new UltraHDRLoader(); - const envMap = await hdrLoader.loadAsync('textures/equirectangular/spruit_sunrise_2k.hdr.jpg'); - envMap.mapping = THREE.EquirectangularReflectionMapping; - scene.environmentRotation.y = Math.PI * -0.5; - scene.environment = envMap; - - // renderer - - renderer = new THREE.WebGPURenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - renderer.toneMapping = THREE.NeutralToneMapping; - document.body.appendChild(renderer.domElement); - - // post processing - - renderPipeline = new THREE.RenderPipeline(renderer); - renderPipeline.outputColorTransform = false; - - // DOF uniforms - - const blurSize = uniform(2); // determines the kernel size of the blur - const blurSpread = uniform(4); // determines how far the blur is spread - const minDistance = uniform(1); // all positions at or below minDistance will be completely in focus. - const maxDistance = uniform(3); // all positions at or beyond maxDistance will be completely out of focus. - - // beauty and blur/out-of-focus pass - - const scenePass = pass(scene, camera); - - const scenePassColor = scenePass.getTextureNode().toInspector('Color'); - const scenePassViewZ = scenePass.getViewZNode(); - const scenePassBlurred = boxBlur(scenePassColor, { size: blurSize, separation: blurSpread }); - - // simple DOF from https://lettier.github.io/3d-game-shaders-for-beginners/depth-of-field.html - - const blur = smoothstep(minDistance, maxDistance, scenePassViewZ.sub(focusPointView.z).abs()); - const dofPass = mix(scenePassColor, scenePassBlurred, blur); - - const outputPass = renderOutput(dofPass); - const fxaaPass = fxaa(outputPass); - - renderPipeline.outputNode = fxaaPass; - - // GUI - - const gui = renderer.inspector.createParameters('Settings'); - gui.add(minDistance, 'value', 0, 3).name('min distance'); - gui.add(maxDistance, 'value', 0, 5).name('max distance'); - gui.add(blurSize, 'value', 1, 3, 1).name('blur size'); - gui.add(blurSpread, 'value', 1, 7, 1).name('blur spread'); - - // - - controls.connect(renderer.domElement); - - renderer.domElement.addEventListener('pointerdown', onPointerDown); - - window.addEventListener('resize', onWindowResize); -} - -function onPointerDown(event) { - pointerCoords.set((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1); - - raycaster.setFromCamera(pointerCoords, camera); - - const intersects = raycaster.intersectObject(model); - - if (intersects.length > 0) { - TWEEN.removeAll(); - - new TWEEN.Tween(focusPoint).to(intersects[0].point, 500).easing(TWEEN.Easing.Cubic.InOut).start(); - } -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - TWEEN.update(); - - controls.update(); - - timer.update(); - - mixer.update(timer.getDelta()); - - // since the focus point is expressed in view space, it must be updated on every - // camera change. for simplicity, do this every frame. - - camera.updateMatrixWorld(); - focusPointView.value.copy(focusPoint).applyMatrix4(camera.matrixWorldInverse); - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_postprocessing_fxaa.ts b/examples-testing/examples/webgpu_postprocessing_fxaa.ts deleted file mode 100644 index 24c01a950..000000000 --- a/examples-testing/examples/webgpu_postprocessing_fxaa.ts +++ /dev/null @@ -1,129 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { pass, renderOutput } from 'three/tsl'; -import { fxaa } from 'three/addons/tsl/display/FXAANode.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -const params = { - enabled: true, - animated: false, -}; - -let camera, scene, renderer, timer, group; -let renderPipeline; - -init(); - -async function init() { - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 200); - camera.position.z = 50; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xffffff); - - timer = new THREE.Timer(); - timer.connect(document); - - // - - const hemiLight = new THREE.HemisphereLight(0xffffff, 0x8d8d8d); - hemiLight.position.set(0, 1000, 0); - scene.add(hemiLight); - - const dirLight = new THREE.DirectionalLight(0xffffff, 3); - dirLight.position.set(-3000, 1000, -1000); - scene.add(dirLight); - - // - - group = new THREE.Group(); - - const geometry = new THREE.TetrahedronGeometry(); - const material = new THREE.MeshStandardMaterial({ color: 0xf73232, flatShading: true }); - - const mesh = new THREE.InstancedMesh(geometry, material, 100); - const dummy = new THREE.Object3D(); - - for (let i = 0; i < 100; i++) { - dummy.position.x = Math.random() * 50 - 25; - dummy.position.y = Math.random() * 50 - 25; - dummy.position.z = Math.random() * 50 - 25; - - dummy.scale.setScalar(Math.random() * 2 + 1); - - dummy.rotation.x = Math.random() * Math.PI; - dummy.rotation.y = Math.random() * Math.PI; - dummy.rotation.z = Math.random() * Math.PI; - - dummy.updateMatrix(); - mesh.setMatrixAt(i, dummy.matrix); - } - - group.add(mesh); - scene.add(group); - - renderer = new THREE.WebGPURenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // post processing - - renderPipeline = new THREE.RenderPipeline(renderer); - - // ignore default output color transform ( toneMapping and outputColorSpace ) - // use renderOutput() for control the sequence - - renderPipeline.outputColorTransform = false; - - // scene pass - - const scenePass = pass(scene, camera).toInspector('Color'); - const outputPass = renderOutput(scenePass); - - // FXAA must be computed in sRGB color space (so after tone mapping and color space conversion) - - const fxaaPass = fxaa(outputPass); - renderPipeline.outputNode = fxaaPass; - - // - - window.addEventListener('resize', onWindowResize); - - // - - const gui = renderer.inspector.createParameters('Settings'); - gui.add(params, 'enabled').onChange(value => { - if (value === true) { - renderPipeline.outputNode = fxaaPass; - } else { - renderPipeline.outputNode = outputPass; - } - - renderPipeline.needsUpdate = true; - }); - gui.add(params, 'animated'); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - timer.update(); - - const delta = timer.getDelta(); - - if (params.animated === true) { - group.rotation.y += delta * 0.1; - } - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_postprocessing_godrays.ts b/examples-testing/examples/webgpu_postprocessing_godrays.ts deleted file mode 100644 index 32e619a47..000000000 --- a/examples-testing/examples/webgpu_postprocessing_godrays.ts +++ /dev/null @@ -1,209 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { pass, uniform, float, int, color } from 'three/tsl'; -import { godrays } from 'three/addons/tsl/display/GodraysNode.js'; -import { bilateralBlur } from 'three/addons/tsl/display/BilateralBlurNode.js'; -import { depthAwareBlend } from 'three/addons/tsl/display/depthAwareBlend.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, controls, scene, renderer, renderPipeline; - -const params = { - enabledBlur: true, -}; - -init(); - -async function init() { - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000); - camera.position.set(-175, 50, 0); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x000000); - - // asset - - const loader = new GLTFLoader(); - const gltf = await loader.loadAsync('models/gltf/godrays_demo.glb'); - scene.add(gltf.scene); - - const pillars = gltf.scene.getObjectByName('concrete'); - pillars.material = new THREE.MeshStandardMaterial({ - color: 0x333333, - }); - - const base = gltf.scene.getObjectByName('base'); - base.material = new THREE.MeshStandardMaterial({ - color: 0x333333, - side: THREE.DoubleSide, - }); - - // lights - - const lightPos = new THREE.Vector3(0, 50, 0); - const lightSphereMaterial = new THREE.MeshBasicMaterial({ - color: 0xffffff, - }); - const lightSphere = new THREE.Mesh(new THREE.SphereGeometry(0.5, 16, 16), lightSphereMaterial); - lightSphere.position.copy(lightPos); - scene.add(lightSphere); - - scene.add(new THREE.AmbientLight(0xcccccc, 0.4)); - - const pointLight = new THREE.PointLight(0xf6287d, 10000); - pointLight.castShadow = true; - pointLight.shadow.bias = -0.00001; - pointLight.shadow.mapSize.width = 2048; - pointLight.shadow.mapSize.height = 2048; - pointLight.position.copy(lightPos); - scene.add(pointLight); - - setupBackdrop(); - - // shadow setup - - scene.traverse(obj => { - if (obj.isMesh === true) { - obj.castShadow = true; - obj.receiveShadow = true; - } - }); - - lightSphere.castShadow = false; - lightSphere.receiveShadow = false; - - // renderer - - renderer = new THREE.WebGPURenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - renderer.shadowMap.enabled = true; - document.body.appendChild(renderer.domElement); - - // post processing - - renderPipeline = new THREE.RenderPipeline(renderer); - - // beauty - - const scenePass = pass(scene, camera); - const scenePassColor = scenePass.getTextureNode('output'); - const scenePassDepth = scenePass.getTextureNode('depth'); - - // godrays - - const godraysPass = godrays(scenePassDepth, camera, pointLight); - const godraysPassColor = godraysPass.getTextureNode(); - - // blur - - const blurPass = bilateralBlur(godraysPassColor); - const blurPassColor = blurPass.getTextureNode(); - - // composite - - const blendColor = uniform(color(0xf6287d)); - const edgeRadius = uniform(int(2)); - const edgeStrength = uniform(float(2)); - - const outputBlurred = depthAwareBlend(scenePassColor, blurPassColor, scenePassDepth, camera, { - blendColor, - edgeRadius, - edgeStrength, - }); - const outputRaw = depthAwareBlend(scenePassColor, godraysPassColor, scenePassDepth, camera, { - blendColor, - edgeRadius, - edgeStrength, - }); - - renderPipeline.outputNode = outputBlurred; - - // GUI - - const gui = renderer.inspector.createParameters('Settings'); - const godraysFolder = gui.addFolder('Godrays'); - godraysFolder.add(godraysPass.raymarchSteps, 'value', 24, 120).step(1).name('raymarch steps'); - godraysFolder.add(godraysPass.density, 'value', 0, 1).name('density'); - godraysFolder.add(godraysPass.maxDensity, 'value', 0, 1).name('max density'); - godraysFolder.add(godraysPass.distanceAttenuation, 'value', 0, 5).name('distance attenuation'); - - const compositeFolder = gui.addFolder('composite'); - compositeFolder.add(edgeRadius, 'value', 0, 5, 1).name('edge radius'); - compositeFolder.add(edgeStrength, 'value', 0, 5).name('edge strength'); - - const blurFolder = gui.addFolder('blur'); - blurFolder - .add(params, 'enabledBlur') - .name('enabled') - .onChange(value => { - if (value === true) { - renderPipeline.outputNode = outputBlurred; - } else { - renderPipeline.outputNode = outputRaw; - } - - renderPipeline.needsUpdate = true; - }); - - // - - controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, 0.5, 0); - controls.enableDamping = true; - controls.maxDistance = 200; - controls.update(); - - window.addEventListener('resize', onWindowResize); -} - -function setupBackdrop() { - const backdropDistance = 200; - // Add backdrop walls `backdropDistance` units away from the origin - const backdropGeometry = new THREE.PlaneGeometry(400, 200); - const backdropMaterial = new THREE.MeshBasicMaterial({ - color: 0x000000, - side: THREE.DoubleSide, - }); - const backdropLeft = new THREE.Mesh(backdropGeometry, backdropMaterial); - backdropLeft.position.set(-backdropDistance, 100, 0); - backdropLeft.rotateY(Math.PI / 2); - scene.add(backdropLeft); - - const backdropRight = new THREE.Mesh(backdropGeometry, backdropMaterial); - backdropRight.position.set(backdropDistance, 100, 0); - backdropRight.rotateY(Math.PI / 2); - scene.add(backdropRight); - - const backdropFront = new THREE.Mesh(backdropGeometry, backdropMaterial); - backdropFront.position.set(0, 100, -backdropDistance); - scene.add(backdropFront); - - const backdropBack = new THREE.Mesh(backdropGeometry, backdropMaterial); - backdropBack.position.set(0, 100, backdropDistance); - scene.add(backdropBack); - - const backdropTop = new THREE.Mesh(backdropGeometry, backdropMaterial); - backdropTop.position.set(0, 200, 0); - backdropTop.rotateX(Math.PI / 2); - backdropTop.scale.set(3, 6, 1); - scene.add(backdropTop); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - controls.update(); - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_postprocessing_lensflare.ts b/examples-testing/examples/webgpu_postprocessing_lensflare.ts deleted file mode 100644 index 39b1e5f6e..000000000 --- a/examples-testing/examples/webgpu_postprocessing_lensflare.ts +++ /dev/null @@ -1,135 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { pass, mrt, output, emissive, uniform } from 'three/tsl'; -import { bloom } from 'three/addons/tsl/display/BloomNode.js'; -import { lensflare } from 'three/addons/tsl/display/LensflareNode.js'; -import { gaussianBlur } from 'three/addons/tsl/display/GaussianBlurNode.js'; - -import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer, controls; -let renderPipeline; - -init(); - -async function init() { - // - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(0, 0.5, -0.5); - - scene = new THREE.Scene(); - - const texture = await new UltraHDRLoader().loadAsync('textures/equirectangular/ice_planet_close.jpg'); - - texture.mapping = THREE.EquirectangularReflectionMapping; - - scene.background = texture; - scene.environment = texture; - - scene.backgroundIntensity = 2; - scene.environmentIntensity = 15; - - // model - - const loader = new GLTFLoader(); - const gltf = await loader.loadAsync('models/gltf/space_ship_hallway.glb'); - - const object = gltf.scene; - - const aabb = new THREE.Box3().setFromObject(object); - const center = aabb.getCenter(new THREE.Vector3()); - - object.position.x += object.position.x - center.x; - object.position.y += object.position.y - center.y; - object.position.z += object.position.z - center.z; - - scene.add(object); - - // - - renderer = new THREE.WebGPURenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(render); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // - - const scenePass = pass(scene, camera); - scenePass.setMRT( - mrt({ - output, - emissive, - }), - ); - - const outputPass = scenePass.getTextureNode().toInspector('Color'); - const emissivePass = scenePass.getTextureNode('emissive').toInspector('Emissive'); - - const bloomPass = bloom(emissivePass, 1, 1).toInspector('Bloom'); - - const threshold = uniform(0.5); - const ghostAttenuationFactor = uniform(25); - const ghostSpacing = uniform(0.25); - - const flarePass = lensflare(bloomPass, { - threshold, - ghostAttenuationFactor, - ghostSpacing, - }); - - const blurPass = gaussianBlur(flarePass, 8); // optional (blurring produces better flare quality but also adds some overhead) - - renderPipeline = new THREE.RenderPipeline(renderer); - renderPipeline.outputNode = outputPass.add(bloomPass).add(blurPass); - - // - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - controls.enablePan = false; - controls.enableZoom = false; - controls.target.copy(camera.position); - controls.target.z -= 0.01; - controls.update(); - - window.addEventListener('resize', onWindowResize); - - // - - const gui = renderer.inspector.createParameters('Settings'); - - const bloomFolder = gui.addFolder('bloom'); - bloomFolder.add(bloomPass.strength, 'value', 0.0, 2.0).name('strength'); - bloomFolder.add(bloomPass.radius, 'value', 0.0, 1.0).name('radius'); - - const lensflareFolder = gui.addFolder('lensflare'); - lensflareFolder.add(threshold, 'value', 0.0, 1.0).name('threshold'); - lensflareFolder.add(ghostAttenuationFactor, 'value', 10.0, 50.0).name('attenuation'); - lensflareFolder.add(ghostSpacing, 'value', 0.0, 0.3).name('spacing'); - - const toneMappingFolder = gui.addFolder('tone mapping'); - toneMappingFolder.add(renderer, 'toneMappingExposure', 0.1, 2).name('exposure'); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function render() { - controls.update(); - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_postprocessing_masking.ts b/examples-testing/examples/webgpu_postprocessing_masking.ts deleted file mode 100644 index b234abc66..000000000 --- a/examples-testing/examples/webgpu_postprocessing_masking.ts +++ /dev/null @@ -1,88 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { pass, texture } from 'three/tsl'; -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, renderPipeline, renderer; -let box, torus; - -init(); - -function init() { - // scene - - const baseScene = new THREE.Scene(); - baseScene.background = new THREE.Color(0xe0e0e0); - - const maskScene1 = new THREE.Scene(); - box = new THREE.Mesh(new THREE.BoxGeometry(4, 4, 4)); - maskScene1.add(box); - - const maskScene2 = new THREE.Scene(); - torus = new THREE.Mesh(new THREE.TorusGeometry(3, 1, 16, 32)); - maskScene2.add(torus); - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.z = 10; - - // textures - - const texture1 = new THREE.TextureLoader().load('textures/758px-Canestra_di_frutta_(Caravaggio).jpg'); - texture1.colorSpace = THREE.SRGBColorSpace; - texture1.minFilter = THREE.LinearFilter; - texture1.generateMipmaps = false; - texture1.flipY = false; - - const texture2 = new THREE.TextureLoader().load('textures/2294472375_24a3b8ef46_o.jpg'); - texture2.colorSpace = THREE.SRGBColorSpace; - texture2.flipY = false; - - // renderer - - renderer = new THREE.WebGPURenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - window.addEventListener('resize', onWindowResize); - - // post processing - - const base = pass(baseScene, camera); - const sceneMask1 = pass(maskScene1, camera).a; - const sceneMask2 = pass(maskScene2, camera).a; - - let compose = base; - compose = sceneMask1.mix(compose, texture(texture1)); - compose = sceneMask2.mix(compose, texture(texture2)); - - renderPipeline = new THREE.RenderPipeline(renderer); - renderPipeline.outputNode = compose; -} - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); -} - -function animate() { - const time = performance.now() * 0.001 + 6000; - - box.position.x = Math.cos(time / 1.5) * 2; - box.position.y = Math.sin(time) * 2; - box.rotation.x = time; - box.rotation.y = time / 2; - - torus.position.x = Math.cos(time) * 2; - torus.position.y = Math.sin(time / 1.5) * 2; - torus.rotation.x = time; - torus.rotation.y = time / 2; - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_postprocessing_motion_blur.ts b/examples-testing/examples/webgpu_postprocessing_motion_blur.ts deleted file mode 100644 index 5ae3d9661..000000000 --- a/examples-testing/examples/webgpu_postprocessing_motion_blur.ts +++ /dev/null @@ -1,199 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { pass, texture, uniform, output, mrt, velocity, uv, screenUV } from 'three/tsl'; -import { motionBlur } from 'three/addons/tsl/display/MotionBlur.js'; - -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer; -let boxLeft, boxRight, model, mixer, timer; -let renderPipeline; -let controls; - -const params = { - speed: 1.0, -}; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.25, 30); - camera.position.set(0, 1.5, 4.5); - - scene = new THREE.Scene(); - scene.fog = new THREE.Fog(0x0487e2, 7, 25); - - const sunLight = new THREE.DirectionalLight(0xffe499, 5); - sunLight.castShadow = true; - sunLight.shadow.camera.near = 0.1; - sunLight.shadow.camera.far = 10; - sunLight.shadow.camera.right = 2; - sunLight.shadow.camera.left = -2; - sunLight.shadow.camera.top = 2; - sunLight.shadow.camera.bottom = -2; - sunLight.shadow.mapSize.width = 1024; - sunLight.shadow.mapSize.height = 1024; - sunLight.position.set(4, 4, 2); - - const waterAmbientLight = new THREE.HemisphereLight(0x333366, 0x74ccf4, 5); - const skyAmbientLight = new THREE.HemisphereLight(0x74ccf4, 0, 1); - - scene.add(sunLight); - scene.add(skyAmbientLight); - scene.add(waterAmbientLight); - - timer = new THREE.Timer(); - timer.connect(document); - - // animated model - - const loader = new GLTFLoader(); - loader.load('models/gltf/Xbot.glb', function (gltf) { - model = gltf.scene; - - model.rotation.y = Math.PI / 2; - - model.traverse(function (child) { - if (child.isMesh) { - child.castShadow = true; - child.receiveShadow = true; - } - }); - - mixer = new THREE.AnimationMixer(model); - - const action = mixer.clipAction(gltf.animations[3]); - action.play(); - - scene.add(model); - }); - - // textures - - const textureLoader = new THREE.TextureLoader(); - - const floorColor = textureLoader.load('textures/floors/FloorsCheckerboard_S_Diffuse.jpg'); - floorColor.wrapS = THREE.RepeatWrapping; - floorColor.wrapT = THREE.RepeatWrapping; - floorColor.colorSpace = THREE.SRGBColorSpace; - - const floorNormal = textureLoader.load('textures/floors/FloorsCheckerboard_S_Normal.jpg'); - floorNormal.wrapS = THREE.RepeatWrapping; - floorNormal.wrapT = THREE.RepeatWrapping; - - // floor - - const floorUV = uv().mul(5); - - const floorMaterial = new THREE.MeshPhongNodeMaterial(); - floorMaterial.colorNode = texture(floorColor, floorUV); - - const floor = new THREE.Mesh(new THREE.BoxGeometry(15, 0.001, 15), floorMaterial); - floor.receiveShadow = true; - - floor.position.set(0, 0, 0); - scene.add(floor); - - const walls = new THREE.Mesh( - new THREE.BoxGeometry(15, 15, 15), - new THREE.MeshPhongNodeMaterial({ colorNode: floorMaterial.colorNode, side: THREE.BackSide }), - ); - scene.add(walls); - - const map = new THREE.TextureLoader().load('textures/uv_grid_opengl.jpg'); - map.colorSpace = THREE.SRGBColorSpace; - - const geometry = new THREE.TorusGeometry(0.8); - const material = new THREE.MeshBasicMaterial({ map }); - - boxRight = new THREE.Mesh(geometry, material); - boxRight.position.set(3.5, 1.5, -4); - scene.add(boxRight); - - boxLeft = new THREE.Mesh(geometry, material); - boxLeft.position.set(-3.5, 1.5, -4); - scene.add(boxLeft); - - // renderer - - renderer = new THREE.WebGPURenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.shadowMap.enabled = true; - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 1; - controls.maxDistance = 10; - controls.maxPolarAngle = Math.PI / 2; - controls.autoRotate = true; - controls.autoRotateSpeed = 1; - controls.target.set(0, 1, 0); - controls.enableDamping = true; - controls.dampingFactor = 0.05; - controls.update(); - - // post-processing - - const blurAmount = uniform(1); - - const scenePass = pass(scene, camera); - - scenePass.setMRT( - mrt({ - output, - velocity, - }), - ); - - const beauty = scenePass.getTextureNode().toInspector('Color'); - const vel = scenePass.getTextureNode('velocity').toInspector('Velocity').mul(blurAmount); - - const mBlur = motionBlur(beauty, vel); - - const vignette = screenUV.distance(0.5).remap(0.6, 1).mul(2).clamp().oneMinus(); - - renderPipeline = new THREE.RenderPipeline(renderer); - renderPipeline.outputNode = mBlur.mul(vignette); - - // - - const gui = renderer.inspector.createParameters('Motion Blur Settings'); - gui.add(controls, 'autoRotate'); - gui.add(blurAmount, 'value', 0, 3).name('blur amount'); - gui.add(params, 'speed', 0, 2); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - controls.update(); - - const delta = timer.getDelta(); - const speed = params.speed; - - boxRight.rotation.y += delta * 4 * speed; - boxLeft.scale.setScalar(1 + Math.sin(timer.getElapsed() * 10 * speed) * 0.2); - - if (model) { - mixer.update(delta * speed); - } - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_postprocessing_outline.ts b/examples-testing/examples/webgpu_postprocessing_outline.ts deleted file mode 100644 index f87415b74..000000000 --- a/examples-testing/examples/webgpu_postprocessing_outline.ts +++ /dev/null @@ -1,232 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { pass, uniform, time, oscSine } from 'three/tsl'; -import { outline } from 'three/addons/tsl/display/OutlineNode.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { OBJLoader } from 'three/addons/loaders/OBJLoader.js'; - -let camera, scene, renderer, controls; -let renderPipeline, outlinePass; - -let selectedObjects = []; - -const raycaster = new THREE.Raycaster(); -const mouse = new THREE.Vector2(); - -const obj3d = new THREE.Object3D(); -const group = new THREE.Group(); - -init(); - -function init() { - const width = window.innerWidth; - const height = window.innerHeight; - - renderer = new THREE.WebGPURenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(width, height); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - renderer.shadowMap.enabled = true; - document.body.appendChild(renderer.domElement); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 100); - camera.position.set(0, 0, 8); - - controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 5; - controls.maxDistance = 20; - controls.enablePan = false; - controls.enableDamping = true; - controls.dampingFactor = 0.05; - - // - - scene.add(new THREE.AmbientLight(0xaaaaaa, 0.6)); - - const light = new THREE.DirectionalLight(0xddffdd, 2); - light.position.set(5, 5, 5); - light.castShadow = true; - light.shadow.mapSize.width = 2048; - light.shadow.mapSize.height = 2048; - - const d = 10; - - light.shadow.camera.left = -d; - light.shadow.camera.right = d; - light.shadow.camera.top = d; - light.shadow.camera.bottom = -d; - light.shadow.camera.far = 25; - - scene.add(light); - - // model - - const loader = new OBJLoader(); - loader.load('models/obj/tree.obj', function (object) { - let scale = 1.0; - - object.traverse(function (child) { - if (child instanceof THREE.Mesh) { - child.geometry.center(); - child.geometry.computeBoundingSphere(); - scale = 0.2 * child.geometry.boundingSphere.radius; - - const phongMaterial = new THREE.MeshPhongMaterial({ - color: 0xffffff, - specular: 0x111111, - shininess: 5, - }); - child.material = phongMaterial; - child.receiveShadow = true; - child.castShadow = true; - } - }); - - object.position.y = 1; - object.scale.divideScalar(scale); - obj3d.add(object); - }); - - scene.add(group); - - group.add(obj3d); - - // - - const geometry = new THREE.SphereGeometry(3, 48, 24); - - for (let i = 0; i < 20; i++) { - const material = new THREE.MeshLambertMaterial(); - material.color.setHSL(Math.random(), 1.0, 0.3); - - const mesh = new THREE.Mesh(geometry, material); - mesh.position.x = Math.random() * 4 - 2; - mesh.position.y = Math.random() * 4 - 2; - mesh.position.z = Math.random() * 4 - 2; - mesh.receiveShadow = true; - mesh.castShadow = true; - mesh.scale.multiplyScalar(Math.random() * 0.3 + 0.1); - group.add(mesh); - } - - const floorMaterial = new THREE.MeshLambertMaterial({ side: THREE.DoubleSide }); - - const floorGeometry = new THREE.PlaneGeometry(12, 12); - const floorMesh = new THREE.Mesh(floorGeometry, floorMaterial); - floorMesh.rotation.x -= Math.PI * 0.5; - floorMesh.position.y -= 1.5; - group.add(floorMesh); - floorMesh.receiveShadow = true; - - const torusGeometry = new THREE.TorusGeometry(1, 0.3, 16, 100); - const torusMaterial = new THREE.MeshPhongMaterial({ color: 0xffaaff }); - const torus = new THREE.Mesh(torusGeometry, torusMaterial); - torus.position.z = -4; - group.add(torus); - torus.receiveShadow = true; - torus.castShadow = true; - - // outline pass - - const edgeStrength = uniform(3.0); - const edgeGlow = uniform(0.0); - const edgeThickness = uniform(1.0); - const pulsePeriod = uniform(0); - const visibleEdgeColor = uniform(new THREE.Color(0xffffff)); - const hiddenEdgeColor = uniform(new THREE.Color(0x4e3636)); - - outlinePass = outline(scene, camera, { - selectedObjects, - edgeGlow, - edgeThickness, - }); - - const { visibleEdge, hiddenEdge } = outlinePass; - - const period = time.div(pulsePeriod).mul(2); - const osc = oscSine(period).mul(0.5).add(0.5); // osc [ 0.5, 1.0 ] - - const outlineColor = visibleEdge.mul(visibleEdgeColor).add(hiddenEdge.mul(hiddenEdgeColor)).mul(edgeStrength); - const outlinePulse = pulsePeriod.greaterThan(0).select(outlineColor.mul(osc), outlineColor); - - // postprocessing - - const scenePass = pass(scene, camera).toInspector('Color'); - - renderPipeline = new THREE.RenderPipeline(renderer); - renderPipeline.outputNode = outlinePulse.add(scenePass); - - // gui - - const gui = renderer.inspector.createParameters('Settings'); - gui.add(edgeStrength, 'value', 0.01, 10).name('edgeStrength'); - gui.add(edgeGlow, 'value', 0.0, 1).name('edgeGlow'); - gui.add(edgeThickness, 'value', 1, 4).name('edgeThickness'); - gui.add(pulsePeriod, 'value', 0.0, 5).name('pulsePeriod'); - gui.addColor({ color: visibleEdgeColor.value.getHex(THREE.SRGBColorSpace) }, 'color') - .onChange(value => { - visibleEdgeColor.value.set(value); - }) - .name('visibleEdgeColor'); - gui.addColor({ color: hiddenEdgeColor.value.getHex(THREE.SRGBColorSpace) }, 'color') - .onChange(value => { - hiddenEdgeColor.value.set(value); - }) - .name('hiddenEdgeColor'); - - // - - window.addEventListener('resize', onWindowResize); - - renderer.domElement.style.touchAction = 'none'; - renderer.domElement.addEventListener('pointermove', onPointerMove); - - function onPointerMove(event) { - if (event.isPrimary === false) return; - - mouse.x = (event.clientX / window.innerWidth) * 2 - 1; - mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; - - checkIntersection(); - } - - function addSelectedObject(object) { - selectedObjects = []; - selectedObjects.push(object); - } - - function checkIntersection() { - raycaster.setFromCamera(mouse, camera); - - const intersects = raycaster.intersectObject(scene, true); - - if (intersects.length > 0) { - const selectedObject = intersects[0].object; - addSelectedObject(selectedObject); - outlinePass.selectedObjects = selectedObjects; - } else { - // outlinePass.selectedObjects = []; - } - } -} - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); -} - -function animate() { - controls.update(); - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_postprocessing_pixel.ts b/examples-testing/examples/webgpu_postprocessing_pixel.ts deleted file mode 100644 index a3d58f203..000000000 --- a/examples-testing/examples/webgpu_postprocessing_pixel.ts +++ /dev/null @@ -1,237 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { uniform } from 'three/tsl'; -import { pixelationPass } from 'three/addons/tsl/display/PixelationPassNode.js'; - -let camera, scene, renderer, renderPipeline, crystalMesh, timer; -let effectController; - -init(); - -function init() { - const aspectRatio = window.innerWidth / window.innerHeight; - - camera = new THREE.OrthographicCamera(-aspectRatio, aspectRatio, 1, -1, 0.1, 10); - camera.position.y = 2 * Math.tan(Math.PI / 6); - camera.position.z = 2; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x151729); - - timer = new THREE.Timer(); - timer.connect(document); - - // textures - - const loader = new THREE.TextureLoader(); - const texChecker = pixelTexture(loader.load('textures/checker.png')); - const texChecker2 = pixelTexture(loader.load('textures/checker.png')); - texChecker.repeat.set(3, 3); - texChecker2.repeat.set(1.5, 1.5); - - // meshes - - const boxMaterial = new THREE.MeshPhongMaterial({ map: texChecker2 }); - - function addBox(boxSideLength, x, z, rotation) { - const mesh = new THREE.Mesh(new THREE.BoxGeometry(boxSideLength, boxSideLength, boxSideLength), boxMaterial); - mesh.castShadow = true; - mesh.receiveShadow = true; - mesh.rotation.y = rotation; - mesh.position.y = boxSideLength / 2; - mesh.position.set(x, boxSideLength / 2 + 0.0001, z); - scene.add(mesh); - return mesh; - } - - addBox(0.4, 0, 0, Math.PI / 4); - addBox(0.5, -0.5, -0.5, Math.PI / 4); - - const planeSideLength = 2; - const planeMesh = new THREE.Mesh( - new THREE.PlaneGeometry(planeSideLength, planeSideLength), - new THREE.MeshPhongMaterial({ map: texChecker }), - ); - planeMesh.receiveShadow = true; - planeMesh.rotation.x = -Math.PI / 2; - scene.add(planeMesh); - - const radius = 0.2; - const geometry = new THREE.IcosahedronGeometry(radius); - crystalMesh = new THREE.Mesh( - geometry, - new THREE.MeshPhongMaterial({ - color: 0x68b7e9, - emissive: 0x4f7e8b, - shininess: 10, - specular: 0xffffff, - }), - ); - crystalMesh.receiveShadow = true; - crystalMesh.castShadow = true; - scene.add(crystalMesh); - - // lights - - scene.add(new THREE.AmbientLight(0x757f8e, 3)); - - const directionalLight = new THREE.DirectionalLight(0xfffecd, 1.5); - directionalLight.position.set(100, 100, 100); - directionalLight.castShadow = true; - directionalLight.shadow.mapSize.set(2048, 2048); - scene.add(directionalLight); - - const spotLight = new THREE.SpotLight(0xffc100, 10, 10, Math.PI / 16, 0.02, 2); - spotLight.position.set(2, 2, 0); - const target = spotLight.target; - scene.add(target); - target.position.set(0, 0, 0); - spotLight.castShadow = true; - scene.add(spotLight); - - renderer = new THREE.WebGPURenderer(); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - renderer.shadowMap.enabled = true; - renderer.shadowMap.type = THREE.BasicShadowMap; - document.body.appendChild(renderer.domElement); - - effectController = { - pixelSize: uniform(6), - normalEdgeStrength: uniform(0.3), - depthEdgeStrength: uniform(0.4), - pixelAlignedPanning: true, - }; - - renderPipeline = new THREE.RenderPipeline(renderer); - const scenePass = pixelationPass( - scene, - camera, - effectController.pixelSize, - effectController.normalEdgeStrength, - effectController.depthEdgeStrength, - ); - renderPipeline.outputNode = scenePass; - - window.addEventListener('resize', onWindowResize); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.maxZoom = 2; - - // gui - - const gui = renderer.inspector.createParameters('Settings'); - gui.add(effectController.pixelSize, 'value', 1, 16, 1).name('Pixel Size'); - gui.add(effectController.normalEdgeStrength, 'value', 0, 2, 0.05).name('Normal Edge Strength'); - gui.add(effectController.depthEdgeStrength, 'value', 0, 1, 0.05).name('Depth Edge Strength'); - gui.add(effectController, 'pixelAlignedPanning'); -} - -function onWindowResize() { - const aspectRatio = window.innerWidth / window.innerHeight; - camera.left = -aspectRatio; - camera.right = aspectRatio; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - const t = timer.getElapsed(); - - crystalMesh.material.emissiveIntensity = Math.sin(t * 3) * 0.5 + 0.5; - crystalMesh.position.y = 0.7 + Math.sin(t * 2) * 0.05; - crystalMesh.rotation.y = stopGoEased(t, 2, 4) * 2 * Math.PI; - - const rendererSize = renderer.getSize(new THREE.Vector2()); - const aspectRatio = rendererSize.x / rendererSize.y; - - if (effectController.pixelAlignedPanning) { - const pixelSize = effectController.pixelSize.value; - - pixelAlignFrustum( - camera, - aspectRatio, - Math.floor(rendererSize.x / pixelSize), - Math.floor(rendererSize.y / pixelSize), - ); - } else if (camera.left != -aspectRatio || camera.top != 1.0) { - // Reset the Camera Frustum if it has been modified - camera.left = -aspectRatio; - camera.right = aspectRatio; - camera.top = 1.0; - camera.bottom = -1.0; - camera.updateProjectionMatrix(); - } - - renderPipeline.render(); -} - -// Helper functions - -function pixelTexture(texture) { - texture.minFilter = THREE.NearestFilter; - texture.magFilter = THREE.NearestFilter; - texture.generateMipmaps = false; - texture.wrapS = THREE.RepeatWrapping; - texture.wrapT = THREE.RepeatWrapping; - texture.colorSpace = THREE.SRGBColorSpace; - return texture; -} - -function easeInOutCubic(x) { - return x ** 2 * 3 - x ** 3 * 2; -} - -function linearStep(x, edge0, edge1) { - const w = edge1 - edge0; - const m = 1 / w; - const y0 = -m * edge0; - return THREE.MathUtils.clamp(y0 + m * x, 0, 1); -} - -function stopGoEased(x, downtime, period) { - const cycle = (x / period) | 0; - const tween = x - cycle * period; - const linStep = easeInOutCubic(linearStep(tween, downtime, period)); - return cycle + linStep; -} - -function pixelAlignFrustum(camera, aspectRatio, pixelsPerScreenWidth, pixelsPerScreenHeight) { - // 0. Get Pixel Grid Units - const worldScreenWidth = (camera.right - camera.left) / camera.zoom; - const worldScreenHeight = (camera.top - camera.bottom) / camera.zoom; - const pixelWidth = worldScreenWidth / pixelsPerScreenWidth; - const pixelHeight = worldScreenHeight / pixelsPerScreenHeight; - - // 1. Project the current camera position along its local rotation bases - const camPos = new THREE.Vector3(); - camera.getWorldPosition(camPos); - const camRot = new THREE.Quaternion(); - camera.getWorldQuaternion(camRot); - const camRight = new THREE.Vector3(1.0, 0.0, 0.0).applyQuaternion(camRot); - const camUp = new THREE.Vector3(0.0, 1.0, 0.0).applyQuaternion(camRot); - const camPosRight = camPos.dot(camRight); - const camPosUp = camPos.dot(camUp); - - // 2. Find how far along its position is along these bases in pixel units - const camPosRightPx = camPosRight / pixelWidth; - const camPosUpPx = camPosUp / pixelHeight; - - // 3. Find the fractional pixel units and convert to world units - const fractX = camPosRightPx - Math.round(camPosRightPx); - const fractY = camPosUpPx - Math.round(camPosUpPx); - - // 4. Add fractional world units to the left/right top/bottom to align with the pixel grid - camera.left = -aspectRatio - fractX * pixelWidth; - camera.right = aspectRatio - fractX * pixelWidth; - camera.top = 1.0 - fractY * pixelHeight; - camera.bottom = -1.0 - fractY * pixelHeight; - camera.updateProjectionMatrix(); -} diff --git a/examples-testing/examples/webgpu_postprocessing_radial_blur.ts b/examples-testing/examples/webgpu_postprocessing_radial_blur.ts deleted file mode 100644 index f358421e3..000000000 --- a/examples-testing/examples/webgpu_postprocessing_radial_blur.ts +++ /dev/null @@ -1,149 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { float, int, pass, uniform } from 'three/tsl'; -import { radialBlur } from 'three/addons/tsl/display/radialBlur.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -const params = { - enabled: true, - animated: true, -}; - -let camera, scene, renderer, timer, group; -let renderPipeline; - -init(); - -async function init() { - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 200); - camera.position.z = 50; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x000000); - - timer = new THREE.Timer(); - - // - - const hemiLight = new THREE.HemisphereLight(0xffffff, 0x8d8d8d); - hemiLight.position.set(0, 1000, 0); - scene.add(hemiLight); - - const pointLight = new THREE.PointLight(0xffffff, 1000); - pointLight.position.set(0, 0, 0); - scene.add(pointLight); - - // - - group = new THREE.Group(); - - const geometry = new THREE.TetrahedronGeometry(); - const material = new THREE.MeshStandardMaterial({ flatShading: true }); - - const mesh = new THREE.InstancedMesh(geometry, material, 100); - const dummy = new THREE.Object3D(); - const col = new THREE.Color(); - const center = new THREE.Vector2(); - - for (let i = 0; i < mesh.count; i++) { - dummy.position.x = Math.random() * 50 - 25; - dummy.position.y = Math.random() * 50 - 25; - dummy.position.z = Math.random() * 50 - 25; - - center.set(dummy.position.x, dummy.position.y); - - // make sure tetrahedrons are not positioned at the origin - - if (center.length() < 6) { - center.normalize().multiplyScalar(6); - dummy.position.x = center.x; - dummy.position.y = center.y; - } - - dummy.scale.setScalar(Math.random() * 2 + 1); - - dummy.rotation.x = Math.random() * Math.PI; - dummy.rotation.y = Math.random() * Math.PI; - dummy.rotation.z = Math.random() * Math.PI; - - dummy.updateMatrix(); - mesh.setMatrixAt(i, dummy.matrix); - - col.setHSL(0.55 + (i / mesh.count) * 0.15, 1, 0.2); - - mesh.setColorAt(i, col); - } - - group.add(mesh); - scene.add(group); - - renderer = new THREE.WebGPURenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - renderer.toneMapping = THREE.NeutralToneMapping; - document.body.appendChild(renderer.domElement); - - // post processing - - renderPipeline = new THREE.RenderPipeline(renderer); - - const scenePass = pass(scene, camera); - - const weightUniform = uniform(float(0.9)); - const decayUniform = uniform(float(0.95)); - const exposureUniform = uniform(int(5)); - const countUniform = uniform(int(32)); - - const blurPass = radialBlur(scenePass, { - weight: weightUniform, - decay: decayUniform, - count: countUniform, - exposure: exposureUniform, - }); - - renderPipeline.outputNode = blurPass; - - // - - window.addEventListener('resize', onWindowResize); - - // - - const gui = renderer.inspector.createParameters('Settings'); - gui.add(weightUniform, 'value', 0, 1).name('weight'); - gui.add(decayUniform, 'value', 0, 1).name('decay'); - gui.add(countUniform, 'value', 16, 64, 1).name('sample count'); - gui.add(exposureUniform, 'value', 1, 10).name('exposure'); - gui.add(params, 'enabled').onChange(value => { - if (value === true) { - renderPipeline.outputNode = blurPass; - } else { - renderPipeline.outputNode = scenePass; - } - - renderPipeline.needsUpdate = true; - }); - gui.add(params, 'animated'); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - timer.update(); - const delta = timer.getDelta(); - - if (params.animated === true) { - group.rotation.y += delta * 0.1; - } - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_postprocessing_retro.ts b/examples-testing/examples/webgpu_postprocessing_retro.ts deleted file mode 100644 index b9958352f..000000000 --- a/examples-testing/examples/webgpu_postprocessing_retro.ts +++ /dev/null @@ -1,370 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { - pass, - mix, - mul, - oneMinus, - positionLocal, - smoothstep, - texture, - time, - rotateUV, - Fn, - uv, - vec2, - vec3, - vec4, - uniform, - posterize, - floor, - float, - sin, - fract, - dot, - step, - color, - normalWorld, - length, - atan, - replaceDefaultUV, - screenSize, -} from 'three/tsl'; -import { retroPass } from 'three/addons/tsl/display/RetroPassNode.js'; -import { bayerDither } from 'three/addons/tsl/math/Bayer.js'; -import { scanlines, vignette, colorBleeding, barrelUV } from 'three/addons/tsl/display/CRT.js'; -import { circle } from 'three/addons/tsl/display/Shape.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer, renderPipeline, controls; -let environment; -let currentModel; - -init(); - -async function init() { - camera = new THREE.PerspectiveCamera(25, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(8, 5, 20); - - scene = new THREE.Scene(); - - // PS1-style background: gradient sky with simple stars - - const ps1Background = Fn(() => { - // Flip Y coordinate for correct orientation - const flippedY = normalWorld.y.negate(); - const skyUV = flippedY.mul(0.5).add(0.5); - - // Simple gradient sky (dark blue at top to purple/orange at horizon) - const topColor = color(0x000033); // dark blue night sky - const midColor = color(0x330066); // purple - const horizonColor = color(0x663322); // warm orange/brown horizon - - // Two-step gradient (inverted - top is dark, horizon is warm) - const skyGradient = mix( - horizonColor, - mix(midColor, topColor, skyUV.smoothstep(0.4, 0.9)), - skyUV.smoothstep(0.0, 0.4), - ); - - // PS1-style "stars" using spherical coordinates - const longitude = atan(normalWorld.x, normalWorld.z); - const latitude = flippedY.asin(); // Use flipped Y for latitude too - - // More stars with smaller scale - const starScale = float(50.0); - const starUV = vec2(longitude.mul(starScale), latitude.mul(starScale)); - const starCell = floor(starUV); - - // Hash for randomness - const cellHash = fract(sin(dot(starCell, vec2(12.9898, 78.233))).mul(43758.5453)); - - // Position within cell (0-1) - const cellUV = fract(starUV); - const toCenter = cellUV.sub(0.5); - - // Gemini-style star: bright center with soft glow + cross flare - const distToCenter = length(toCenter); - - // Core (small bright center) - const core = smoothstep(float(0.08), float(0.0), distToCenter); - - // Soft glow around - const glow = smoothstep(float(0.25), float(0.0), distToCenter).mul(0.4); - - // Cross/diamond flare effect - const crossX = smoothstep(float(0.15), float(0.0), toCenter.x.abs()).mul( - smoothstep(float(0.4), float(0.0), toCenter.y.abs()), - ); - const crossY = smoothstep(float(0.15), float(0.0), toCenter.y.abs()).mul( - smoothstep(float(0.4), float(0.0), toCenter.x.abs()), - ); - const cross = crossX.add(crossY).mul(0.3); - - // Combine star shape - const starShape = core.add(glow).add(cross); - - // More stars (lower threshold = more stars) - const isStar = step(0.85, cellHash); - - // Show stars from horizon up - const aboveHorizon = smoothstep(float(-0.2), float(0.1), flippedY); - - // Star brightness varies + twinkle color - const starIntensity = isStar.mul(aboveHorizon).mul(starShape).mul(cellHash.mul(0.6).add(0.4)); - - // Slight color variation (white to light blue) - const starColor = mix(vec3(1.0, 1.0, 0.95), vec3(0.8, 0.9, 1.0), cellHash); - - // Combine sky and stars - const finalColor = mix(skyGradient, starColor, starIntensity.clamp(0.0, 1.0)); - - return finalColor; - })(); - - scene.backgroundNode = ps1Background; - - // Loaders - - const gltfLoader = new GLTFLoader(); - const textureLoader = new THREE.TextureLoader(); - - // Model - - const models = { - 'Coffee Mug': 'models/gltf/coffeeMug.glb', - 'Damaged Helmet': 'models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf', - }; - - function loadModel(name) { - function loadEnvironment() { - if (environment) return; - - environment = new HDRLoader().setPath('textures/equirectangular/').load('venice_sunset_1k.hdr', texture => { - texture.mapping = THREE.EquirectangularReflectionMapping; - scene.environment = texture; - - // re-invalidate retro pass textures - - retro.dispose(); - }); - } - - if (currentModel) { - scene.remove(currentModel); - currentModel.traverse(child => { - if (child.isMesh) { - child.geometry.dispose(); - child.material.dispose(); - } - }); - } - - gltfLoader.load(models[name], gltf => { - currentModel = gltf.scene; - currentModel.position.set(0, 0, 0); - - smoke.visible = false; - - if (name === 'Damaged Helmet') { - loadEnvironment(); - - currentModel.scale.setScalar(3); - currentModel.position.y = 1; - } else if (name === 'Coffee Mug') { - smoke.visible = true; - } - - scene.add(currentModel); - }); - } - - loadModel('Coffee Mug'); - - // lighting - - const ambientLight = new THREE.AmbientLight(0x404040, 2); - scene.add(ambientLight); - - const directionalLight = new THREE.DirectionalLight(0xffffff, 3); - directionalLight.position.set(5, 10, 5); - scene.add(directionalLight); - - const pointLight = new THREE.PointLight(0xff6600, 5, 20); - pointLight.position.set(-3, 3, 2); - scene.add(pointLight); - - // geometry - - const smokeGeometry = new THREE.PlaneGeometry(1, 1, 16, 64); - smokeGeometry.translate(0, 0.5, 0); - smokeGeometry.scale(1.5, 6, 1.5); - - // texture - - const noiseTexture = textureLoader.load('./textures/noises/perlin/128x128.png'); - noiseTexture.wrapS = THREE.RepeatWrapping; - noiseTexture.wrapT = THREE.RepeatWrapping; - - // material - - const smokeMaterial = new THREE.MeshBasicNodeMaterial({ - transparent: true, - side: THREE.DoubleSide, - depthWrite: false, - }); - - // position - - smokeMaterial.positionNode = Fn(() => { - // twist - - const twistNoiseUv = vec2(0.5, uv().y.mul(0.2).sub(time.mul(0.005)).mod(1)); - const twist = texture(noiseTexture, twistNoiseUv).r.mul(10); - positionLocal.xz.assign(rotateUV(positionLocal.xz, twist, vec2(0))); - - // wind - - const windOffset = vec2( - texture(noiseTexture, vec2(0.25, time.mul(0.01)).mod(1)).r.sub(0.5), - texture(noiseTexture, vec2(0.75, time.mul(0.01)).mod(1)).r.sub(0.5), - ).mul(uv().y.pow(2).mul(10)); - positionLocal.addAssign(windOffset); - - return positionLocal; - })(); - - // color - - smokeMaterial.colorNode = Fn(() => { - // alpha - - const alphaNoiseUv = uv() - .mul(vec2(0.5, 0.3)) - .add(vec2(0, time.mul(0.03).negate())); - const alpha = mul( - // pattern - texture(noiseTexture, alphaNoiseUv).r.smoothstep(0.4, 1), - - // edges fade - smoothstep(0, 0.1, uv().x), - smoothstep(0, 0.1, oneMinus(uv().x)), - smoothstep(0, 0.1, uv().y), - smoothstep(0, 0.1, oneMinus(uv().y)), - ); - - // color - - const finalColor = mix(vec3(0.6, 0.3, 0.2), vec3(1, 1, 1), alpha.pow(3)); - - return vec4(finalColor, alpha); - })(); - - // mesh - - const smoke = new THREE.Mesh(smokeGeometry, smokeMaterial); - smoke.position.y = 1.83; - scene.add(smoke); - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // uniforms - - // PS1-style: 15-bit color (32 levels per channel) - const colorDepthSteps = uniform(32); - - // CRT effect parameters (subtle for PS1 look) - const scanlineIntensity = uniform(0.3); // subtle scanlines - const scanlineDensity = uniform(1); // 0.1-1: normalized scanline density (1 = full screen resolution) - const scanlineSpeed = uniform(0.0); // no scanline movement - const vignetteIntensity = uniform(0.3); // subtle vignette - const bleeding = uniform(0.001); // minimal bleeding - const curvature = uniform(0.02); // subtle curve - const affineDistortion = uniform(0); // no affine distortion - - // render pipeline - - renderPipeline = new THREE.RenderPipeline(renderer); - - // retro pipeline - - const distortedUV = barrelUV(curvature); - const distortedDelta = circle(curvature.add(0.1).mul(10), 1).mul(curvature).mul(0.05); - - const retro = retroPass(scene, camera, { affineDistortion }); - - let retroPipeline = retro; - retroPipeline = replaceDefaultUV(distortedUV, retroPipeline); - retroPipeline = colorBleeding(retroPipeline, bleeding.add(distortedDelta)); - retroPipeline = bayerDither(retroPipeline, colorDepthSteps); - retroPipeline = posterize(retroPipeline, colorDepthSteps); - retroPipeline = vignette(retroPipeline, vignetteIntensity, 0.6); - retroPipeline = scanlines(retroPipeline, scanlineIntensity, screenSize.y.mul(scanlineDensity), scanlineSpeed); - - renderPipeline.outputNode = retroPipeline; - - // default pass (no post-processing) - - const defaultPass = pass(scene, camera); - - // controls - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - controls.minDistance = 0.1; - controls.maxDistance = 50; - controls.target.y = 1; - - // gui - - const gui = renderer.inspector.createParameters('Settings'); - - gui.add({ model: 'Coffee Mug' }, 'model', Object.keys(models)).name('Model').onChange(loadModel); - - gui.add({ enabled: true }, 'enabled') - .onChange(v => { - renderPipeline.outputNode = v ? retroPipeline : defaultPass; - renderPipeline.needsUpdate = true; - }) - .name('Retro Pipeline'); - - gui.add(curvature, 'value', 0, 0.2, 0.01).name('Curvature'); - gui.add(colorDepthSteps, 'value', 4, 32, 1).name('Color Depth'); - gui.add(scanlineIntensity, 'value', 0, 1, 0.01).name('Scanlines'); - gui.add(scanlineDensity, 'value', 0.02, 1, 0.01).name('Scanline Density'); - gui.add(scanlineSpeed, 'value', 0, 0.1, 0.01).name('Scanline Speed'); - gui.add(vignetteIntensity, 'value', 0, 1, 0.01).name('Vignette'); - gui.add(bleeding, 'value', 0, 0.005, 0.001).name('Color Bleeding'); - gui.add(affineDistortion, 'value', 0, 1).name('Affine Distortion'); - gui.add(retro, 'filterTextures') - .name('Filter Textures') - .onChange(() => { - retro.dispose(); - }); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -async function animate() { - controls.update(); - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_postprocessing_smaa.ts b/examples-testing/examples/webgpu_postprocessing_smaa.ts deleted file mode 100644 index f06374258..000000000 --- a/examples-testing/examples/webgpu_postprocessing_smaa.ts +++ /dev/null @@ -1,98 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { pass } from 'three/tsl'; -import { smaa } from 'three/addons/tsl/display/SMAANode.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer, renderPipeline; - -const params = { - enabled: true, - autoRotate: true, -}; - -init(); - -function init() { - renderer = new THREE.WebGPURenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // - - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.z = 300; - - scene = new THREE.Scene(); - - const geometry = new THREE.BoxGeometry(120, 120, 120); - const material1 = new THREE.MeshBasicMaterial({ color: 0xffffff, wireframe: true }); - - const mesh1 = new THREE.Mesh(geometry, material1); - mesh1.position.x = -100; - scene.add(mesh1); - - const texture = new THREE.TextureLoader().load('textures/brick_diffuse.jpg'); - texture.colorSpace = THREE.SRGBColorSpace; - - const material2 = new THREE.MeshBasicMaterial({ map: texture }); - - const mesh2 = new THREE.Mesh(geometry, material2); - mesh2.position.x = 100; - scene.add(mesh2); - - // post processing - - renderPipeline = new THREE.RenderPipeline(renderer); - - const scenePass = pass(scene, camera).toInspector('Color'); - const smaaPass = smaa(scenePass); - - renderPipeline.outputNode = smaaPass; - - // - - window.addEventListener('resize', onWindowResize); - - const gui = renderer.inspector.createParameters('Settings'); - - const smaaFolder = gui.addFolder('SMAA'); - smaaFolder.add(params, 'enabled').onChange(value => { - if (value === true) { - renderPipeline.outputNode = smaaPass; - } else { - renderPipeline.outputNode = scenePass; - } - - renderPipeline.needsUpdate = true; - }); - - const sceneFolder = gui.addFolder('Scene'); - sceneFolder.add(params, 'autoRotate'); -} - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); -} - -function animate() { - if (params.autoRotate === true) { - for (let i = 0; i < scene.children.length; i++) { - const child = scene.children[i]; - - child.rotation.x += 0.005; - child.rotation.y += 0.01; - } - } - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_postprocessing_sobel.ts b/examples-testing/examples/webgpu_postprocessing_sobel.ts deleted file mode 100644 index bdc37d2fd..000000000 --- a/examples-testing/examples/webgpu_postprocessing_sobel.ts +++ /dev/null @@ -1,94 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { pass, renderOutput } from 'three/tsl'; -import { sobel } from 'three/addons/tsl/display/SobelOperatorNode.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer, controls; -let renderPipeline; - -const params = { - enabled: true, -}; - -init(); - -async function init() { - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(0, 1, 3); - - // - - const loader = new GLTFLoader(); - const gltf = await loader.loadAsync('models/gltf/DragonAttenuation.glb'); - const model = gltf.scene.children[1]; - model.material = new THREE.MeshStandardNodeMaterial(); - - scene.add(model); - - // - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - renderer.toneMapping = THREE.LinearToneMapping; - document.body.appendChild(renderer.domElement); - - await renderer.init(); - - const environment = new RoomEnvironment(); - const pmremGenerator = new THREE.PMREMGenerator(renderer); - - scene.environment = pmremGenerator.fromScene(environment, 0.04).texture; - pmremGenerator.dispose(); - - // - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - controls.enableZoom = false; - controls.target.set(0, 0.5, 0); - controls.update(); - - // postprocessing - - renderPipeline = new THREE.RenderPipeline(renderer); - renderPipeline.outputColorTransform = false; - - const scenePass = pass(scene, camera); - - renderPipeline.outputNode = sobel(renderOutput(scenePass)); - - // - - const gui = renderer.inspector.createParameters('Settings'); - gui.add(params, 'enabled'); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - controls.update(); - - if (params.enabled === true) { - renderPipeline.render(); - } else { - renderer.render(scene, camera); - } -} diff --git a/examples-testing/examples/webgpu_postprocessing_ssaa.ts b/examples-testing/examples/webgpu_postprocessing_ssaa.ts deleted file mode 100644 index c38832c7a..000000000 --- a/examples-testing/examples/webgpu_postprocessing_ssaa.ts +++ /dev/null @@ -1,170 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { ssaaPass } from 'three/addons/tsl/display/SSAAPassNode.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let scene, mesh, renderer, renderPipeline; -let camera, ssaaRenderPass; -let timer; - -const params = { - sampleLevel: 3, - camera: 'perspective', - clearColor: 'black', - clearAlpha: 1.0, - viewOffsetX: 0, - autoRotate: true, -}; - -init(); - -function init() { - const width = window.innerWidth; - const height = window.innerHeight; - - renderer = new THREE.WebGPURenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - timer = new THREE.Timer(); - timer.connect(document); - - camera = new THREE.PerspectiveCamera(65, width / height, 3, 10); - camera.position.z = 7; - camera.setViewOffset(width, height, params.viewOffsetX, 0, width, height); - - scene = new THREE.Scene(); - - const group = new THREE.Group(); - scene.add(group); - - const light = new THREE.PointLight(0xefffef, 500); - light.position.z = 10; - light.position.y = -10; - light.position.x = -10; - scene.add(light); - - const light2 = new THREE.PointLight(0xffefef, 500); - light2.position.z = 10; - light2.position.x = -10; - light2.position.y = 10; - scene.add(light2); - - const light3 = new THREE.PointLight(0xefefff, 500); - light3.position.z = 10; - light3.position.x = 10; - light3.position.y = -10; - scene.add(light3); - - const light4 = new THREE.AmbientLight(0xffffff, 0.2); - scene.add(light4); - - const geometry = new THREE.SphereGeometry(3, 48, 24); - const material = new THREE.MeshStandardMaterial(); - - mesh = new THREE.InstancedMesh(geometry, material, 120); - - const dummy = new THREE.Mesh(); - const color = new THREE.Color(); - - for (let i = 0; i < mesh.count; i++) { - dummy.position.x = Math.random() * 4 - 2; - dummy.position.y = Math.random() * 4 - 2; - dummy.position.z = Math.random() * 4 - 2; - dummy.rotation.x = Math.random(); - dummy.rotation.y = Math.random(); - dummy.rotation.z = Math.random(); - dummy.scale.setScalar(Math.random() * 0.2 + 0.05); - - dummy.updateMatrix(); - - color.setHSL(Math.random(), 1.0, 0.3); - - mesh.setMatrixAt(i, dummy.matrix); - mesh.setColorAt(i, color); - } - - scene.add(mesh); - - // postprocessing - - renderPipeline = new THREE.RenderPipeline(renderer); - - ssaaRenderPass = ssaaPass(scene, camera); - const scenePassColor = ssaaRenderPass.getTextureNode(); - - renderPipeline.outputNode = scenePassColor; - - window.addEventListener('resize', onWindowResize); - - // GUI - - const gui = renderer.inspector.createParameters('Settings'); - - gui.add(params, 'sampleLevel', { - 'Level 0: 1 Sample': 0, - 'Level 1: 2 Samples': 1, - 'Level 2: 4 Samples': 2, - 'Level 3: 8 Samples': 3, - 'Level 4: 16 Samples': 4, - 'Level 5: 32 Samples': 5, - }); - gui.add(params, 'clearColor', ['black', 'white', 'blue', 'green', 'red']); - gui.add(params, 'clearAlpha', 0, 1); - gui.add(params, 'viewOffsetX', -100, 100); - gui.add(params, 'autoRotate'); -} - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.setViewOffset(width, height, params.viewOffsetX, 0, width, height); - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); -} - -function animate() { - timer.update(); - - if (params.autoRotate) { - const delta = timer.getDelta(); - - mesh.rotation.x += delta * 0.25; - mesh.rotation.y += delta * 0.5; - } - - let newColor = ssaaRenderPass.clearColor; - - switch (params.clearColor) { - case 'blue': - newColor = 0x0000ff; - break; - case 'red': - newColor = 0xff0000; - break; - case 'green': - newColor = 0x00ff00; - break; - case 'white': - newColor = 0xffffff; - break; - case 'black': - newColor = 0x000000; - break; - } - - ssaaRenderPass.clearColor.set(newColor); - ssaaRenderPass.clearAlpha = params.clearAlpha; - - ssaaRenderPass.sampleLevel = params.sampleLevel; - - camera.view.offsetX = params.viewOffsetX; - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_postprocessing_ssgi.ts b/examples-testing/examples/webgpu_postprocessing_ssgi.ts deleted file mode 100644 index 8c8e4a91c..000000000 --- a/examples-testing/examples/webgpu_postprocessing_ssgi.ts +++ /dev/null @@ -1,244 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { - pass, - mrt, - output, - normalView, - diffuseColor, - velocity, - add, - vec3, - vec4, - directionToColor, - colorToDirection, - sample, -} from 'three/tsl'; -import { ssgi } from 'three/addons/tsl/display/SSGINode.js'; -import { traa } from 'three/addons/tsl/display/TRAANode.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer, renderPipeline, controls; - -init(); - -async function init() { - camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(0, 10, 30); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xaaaaaa); - - renderer = new THREE.WebGPURenderer(); - //renderer.setPixelRatio( window.devicePixelRatio ); // probably too costly for most hardware - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.shadowMap.enabled = true; - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // - - controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, 7, 0); - controls.enablePan = true; - controls.minDistance = 1; - controls.maxDistance = 100; - controls.update(); - - // - - renderPipeline = new THREE.RenderPipeline(renderer); - - const scenePass = pass(scene, camera); - scenePass.setMRT( - mrt({ - output: output, - diffuseColor: diffuseColor, - normal: directionToColor(normalView), - velocity: velocity, - }), - ); - - const scenePassColor = scenePass.getTextureNode('output'); - const scenePassDiffuse = scenePass.getTextureNode('diffuseColor').toInspector('Diffuse Color'); - const scenePassDepth = scenePass.getTextureNode('depth').toInspector('Depth', () => { - return scenePass.getLinearDepthNode(); - }); - - const scenePassNormal = scenePass.getTextureNode('normal').toInspector('Normal'); - const scenePassVelocity = scenePass.getTextureNode('velocity').toInspector('Velocity'); - - // bandwidth optimization - - const diffuseTexture = scenePass.getTexture('diffuseColor'); - diffuseTexture.type = THREE.UnsignedByteType; - - const normalTexture = scenePass.getTexture('normal'); - normalTexture.type = THREE.UnsignedByteType; - - const sceneNormal = sample(uv => { - return colorToDirection(scenePassNormal.sample(uv)); - }); - - // gi - - const giPass = ssgi(scenePassColor, scenePassDepth, sceneNormal, camera); - giPass.sliceCount.value = 2; - giPass.stepCount.value = 8; - - // composite - - const gi = giPass.rgb.toInspector('SSGI'); - const ao = giPass.a.toInspector('AO'); - - const compositePass = vec4(add(scenePassColor.rgb.mul(ao), scenePassDiffuse.rgb.mul(gi)), scenePassColor.a); - compositePass.name = 'Composite'; - - // traa - - const traaPass = traa(compositePass, scenePassDepth, scenePassVelocity, camera); - renderPipeline.outputNode = traaPass; - - // Cornell Box inspired scene - - // Walls - const wallGeometry = new THREE.PlaneGeometry(1, 1); - - // Left wall - red - const redWallMaterial = new THREE.MeshPhysicalMaterial({ color: '#ff0000' }); - const leftWall = new THREE.Mesh(wallGeometry, redWallMaterial); - leftWall.scale.set(20, 15, 1); - leftWall.rotation.y = Math.PI * 0.5; - leftWall.position.set(-10, 7.5, 0); - leftWall.receiveShadow = true; - scene.add(leftWall); - - // Right wall - green - const greenWallMaterial = new THREE.MeshPhysicalMaterial({ color: '#00ff00' }); - const rightWall = new THREE.Mesh(wallGeometry, greenWallMaterial); - rightWall.scale.set(20, 15, 1); - rightWall.rotation.y = Math.PI * -0.5; - rightWall.position.set(10, 7.5, 0); - rightWall.receiveShadow = true; - scene.add(rightWall); - - // White walls and boxes - const whiteMaterial = new THREE.MeshPhysicalMaterial({ color: '#fff' }); - - // Floor - const floor = new THREE.Mesh(wallGeometry, whiteMaterial); - floor.scale.set(20, 20, 1); - floor.rotation.x = Math.PI * -0.5; - floor.receiveShadow = true; - scene.add(floor); - - // Back wall - const backWall = new THREE.Mesh(wallGeometry, whiteMaterial); - backWall.scale.set(15, 20, 1); - backWall.rotation.z = Math.PI * -0.5; - backWall.position.set(0, 7.5, -10); - backWall.receiveShadow = true; - scene.add(backWall); - - // Ceiling - const ceiling = new THREE.Mesh(wallGeometry, whiteMaterial); - ceiling.scale.set(20, 20, 1); - ceiling.rotation.x = Math.PI * 0.5; - ceiling.position.set(0, 15, 0); - ceiling.receiveShadow = true; - scene.add(ceiling); - - // Boxes - const tallBoxGeometry = new THREE.BoxGeometry(5, 7, 5); - const tallBox = new THREE.Mesh(tallBoxGeometry, whiteMaterial); - tallBox.rotation.y = Math.PI * 0.25; - tallBox.position.set(-3, 3.5, -2); - tallBox.castShadow = true; - tallBox.receiveShadow = true; - scene.add(tallBox); - - const shortBoxGeometry = new THREE.BoxGeometry(4, 4, 4); - const shortBox = new THREE.Mesh(shortBoxGeometry, whiteMaterial); - shortBox.rotation.y = Math.PI * -0.1; - shortBox.position.set(4, 2, 4); - shortBox.castShadow = true; - shortBox.receiveShadow = true; - scene.add(shortBox); - - // Light source geometry - const lightSourceGeometry = new THREE.CylinderGeometry(2.5, 2.5, 1, 64); - const lightSourceMaterial = new THREE.MeshBasicMaterial(); - const lightSource = new THREE.Mesh(lightSourceGeometry, lightSourceMaterial); - lightSource.position.y = 15; - scene.add(lightSource); - - // Point light - const pointLight = new THREE.PointLight('#ffffff', 100); - pointLight.position.set(0, 13, 0); - pointLight.distance = 100; - pointLight.castShadow = true; - pointLight.shadow.mapSize.width = 1024; - pointLight.shadow.mapSize.height = 1024; - scene.add(pointLight); - - // Ambient light - const ambientLight = new THREE.AmbientLight('#0c0c0c'); - scene.add(ambientLight); - - window.addEventListener('resize', onWindowResize); - - // - - const params = { - output: 0, - }; - - const types = { Combined: 0, Direct: 3, AO: 1, GI: 2 }; - - const gui = renderer.inspector.createParameters('SSGI settings'); - gui.add(params, 'output', types).onChange(updatePostprocessing); - gui.add(giPass.sliceCount, 'value', 1, 4, 1).name('slice count'); - gui.add(giPass.stepCount, 'value', 1, 32, 1).name('step count'); - gui.add(giPass.radius, 'value', 1, 25).name('radius'); - gui.add(giPass.expFactor, 'value', 1, 3).name('exp factor'); - gui.add(giPass.thickness, 'value', 0.01, 10).name('thickness'); - gui.add(giPass.backfaceLighting, 'value', 0, 1).name('backface lighting'); - gui.add(giPass.aoIntensity, 'value', 0, 4).name('AO intensity'); - gui.add(giPass.giIntensity, 'value', 0, 100).name('GI intensity'); - gui.add(giPass.useLinearThickness, 'value').name('use linear thickness'); - gui.add(giPass.useScreenSpaceSampling, 'value').name('screen-space sampling'); - gui.add(giPass, 'useTemporalFiltering').name('temporal filtering').onChange(updatePostprocessing); - - function updatePostprocessing(value) { - if (value === 1) { - renderPipeline.outputNode = vec4(vec3(ao), 1); - } else if (value === 2) { - renderPipeline.outputNode = vec4(gi, 1); - } else if (value === 3) { - renderPipeline.outputNode = scenePassColor; - } else { - renderPipeline.outputNode = giPass.useTemporalFiltering ? traaPass : compositePass; - } - - renderPipeline.needsUpdate = true; - } -} - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); -} - -function animate() { - controls.update(); - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_postprocessing_ssgi_ballpool.ts b/examples-testing/examples/webgpu_postprocessing_ssgi_ballpool.ts deleted file mode 100644 index 4d1e617c1..000000000 --- a/examples-testing/examples/webgpu_postprocessing_ssgi_ballpool.ts +++ /dev/null @@ -1,413 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { - pass, - mrt, - output, - normalView, - diffuseColor, - velocity, - add, - vec4, - directionToColor, - colorToDirection, - sample, -} from 'three/tsl'; -import { ssgi } from 'three/addons/tsl/display/SSGINode.js'; -import { traa } from 'three/addons/tsl/display/TRAANode.js'; -import { World } from '@perplexdotgg/bounce'; - -const BALL_RADIUS = 0.4; -const FILL_RATIO = 0.4; -const PACKING = 0.6; -const BOX_HEIGHT = 6; -const BOX_DEPTH = 8; -const WALL_THICKNESS = 0.5; -const CAM_FOV = 45; -const EASE_SPEED = 8; - -let camera, scene, renderer, renderPipeline; -let raycaster, pointer; -let mouseLight; - -let world; -let ballCount = 0; -let bodies = []; -let ballsMesh = null; -let wallMeshes = []; -const boxSize = { w: 8, h: BOX_HEIGHT, d: BOX_DEPTH }; - -let mouseRayOrigin = new THREE.Vector3(); -let mouseRayDir = new THREE.Vector3(); -let mouseMoving = false; -let mouseStopTimer = 0; -let pointerDown = false; -const activePointers = new Set(); -const mouseLightTarget = new THREE.Vector3(0, BOX_HEIGHT / 2, BOX_DEPTH / 2); -const mouseRayOriginTarget = new THREE.Vector3(); -const mouseRayDirTarget = new THREE.Vector3(); - -init(); - -async function init() { - camera = new THREE.PerspectiveCamera(CAM_FOV, window.innerWidth / window.innerHeight, 0.1, 100); - - scene = new THREE.Scene(); - - renderer = new THREE.WebGPURenderer({ antialias: false }); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = 0.5; - renderer.shadowMap.enabled = true; - document.body.appendChild(renderer.domElement); - - // - - renderPipeline = new THREE.RenderPipeline(renderer); - - const scenePass = pass(scene, camera); - scenePass.setMRT( - mrt({ - output: output, - diffuseColor: diffuseColor, - normal: directionToColor(normalView), - velocity: velocity, - }), - ); - - const scenePassColor = scenePass.getTextureNode('output'); - const scenePassDiffuse = scenePass.getTextureNode('diffuseColor'); - const scenePassDepth = scenePass.getTextureNode('depth'); - const scenePassNormal = scenePass.getTextureNode('normal'); - const scenePassVelocity = scenePass.getTextureNode('velocity'); - - // bandwidth optimization - - const diffuseTexture = scenePass.getTexture('diffuseColor'); - diffuseTexture.type = THREE.UnsignedByteType; - - const normalTexture = scenePass.getTexture('normal'); - normalTexture.type = THREE.UnsignedByteType; - - const sceneNormal = sample(uv => { - return colorToDirection(scenePassNormal.sample(uv)); - }); - - // gi - - const giPass = ssgi(scenePassColor, scenePassDepth, sceneNormal, camera); - giPass.sliceCount.value = 2; - giPass.stepCount.value = 8; - - // composite - - const gi = giPass.rgb; - const ao = giPass.a; - - const compositePass = vec4(add(scenePassColor.rgb.mul(ao), scenePassDiffuse.rgb.mul(gi)), scenePassColor.a); - - // traa - - const traaPass = traa(compositePass, scenePassDepth, scenePassVelocity, camera); - renderPipeline.outputNode = traaPass; - - // light - - mouseLight = new THREE.PointLight(0xffffff, 80); - mouseLight.position.set(0, BOX_HEIGHT / 2, BOX_DEPTH / 2); - mouseLight.castShadow = true; - mouseLight.shadow.mapSize.set(1024, 1024); - mouseLight.shadow.radius = 20; - scene.add(mouseLight); - - // - - raycaster = new THREE.Raycaster(); - pointer = new THREE.Vector2(); - - rebuildScene(); - - // - - renderer.domElement.addEventListener('pointerdown', onPointerDown); - renderer.domElement.addEventListener('pointermove', onPointerMove); - renderer.domElement.addEventListener('pointerup', onPointerUp); - renderer.domElement.addEventListener('pointercancel', onPointerUp); - window.addEventListener('resize', onWindowResize); -} - -function getBoxWidth() { - const aspect = window.innerWidth / window.innerHeight; - const vFov = THREE.MathUtils.degToRad(CAM_FOV / 2); - const dist = BOX_HEIGHT / 2 / Math.tan(vFov); - return Math.tan(vFov) * aspect * dist * 2; -} - -function getBallCount() { - const roomVolume = boxSize.w * boxSize.h * boxSize.d; - const ballVolume = (4 / 3) * Math.PI * Math.pow(BALL_RADIUS, 3); - return Math.floor((roomVolume * FILL_RATIO * PACKING) / ballVolume); -} - -function rebuildScene() { - for (const mesh of wallMeshes) { - scene.remove(mesh); - mesh.geometry.dispose(); - } - - wallMeshes = []; - - if (ballsMesh) { - scene.remove(ballsMesh); - ballsMesh.dispose(); - ballsMesh = null; - } - - bodies = []; - - boxSize.w = getBoxWidth(); - ballCount = getBallCount(); - - world = new World({ - gravity: [0, -9.81, 0], - solveVelocityIterations: 6, - solvePositionIterations: 2, - linearDamping: 0.1, - angularDamping: 0.1, - restitution: 0.4, - friction: 0.5, - }); - - fitCameraToBox(); - createBox(); - createBalls(); -} - -function createBox() { - const hw = boxSize.w / 2; - const hh = boxSize.h / 2; - const hd = boxSize.d / 2; - const t = WALL_THICKNESS; - - const whiteMaterial = new THREE.MeshPhysicalMaterial({ - color: 0xeeeeee, - roughness: 0.7, - metalness: 0.0, - }); - - const redMaterial = new THREE.MeshPhysicalMaterial({ - color: 0xff2222, - roughness: 0.7, - metalness: 0.0, - }); - - const greenMaterial = new THREE.MeshPhysicalMaterial({ - color: 0x22ff22, - roughness: 0.7, - metalness: 0.0, - }); - - const walls = [ - { size: [boxSize.w, t, boxSize.d], pos: [0, -t / 2, 0], mat: whiteMaterial }, - { size: [boxSize.w, t, boxSize.d], pos: [0, boxSize.h + t / 2, 0], mat: whiteMaterial }, - { size: [boxSize.w, boxSize.h, t], pos: [0, hh, -hd - t / 2], mat: whiteMaterial }, - { size: [boxSize.w, boxSize.h, t], pos: [0, hh, hd + t / 2], noMesh: true }, - { size: [t, boxSize.h, boxSize.d], pos: [-hw - t / 2, hh, 0], mat: redMaterial }, - { size: [t, boxSize.h, boxSize.d], pos: [hw + t / 2, hh, 0], mat: greenMaterial }, - ]; - - for (const w of walls) { - const shape = world.createBox({ width: w.size[0], height: w.size[1], depth: w.size[2] }); - world.createStaticBody({ shape, position: w.pos }); - - if (!w.noMesh) { - const geo = new THREE.BoxGeometry(w.size[0], w.size[1], w.size[2]); - const mesh = new THREE.Mesh(geo, w.mat); - mesh.position.set(...w.pos); - mesh.receiveShadow = true; - scene.add(mesh); - wallMeshes.push(mesh); - } - } -} - -function createBalls() { - const sphereGeo = new THREE.SphereGeometry(BALL_RADIUS, 32, 16); - const sphereShape = world.createSphere({ radius: BALL_RADIUS }); - - const material = new THREE.MeshPhysicalMaterial({ - roughness: 0.3, - metalness: 0.1, - }); - - ballsMesh = new THREE.InstancedMesh(sphereGeo, material, ballCount); - ballsMesh.castShadow = true; - ballsMesh.receiveShadow = true; - scene.add(ballsMesh); - - const colors = [0xff4444, 0x44ff44, 0x4488ff, 0xffaa00, 0xff44ff, 0x44ffff, 0xffff44, 0xff8844, 0x8844ff, 0x44ff88]; - - const color = new THREE.Color(); - const hw = boxSize.w / 2 - BALL_RADIUS - 0.1; - const hd = boxSize.d / 2 - BALL_RADIUS - 0.1; - - for (let i = 0; i < ballCount; i++) { - color.set(colors[i % colors.length]); - ballsMesh.setColorAt(i, color); - - const x = (Math.random() - 0.5) * 2 * hw; - const y = BALL_RADIUS + Math.random() * (boxSize.h - BALL_RADIUS * 2); - const z = (Math.random() - 0.5) * 2 * hd; - - const body = world.createDynamicBody({ - shape: sphereShape, - position: [x, y, z], - mass: 1, - restitution: 0.5, - friction: 0.4, - }); - - bodies.push(body); - } -} - -function onPointerDown(event) { - activePointers.add(event.pointerId); - - if (event.pointerType === 'touch') { - pointerDown = activePointers.size >= 2; - } else { - pointerDown = true; - } -} - -function onPointerUp(event) { - activePointers.delete(event.pointerId); - - if (event.pointerType === 'touch') { - pointerDown = activePointers.size >= 2; - } else { - pointerDown = false; - } -} - -function onPointerMove(event) { - pointer.x = (event.clientX / window.innerWidth) * 2 - 1; - pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; - - raycaster.setFromCamera(pointer, camera); - - mouseRayOriginTarget.copy(raycaster.ray.origin); - mouseRayDirTarget.copy(raycaster.ray.direction); - mouseMoving = true; - clearTimeout(mouseStopTimer); - mouseStopTimer = setTimeout(() => (mouseMoving = false), 50); - - const frontPlane = new THREE.Plane(new THREE.Vector3(0, 0, 1), -boxSize.d / 2); - const hit = new THREE.Vector3(); - if (raycaster.ray.intersectPlane(frontPlane, hit)) { - mouseLightTarget.copy(hit); - } -} - -function respawnBalls() { - const hw = boxSize.w / 2 - BALL_RADIUS - 0.1; - const hd = boxSize.d / 2 - BALL_RADIUS - 0.1; - const count = 5; - - for (let i = 0; i < count; i++) { - const body = bodies[Math.floor(Math.random() * bodies.length)]; - - body.position.set([ - (Math.random() - 0.5) * 2 * hw, - boxSize.h - BALL_RADIUS - Math.random() * 1, - (Math.random() - 0.5) * 2 * hd, - ]); - body.linearVelocity.set([0, 0, 0]); - body.angularVelocity.set([0, 0, 0]); - body.commitChanges(); - } -} - -function fitCameraToBox() { - const vFov = THREE.MathUtils.degToRad(CAM_FOV / 2); - const dist = boxSize.h / 2 / Math.tan(vFov); - - camera.aspect = window.innerWidth / window.innerHeight; - camera.position.set(0, boxSize.h / 2, dist + boxSize.d / 2); - camera.lookAt(0, boxSize.h / 2, 0); - camera.updateProjectionMatrix(); -} - -function onWindowResize() { - renderer.setSize(window.innerWidth, window.innerHeight); - rebuildScene(); -} - -const timer = new THREE.Timer(); -const _dummy = new THREE.Object3D(); - -function animate() { - timer.update(); - const dt = Math.min(timer.getDelta(), 1 / 30); - - const easeFactor = 1 - Math.exp(-EASE_SPEED * dt); - mouseRayOrigin.lerp(mouseRayOriginTarget, easeFactor); - mouseRayDir.lerp(mouseRayDirTarget, easeFactor); - mouseLight.position.lerp(mouseLightTarget, easeFactor); - - if (pointerDown) { - respawnBalls(); - } - - if (mouseMoving) { - const pushRadius = 1.5; - const pushStrength = 15; - const _closest = new THREE.Vector3(); - const _ballPos = new THREE.Vector3(); - const _pushDir = new THREE.Vector3(); - - for (const body of bodies) { - const bp = body.position; - _ballPos.set(bp.x, bp.y, bp.z); - - const ray = new THREE.Ray(mouseRayOrigin, mouseRayDir); - ray.closestPointToPoint(_ballPos, _closest); - - const dist = _closest.distanceTo(_ballPos); - - if (dist < pushRadius) { - _pushDir.subVectors(_ballPos, _closest); - if (_pushDir.lengthSq() < 0.001) _pushDir.set(0, 1, 0); - _pushDir.normalize(); - - const strength = pushStrength * (1 - dist / pushRadius); - body.applyLinearImpulse({ - x: _pushDir.x * strength, - y: _pushDir.y * strength, - z: _pushDir.z * strength, - }); - } - } - } - - if (world) { - world.advanceTime(1 / 60, dt); - } - - for (let i = 0; i < bodies.length; i++) { - const body = bodies[i]; - const p = body.position; - const q = body.orientation; - - _dummy.position.set(p.x, p.y, p.z); - _dummy.quaternion.set(q.x, q.y, q.z, q.w); - _dummy.updateMatrix(); - - ballsMesh.setMatrixAt(i, _dummy.matrix); - } - - ballsMesh.instanceMatrix.needsUpdate = true; - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_postprocessing_ssr.ts b/examples-testing/examples/webgpu_postprocessing_ssr.ts deleted file mode 100644 index 7ce11780a..000000000 --- a/examples-testing/examples/webgpu_postprocessing_ssr.ts +++ /dev/null @@ -1,211 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { - pass, - mrt, - output, - normalView, - metalness, - roughness, - screenUV, - color, - sample, - directionToColor, - colorToDirection, - vec2, - colorSpaceToWorking, -} from 'three/tsl'; -import { ssr } from 'three/addons/tsl/display/SSRNode.js'; -import { smaa } from 'three/addons/tsl/display/SMAANode.js'; - -import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -const params = { - quality: 0.5, - blurQuality: 1, - maxDistance: 1, - opacity: 1, - thickness: 0.03, - roughness: 1, - enabled: true, -}; - -let camera, scene, model, renderer, renderPipeline, ssrPass; -let controls; - -init(); - -async function init() { - camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 0.1, 50); - camera.position.set(3, 2, 3); - - scene = new THREE.Scene(); - scene.backgroundNode = screenUV.distance(0.5).remap(0, 0.5).mix(color(0x888877), color(0x776666)); - - const dracoLoader = new DRACOLoader(); - dracoLoader.setDecoderPath('jsm/libs/draco/'); - dracoLoader.setDecoderConfig({ type: 'js' }); - - const loader = new GLTFLoader(); - loader.setDRACOLoader(dracoLoader); - loader.load('models/gltf/steampunk_camera.glb', function (gltf) { - model = gltf.scene; - - model.traverse(function (object) { - if (object.material) { - if (object.material.name === 'Lense_Casing') { - object.material.transparent = true; - } - - // Avoid overdrawing - object.material.side = THREE.FrontSide; - } - }); - - model.position.y = 0.1; - scene.add(model); - }); - - // Add a reflective plane under the camera - - const floorGeometry = new THREE.CircleGeometry(2, 64); - const floorMaterial = new THREE.MeshStandardMaterial({ - color: 0xffffff, - metalness: 1, - roughness: 0.5, - }); - const floor = new THREE.Mesh(floorGeometry, floorMaterial); - floor.rotation.x = -Math.PI / 2; - floor.position.y = -0.8; - scene.add(floor); - - // - - renderer = new THREE.WebGPURenderer(); - // renderer.setPixelRatio( window.devicePixelRatio ); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - await renderer.init(); - - const environment = new RoomEnvironment(); - const pmremGenerator = new THREE.PMREMGenerator(renderer); - - scene.environment = pmremGenerator.fromScene(environment, 0.04).texture; - scene.environmentIntensity = 1.25; - pmremGenerator.dispose(); - - // - - renderPipeline = new THREE.RenderPipeline(renderer); - - const scenePass = pass(scene, camera); - scenePass.setMRT( - mrt({ - output: output, - normal: directionToColor(normalView), - metalrough: vec2(metalness, roughness), // pack metalness and roughness into a single attachment - }), - ); - - const scenePassColor = scenePass.getTextureNode('output').toInspector('Color'); - const scenePassNormal = scenePass.getTextureNode('normal').toInspector('Normal', node => { - return colorSpaceToWorking(node, THREE.SRGBColorSpace); - }); - const scenePassDepth = scenePass.getTextureNode('depth').toInspector('Depth', () => { - return scenePass.getLinearDepthNode(); - }); - const scenePassMetalRough = scenePass.getTextureNode('metalrough').toInspector('Metalness-Roughness'); - - // optional: optimize bandwidth by reducing the texture precision for normals and metal/roughness - - const normalTexture = scenePass.getTexture('normal'); - normalTexture.type = THREE.UnsignedByteType; - - const metalRoughTexture = scenePass.getTexture('metalrough'); - metalRoughTexture.type = THREE.UnsignedByteType; - - const sceneNormal = sample(uv => { - return colorToDirection(scenePassNormal.sample(uv)); - }); - - // - - ssrPass = ssr( - scenePassColor, - scenePassDepth, - sceneNormal, - scenePassMetalRough.r, - scenePassMetalRough.g, - ).toInspector('SSR'); - - // blend SSR over beauty (SSR outputs premultiplied color, so use additive blending) - - const outputNode = smaa(scenePassColor.add(ssrPass.rgb)); - - renderPipeline.outputNode = outputNode; - - // - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - controls.update(); - - window.addEventListener('resize', onWindowResize); - - // GUI - - const gui = renderer.inspector.createParameters('Settings'); - const ssrFolder = gui.addFolder('SSR'); - ssrFolder.add(params, 'quality', 0, 1).onChange(updateParameters); - ssrFolder.add(params, 'blurQuality', 1, 3, 1).onChange(updateParameters); - ssrFolder.add(params, 'maxDistance', 0, 1).onChange(updateParameters); - ssrFolder.add(params, 'opacity', 0, 1).onChange(updateParameters); - ssrFolder.add(params, 'thickness', 0, 0.05).onChange(updateParameters); - ssrFolder.add(params, 'enabled').onChange(() => { - if (params.enabled === true) { - renderPipeline.outputNode = outputNode; - } else { - renderPipeline.outputNode = scenePass; - } - - renderPipeline.needsUpdate = true; - }); - const modelFolder = gui.addFolder('Model'); - modelFolder.add(params, 'roughness', 0, 1).onChange(value => { - model.traverse(function (object) { - if (object.material) { - object.material.roughness = value; - } - }); - }); - - updateParameters(); -} - -function updateParameters() { - ssrPass.quality.value = params.quality; - ssrPass.blurQuality.value = params.blurQuality; - ssrPass.maxDistance.value = params.maxDistance; - ssrPass.opacity.value = params.opacity; - ssrPass.thickness.value = params.thickness; -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - controls.update(); - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_postprocessing_sss.ts b/examples-testing/examples/webgpu_postprocessing_sss.ts deleted file mode 100644 index 42cc26f39..000000000 --- a/examples-testing/examples/webgpu_postprocessing_sss.ts +++ /dev/null @@ -1,185 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { pass, vec3, vec4, mrt, screenUV, velocity, builtinShadowContext } from 'three/tsl'; -import { sss } from 'three/addons/tsl/display/SSSNode.js'; -import { traa } from 'three/addons/tsl/display/TRAANode.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer, renderPipeline, controls; - -init(); - -async function init() { - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(1, 2.5, -3.5); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xa0a0a0); - scene.fog = new THREE.Fog(0xa0a0a0, 10, 50); - - // lights - - const hemiLight = new THREE.HemisphereLight(0xffffff, 0x8d8d8d, 2); - hemiLight.position.set(0, 20, 0); - scene.add(hemiLight); - - const dirLight = new THREE.DirectionalLight(0xffffff, 3); - dirLight.position.set(-3, 10, -10); - dirLight.castShadow = true; - dirLight.shadow.bias = -0.001; // remove self-shadowing artifacts - dirLight.shadow.camera.top = 4; - dirLight.shadow.camera.bottom = -4; - dirLight.shadow.camera.left = -4; - dirLight.shadow.camera.right = 4; - dirLight.shadow.camera.near = 0.1; - dirLight.shadow.camera.far = 40; - dirLight.shadow.mapSize.width = 1024; - dirLight.shadow.mapSize.height = 1024; - scene.add(dirLight); - - // scene.add( new THREE.CameraHelper( dirLight.shadow.camera ) ); - - // ground - - const mesh = new THREE.Mesh( - new THREE.PlaneGeometry(100, 100), - new THREE.MeshPhongMaterial({ color: 0xcbcbcb, depthWrite: false }), - ); - mesh.rotation.x = -Math.PI / 2; - mesh.receiveShadow = true; - scene.add(mesh); - - // - - const dracoLoader = new DRACOLoader(); - dracoLoader.setDecoderPath('jsm/libs/draco/gltf/'); - - const loader = new GLTFLoader(); - loader.setDRACOLoader(dracoLoader); - loader.load('models/gltf/nemetona.glb', function (gltf) { - const model = gltf.scene; - model.rotation.y = Math.PI; - model.scale.setScalar(10); - model.position.y = 0.45; - - scene.add(model); - - model.traverse(function (object) { - if (object.isMesh) { - object.castShadow = true; - object.receiveShadow = true; - object.material.aoMap = null; // remove AO to better see the effect of shadows - } - }); - }); - - // - - renderer = new THREE.WebGPURenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.shadowMap.enabled = true; - renderer.shadowMap.type = THREE.PCFSoftShadowMap; - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // post-processing - - renderPipeline = new THREE.RenderPipeline(renderer); - - // pre-pass - - const prePass = pass(scene, camera); - prePass.name = 'Pre-Pass'; - prePass.transparent = false; - - prePass.setMRT( - mrt({ - output: velocity, - }), - ); - - const prePassDepth = prePass.getTextureNode('depth').toInspector('Depth', () => prePass.getLinearDepthNode()); - const prePassVelocity = prePass.getTextureNode('output').toInspector('Velocity'); - - // scene pass - - const scenePass = pass(scene, camera).toInspector('Color'); - - // sss - - const sssPass = sss(prePassDepth, camera, dirLight); - sssPass.maxDistance.value = 0.2; - sssPass.useTemporalFiltering = true; - - // scene context - - const sssSample = sssPass.getTextureNode().sample(screenUV).r; - const sssContext = builtinShadowContext(sssSample, dirLight); - - scenePass.contextNode = sssContext; - - // traa - - const traaPass = traa(scenePass, prePassDepth, prePassVelocity, camera); - renderPipeline.outputNode = traaPass; - - // - - controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 1; - controls.maxDistance = 20; - controls.target.set(0, 2, 0); - controls.enableDamping = true; - controls.update(); - - // - - const params = { - output: 0, - }; - - const types = { 'Scene with Shadow Maps + SSS': 0, 'Scene with Shadow Maps': 1, SSS: 2 }; - - const gui = renderer.inspector.createParameters('SSS settings'); - gui.add(params, 'output', types).onChange(updatePostprocessing); - gui.add(sssPass.shadowIntensity, 'value', 0, 1).name('shadow intensity'); - gui.add(sssPass.maxDistance, 'value', 0.01, 1).name('max ray distance'); - gui.add(sssPass.quality, 'value', 0, 1).name('quality'); - gui.add(sssPass.thickness, 'value', 0.01, 0.1).name('thickness'); - gui.add(sssPass, 'useTemporalFiltering').name('temporal filtering').onChange(updatePostprocessing); - - function updatePostprocessing() { - scenePass.contextNode = params.output !== 1 ? sssContext : null; - - if (params.output === 2) { - renderPipeline.outputNode = vec4(vec3(sssPass.r), 1); - } else { - renderPipeline.outputNode = sssPass.useTemporalFiltering ? traaPass : scenePass; - } - - renderPipeline.needsUpdate = true; - } - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); -} - -function animate() { - controls.update(); - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_postprocessing_traa.ts b/examples-testing/examples/webgpu_postprocessing_traa.ts deleted file mode 100644 index 01d17ca17..000000000 --- a/examples-testing/examples/webgpu_postprocessing_traa.ts +++ /dev/null @@ -1,93 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { mrt, output, pass, velocity } from 'three/tsl'; -import { traa } from 'three/addons/tsl/display/TRAANode.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer, renderPipeline; -let index = 0; - -init(); - -function init() { - renderer = new THREE.WebGPURenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 10); - camera.position.z = 2.5; - - scene = new THREE.Scene(); - - const geometry = new THREE.BoxGeometry(); - const material1 = new THREE.MeshBasicMaterial({ color: 0xffffff, wireframe: true }); - - const mesh1 = new THREE.Mesh(geometry, material1); - mesh1.position.x = -1; - scene.add(mesh1); - - const texture = new THREE.TextureLoader().load('textures/brick_diffuse.jpg'); - texture.minFilter = THREE.NearestFilter; - texture.magFilter = THREE.NearestFilter; - texture.generateMipmaps = false; - texture.colorSpace = THREE.SRGBColorSpace; - - const material2 = new THREE.MeshBasicMaterial({ map: texture }); - - const mesh2 = new THREE.Mesh(geometry, material2); - mesh2.position.x = 1; - scene.add(mesh2); - - // postprocessing - - renderPipeline = new THREE.RenderPipeline(renderer); - const scenePass = pass(scene, camera); - scenePass.setMRT( - mrt({ - output: output, - velocity: velocity, - }), - ); - - const scenePassColor = scenePass.getTextureNode('output').toInspector('Color'); - const scenePassDepth = scenePass.getTextureNode('depth').toInspector('Depth', () => { - return scenePass.getLinearDepthNode(); - }); - const scenePassVelocity = scenePass.getTextureNode('velocity').toInspector('Velocity'); - - const traaNode = traa(scenePassColor, scenePassDepth, scenePassVelocity, camera); - - renderPipeline.outputNode = traaNode; - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); -} - -function animate() { - index++; - - if (Math.round(index / 200) % 2 === 0) { - for (let i = 0; i < scene.children.length; i++) { - const child = scene.children[i]; - - child.rotation.x += 0.005; - child.rotation.y += 0.01; - } - } - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_postprocessing_transition.ts b/examples-testing/examples/webgpu_postprocessing_transition.ts deleted file mode 100644 index 1a5a88fc1..000000000 --- a/examples-testing/examples/webgpu_postprocessing_transition.ts +++ /dev/null @@ -1,207 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import TWEEN from 'three/addons/libs/tween.module.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { uniform, pass } from 'three/tsl'; -import { transition } from 'three/addons/tsl/display/TransitionNode.js'; - -let renderer, renderPipeline, transitionController, transitionPass; - -const textures = []; -const timer = new THREE.Timer(); -timer.connect(document); - -const effectController = { - animateScene: true, - animateTransition: true, - transition: 0, - _transition: uniform(0), - useTexture: true, - _useTexture: uniform(1), - texture: 5, - cycle: true, - threshold: uniform(0.1), -}; - -function generateInstancedMesh(geometry, material, count) { - const mesh = new THREE.InstancedMesh(geometry, material, count); - - const dummy = new THREE.Object3D(); - const color = new THREE.Color(); - - for (let i = 0; i < count; i++) { - dummy.position.x = Math.random() * 100 - 50; - dummy.position.y = Math.random() * 60 - 30; - dummy.position.z = Math.random() * 80 - 40; - - dummy.rotation.x = Math.random() * 2 * Math.PI; - dummy.rotation.y = Math.random() * 2 * Math.PI; - dummy.rotation.z = Math.random() * 2 * Math.PI; - - dummy.scale.x = Math.random() * 2 + 1; - - if (geometry.type === 'BoxGeometry') { - dummy.scale.y = Math.random() * 2 + 1; - dummy.scale.z = Math.random() * 2 + 1; - } else { - dummy.scale.y = dummy.scale.x; - dummy.scale.z = dummy.scale.x; - } - - dummy.updateMatrix(); - - mesh.setMatrixAt(i, dummy.matrix); - mesh.setColorAt(i, color.setScalar(0.1 + 0.9 * Math.random())); - } - - return mesh; -} - -function FXScene(geometry, rotationSpeed, backgroundColor) { - const camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.z = 20; - - // Setup scene - const scene = new THREE.Scene(); - scene.background = new THREE.Color(backgroundColor); - scene.add(new THREE.AmbientLight(0xaaaaaa, 3)); - - const light = new THREE.DirectionalLight(0xffffff, 3); - light.position.set(0, 1, 4); - scene.add(light); - - this.rotationSpeed = rotationSpeed; - - const color = geometry.type === 'BoxGeometry' ? 0x0000ff : 0xff0000; - const material = new THREE.MeshPhongNodeMaterial({ color: color, flatShading: true }); - const mesh = generateInstancedMesh(geometry, material, 500); - scene.add(mesh); - - this.scene = scene; - this.camera = camera; - this.mesh = mesh; - - this.update = function (delta) { - if (effectController.animateScene) { - mesh.rotation.x += this.rotationSpeed.x * delta; - mesh.rotation.y += this.rotationSpeed.y * delta; - mesh.rotation.z += this.rotationSpeed.z * delta; - } - }; - - this.resize = function () { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - }; -} - -const fxSceneA = new FXScene(new THREE.BoxGeometry(2, 2, 2), new THREE.Vector3(0, -0.4, 0), 0xffffff); -const fxSceneB = new FXScene(new THREE.IcosahedronGeometry(1, 1), new THREE.Vector3(0, 0.2, 0.1), 0x000000); - -function init() { - // Initialize textures - - const loader = new THREE.TextureLoader(); - - for (let i = 0; i < 6; i++) { - textures[i] = loader.load('textures/transition/transition' + (i + 1) + '.png'); - } - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.inspector = new Inspector(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - renderPipeline = new THREE.RenderPipeline(renderer); - - const scenePassA = pass(fxSceneA.scene, fxSceneA.camera); - const scenePassB = pass(fxSceneB.scene, fxSceneB.camera); - - transitionPass = transition( - scenePassA, - scenePassB, - new THREE.TextureNode(textures[effectController.texture]), - effectController._transition, - effectController.threshold, - effectController._useTexture, - ); - - renderPipeline.outputNode = transitionPass; - - const gui = renderer.inspector.createParameters('Settings'); - - gui.add(effectController, 'animateScene').name('Animate Scene'); - gui.add(effectController, 'animateTransition').name('Animate Transition'); - transitionController = gui - .add(effectController, 'transition', 0, 1, 0.01) - .name('transition') - .onChange(() => { - effectController._transition.value = effectController.transition; - }); - gui.add(effectController, 'useTexture').onChange(() => { - const value = effectController.useTexture ? 1 : 0; - effectController._useTexture.value = value; - }); - gui.add(effectController, 'texture', { Perlin: 0, Squares: 1, Cells: 2, Distort: 3, Gradient: 4, Radial: 5 }); - gui.add(effectController, 'cycle'); - gui.add(effectController.threshold, 'value', 0, 1, 0.01).name('threshold'); -} - -window.addEventListener('resize', onWindowResize); - -function onWindowResize() { - fxSceneA.resize(); - fxSceneB.resize(); - renderer.setSize(window.innerWidth, window.innerHeight); -} - -new TWEEN.Tween(effectController) - .to({ transition: 1 }, 1500) - .onUpdate(function () { - transitionController.setValue(effectController.transition); - - // Change the current alpha texture after each transition - if (effectController.cycle) { - if (effectController.transition == 0 || effectController.transition == 1) { - effectController.texture = (effectController.texture + 1) % textures.length; - } - } - }) - .repeat(Infinity) - .delay(2000) - .yoyo(true) - .start(); - -function animate() { - timer.update(); - - if (effectController.animateTransition) TWEEN.update(); - - if (textures[effectController.texture]) { - const mixTexture = textures[effectController.texture]; - transitionPass.mixTextureNode.value = mixTexture; - } - - const delta = timer.getDelta(); - fxSceneA.update(delta); - fxSceneB.update(delta); - - render(); -} - -function render() { - // Prevent render both scenes when it's not necessary - if (effectController.transition === 0) { - renderer.render(fxSceneB.scene, fxSceneB.camera); - } else if (effectController.transition === 1) { - renderer.render(fxSceneA.scene, fxSceneA.camera); - } else { - renderPipeline.render(); - } -} - -init(); diff --git a/examples-testing/examples/webgpu_procedural_texture.ts b/examples-testing/examples/webgpu_procedural_texture.ts deleted file mode 100644 index 21fb4a3c2..000000000 --- a/examples-testing/examples/webgpu_procedural_texture.ts +++ /dev/null @@ -1,75 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { checker, uv, uniform, convertToTexture } from 'three/tsl'; -import { gaussianBlur } from 'three/addons/tsl/display/GaussianBlurNode.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer; - -init(); - -function init() { - const aspect = window.innerWidth / window.innerHeight; - camera = new THREE.OrthographicCamera(-aspect, aspect, 1, -1, 0, 2); - camera.position.z = 1; - - scene = new THREE.Scene(); - - // procedural to texture - - const uvScale = uniform(4); - const blurAmount = uniform(0.5); - - const procedural = checker(uv().mul(uvScale)); - const proceduralToTexture = convertToTexture(procedural, 512, 512); // ( node, width, height ) - - const colorNode = gaussianBlur(proceduralToTexture, blurAmount, 20); - - // extra - - //proceduralToTexture.autoUpdate = false; // update just once - //proceduralToTexture.textureNeedsUpdate = true; // manually update - - // scene - - const material = new THREE.MeshBasicNodeMaterial(); - material.colorNode = colorNode; - - const plane = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), material); - scene.add(plane); - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(render); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - window.addEventListener('resize', onWindowResize); - - // gui - - const gui = renderer.inspector.createParameters('Procedural Texture'); - gui.add(uvScale, 'value', 1, 10).name('uv scale ( before rtt )'); - gui.add(blurAmount, 'value', 0, 2).name('blur amount ( after rtt )'); - gui.add(proceduralToTexture, 'autoUpdate').name('auto update'); -} - -function onWindowResize() { - renderer.setSize(window.innerWidth, window.innerHeight); - - const aspect = window.innerWidth / window.innerHeight; - - const frustumHeight = camera.top - camera.bottom; - - camera.left = (-frustumHeight * aspect) / 2; - camera.right = (frustumHeight * aspect) / 2; - - camera.updateProjectionMatrix(); -} - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_reflection.ts b/examples-testing/examples/webgpu_reflection.ts deleted file mode 100644 index d5dd23318..000000000 --- a/examples-testing/examples/webgpu_reflection.ts +++ /dev/null @@ -1,345 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { - abs, - blendOverlay, - color, - float, - Fn, - instancedBufferAttribute, - max, - normalWorldGeometry, - pass, - positionGeometry, - positionLocal, - pow2, - reflector, - screenUV, - sin, - sub, - texture, - time, - uniform, - uv, - vec2, - vec3, -} from 'three/tsl'; -import { gaussianBlur } from 'three/addons/tsl/display/GaussianBlurNode.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import TWEEN from 'three/addons/libs/tween.module.js'; - -let camera, scene, renderer; -let renderPipeline; -let controls; - -// below uniforms will be animated via TWEEN.js - -const uniformEffector1 = uniform(-0.2); -const uniformEffector2 = uniform(-0.2); - -init(); - -async function init() { - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.25, 30); - camera.position.set(4, 2, 4); - - scene = new THREE.Scene(); - scene.fog = new THREE.Fog(0x4195a4, 1, 20); - scene.backgroundNode = normalWorldGeometry.y.mix(color(0x4195a4), color(0x0066ff)); - camera.lookAt(0, 1, 0); - - const sunLight = new THREE.DirectionalLight(0xffe499, 2); - sunLight.position.set(7, 5, 7); - sunLight.castShadow = true; - sunLight.shadow.camera.zoom = 1.5; - sunLight.shadow.mapSize.set(1024, 1024); - sunLight.shadow.bias = -0.0001; // remove self-shadowing artifacts - scene.add(sunLight); - - const backLight = new THREE.DirectionalLight(0x0487e2, 0.5); - backLight.position.set(7, -5, 7); - scene.add(backLight); - - // textures - - const textureLoader = new THREE.TextureLoader(); - - const floorColor = await textureLoader.loadAsync('textures/floors/FloorsCheckerboard_S_Diffuse.jpg'); - floorColor.wrapS = THREE.RepeatWrapping; - floorColor.wrapT = THREE.RepeatWrapping; - floorColor.colorSpace = THREE.SRGBColorSpace; - floorColor.repeat.set(15, 15); - - const floorNormal = await textureLoader.loadAsync('textures/floors/FloorsCheckerboard_S_Normal.jpg'); - floorNormal.wrapS = THREE.RepeatWrapping; - floorNormal.wrapT = THREE.RepeatWrapping; - floorNormal.repeat.set(15, 15); - - // tree - - const treeMesh = createTreeMesh(); - treeMesh.castShadow = true; - treeMesh.receiveShadow = true; - scene.add(treeMesh); - - // floor - - const floorUV = uv().mul(15); - const floorNormalOffset = texture(floorNormal, floorUV).xy.mul(2).sub(1).mul(0.02); - - const reflection = reflector({ resolutionScale: 0.2 }); - reflection.target.rotateX(-Math.PI / 2); - reflection.uvNode = reflection.uvNode.add(floorNormalOffset); - scene.add(reflection.target); - - const floorMaterial = new THREE.MeshPhongNodeMaterial(); - floorMaterial.colorNode = texture(floorColor, floorUV); - floorMaterial.emissiveNode = reflection.mul(0.25); - floorMaterial.normalMap = floorNormal; - floorMaterial.normalScale.set(0.2, -0.2); - - const floor = new THREE.Mesh(new THREE.BoxGeometry(50, 0.001, 50), floorMaterial); - floor.receiveShadow = true; - scene.add(floor); - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.shadowMap.enabled = true; - renderer.shadowMap.type = THREE.PCFSoftShadowMap; - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // controls - - controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 1; - controls.maxDistance = 10; - controls.maxPolarAngle = Math.PI / 2; - controls.enableDamping = true; - controls.autoRotate = true; - controls.autoRotateSpeed = 1; - controls.target.set(0, 1, 0); - controls.update(); - - // post-processing - - const scenePass = pass(scene, camera); - const scenePassColor = scenePass.getTextureNode(); - const scenePassDepth = scenePass.getLinearDepthNode().remapClamp(0.3, 0.7); - - const scenePassColorBlurred = gaussianBlur(scenePassColor); - scenePassColorBlurred.directionNode = scenePassDepth; - - const vignette = screenUV.distance(0.5).mul(1.25).clamp().oneMinus().sub(0.2); - - renderPipeline = new THREE.RenderPipeline(renderer); - renderPipeline.outputNode = blendOverlay(scenePassColorBlurred, vignette); - - // tweens - - new TWEEN.Tween(uniformEffector1) - .to({ value: 1.2 }, 3000) - .delay(800) - .repeat(Infinity) - .easing(TWEEN.Easing.Sinusoidal.InOut) - .start(); - - new TWEEN.Tween(uniformEffector2) - .to({ value: 1.2 }, 3000) - .repeat(Infinity) - .easing(TWEEN.Easing.Sinusoidal.InOut) - .start(); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - controls.update(); - - TWEEN.update(); - - renderPipeline.render(); -} - -function random() { - return (Math.random() - 0.5) * 2.0; -} - -function createTreeMesh() { - const maxSteps = 5; - const lengthMult = 0.8; - - const positions = []; - const normals = []; - const colors = []; - const data = []; // will save seed, size and time - - let instanceCount = 0; - - const newPosition = new THREE.Vector3(); - const position = new THREE.Vector3(); - const normal = new THREE.Vector3(); - const color = new THREE.Color(); - - function createTreePart(angle, x, y, z, length, count) { - if (Math.random() > (maxSteps / count) * 0.25) return; - - if (count < maxSteps) { - const newLength = length * lengthMult; - const newX = x + Math.cos(angle) * length; - const newY = y + Math.sin(angle) * length; - const countSq = Math.min(3.2, count * count); - const newZ = z + (Math.random() * countSq - countSq / 2) * length; - - let size = 30 - count * 8; - if (size > 25) size = 25; - if (size < 10) size = 10; - - size = size / 100; - - const subSteps = 50; - - // below loop generates the instanced data for a tree part - - for (let i = 0; i < subSteps; i++) { - instanceCount++; - - const percent = i / subSteps; - const extra = 1 / maxSteps; - - // position - - newPosition.set(x, y, z).lerp(new THREE.Vector3(newX, newY, newZ), percent); - position.copy(newPosition); - - position.x += random() * size * 3; - position.y += random() * size * 3; - position.z += random() * size * 3; - - positions.push(position.x, position.y, position.z); - - const scale = Math.random() + 5; - - // normal - - normal.copy(position).sub(newPosition).normalize(); - normals.push(normal.x, normal.y, normal.z); - - // color - - color.setHSL((count / maxSteps) * 0.5 + Math.random() * 0.05, 0.75, 0.6 + Math.random() * 0.1); - colors.push(color.r, color.g, color.b); - - // to save vertex buffers, we store the size, time and seed in a single attribute - - const instanceSize = size * scale; - const instanceTime = count / maxSteps + percent * extra; - const instanceSeed = Math.random(); - - data.push(instanceSize, instanceTime, instanceSeed); - } - - createTreePart(angle + random(), newX, newY, newZ, newLength + random(), count + 1); - createTreePart(angle + random(), newX, newY, newZ, newLength + random(), count + 1); - createTreePart(angle + random(), newX, newY, newZ, newLength + random(), count + 1); - createTreePart(angle + random(), newX, newY, newZ, newLength + random(), count + 1); - createTreePart(angle + random(), newX, newY, newZ, newLength + random(), count + 1); - createTreePart(angle + random(), newX, newY, newZ, newLength + random(), count + 1); - } - } - - const angle = Math.PI * 0.5; - - // the tree is represented as a collection of instances boxes generated with below recursive function - - createTreePart(angle, 0, 0, 0, 16, 0); - - const geometry = new THREE.BoxGeometry(); - const material = new THREE.MeshStandardNodeMaterial(); - const mesh = new THREE.Mesh(geometry, material); - mesh.scale.setScalar(0.05); - mesh.count = instanceCount; - mesh.frustumCulled = false; - - // instanced data - - const attributePosition = new THREE.InstancedBufferAttribute(new Float32Array(positions), 3); - const attributeNormal = new THREE.InstancedBufferAttribute(new Float32Array(normals), 3); - const attributeColor = new THREE.InstancedBufferAttribute(new Float32Array(colors), 3); - const attributeData = new THREE.InstancedBufferAttribute(new Float32Array(data), 3); - - // TSL - - const instancePosition = instancedBufferAttribute(attributePosition); - const instanceNormal = instancedBufferAttribute(attributeNormal); - const instanceColor = instancedBufferAttribute(attributeColor); - const instanceData = instancedBufferAttribute(attributeData); - - material.positionNode = Fn(() => { - const instanceSize = instanceData.x; - const instanceTime = instanceData.y; - const instanceSeed = instanceData.z; - - // effectors (these are responsible for the blob-like scale effects) - - const dif1 = abs(instanceTime.sub(uniformEffector1)).toConst(); - let effect = dif1.lessThanEqual(0.15).select(sub(0.15, dif1).mul(sub(1.7, instanceTime).mul(10)), float(0)); - - const dif2 = abs(instanceTime.sub(uniformEffector2)).toConst(); - effect = dif2.lessThanEqual(0.15).select(sub(0.15, dif2).mul(sub(1.7, instanceTime).mul(10)), effect); - - // accumulate different vertex animations - - let animated = positionLocal.add(instancePosition).toVar(); - const direction = positionGeometry.normalize().toConst(); - - animated = animated.add(direction.mul(effect.add(instanceSize))); - animated = animated.sub(direction.mul(effect)); - animated = animated.add(instanceNormal.mul(effect.mul(1))); - animated = animated.add(instanceNormal.mul(abs(sin(time.add(instanceSeed.mul(2))).mul(1.5)))); - - return animated; - })(); - - const squareEdge = Fn(() => { - const pos = uv().sub(vec2(0.5, 0.5)); - const squareDistance = max(abs(pos.x), abs(pos.y)); - return squareDistance.div(0.5).clamp(0.85, 1).sub(0.5).mul(2.0); - })(); - - material.colorNode = Fn(() => { - return squareEdge.sub(instanceColor); - })(); - - material.emissiveNode = Fn(() => { - const instanceTime = instanceData.y; - - const dif1 = abs(instanceTime.sub(uniformEffector1)).toConst(); - const effect1 = dif1.lessThanEqual(0.15).select(sub(0.15, dif1).mul(sub(1.7, instanceTime).mul(10)), float(0)); - - const dif2 = abs(instanceTime.sub(uniformEffector2)).toConst(); - const effect2 = dif2.lessThanEqual(0.15).select(sub(0.15, dif2).mul(sub(1.7, instanceTime).mul(10)), effect1); - - return pow2(vec3(effect1, 0, effect2)).mul(instanceColor); - })(); - - return mesh; -} diff --git a/examples-testing/examples/webgpu_reflection_roughness.ts b/examples-testing/examples/webgpu_reflection_roughness.ts deleted file mode 100644 index 2b6b4a78d..000000000 --- a/examples-testing/examples/webgpu_reflection_roughness.ts +++ /dev/null @@ -1,121 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { Fn, vec2, vec4, texture, uv, textureBicubic, rangeFogFactor, reflector, time } from 'three/tsl'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer; -let controls; - -init(); - -async function init() { - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.25, 30); - camera.position.set(-4, 1, 4); - - scene = new THREE.Scene(); - - const loader = new UltraHDRLoader(); - loader.load('textures/equirectangular/spruit_sunrise_2k.hdr.jpg', function (texture) { - texture.mapping = THREE.EquirectangularReflectionMapping; - texture.needsUpdate = true; - - scene.background = texture; - scene.environment = texture; - }); - - // textures - - const textureLoader = new THREE.TextureLoader(); - - const uvMap = textureLoader.load('textures/uv_grid_directx.jpg'); - uvMap.colorSpace = THREE.SRGBColorSpace; - - const perlinMap = textureLoader.load('./textures/noises/perlin/rgb-256x256.png'); - perlinMap.wrapS = THREE.RepeatWrapping; - perlinMap.wrapT = THREE.RepeatWrapping; - perlinMap.colorSpace = THREE.SRGBColorSpace; - - // uv box for debugging - - const mesh = new THREE.Mesh( - new THREE.BoxGeometry(1, 1, 1), - new THREE.MeshStandardNodeMaterial({ - map: uvMap, - roughnessMap: uvMap, - emissiveMap: uvMap, - emissive: 0xffffff, - }), - ); - mesh.position.set(0, 1.25, 0); - mesh.scale.setScalar(2); - scene.add(mesh); - - // reflection - - const reflection = reflector({ resolutionScale: 0.5, bounces: false, generateMipmaps: true }); // 0.5 is half of the rendering view - reflection.target.rotateX(-Math.PI / 2); - scene.add(reflection.target); - - const animatedUV = uv() - .mul(10) - .add(vec2(time.mul(0.1), 0)); - const roughness = texture(perlinMap, animatedUV).r.mul(2).saturate(); - - const floorMaterial = new THREE.MeshStandardNodeMaterial(); - floorMaterial.transparent = true; - floorMaterial.metalness = 1; - floorMaterial.roughnessNode = roughness.mul(0.2); - floorMaterial.colorNode = Fn(() => { - // blur reflection using textureBicubic() - const dirtyReflection = textureBicubic(reflection, roughness.mul(0.9)); - - // falloff opacity by distance like an opacity-fog - const opacity = rangeFogFactor(7, 25).oneMinus(); - - return vec4(dirtyReflection.rgb, opacity); - })(); - - const floor = new THREE.Mesh(new THREE.BoxGeometry(50, 0.001, 50), floorMaterial); - floor.position.set(0, 0, 0); - scene.add(floor); - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.toneMapping = THREE.NeutralToneMapping; - renderer.toneMappingExposure = 1.5; - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 1; - controls.maxDistance = 10; - controls.maxPolarAngle = Math.PI / 2; - controls.autoRotate = true; - controls.autoRotateSpeed = -0.1; - controls.target.set(0, 0.75, 0); - controls.update(); - - // events - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - controls.update(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_refraction.ts b/examples-testing/examples/webgpu_refraction.ts deleted file mode 100644 index d475ed2de..000000000 --- a/examples-testing/examples/webgpu_refraction.ts +++ /dev/null @@ -1,144 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { viewportSafeUV, viewportSharedTexture, screenUV, texture, uv } from 'three/tsl'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer; - -let cameraControls; - -let smallSphere; - -init(); - -function init() { - // scene - scene = new THREE.Scene(); - - // camera - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 500); - camera.position.set(0, 50, 160); - - // - - const geometry = new THREE.IcosahedronGeometry(5, 0); - const material = new THREE.MeshPhongMaterial({ color: 0xffffff, emissive: 0x7b7b7b, flatShading: true }); - smallSphere = new THREE.Mesh(geometry, material); - scene.add(smallSphere); - - // textures - - const loader = new THREE.TextureLoader(); - - const floorNormal = loader.load('textures/floors/FloorsCheckerboard_S_Normal.jpg'); - floorNormal.wrapS = THREE.RepeatWrapping; - floorNormal.wrapT = THREE.RepeatWrapping; - - // refractor - - const verticalNormalScale = 0.1; - const verticalUVOffset = texture(floorNormal, uv().mul(5)).xy.mul(2).sub(1).mul(verticalNormalScale); - - const refractorUV = screenUV.add(verticalUVOffset); - const verticalRefractor = viewportSharedTexture(viewportSafeUV(refractorUV)).toInspector('Viewport Texture'); - - const planeGeo = new THREE.PlaneGeometry(100.1, 100.1); - - const planeRefractor = new THREE.Mesh( - planeGeo, - new THREE.MeshBasicNodeMaterial({ - backdropNode: verticalRefractor, - }), - ); - planeRefractor.material.transparent = true; - planeRefractor.position.y = 50; - scene.add(planeRefractor); - - // walls - - const planeTop = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xffffff })); - planeTop.position.y = 100; - planeTop.rotateX(Math.PI / 2); - scene.add(planeTop); - - const planeBottom = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xffffff })); - planeBottom.rotateX(-Math.PI / 2); - scene.add(planeBottom); - - const planeBack = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0x7f7fff })); - planeBack.position.z = -50; - planeBack.position.y = 50; - scene.add(planeBack); - - const planeRight = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0x00ff00 })); - planeRight.position.x = 50; - planeRight.position.y = 50; - planeRight.rotateY(-Math.PI / 2); - scene.add(planeRight); - - const planeLeft = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xff0000 })); - planeLeft.position.x = -50; - planeLeft.position.y = 50; - planeLeft.rotateY(Math.PI / 2); - scene.add(planeLeft); - - // lights - - const mainLight = new THREE.PointLight(0xe7e7e7, 2.5, 250, 0); - mainLight.position.y = 60; - scene.add(mainLight); - - const greenLight = new THREE.PointLight(0x00ff00, 0.5, 1000, 0); - greenLight.position.set(550, 50, 0); - scene.add(greenLight); - - const redLight = new THREE.PointLight(0xff0000, 0.5, 1000, 0); - redLight.position.set(-550, 50, 0); - scene.add(redLight); - - const blueLight = new THREE.PointLight(0xbbbbfe, 0.5, 1000, 0); - blueLight.position.set(0, 50, 550); - scene.add(blueLight); - - // renderer - - renderer = new THREE.WebGPURenderer(/*{ antialias: true }*/); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // controls - - cameraControls = new OrbitControls(camera, renderer.domElement); - cameraControls.target.set(0, 50, 0); - cameraControls.maxDistance = 400; - cameraControls.minDistance = 10; - cameraControls.update(); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - const timer = Date.now() * 0.01; - - smallSphere.position.set( - Math.cos(timer * 0.1) * 30, - Math.abs(Math.cos(timer * 0.2)) * 20 + 5, - Math.sin(timer * 0.1) * 30, - ); - smallSphere.rotation.y = Math.PI / 2 - timer * 0.1; - smallSphere.rotation.z = timer * 0.8; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_rendertarget_2d-array_3d.ts b/examples-testing/examples/webgpu_rendertarget_2d-array_3d.ts deleted file mode 100644 index 627fd6b18..000000000 --- a/examples-testing/examples/webgpu_rendertarget_2d-array_3d.ts +++ /dev/null @@ -1,237 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { vec2, uniform, screenUV, color, texture, diffuseColor, attribute, vec3, vec4 } from 'three/tsl'; - -import { TextureHelper } from 'three/addons/helpers/TextureHelperGPU.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { unzipSync } from 'three/addons/libs/fflate.module.js'; - -let renderer; -let views = []; - -class View { - constructor(left, top, width, height) { - this.left = left; - this.top = top; - this.width = width; - this.height = height; - - const aspect = (window.innerWidth * width) / (window.innerHeight * height); - - // Set up perspective camera - this.camera = new THREE.PerspectiveCamera(50, aspect, 0.1, 100); - this.camera.position.set(-7, 0, 10); - this.camera.lookAt(0, 0, 0); - this.camera.updateProjectionMatrix(); - - this.scene = new THREE.Scene(); - - const normalizedUV = screenUV.mul(vec2(1, -1)).add(vec2(0, 1)); // Flip Y and offset - - // Calculate viewport center in normalized coordinates - const viewportCenter = vec2( - this.left + this.width * 0.5, - this.top + this.height * 0.5, // Invert Y coordinate for proper alignment - ); - - const distanceEffect = normalizedUV.distance(viewportCenter).smoothstep(0, 0.2); - - const backgroundEffect = color(this.top > 0 ? 0x212121 : 0x616161).sub(distanceEffect.pow(0.3).mul(0.1)); - - this.scene.backgroundNode = backgroundEffect; - } - - // Method to handle viewport resize - updateSize(left, top, width, height) { - this.left = left; - this.top = top; - this.width = width; - this.height = height; - - const aspect = (window.innerWidth * width) / (window.innerHeight * height); - this.camera.aspect = aspect; - this.camera.updateProjectionMatrix(); - } -} - -async function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - renderer = new THREE.WebGPURenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.autoClear = false; - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - await renderer.init(); - - // Create views after renderer initialization - views = [ - new View(0.0, 0.0, 0.5, 0.5), - new View(0.5, 0.0, 0.5, 0.5), - new View(0.0, 0.5, 0.5, 0.5), - new View(0.5, 0.5, 0.5, 0.5), - ]; - - // Add OrbitControls after views and renderer are created - views.forEach(view => { - view.controls = new OrbitControls(view.camera, renderer.domElement); - view.controls.minDistance = 1; - view.controls.maxDistance = 20; - view.controls.minAzimuthAngle = -Math.PI / 3; - view.controls.maxAzimuthAngle = Math.PI / 3; - view.controls.minPolarAngle = Math.PI / 4; - view.controls.maxPolarAngle = Math.PI / 1.25; - view.controls.enableDamping = true; - }); - - const size = { - width: 256, - height: 256, - depth: 109, - }; - - new THREE.FileLoader().setResponseType('arraybuffer').load('textures/3d/head256x256x109.zip', function (data) { - const zip = unzipSync(new Uint8Array(data)); - const array = new Uint8Array(zip['head256x256x109'].buffer); - - const map3D = new THREE.Data3DTexture(array, size.width, size.height, size.depth); - map3D.name = 'Data3DTexture'; - map3D.format = THREE.RedFormat; - map3D.minFilter = THREE.LinearFilter; - map3D.magFilter = THREE.LinearFilter; - map3D.unpackAlignment = 1; - map3D.needsUpdate = true; - - const depth = size.depth / 20; - - // 3D - const helper3D = new TextureHelper(map3D, 10, 10, depth); - helper3D.material.outputNode = vec4( - vec3(diffuseColor.r.mul(attribute('uvw').z.mul(diffuseColor.r))), - diffuseColor.r.mul(diffuseColor.a), - ); - views[1].scene.add(helper3D); - - const fbo3D = new THREE.RenderTarget3D(size.width, size.height, size.depth, { - depthBuffer: false, - }); - fbo3D.texture.name = 'RenderTarget3D'; - - const fbo3DHelper = new TextureHelper(fbo3D.texture, 10, 10, depth); - fbo3DHelper.material.outputNode = vec4(vec3(diffuseColor.r), diffuseColor.r); - views[3].scene.add(fbo3DHelper); - - // 2D Array - - const mapArray = new THREE.DataArrayTexture(array, size.width, size.height, size.depth); - mapArray.name = 'DataArrayTexture'; - mapArray.format = THREE.RedFormat; - mapArray.minFilter = THREE.LinearFilter; - mapArray.magFilter = THREE.LinearFilter; - mapArray.unpackAlignment = 1; - mapArray.needsUpdate = true; - - const helperArray = new TextureHelper(mapArray, 10, 10, depth); - helperArray.material.outputNode = vec4( - vec3(diffuseColor.r.mul(attribute('uvw').z.div(size.depth).mul(diffuseColor.r))), - diffuseColor.r.mul(diffuseColor.a), - ); - views[0].scene.add(helperArray); - - // Setup render targets - const materialQuad = new THREE.NodeMaterial(); - const uZCoord = uniform(0); - materialQuad.depthTest = false; - materialQuad.outputNode = vec4(texture(mapArray).depth(uZCoord).rgb, 1); - - const fboArray = new THREE.RenderTarget(size.width, size.height, { - depthBuffer: false, - depth: size.depth, - }); - fboArray.texture.name = 'RenderTargetArray'; - - const fboArrayHelper = new TextureHelper(fboArray.texture, 10, 10, depth); - fboArrayHelper.material.outputNode = vec4(vec3(diffuseColor.r), diffuseColor.r); - views[2].scene.add(fboArrayHelper); - - const quadMesh = new THREE.QuadMesh(materialQuad); - - // In WebGPU we need to clear all the layers of the 3D render target before rendering to it (WebGPU limitation?) - if (renderer.backend.isWebGPUBackend) { - const materialClear = new THREE.NodeMaterial(); - materialClear.outputNode = vec4(0); - const clearQuadMesh = new THREE.QuadMesh(materialClear); - for (let i = 0; i < size.depth; i++) { - renderer.setRenderTarget(fbo3D, i); - clearQuadMesh.render(renderer); - } - } - - let j = 0; - - const loop = () => { - if (j === size.depth) { - clearInterval(interval); - return; - } - - // Disable viewport and scissor for FBO rendering - renderer.setViewport(0, 0, size.width, size.height); - renderer.setScissor(0, 0, size.width, size.height); - renderer.setScissorTest(false); - - uZCoord.value = j; - - renderer.setRenderTarget(fboArray, j); - renderer.clear(); - quadMesh.render(renderer); - - renderer.setRenderTarget(fbo3D, j); - renderer.clear(); - quadMesh.render(renderer); - - renderer.setRenderTarget(null); - - j = (j + 1) % size.depth; - }; - - const interval = setInterval(loop, 50); - - loop(); - }); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - renderer.setSize(width, height); - - views.forEach(view => { - view.updateSize(view.left, view.top, view.width, view.height); - }); -} - -function animate() { - views.forEach(view => { - view.controls.update(); - - const left = Math.floor(view.left * window.innerWidth); - const bottom = Math.floor((1 - view.top - view.height) * window.innerHeight); - const width = Math.floor(view.width * window.innerWidth); - const height = Math.floor(view.height * window.innerHeight); - - renderer.setViewport(left, bottom, width, height); - renderer.setScissor(left, bottom, width, height); - renderer.setScissorTest(true); - - renderer.clear(); - renderer.render(view.scene, view.camera); - }); -} - -init(); diff --git a/examples-testing/examples/webgpu_reversed_depth_buffer.ts b/examples-testing/examples/webgpu_reversed_depth_buffer.ts deleted file mode 100644 index d159748f5..000000000 --- a/examples-testing/examples/webgpu_reversed_depth_buffer.ts +++ /dev/null @@ -1,169 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { pass } from 'three/tsl'; - -let camera, reversedCamera, scene; -let normalRenderer, logarithmicRenderer, reverseRenderer; -let normalRenderPipeline, logarithmicRenderPipeline, reverseRenderPipeline; -const meshes = []; - -init().then(animate); - -async function init() { - camera = new THREE.PerspectiveCamera(72, (0.33 * window.innerWidth) / window.innerHeight, 5, 9999); - camera.position.z = 12; - - reversedCamera = camera.clone(); - - scene = new THREE.Scene(); - - const xCount = 1; - const yCount = 5; - const numInstances = xCount * yCount; - - const d = 0.0001; // half distance between two planes - const o = 0.5; // half x offset to shift planes so they are only partially overlapping - - const positions = new Float32Array([ - -1 - o, - -1, - d, - 1 - o, - -1, - d, - -1 - o, - 1, - d, - 1 - o, - -1, - d, - 1 - o, - 1, - d, - -1 - o, - 1, - d, - - -1 + o, - -1, - -d, - 1 + o, - -1, - -d, - -1 + o, - 1, - -d, - 1 + o, - -1, - -d, - 1 + o, - 1, - -d, - -1 + o, - 1, - -d, - ]); - - const colors = new Float32Array([ - 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, - - 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, - ]); - - const geometry = new THREE.BufferGeometry(); - geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); - geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3)); - - const material = new THREE.MeshBasicMaterial({ vertexColors: true }); - - for (let i = 0; i < numInstances; i++) { - const mesh = new THREE.Mesh(geometry, material); - meshes.push(mesh); - scene.add(mesh); - } - - let i = 0; - for (let x = 0; x < xCount; x++) { - for (let y = 0; y < yCount; y++) { - const z = -800 * i; - const s = 1 + 50 * i; - - const mesh = meshes[i]; - mesh.position.set(x - xCount / 2 + 0.5, (4.0 - 0.2 * z) * (y - yCount / 2 + 1.0), z); - mesh.scale.setScalar(s); - - i++; - } - } - - const normalContainer = document.getElementById('container_normal'); - normalRenderer = new THREE.WebGPURenderer(); - normalRenderer.setPixelRatio(window.devicePixelRatio); - normalRenderer.setSize(0.33 * window.innerWidth, window.innerHeight); - normalRenderer.domElement.style.position = 'relative'; - normalContainer.appendChild(normalRenderer.domElement); - - const logarithmicContainer = document.getElementById('container_logarithmic'); - logarithmicRenderer = new THREE.WebGPURenderer({ logarithmicDepthBuffer: true }); - logarithmicRenderer.setPixelRatio(window.devicePixelRatio); - logarithmicRenderer.setSize(0.33 * window.innerWidth, window.innerHeight); - logarithmicRenderer.domElement.style.position = 'relative'; - logarithmicContainer.appendChild(logarithmicRenderer.domElement); - - const reverseContainer = document.getElementById('container_reverse'); - reverseRenderer = new THREE.WebGPURenderer({ reversedDepthBuffer: true }); - reverseRenderer.setPixelRatio(window.devicePixelRatio); - reverseRenderer.setSize(0.33 * window.innerWidth, window.innerHeight); - reverseRenderer.domElement.style.position = 'relative'; - reverseContainer.appendChild(reverseRenderer.domElement); - - // - - normalRenderPipeline = new THREE.RenderPipeline(normalRenderer); - normalRenderPipeline.outputNode = pass(scene, camera); - - logarithmicRenderPipeline = new THREE.RenderPipeline(logarithmicRenderer); - logarithmicRenderPipeline.outputNode = pass(scene, camera); - - reverseRenderPipeline = new THREE.RenderPipeline(reverseRenderer); - reverseRenderPipeline.outputNode = pass(scene, reversedCamera); - - await Promise.all([normalRenderer.init(), logarithmicRenderer.init(), reverseRenderer.init()]); - - window.addEventListener('resize', onWindowResize); - onWindowResize(); -} - -function animate() { - requestAnimationFrame(animate); - - const now = performance.now() / 1000; - - for (let i = 0; i < meshes.length; i++) { - const angle = THREE.MathUtils.degToRad(30); - const axis = new THREE.Vector3(Math.sin(now), Math.cos(now), 0); - meshes[i].quaternion.setFromAxisAngle(axis, angle); - } - - render(); -} - -function render() { - normalRenderPipeline.render(); - logarithmicRenderPipeline.render(); - reverseRenderPipeline.render(); -} - -function onWindowResize() { - const width = 0.33 * window.innerWidth; - const height = window.innerHeight; - - normalRenderer.setSize(width, height); - logarithmicRenderer.setSize(width, height); - reverseRenderer.setSize(width, height); - - camera.aspect = (0.33 * window.innerWidth) / window.innerHeight; - camera.updateProjectionMatrix(); - - reversedCamera.aspect = (0.33 * window.innerWidth) / window.innerHeight; - reversedCamera.updateProjectionMatrix(); -} diff --git a/examples-testing/examples/webgpu_rtt.ts b/examples-testing/examples/webgpu_rtt.ts deleted file mode 100644 index fa7577ea8..000000000 --- a/examples-testing/examples/webgpu_rtt.ts +++ /dev/null @@ -1,90 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { texture, uniform, saturation, hue } from 'three/tsl'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer; -const mouse = new THREE.Vector2(); - -let quadMesh, renderTarget; - -let box; - -const dpr = window.devicePixelRatio; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 10); - camera.position.z = 3; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x0066ff); - - // textured mesh - - const loader = new THREE.TextureLoader(); - const uvTexture = loader.load('./textures/uv_grid_opengl.jpg'); - - const geometryBox = new THREE.BoxGeometry(); - const materialBox = new THREE.MeshBasicNodeMaterial(); - materialBox.colorNode = texture(uvTexture); - - // - - box = new THREE.Mesh(geometryBox, materialBox); - scene.add(box); - - // - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(dpr); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - renderer.inspector = new Inspector(); - - renderTarget = new THREE.RenderTarget(window.innerWidth * dpr, window.innerHeight * dpr); - - window.addEventListener('mousemove', onWindowMouseMove); - window.addEventListener('resize', onWindowResize); - - // FX - - // modulate the final color based on the mouse position - - const screenFXNode = uniform(mouse); - - const materialFX = new THREE.MeshBasicNodeMaterial(); - - const scenePassTexture = texture(renderTarget.texture).toInspector('Scene Pass'); - materialFX.colorNode = hue(saturation(scenePassTexture.rgb, screenFXNode.x.oneMinus()), screenFXNode.y); - - quadMesh = new THREE.QuadMesh(materialFX); - quadMesh.name = 'Post-Processing'; -} - -function onWindowMouseMove(e) { - mouse.x = e.offsetX / window.innerWidth; - mouse.y = e.offsetY / window.innerHeight; -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); - renderTarget.setSize(window.innerWidth * dpr, window.innerHeight * dpr); -} - -function animate() { - box.rotation.x += 0.01; - box.rotation.y += 0.02; - - renderer.setRenderTarget(renderTarget); - renderer.render(scene, camera); - - renderer.setRenderTarget(null); - quadMesh.render(renderer); -} diff --git a/examples-testing/examples/webgpu_shadow_contact.ts b/examples-testing/examples/webgpu_shadow_contact.ts deleted file mode 100644 index ac3e4fbd8..000000000 --- a/examples-testing/examples/webgpu_shadow_contact.ts +++ /dev/null @@ -1,205 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { vec3, uniform, texture, depth, float } from 'three/tsl'; -import { gaussianBlur } from 'three/addons/tsl/display/GaussianBlurNode.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer; -let shadowCamera, shadowGroup; -let renderTarget; -let plane, fillPlane, cameraHelper; -let depthMaterial, shadowPlaneMaterial, fillPlaneMaterial; - -const meshes = []; - -const PLANE_WIDTH = 2.5; -const PLANE_HEIGHT = 2.5; -const CAMERA_HEIGHT = 0.3; - -const state = { - shadow: { blur: 3.5, darkness: 1.0, opacity: 1.0 }, - plane: { color: '#ffffff', opacity: 1.0 }, - showWireframe: false, -}; - -const uBlur = uniform(state.shadow.blur); -const uDarkness = uniform(state.shadow.darkness); -const uShadowOpacity = uniform(state.shadow.opacity); -const uPlaneOpacity = uniform(state.plane.opacity); -const uPlaneColor = uniform(new THREE.Color(state.plane.color)); -const uPlaneY = uniform(-0.3); - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(0.5, 1, 2); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xffffff); - - window.addEventListener('resize', onWindowResize); - - const geometries = [ - new THREE.BoxGeometry(0.4, 0.4, 0.4), - new THREE.IcosahedronGeometry(0.3), - new THREE.TorusKnotGeometry(0.4, 0.05, 256, 24, 1, 3), - ]; - - const material = new THREE.MeshNormalMaterial(); - - for (let i = 0, l = geometries.length; i < l; i++) { - const angle = (i / l) * Math.PI * 2; - const mesh = new THREE.Mesh(geometries[i], material); - mesh.position.y = 0.1; - mesh.position.x = Math.cos(angle) / 2.0; - mesh.position.z = Math.sin(angle) / 2.0; - scene.add(mesh); - meshes.push(mesh); - } - - shadowGroup = new THREE.Group(); - shadowGroup.position.y = uPlaneY.value; - scene.add(shadowGroup); - - renderTarget = new THREE.RenderTarget(512, 512, { depthBuffer: true }); - renderTarget.texture.generateMipmaps = false; - - const planeGeometry = new THREE.PlaneGeometry(PLANE_WIDTH, PLANE_HEIGHT).rotateX(Math.PI / 2); - - depthMaterial = new THREE.NodeMaterial(); - - const alphaDepth = float(1).sub(depth).mul(uDarkness); - - depthMaterial.colorNode = vec3(0); - depthMaterial.opacityNode = alphaDepth; - depthMaterial.depthTest = false; - depthMaterial.depthWrite = false; - - shadowPlaneMaterial = new THREE.NodeMaterial(); - shadowPlaneMaterial.transparent = true; - shadowPlaneMaterial.depthWrite = false; - - if (!renderTarget.texture.image) renderTarget.texture.image = { width: 512, height: 512 }; - - const blurredShadow = gaussianBlur(texture(renderTarget.texture), uBlur, 4, { premultipliedAlpha: false }); - shadowPlaneMaterial.colorNode = vec3(0); - shadowPlaneMaterial.opacityNode = blurredShadow.a.mul(uShadowOpacity); - - plane = new THREE.Mesh(planeGeometry, shadowPlaneMaterial); - plane.renderOrder = 1; - plane.scale.y = -1; - plane.scale.z = -1; - shadowGroup.add(plane); - - fillPlaneMaterial = new THREE.NodeMaterial(); - fillPlaneMaterial.transparent = true; - fillPlaneMaterial.depthWrite = false; - fillPlaneMaterial.colorNode = uPlaneColor; - fillPlaneMaterial.opacityNode = uPlaneOpacity; - fillPlane = new THREE.Mesh(planeGeometry, fillPlaneMaterial); - fillPlane.rotateX(Math.PI); - shadowGroup.add(fillPlane); - - shadowCamera = new THREE.OrthographicCamera( - -PLANE_WIDTH / 2, - PLANE_WIDTH / 2, - PLANE_HEIGHT / 2, - -PLANE_HEIGHT / 2, - 0, - CAMERA_HEIGHT, - ); - shadowCamera.rotation.x = Math.PI / 2; - shadowGroup.add(shadowCamera); - - cameraHelper = new THREE.CameraHelper(shadowCamera); - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - renderer.inspector = new Inspector(); - - const params = { - shadowBlur: state.shadow.blur, - shadowDarkness: state.shadow.darkness, - shadowOpacity: state.shadow.opacity, - planeColor: state.plane.color, - planeOpacity: state.plane.opacity, - showWireframe: state.showWireframe, - }; - - const gui = renderer.inspector.createParameters('Settings'); - - gui.add(params, 'shadowBlur', 0, 15, 0.1).onChange(() => { - state.shadow.blur = params.shadowBlur; - uBlur.value = state.shadow.blur; - }); - - gui.add(params, 'shadowDarkness', 0.1, 5, 0.1).onChange(() => { - state.shadow.darkness = params.shadowDarkness; - uDarkness.value = state.shadow.darkness; - }); - - gui.add(params, 'shadowOpacity', 0, 1, 0.01).onChange(() => { - state.shadow.opacity = params.shadowOpacity; - uShadowOpacity.value = state.shadow.opacity; - }); - - gui.addColor(params, 'planeColor').onChange(() => { - state.plane.color = params.planeColor; - uPlaneColor.value.set(state.plane.color); - }); - - gui.add(params, 'planeOpacity', 0, 1, 0.01).onChange(() => { - state.plane.opacity = params.planeOpacity; - uPlaneOpacity.value = state.plane.opacity; - }); - - gui.add(params, 'showWireframe').onChange(() => { - if (params.showWireframe) scene.add(cameraHelper); - else scene.remove(cameraHelper); - }); - - new OrbitControls(camera, renderer.domElement); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - meshes.forEach(m => { - m.rotation.x += 0.01; - m.rotation.y += 0.02; - }); - - const initialBackground = scene.background; - scene.background = null; - - const prevOverride = scene.overrideMaterial; - const prevHelperVisible = cameraHelper.visible; - cameraHelper.visible = false; - scene.overrideMaterial = depthMaterial; - const initialAutoClear = renderer.autoClear; - renderer.autoClear = true; - const initialClearAlpha = renderer.getClearAlpha ? renderer.getClearAlpha() : undefined; - if (initialClearAlpha !== undefined) renderer.setClearAlpha(0); - - renderer.setRenderTarget(renderTarget); - renderer.clear(); - renderer.render(scene, shadowCamera); - - scene.overrideMaterial = prevOverride; - renderer.setRenderTarget(null); - renderer.autoClear = initialAutoClear; - if (initialClearAlpha !== undefined) renderer.setClearAlpha(initialClearAlpha); - scene.background = initialBackground; - cameraHelper.visible = prevHelperVisible; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_shadowmap.ts b/examples-testing/examples/webgpu_shadowmap.ts deleted file mode 100644 index f6edec750..000000000 --- a/examples-testing/examples/webgpu_shadowmap.ts +++ /dev/null @@ -1,166 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { mx_fractal_noise_float, mx_fractal_noise_vec3, positionLocal, positionWorld, Fn, color } from 'three/tsl'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer, timer; -let dirLight, spotLight; -let torusKnot, dirGroup; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(0, 10, 20); - - scene = new THREE.Scene(); - scene.backgroundNode = color(0x222244); - scene.fog = new THREE.Fog(0x222244, 50, 100); - - // lights - - scene.add(new THREE.AmbientLight(0x444444, 2)); - - spotLight = new THREE.SpotLight(0xff8888, 400); - spotLight.angle = Math.PI / 5; - spotLight.penumbra = 0.3; - spotLight.position.set(8, 10, 5); - spotLight.castShadow = true; - spotLight.shadow.camera.near = 8; - spotLight.shadow.camera.far = 200; - spotLight.shadow.mapSize.width = 2048; - spotLight.shadow.mapSize.height = 2048; - spotLight.shadow.radius = 4; - scene.add(spotLight); - - dirLight = new THREE.DirectionalLight(0x8888ff, 3); - dirLight.position.set(3, 12, 17); - dirLight.castShadow = true; - dirLight.shadow.camera.near = 0.1; - dirLight.shadow.camera.far = 500; - dirLight.shadow.camera.right = 17; - dirLight.shadow.camera.left = -17; - dirLight.shadow.camera.top = 17; - dirLight.shadow.camera.bottom = -17; - dirLight.shadow.mapSize.width = 2048; - dirLight.shadow.mapSize.height = 2048; - dirLight.shadow.radius = 4; - - dirGroup = new THREE.Group(); - dirGroup.add(dirLight); - scene.add(dirGroup); - - // geometry - - const geometry = new THREE.TorusKnotGeometry(25, 8, 75, 80); - const material = new THREE.MeshPhongNodeMaterial({ - color: 0x999999, - shininess: 0, - specular: 0x222222, - }); - - const materialCustomShadow = material.clone(); - materialCustomShadow.transparent = true; - - const discardNode = mx_fractal_noise_float(positionLocal.mul(0.1)).x.greaterThan(0.0); - - materialCustomShadow.maskNode = discardNode; - - torusKnot = new THREE.Mesh(geometry, materialCustomShadow); - torusKnot.scale.multiplyScalar(1 / 18); - torusKnot.position.y = 3; - torusKnot.castShadow = true; - torusKnot.receiveShadow = true; - scene.add(torusKnot); - - const cylinderGeometry = new THREE.CylinderGeometry(0.75, 0.75, 7, 32); - - const pillar1 = new THREE.Mesh(cylinderGeometry, material); - pillar1.position.set(8, 3.5, 8); - pillar1.castShadow = true; - - const pillar2 = pillar1.clone(); - pillar2.position.set(8, 3.5, -8); - const pillar3 = pillar1.clone(); - pillar3.position.set(-8, 3.5, 8); - const pillar4 = pillar1.clone(); - pillar4.position.set(-8, 3.5, -8); - - scene.add(pillar1); - scene.add(pillar2); - scene.add(pillar3); - scene.add(pillar4); - - const planeGeometry = new THREE.PlaneGeometry(200, 200); - - const planeMaterial = new THREE.MeshPhongNodeMaterial(); - planeMaterial.color.setHex(0x999999); - planeMaterial.shininess = 0; - planeMaterial.specular.setHex(0x111111); - - planeMaterial.receivedShadowPositionNode = Fn(() => { - const pos = positionWorld.toVar(); - pos.xz.addAssign(mx_fractal_noise_vec3(positionWorld.mul(2)).saturate().xz); - return pos; - })(); - - planeMaterial.colorNode = Fn(() => { - const pos = positionWorld.toVar(); - pos.xz.addAssign(mx_fractal_noise_vec3(positionWorld.mul(2)).saturate().xz); - return mx_fractal_noise_vec3(positionWorld.mul(2)).saturate().zzz.mul(0.2).add(0.5); - })(); - - const ground = new THREE.Mesh(planeGeometry, planeMaterial); - ground.rotation.x = -Math.PI / 2; - ground.scale.multiplyScalar(3); - ground.castShadow = true; - ground.receiveShadow = true; - scene.add(ground); - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.shadowMap.enabled = true; - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // Mouse control - const controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, 2, 0); - controls.minDistance = 7; - controls.maxDistance = 40; - controls.update(); - - timer = new THREE.Timer(); - timer.connect(document); - - window.addEventListener('resize', resize); -} - -function resize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate(time) { - timer.update(); - - const delta = timer.getDelta(); - - torusKnot.rotation.x += 0.25 * delta; - torusKnot.rotation.y += 0.5 * delta; - torusKnot.rotation.z += 1 * delta; - - dirGroup.rotation.y += 0.7 * delta; - dirLight.position.z = 17 + Math.sin(time * 0.001) * 5; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_shadowmap_array.ts b/examples-testing/examples/webgpu_shadowmap_array.ts deleted file mode 100644 index 0dfe8084f..000000000 --- a/examples-testing/examples/webgpu_shadowmap_array.ts +++ /dev/null @@ -1,330 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { mx_fractal_noise_vec3, positionWorld, Fn, color } from 'three/tsl'; - -import { TileShadowNode } from 'three/addons/tsl/shadows/TileShadowNode.js'; -import { TileShadowNodeHelper } from 'three/addons/tsl/shadows/TileShadowNodeHelper.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer, timer; -let dirLight; -let torusKnot, dirGroup; -let tsmHelper; - -init(); - -async function init() { - // Renderer setup - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - - renderer.inspector = new Inspector(); - - renderer.shadowMap.enabled = true; - renderer.shadowMap.type = THREE.BasicShadowMap; - // renderer.shadowMap.type = THREE.PCFSoftShadowMap; - - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = 1.2; - document.body.appendChild(renderer.domElement); - - await renderer.init(); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(45, 60, 100); - - scene = new THREE.Scene(); - scene.backgroundNode = color(0xccccff); // Brighter blue sky - scene.fog = new THREE.Fog(0xccccff, 700, 1000); - - // Enhanced lighting for a brighter scene - scene.add(new THREE.AmbientLight(0xccccff, 3)); - - // Main directional light (sun) - dirLight = new THREE.DirectionalLight(0xffffaa, 5); - dirLight.position.set(0, 80, 30); - dirLight.castShadow = true; - dirLight.shadow.camera.near = 1; - dirLight.shadow.camera.far = 200; - dirLight.shadow.camera.right = 180; - dirLight.shadow.camera.left = -180; - dirLight.shadow.camera.top = 180; - dirLight.shadow.camera.bottom = -160; - dirLight.shadow.mapSize.width = 1024 * 4; - dirLight.shadow.mapSize.height = 1024 * 4; - dirLight.shadow.radius = 1; - - // Set up the tile shadow mapping - const tsm = new TileShadowNode(dirLight, { - tilesX: 2, - tilesY: 2, - }); - - dirLight.shadow.shadowNode = tsm; - scene.add(dirLight); - - tsmHelper = new TileShadowNodeHelper(tsm); - scene.add(tsmHelper); - - dirGroup = new THREE.Group(); - dirGroup.add(dirLight); - scene.add(dirGroup); - - // Create the ground with enhanced texture - const planeGeometry = new THREE.PlaneGeometry(1500, 1500, 2, 2); - const planeMaterial = new THREE.MeshPhongMaterial({ - color: 0x88aa44, - shininess: 5, - specular: 0x222222, - }); - - planeMaterial.colorNode = Fn(() => { - const noise = mx_fractal_noise_vec3(positionWorld.mul(0.05)).saturate(); - // Mix of greens and browns for a more natural ground - const green = color(0.4, 0.7, 0.3); - const brown = color(0.6, 0.5, 0.3); - return noise.x.mix(green, brown); - })(); - - const ground = new THREE.Mesh(planeGeometry, planeMaterial); - ground.rotation.x = -Math.PI / 2; - ground.receiveShadow = true; - scene.add(ground); - - // Spread various objects across the scene - createScenery(); - - // Camera controls - const controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, 5, 0); - controls.minDistance = 0.01; - controls.maxDistance = 400; - controls.maxPolarAngle = Math.PI / 2 - 0.1; // Prevent camera from going below ground - controls.update(); - - timer = new THREE.Timer(); - timer.connect(document); - - window.addEventListener('resize', resize); -} - -function createScenery() { - // 1. Columns using instanced mesh - const columnGeometry = new THREE.CylinderGeometry(0.8, 1, 1, 16); - const columnMaterial = new THREE.MeshPhongMaterial({ - color: 0xdddddd, - shininess: 20, - }); - - const columnPositions = []; - const columnScales = []; - - for (let x = -100; x <= 100; x += 40) { - for (let z = -100; z <= 100; z += 40) { - if (Math.random() > 0.3) { - const height = 5 + Math.random() * 10; - const posX = x + (Math.random() * 10 - 5); - const posY = height / 2; - const posZ = z + (Math.random() * 10 - 5); - - columnPositions.push(posX, posY, posZ); - columnScales.push(1, height, 1); // Only scale Y to match height - } - } - } - - const columnCount = columnPositions.length / 3; - const columnInstancedMesh = new THREE.InstancedMesh(columnGeometry, columnMaterial, columnCount); - - const matrix = new THREE.Matrix4(); - for (let i = 0; i < columnCount; i++) { - const x = columnPositions[i * 3]; - const y = columnPositions[i * 3 + 1]; - const z = columnPositions[i * 3 + 2]; - const scaleY = columnScales[i * 3 + 1]; - - matrix.makeScale(1, scaleY, 1); - matrix.setPosition(x, y, z); - columnInstancedMesh.setMatrixAt(i, matrix); - } - - columnInstancedMesh.castShadow = true; - columnInstancedMesh.receiveShadow = true; - scene.add(columnInstancedMesh); - - // 2. Add a central feature - the torus knot (kept as regular mesh for animation) - const torusKnotGeometry = new THREE.TorusKnotGeometry(25, 8, 100, 30); - const torusKnotMaterial = new THREE.MeshPhongNodeMaterial({ - color: 0xff6347, // Tomato color - shininess: 30, - }); - - torusKnot = new THREE.Mesh(torusKnotGeometry, torusKnotMaterial); - torusKnot.scale.multiplyScalar(1 / 18); - torusKnot.position.x = 5; - torusKnot.position.y = 5; - torusKnot.castShadow = true; - torusKnot.receiveShadow = true; - scene.add(torusKnot); - - // 3. Cubes using instanced mesh - const cubeGeometry = new THREE.BoxGeometry(3, 3, 3); - const cubeMaterials = [ - new THREE.MeshPhongMaterial({ color: 0x6699cc, shininess: 20 }), - new THREE.MeshPhongMaterial({ color: 0xcc6666, shininess: 20 }), - new THREE.MeshPhongMaterial({ color: 0xcccc66, shininess: 20 }), - ]; - - const cubeCount = 10; - const cubeInstances = cubeMaterials.map(material => { - return new THREE.InstancedMesh(cubeGeometry, material, cubeCount); - }); - - for (let i = 0; i < 30; i++) { - const materialIndex = i % 3; - const instanceIndex = Math.floor(i / 3); - - const x = Math.random() * 300 - 150; - const y = 1.5; - const z = Math.random() * 300 - 150; - const rotY = Math.random() * Math.PI * 2; - - matrix.makeRotationY(rotY); - matrix.setPosition(x, y, z); - - cubeInstances[materialIndex].setMatrixAt(instanceIndex, matrix); - } - - cubeInstances.forEach(instance => { - instance.castShadow = true; - instance.receiveShadow = true; - scene.add(instance); - }); - - // 4. Spheres using instanced mesh - const sphereGeometry = new THREE.SphereGeometry(2, 32, 32); - const sphereMaterial = new THREE.MeshPhongMaterial({ - color: 0x88ccaa, - shininess: 40, - }); - - const sphereCount = 25; - const sphereInstancedMesh = new THREE.InstancedMesh(sphereGeometry, sphereMaterial, sphereCount); - - for (let i = 0; i < sphereCount; i++) { - const x = Math.random() * 180 - 90; - const y = 2; - const z = Math.random() * 180 - 90; - - matrix.makeScale(1, 1, 1); - matrix.setPosition(x, y, z); - sphereInstancedMesh.setMatrixAt(i, matrix); - } - - sphereInstancedMesh.castShadow = true; - sphereInstancedMesh.receiveShadow = true; - scene.add(sphereInstancedMesh); - - // 5. Trees using instanced mesh for trunks and tops separately - const trunkGeometry = new THREE.CylinderGeometry(0.5, 0.5, 2, 8); - const topGeometry = new THREE.ConeGeometry(2, 8, 8); - const treeMaterial = new THREE.MeshPhongMaterial({ - vertexColors: true, - shininess: 5, - }); - - const treeCount = 40; - const totalInstanceCount = treeCount * 2; - - const trunkVertexCount = trunkGeometry.attributes.position.count; - const trunkIndexCount = trunkGeometry.index ? trunkGeometry.index.count : 0; - const topVertexCount = topGeometry.attributes.position.count; - const topIndexCount = topGeometry.index ? topGeometry.index.count : 0; - - const totalVertexCount = (trunkVertexCount + topVertexCount) * 2; // Multiple for safety - const totalIndexCount = (trunkIndexCount + topIndexCount) * 2; - const treeBatchedMesh = new THREE.BatchedMesh(totalInstanceCount, totalVertexCount, totalIndexCount, treeMaterial); - treeBatchedMesh.castShadow = true; - treeBatchedMesh.perObjectFrustumCulled = false; - const trunkGeometryId = treeBatchedMesh.addGeometry(trunkGeometry); - const topGeometryId = treeBatchedMesh.addGeometry(topGeometry); - - const trunkColor = new THREE.Color(0x8b4513); - const topColor = new THREE.Color(0x336633); - - for (let i = 0; i < treeCount; i++) { - const x = Math.random() * 300 - 150; - const z = Math.random() * 300 - 150; - - const trunkId = treeBatchedMesh.addInstance(trunkGeometryId); - matrix.makeScale(1, 1, 1); - matrix.setPosition(x, 1, z); - treeBatchedMesh.setMatrixAt(trunkId, matrix); - treeBatchedMesh.setColorAt(trunkId, trunkColor); - - const topId = treeBatchedMesh.addInstance(topGeometryId); - matrix.makeScale(1, 1, 1); - matrix.setPosition(x, 6, z); - treeBatchedMesh.setMatrixAt(topId, matrix); - treeBatchedMesh.setColorAt(topId, topColor); - } - - scene.add(treeBatchedMesh); - - // 6. Torus shapes using instanced mesh - const torusGeometry = new THREE.TorusGeometry(3, 1, 16, 50); - const torusMaterial = new THREE.MeshPhongMaterial({ - color: 0xff99cc, - shininess: 30, - }); - - const torusCount = 15; - const torusInstancedMesh = new THREE.InstancedMesh(torusGeometry, torusMaterial, torusCount); - - for (let i = 0; i < torusCount; i++) { - const x = Math.random() * 320 - 160; - const y = 2; - const z = Math.random() * 320 - 160; - const rotZ = Math.random() * Math.PI * 2; - - // Apply rotation (PI/2 on X-axis and random on Z-axis) - matrix.makeRotationX(Math.PI / 2); - const rotMatrix = new THREE.Matrix4().makeRotationZ(rotZ); - matrix.multiply(rotMatrix); - matrix.setPosition(x, y, z); - - torusInstancedMesh.setMatrixAt(i, matrix); - } - - torusInstancedMesh.castShadow = true; - torusInstancedMesh.receiveShadow = true; - scene.add(torusInstancedMesh); -} - -function resize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - renderer.setSize(window.innerWidth, window.innerHeight); -} - -async function animate(time) { - timer.update(); - - const delta = timer.getDelta(); - - // Rotate the central torus knot - torusKnot.rotation.x += 0.25 * delta; - torusKnot.rotation.y += 0.5 * delta; - torusKnot.rotation.z += 1 * delta; - - dirLight.position.x = Math.sin(time * 0.0001) * 30; - dirLight.position.z = Math.cos(time * 0.0001) * 30; - - renderer.render(scene, camera); - - tsmHelper.update(); -} diff --git a/examples-testing/examples/webgpu_shadowmap_csm.ts b/examples-testing/examples/webgpu_shadowmap_csm.ts deleted file mode 100644 index 5be061456..000000000 --- a/examples-testing/examples/webgpu_shadowmap_csm.ts +++ /dev/null @@ -1,268 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { CSMShadowNode } from 'three/addons/csm/CSMShadowNode.js'; -import { CSMHelper } from 'three/addons/csm/CSMHelper.js'; - -let renderer, scene, camera, orthoCamera, controls, csm, csmHelper, csmDirectionalLight; - -const params = { - orthographic: false, - fade: false, - shadows: true, - maxFar: 1000, - mode: 'practical', - lightX: -1, - lightY: -1, - lightZ: -1, - margin: 100, - shadowNear: 1, - shadowFar: 2000, - autoUpdateHelper: true, - updateHelper: function () { - csmHelper.update(); - }, -}; - -init(); - -function updateOrthoCamera() { - const size = controls.target.distanceTo(camera.position); - const aspect = camera.aspect; - - orthoCamera.left = (size * aspect) / -2; - orthoCamera.right = (size * aspect) / 2; - - orthoCamera.top = size / 2; - orthoCamera.bottom = size / -2; - orthoCamera.position.copy(camera.position); - orthoCamera.rotation.copy(camera.rotation); - orthoCamera.updateProjectionMatrix(); -} - -function init() { - scene = new THREE.Scene(); - scene.background = new THREE.Color('#454e61'); - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 5000); - orthoCamera = new THREE.OrthographicCamera(); - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - - renderer.shadowMap.enabled = params.shadows; - renderer.shadowMap.type = THREE.PCFSoftShadowMap; - - renderer.inspector = new Inspector(); - - document.body.appendChild(renderer.domElement); - - controls = new OrbitControls(camera, renderer.domElement); - controls.maxPolarAngle = Math.PI / 2; - camera.position.set(60, 60, 0); - controls.target = new THREE.Vector3(-100, 10, 0); - controls.update(); - - const ambientLight = new THREE.AmbientLight(0xffffff, 1.5); - scene.add(ambientLight); - - const additionalDirectionalLight = new THREE.DirectionalLight(0x000020, 1.5); - additionalDirectionalLight.position - .set(params.lightX, params.lightY, params.lightZ) - .normalize() - .multiplyScalar(-200); - scene.add(additionalDirectionalLight); - - csmDirectionalLight = new THREE.DirectionalLight(0xffffff, 3.0); - - csmDirectionalLight.castShadow = true; - csmDirectionalLight.shadow.mapSize.width = 2048; - csmDirectionalLight.shadow.mapSize.height = 2048; - csmDirectionalLight.shadow.camera.near = params.shadowNear; - csmDirectionalLight.shadow.camera.far = params.shadowFar; - csmDirectionalLight.shadow.camera.top = 1000; - csmDirectionalLight.shadow.camera.bottom = -1000; - csmDirectionalLight.shadow.camera.left = -1000; - csmDirectionalLight.shadow.camera.right = 1000; - - csm = new CSMShadowNode(csmDirectionalLight, { cascades: 4, maxFar: params.maxFar, mode: params.mode }); - - csmDirectionalLight.position.set(params.lightX, params.lightY, params.lightZ).normalize().multiplyScalar(-200); - - csmDirectionalLight.shadow.shadowNode = csm; - - scene.add(csmDirectionalLight); - - csmHelper = new CSMHelper(csm); - csmHelper.visible = false; - scene.add(csmHelper); - - const floorMaterial = new THREE.MeshPhongMaterial({ color: '#252a34' }); - - const floor = new THREE.Mesh(new THREE.PlaneGeometry(10000, 10000, 8, 8), floorMaterial); - floor.rotation.x = -Math.PI / 2; - floor.castShadow = true; - floor.receiveShadow = true; - scene.add(floor); - - const material1 = new THREE.MeshPhongMaterial({ color: '#08d9d6' }); - - const material2 = new THREE.MeshPhongMaterial({ color: '#ff2e63' }); - - const geometry = new THREE.BoxGeometry(10, 10, 10); - - for (let i = 0; i < 40; i++) { - const cube1 = new THREE.Mesh(geometry, i % 2 === 0 ? material1 : material2); - cube1.castShadow = true; - cube1.receiveShadow = true; - scene.add(cube1); - cube1.position.set(-i * 25, 20, 30); - cube1.scale.y = Math.random() * 2 + 6; - - const cube2 = new THREE.Mesh(geometry, i % 2 === 0 ? material2 : material1); - cube2.castShadow = true; - cube2.receiveShadow = true; - scene.add(cube2); - cube2.position.set(-i * 25, 20, -30); - cube2.scale.y = Math.random() * 2 + 6; - } - - const gui = renderer.inspector.createParameters('Settings'); - - gui.add(params, 'orthographic').onChange(function (value) { - csm.camera = value ? orthoCamera : camera; - csm.updateFrustums(); - }); - - // gui.add( params, 'fade' ).onChange( function ( value ) { - - // csm.fade = value; - // csm.updateFrustums(); - // TODO: Changing "fade" requires toggling shadows right now - - // } ); - - gui.add(params, 'shadows').onChange(function (value) { - csmDirectionalLight.castShadow = value; - }); - - gui.add(params, 'maxFar', 1, 5000, 1) - .name('max shadow far') - .onChange(function (value) { - csm.maxFar = value; - csm.updateFrustums(); - }); - - gui.add(params, 'mode', ['uniform', 'logarithmic', 'practical']) - .name('frustum split mode') - .onChange(function (value) { - csm.mode = value; - csm.updateFrustums(); - }); - - gui.add(params, 'lightX', -1, 1) - .name('light direction x') - .onChange(function () { - csmDirectionalLight.position - .set(params.lightX, params.lightY, params.lightZ) - .normalize() - .multiplyScalar(-200); - }); - - gui.add(params, 'lightY', -1, 1) - .name('light direction y') - .onChange(function () { - csmDirectionalLight.position - .set(params.lightX, params.lightY, params.lightZ) - .normalize() - .multiplyScalar(-200); - }); - - gui.add(params, 'lightZ', -1, 1) - .name('light direction z') - .onChange(function () { - csmDirectionalLight.position - .set(params.lightX, params.lightY, params.lightZ) - .normalize() - .multiplyScalar(-200); - }); - - gui.add(params, 'margin', 0, 200) - .name('light margin') - .onChange(function (value) { - csm.lightMargin = value; - }); - - gui.add(params, 'shadowNear', 1, 10000) - .name('shadow near') - .onChange(function (value) { - for (let i = 0; i < csm.lights.length; i++) { - csm.lights[i].shadow.camera.near = value; - csm.lights[i].shadow.camera.updateProjectionMatrix(); - } - }); - - gui.add(params, 'shadowFar', 1, 10000) - .name('shadow far') - .onChange(function (value) { - for (let i = 0; i < csm.lights.length; i++) { - csm.lights[i].shadow.camera.far = value; - csm.lights[i].shadow.camera.updateProjectionMatrix(); - } - }); - - const helperFolder = gui.addFolder('helper'); - - helperFolder.add(csmHelper, 'visible'); - - helperFolder.add(csmHelper, 'displayFrustum').onChange(function () { - csmHelper.updateVisibility(); - }); - - helperFolder.add(csmHelper, 'displayPlanes').onChange(function () { - csmHelper.updateVisibility(); - }); - - helperFolder.add(csmHelper, 'displayShadowBounds').onChange(function () { - csmHelper.updateVisibility(); - }); - - helperFolder.add(params, 'autoUpdateHelper').name('auto update'); - - helperFolder.add(params, 'updateHelper').name('update'); - - window.addEventListener('resize', function () { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - updateOrthoCamera(); - csm.updateFrustums(); - - renderer.setSize(window.innerWidth, window.innerHeight); - }); -} - -function animate() { - camera.updateMatrixWorld(); - controls.update(); - - if (params.orthographic) { - updateOrthoCamera(); - csm.updateFrustums(); - - if (params.autoUpdateHelper) { - csmHelper.update(); - } - - renderer.render(scene, orthoCamera); - } else { - if (params.autoUpdateHelper) { - csmHelper.update(); - } - - renderer.render(scene, camera); - } -} diff --git a/examples-testing/examples/webgpu_shadowmap_opacity.ts b/examples-testing/examples/webgpu_shadowmap_opacity.ts deleted file mode 100644 index e508e1039..000000000 --- a/examples-testing/examples/webgpu_shadowmap_opacity.ts +++ /dev/null @@ -1,117 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { Fn, mix } from 'three/tsl'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -let camera, scene, renderer; - -init(); - -async function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 40); - camera.position.set(-4, 2, 6); - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(render); - renderer.toneMapping = THREE.AgXToneMapping; - renderer.toneMappingExposure = 1.5; - renderer.shadowMap.enabled = true; - renderer.shadowMap.transmitted = true; - renderer.inspector = new Inspector(); - container.appendChild(renderer.domElement); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x9e9eff); - - // light + shadow - - const hemi = new THREE.AmbientLight(0xffffff, 0.5); - scene.add(hemi); - - const dirLight = new THREE.DirectionalLight(0x6666ff, 10); - dirLight.position.set(3, 5, 17); - dirLight.castShadow = true; - dirLight.shadow.camera.near = 0.1; - dirLight.shadow.camera.far = 50; - dirLight.shadow.camera.right = 5; - dirLight.shadow.camera.left = -5; - dirLight.shadow.camera.top = 5; - dirLight.shadow.camera.bottom = -5; - dirLight.shadow.mapSize.width = 2048; - dirLight.shadow.mapSize.height = 2048; - dirLight.shadow.radius = 4; - - dirLight.shadow.autoUpdate = false; - dirLight.shadow.needsUpdate = true; - - scene.add(dirLight); - - // - - const loader = new GLTFLoader(); - const gltf = await loader.loadAsync('models/gltf/DragonAttenuation.glb'); - gltf.scene.position.set(0, 0, -0.5); - - const floor = gltf.scene.children[0]; - floor.scale.x += 4; - floor.scale.y += 4; - - const dragon = gltf.scene.children[1]; - dragon.position.set(-1.5, -0.8, 1); - - const dragon2 = dragon.clone(); - dragon2.material = dragon.material.clone(); - dragon2.material.attenuationColor = new THREE.Color(0xff0000); - dragon2.position.x += 4; - gltf.scene.add(dragon2); - - // shadow node - - const customShadow = Fn(([color, opacity = 1]) => { - //return vec4( color, opacity ); // opacity by blending - return mix(1, color, opacity); // opacity by color - }); - - // apply shadow - - floor.receiveShadow = true; - - dragon.castShadow = dragon2.castShadow = true; - dragon.receiveShadow = dragon2.receiveShadow = true; - - dragon.material.castShadowNode = customShadow(dragon.material.attenuationColor); - dragon2.material.castShadowNode = customShadow(dragon2.material.attenuationColor); - - // - - scene.add(gltf.scene); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 0.1; - controls.maxDistance = 10; - controls.target.set(0, 0, 0); - controls.update(); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_shadowmap_pointlight.ts b/examples-testing/examples/webgpu_shadowmap_pointlight.ts deleted file mode 100644 index 3cb878e6f..000000000 --- a/examples-testing/examples/webgpu_shadowmap_pointlight.ts +++ /dev/null @@ -1,142 +0,0 @@ -import * as THREE from 'three'; - -import Stats from 'three/addons/libs/stats.module.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let camera, scene, renderer, stats; -let pointLight, pointLight2; - -init(); - -async function init() { - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(0, 10, 40); - - scene = new THREE.Scene(); - scene.add(new THREE.AmbientLight(0x111122, 3)); - - // lights - - function createLight(color) { - const intensity = 200; - - const light = new THREE.PointLight(color, intensity, 20); - light.castShadow = true; - light.shadow.bias = -0.005; // reduces self-shadowing on double-sided objects - light.shadow.mapSize.width = 128; - light.shadow.radius = 10; - - let geometry = new THREE.SphereGeometry(0.3, 12, 6); - let material = new THREE.MeshBasicMaterial({ color: color }); - material.color.multiplyScalar(intensity); - let sphere = new THREE.Mesh(geometry, material); - light.add(sphere); - - const texture = new THREE.CanvasTexture(generateTexture()); - texture.magFilter = THREE.NearestFilter; - texture.wrapT = THREE.RepeatWrapping; - texture.wrapS = THREE.RepeatWrapping; - texture.repeat.set(1, 4.5); - - geometry = new THREE.SphereGeometry(2, 32, 8); - material = new THREE.MeshPhongNodeMaterial({ - side: THREE.DoubleSide, - alphaMap: texture, - alphaTest: 0.5, - }); - - sphere = new THREE.Mesh(geometry, material); - sphere.castShadow = true; - sphere.receiveShadow = true; - light.add(sphere); - - return light; - } - - pointLight = createLight(0x0088ff); - scene.add(pointLight); - - pointLight2 = createLight(0xff8888); - scene.add(pointLight2); - // - - const geometry = new THREE.BoxGeometry(30, 30, 30); - - const material = new THREE.MeshPhongNodeMaterial({ - color: 0xa0adaf, - shininess: 10, - specular: 0x111111, - side: THREE.BackSide, - }); - - const mesh = new THREE.Mesh(geometry, material); - mesh.position.y = 10; - mesh.receiveShadow = true; - scene.add(mesh); - - // - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.shadowMap.enabled = true; - // renderer.shadowMap.type = THREE.BasicShadowMap; - await renderer.init(); - document.body.appendChild(renderer.domElement); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, 10, 0); - controls.update(); - - stats = new Stats(); - document.body.appendChild(stats.dom); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function generateTexture() { - const canvas = document.createElement('canvas'); - canvas.width = 2; - canvas.height = 2; - - const context = canvas.getContext('2d'); - context.fillStyle = 'white'; - context.fillRect(0, 1, 2, 1); - - return canvas; -} - -function animate() { - let time = performance.now() * 0.001; - - pointLight.position.x = Math.sin(time * 0.6) * 9; - pointLight.position.y = Math.sin(time * 0.7) * 9 + 6; - pointLight.position.z = Math.sin(time * 0.8) * 9; - - pointLight.rotation.x = time; - pointLight.rotation.z = time; - - time += 10000; - - pointLight2.position.x = Math.sin(time * 0.6) * 9; - pointLight2.position.y = Math.sin(time * 0.7) * 9 + 6; - pointLight2.position.z = Math.sin(time * 0.8) * 9; - - pointLight2.rotation.x = time; - pointLight2.rotation.z = time; - - renderer.render(scene, camera); - - stats.update(); -} diff --git a/examples-testing/examples/webgpu_shadowmap_progressive.ts b/examples-testing/examples/webgpu_shadowmap_progressive.ts deleted file mode 100644 index ca5ee0f72..000000000 --- a/examples-testing/examples/webgpu_shadowmap_progressive.ts +++ /dev/null @@ -1,203 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { TransformControls } from 'three/addons/controls/TransformControls.js'; -import { ProgressiveLightMap } from 'three/addons/misc/ProgressiveLightMapGPU.js'; - -// ShadowMap + LightMap Res and Number of Directional Lights -const shadowMapRes = 1024, - lightMapRes = 1024, - lightCount = 4; -let camera, - scene, - renderer, - controls, - control, - control2, - object = new THREE.Mesh(), - lightOrigin = null, - progressiveSurfacemap; -const dirLights = [], - lightmapObjects = []; -const params = { - Enable: true, - 'Blur Edges': true, - 'Blend Window': 200, - 'Light Radius': 50, - 'Ambient Weight': 0.5, - 'Debug Lightmap': false, -}; -init(); -createGUI(); - -function init() { - // renderer - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.shadowMap.enabled = true; - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // camera - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(0, 100, 200); - camera.name = 'Camera'; - - // scene - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x949494); - scene.fog = new THREE.Fog(0x949494, 1000, 3000); - - // progressive lightmap - progressiveSurfacemap = new ProgressiveLightMap(renderer, lightMapRes); - - // directional lighting "origin" - lightOrigin = new THREE.Group(); - lightOrigin.position.set(60, 150, 100); - scene.add(lightOrigin); - - // transform gizmo - control = new TransformControls(camera, renderer.domElement); - control.addEventListener('dragging-changed', event => { - controls.enabled = !event.value; - }); - control.attach(lightOrigin); - scene.add(control.getHelper()); - - // create 8 directional lights to speed up the convergence - for (let l = 0; l < lightCount; l++) { - const dirLight = new THREE.DirectionalLight(0xffffff, Math.PI / lightCount); - dirLight.name = 'Dir. Light ' + l; - dirLight.position.set(200, 200, 200); - dirLight.castShadow = true; - dirLight.shadow.camera.near = 100; - dirLight.shadow.camera.far = 5000; - dirLight.shadow.camera.right = 150; - dirLight.shadow.camera.left = -150; - dirLight.shadow.camera.top = 150; - dirLight.shadow.camera.bottom = -150; - dirLight.shadow.mapSize.width = shadowMapRes; - dirLight.shadow.mapSize.height = shadowMapRes; - lightmapObjects.push(dirLight); - dirLights.push(dirLight); - } - - // ground - const groundMesh = new THREE.Mesh( - new THREE.PlaneGeometry(600, 600), - new THREE.MeshPhongMaterial({ color: 0xffffff, depthWrite: true }), - ); - groundMesh.position.y = -0.1; - groundMesh.rotation.x = -Math.PI / 2; - groundMesh.name = 'Ground Mesh'; - lightmapObjects.push(groundMesh); - scene.add(groundMesh); - - // model - function loadModel() { - object.traverse(function (child) { - if (child.isMesh) { - child.name = 'Loaded Mesh'; - child.castShadow = true; - child.receiveShadow = true; - child.material = new THREE.MeshPhongMaterial(); - - // This adds the model to the lightmap - lightmapObjects.push(child); - progressiveSurfacemap.addObjectsToLightMap(lightmapObjects); - } else { - child.layers.disableAll(); // Disable Rendering for this - } - }); - scene.add(object); - object.scale.set(2, 2, 2); - object.position.set(0, -16, 0); - control2 = new TransformControls(camera, renderer.domElement); - control2.addEventListener('dragging-changed', event => { - controls.enabled = !event.value; - }); - control2.attach(object); - scene.add(control2.getHelper()); - const lightTarget = new THREE.Group(); - lightTarget.position.set(0, 20, 0); - for (let l = 0; l < dirLights.length; l++) { - dirLights[l].target = lightTarget; - } - - object.add(lightTarget); - } - - const manager = new THREE.LoadingManager(loadModel); - const loader = new GLTFLoader(manager); - loader.load('models/gltf/ShadowmappableMesh.glb', function (obj) { - object = obj.scene.children[0]; - }); - - // controls - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; // an animation loop is required when either damping or auto-rotation are enabled - controls.dampingFactor = 0.05; - controls.screenSpacePanning = true; - controls.minDistance = 100; - controls.maxDistance = 500; - controls.maxPolarAngle = Math.PI / 1.5; - controls.target.set(0, 100, 0); - - window.addEventListener('resize', onWindowResize); -} - -function createGUI() { - const gui = renderer.inspector.createParameters('Accumulation Settings'); - gui.add(params, 'Enable'); - gui.add(params, 'Blur Edges'); - gui.add(params, 'Blend Window', 1, 500, 1); - gui.add(params, 'Light Radius', 0, 200, 10); - gui.add(params, 'Ambient Weight', 0, 1, 0.1); - gui.add(params, 'Debug Lightmap').onChange(value => progressiveSurfacemap.showDebugLightmap(value)); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - // Update the inertia on the orbit controls - controls.update(); - - // Accumulate Surface Maps - if (params['Enable']) { - progressiveSurfacemap.update(camera, params['Blend Window'], params['Blur Edges']); - } - - // Manually Update the Directional Lights - for (let l = 0; l < dirLights.length; l++) { - // Sometimes they will be sampled from the target direction - // Sometimes they will be uniformly sampled from the upper hemisphere - if (Math.random() > params['Ambient Weight']) { - dirLights[l].position.set( - lightOrigin.position.x + Math.random() * params['Light Radius'], - lightOrigin.position.y + Math.random() * params['Light Radius'], - lightOrigin.position.z + Math.random() * params['Light Radius'], - ); - } else { - // Uniform Hemispherical Surface Distribution for Ambient Occlusion - const lambda = Math.acos(2 * Math.random() - 1) - 3.14159 / 2.0; - const phi = 2 * 3.14159 * Math.random(); - dirLights[l].position.set( - Math.cos(lambda) * Math.cos(phi) * 300 + object.position.x, - Math.abs(Math.cos(lambda) * Math.sin(phi) * 300) + object.position.y + 20, - Math.sin(lambda) * 300 + object.position.z, - ); - } - } - - // Render Scene - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_shadowmap_vsm.ts b/examples-testing/examples/webgpu_shadowmap_vsm.ts deleted file mode 100644 index edaf4fd9e..000000000 --- a/examples-testing/examples/webgpu_shadowmap_vsm.ts +++ /dev/null @@ -1,197 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let camera, scene, renderer, timer; -let dirLight, spotLight; -let torusKnot, dirGroup; - -const config = { - spotlightRadius: 4, - spotlightSamples: 8, - dirlightRadius: 4, - dirlightSamples: 8, - animate: true, -}; - -init(); - -function init() { - initScene(); - initMisc(); - - // Init gui - const gui = renderer.inspector.createParameters('Settings'); - - const spotlightFolder = gui.addFolder('Spotlight'); - spotlightFolder - .add(config, 'spotlightRadius', 0, 25) - .name('radius') - .onChange(function (value) { - spotLight.shadow.radius = value; - }); - - spotlightFolder - .add(config, 'spotlightSamples', 1, 25, 1) - .name('samples') - .onChange(function (value) { - spotLight.shadow.blurSamples = value; - }); - - const dirlightFolder = gui.addFolder('Directional Light'); - dirlightFolder - .add(config, 'dirlightRadius', 0, 25) - .name('radius') - .onChange(function (value) { - dirLight.shadow.radius = value; - }); - - dirlightFolder - .add(config, 'dirlightSamples', 1, 25, 1) - .name('samples') - .onChange(function (value) { - dirLight.shadow.blurSamples = value; - }); - - gui.add(config, 'animate'); - - document.body.appendChild(renderer.domElement); - window.addEventListener('resize', onWindowResize); -} - -function initScene() { - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.set(0, 10, 30); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x222244); - scene.fog = new THREE.Fog(0x222244, 50, 100); - - // Lights - - scene.add(new THREE.AmbientLight(0x444444)); - - spotLight = new THREE.SpotLight(0xff8888, 400); - spotLight.angle = Math.PI / 5; - spotLight.penumbra = 0.3; - spotLight.position.set(8, 10, 5); - spotLight.castShadow = true; - spotLight.shadow.camera.near = 8; - spotLight.shadow.camera.far = 200; - spotLight.shadow.mapSize.width = 256; - spotLight.shadow.mapSize.height = 256; - spotLight.shadow.bias = -0.002; - spotLight.shadow.radius = 4; - scene.add(spotLight); - - dirLight = new THREE.DirectionalLight(0x8888ff, 3); - dirLight.position.set(3, 12, 17); - dirLight.castShadow = true; - dirLight.shadow.camera.near = 0.1; - dirLight.shadow.camera.far = 500; - dirLight.shadow.camera.right = 17; - dirLight.shadow.camera.left = -17; - dirLight.shadow.camera.top = 17; - dirLight.shadow.camera.bottom = -17; - dirLight.shadow.mapSize.width = 512; - dirLight.shadow.mapSize.height = 512; - dirLight.shadow.radius = 4; - dirLight.shadow.bias = -0.0005; - - dirGroup = new THREE.Group(); - dirGroup.add(dirLight); - scene.add(dirGroup); - - // Geometry - - const geometry = new THREE.TorusKnotGeometry(25, 8, 75, 20); - const material = new THREE.MeshPhongMaterial({ - color: 0x999999, - shininess: 0, - specular: 0x222222, - }); - - torusKnot = new THREE.Mesh(geometry, material); - torusKnot.scale.multiplyScalar(1 / 18); - torusKnot.position.y = 3; - torusKnot.castShadow = true; - torusKnot.receiveShadow = true; - scene.add(torusKnot); - - const cylinderGeometry = new THREE.CylinderGeometry(0.75, 0.75, 7, 32); - - const pillar1 = new THREE.Mesh(cylinderGeometry, material); - pillar1.position.set(8, 3.5, 8); - pillar1.castShadow = true; - pillar1.receiveShadow = true; - - const pillar2 = pillar1.clone(); - pillar2.position.set(8, 3.5, -8); - const pillar3 = pillar1.clone(); - pillar3.position.set(-8, 3.5, 8); - const pillar4 = pillar1.clone(); - pillar4.position.set(-8, 3.5, -8); - - scene.add(pillar1); - scene.add(pillar2); - scene.add(pillar3); - scene.add(pillar4); - - const planeGeometry = new THREE.PlaneGeometry(200, 200); - const planeMaterial = new THREE.MeshPhongMaterial({ - color: 0x999999, - shininess: 0, - specular: 0x111111, - }); - - const ground = new THREE.Mesh(planeGeometry, planeMaterial); - ground.rotation.x = -Math.PI / 2; - ground.scale.multiplyScalar(3); - ground.castShadow = true; - ground.receiveShadow = true; - scene.add(ground); -} - -function initMisc() { - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - renderer.shadowMap.enabled = true; - renderer.shadowMap.type = THREE.VSMShadowMap; - - // Mouse control - const controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, 2, 0); - controls.update(); - - timer = new THREE.Timer(); - timer.connect(document); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate(time) { - timer.update(); - - const delta = timer.getDelta(); - - if (config.animate === true) { - torusKnot.rotation.x += 0.25 * delta; - torusKnot.rotation.y += 0.5 * delta; - torusKnot.rotation.z += 1 * delta; - - dirGroup.rotation.y += 0.7 * delta; - dirLight.position.z = 17 + Math.sin(time * 0.001) * 5; - } - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_skinning.ts b/examples-testing/examples/webgpu_skinning.ts deleted file mode 100644 index 36669a5d4..000000000 --- a/examples-testing/examples/webgpu_skinning.ts +++ /dev/null @@ -1,72 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { color, screenUV } from 'three/tsl'; - -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -let camera, scene, renderer; - -let mixer, timer; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.01, 100); - camera.position.set(1, 2, 3); - - scene = new THREE.Scene(); - scene.backgroundNode = screenUV.y.mix(color(0x66bbff), color(0x4466ff)); - camera.lookAt(0, 1, 0); - - timer = new THREE.Timer(); - timer.connect(document); - - //lights - - const light = new THREE.PointLight(0xffffff, 1, 100); - light.power = 2500; - camera.add(light); - scene.add(camera); - - const ambient = new THREE.AmbientLight(0x4466ff, 1); - scene.add(ambient); - - const loader = new GLTFLoader(); - loader.load('models/gltf/Michelle.glb', function (gltf) { - const object = gltf.scene; - mixer = new THREE.AnimationMixer(object); - - const action = mixer.clipAction(gltf.animations[0]); - action.play(); - - scene.add(object); - }); - - //renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.toneMapping = THREE.LinearToneMapping; - renderer.toneMappingExposure = 0.4; - document.body.appendChild(renderer.domElement); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - const delta = timer.getDelta(); - - if (mixer) mixer.update(delta); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_skinning_instancing.ts b/examples-testing/examples/webgpu_skinning_instancing.ts deleted file mode 100644 index 877ee46cb..000000000 --- a/examples-testing/examples/webgpu_skinning_instancing.ts +++ /dev/null @@ -1,128 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { pass, mix, range, color, oscSine, time } from 'three/tsl'; -import { gaussianBlur } from 'three/addons/tsl/display/GaussianBlurNode.js'; - -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -let camera, scene, renderer; -let renderPipeline; - -let mixer, timer; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.01, 40); - camera.position.set(1, 2, 3); - - scene = new THREE.Scene(); - camera.lookAt(0, 1, 0); - - timer = new THREE.Timer(); - timer.connect(document); - - // lights - - const centerLight = new THREE.PointLight(0xff9900, 1, 100); - centerLight.position.y = 4.5; - centerLight.position.z = -2; - centerLight.power = 400; - scene.add(centerLight); - - const cameraLight = new THREE.PointLight(0x0099ff, 1, 100); - cameraLight.power = 400; - camera.add(cameraLight); - scene.add(camera); - - const geometry = new THREE.PlaneGeometry(1000, 1000); - geometry.rotateX(-Math.PI / 2); - - const plane = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ color: 0x000000, visible: true })); - scene.add(plane); - - const loader = new GLTFLoader(); - loader.load('models/gltf/Michelle.glb', function (gltf) { - const object = gltf.scene; - - mixer = new THREE.AnimationMixer(object); - - const action = mixer.clipAction(gltf.animations[0]); - action.play(); - - const instanceCount = 30; - const dummy = new THREE.Object3D(); - - object.traverse(child => { - if (child.isMesh) { - const oscNode = oscSine(time.mul(0.1)); - - // random colors between instances from 0x000000 to 0xFFFFFF - const randomColors = range(new THREE.Color(0x000000), new THREE.Color(0xffffff)); - - // random [ 0, 1 ] values between instances - const randomMetalness = range(0, 1); - - child.material = new THREE.MeshStandardNodeMaterial(); - child.material.roughness = 0.1; - child.material.metalnessNode = mix(0.0, randomMetalness, oscNode); - child.material.colorNode = mix(color(0xffffff), randomColors, oscNode); - - child.isInstancedMesh = true; - child.instanceMatrix = new THREE.InstancedBufferAttribute(new Float32Array(instanceCount * 16), 16); - child.count = instanceCount; - - for (let i = 0; i < instanceCount; i++) { - dummy.position.x = -200 + (i % 5) * 70; - dummy.position.y = Math.floor(i / 5) * -200; - - dummy.updateMatrix(); - - dummy.matrix.toArray(child.instanceMatrix.array, i * 16); - } - } - }); - - scene.add(object); - }); - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // post processing - - const scenePass = pass(scene, camera); - const scenePassColor = scenePass.getTextureNode(); - const scenePassDepth = scenePass.getLinearDepthNode().remapClamp(0.15, 0.3); - - const scenePassColorBlurred = gaussianBlur(scenePassColor); - scenePassColorBlurred.directionNode = scenePassDepth; - - renderPipeline = new THREE.RenderPipeline(renderer); - renderPipeline.outputNode = scenePassColorBlurred; - - // events - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - const delta = timer.getDelta(); - - if (mixer) mixer.update(delta); - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_sprites.ts b/examples-testing/examples/webgpu_sprites.ts deleted file mode 100644 index de9f219b8..000000000 --- a/examples-testing/examples/webgpu_sprites.ts +++ /dev/null @@ -1,102 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { texture, uv, userData, fog, rangeFogFactor, color } from 'three/tsl'; - -let camera, scene, renderer; - -let map; - -let group; - -let imageWidth = 1, - imageHeight = 1; - -init(); - -function init() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera = new THREE.PerspectiveCamera(60, width / height, 1, 2100); - camera.position.z = 1500; - - scene = new THREE.Scene(); - scene.fogNode = fog(color(0x0000ff), rangeFogFactor(1500, 2100)); - - // create sprites - - const amount = 200; - const radius = 500; - - const textureLoader = new THREE.TextureLoader(); - - map = textureLoader.load('textures/sprite1.png', map => { - imageWidth = map.image.width; - imageHeight = map.image.height; - }); - - group = new THREE.Group(); - - const textureNode = texture(map); - - const material = new THREE.SpriteNodeMaterial(); - material.colorNode = textureNode.mul(uv()).mul(2).saturate(); - material.opacityNode = textureNode.a; - material.rotationNode = userData('rotation', 'float'); // get value of: sprite.userData.rotation - - for (let a = 0; a < amount; a++) { - const x = Math.random() - 0.5; - const y = Math.random() - 0.5; - const z = Math.random() - 0.5; - - const sprite = new THREE.Sprite(material); - - sprite.position.set(x, y, z); - sprite.position.normalize(); - sprite.position.multiplyScalar(radius); - - // individual rotation per sprite - sprite.userData.rotation = 0; - - group.add(sprite); - } - - scene.add(group); - - // - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(render); - document.body.appendChild(renderer.domElement); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function render() { - const time = Date.now() / 1000; - - for (let i = 0, l = group.children.length; i < l; i++) { - const sprite = group.children[i]; - const scale = Math.sin(time + sprite.position.x * 0.01) * 0.3 + 1.0; - - sprite.userData.rotation += 0.1 * (i / l); - sprite.scale.set(scale * imageWidth, scale * imageHeight, 1.0); - } - - group.rotation.x = time * 0.5; - group.rotation.y = time * 0.75; - group.rotation.z = time * 1.0; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_storage_buffer.ts b/examples-testing/examples/webgpu_storage_buffer.ts deleted file mode 100644 index 1b4461d33..000000000 --- a/examples-testing/examples/webgpu_storage_buffer.ts +++ /dev/null @@ -1,169 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { storage, If, vec3, uv, uint, float, Fn, instanceIndex, workgroupBarrier } from 'three/tsl'; - -const timestamps = { - webgpu: document.getElementById('timestamps'), - webgl: document.getElementById('timestamps_webgl'), -}; - -// WebGPU Backend -init(); - -// WebGL Backend -init(true); - -async function init(forceWebGL = false) { - const aspect = window.innerWidth / 2 / window.innerHeight; - const camera = new THREE.OrthographicCamera(-aspect, aspect, 1, -1, 0, 2); - camera.position.z = 1; - - const scene = new THREE.Scene(); - - // texture - - const size = 32; // non power of two buffer size is not well supported in WebGPU - const barCount = 32; - - const type = ['float', 'vec2', 'vec3', 'vec4']; - - const arrayBufferNodes = []; - - for (let i = 0; i < type.length; i++) { - const typeSize = i + 1; - const array = new Array(size * typeSize).fill(0); - - const arrayBuffer = new THREE.StorageInstancedBufferAttribute(new Float32Array(array), typeSize); - - arrayBufferNodes.push(storage(arrayBuffer, type[i], size).setPBO(true)); - } - - const computeInitOrder = Fn(() => { - for (let i = 0; i < type.length; i++) { - arrayBufferNodes[i].element(instanceIndex).assign(instanceIndex); - } - }); - - const computeInvertOrder = Fn(() => { - for (let i = 0; i < type.length; i++) { - const invertIndex = arrayBufferNodes[i].element(uint(size - 1).sub(instanceIndex)).toVar(); - workgroupBarrier(); - arrayBufferNodes[i].element(instanceIndex).assign(invertIndex); - } - }); - - // compute - - const computeInit = computeInitOrder().compute(size); - - const compute = computeInvertOrder().compute(size); - - const material = new THREE.MeshBasicNodeMaterial({ color: 0x00ff00 }); - - material.colorNode = Fn(() => { - const index = uint(uv().x.mul(size).floor()).toVar(); - - If(index.greaterThanEqual(size), () => { - index.assign(uint(size).sub(1)); - }); - - const color = vec3(0, 0, 0).toVar(); - - If(uv().y.greaterThan(0.0), () => { - const indexValue = arrayBufferNodes[0].element(index).toVar(); - const value = float(indexValue).div(float(size)).mul(barCount).floor().div(barCount); - - color.assign(vec3(value, 0, 0)); - }); - - If(uv().y.greaterThan(0.25), () => { - const indexValue = arrayBufferNodes[1].element(index).toVar(); - const value = float(indexValue).div(float(size)).mul(barCount).floor().div(barCount); - - color.assign(vec3(0, value, 0)); - }); - - If(uv().y.greaterThan(0.5), () => { - const indexValue = arrayBufferNodes[2].element(index).toVar(); - const value = float(indexValue).div(float(size)).mul(barCount).floor().div(barCount); - - color.assign(vec3(0, 0, value)); - }); - - If(uv().y.greaterThan(0.75), () => { - const indexValue = arrayBufferNodes[3].element(index).toVar(); - const value = float(indexValue).div(float(size)).mul(barCount).floor().div(barCount); - - color.assign(vec3(value, value, value)); - }); - - return color; - })(); - - // - - const plane = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), material); - scene.add(plane); - - const renderer = new THREE.WebGPURenderer({ antialias: false, forceWebGL: forceWebGL, trackTimestamp: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth / 2, window.innerHeight); - - await renderer.init(); - - document.body.appendChild(renderer.domElement); - renderer.domElement.style.position = 'absolute'; - renderer.domElement.style.top = '0'; - renderer.domElement.style.left = '0'; - renderer.domElement.style.width = '50%'; - renderer.domElement.style.height = '100%'; - - if (forceWebGL) { - renderer.domElement.style.left = '50%'; - - scene.background = new THREE.Color(0x212121); - } else { - scene.background = new THREE.Color(0x313131); - } - - renderer.compute(computeInit); - - // - - renderer.info.autoReset = false; - - const stepAnimation = async function () { - renderer.info.reset(); - - renderer.compute(compute); - renderer.render(scene, camera); - - renderer.resolveTimestampsAsync(THREE.TimestampQuery.COMPUTE); - renderer.resolveTimestampsAsync(THREE.TimestampQuery.RENDER); - - timestamps[forceWebGL ? 'webgl' : 'webgpu'].innerHTML = ` - - Compute ${renderer.info.compute.frameCalls} pass in ${renderer.info.compute.timestamp.toFixed(6)}ms
- Draw ${renderer.info.render.drawCalls} pass in ${renderer.info.render.timestamp.toFixed(6)}ms`; - - setTimeout(stepAnimation, 1000); - }; - - stepAnimation(); - - window.addEventListener('resize', onWindowResize); - - function onWindowResize() { - renderer.setSize(window.innerWidth / 2, window.innerHeight); - - const aspect = window.innerWidth / 2 / window.innerHeight; - - const frustumHeight = camera.top - camera.bottom; - - camera.left = (-frustumHeight * aspect) / 2; - camera.right = (frustumHeight * aspect) / 2; - - camera.updateProjectionMatrix(); - - renderer.render(scene, camera); - } -} diff --git a/examples-testing/examples/webgpu_struct_drawindirect.ts b/examples-testing/examples/webgpu_struct_drawindirect.ts deleted file mode 100644 index b0d1139db..000000000 --- a/examples-testing/examples/webgpu_struct_drawindirect.ts +++ /dev/null @@ -1,212 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { - struct, - storage, - sin, - cross, - normalize, - abs, - mix, - Fn, - vec4, - max, - pow, - time, - varyingProperty, - attribute, - uint, - atomicStore, -} from 'three/tsl'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import WebGPU from 'three/addons/capabilities/WebGPU.js'; - -if (WebGPU.isAvailable() === false) { - document.body.appendChild(WebGPU.getErrorMessage()); - - throw new Error('No WebGPU support'); -} - -const renderer = new THREE.WebGPURenderer({ antialias: true }); -renderer.outputColorSpace = THREE.SRGBColorSpace; -renderer.setPixelRatio(window.devicePixelRatio); -renderer.setSize(window.innerWidth, window.innerHeight); -renderer.setClearColor(0x000000); -renderer.setClearAlpha(0); -document.body.appendChild(renderer.domElement); - -const aspect = window.innerWidth / window.innerHeight; - -const camera = new THREE.PerspectiveCamera(50.0, aspect, 0.1, 10000); -const scene = new THREE.Scene(); - -scene.background = new THREE.Color(0x00001f); -camera.position.set(1, 1, 1); -const controls = new OrbitControls(camera, renderer.domElement); - -let computeDrawBuffer, computeInitDrawBuffer; - -init(); - -async function init() { - await renderer.init(); - - // geometry - - const vector = new THREE.Vector4(); - - const instances = 100000; - - const positions = []; - const offsets = []; - const colors = []; - const orientationsStart = []; - const orientationsEnd = []; - - positions.push(0.025, -0.025, 0); - positions.push(-0.025, 0.025, 0); - positions.push(0, 0, 0.025); - - // instanced attributes - - for (let i = 0; i < instances; i++) { - // offsets - - offsets.push(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5); - - // colors - - colors.push(Math.random(), Math.random(), Math.random(), Math.random()); - - // orientation start - - vector.set(Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1); - vector.normalize(); - - orientationsStart.push(vector.x, vector.y, vector.z, vector.w); - - // orientation end - - vector.set(Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1); - vector.normalize(); - - orientationsEnd.push(vector.x, vector.y, vector.z, vector.w); - } - - const geometry = new THREE.InstancedBufferGeometry(); - geometry.instanceCount = instances; - - geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)); - geometry.setAttribute('offset', new THREE.InstancedBufferAttribute(new Float32Array(offsets), 3)); - geometry.setAttribute('color', new THREE.InstancedBufferAttribute(new Float32Array(colors), 4)); - geometry.setAttribute( - 'orientationStart', - new THREE.InstancedBufferAttribute(new Float32Array(orientationsStart), 4), - ); - geometry.setAttribute('orientationEnd', new THREE.InstancedBufferAttribute(new Float32Array(orientationsEnd), 4)); - - const drawBuffer = new THREE.IndirectStorageBufferAttribute(new Uint32Array(5), 5); - geometry.setIndirect(drawBuffer); - - const drawBufferStruct = struct( - { - vertexCount: 'uint', - instanceCount: { type: 'uint', atomic: true }, - firstVertex: 'uint', - firstInstance: 'uint', - offset: 'uint', - }, - 'DrawBuffer', - ); - - const drawStorage = storage(drawBuffer, drawBufferStruct, drawBuffer.count); - - computeDrawBuffer = Fn(() => { - const halfTime = sin(time.mul(0.5)); - - const instanceCount = max(pow(halfTime.add(1), 4.0).mul(instances), 100).toVar('instanceCount'); - atomicStore(drawStorage.get('instanceCount'), instanceCount); - })().compute(instances); - - computeInitDrawBuffer = Fn(() => { - const drawInfo = drawStorage; - - drawInfo.get('vertexCount').assign(3); - atomicStore(drawInfo.get('instanceCount'), uint(0)); - drawInfo.get('firstVertex').assign(0); - drawInfo.get('firstInstance').assign(0); - drawInfo.get('offset').assign(0); - })().compute(1); - - const vPosition = varyingProperty('vec3', 'vPosition'); - const vColor = varyingProperty('vec4', 'vColor'); - - const positionShaderParams = { - position: attribute('position'), - offset: attribute('offset'), - color: attribute('color'), - orientationStart: attribute('orientationStart'), - orientationEnd: attribute('orientationEnd'), - time: time, - }; - - const positionFn = Fn(() => { - const { position, offset, color, orientationStart, orientationEnd } = positionShaderParams; - - const halfTime = sin(time.mul(0.5)); - - // Convert slowed sign range of (-1 to 1) to range of (1 -> 0 / 0.5 -> 3) - const oscilationRange = max(abs(halfTime.mul(2.0).add(1.0)), 0.5); - - const sphereOscilation = offset.mul(oscilationRange).add(position).toVar(); - - const orientation = normalize(mix(orientationStart, orientationEnd, halfTime)); - const vcV = cross(orientation.xyz, sphereOscilation); - const crossvcV = cross(orientation.xyz, vcV); - - vPosition.assign(vcV.mul(orientation.w.mul(2.0)).add(crossvcV.mul(2.0).add(sphereOscilation))); - vColor.assign(color); - - return vPosition; - })(); - - const fragmentFn = Fn(() => { - const color = vec4(vColor).toVar(); - - color.r.addAssign(sin(vPosition.x.mul(10.0).add(time)).mul(0.5)); - - return color; - })(); - - const material = new THREE.MeshBasicNodeMaterial({ - side: THREE.DoubleSide, - forceSinglePass: true, - transparent: true, - }); - - material.positionNode = positionFn; - material.fragmentNode = fragmentFn; - - const mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - renderer.setAnimationLoop(render); - - window.addEventListener('resize', onWindowResize, false); -} - -function render() { - controls.update(); - - renderer.render(scene, camera); - - renderer.compute(computeInitDrawBuffer); - renderer.compute(computeDrawBuffer); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - renderer.setSize(window.innerWidth, window.innerHeight); -} diff --git a/examples-testing/examples/webgpu_test_memory.ts b/examples-testing/examples/webgpu_test_memory.ts deleted file mode 100644 index aadde4dc6..000000000 --- a/examples-testing/examples/webgpu_test_memory.ts +++ /dev/null @@ -1,223 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { pass, mrt, directionToColor, normalView, screenUV, context, sample, colorToDirection } from 'three/tsl'; -import { outline } from 'three/addons/tsl/display/OutlineNode.js'; -import { ao } from 'three/addons/tsl/display/GTAONode.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer, light; -let generateMeshes = true; -let mesh; -let renderPipeline; -let aoNode, outlineNode, scenePass, prePass; -const selectedObjects = []; - -const params = { - castShadow: true, - enablePP: false, - enableOutline: true, - enableAO: true, - recreateLight: () => { - // Remove existing light - if (light) { - scene.remove(light); - light.dispose(); - light = null; - } - - // Create new directional light - light = new THREE.DirectionalLight(0xffffff, 1); - light.position.set(Math.random() * 200 - 100, 100, Math.random() * 200 - 100); - light.castShadow = params.castShadow; - scene.add(light); - }, - start: () => { - generateMeshes = true; - }, - stop: () => { - generateMeshes = false; - }, -}; - -init(); - -async function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 10000); - camera.position.z = 200; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xffffff); - - renderer = new THREE.WebGPURenderer(); - renderer.shadowMap.enabled = true; - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - container.appendChild(renderer.domElement); - - await renderer.init(); - - light = new THREE.DirectionalLight(0xffffff, 1); - light.position.set(0, 100, 0); - light.castShadow = true; - scene.add(light); - - const planeGeometry = new THREE.PlaneGeometry(1000, 1000); - const planeMaterial = new THREE.MeshLambertMaterial({ color: 0xcccccc }); - const plane = new THREE.Mesh(planeGeometry, planeMaterial); - plane.rotation.x = -Math.PI / 2; - plane.position.y = -100; - plane.receiveShadow = true; - scene.add(plane); - - // Inspector UI - const gui = renderer.inspector.createParameters('Settings'); - - gui.add(params, 'castShadow') - .name('Cast Shadow') - .onChange(value => { - if (light) light.castShadow = value; - }); - - const ppFolder = gui.addFolder('Post Processing'); - ppFolder.add(params, 'enablePP').name('Enable').onChange(updatePostProcessing); - ppFolder.add(params, 'enableOutline').name('Outline').onChange(updatePostProcessing); - ppFolder.add(params, 'enableAO').name('AO').onChange(updatePostProcessing); - - gui.add(params, 'recreateLight').name('Recreate Directional Light'); - gui.add(params, 'start').name('Start Creating Meshes'); - gui.add(params, 'stop').name('Stop Creating Meshes'); - - window.addEventListener('resize', onWindowResize); -} - -function updatePostProcessing() { - if (renderPipeline) { - renderPipeline.dispose(); - renderPipeline = null; - } - - if (scenePass) { - scenePass.dispose(); - scenePass = null; - } - - if (prePass) { - prePass.dispose(); - prePass = null; - } - - if (aoNode) { - aoNode.dispose(); - aoNode = null; - } - - if (outlineNode) { - outlineNode.dispose(); - outlineNode = null; - } - - if (params.enablePP) { - renderPipeline = new THREE.RenderPipeline(renderer); - - scenePass = pass(scene, camera); - let colorNode = scenePass; - - if (params.enableAO) { - prePass = pass(scene, camera); - prePass.setMRT( - mrt({ - output: directionToColor(normalView), - }), - ); - - const prePassNormal = sample(uv => { - return colorToDirection(prePass.getTextureNode().sample(uv)); - }); - const prePassDepth = prePass.getTextureNode('depth'); - - aoNode = ao(prePassDepth, prePassNormal, camera); - - scenePass.contextNode = context({ - ao: aoNode.getTextureNode().sample(screenUV).r, - }); - } - - if (params.enableOutline) { - outlineNode = outline(scene, camera, { - selectedObjects: selectedObjects, - }); - colorNode = colorNode.add(outlineNode); - } - - renderPipeline.outputNode = colorNode; - } -} - -function onWindowResize() { - const width = window.innerWidth; - const height = window.innerHeight; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); -} - -function createImage() { - const canvas = document.createElement('canvas'); - canvas.width = 256; - canvas.height = 256; - - const canvas2DContext = canvas.getContext('2d'); - canvas2DContext.fillStyle = - 'rgb(' + - Math.floor(Math.random() * 256) + - ',' + - Math.floor(Math.random() * 256) + - ',' + - Math.floor(Math.random() * 256) + - ')'; - canvas2DContext.fillRect(0, 0, 256, 256); - - return canvas; -} - -// - -function animate() { - if (generateMeshes) { - if (mesh) { - scene.remove(mesh); - - mesh.geometry.dispose(); - mesh.material.map.dispose(); - mesh.material.dispose(); - } - - const geometry = new THREE.SphereGeometry(50, Math.random() * 64, Math.random() * 32); - - const texture = new THREE.CanvasTexture(createImage()); - - const material = new THREE.MeshLambertMaterial({ map: texture }); - - mesh = new THREE.Mesh(geometry, material); - mesh.castShadow = true; - - scene.add(mesh); - - if (outlineNode) { - selectedObjects[0] = mesh; - } - } - - if (renderPipeline) { - renderPipeline.render(); - } else { - renderer.render(scene, camera); - } -} diff --git a/examples-testing/examples/webgpu_texturegrad.ts b/examples-testing/examples/webgpu_texturegrad.ts deleted file mode 100644 index da07b4d31..000000000 --- a/examples-testing/examples/webgpu_texturegrad.ts +++ /dev/null @@ -1,116 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { If, vec4, float, time, cos, pow, vec2, uv, texture, Fn } from 'three/tsl'; - -// WebGPU Backend -init(); - -// WebGL Backend -init(true); - -async function init(forceWebGL = false) { - const aspect = window.innerWidth / 2 / window.innerHeight; - const camera = new THREE.OrthographicCamera(-aspect, aspect); - camera.position.z = 2; - - const scene = new THREE.Scene(); - - // texture - - const material = new THREE.MeshBasicNodeMaterial({ color: 0xffffff }); - - // load async brick_diffuse - const map = await new THREE.TextureLoader().loadAsync('textures/uv_grid_opengl.jpg'); - - material.colorNode = Fn(() => { - const color = vec4(1).toVar(); - - const vuv = uv().toVar(); - const blur = pow( - float(0.0625) - .sub(cos(vuv.x.mul(20.0).add(time))) - .mul(0.0625), - 2.0, - ); - - const grad = vec2(blur).toVar(); - - If(vuv.y.greaterThan(0.5), () => { - grad.assign(0); - }); - - color.assign( - texture(map, vuv.add(vec2(blur, blur).mul(0.5))) - .grad(grad, grad) - .mul(0.25) - .add( - texture(map, vuv.add(vec2(blur, blur.negate()).mul(0.5))) - .grad(grad, grad) - .mul(0.25), - ) - .add( - texture(map, vuv.add(vec2(blur.negate(), blur).mul(0.5))) - .grad(grad, grad) - .mul(0.25), - ) - .add( - texture(map, vuv.add(vec2(blur.negate(), blur.negate()).mul(0.5))) - .grad(grad, grad) - .mul(0.25), - ), - ); - - If(vuv.y.greaterThan(0.497).and(vuv.y.lessThan(0.503)), () => { - color.assign(1); - }); - - return color; - })(); - - // - - const box = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), material); - scene.add(box); - - const renderer = new THREE.WebGPURenderer({ antialias: false, forceWebGL: forceWebGL }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth / 2, window.innerHeight); - renderer.setAnimationLoop(animate); - - document.body.appendChild(renderer.domElement); - renderer.domElement.style.position = 'absolute'; - renderer.domElement.style.top = '0'; - renderer.domElement.style.left = '0'; - renderer.domElement.style.width = '50%'; - renderer.domElement.style.height = '100%'; - - if (forceWebGL) { - renderer.domElement.style.left = '50%'; - - scene.background = new THREE.Color(0x212121); - } else { - scene.background = new THREE.Color(0x313131); - } - - // - - function animate() { - renderer.render(scene, camera); - } - - window.addEventListener('resize', onWindowResize); - - function onWindowResize() { - renderer.setSize(window.innerWidth / 2, window.innerHeight); - - const aspect = window.innerWidth / 2 / window.innerHeight; - - const frustumHeight = camera.top - camera.bottom; - - camera.left = (-frustumHeight * aspect) / 2; - camera.right = (frustumHeight * aspect) / 2; - - camera.updateProjectionMatrix(); - - renderer.render(scene, camera); - } -} diff --git a/examples-testing/examples/webgpu_textures_2d-array.ts b/examples-testing/examples/webgpu_textures_2d-array.ts deleted file mode 100644 index a1a354ff7..000000000 --- a/examples-testing/examples/webgpu_textures_2d-array.ts +++ /dev/null @@ -1,69 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { texture, uv, time, oscTriangle } from 'three/tsl'; - -import { unzipSync } from 'three/addons/libs/fflate.module.js'; - -// - -let camera, scene, mesh, renderer; - -const planeWidth = 50; -const planeHeight = 50; - -init(); - -function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 2000); - camera.position.z = 70; - - scene = new THREE.Scene(); - - // width 256, height 256, depth 109, 8-bit, zip archived raw data - - new THREE.FileLoader().setResponseType('arraybuffer').load('textures/3d/head256x256x109.zip', function (data) { - const zip = unzipSync(new Uint8Array(data)); - const array = new Uint8Array(zip['head256x256x109'].buffer); - - const map = new THREE.DataArrayTexture(array, 256, 256, 109); - map.format = THREE.RedFormat; - map.needsUpdate = true; - - let coord = uv(); - coord = coord.setY(coord.y.oneMinus()); // flip y - - let oscLayers = oscTriangle(time.mul(0.5)); // [ /\/ ] triangle osc animation - oscLayers = oscLayers.add(1).mul(0.5); // convert osc range of [ -1, 1 ] to [ 0, 1 ] - oscLayers = oscLayers.mul(map.image.depth); // scale osc range to texture depth - - const material = new THREE.MeshBasicNodeMaterial(); - material.colorNode = texture(map, coord).depth(oscLayers).r.remap(0, 1, -0.1, 1.8); // remap to make it more visible - - const geometry = new THREE.PlaneGeometry(planeWidth, planeHeight); - - mesh = new THREE.Mesh(geometry, material); - - scene.add(mesh); - }); - - renderer = new THREE.WebGPURenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(render); - container.appendChild(renderer.domElement); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_textures_2d-array_compressed.ts b/examples-testing/examples/webgpu_textures_2d-array_compressed.ts deleted file mode 100644 index 62b516458..000000000 --- a/examples-testing/examples/webgpu_textures_2d-array_compressed.ts +++ /dev/null @@ -1,86 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { texture, uniform, uv } from 'three/tsl'; - -import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js'; - -// - -let camera, scene, mesh, renderer, timer; - -const depth = uniform(0); - -const planeWidth = 50; -const planeHeight = 25; - -let depthStep = 1; - -init(); - -async function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 2000); - camera.position.z = 70; - - scene = new THREE.Scene(); - - // - timer = new THREE.Timer(); - timer.connect(document); - - renderer = new THREE.WebGPURenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - await renderer.init(); - - // - - const ktx2Loader = new KTX2Loader(); - ktx2Loader.setTranscoderPath('jsm/libs/basis/'); - ktx2Loader.detectSupport(renderer); - - ktx2Loader.load('textures/spiritedaway.ktx2', function (texturearray) { - const material = new THREE.NodeMaterial(); - - material.colorNode = texture(texturearray, uv().flipY()).depth(depth); - const geometry = new THREE.PlaneGeometry(planeWidth, planeHeight); - - mesh = new THREE.Mesh(geometry, material); - - scene.add(mesh); - }); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - if (mesh) { - const delta = timer.getDelta() * 10; - - depthStep += delta; - - const value = depthStep % 5; - - depth.value = value; - } - - render(); -} - -function render() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_textures_anisotropy.ts b/examples-testing/examples/webgpu_textures_anisotropy.ts deleted file mode 100644 index 0444823f5..000000000 --- a/examples-testing/examples/webgpu_textures_anisotropy.ts +++ /dev/null @@ -1,145 +0,0 @@ -import * as THREE from 'three/webgpu'; - -let container; - -let camera, scene1, scene2, renderer; - -let mouseX = 0, - mouseY = 0; - -init(); - -function init() { - const SCREEN_WIDTH = window.innerWidth; - const SCREEN_HEIGHT = window.innerHeight; - - container = document.createElement('div'); - document.body.appendChild(container); - - renderer = new THREE.WebGPURenderer({ antialias: true, forceWebGL: false }); - - // RENDERER - - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); - renderer.setAnimationLoop(render); - renderer.autoClear = false; - - renderer.domElement.style.position = 'relative'; - container.appendChild(renderer.domElement); - - // - - camera = new THREE.PerspectiveCamera(35, SCREEN_WIDTH / SCREEN_HEIGHT, 1, 25000); - camera.position.z = 1500; - - scene1 = new THREE.Scene(); - scene1.fog = new THREE.Fog(0xf2f7ff, 1, 25000); - - scene2 = new THREE.Scene(); - scene2.fog = new THREE.Fog(0xf2f7ff, 1, 25000); - - scene1.add(new THREE.AmbientLight(0xeef0ff, 3)); - scene2.add(new THREE.AmbientLight(0xeef0ff, 3)); - - const light1 = new THREE.DirectionalLight(0xffffff, 6); - light1.position.set(1, 1, 1); - scene1.add(light1); - - const light2 = new THREE.DirectionalLight(0xffffff, 6); - light2.position.set(1, 1, 1); - scene2.add(light2); - - // GROUND - - const textureLoader = new THREE.TextureLoader(); - - const maxAnisotropy = renderer.getMaxAnisotropy(); - - const texture1 = textureLoader.load('textures/crate.gif'); - const material1 = new THREE.MeshPhongMaterial({ color: 0xffffff, map: texture1 }); - - texture1.colorSpace = THREE.SRGBColorSpace; - texture1.anisotropy = renderer.getMaxAnisotropy(); - texture1.wrapS = texture1.wrapT = THREE.RepeatWrapping; - texture1.repeat.set(512, 512); - - const texture2 = textureLoader.load('textures/crate.gif'); - const material2 = new THREE.MeshPhongMaterial({ color: 0xffffff, map: texture2 }); - - texture2.colorSpace = THREE.SRGBColorSpace; - texture2.anisotropy = 1; - texture2.wrapS = texture2.wrapT = THREE.RepeatWrapping; - texture2.repeat.set(512, 512); - - if (maxAnisotropy > 0) { - document.getElementById('val_left').innerHTML = texture1.anisotropy; - document.getElementById('val_right').innerHTML = texture2.anisotropy; - } else { - document.getElementById('val_left').innerHTML = 'not supported'; - document.getElementById('val_right').innerHTML = 'not supported'; - } - - // - - const geometry = new THREE.PlaneGeometry(100, 100); - - const mesh1 = new THREE.Mesh(geometry, material1); - mesh1.rotation.x = -Math.PI / 2; - mesh1.scale.set(1000, 1000, 1000); - - const mesh2 = new THREE.Mesh(geometry, material2); - mesh2.rotation.x = -Math.PI / 2; - mesh2.scale.set(1000, 1000, 1000); - - scene1.add(mesh1); - scene2.add(mesh2); - - // - - document.addEventListener('mousemove', onDocumentMouseMove); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function onDocumentMouseMove(event) { - const windowHalfX = window.innerWidth / 2; - const windowHalfY = window.innerHeight / 2; - - mouseX = event.clientX - windowHalfX; - mouseY = event.clientY - windowHalfY; -} - -function render() { - const SCREEN_WIDTH = window.innerWidth; - const SCREEN_HEIGHT = window.innerHeight; - - camera.position.x += (mouseX - camera.position.x) * 0.05; - camera.position.y = THREE.MathUtils.clamp( - camera.position.y + (-(mouseY - 200) - camera.position.y) * 0.05, - 50, - 1000, - ); - - camera.lookAt(scene1.position); - renderer.clear(); - - renderer.setScissorTest(true); - - renderer.setScissor(0, 0, SCREEN_WIDTH / 2 - 2, SCREEN_HEIGHT); - renderer.render(scene1, camera); - - renderer.setScissorTest(true); - - renderer.setScissor(SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 2 - 2, SCREEN_HEIGHT); - renderer.render(scene2, camera); - - renderer.setScissorTest(false); -} diff --git a/examples-testing/examples/webgpu_textures_partialupdate.ts b/examples-testing/examples/webgpu_textures_partialupdate.ts deleted file mode 100644 index d893a041d..000000000 --- a/examples-testing/examples/webgpu_textures_partialupdate.ts +++ /dev/null @@ -1,105 +0,0 @@ -import * as THREE from 'three/webgpu'; - -let camera, scene, renderer, timer, dataTexture, diffuseMap; - -let last = 0; -const position = new THREE.Vector2(); -const color = new THREE.Color(); - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 10); - camera.position.z = 2; - - scene = new THREE.Scene(); - - timer = new THREE.Timer(); - timer.connect(document); - - const loader = new THREE.TextureLoader(); - diffuseMap = loader.load('textures/carbon/Carbon.png'); - diffuseMap.colorSpace = THREE.SRGBColorSpace; - diffuseMap.minFilter = THREE.LinearFilter; - diffuseMap.generateMipmaps = false; - - const geometry = new THREE.PlaneGeometry(2, 2); - const material = new THREE.MeshBasicMaterial({ map: diffuseMap }); - - const mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - // - - const width = 32; - const height = 32; - - const data = new Uint8Array(width * height * 4); - dataTexture = new THREE.DataTexture(data, width, height); - dataTexture.colorSpace = THREE.SRGBColorSpace; - - // - - renderer = new THREE.WebGPURenderer({ antialias: true, forceWebGL: false }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - const elapsedTime = timer.getElapsed(); - - renderer.render(scene, camera); - - if (elapsedTime - last > 0.1) { - last = elapsedTime; - - position.x = 32 * THREE.MathUtils.randInt(1, 16) - 32; - position.y = 32 * THREE.MathUtils.randInt(1, 16) - 32; - - // generate new color data - updateDataTexture(dataTexture); - - // perform copy from src to dest texture to a random position - - renderer.copyTextureToTexture(dataTexture, diffuseMap, null, position); - } -} - -function updateDataTexture(texture) { - const size = texture.image.width * texture.image.height; - const data = texture.image.data; - - // generate a random color and update texture data - - color.setHex(Math.random() * 0xffffff); - - const r = Math.floor(color.r * 255); - const g = Math.floor(color.g * 255); - const b = Math.floor(color.b * 255); - - for (let i = 0; i < size; i++) { - const stride = i * 4; - - data[stride] = r; - data[stride + 1] = g; - data[stride + 2] = b; - data[stride + 3] = 1; - } - - texture.needsUpdate = true; -} diff --git a/examples-testing/examples/webgpu_tonemapping.ts b/examples-testing/examples/webgpu_tonemapping.ts deleted file mode 100644 index 4140a49fd..000000000 --- a/examples-testing/examples/webgpu_tonemapping.ts +++ /dev/null @@ -1,141 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; -import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; - -let renderer, scene, camera, controls; -let gui; - -const params = { - exposure: 1.0, - toneMapping: 'Neutral', - blurriness: 0.3, - intensity: 1.0, -}; - -const toneMappingOptions = { - None: THREE.NoToneMapping, - Linear: THREE.LinearToneMapping, - Reinhard: THREE.ReinhardToneMapping, - Cineon: THREE.CineonToneMapping, - ACESFilmic: THREE.ACESFilmicToneMapping, - AgX: THREE.AgXToneMapping, - Neutral: THREE.NeutralToneMapping, -}; - -init().catch(function (err) { - console.error(err); -}); - -async function init() { - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - renderer.toneMapping = toneMappingOptions[params.toneMapping]; - renderer.toneMappingExposure = params.exposure; - - scene = new THREE.Scene(); - scene.backgroundBlurriness = params.blurriness; - - const light = new THREE.DirectionalLight(0xfff3ee, 3); // simulate sun - light.position.set(1, 0.05, 0.7); - scene.add(light); - - // scene.add( new THREE.DirectionalLightHelper( light, 1, 0x000000 ) ); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.01, 10); - camera.position.set(-0.02, 0.03, 0.05); - - controls = new OrbitControls(camera, renderer.domElement); - controls.enablePan = false; - controls.enableDamping = true; - controls.minDistance = 0.03; - controls.maxDistance = 0.2; - controls.target.set(0, 0.03, 0); - controls.update(); - - const hdrLoader = new HDRLoader().setPath('textures/equirectangular/'); - - const dracoLoader = new DRACOLoader(); - dracoLoader.setDecoderPath('jsm/libs/draco/gltf/'); - - const gltfLoader = new GLTFLoader(); - gltfLoader.setDRACOLoader(dracoLoader); - gltfLoader.setPath('models/gltf/'); - - const [texture, gltf] = await Promise.all([ - hdrLoader.loadAsync('venice_sunset_1k.hdr'), - gltfLoader.loadAsync('venice_mask.glb'), - ]); - - // environment - - texture.mapping = THREE.EquirectangularReflectionMapping; - - scene.background = texture; - scene.environment = texture; - - // model - - scene.add(gltf.scene); - - window.addEventListener('resize', onWindowResize); - - // - - gui = renderer.inspector.createParameters('Settings'); - const toneMappingFolder = gui.addFolder('Tone Mapping'); - - toneMappingFolder - .add(params, 'toneMapping', Object.keys(toneMappingOptions)) - - .name('type') - .onChange(function () { - renderer.toneMapping = toneMappingOptions[params.toneMapping]; - }); - - toneMappingFolder - .add(params, 'exposure', 0, 2) - - .onChange(function (value) { - renderer.toneMappingExposure = value; - }); - - const backgroundFolder = gui.addFolder('Background'); - - backgroundFolder - .add(params, 'blurriness', 0, 1) - - .onChange(function (value) { - scene.backgroundBlurriness = value; - }); - - backgroundFolder - .add(params, 'intensity', 0, 1) - - .onChange(function (value) { - scene.backgroundIntensity = value; - }); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - controls.update(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_tsl_angular_slicing.ts b/examples-testing/examples/webgpu_tsl_angular_slicing.ts deleted file mode 100644 index d74e1bc0a..000000000 --- a/examples-testing/examples/webgpu_tsl_angular_slicing.ts +++ /dev/null @@ -1,175 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { If, TWO_PI, atan, color, frontFacing, output, positionLocal, Fn, uniform, vec4 } from 'three/tsl'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let camera, scene, renderer, controls; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(-5, 5, 12); - - scene = new THREE.Scene(); - - // environment - - const hdrLoader = new UltraHDRLoader(); - hdrLoader.load('textures/equirectangular/royal_esplanade_2k.hdr.jpg', environmentMap => { - environmentMap.mapping = THREE.EquirectangularReflectionMapping; - - scene.background = environmentMap; - scene.environment = environmentMap; - }); - - // lights - - const directionalLight = new THREE.DirectionalLight('#ffffff', 4); - directionalLight.position.set(6.25, 3, 4); - directionalLight.castShadow = true; - directionalLight.shadow.mapSize.set(2048, 2048); - directionalLight.shadow.camera.near = 0.1; - directionalLight.shadow.camera.far = 30; - directionalLight.shadow.camera.top = 8; - directionalLight.shadow.camera.right = 8; - directionalLight.shadow.camera.bottom = -8; - directionalLight.shadow.camera.left = -8; - directionalLight.shadow.normalBias = 0.05; - scene.add(directionalLight); - - // TSL functions - - const inAngle = Fn(([position, angleStart, angleArc]) => { - const angle = atan(position.y, position.x).sub(angleStart).mod(TWO_PI).toVar(); - return angle.greaterThan(0).and(angle.lessThan(angleArc)); - }); - - // materials - - const defaultMaterial = new THREE.MeshPhysicalNodeMaterial({ - metalness: 0.5, - roughness: 0.25, - envMapIntensity: 0.5, - color: '#858080', - }); - - const slicedMaterial = new THREE.MeshPhysicalNodeMaterial({ - metalness: 0.5, - roughness: 0.25, - envMapIntensity: 0.5, - color: '#858080', - side: THREE.DoubleSide, - }); - - // uniforms - - const sliceStart = uniform(1.75); - const sliceArc = uniform(1.25); - const sliceColor = uniform(color('#b62f58')); - - // mask - - const mask = inAngle(positionLocal.xy, sliceStart, sliceArc).not(); - - slicedMaterial.maskNode = mask; - //slicedMaterial.maskShadowNode = mask; // optional: custom mask shadows - - // output - - slicedMaterial.outputNode = Fn(() => { - // backface color - - const finalOutput = output; - If(frontFacing.not(), () => { - finalOutput.assign(vec4(sliceColor, 1)); - }); - - return finalOutput; - })(); - - // model - - const dracoLoader = new DRACOLoader(); - dracoLoader.setDecoderPath('jsm/libs/draco/'); - - const gltfLoader = new GLTFLoader(); - gltfLoader.setDRACOLoader(dracoLoader); - - gltfLoader.load('./models/gltf/gears.glb', gltf => { - const model = gltf.scene; - - model.traverse(child => { - if (child.isMesh) { - if (child.name === 'outerHull') child.material = slicedMaterial; - else child.material = defaultMaterial; - - child.castShadow = true; - child.receiveShadow = true; - } - }); - - scene.add(model); - }); - - // plane - - const plane = new THREE.Mesh( - new THREE.PlaneGeometry(10, 10, 10), - new THREE.MeshStandardMaterial({ color: '#aaaaaa' }), - ); - plane.receiveShadow = true; - plane.position.set(-4, -3, -4); - plane.lookAt(new THREE.Vector3(0, 0, 0)); - scene.add(plane); - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = 1; - renderer.shadowMap.enabled = true; - renderer.inspector = new Inspector(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // controls - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - controls.minDistance = 0.1; - controls.maxDistance = 50; - - // events - - window.addEventListener('resize', onWindowResize); - - // debug - - const gui = renderer.inspector.createParameters('Parameters'); - gui.add(sliceStart, 'value', -Math.PI, Math.PI, 0.001).name('sliceStart'); - gui.add(sliceArc, 'value', 0, Math.PI * 2, 0.001).name('sliceArc'); - gui.addColor({ color: sliceColor.value.getHexString(THREE.SRGBColorSpace) }, 'color').onChange(value => - sliceColor.value.set(value), - ); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -async function animate() { - controls.update(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_tsl_earth.ts b/examples-testing/examples/webgpu_tsl_earth.ts deleted file mode 100644 index dece09afb..000000000 --- a/examples-testing/examples/webgpu_tsl_earth.ts +++ /dev/null @@ -1,174 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { - step, - normalWorldGeometry, - output, - texture, - vec3, - vec4, - normalize, - positionWorld, - bumpMap, - cameraPosition, - color, - uniform, - mix, - uv, - max, -} from 'three/tsl'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer, controls, globe, timer; - -init(); - -function init() { - timer = new THREE.Timer(); - timer.connect(document); - - camera = new THREE.PerspectiveCamera(25, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(4.5, 2, 3); - - scene = new THREE.Scene(); - - // sun - - const sun = new THREE.DirectionalLight('#ffffff', 2); - sun.position.set(0, 0, 3); - scene.add(sun); - - // uniforms - - const atmosphereDayColor = uniform(color('#4db2ff')); - const atmosphereTwilightColor = uniform(color('#bc490b')); - const roughnessLow = uniform(0.25); - const roughnessHigh = uniform(0.35); - - // textures - - const textureLoader = new THREE.TextureLoader(); - - const dayTexture = textureLoader.load('./textures/planets/earth_day_4096.jpg'); - dayTexture.colorSpace = THREE.SRGBColorSpace; - dayTexture.anisotropy = 8; - - const nightTexture = textureLoader.load('./textures/planets/earth_night_4096.jpg'); - nightTexture.colorSpace = THREE.SRGBColorSpace; - nightTexture.anisotropy = 8; - - const bumpRoughnessCloudsTexture = textureLoader.load('./textures/planets/earth_bump_roughness_clouds_4096.jpg'); - bumpRoughnessCloudsTexture.anisotropy = 8; - - // fresnel - - const viewDirection = positionWorld.sub(cameraPosition).normalize(); - const fresnel = viewDirection.dot(normalWorldGeometry).abs().oneMinus().toVar(); - - // sun orientation - - const sunOrientation = normalWorldGeometry.dot(normalize(sun.position)).toVar(); - - // atmosphere color - - const atmosphereColor = mix(atmosphereTwilightColor, atmosphereDayColor, sunOrientation.smoothstep(-0.25, 0.75)); - - // globe - - const globeMaterial = new THREE.MeshStandardNodeMaterial(); - - const cloudsStrength = texture(bumpRoughnessCloudsTexture, uv()).b.smoothstep(0.2, 1); - - globeMaterial.colorNode = mix(texture(dayTexture), vec3(1), cloudsStrength.mul(2)); - - const roughness = max(texture(bumpRoughnessCloudsTexture).g, step(0.01, cloudsStrength)); - globeMaterial.roughnessNode = roughness.remap(0, 1, roughnessLow, roughnessHigh); - - const night = texture(nightTexture); - const dayStrength = sunOrientation.smoothstep(-0.25, 0.5); - - const atmosphereDayStrength = sunOrientation.smoothstep(-0.5, 1); - const atmosphereMix = atmosphereDayStrength.mul(fresnel.pow(2)).clamp(0, 1); - - let finalOutput = mix(night.rgb, output.rgb, dayStrength); - finalOutput = mix(finalOutput, atmosphereColor, atmosphereMix); - - globeMaterial.outputNode = vec4(finalOutput, output.a); - - const bumpElevation = max(texture(bumpRoughnessCloudsTexture).r, cloudsStrength); - globeMaterial.normalNode = bumpMap(bumpElevation); - - const sphereGeometry = new THREE.SphereGeometry(1, 64, 64); - globe = new THREE.Mesh(sphereGeometry, globeMaterial); - scene.add(globe); - - // atmosphere - - const atmosphereMaterial = new THREE.MeshBasicNodeMaterial({ side: THREE.BackSide, transparent: true }); - let alpha = fresnel.remap(0.73, 1, 1, 0).pow(3); - alpha = alpha.mul(sunOrientation.smoothstep(-0.5, 1)); - atmosphereMaterial.outputNode = vec4(atmosphereColor, alpha); - - const atmosphere = new THREE.Mesh(sphereGeometry, atmosphereMaterial); - atmosphere.scale.setScalar(1.04); - scene.add(atmosphere); - - // renderer - - renderer = new THREE.WebGPURenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // controls - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - controls.minDistance = 0.1; - controls.maxDistance = 50; - - // events - - window.addEventListener('resize', onWindowResize); - - // debug - - const gui = renderer.inspector.createParameters('Parameters'); - - gui.addColor({ color: atmosphereDayColor.value.getHex(THREE.SRGBColorSpace) }, 'color') - .onChange(value => { - atmosphereDayColor.value.set(value); - }) - .name('atmosphereDayColor'); - - gui.addColor({ color: atmosphereTwilightColor.value.getHex(THREE.SRGBColorSpace) }, 'color') - .onChange(value => { - atmosphereTwilightColor.value.set(value); - }) - .name('atmosphereTwilightColor'); - - gui.add(roughnessLow, 'value', 0, 1, 0.001).name('roughnessLow'); - gui.add(roughnessHigh, 'value', 0, 1, 0.001).name('roughnessHigh'); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -async function animate() { - timer.update(); - - const delta = timer.getDelta(); - globe.rotation.y += delta * 0.025; - - controls.update(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_tsl_galaxy.ts b/examples-testing/examples/webgpu_tsl_galaxy.ts deleted file mode 100644 index 739138504..000000000 --- a/examples-testing/examples/webgpu_tsl_galaxy.ts +++ /dev/null @@ -1,99 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { color, cos, float, mix, range, sin, time, uniform, uv, vec3, vec4, TWO_PI } from 'three/tsl'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -let camera, scene, renderer, controls; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(4, 2, 5); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x201919); - - // galaxy - - const material = new THREE.SpriteNodeMaterial({ - depthWrite: false, - blending: THREE.AdditiveBlending, - }); - - const size = uniform(0.08); - material.scaleNode = range(0, 1).mul(size); - - const radiusRatio = range(0, 1); - const radius = radiusRatio.pow(1.5).mul(5).toVar(); - - const branches = 3; - const branchAngle = range(0, branches).floor().mul(TWO_PI.div(branches)); - const angle = branchAngle.add(time.mul(radiusRatio.oneMinus())); - - const position = vec3(cos(angle), 0, sin(angle)).mul(radius); - - const randomOffset = range(vec3(-1), vec3(1)).pow3().mul(radiusRatio).add(0.2); - - material.positionNode = position.add(randomOffset); - - const colorInside = uniform(color('#ffa575')); - const colorOutside = uniform(color('#311599')); - const colorFinal = mix(colorInside, colorOutside, radiusRatio.oneMinus().pow(2).oneMinus()); - const alpha = float(0.1).div(uv().sub(0.5).length()).sub(0.2); - material.colorNode = vec4(colorFinal, alpha); - - const mesh = new THREE.InstancedMesh(new THREE.PlaneGeometry(1, 1), material, 20000); - scene.add(mesh); - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - controls.minDistance = 0.1; - controls.maxDistance = 50; - - // events - - window.addEventListener('resize', onWindowResize); - - // debug - - const gui = renderer.inspector.createParameters('Parameters'); - - gui.add(size, 'value', 0, 1, 0.001).name('size'); - - gui.addColor({ color: colorInside.value.getHex(THREE.SRGBColorSpace) }, 'color') - .name('colorInside') - .onChange(function (value) { - colorInside.value.set(value); - }); - - gui.addColor({ color: colorOutside.value.getHex(THREE.SRGBColorSpace) }, 'color') - .name('colorOutside') - .onChange(function (value) { - colorOutside.value.set(value); - }); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - controls.update(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_tsl_graph.ts b/examples-testing/examples/webgpu_tsl_graph.ts deleted file mode 100644 index cdde58a9a..000000000 --- a/examples-testing/examples/webgpu_tsl_graph.ts +++ /dev/null @@ -1,228 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { Fn, abs, fract, fwidth, length, max, saturate, smoothstep, vec3, vec4, positionWorld, float } from 'three/tsl'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { TSLGraphLoader } from 'three/addons/inspector/extensions/tsl-graph/TSLGraphLoader.js'; - -let camera, scene, renderer; -let controls; -let prefab; - -init(); - -async function initTSLGraph() { - // Create Materials - - const m1 = new THREE.MeshPhysicalNodeMaterial(); - m1.userData.graphId = 'mat_1-physical'; - - const m2 = new THREE.MeshStandardNodeMaterial(); - m2.userData.graphId = 'mat_2-standard'; - - const m3 = new THREE.MeshPhongNodeMaterial(); - m3.userData.graphId = 'mat_3-phong'; - - const m4 = new THREE.MeshBasicNodeMaterial(); - m4.userData.graphId = 'mat_4-basic'; - - const materials = [m1, m2, m3, m4]; - - for (let i = 0; i < materials.length; i++) { - createShaderBall(materials[i], new THREE.Vector3((i & 1) * 4 - 2, 0, (i & 2) * 2 - 2)); - } - - // TSL Graph Editor - - renderer.inspector.onExtension('TSL Graph', async tslGraph => { - renderer.inspector.setActiveTab(tslGraph); - - // Apply TSL Graph from Local Storage if exists - // Every time a TSL Graph is changed, it will be stored in the local storage - - if (tslGraph.hasGraphs) { - tslGraph.apply(scene); - } else { - // Load a TSL Graph from a file - // Use it for production - - const tslLoader = new TSLGraphLoader(); - const applier = await tslLoader.setPath('./shaders/').loadAsync('tsl-graphs.json'); - - applier.apply(scene); - } - }); - - // Active TSL Graph Editor - // Only is needed if you don't activate it from the GUI - - renderer.inspector.setActiveExtension('TSL Graph', true); -} - -async function init() { - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.toneMapping = THREE.NeutralToneMapping; - renderer.toneMappingExposure = 0.9; - renderer.inspector = new Inspector(); - renderer.setAnimationLoop(render); - document.body.appendChild(renderer.domElement); - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 200); - camera.position.set(3, 5, 8); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x444444); - - await renderer.init(); - - // Ground plane - - const plane = new THREE.Mesh(new THREE.CircleGeometry(40), createGroudMaterial()); - plane.rotation.x = -Math.PI / 2; - plane.renderOrder = -1; - scene.add(plane); - - // - - controls = new OrbitControls(camera); - controls.connect(renderer.domElement); - controls.enableDamping = true; - controls.minDistance = 2; - controls.maxDistance = 40; - - // - - const environment = await new HDRLoader() - .setPath('textures/equirectangular/') - .loadAsync('monochrome_studio_02_1k.hdr'); - environment.mapping = THREE.EquirectangularReflectionMapping; - - const light = new THREE.DirectionalLight(0xffffff, 1); - light.position.set(1, 1, 1); - scene.add(light); - - scene.environment = environment; - - prefab = (await new GLTFLoader().loadAsync('./models/gltf/ShaderBall.glb')).scene; - - await initTSLGraph(); - - initGUI(); - - // Events - - resize(); - - window.addEventListener('resize', resize); - renderer.inspector.addEventListener('resize', resize); -} - -async function createShaderBall(material, position = new THREE.Vector3()) { - const model = prefab.clone(); - model.position.copy(position); - scene.add(model); - - // - - const calibrationMesh = model.getObjectByName('Calibration_Mesh'); - calibrationMesh.material = material; - - const previewMesh = model.getObjectByName('Preview_Mesh'); - previewMesh.material = material; - - calibrationMesh.renderOrder = 1; - previewMesh.renderOrder = 2; -} - -function initGUI() { - const gui = renderer.inspector.createParameters('Shader Ball'); - - const API = { - showCalibrationMesh: true, - showPreviewMesh: true, - }; - - gui.add(API, 'showCalibrationMesh') - .name('Calibration Mesh') - .onChange(function (value) { - setVisibility('Calibration_Mesh', value); - }); - - gui.add(API, 'showPreviewMesh') - .name('Preview Mesh') - .onChange(function (value) { - setVisibility('Preview_Mesh', value); - }); - - renderer.inspector.hide(); -} - -function resize() { - const size = renderer.inspector.getSize(); - const width = window.innerWidth - size.width; - const height = window.innerHeight - size.height; - - camera.aspect = width / height; - camera.updateProjectionMatrix(); - - renderer.setSize(width, height); -} - -function render() { - if (controls) controls.update(); - - renderer.render(scene, camera); -} - -// - -function setVisibility(name, visible) { - scene.traverse(function (node) { - if (node.isMesh) { - if (node.name == name) node.visible = visible; - } - }); -} - -function createGroudMaterial() { - const material = new THREE.MeshBasicNodeMaterial(); - - const grid = Fn(([coord, lineWidth = float(0.01), dotSize = float(0.03)]) => { - // https://bgolus.medium.com/the-best-darn-grid-shader-yet-727f9278b9d8 - - const g = fract(coord); - const fw = fwidth(coord); - const gx = abs(g.x.sub(0.5)); - const gy = abs(g.y.sub(0.5)); - - const lineX = saturate(lineWidth.sub(gx).div(fw.x).add(0.5)); - const lineY = saturate(lineWidth.sub(gy).div(fw.y).add(0.5)); - const lines = max(lineX, lineY); - - const squareDist = max(gx, gy); - const aa = max(fw.x, fw.y); - const dots = smoothstep(dotSize.add(aa), dotSize.sub(aa), squareDist); - - return max(dots, lines); - }); - - const fade = Fn(([radius = float(10.0), falloff = float(1.0)]) => { - return smoothstep(radius, radius.sub(falloff), length(positionWorld)); - }); - - const gridColor = vec4(vec3(0.2), 1.0); - const baseColor = vec4(vec3(0.4), 0.0); - - material.colorNode = grid(positionWorld.xz, 0.007, 0.03).mix(baseColor, gridColor).mul(fade(30.0, 20.0)); - material.transparent = true; - - return material; -} diff --git a/examples-testing/examples/webgpu_tsl_halftone.ts b/examples-testing/examples/webgpu_tsl_halftone.ts deleted file mode 100644 index 682e1df64..000000000 --- a/examples-testing/examples/webgpu_tsl_halftone.ts +++ /dev/null @@ -1,214 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { color, mix, normalWorld, output, Fn, uniform, vec4, rotate, screenCoordinate, screenSize } from 'three/tsl'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -let camera, scene, renderer, controls, timer, halftoneSettings; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(25, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(6, 3, 10); - - scene = new THREE.Scene(); - - timer = new THREE.Timer(); - timer.connect(document); - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.setClearColor('#000000'); - document.body.appendChild(renderer.domElement); - - // inspector/gui - - renderer.inspector = new Inspector(); - - const gui = renderer.inspector.createParameters('Parameters'); - - // lights - - const ambientLight = new THREE.AmbientLight('#ffffff', 3); - scene.add(ambientLight); - - const directionalLight = new THREE.DirectionalLight('#ffffff', 8); - directionalLight.position.set(4, 3, 1); - scene.add(directionalLight); - - const lightsFolder = gui.addFolder('💡 lights'); - lightsFolder.add(ambientLight, 'intensity', 0, 10, 0.001).name('ambient intensity'); - lightsFolder.add(directionalLight, 'intensity', 0, 20, 0.001).name('directional intensity'); - - // halftone settings - - halftoneSettings = [ - // purple shade - - { - count: 140, - color: '#fb00ff', - direction: new THREE.Vector3(-0.4, -1, 0.5), - start: 1, - end: 0, - mixLow: 0, - mixHigh: 0.5, - radius: 0.8, - }, - - // cyan highlight - - { - count: 180, - color: '#94ffd1', - direction: new THREE.Vector3(0.5, 0.5, -0.2), - start: 0.55, - end: 0.2, - mixLow: 0.5, - mixHigh: 1, - radius: 0.8, - }, - ]; - - for (const index in halftoneSettings) { - const settings = halftoneSettings[index]; - - // uniforms - - const uniforms = {}; - - uniforms.count = uniform(settings.count); - uniforms.color = uniform(color(settings.color)); - uniforms.direction = uniform(settings.direction); - uniforms.start = uniform(settings.start); - uniforms.end = uniform(settings.end); - uniforms.mixLow = uniform(settings.mixLow); - uniforms.mixHigh = uniform(settings.mixHigh); - uniforms.radius = uniform(settings.radius); - - settings.uniforms = uniforms; - - // debug - - const folder = gui.addFolder(`⚪️ halftone ${index}`); - - folder.addColor({ color: uniforms.color.value }, 'color'); - folder.add(uniforms.count, 'value', 1, 200, 1).name('count'); - folder.add(uniforms.direction.value, 'x', -1, 1, 0.01).listen(); - folder.add(uniforms.direction.value, 'y', -1, 1, 0.01).listen(); - folder.add(uniforms.direction.value, 'z', -1, 1, 0.01).listen(); - folder.add(uniforms.start, 'value', -1, 1, 0.01).name('start'); - folder.add(uniforms.end, 'value', -1, 1, 0.01).name('end'); - folder.add(uniforms.mixLow, 'value', 0, 1, 0.01).name('mix low'); - folder.add(uniforms.mixHigh, 'value', 0, 1, 0.01).name('mix high'); - folder.add(uniforms.radius, 'value', 0, 1, 0.01).name('radius'); - } - - // halftone functions - - const halftone = Fn(([count, color, direction, start, end, radius, mixLow, mixHigh]) => { - // grid pattern - - let gridUv = screenCoordinate.xy.div(screenSize.yy).mul(count); - gridUv = rotate(gridUv, Math.PI * 0.25).mod(1); - - // orientation strength - - const orientationStrength = normalWorld.dot(direction.normalize()).remapClamp(end, start, 0, 1); - - // mask - - const mask = orientationStrength - .mul(radius) - .mul(0.5) - .step(gridUv.sub(0.5).length()) - .mul(mix(mixLow, mixHigh, orientationStrength)); - - return vec4(color, mask); - }); - - const halftones = Fn(([input]) => { - const halftonesOutput = input; - - for (const settings of halftoneSettings) { - const halfToneOutput = halftone( - settings.uniforms.count, - settings.uniforms.color, - settings.uniforms.direction, - settings.uniforms.start, - settings.uniforms.end, - settings.uniforms.radius, - settings.uniforms.mixLow, - settings.uniforms.mixHigh, - ); - halftonesOutput.rgb.assign(mix(halftonesOutput.rgb, halfToneOutput.rgb, halfToneOutput.a)); - } - - return halftonesOutput; - }); - - // default material - - const defaultMaterial = new THREE.MeshStandardNodeMaterial({ color: '#ff622e' }); - defaultMaterial.outputNode = halftones(output); - - const folder = gui.addFolder('🎨 default material'); - folder.addColor({ color: defaultMaterial.color }, 'color'); - - // objects - - const torusKnot = new THREE.Mesh(new THREE.TorusKnotGeometry(0.6, 0.25, 128, 32), defaultMaterial); - torusKnot.position.x = 3; - scene.add(torusKnot); - - const sphere = new THREE.Mesh(new THREE.SphereGeometry(1, 64, 64), defaultMaterial); - sphere.position.x = -3; - scene.add(sphere); - - const gltfLoader = new GLTFLoader(); - gltfLoader.load('./models/gltf/Michelle.glb', gltf => { - const model = gltf.scene; - model.position.y = -2; - model.scale.setScalar(2.5); - model.traverse(child => { - if (child.isMesh) child.material.outputNode = halftones(output); - }); - - scene.add(model); - }); - - // controls - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - controls.minDistance = 0.1; - controls.maxDistance = 50; - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -async function animate() { - timer.update(); - - controls.update(); - - const time = timer.getElapsed(); - halftoneSettings[1].uniforms.direction.value.x = Math.cos(time); - halftoneSettings[1].uniforms.direction.value.y = Math.sin(time); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_tsl_interoperability.ts b/examples-testing/examples/webgpu_tsl_interoperability.ts deleted file mode 100644 index e57c66eeb..000000000 --- a/examples-testing/examples/webgpu_tsl_interoperability.ts +++ /dev/null @@ -1,263 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { - Fn, - attribute, - varyingProperty, - time, - uniform, - wgslFn, - texture, - sampler, - uv, - clamp, - float, - vec2, - vec3, - fract, - floor, - positionGeometry, - sin, -} from 'three/tsl'; - -import WebGPU from 'three/addons/capabilities/WebGPU.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let renderer, camera, scene; -const dpr = window.devicePixelRatio; - -const crtWidthUniform = uniform(1608); -const crtHeightUniform = uniform(1608); - -const canvas = document.getElementById('c'); - -function onWindowResize() { - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - renderer.render(scene, camera); -} - -function init() { - if (WebGPU.isAvailable() === false) { - document.body.appendChild(WebGPU.getErrorMessage()); - - throw new Error('No WebGPU support'); - } - - const vUv = varyingProperty('vec2', 'vUv'); - - // In WGSL, access varying properties from the varying struct - const wgslVertexShader = wgslFn( - ` - fn crtVertex( - position: vec3f, - uv: vec2f - ) -> vec3 { - varyings.vUv = uv; - return position; - } - `, - [vUv], - ); - - // Only wgsl vertex shaders take varyings arguments when defined. - // For a wgsl fragment shader, pass the varyingProperty node to the - // fragment shader's constructor to access the varying value computed - // by the vertex shader. - const wgslFragmentShader = wgslFn(` - fn crtFragment( - vUv: vec2f, - tex: texture_2d, - texSampler: sampler, - crtWidth: f32, - crtHeight: f32, - cellOffset: f32, - cellSize: f32, - borderMask: f32, - time: f32, - speed: f32, - pulseIntensity: f32, - pulseWidth: f32, - pulseRate: f32 - ) -> vec3 { - // Convert uv into map of pixels - var pixel = ( vUv * 0.5 + 0.5 ) * vec2( - crtWidth, - crtHeight - ); - // Coordinate for each cell in the pixel map - let coord = pixel / cellSize; - // Three color values for each cell (r, g, b) - let subcoord = coord * vec2f( 3.0, 1.0 ); - let offset = vec2( 0, fract( floor( coord.x ) * cellOffset ) ); - - let maskCoord = floor( coord + offset ) * cellSize; - - var samplePoint = maskCoord / vec2(crtWidth, crtHeight); - samplePoint.x += fract( time * speed / 20 ); - - var color = textureSample( - tex, - texSampler, - samplePoint - ).xyz; - - // Current implementation does not give an even amount of space to each r, g, b unit of a cell - // Fix/hack this by multiplying subCoord.x by cellSize at cellSizes below 6 - let ind = floor( subcoord.x ) % 3; - - var maskColor = vec3( - f32( ind == 0.0 ), - f32( ind == 1.0 ), - f32( ind == 2.0 ) - ) * 3.0; - - let cellUV = fract( subcoord + offset ) * 2.0 - 1.0; - var border: vec2 = 1.0 - cellUV * cellUV * borderMask; - - maskColor *= vec3f( clamp( border.x, 0.0, 1.0 ) * clamp( border.y, 0.0, 1.0) ); - - color *= maskColor; - - color.r *= 1.0 + pulseIntensity * sin( pixel.y / pulseWidth + time * pulseRate ); - color.b *= 1.0 + pulseIntensity * sin( pixel.y / pulseWidth + time * pulseRate ); - color.g *= 1.0 + pulseIntensity * sin( pixel.y / pulseWidth + time * pulseRate ); - - return color; - } - `); - - const textureLoader = new THREE.TextureLoader(); - const planetTexture = textureLoader.load('textures/planets/earth_lights_2048.png'); - planetTexture.wrapS = THREE.RepeatWrapping; - planetTexture.wrapT = THREE.RepeatWrapping; - - // Node Uniforms: - // Passed to WGSL Functions. - // Manipulated directly in TSL Functions. - const cellOffsetUniform = uniform(0.5); - const cellSizeUniform = uniform(6); - const borderMaskUniform = uniform(1); - const pulseIntensityUniform = uniform(0.06); - const pulseWidthUniform = uniform(60); - const pulseRateUniform = uniform(20); - const wgslShaderSpeedUniform = uniform(1.0); - const tslShaderSpeedUniform = uniform(1.0); - - // - - const wgslShaderMaterial = new THREE.MeshBasicNodeMaterial(); - - // Accessed attributes correspond to a Mesh or BufferGeometry's setAttribute() calls. - wgslShaderMaterial.positionNode = wgslVertexShader({ - position: attribute('position'), - uv: attribute('uv'), - }); - - wgslShaderMaterial.fragmentNode = wgslFragmentShader({ - vUv: vUv, - tex: texture(planetTexture), - texSampler: sampler(planetTexture), - crtWidth: crtWidthUniform, - crtHeight: crtHeightUniform, - cellOffset: cellOffsetUniform, - cellSize: cellSizeUniform, - borderMask: borderMaskUniform, - time: time, - speed: wgslShaderSpeedUniform, - pulseIntensity: pulseIntensityUniform, - pulseWidth: pulseWidthUniform, - pulseRate: pulseRateUniform, - }); - - // - - const tslVertexShader = Fn(() => { - vUv.assign(uv()); - return positionGeometry; - }); - - const tslFragmentShader = Fn(() => { - const dimensions = vec2(crtWidthUniform, crtHeightUniform); - const translatedUV = vUv.mul(0.5).add(0.5); - const pixel = translatedUV.mul(dimensions); - - const coord = pixel.div(cellSizeUniform); - const subCoord = coord.mul(vec2(3.0, 1.0)); - - const cellOffset = vec2(0.0, fract(floor(coord.x).mul(cellOffsetUniform))); - - const maskCoord = floor(coord.add(cellOffset)).mul(cellSizeUniform); - const samplePoint = maskCoord.div(dimensions); - const scaledTime = time.mul(tslShaderSpeedUniform); - samplePoint.x = samplePoint.x.add(fract(scaledTime.div(20))); - samplePoint.y = samplePoint.y.sub(1.5); - - let color = texture(planetTexture, samplePoint); - - const ind = floor(subCoord.x).mod(3); - - let maskColor = vec3(ind.equal(0.0), ind.equal(1.0), ind.equal(2.0)).mul(3.0); - - const subCoordOffset = fract(subCoord.add(cellOffset)); - let cellUV = subCoordOffset.mul(2.0); - cellUV = cellUV.sub(1.0); - - const border = float(1.0).sub(cellUV.mul(cellUV).mul(borderMaskUniform)); - - const clampX = clamp(border.x, 0.0, 1.0); - const clampY = clamp(border.y, 0.0, 1.0); - const borderClamp = clampX.mul(clampY); - maskColor = maskColor.mul(borderClamp); - - color = color.mul(maskColor); - - const pixelDampen = pixel.y.div(pulseWidthUniform); - let pulse = sin(pixelDampen.add(time.mul(pulseRateUniform))); - pulse = pulse.mul(pulseIntensityUniform); - color = color.mul(float(1.0).add(pulse)); - - return color; - }); - - const tslShaderMaterial = new THREE.MeshBasicNodeMaterial(); - tslShaderMaterial.positionNode = tslVertexShader(); - tslShaderMaterial.colorNode = tslFragmentShader(); - - camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1); - scene = new THREE.Scene(); - - const geometry = new THREE.PlaneGeometry(2, 1); - - const wgslQuad = new THREE.Mesh(geometry, wgslShaderMaterial); - wgslQuad.position.y += 0.5; - scene.add(wgslQuad); - - const tslQuad = new THREE.Mesh(geometry, tslShaderMaterial); - tslQuad.position.y -= 0.5; - scene.add(tslQuad); - - renderer = new THREE.WebGPURenderer({ antialias: true, canvas: canvas }); - renderer.setPixelRatio(dpr); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.outputColorSpace = THREE.LinearSRGBColorSpace; - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - window.addEventListener('resize', onWindowResize); - - const gui = renderer.inspector.createParameters('Parameters'); - - gui.add(cellSizeUniform, 'value', 6, 50, 1).name('Cell Size'); - gui.add(cellOffsetUniform, 'value', 0, 1, 0.1).name('Cell Offset'); - gui.add(borderMaskUniform, 'value', 0, 5, 0.1).name('Border Mask'); - gui.add(pulseIntensityUniform, 'value', 0, 0.5, 0.01).name('Pulse Intensity'); - gui.add(pulseWidthUniform, 'value', 10, 100, 5).name('Pulse Width'); - gui.add(wgslShaderSpeedUniform, 'value', 1, 10).name('WGSL Shader Speed'); - gui.add(tslShaderSpeedUniform, 'value', 1, 10).name('TSL Shader Speed'); -} - -init(); diff --git a/examples-testing/examples/webgpu_tsl_procedural_terrain.ts b/examples-testing/examples/webgpu_tsl_procedural_terrain.ts deleted file mode 100644 index 5630137b8..000000000 --- a/examples-testing/examples/webgpu_tsl_procedural_terrain.ts +++ /dev/null @@ -1,318 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { - mx_noise_float, - color, - cross, - dot, - float, - transformNormalToView, - positionLocal, - sign, - step, - Fn, - uniform, - varying, - vec2, - vec3, - Loop, -} from 'three/tsl'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; - -let camera, scene, renderer, controls, drag; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(-10, 8, -2.2); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x201919); - - // environment - - const hdrLoader = new HDRLoader(); - hdrLoader.load('./textures/equirectangular/pedestrian_overpass_1k.hdr', environmentMap => { - environmentMap.mapping = THREE.EquirectangularReflectionMapping; - - scene.background = environmentMap; - scene.backgroundBlurriness = 0.5; - scene.environment = environmentMap; - }); - - // lights - - const directionalLight = new THREE.DirectionalLight('#ffffff', 2); - directionalLight.position.set(6.25, 3, 4); - directionalLight.castShadow = true; - directionalLight.shadow.mapSize.set(1024, 1024); - directionalLight.shadow.camera.near = 0.1; - directionalLight.shadow.camera.far = 30; - directionalLight.shadow.camera.top = 8; - directionalLight.shadow.camera.right = 8; - directionalLight.shadow.camera.bottom = -8; - directionalLight.shadow.camera.left = -8; - directionalLight.shadow.normalBias = 0.05; - scene.add(directionalLight); - - // terrain - - const material = new THREE.MeshStandardNodeMaterial({ - metalness: 0, - roughness: 0.5, - color: '#85d534', - }); - - const noiseIterations = uniform(3); - const positionFrequency = uniform(0.175); - const warpFrequency = uniform(6); - const warpStrength = uniform(1); - const strength = uniform(10); - const offset = uniform(vec2(0, 0)); - const normalLookUpShift = uniform(0.01); - const colorSand = uniform(color('#ffe894')); - const colorGrass = uniform(color('#85d534')); - const colorSnow = uniform(color('#ffffff')); - const colorRock = uniform(color('#bfbd8d')); - - const vNormal = varying(vec3()); - const vPosition = varying(vec3()); - - const terrainElevation = Fn(([position]) => { - const warpedPosition = position.add(offset).toVar(); - warpedPosition.addAssign( - mx_noise_float(warpedPosition.mul(positionFrequency).mul(warpFrequency), 1, 0).mul(warpStrength), - ); - - const elevation = float(0).toVar(); - Loop({ type: 'float', start: float(1), end: noiseIterations.toFloat(), condition: '<=' }, ({ i }) => { - const noiseInput = warpedPosition.mul(positionFrequency).mul(i.mul(2)).add(i.mul(987)); - const noise = mx_noise_float(noiseInput, 1, 0).div(i.add(1).mul(2)); - elevation.addAssign(noise); - }); - - const elevationSign = sign(elevation); - elevation.assign(elevation.abs().pow(2).mul(elevationSign).mul(strength)); - - return elevation; - }); - - material.positionNode = Fn(() => { - // neighbours positions - - const neighbourA = positionLocal.xyz.add(vec3(normalLookUpShift, 0.0, 0.0)).toVar(); - const neighbourB = positionLocal.xyz.add(vec3(0.0, 0.0, normalLookUpShift.negate())).toVar(); - - // elevations - - const position = positionLocal.xyz.toVar(); - const elevation = terrainElevation(positionLocal.xz); - position.y.addAssign(elevation); - - neighbourA.y.addAssign(terrainElevation(neighbourA.xz)); - neighbourB.y.addAssign(terrainElevation(neighbourB.xz)); - - // compute normal - - const toA = neighbourA.sub(position).normalize(); - const toB = neighbourB.sub(position).normalize(); - vNormal.assign(cross(toA, toB)); - - // varyings - - vPosition.assign(position.add(vec3(offset.x, 0, offset.y))); - - return position; - })(); - - material.normalNode = transformNormalToView(vNormal); - - material.colorNode = Fn(() => { - const finalColor = colorSand.toVar(); - - // grass - - const grassMix = step(-0.06, vPosition.y); - finalColor.assign(grassMix.mix(finalColor, colorGrass)); - - // rock - - const rockMix = step(0.5, dot(vNormal, vec3(0, 1, 0))) - .oneMinus() - .mul(step(-0.06, vPosition.y)); - finalColor.assign(rockMix.mix(finalColor, colorRock)); - - // snow - - const snowThreshold = mx_noise_float(vPosition.xz.mul(25), 1, 0).mul(0.1).add(0.45); - const snowMix = step(snowThreshold, vPosition.y); - finalColor.assign(snowMix.mix(finalColor, colorSnow)); - - return finalColor; - })(); - - const geometry = new THREE.PlaneGeometry(10, 10, 500, 500); - geometry.deleteAttribute('uv'); - geometry.deleteAttribute('normal'); - geometry.rotateX(-Math.PI * 0.5); - - const terrain = new THREE.Mesh(geometry, material); - terrain.receiveShadow = true; - terrain.castShadow = true; - scene.add(terrain); - - // water - - const water = new THREE.Mesh( - new THREE.PlaneGeometry(10, 10, 1, 1), - new THREE.MeshPhysicalMaterial({ - transmission: 1, - roughness: 0.5, - ior: 1.333, - color: '#4db2ff', - }), - ); - water.rotation.x = -Math.PI * 0.5; - water.position.y = -0.1; - scene.add(water); - - // drag - - drag = {}; - drag.screenCoords = new THREE.Vector2(); - drag.prevWorldCoords = new THREE.Vector3(); - drag.worldCoords = new THREE.Vector3(); - drag.raycaster = new THREE.Raycaster(); - drag.down = false; - drag.hover = false; - - drag.object = new THREE.Mesh(new THREE.PlaneGeometry(10, 10, 1, 1), new THREE.MeshBasicMaterial()); - drag.object.rotation.x = -Math.PI * 0.5; - drag.object.visible = false; - scene.add(drag.object); - - drag.getIntersect = () => { - drag.raycaster.setFromCamera(drag.screenCoords, camera); - const intersects = drag.raycaster.intersectObject(drag.object); - if (intersects.length) return intersects[0]; - - return null; - }; - - drag.update = () => { - const intersect = drag.getIntersect(); - - if (intersect) { - drag.hover = true; - - if (!drag.down) renderer.domElement.style.cursor = 'grab'; - } else { - drag.hover = false; - renderer.domElement.style.cursor = 'default'; - } - - if (drag.hover && drag.down) { - drag.worldCoords.copy(intersect.point); - const delta = drag.prevWorldCoords.sub(drag.worldCoords); - - offset.value.x += delta.x; - offset.value.y += delta.z; - } - - drag.prevWorldCoords.copy(drag.worldCoords); - }; - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.shadowMap.enabled = true; - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // inspector - - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.inspector.domElement); - - // controls - - controls = new OrbitControls(camera, renderer.domElement); - controls.maxPolarAngle = Math.PI * 0.45; - controls.target.y = -0.5; - controls.enableDamping = true; - controls.minDistance = 0.1; - controls.maxDistance = 50; - - // debug - - const gui = renderer.inspector.createParameters('Parameters'); - - const terrainGui = gui.addFolder('🏔️ terrain'); - - terrainGui.add(noiseIterations, 'value', 0, 10, 1).name('noiseIterations'); - terrainGui.add(positionFrequency, 'value', 0, 1, 0.001).name('positionFrequency'); - terrainGui.add(strength, 'value', 0, 20, 0.001).name('strength'); - terrainGui.add(warpFrequency, 'value', 0, 20, 0.001).name('warpFrequency'); - terrainGui.add(warpStrength, 'value', 0, 2, 0.001).name('warpStrength'); - - terrainGui.addColor({ color: colorSand.value }, 'color').name('colorSand'); - terrainGui.addColor({ color: colorGrass.value }, 'color').name('colorGrass'); - terrainGui.addColor({ color: colorSnow.value }, 'color').name('colorSnow'); - terrainGui.addColor({ color: colorRock.value }, 'color').name('colorRock'); - - const waterGui = gui.addFolder('💧 water'); - - waterGui.add(water.material, 'roughness', 0, 1, 0.01); - waterGui.add(water.material, 'ior', 1, 2, 0.001); - waterGui.addColor({ color: water.material.color }, 'color').name('color'); - - // events - - window.addEventListener('pointermove', event => { - drag.screenCoords.x = (event.clientX / window.innerWidth - 0.5) * 2; - drag.screenCoords.y = -(event.clientY / window.innerHeight - 0.5) * 2; - }); - - renderer.domElement.addEventListener('pointerdown', () => { - if (drag.hover) { - renderer.domElement.style.cursor = 'grabbing'; - controls.enabled = false; - drag.down = true; - drag.object.scale.setScalar(10); - - const intersect = drag.getIntersect(); - drag.prevWorldCoords.copy(intersect.point); - drag.worldCoords.copy(intersect.point); - } - }); - - window.addEventListener('pointerup', () => { - drag.down = false; - controls.enabled = true; - drag.object.scale.setScalar(1); - }); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -async function animate() { - controls.update(); - - drag.update(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_tsl_raging_sea.ts b/examples-testing/examples/webgpu_tsl_raging_sea.ts deleted file mode 100644 index cb513d075..000000000 --- a/examples-testing/examples/webgpu_tsl_raging_sea.ts +++ /dev/null @@ -1,178 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { - float, - mx_noise_float, - Loop, - color, - positionLocal, - sin, - vec2, - vec3, - mul, - time, - uniform, - Fn, - transformNormalToView, -} from 'three/tsl'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer, controls; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 10); - camera.position.set(1.25, 1.25, 1.25); - - scene = new THREE.Scene(); - - // lights - - const directionalLight = new THREE.DirectionalLight('#ffffff', 3); - directionalLight.position.set(-4, 2, 0); - scene.add(directionalLight); - - // material - - const material = new THREE.MeshStandardNodeMaterial({ color: '#271442', roughness: 0.15 }); - - const emissiveColor = uniform(color('#ff0a81')); - const emissiveLow = uniform(-0.25); - const emissiveHigh = uniform(0.2); - const emissivePower = uniform(7); - const largeWavesFrequency = uniform(vec2(3, 1)); - const largeWavesSpeed = uniform(1.25); - const largeWavesMultiplier = uniform(0.15); - const smallWavesIterations = uniform(3); - const smallWavesFrequency = uniform(2); - const smallWavesSpeed = uniform(0.3); - const smallWavesMultiplier = uniform(0.18); - const normalComputeShift = uniform(0.01); - - // TSL functions - - const wavesElevation = Fn(([position]) => { - // large waves - - const elevation = mul( - sin(position.x.mul(largeWavesFrequency.x).add(time.mul(largeWavesSpeed))), - sin(position.z.mul(largeWavesFrequency.y).add(time.mul(largeWavesSpeed))), - largeWavesMultiplier, - ).toVar(); - - Loop({ start: float(1), end: smallWavesIterations.add(1) }, ({ i }) => { - const noiseInput = vec3( - position.xz - .add(2) // avoids a-hole pattern - .mul(smallWavesFrequency) - .mul(i), - time.mul(smallWavesSpeed), - ); - - const wave = mx_noise_float(noiseInput, 1, 0).mul(smallWavesMultiplier).div(i).abs(); - - elevation.subAssign(wave); - }); - - return elevation; - }); - - // position - - const elevation = wavesElevation(positionLocal); - const position = positionLocal.add(vec3(0, elevation, 0)); - - material.positionNode = position; - - // normals - - let positionA = positionLocal.add(vec3(normalComputeShift, 0, 0)); - let positionB = positionLocal.add(vec3(0, 0, normalComputeShift.negate())); - - positionA = positionA.add(vec3(0, wavesElevation(positionA), 0)); - positionB = positionB.add(vec3(0, wavesElevation(positionB), 0)); - - const toA = positionA.sub(position).normalize(); - const toB = positionB.sub(position).normalize(); - const normal = toA.cross(toB); - - material.normalNode = transformNormalToView(normal); - - // emissive - - const emissive = elevation.remap(emissiveHigh, emissiveLow).pow(emissivePower); - material.emissiveNode = emissiveColor.mul(emissive); - - // mesh - - const geometry = new THREE.PlaneGeometry(2, 2, 256, 256); - geometry.rotateX(-Math.PI * 0.5); - const mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // controls - - controls = new OrbitControls(camera, renderer.domElement); - controls.target.y = -0.25; - controls.enableDamping = true; - controls.minDistance = 0.1; - controls.maxDistance = 50; - - // debug - - const gui = renderer.inspector.createParameters('Parameters'); - - gui.addColor({ color: material.color.getHex(THREE.SRGBColorSpace) }, 'color') - .name('color') - .onChange(value => material.color.set(value)); - gui.add(material, 'roughness', 0, 1, 0.001); - - const colorGui = gui.addFolder('emissive'); - colorGui - .addColor({ color: emissiveColor.value.getHex(THREE.SRGBColorSpace) }, 'color') - .name('color') - .onChange(value => emissiveColor.value.set(value)); - colorGui.add(emissiveLow, 'value', -1, 0, 0.001).name('low'); - colorGui.add(emissiveHigh, 'value', 0, 1, 0.001).name('high'); - colorGui.add(emissivePower, 'value', 1, 10, 1).name('power'); - - const wavesGui = gui.addFolder('waves'); - wavesGui.add(largeWavesSpeed, 'value', 0, 5).name('largeSpeed'); - wavesGui.add(largeWavesMultiplier, 'value', 0, 1).name('largeMultiplier'); - wavesGui.add(largeWavesFrequency.value, 'x', 0, 10).name('largeFrequencyX'); - wavesGui.add(largeWavesFrequency.value, 'y', 0, 10).name('largeFrequencyY'); - wavesGui.add(smallWavesIterations, 'value', 0, 5, 1).name('smallIterations'); - wavesGui.add(smallWavesFrequency, 'value', 0, 10).name('smallFrequency'); - wavesGui.add(smallWavesSpeed, 'value', 0, 1).name('smallSpeed'); - wavesGui.add(smallWavesMultiplier, 'value', 0, 1).name('smallMultiplier'); - wavesGui.add(normalComputeShift, 'value', 0, 0.1, 0.0001).name('normalComputeShift'); - - // events - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -async function animate() { - controls.update(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_tsl_vfx_flames.ts b/examples-testing/examples/webgpu_tsl_vfx_flames.ts deleted file mode 100644 index f1e423444..000000000 --- a/examples-testing/examples/webgpu_tsl_vfx_flames.ts +++ /dev/null @@ -1,203 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { - TWO_PI, - oneMinus, - spherizeUV, - sin, - step, - texture, - time, - Fn, - uv, - vec2, - vec3, - vec4, - mix, - billboarding, -} from 'three/tsl'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer, controls; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(25, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(1, 1, 3); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x201919); - - // textures - - const textureLoader = new THREE.TextureLoader(); - - const cellularTexture = textureLoader.load('./textures/noises/voronoi/grayscale-256x256.png'); - const perlinTexture = textureLoader.load('./textures/noises/perlin/rgb-256x256.png'); - - // gradient canvas - - const gradient = {}; - gradient.element = document.createElement('canvas'); - gradient.element.width = 128; - gradient.element.height = 1; - gradient.context = gradient.element.getContext('2d'); - - gradient.colors = ['#090033', '#5f1f93', '#e02e96', '#ffbd80', '#fff0db']; - - gradient.texture = new THREE.CanvasTexture(gradient.element); - gradient.texture.colorSpace = THREE.SRGBColorSpace; - - gradient.update = () => { - const fillGradient = gradient.context.createLinearGradient(0, 0, gradient.element.width, 0); - - for (let i = 0; i < gradient.colors.length; i++) { - const progress = i / (gradient.colors.length - 1); - const color = gradient.colors[i]; - fillGradient.addColorStop(progress, color); - } - - gradient.context.fillStyle = fillGradient; - gradient.context.fillRect(0, 0, gradient.element.width, gradient.element.height); - - gradient.texture.needsUpdate = true; - }; - - gradient.update(); - - // flame 1 material - - const flame1Material = new THREE.SpriteNodeMaterial({ side: THREE.DoubleSide }); - - flame1Material.colorNode = Fn(() => { - // main UV - const mainUv = uv().toVar(); - mainUv.assign(spherizeUV(mainUv, 10).mul(0.6).add(0.2)); // spherize - mainUv.assign(mainUv.pow(vec2(1, 2))); // stretch - mainUv.assign(mainUv.mul(2, 1).sub(vec2(0.5, 0))); // scale - - // gradients - const gradient1 = sin(time.mul(10).sub(mainUv.y.mul(TWO_PI).mul(2))).toVar(); - const gradient2 = mainUv.y.smoothstep(0, 1).toVar(); - mainUv.x.addAssign(gradient1.mul(gradient2).mul(0.2)); - - // cellular noise - const cellularUv = mainUv - .mul(0.5) - .add(vec2(0, time.negate().mul(0.5))) - .mod(1); - const cellularNoise = texture(cellularTexture, cellularUv, 0).r.oneMinus().smoothstep(0, 0.5).oneMinus(); - cellularNoise.mulAssign(gradient2); - - // shape - const shape = mainUv.sub(0.5).mul(vec2(3, 2)).length().oneMinus().toVar(); - shape.assign(shape.sub(cellularNoise)); - - // gradient color - const gradientColor = texture(gradient.texture, vec2(shape.remap(0, 1, 0, 1), 0)); - - // output - const color = mix(gradientColor, vec3(1), shape.step(0.8)); - const alpha = shape.smoothstep(0, 0.3); - return vec4(color.rgb, alpha); - })(); - - // flame 2 material - - const flame2Material = new THREE.SpriteNodeMaterial({ side: THREE.DoubleSide }); - - flame2Material.colorNode = Fn(() => { - // main UV - const mainUv = uv().toVar(); - mainUv.assign(spherizeUV(mainUv, 10).mul(0.6).add(0.2)); // spherize - mainUv.assign(mainUv.abs().pow(vec2(1, 3)).mul(mainUv.sign())); // stretch - mainUv.assign(mainUv.mul(2, 1).sub(vec2(0.5, 0))); // scale - - // perlin noise - const perlinUv = mainUv.add(vec2(0, time.negate().mul(1))).mod(1); - const perlinNoise = texture(perlinTexture, perlinUv, 0).sub(0.5).mul(1); - mainUv.x.addAssign(perlinNoise.x.mul(0.5)); - - // gradients - const gradient1 = sin(time.mul(10).sub(mainUv.y.mul(TWO_PI).mul(2))); - const gradient2 = mainUv.y.smoothstep(0, 1); - const gradient3 = oneMinus(mainUv.y).smoothstep(0, 0.3); - mainUv.x.addAssign(gradient1.mul(gradient2).mul(0.2)); - - // displaced perlin noise - const displacementPerlinUv = mainUv - .mul(0.5) - .add(vec2(0, time.negate().mul(0.25))) - .mod(1); - const displacementPerlinNoise = texture(perlinTexture, displacementPerlinUv, 0).sub(0.5).mul(1); - const displacedPerlinUv = mainUv - .add(vec2(0, time.negate().mul(0.5))) - .add(displacementPerlinNoise) - .mod(1); - const displacedPerlinNoise = texture(perlinTexture, displacedPerlinUv, 0).sub(0.5).mul(1); - mainUv.x.addAssign(displacedPerlinNoise.mul(0.5)); - - // cellular noise - const cellularUv = mainUv.add(vec2(0, time.negate().mul(1.5))).mod(1); - const cellularNoise = texture(cellularTexture, cellularUv, 0).r.oneMinus().smoothstep(0.25, 1); - - // shape - const shape = step(mainUv.sub(0.5).mul(vec2(6, 1)).length(), 0.5); - shape.assign(shape.mul(cellularNoise)); - shape.mulAssign(gradient3); - shape.assign(step(0.01, shape)); - - // output - return vec4(vec3(1), shape); - })(); - - // billboarding - follow the camera rotation only horizontally - - flame1Material.vertexNode = billboarding(); - flame2Material.vertexNode = billboarding(); - - // meshes - - const flame1 = new THREE.Sprite(flame1Material); - flame1.center.set(0.5, 0); - flame1.scale.x = 0.5; // optional - flame1.position.x = -0.5; - scene.add(flame1); - - const flame2 = new THREE.Sprite(flame2Material); - flame2.center.set(0.5, 0); - flame2.position.x = 0.5; - scene.add(flame2); - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - controls.minDistance = 0.1; - controls.maxDistance = 50; - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -async function animate() { - controls.update(); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_tsl_vfx_linkedparticles.ts b/examples-testing/examples/webgpu_tsl_vfx_linkedparticles.ts deleted file mode 100644 index e220c164b..000000000 --- a/examples-testing/examples/webgpu_tsl_vfx_linkedparticles.ts +++ /dev/null @@ -1,445 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { - atan, - cos, - float, - max, - min, - mix, - PI, - TWO_PI, - sin, - vec2, - vec3, - color, - Fn, - hash, - hue, - If, - instanceIndex, - Loop, - mx_fractal_noise_float, - mx_fractal_noise_vec3, - pass, - pcurve, - storage, - deltaTime, - time, - uv, - uniform, - step, -} from 'three/tsl'; -import { bloom } from 'three/addons/tsl/display/BloomNode.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import WebGPU from 'three/addons/capabilities/WebGPU.js'; - -let camera, scene, renderer, renderPipeline, controls, timer, light; - -let updateParticles, spawnParticles; // TSL compute nodes -let getInstanceColor; // TSL function - -const screenPointer = new THREE.Vector2(); -const scenePointer = new THREE.Vector3(); -const raycastPlane = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0); -const raycaster = new THREE.Raycaster(); - -const nbParticles = Math.pow(2, 13); - -const timeScale = uniform(1.0); -const particleLifetime = uniform(0.5); -const particleSize = uniform(1.0); -const linksWidth = uniform(0.005); - -const colorOffset = uniform(0.0); -const colorVariance = uniform(2.0); -const colorRotationSpeed = uniform(1.0); - -const spawnIndex = uniform(0); -const nbToSpawn = uniform(5); -const spawnPosition = uniform(vec3(0.0)); -const previousSpawnPosition = uniform(vec3(0.0)); - -const turbFrequency = uniform(0.5); -const turbAmplitude = uniform(0.5); -const turbOctaves = uniform(2); -const turbLacunarity = uniform(2.0); -const turbGain = uniform(0.5); -const turbFriction = uniform(0.01); - -init(); - -async function init() { - if (WebGPU.isAvailable() === false) { - document.body.appendChild(WebGPU.getErrorMessage()); - - throw new Error('No WebGPU support'); - } - - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 200); - camera.position.set(0, 0, 10); - - scene = new THREE.Scene(); - - timer = new THREE.Timer(); - timer.connect(document); - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setClearColor(0x14171a); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - document.body.appendChild(renderer.domElement); - - await renderer.init(); - - // TSL function - // current color from index - getInstanceColor = Fn(([i]) => { - return hue( - color(0x0000ff), - colorOffset.add(mx_fractal_noise_float(i.toFloat().mul(0.1), 2, 2.0, 0.5, colorVariance)), - ); - }); - - // Particles - // storage buffers - const particlePositions = storage(new THREE.StorageInstancedBufferAttribute(nbParticles, 4), 'vec4', nbParticles); - const particleVelocities = storage(new THREE.StorageInstancedBufferAttribute(nbParticles, 4), 'vec4', nbParticles); - - // init particles buffers - renderer.compute( - Fn(() => { - particlePositions.element(instanceIndex).xyz.assign(vec3(10000.0)); - particlePositions.element(instanceIndex).w.assign(vec3(-1.0)); // life is stored in w component; x<0 means dead - })().compute(nbParticles), - ); - - // particles output - const particleQuadSize = 0.05; - const particleGeom = new THREE.PlaneGeometry(particleQuadSize, particleQuadSize); - - const particleMaterial = new THREE.SpriteNodeMaterial(); - particleMaterial.blending = THREE.AdditiveBlending; - particleMaterial.depthWrite = false; - particleMaterial.positionNode = particlePositions.toAttribute(); - particleMaterial.scaleNode = vec2(particleSize); - particleMaterial.rotationNode = atan(particleVelocities.toAttribute().y, particleVelocities.toAttribute().x); - - particleMaterial.colorNode = Fn(() => { - const life = particlePositions.toAttribute().w; - const modLife = pcurve(life.oneMinus(), 8.0, 1.0); - const pulse = pcurve( - sin(hash(instanceIndex).mul(TWO_PI).add(time.mul(0.5).mul(TWO_PI))) - .mul(0.5) - .add(0.5), - 0.25, - 0.25, - ) - .mul(10.0) - .add(1.0); - - return getInstanceColor(instanceIndex).mul(pulse.mul(modLife)); - })(); - - particleMaterial.opacityNode = Fn(() => { - const circle = step(uv().xy.sub(0.5).length(), 0.5); - const life = particlePositions.toAttribute().w; - - return circle.mul(life); - })(); - - const particleMesh = new THREE.InstancedMesh(particleGeom, particleMaterial, nbParticles); - particleMesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage); - particleMesh.frustumCulled = false; - - scene.add(particleMesh); - - // Links between particles - // first, we define the indices for the links, 2 quads per particle, the indexation is fixed - const linksIndices = []; - for (let i = 0; i < nbParticles; i++) { - const baseIndex = i * 8; - for (let j = 0; j < 2; j++) { - const offset = baseIndex + j * 4; - linksIndices.push(offset, offset + 1, offset + 2, offset, offset + 2, offset + 3); - } - } - - // storage buffers attributes for the links - const nbVertices = nbParticles * 8; - const linksVerticesSBA = new THREE.StorageBufferAttribute(nbVertices, 4); - const linksColorsSBA = new THREE.StorageBufferAttribute(nbVertices, 4); - - // links output - const linksGeom = new THREE.BufferGeometry(); - linksGeom.setAttribute('position', linksVerticesSBA); - linksGeom.setAttribute('color', linksColorsSBA); - linksGeom.setIndex(linksIndices); - - const linksMaterial = new THREE.MeshBasicNodeMaterial(); - linksMaterial.vertexColors = true; - linksMaterial.side = THREE.DoubleSide; - linksMaterial.transparent = true; - linksMaterial.depthWrite = false; - linksMaterial.depthTest = false; - linksMaterial.blending = THREE.AdditiveBlending; - linksMaterial.opacityNode = storage(linksColorsSBA, 'vec4', linksColorsSBA.count).toAttribute().w; - - const linksMesh = new THREE.Mesh(linksGeom, linksMaterial); - linksMesh.frustumCulled = false; - scene.add(linksMesh); - - // compute nodes - updateParticles = Fn(() => { - const position = particlePositions.element(instanceIndex).xyz; - const life = particlePositions.element(instanceIndex).w; - const velocity = particleVelocities.element(instanceIndex).xyz; - const dt = deltaTime.mul(0.1).mul(timeScale); - - If(life.greaterThan(0.0), () => { - // first we update the particles positions and velocities - // velocity comes from a turbulence field, and is multiplied by the particle lifetime so that it slows down over time - const localVel = mx_fractal_noise_vec3( - position.mul(turbFrequency), - turbOctaves, - turbLacunarity, - turbGain, - turbAmplitude, - ).mul(life.add(0.01)); - velocity.addAssign(localVel); - velocity.mulAssign(turbFriction.oneMinus()); - position.addAssign(velocity.mul(dt)); - - // then we decrease the lifetime - life.subAssign(dt.mul(particleLifetime.reciprocal())); - - // then we find the two closest particles and set a quad to each of them - const closestDist1 = float(10000.0).toVar(); - const closestPos1 = vec3(0.0).toVar(); - const closestLife1 = float(0.0).toVar(); - const closestDist2 = float(10000.0).toVar(); - const closestPos2 = vec3(0.0).toVar(); - const closestLife2 = float(0.0).toVar(); - - Loop(nbParticles, ({ i }) => { - const otherPart = particlePositions.element(i); - - If(i.notEqual(instanceIndex).and(otherPart.w.greaterThan(0.0)), () => { - // if not self and other particle is alive - - const otherPosition = otherPart.xyz; - const dist = position.sub(otherPosition).lengthSq(); - const moreThanZero = dist.greaterThan(0.0); - - If(dist.lessThan(closestDist1).and(moreThanZero), () => { - closestDist1.assign(dist); - closestPos1.assign(otherPosition.xyz); - closestLife1.assign(otherPart.w); - }).ElseIf(dist.lessThan(closestDist2).and(moreThanZero), () => { - closestDist2.assign(dist); - closestPos2.assign(otherPosition.xyz); - closestLife2.assign(otherPart.w); - }); - }); - }); - - // then we update the links correspondingly - const linksPositions = storage(linksVerticesSBA, 'vec4', linksVerticesSBA.count); - const linksColors = storage(linksColorsSBA, 'vec4', linksColorsSBA.count); - const firstLinkIndex = instanceIndex.mul(8); - const secondLinkIndex = firstLinkIndex.add(4); - - // positions link 1 - linksPositions.element(firstLinkIndex).xyz.assign(position); - linksPositions.element(firstLinkIndex).y.addAssign(linksWidth); - linksPositions.element(firstLinkIndex.add(1)).xyz.assign(position); - linksPositions.element(firstLinkIndex.add(1)).y.addAssign(linksWidth.negate()); - linksPositions.element(firstLinkIndex.add(2)).xyz.assign(closestPos1); - linksPositions.element(firstLinkIndex.add(2)).y.addAssign(linksWidth.negate()); - linksPositions.element(firstLinkIndex.add(3)).xyz.assign(closestPos1); - linksPositions.element(firstLinkIndex.add(3)).y.addAssign(linksWidth); - - // positions link 2 - linksPositions.element(secondLinkIndex).xyz.assign(position); - linksPositions.element(secondLinkIndex).y.addAssign(linksWidth); - linksPositions.element(secondLinkIndex.add(1)).xyz.assign(position); - linksPositions.element(secondLinkIndex.add(1)).y.addAssign(linksWidth.negate()); - linksPositions.element(secondLinkIndex.add(2)).xyz.assign(closestPos2); - linksPositions.element(secondLinkIndex.add(2)).y.addAssign(linksWidth.negate()); - linksPositions.element(secondLinkIndex.add(3)).xyz.assign(closestPos2); - linksPositions.element(secondLinkIndex.add(3)).y.addAssign(linksWidth); - - // colors are the same for all vertices of both quads - const linkColor = getInstanceColor(instanceIndex); - - // store the minimum lifetime of the closest particles in the w component of colors - const l1 = max(0.0, min(closestLife1, life)).pow(0.8); // pow is here to apply a slight curve to the opacity - const l2 = max(0.0, min(closestLife2, life)).pow(0.8); - - Loop(4, ({ i }) => { - linksColors.element(firstLinkIndex.add(i)).xyz.assign(linkColor); - linksColors.element(firstLinkIndex.add(i)).w.assign(l1); - linksColors.element(secondLinkIndex.add(i)).xyz.assign(linkColor); - linksColors.element(secondLinkIndex.add(i)).w.assign(l2); - }); - }); - })() - .compute(nbParticles) - .setName('Update Particles'); - - spawnParticles = Fn(() => { - const particleIndex = spawnIndex.add(instanceIndex).mod(nbParticles).toInt(); - const position = particlePositions.element(particleIndex).xyz; - const life = particlePositions.element(particleIndex).w; - const velocity = particleVelocities.element(particleIndex).xyz; - - life.assign(1.0); // sets it alive - - // random spherical direction - const rRange = float(0.01); - const rTheta = hash(particleIndex).mul(TWO_PI); - const rPhi = hash(particleIndex.add(1)).mul(PI); - const rx = sin(rTheta).mul(cos(rPhi)); - const ry = sin(rTheta).mul(sin(rPhi)); - const rz = cos(rTheta); - const rDir = vec3(rx, ry, rz); - - // position is interpolated between the previous cursor position and the current one over the number of particles spawned - const pos = mix( - previousSpawnPosition, - spawnPosition, - instanceIndex.toFloat().div(nbToSpawn.sub(1).toFloat()).clamp(), - ); - position.assign(pos.add(rDir.mul(rRange))); - - // start in that direction - velocity.assign(rDir.mul(5.0)); - })() - .compute(nbToSpawn.value) - .setName('Spawn Particles'); - - // background , an inverted icosahedron - const backgroundGeom = new THREE.IcosahedronGeometry(100, 5).applyMatrix4(new THREE.Matrix4().makeScale(-1, 1, 1)); - const backgroundMaterial = new THREE.MeshStandardNodeMaterial(); - backgroundMaterial.roughness = 0.4; - backgroundMaterial.metalness = 0.9; - backgroundMaterial.flatShading = true; - backgroundMaterial.colorNode = color(0x0); - - const backgroundMesh = new THREE.Mesh(backgroundGeom, backgroundMaterial); - scene.add(backgroundMesh); - - // light for the background - light = new THREE.PointLight(0xffffff, 3000); - scene.add(light); - - // post processing - - renderPipeline = new THREE.RenderPipeline(renderer); - - const scenePass = pass(scene, camera); - const scenePassColor = scenePass.getTextureNode('output'); - - const bloomPass = bloom(scenePassColor, 0.75, 0.1, 0.5); - - renderPipeline.outputNode = scenePassColor.add(bloomPass); - - // controls - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - controls.autoRotate = true; - controls.maxDistance = 75; - window.addEventListener('resize', onWindowResize); - - // pointer handling - - window.addEventListener('pointermove', onPointerMove); - - // GUI - - const gui = renderer.inspector.createParameters('Parameters'); - - gui.add(controls, 'autoRotate').name('Auto Rotate'); - gui.add(controls, 'autoRotateSpeed', -10.0, 10.0, 0.01).name('Auto Rotate Speed'); - - const partFolder = gui.addFolder('Particles'); - partFolder.add(timeScale, 'value', 0.0, 4.0, 0.01).name('timeScale'); - partFolder.add(nbToSpawn, 'value', 1, 100, 1).name('Spawn rate'); - partFolder.add(particleSize, 'value', 0.01, 3.0, 0.01).name('Size'); - partFolder.add(particleLifetime, 'value', 0.01, 2.0, 0.01).name('Lifetime'); - partFolder.add(linksWidth, 'value', 0.001, 0.1, 0.001).name('Links width'); - partFolder.add(colorVariance, 'value', 0.0, 10.0, 0.01).name('Color variance'); - partFolder.add(colorRotationSpeed, 'value', 0.0, 5.0, 0.01).name('Color rotation speed'); - - const turbFolder = gui.addFolder('Turbulence'); - turbFolder.add(turbFriction, 'value', 0.0, 0.3, 0.01).name('Friction'); - turbFolder.add(turbFrequency, 'value', 0.0, 1.0, 0.01).name('Frequency'); - turbFolder.add(turbAmplitude, 'value', 0.0, 10.0, 0.01).name('Amplitude'); - turbFolder.add(turbOctaves, 'value', 1, 9, 1).name('Octaves'); - turbFolder.add(turbLacunarity, 'value', 1.0, 5.0, 0.01).name('Lacunarity'); - turbFolder.add(turbGain, 'value', 0.0, 1.0, 0.01).name('Gain'); - - const bloomFolder = gui.addFolder('bloom'); - bloomFolder.add(bloomPass.threshold, 'value', 0, 2.0, 0.01).name('Threshold'); - bloomFolder.add(bloomPass.strength, 'value', 0, 10, 0.01).name('Strength'); - bloomFolder.add(bloomPass.radius, 'value', 0, 1, 0.01).name('Radius'); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function onPointerMove(e) { - screenPointer.x = (e.clientX / window.innerWidth) * 2 - 1; - screenPointer.y = -(e.clientY / window.innerHeight) * 2 + 1; -} - -function updatePointer() { - raycaster.setFromCamera(screenPointer, camera); - raycaster.ray.intersectPlane(raycastPlane, scenePointer); -} - -function animate() { - timer.update(); - - // compute particles - renderer.compute(updateParticles); - renderer.compute(spawnParticles); - - // update particle index for next spawn - spawnIndex.value = (spawnIndex.value + nbToSpawn.value) % nbParticles; - - // update raycast plane to face camera - raycastPlane.normal.applyEuler(camera.rotation); - updatePointer(); - - // lerping spawn position - previousSpawnPosition.value.copy(spawnPosition.value); - spawnPosition.value.lerp(scenePointer, 0.1); - - // rotating colors - colorOffset.value += timer.getDelta() * colorRotationSpeed.value * timeScale.value; - - const elapsedTime = timer.getElapsed(); - light.position.set( - Math.sin(elapsedTime * 0.5) * 30, - Math.cos(elapsedTime * 0.3) * 30, - Math.sin(elapsedTime * 0.2) * 30, - ); - - controls.update(); - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_tsl_vfx_tornado.ts b/examples-testing/examples/webgpu_tsl_vfx_tornado.ts deleted file mode 100644 index 625a9d209..000000000 --- a/examples-testing/examples/webgpu_tsl_vfx_tornado.ts +++ /dev/null @@ -1,289 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { - luminance, - cos, - min, - time, - atan, - uniform, - pass, - PI, - TWO_PI, - color, - positionLocal, - sin, - texture, - Fn, - uv, - vec2, - vec3, - vec4, -} from 'three/tsl'; -import { bloom } from 'three/addons/tsl/display/BloomNode.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let camera, scene, renderer, renderPipeline, controls; - -init(); - -function init() { - camera = new THREE.PerspectiveCamera(25, window.innerWidth / window.innerHeight, 0.1, 50); - camera.position.set(1, 1, 3); - - scene = new THREE.Scene(); - - // textures - - const textureLoader = new THREE.TextureLoader(); - const perlinTexture = textureLoader.load('./textures/noises/perlin/rgb-256x256.png'); - perlinTexture.wrapS = THREE.RepeatWrapping; - perlinTexture.wrapT = THREE.RepeatWrapping; - - // TSL functions - - const toRadialUv = Fn(([uv, multiplier, rotation, offset]) => { - const centeredUv = uv.sub(0.5).toVar(); - const distanceToCenter = centeredUv.length(); - const angle = atan(centeredUv.y, centeredUv.x); - const radialUv = vec2(angle.add(PI).div(TWO_PI), distanceToCenter).toVar(); - radialUv.mulAssign(multiplier); - radialUv.x.addAssign(rotation); - radialUv.y.addAssign(offset); - - return radialUv; - }); - - const toSkewedUv = Fn(([uv, skew]) => { - return vec2(uv.x.add(uv.y.mul(skew.x)), uv.y.add(uv.x.mul(skew.y))); - }); - - const twistedCylinder = Fn(([position, parabolStrength, parabolOffset, parabolAmplitude, time]) => { - const angle = atan(position.z, position.x).toVar(); - const elevation = position.y; - - // parabol - const radius = parabolStrength.mul(position.y.sub(parabolOffset)).pow(2).add(parabolAmplitude).toVar(); - - // turbulences - radius.addAssign(sin(elevation.sub(time).mul(20).add(angle.mul(2))).mul(0.05)); - - const twistedPosition = vec3(cos(angle).mul(radius), elevation, sin(angle).mul(radius)); - - return twistedPosition; - }); - - // uniforms - - const emissiveColor = uniform(color('#ff8b4d')); - const timeScale = uniform(0.2); - const parabolStrength = uniform(1); - const parabolOffset = uniform(0.3); - const parabolAmplitude = uniform(0.2); - - // tornado floor - - const floorMaterial = new THREE.MeshBasicNodeMaterial({ transparent: true, wireframe: false }); - - floorMaterial.outputNode = Fn(() => { - const scaledTime = time.mul(timeScale); - - // noise 1 - const noise1Uv = toRadialUv(uv(), vec2(0.5, 0.5), scaledTime, scaledTime); - noise1Uv.assign(toSkewedUv(noise1Uv, vec2(-1, 0))); - noise1Uv.mulAssign(vec2(4, 1)); - const noise1 = texture(perlinTexture, noise1Uv, 1).r.remap(0.45, 0.7); - - // noise 2 - const noise2Uv = toRadialUv(uv(), vec2(2, 8), scaledTime.mul(2), scaledTime.mul(8)); - noise2Uv.assign(toSkewedUv(noise2Uv, vec2(-0.25, 0))); - noise2Uv.mulAssign(vec2(2, 0.25)); - const noise2 = texture(perlinTexture, noise2Uv, 1).b.remap(0.45, 0.7); - - // outer fade - const distanceToCenter = uv().sub(0.5).toVar(); - const outerFade = min( - distanceToCenter.length().oneMinus().smoothstep(0.5, 0.9), - distanceToCenter.length().smoothstep(0, 0.2), - ); - - // effect - const effect = noise1.mul(noise2).mul(outerFade).toVar(); - - // output - return vec4( - emissiveColor.mul(effect.step(0.2)).mul(3), // Emissive - effect.smoothstep(0, 0.01), // Alpha - ); - })(); - - const floor = new THREE.Mesh(new THREE.PlaneGeometry(2, 2), floorMaterial); - floor.rotation.x = -Math.PI * 0.5; - scene.add(floor); - - // tornado cylinder geometry - - const cylinderGeometry = new THREE.CylinderGeometry(1, 1, 1, 20, 20, true); - cylinderGeometry.translate(0, 0.5, 0); - - // tornado emissive cylinder - - const emissiveMaterial = new THREE.MeshBasicNodeMaterial({ - transparent: true, - side: THREE.DoubleSide, - wireframe: false, - }); - - emissiveMaterial.positionNode = twistedCylinder( - positionLocal, - parabolStrength, - parabolOffset, - parabolAmplitude.sub(0.05), - time.mul(timeScale), - ); - - emissiveMaterial.outputNode = Fn(() => { - const scaledTime = time.mul(timeScale); - - // noise 1 - const noise1Uv = uv().add(vec2(scaledTime, scaledTime.negate())).toVar(); - noise1Uv.assign(toSkewedUv(noise1Uv, vec2(-1, 0))); - noise1Uv.mulAssign(vec2(2, 0.25)); - const noise1 = texture(perlinTexture, noise1Uv, 1).r.remap(0.45, 0.7); - - // noise 2 - const noise2Uv = uv() - .add(vec2(scaledTime.mul(0.5), scaledTime.negate())) - .toVar(); - noise2Uv.assign(toSkewedUv(noise2Uv, vec2(-1, 0))); - noise2Uv.mulAssign(vec2(5, 1)); - const noise2 = texture(perlinTexture, noise2Uv, 1).g.remap(0.45, 0.7); - - // outer fade - const outerFade = min(uv().y.smoothstep(0, 0.1), uv().y.oneMinus().smoothstep(0, 0.4)); - - // effect - const effect = noise1.mul(noise2).mul(outerFade); - - const emissiveColorLuminance = luminance(emissiveColor); - - // output - return vec4( - emissiveColor.mul(1.2).div(emissiveColorLuminance), // emissive - effect.smoothstep(0, 0.1), // alpha - ); - })(); - - const emissive = new THREE.Mesh(cylinderGeometry, emissiveMaterial); - emissive.scale.set(1, 1, 1); - scene.add(emissive); - - // tornado dark cylinder - - const darkMaterial = new THREE.MeshBasicNodeMaterial({ - transparent: true, - side: THREE.DoubleSide, - wireframe: false, - }); - - darkMaterial.positionNode = twistedCylinder( - positionLocal, - parabolStrength, - parabolOffset, - parabolAmplitude, - time.mul(timeScale), - ); - - darkMaterial.outputNode = Fn(() => { - const scaledTime = time.mul(timeScale).add(123.4); - - // noise 1 - const noise1Uv = uv().add(vec2(scaledTime, scaledTime.negate())).toVar(); - noise1Uv.assign(toSkewedUv(noise1Uv, vec2(-1, 0))); - noise1Uv.mulAssign(vec2(2, 0.25)); - const noise1 = texture(perlinTexture, noise1Uv, 1).g.remap(0.45, 0.7); - - // noise 2 - const noise2Uv = uv() - .add(vec2(scaledTime.mul(0.5), scaledTime.negate())) - .toVar(); - noise2Uv.assign(toSkewedUv(noise2Uv, vec2(-1, 0))); - noise2Uv.mulAssign(vec2(5, 1)); - const noise2 = texture(perlinTexture, noise2Uv, 1).b.remap(0.45, 0.7); - - // outer fade - const outerFade = min(uv().y.smoothstep(0, 0.2), uv().y.oneMinus().smoothstep(0, 0.4)); - - // effect - const effect = noise1.mul(noise2).mul(outerFade); - - return vec4(vec3(0), effect.smoothstep(0, 0.01)); - })(); - - const dark = new THREE.Mesh(cylinderGeometry, darkMaterial); - dark.scale.set(1, 1, 1); - scene.add(dark); - - // renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setClearColor(0x201919); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // post processing - - renderPipeline = new THREE.RenderPipeline(renderer); - - const scenePass = pass(scene, camera); - const scenePassColor = scenePass.getTextureNode('output'); - - const bloomPass = bloom(scenePassColor, 1, 0.1, 1); - - renderPipeline.outputNode = scenePassColor.add(bloomPass); - - // controls - - controls = new OrbitControls(camera, renderer.domElement); - controls.target.y = 0.4; - controls.enableDamping = true; - controls.minDistance = 0.1; - controls.maxDistance = 50; - - window.addEventListener('resize', onWindowResize); - - // debug - - const gui = renderer.inspector.createParameters('Parameters'); - - gui.addColor({ color: emissiveColor.value.getHexString(THREE.SRGBColorSpace) }, 'color') - .onChange(value => emissiveColor.value.set(value)) - .name('emissiveColor'); - gui.add(timeScale, 'value', -1, 1, 0.01).name('timeScale'); - gui.add(parabolStrength, 'value', 0, 2, 0.01).name('parabolStrength'); - gui.add(parabolOffset, 'value', 0, 1, 0.01).name('parabolOffset'); - gui.add(parabolAmplitude, 'value', 0, 2, 0.01).name('parabolAmplitude'); - - const bloomGui = gui.addFolder('bloom'); - bloomGui.add(bloomPass.strength, 'value', 0, 10, 0.01).name('strength'); - bloomGui.add(bloomPass.radius, 'value', 0, 1, 0.01).name('radius'); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -async function animate() { - controls.update(); - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_tsl_wood.ts b/examples-testing/examples/webgpu_tsl_wood.ts deleted file mode 100644 index 5a4e06502..000000000 --- a/examples-testing/examples/webgpu_tsl_wood.ts +++ /dev/null @@ -1,249 +0,0 @@ -import * as THREE from 'three'; -import * as TSL from 'three/tsl'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import WebGPU from 'three/addons/capabilities/WebGPU.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; -import { FontLoader } from 'three/addons/loaders/FontLoader.js'; -import { TextGeometry } from 'three/addons/geometries/TextGeometry.js'; - -import { RoundedBoxGeometry } from 'three/addons/geometries/RoundedBoxGeometry.js'; -import { WoodNodeMaterial, WoodGenuses, Finishes } from 'three/addons/materials/WoodNodeMaterial.js'; - -let scene, base, camera, renderer, controls, font, blockGeometry, gui; - -// Helper function to get grid position -function getGridPosition(woodIndex, finishIndex) { - return { - x: 0, - y: (finishIndex - Finishes.length / 2) * 1.0, - z: (woodIndex - WoodGenuses.length / 2 + 0.45) * 1.0, - }; -} - -// Helper function to create the grid plane -function createGridPlane() { - const material = new THREE.MeshBasicNodeMaterial(); - - const gridXZ = TSL.Fn(([gridSize = TSL.float(1.0), dotWidth = TSL.float(0.1), lineWidth = TSL.float(0.02)]) => { - const coord = TSL.positionWorld.xz.div(gridSize); - const grid = TSL.fract(coord); - - // Screen-space derivative for automatic antialiasing - const fw = TSL.fwidth(coord); - const smoothing = TSL.max(fw.x, fw.y).mul(0.5); - - // Create squares at cell centers - const squareDist = TSL.max(TSL.abs(grid.x.sub(0.5)), TSL.abs(grid.y.sub(0.5))); - const dots = TSL.smoothstep(dotWidth.add(smoothing), dotWidth.sub(smoothing), squareDist); - - // Create grid lines - const lineX = TSL.smoothstep(lineWidth.add(smoothing), lineWidth.sub(smoothing), TSL.abs(grid.x.sub(0.5))); - const lineZ = TSL.smoothstep(lineWidth.add(smoothing), lineWidth.sub(smoothing), TSL.abs(grid.y.sub(0.5))); - const lines = TSL.max(lineX, lineZ); - - return TSL.max(dots, lines); - }); - - const radialGradient = TSL.Fn(([radius = TSL.float(10.0), falloff = TSL.float(1.0)]) => { - return TSL.smoothstep(radius, radius.sub(falloff), TSL.length(TSL.positionWorld)); - }); - - // Create grid pattern - const gridPattern = gridXZ(1.0, 0.03, 0.005); - const baseColor = TSL.vec4(1.0, 1.0, 1.0, 0.0); - const gridColor = TSL.vec4(0.5, 0.5, 0.5, 1.0); - - // Mix base color with grid lines - material.colorNode = gridPattern.mix(baseColor, gridColor).mul(radialGradient(30.0, 20.0)); - material.transparent = true; - - const plane = new THREE.Mesh(new THREE.CircleGeometry(40), material); - plane.rotation.x = -Math.PI / 2; - plane.renderOrder = -1; - - return plane; -} - -// Helper function to create and position labels -function createLabel(text, font, material, position) { - const txt_geo = new TextGeometry(text, { - font: font, - size: 0.1, - depth: 0.001, - curveSegments: 12, - bevelEnabled: false, - }); - - txt_geo.computeBoundingBox(); - const offx = -0.5 * (txt_geo.boundingBox.max.x - txt_geo.boundingBox.min.x); - const offy = -0.5 * (txt_geo.boundingBox.max.y - txt_geo.boundingBox.min.y); - const offz = -0.5 * (txt_geo.boundingBox.max.z - txt_geo.boundingBox.min.z); - txt_geo.translate(offx, offy, offz); - - const label = new THREE.Group(); - const mesh = new THREE.Mesh(txt_geo); - label.add(mesh); - - // Apply default rotation for labels - label.rotateY(-Math.PI / 2); - - label.children[0].material = material; - label.position.copy(position); - base.add(label); -} - -async function init() { - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xffffff); - - camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); - camera.position.set(-0.1, 5, 0.548); - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(1.0); // important for performance - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.toneMapping = THREE.NeutralToneMapping; - renderer.toneMappingExposure = 1.0; - renderer.inspector = new Inspector(); - renderer.setAnimationLoop(render); - document.body.appendChild(renderer.domElement); - - controls = new OrbitControls(camera, renderer.domElement); - controls.target.set(0, 0, 0.548); - - gui = renderer.inspector.createParameters('Parameters'); - - font = await new FontLoader().loadAsync('./fonts/helvetiker_regular.typeface.json'); - - // Create shared block geometry - blockGeometry = new RoundedBoxGeometry(0.125, 0.9, 0.9, 10, 0.02); - - base = new THREE.Group(); - base.rotation.set(0, 0, -Math.PI / 2); - base.position.set(0, 0, 0.548); - scene.add(base); - - const text_mat = new THREE.MeshStandardMaterial(); - text_mat.colorNode = TSL.color('#000000'); - - // Create finish labels (using negative wood index for left column) - for (let y = 0; y < Finishes.length; y++) { - createLabel(Finishes[y], font, text_mat, getGridPosition(-1, y)); - } - - // Create and add the grid plane - const plane = createGridPlane(); - scene.add(plane); - - await new HDRLoader() - .setPath('textures/equirectangular/') - .loadAsync('san_giuseppe_bridge_2k.hdr') - .then(texture => { - texture.mapping = THREE.EquirectangularReflectionMapping; - - scene.environment = texture; - scene.environmentIntensity = 2; - }); - - // Create wood labels (using negative finish index for top row) - for (let x = 0; x < WoodGenuses.length; x++) { - createLabel(WoodGenuses[x], font, text_mat, getGridPosition(x, -1)); - } - - // Create wood blocks - for (let x = 0; x < WoodGenuses.length; x++) { - for (let y = 0; y < Finishes.length; y++) { - const material = WoodNodeMaterial.fromPreset(WoodGenuses[x], Finishes[y]); - const cube = new THREE.Mesh(blockGeometry, material); - - cube.position.copy(getGridPosition(x, y)); - material.transformationMatrix = new THREE.Matrix4().setPosition(new THREE.Vector3(-0.1, 0, Math.random())); - base.add(cube); - - await new Promise(resolve => setTimeout(resolve, 0)); - } - } - - add_custom_wood(text_mat); -} - -function render() { - controls.update(); - - renderer.render(scene, camera); -} - -window.addEventListener('resize', () => { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - renderer.setSize(window.innerWidth, window.innerHeight); -}); - -if (WebGPU.isAvailable()) { - init(); -} else { - document.body.appendChild(WebGPU.getErrorMessage()); -} - -function add_custom_wood(text_mat) { - // Add "Custom" label (positioned at the end of the grid) - createLabel('custom', font, text_mat, getGridPosition(Math.round(WoodGenuses.length / 2 - 1), 5)); - - // Create custom wood material with unique parameters - const customMaterial = new WoodNodeMaterial({ - centerSize: 1.11, - largeWarpScale: 0.32, - largeGrainStretch: 0.24, - smallWarpStrength: 0.059, - smallWarpScale: 2, - fineWarpStrength: 0.006, - fineWarpScale: 32.8, - ringThickness: 1 / 34, - ringBias: 0.03, - ringSizeVariance: 0.03, - ringVarianceScale: 4.4, - barkThickness: 0.3, - splotchScale: 0.2, - splotchIntensity: 0.541, - cellScale: 910, - cellSize: 0.1, - darkGrainColor: new THREE.Color('#0c0504'), - lightGrainColor: new THREE.Color('#926c50'), - clearcoat: 1, - clearcoatRoughness: 0.2, - }); - - gui.add(customMaterial, 'centerSize', 0.0, 2.0, 0.01).name('centerSize'); - gui.add(customMaterial, 'largeWarpScale', 0.0, 1.0, 0.001).name('largeWarpScale'); - gui.add(customMaterial, 'largeGrainStretch', 0.0, 1.0, 0.001).name('largeGrainStretch'); - gui.add(customMaterial, 'smallWarpStrength', 0.0, 0.2, 0.001).name('smallWarpStrength'); - gui.add(customMaterial, 'smallWarpScale', 0.0, 5.0, 0.01).name('smallWarpScale'); - gui.add(customMaterial, 'fineWarpStrength', 0.0, 0.05, 0.001).name('fineWarpStrength'); - gui.add(customMaterial, 'fineWarpScale', 0.0, 50.0, 0.1).name('fineWarpScale'); - gui.add(customMaterial, 'ringThickness', 0.0, 0.1, 0.001).name('ringThickness'); - gui.add(customMaterial, 'ringBias', -0.2, 0.2, 0.001).name('ringBias'); - gui.add(customMaterial, 'ringSizeVariance', 0.0, 0.2, 0.001).name('ringSizeVariance'); - gui.add(customMaterial, 'ringVarianceScale', 0.0, 10.0, 0.1).name('ringVarianceScale'); - gui.add(customMaterial, 'barkThickness', 0.0, 1.0, 0.01).name('barkThickness'); - gui.add(customMaterial, 'splotchScale', 0.0, 1.0, 0.01).name('splotchScale'); - gui.add(customMaterial, 'splotchIntensity', 0.0, 1.0, 0.01).name('splotchIntensity'); - gui.add(customMaterial, 'cellScale', 100, 2000, 1).name('cellScale'); - gui.add(customMaterial, 'cellSize', 0.01, 0.5, 0.001).name('cellSize'); - gui.addColor({ darkGrainColor: '#0c0504' }, 'darkGrainColor').onChange(v => customMaterial.darkGrainColor.set(v)); - gui.addColor({ lightGrainColor: '#926c50' }, 'lightGrainColor').onChange(v => - customMaterial.lightGrainColor.set(v), - ); - gui.add(customMaterial, 'clearcoat', 0.0, 1.0, 0.01).name('clearcoat'); - gui.add(customMaterial, 'clearcoatRoughness', 0.0, 1.0, 0.01).name('clearcoatRoughness'); - - const cube = new THREE.Mesh(blockGeometry, customMaterial); - - customMaterial.transformationMatrix = new THREE.Matrix4().setPosition(new THREE.Vector3(-0.1, 0, Math.random())); - cube.position.copy(getGridPosition(Math.round(WoodGenuses.length / 2), 5)); - - base.add(cube); -} diff --git a/examples-testing/examples/webgpu_video_panorama.ts b/examples-testing/examples/webgpu_video_panorama.ts deleted file mode 100644 index f52b15ffe..000000000 --- a/examples-testing/examples/webgpu_video_panorama.ts +++ /dev/null @@ -1,99 +0,0 @@ -import * as THREE from 'three/webgpu'; - -let camera, scene, renderer; - -let isUserInteracting = false, - lon = 0, - lat = 0, - phi = 0, - theta = 0, - onPointerDownPointerX = 0, - onPointerDownPointerY = 0, - onPointerDownLon = 0, - onPointerDownLat = 0; - -const distance = 0.5; - -init(); - -function init() { - const container = document.getElementById('container'); - - camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.25, 10); - - scene = new THREE.Scene(); - - const geometry = new THREE.SphereGeometry(5, 60, 40); - // invert the geometry on the x-axis so that all of the faces point inward - geometry.scale(-1, 1, 1); - - const video = document.getElementById('video'); - video.play(); - - const texture = new THREE.VideoTexture(video); - texture.colorSpace = THREE.SRGBColorSpace; - const material = new THREE.MeshBasicMaterial({ map: texture }); - - const mesh = new THREE.Mesh(geometry, material); - scene.add(mesh); - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - container.appendChild(renderer.domElement); - - document.addEventListener('pointerdown', onPointerDown); - document.addEventListener('pointermove', onPointerMove); - document.addEventListener('pointerup', onPointerUp); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function onPointerDown(event) { - isUserInteracting = true; - - onPointerDownPointerX = event.clientX; - onPointerDownPointerY = event.clientY; - - onPointerDownLon = lon; - onPointerDownLat = lat; -} - -function onPointerMove(event) { - if (isUserInteracting === true) { - lon = (onPointerDownPointerX - event.clientX) * 0.1 + onPointerDownLon; - lat = (onPointerDownPointerY - event.clientY) * 0.1 + onPointerDownLat; - } -} - -function onPointerUp() { - isUserInteracting = false; -} - -function animate() { - update(); -} - -function update() { - lat = Math.max(-85, Math.min(85, lat)); - phi = THREE.MathUtils.degToRad(90 - lat); - theta = THREE.MathUtils.degToRad(lon); - - camera.position.x = distance * Math.sin(phi) * Math.cos(theta); - camera.position.y = distance * Math.cos(phi); - camera.position.z = distance * Math.sin(phi) * Math.sin(theta); - - camera.lookAt(0, 0, 0); - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_volume_caustics.ts b/examples-testing/examples/webgpu_volume_caustics.ts deleted file mode 100644 index 0c85390dd..000000000 --- a/examples-testing/examples/webgpu_volume_caustics.ts +++ /dev/null @@ -1,304 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { - uniform, - refract, - div, - frameId, - lightViewPosition, - float, - positionView, - positionViewDirection, - screenUV, - pass, - texture3D, - time, - screenCoordinate, - normalView, - texture, - Fn, - vec2, - vec3, -} from 'three/tsl'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; -import { ImprovedNoise } from 'three/addons/math/ImprovedNoise.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { bayer16 } from 'three/addons/tsl/math/Bayer.js'; -import { bloom } from 'three/addons/tsl/display/BloomNode.js'; - -let camera, scene, renderer, controls; -let renderPipeline; -let gltf; - -init(); - -async function init() { - const LAYER_VOLUMETRIC_LIGHTING = 10; - - camera = new THREE.PerspectiveCamera(25, window.innerWidth / window.innerHeight, 0.025, 5); - camera.position.set(-0.7, 0.2, 0.2); - - scene = new THREE.Scene(); - - // Light - - const spotLight = new THREE.SpotLight(0xffffff, 1); - spotLight.position.set(0.2, 0.3, 0.2); - spotLight.castShadow = true; - spotLight.angle = Math.PI / 6; - spotLight.penumbra = 1; - spotLight.decay = 2; - spotLight.distance = 0; - spotLight.shadow.mapType = THREE.HalfFloatType; // For HDR Caustics - spotLight.shadow.mapSize.width = 1024; - spotLight.shadow.mapSize.height = 1024; - spotLight.shadow.camera.near = 0.1; - spotLight.shadow.camera.far = 1; - spotLight.shadow.intensity = 0.95; - spotLight.layers.enable(LAYER_VOLUMETRIC_LIGHTING); - scene.add(spotLight); - - // Model / Textures - - const dracoLoader = new DRACOLoader(); - dracoLoader.setDecoderPath('jsm/libs/draco/'); - dracoLoader.setDecoderConfig({ type: 'js' }); - - gltf = (await new GLTFLoader().setDRACOLoader(dracoLoader).loadAsync('./models/gltf/duck.glb')).scene; - gltf.scale.setScalar(0.5); - scene.add(gltf); - - const causticMap = new THREE.TextureLoader().load('./textures/opengameart/Caustic_Free.jpg'); - causticMap.wrapS = causticMap.wrapT = THREE.RepeatWrapping; - causticMap.colorSpace = THREE.SRGBColorSpace; - - // Material - - const duck = gltf.children[0]; - duck.material = new THREE.MeshPhysicalNodeMaterial(); - duck.material.side = THREE.DoubleSide; - duck.material.transparent = true; - duck.material.color = new THREE.Color(0xffd700); - duck.material.transmission = 1; - duck.material.thickness = 0.25; - duck.material.ior = 1.5; - duck.material.metalness = 0; - duck.material.roughness = 0.1; - duck.castShadow = true; - - // TSL Shader - - const causticOcclusion = uniform(1); - - const causticEffect = Fn(() => { - const refractionVector = refract( - positionViewDirection.negate(), - normalView, - div(1.0, duck.material.ior), - ).normalize(); - const viewZ = normalView.z.pow(causticOcclusion); - - const textureUV = refractionVector.xy.mul(0.6); - - const causticColor = uniform(duck.material.color); - const chromaticAberrationOffset = normalView.z.pow(-0.9).mul(0.004); - - const causticProjection = vec3( - texture(causticMap, textureUV.add(vec2(chromaticAberrationOffset.negate(), 0))).r, - texture(causticMap, textureUV.add(vec2(0, chromaticAberrationOffset.negate()))).g, - texture(causticMap, textureUV.add(vec2(chromaticAberrationOffset, chromaticAberrationOffset))).b, - ); - - return causticProjection.mul(viewZ.mul(60)).add(viewZ).mul(causticColor); - })().toVar(); - - duck.material.castShadowNode = causticEffect; - - duck.material.emissiveNode = Fn(() => { - // Custom emissive for illuminating backside of the mesh based on the caustic effect and light direction - - const thicknessPowerNode = float(3.0); - - const scatteringHalf = lightViewPosition(spotLight).sub(positionView).normalize(); - const scatteringDot = float( - positionViewDirection.dot(scatteringHalf.negate()).saturate().pow(thicknessPowerNode), - ); - - return causticEffect.mul(scatteringDot.add(0.1)).mul(0.02); - })(); - - // Ground - - const textureLoader = new THREE.TextureLoader(); - const map = textureLoader.load('textures/hardwood2_diffuse.jpg'); - map.wrapS = map.wrapT = THREE.RepeatWrapping; - map.repeat.set(10, 10); - - const geometry = new THREE.PlaneGeometry(2, 2); - const material = new THREE.MeshStandardMaterial({ color: 0 }); - - const ground = new THREE.Mesh(geometry, material); - ground.rotation.x = -Math.PI / 2; - ground.receiveShadow = true; - scene.add(ground); - - // Renderer - - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.shadowMap.enabled = true; - renderer.shadowMap.transmitted = true; - renderer.inspector = new Inspector(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - document.body.appendChild(renderer.domElement); - - // GUI - - const gui = renderer.inspector.createParameters('Volumetric Caustics'); - gui.add(causticOcclusion, 'value', 0, 20).name('caustic occlusion'); - gui.addColor(duck.material, 'color').name('material color'); - - // Post-Processing - - renderPipeline = new THREE.RenderPipeline(renderer); - - // Layers - - const volumetricLightingIntensity = uniform(0.7); - - const volumetricLayer = new THREE.Layers(); - volumetricLayer.disableAll(); - volumetricLayer.enable(LAYER_VOLUMETRIC_LIGHTING); - - // Volumetric Fog Area - - function createTexture3D() { - let i = 0; - - const size = 128; - const data = new Uint8Array(size * size * size); - - const scale = 10; - const perlin = new ImprovedNoise(); - - const repeatFactor = 5.0; - - for (let z = 0; z < size; z++) { - for (let y = 0; y < size; y++) { - for (let x = 0; x < size; x++) { - const nx = (x / size) * repeatFactor; - const ny = (y / size) * repeatFactor; - const nz = (z / size) * repeatFactor; - - const noiseValue = perlin.noise(nx * scale, ny * scale, nz * scale); - - data[i] = 128 + 128 * noiseValue; - - i++; - } - } - } - - const texture = new THREE.Data3DTexture(data, size, size, size); - texture.format = THREE.RedFormat; - texture.minFilter = THREE.LinearFilter; - texture.magFilter = THREE.LinearFilter; - texture.wrapS = THREE.RepeatWrapping; - texture.wrapT = THREE.RepeatWrapping; - texture.unpackAlignment = 1; - texture.needsUpdate = true; - - return texture; - } - - const noiseTexture3D = createTexture3D(); - - const smokeAmount = uniform(3); - - const volumetricMaterial = new THREE.VolumeNodeMaterial(); - volumetricMaterial.steps = 20; - volumetricMaterial.offsetNode = bayer16(screenCoordinate.add(frameId)); // Add dithering to reduce banding - volumetricMaterial.scatteringNode = Fn(({ positionRay }) => { - // Return the amount of fog based on the noise texture - - const timeScaled = vec3(time.mul(0.01), 0, time.mul(0.03)); - - const sampleGrain = (scale, timeScale = 1) => - texture3D(noiseTexture3D, positionRay.add(timeScaled.mul(timeScale)).mul(scale).mod(1), 0).r.add(0.5); - - let density = sampleGrain(1); - density = density.mul(sampleGrain(0.5, 1)); - density = density.mul(sampleGrain(0.2, 2)); - - return smokeAmount.mix(1, density); - }); - - const volumetricMesh = new THREE.Mesh(new THREE.BoxGeometry(1.5, 0.5, 1.5), volumetricMaterial); - volumetricMesh.receiveShadow = true; - volumetricMesh.position.y = 0.25; - volumetricMesh.layers.disableAll(); - volumetricMesh.layers.enable(LAYER_VOLUMETRIC_LIGHTING); - scene.add(volumetricMesh); - - // Scene Pass - - const scenePass = pass(scene, camera).toInspector(); - scenePass.name = 'Scene'; - - const sceneDepth = scenePass.getTextureNode('depth'); - sceneDepth.name = 'Scene Depth'; - - // Material - Apply occlusion depth of volumetric lighting based on the scene depth - - volumetricMaterial.depthNode = sceneDepth.sample(screenUV); - - // Volumetric Lighting Pass - - const volumetricPass = pass(scene, camera, { depthBuffer: false, samples: 0 }).toInspector( - 'Volumetric Lighting / Raw', - ); - volumetricPass.name = 'Volumetric Lighting'; - volumetricPass.setLayers(volumetricLayer); - volumetricPass.setResolutionScale(0.5); - - // Compose and Denoise - - const bloomPass = bloom(volumetricPass, 1, 1, 0).toInspector('Volumetric Lighting / Mip-Chain Gaussian Blur'); - bloomPass.name = 'Bloom'; - - const scenePassColor = scenePass.add(bloomPass.mul(volumetricLightingIntensity)); - - renderPipeline.outputNode = scenePassColor; - - // Controls - - controls = new OrbitControls(camera, renderer.domElement); - controls.target.z = -0.05; - controls.target.y = 0.02; - controls.maxDistance = 1; - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - for (const mesh of gltf.children) { - mesh.rotation.y -= 0.01; - } - - controls.update(); - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_volume_cloud.ts b/examples-testing/examples/webgpu_volume_cloud.ts deleted file mode 100644 index 1d46c3543..000000000 --- a/examples-testing/examples/webgpu_volume_cloud.ts +++ /dev/null @@ -1,165 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { float, vec3, vec4, If, Break, Fn, smoothstep, texture3D, uniform } from 'three/tsl'; - -import { RaymarchingBox } from 'three/addons/tsl/utils/Raymarching.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { ImprovedNoise } from 'three/addons/math/ImprovedNoise.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let renderer, scene, camera; -let mesh; - -init(); - -function init() { - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(0, 0, 1.5); - - new OrbitControls(camera, renderer.domElement); - - // Sky - - const canvas = document.createElement('canvas'); - canvas.width = 1; - canvas.height = 32; - - const context = canvas.getContext('2d'); - const gradient = context.createLinearGradient(0, 0, 0, 32); - gradient.addColorStop(0.0, '#014a84'); - gradient.addColorStop(0.5, '#0561a0'); - gradient.addColorStop(1.0, '#437ab6'); - context.fillStyle = gradient; - context.fillRect(0, 0, 1, 32); - - const skyMap = new THREE.CanvasTexture(canvas); - skyMap.colorSpace = THREE.SRGBColorSpace; - - const sky = new THREE.Mesh( - new THREE.SphereGeometry(10), - new THREE.MeshBasicNodeMaterial({ map: skyMap, side: THREE.BackSide }), - ); - scene.add(sky); - - // Texture - - const size = 128; - const data = new Uint8Array(size * size * size); - - let i = 0; - const scale = 0.05; - const perlin = new ImprovedNoise(); - const vector = new THREE.Vector3(); - - for (let z = 0; z < size; z++) { - for (let y = 0; y < size; y++) { - for (let x = 0; x < size; x++) { - const d = - 1.0 - - vector - .set(x, y, z) - .subScalar(size / 2) - .divideScalar(size) - .length(); - data[i] = (128 + 128 * perlin.noise((x * scale) / 1.5, y * scale, (z * scale) / 1.5)) * d * d; - i++; - } - } - } - - const texture = new THREE.Data3DTexture(data, size, size, size); - texture.format = THREE.RedFormat; - texture.minFilter = THREE.LinearFilter; - texture.magFilter = THREE.LinearFilter; - texture.unpackAlignment = 1; - texture.needsUpdate = true; - - // Shader - - const transparentRaymarchingTexture = Fn( - ({ texture, range = float(0.1), threshold = float(0.25), opacity = float(0.25), steps = float(100) }) => { - const finalColor = vec4(0).toVar(); - - RaymarchingBox(steps, ({ positionRay }) => { - const mapValue = float(texture.sample(positionRay.add(0.5)).r).toVar(); - - mapValue.assign(smoothstep(threshold.sub(range), threshold.add(range), mapValue).mul(opacity)); - - const shading = texture - .sample(positionRay.add(vec3(-0.01))) - .r.sub(texture.sample(positionRay.add(vec3(0.01))).r); - - const col = shading.mul(3.0).add(positionRay.x.add(positionRay.y).mul(0.25)).add(0.2); - - finalColor.rgb.addAssign(finalColor.a.oneMinus().mul(mapValue).mul(col)); - - finalColor.a.addAssign(finalColor.a.oneMinus().mul(mapValue)); - - If(finalColor.a.greaterThanEqual(0.95), () => { - Break(); - }); - }); - - return finalColor; - }, - ); - - // Material - - const baseColor = uniform(new THREE.Color(0x798aa0)); - const range = uniform(0.1); - const threshold = uniform(0.25); - const opacity = uniform(0.25); - const steps = uniform(100); - - const cloud3d = transparentRaymarchingTexture({ - texture: texture3D(texture, null, 0), - range, - threshold, - opacity, - steps, - }); - - const finalCloud = cloud3d.setRGB(cloud3d.rgb.add(baseColor)); - - const material = new THREE.NodeMaterial(); - material.colorNode = finalCloud; - material.side = THREE.BackSide; - material.transparent = true; - - mesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), material); - scene.add(mesh); - - // - - const gui = renderer.inspector.createParameters('Parameters'); - gui.add(threshold, 'value', 0, 1, 0.01).name('threshold'); - gui.add(opacity, 'value', 0, 1, 0.01).name('opacity'); - gui.add(range, 'value', 0, 1, 0.01).name('range'); - gui.add(steps, 'value', 0, 200, 1).name('steps'); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - mesh.rotation.y = -performance.now() / 7500; - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_volume_lighting.ts b/examples-testing/examples/webgpu_volume_lighting.ts deleted file mode 100644 index 38099c6a5..000000000 --- a/examples-testing/examples/webgpu_volume_lighting.ts +++ /dev/null @@ -1,248 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { vec3, Fn, time, texture3D, screenUV, uniform, screenCoordinate, pass } from 'three/tsl'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { ImprovedNoise } from 'three/addons/math/ImprovedNoise.js'; -import { TeapotGeometry } from 'three/addons/geometries/TeapotGeometry.js'; - -import { bayer16 } from 'three/addons/tsl/math/Bayer.js'; -import { gaussianBlur } from 'three/addons/tsl/display/GaussianBlurNode.js'; - -let renderer, scene, camera; -let volumetricMesh, teapot, pointLight, spotLight; -let renderPipeline; - -init(); - -function createTexture3D() { - let i = 0; - - const size = 128; - const data = new Uint8Array(size * size * size); - - const scale = 10; - const perlin = new ImprovedNoise(); - - const repeatFactor = 5.0; - - for (let z = 0; z < size; z++) { - for (let y = 0; y < size; y++) { - for (let x = 0; x < size; x++) { - const nx = (x / size) * repeatFactor; - const ny = (y / size) * repeatFactor; - const nz = (z / size) * repeatFactor; - - const noiseValue = perlin.noise(nx * scale, ny * scale, nz * scale); - - data[i] = 128 + 128 * noiseValue; - - i++; - } - } - } - - const texture = new THREE.Data3DTexture(data, size, size, size); - texture.format = THREE.RedFormat; - texture.minFilter = THREE.LinearFilter; - texture.magFilter = THREE.LinearFilter; - texture.wrapS = THREE.RepeatWrapping; - texture.wrapT = THREE.RepeatWrapping; - texture.unpackAlignment = 1; - texture.needsUpdate = true; - - return texture; -} - -function init() { - const LAYER_VOLUMETRIC_LIGHTING = 10; - - renderer = new THREE.WebGPURenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.toneMapping = THREE.NeutralToneMapping; - renderer.toneMappingExposure = 2; - renderer.shadowMap.enabled = true; - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(-8, 1, -6); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.maxDistance = 40; - controls.minDistance = 2; - - // Volumetric Fog Area - - const noiseTexture3D = createTexture3D(); - - const smokeAmount = uniform(2); - - const volumetricMaterial = new THREE.VolumeNodeMaterial(); - volumetricMaterial.steps = 12; - volumetricMaterial.offsetNode = bayer16(screenCoordinate); // Add dithering to reduce banding - volumetricMaterial.scatteringNode = Fn(({ positionRay }) => { - // Return the amount of fog based on the noise texture - - const timeScaled = vec3(time, 0, time.mul(0.3)); - - const sampleGrain = (scale, timeScale = 1) => - texture3D(noiseTexture3D, positionRay.add(timeScaled.mul(timeScale)).mul(scale).mod(1), 0).r.add(0.5); - - let density = sampleGrain(0.1); - density = density.mul(sampleGrain(0.05, 1)); - density = density.mul(sampleGrain(0.02, 2)); - - return smokeAmount.mix(1, density); - }); - - volumetricMesh = new THREE.Mesh(new THREE.BoxGeometry(20, 10, 20), volumetricMaterial); - volumetricMesh.receiveShadow = true; - volumetricMesh.position.y = 2; - volumetricMesh.layers.disableAll(); - volumetricMesh.layers.enable(LAYER_VOLUMETRIC_LIGHTING); - scene.add(volumetricMesh); - - // Objects - - teapot = new THREE.Mesh( - new TeapotGeometry(0.8, 18), - new THREE.MeshStandardMaterial({ color: 0xffffff, side: THREE.DoubleSide }), - ); - teapot.castShadow = true; - scene.add(teapot); - - const floor = new THREE.Mesh( - new THREE.PlaneGeometry(100, 100), - new THREE.MeshStandardMaterial({ color: 0xffffff }), - ); - floor.rotation.x = -Math.PI / 2; - floor.position.y = -3; - floor.receiveShadow = true; - scene.add(floor); - - // Lights - - pointLight = new THREE.PointLight(0xf9bb50, 3, 100); - pointLight.castShadow = true; - pointLight.position.set(0, 1.4, 0); - pointLight.layers.enable(LAYER_VOLUMETRIC_LIGHTING); - //lightBase.add( new THREE.Mesh( new THREE.SphereGeometry( 0.1, 16, 16 ), new THREE.MeshBasicMaterial( { color: 0xf9bb50 } ) ) ); - scene.add(pointLight); - - spotLight = new THREE.SpotLight(0xffffff, 100); - spotLight.position.set(2.5, 5, 2.5); - spotLight.angle = Math.PI / 6; - spotLight.penumbra = 1; - spotLight.decay = 2; - spotLight.distance = 0; - spotLight.map = new THREE.TextureLoader().setPath('textures/').load('colors.png'); - spotLight.castShadow = true; - spotLight.shadow.intensity = 0.98; - spotLight.shadow.mapSize.width = 1024; - spotLight.shadow.mapSize.height = 1024; - spotLight.shadow.camera.near = 1; - spotLight.shadow.camera.far = 15; - spotLight.shadow.focus = 1; - spotLight.layers.enable(LAYER_VOLUMETRIC_LIGHTING); - //sunLight.add( new THREE.Mesh( new THREE.SphereGeometry( 0.1, 16, 16 ), new THREE.MeshBasicMaterial( { color: 0xffffff } ) ) ); - scene.add(spotLight); - - // Post-Processing - - renderPipeline = new THREE.RenderPipeline(renderer); - - // Layers - - const volumetricLightingIntensity = uniform(1); - - const volumetricLayer = new THREE.Layers(); - volumetricLayer.disableAll(); - volumetricLayer.enable(LAYER_VOLUMETRIC_LIGHTING); - - // Scene Pass - - const scenePass = pass(scene, camera); - const sceneDepth = scenePass.getTextureNode('depth'); - - // Material - Apply occlusion depth of volumetric lighting based on the scene depth - - volumetricMaterial.depthNode = sceneDepth.sample(screenUV); - - // Volumetric Lighting Pass - - const volumetricPass = pass(scene, camera, { depthBuffer: false }); - volumetricPass.name = 'Volumetric Lighting'; - volumetricPass.setLayers(volumetricLayer); - volumetricPass.setResolutionScale(0.25); - - // Compose and Denoise - - const denoiseStrength = uniform(0.6); - - const blurredVolumetricPass = gaussianBlur(volumetricPass, denoiseStrength); - - const scenePassColor = scenePass.add(blurredVolumetricPass.mul(volumetricLightingIntensity)); - - renderPipeline.outputNode = scenePassColor; - - // GUI - - const params = { - resolution: volumetricPass.getResolutionScale(), - denoise: true, - }; - - const gui = renderer.inspector.createParameters('Volumetric Lighting'); - - const rayMarching = gui.addFolder('Ray Marching'); - rayMarching.add(params, 'resolution', 0.1, 1).onChange(resolution => { - volumetricPass.setResolutionScale(resolution); - }); - rayMarching.add(volumetricMaterial, 'steps', 2, 16).name('step count'); - rayMarching.add(denoiseStrength, 'value', 0, 1).name('denoise strength'); - rayMarching.add(params, 'denoise').onChange(denoise => { - const volumetric = denoise ? blurredVolumetricPass : volumetricPass; - - const scenePassColor = scenePass.add(volumetric.mul(volumetricLightingIntensity)); - - renderPipeline.outputNode = scenePassColor; - renderPipeline.needsUpdate = true; - }); - - const lighting = gui.addFolder('Lighting / Scene'); - lighting.add(pointLight, 'intensity', 0, 6).name('light intensity'); - lighting.add(spotLight, 'intensity', 0, 200).name('spot intensity'); - lighting.add(volumetricLightingIntensity, 'value', 0, 2).name('fog intensity'); - lighting.add(smokeAmount, 'value', 0, 3).name('smoke amount'); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - const time = performance.now() * 0.001; - const scale = 2.4; - - pointLight.position.x = Math.sin(time * 0.7) * scale; - pointLight.position.y = Math.cos(time * 0.5) * scale; - pointLight.position.z = Math.cos(time * 0.3) * scale; - - spotLight.position.x = Math.cos(time * 0.3) * scale; - spotLight.lookAt(0, 0, 0); - - teapot.rotation.y = time * 0.2; - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_volume_lighting_rectarea.ts b/examples-testing/examples/webgpu_volume_lighting_rectarea.ts deleted file mode 100644 index 47c82841c..000000000 --- a/examples-testing/examples/webgpu_volume_lighting_rectarea.ts +++ /dev/null @@ -1,256 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { vec3, Fn, time, texture3D, screenUV, uniform, screenCoordinate, pass, checker, uv } from 'three/tsl'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { ImprovedNoise } from 'three/addons/math/ImprovedNoise.js'; -import { RectAreaLightTexturesLib } from 'three/addons/lights/RectAreaLightTexturesLib.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { bayer16 } from 'three/addons/tsl/math/Bayer.js'; -import { gaussianBlur } from 'three/addons/tsl/display/GaussianBlurNode.js'; - -let renderer, scene, camera; -let volumetricMesh; -let rectLight1, rectLight2, rectLight3; -let timer; -let renderPipeline; - -init(); - -function createTexture3D() { - let i = 0; - - const size = 128; - const data = new Uint8Array(size * size * size); - - const scale = 10; - const perlin = new ImprovedNoise(); - - const repeatFactor = 5.0; - - for (let z = 0; z < size; z++) { - for (let y = 0; y < size; y++) { - for (let x = 0; x < size; x++) { - const nx = (x / size) * repeatFactor; - const ny = (y / size) * repeatFactor; - const nz = (z / size) * repeatFactor; - - const noiseValue = perlin.noise(nx * scale, ny * scale, nz * scale); - - data[i] = 128 + 128 * noiseValue; - - i++; - } - } - } - - const texture = new THREE.Data3DTexture(data, size, size, size); - texture.format = THREE.RedFormat; - texture.minFilter = THREE.LinearFilter; - texture.magFilter = THREE.LinearFilter; - texture.wrapS = THREE.RepeatWrapping; - texture.wrapT = THREE.RepeatWrapping; - texture.unpackAlignment = 1; - texture.needsUpdate = true; - - return texture; -} - -function init() { - THREE.RectAreaLightNode.setLTC(RectAreaLightTexturesLib.init()); - - const LAYER_VOLUMETRIC_LIGHTING = 10; - - timer = new THREE.Timer(); - - renderer = new THREE.WebGPURenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.toneMapping = THREE.NeutralToneMapping; - renderer.toneMappingExposure = 2; - renderer.shadowMap.enabled = true; - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 250); - camera.position.set(0, 5, -15); - - // Volumetric Fog Area - - const noiseTexture3D = createTexture3D(); - - const smokeAmount = uniform(2); - - const volumetricMaterial = new THREE.VolumeNodeMaterial(); - volumetricMaterial.steps = 12; - volumetricMaterial.offsetNode = bayer16(screenCoordinate); // Add dithering to reduce banding - volumetricMaterial.scatteringNode = Fn(({ positionRay }) => { - // Return the amount of fog based on the noise texture - - const timeScaled = vec3(time, 0, time.mul(0.3)); - - const sampleGrain = (scale, timeScale = 1) => - texture3D(noiseTexture3D, positionRay.add(timeScaled.mul(timeScale)).mul(scale).mod(1), 0).r.add(0.5); - - let density = sampleGrain(0.1); - density = density.mul(sampleGrain(0.05, 1)); - density = density.mul(sampleGrain(0.02, 2)); - - return smokeAmount.mix(1, density); - }); - - volumetricMesh = new THREE.Mesh(new THREE.BoxGeometry(50, 40, 50), volumetricMaterial); - volumetricMesh.receiveShadow = true; - volumetricMesh.position.y = 20; - volumetricMesh.layers.disableAll(); - volumetricMesh.layers.enable(LAYER_VOLUMETRIC_LIGHTING); - scene.add(volumetricMesh); - - // Objects - - rectLight1 = new THREE.RectAreaLight(0xff0000, 5, 4, 10); - rectLight1.layers.enable(LAYER_VOLUMETRIC_LIGHTING); - rectLight1.position.set(-5, 6, 5); - scene.add(rectLight1); - - rectLight2 = new THREE.RectAreaLight(0x00ff00, 5, 4, 10); - rectLight2.layers.enable(LAYER_VOLUMETRIC_LIGHTING); - rectLight2.position.set(0, 6, 5); - scene.add(rectLight2); - - rectLight3 = new THREE.RectAreaLight(0x0000ff, 5, 4, 10); - rectLight3.layers.enable(LAYER_VOLUMETRIC_LIGHTING); - rectLight3.position.set(5, 6, 5); - scene.add(rectLight3); - - // - - const createRectLightMesh = rectLight => { - const geometry = new THREE.PlaneGeometry(4, 10); - const frontMaterial = new THREE.MeshBasicMaterial({ color: rectLight.color, side: THREE.BackSide }); - const backMaterial = new THREE.MeshStandardMaterial({ color: 0x111111 }); - - const backSide = new THREE.Mesh(geometry, backMaterial); - backSide.position.set(0, 0, 0.08); - - const frontSide = new THREE.Mesh(geometry, frontMaterial); - frontSide.position.set(0, 0, 0.01); - - rectLight.add(backSide); - rectLight.add(frontSide); - }; - - createRectLightMesh(rectLight1); - createRectLightMesh(rectLight2); - createRectLightMesh(rectLight3); - - // - - const geoFloor = new THREE.BoxGeometry(2000, 0.1, 2000); - const matStdFloor = new THREE.MeshStandardMaterial({ color: 0x444444 }); - matStdFloor.roughnessNode = checker(uv().mul(400)); - const mshStdFloor = new THREE.Mesh(geoFloor, matStdFloor); - scene.add(mshStdFloor); - - const geoKnot = new THREE.TorusKnotGeometry(1.5, 0.5, 200, 16); - const matKnot = new THREE.MeshStandardMaterial({ color: 0xffffff, roughness: 0, metalness: 0 }); - const meshKnot = new THREE.Mesh(geoKnot, matKnot); - meshKnot.position.set(0, 5.5, 0); - scene.add(meshKnot); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.minDistance = 5; - controls.maxDistance = 200; - controls.target.copy(meshKnot.position); - controls.update(); - - // Post-Processing - - renderPipeline = new THREE.RenderPipeline(renderer); - - // Layers - - const volumetricLightingIntensity = uniform(1); - - const volumetricLayer = new THREE.Layers(); - volumetricLayer.disableAll(); - volumetricLayer.enable(LAYER_VOLUMETRIC_LIGHTING); - - // Scene Pass - - const scenePass = pass(scene, camera); - const sceneDepth = scenePass.getTextureNode('depth'); - - // Material - Apply occlusion depth of volumetric lighting based on the scene depth - - volumetricMaterial.depthNode = sceneDepth.sample(screenUV); - - // Volumetric Lighting Pass - - const volumetricPass = pass(scene, camera, { depthBuffer: false }); - volumetricPass.setLayers(volumetricLayer); - volumetricPass.setResolutionScale(0.25); - - // Compose and Denoise - - const denoiseStrength = uniform(0.6); - - const blurredVolumetricPass = gaussianBlur(volumetricPass, denoiseStrength); - - const scenePassColor = scenePass.add(blurredVolumetricPass.mul(volumetricLightingIntensity)); - - renderPipeline.outputNode = scenePassColor; - - // GUI - - const params = { - resolution: volumetricPass.getResolutionScale(), - denoise: true, - }; - - const gui = renderer.inspector.createParameters('Volumetric Lighting'); - - const rayMarching = gui.addFolder('Ray Marching'); - rayMarching.add(params, 'resolution', 0.1, 1).onChange(resolution => { - volumetricPass.setResolutionScale(resolution); - }); - rayMarching.add(volumetricMaterial, 'steps', 2, 16).name('step count'); - rayMarching.add(denoiseStrength, 'value', 0, 1).name('denoise strength'); - rayMarching.add(params, 'denoise').onChange(denoise => { - const volumetric = denoise ? blurredVolumetricPass : volumetricPass; - - const scenePassColor = scenePass.add(volumetric.mul(volumetricLightingIntensity)); - - renderPipeline.outputNode = scenePassColor; - renderPipeline.needsUpdate = true; - }); - - const lighting = gui.addFolder('Lighting / Scene'); - lighting.add(volumetricLightingIntensity, 'value', 0, 2).name('fog intensity'); - lighting.add(smokeAmount, 'value', 0, 3).name('smoke amount'); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - const delta = timer.getDelta(); - - rectLight1.rotation.y += -delta; - rectLight2.rotation.y += delta * 0.5; - rectLight3.rotation.y += delta; - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_volume_lighting_traa.ts b/examples-testing/examples/webgpu_volume_lighting_traa.ts deleted file mode 100644 index c5cc55e71..000000000 --- a/examples-testing/examples/webgpu_volume_lighting_traa.ts +++ /dev/null @@ -1,298 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { - vec2, - vec3, - Fn, - texture3D, - screenUV, - uniform, - screenCoordinate, - pass, - depthPass, - mrt, - output, - velocity, - fract, - interleavedGradientNoise, -} from 'three/tsl'; - -import { traa } from 'three/addons/tsl/display/TRAANode.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { ImprovedNoise } from 'three/addons/math/ImprovedNoise.js'; -import { TeapotGeometry } from 'three/addons/geometries/TeapotGeometry.js'; - -// Halton sequence for temporal offset - matches TRAA's 32-sample Halton jitter -// This creates optimal low-discrepancy distribution that accumulates well with TRAA -function halton(index, base) { - let result = 0; - let f = 1; - - while (index > 0) { - f /= base; - result += f * (index % base); - index = Math.floor(index / base); - } - - return result; -} - -// Generate 32 Halton offsets (base 2, 3) - same length as TRAA -const _haltonOffsets = Array.from({ length: 32 }, (_, i) => [halton(i + 1, 2), halton(i + 1, 3)]); - -let renderer, scene, camera; -let volumetricMesh, teapot, pointLight, spotLight; -let renderPipeline; -let temporalOffset, temporalRotation, shaderTime; -let params; - -init(); - -function createTexture3D() { - let i = 0; - - const size = 128; - const data = new Uint8Array(size * size * size); - - const scale = 10; - const perlin = new ImprovedNoise(); - - const repeatFactor = 5.0; - - for (let z = 0; z < size; z++) { - for (let y = 0; y < size; y++) { - for (let x = 0; x < size; x++) { - const nx = (x / size) * repeatFactor; - const ny = (y / size) * repeatFactor; - const nz = (z / size) * repeatFactor; - - const noiseValue = perlin.noise(nx * scale, ny * scale, nz * scale); - - data[i] = 128 + 128 * noiseValue; - - i++; - } - } - } - - const texture = new THREE.Data3DTexture(data, size, size, size); - texture.format = THREE.RedFormat; - texture.minFilter = THREE.LinearFilter; - texture.magFilter = THREE.LinearFilter; - texture.wrapS = THREE.RepeatWrapping; - texture.wrapT = THREE.RepeatWrapping; - texture.unpackAlignment = 1; - texture.needsUpdate = true; - - return texture; -} - -function init() { - renderer = new THREE.WebGPURenderer(); - // renderer.setPixelRatio( window.devicePixelRatio ); // Disable DPR for performance - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.toneMapping = THREE.NeutralToneMapping; - renderer.toneMappingExposure = 2; - renderer.shadowMap.enabled = true; - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(-8, 1, -6); - - const controls = new OrbitControls(camera, renderer.domElement); - controls.maxDistance = 40; - controls.minDistance = 2; - - // Volumetric Fog Area - - const noiseTexture3D = createTexture3D(); - - const smokeAmount = uniform(2); - - const volumetricMaterial = new THREE.VolumeNodeMaterial(); - volumetricMaterial.steps = 12; - volumetricMaterial.transparent = true; - volumetricMaterial.blending = THREE.AdditiveBlending; - - // Temporal dithering using Interleaved Gradient Noise (IGN) + Halton sequence - temporalOffset = uniform(0); - temporalRotation = uniform(0); - shaderTime = uniform(0); - - const temporalJitter2D = vec2(temporalOffset, temporalRotation); - volumetricMaterial.offsetNode = fract( - interleavedGradientNoise(screenCoordinate.add(temporalJitter2D.mul(100))).add(temporalOffset), - ); - volumetricMaterial.scatteringNode = Fn(({ positionRay }) => { - const timeScaled = vec3(shaderTime, 0, shaderTime.mul(0.3)); - - const sampleGrain = (scale, timeScale = 1) => - texture3D(noiseTexture3D, positionRay.add(timeScaled.mul(timeScale)).mul(scale).mod(1), 0).r.add(0.5); - - let density = sampleGrain(0.1); - density = density.mul(sampleGrain(0.05, 1)); - density = density.mul(sampleGrain(0.02, 2)); - - return smokeAmount.mix(1, density); - }); - - volumetricMesh = new THREE.Mesh(new THREE.BoxGeometry(20, 10, 20), volumetricMaterial); - volumetricMesh.receiveShadow = true; - volumetricMesh.position.y = 2; - scene.add(volumetricMesh); - - // Objects - - teapot = new THREE.Mesh( - new TeapotGeometry(0.8, 18), - new THREE.MeshStandardMaterial({ color: 0xffffff, side: THREE.DoubleSide }), - ); - teapot.castShadow = true; - scene.add(teapot); - - const floor = new THREE.Mesh( - new THREE.PlaneGeometry(100, 100), - new THREE.MeshStandardMaterial({ color: 0xffffff }), - ); - floor.rotation.x = -Math.PI / 2; - floor.position.y = -3; - floor.receiveShadow = true; - scene.add(floor); - - // Lights - - pointLight = new THREE.PointLight(0xf9bb50, 3, 100); - pointLight.castShadow = true; - pointLight.position.set(0, 1.4, 0); - scene.add(pointLight); - - spotLight = new THREE.SpotLight(0xffffff, 100); - spotLight.position.set(2.5, 5, 2.5); - spotLight.angle = Math.PI / 6; - spotLight.penumbra = 1; - spotLight.decay = 2; - spotLight.distance = 0; - spotLight.map = new THREE.TextureLoader().setPath('textures/').load('colors.png'); - spotLight.castShadow = true; - spotLight.shadow.intensity = 0.98; - spotLight.shadow.mapSize.width = 1024; - spotLight.shadow.mapSize.height = 1024; - spotLight.shadow.camera.near = 1; - spotLight.shadow.camera.far = 15; - spotLight.shadow.focus = 1; - scene.add(spotLight); - - // Render Pipeline - - renderPipeline = new THREE.RenderPipeline(renderer); - - const volumetricIntensity = uniform(1); - - // Pre-Pass: Opaque objects only (volumetric is transparent, excluded automatically) - - const prePass = depthPass(scene, camera); - prePass.name = 'Pre Pass'; - prePass.transparent = false; - - const prePassDepth = prePass.getTextureNode('depth').toInspector('Depth', () => prePass.getLinearDepthNode()); - - // Apply depth to volumetric material for proper occlusion - - volumetricMaterial.depthNode = prePassDepth.sample(screenUV); - - // Scene Pass: Full scene including volumetric with MRT - - const scenePass = pass(scene, camera).toInspector('Scene'); - scenePass.name = 'Scene Pass'; - scenePass.setMRT( - mrt({ - output: output, - velocity: velocity, - }), - ); - - const scenePassColor = scenePass.getTextureNode().toInspector('Output'); - const scenePassVelocity = scenePass.getTextureNode('velocity').toInspector('Velocity'); - - // TRAA with scene pass depth/velocity (includes volumetric) - - const traaPass = traa(scenePassColor, prePassDepth, scenePassVelocity, camera); - - renderPipeline.outputNode = traaPass; - - // GUI - - params = { - traa: true, - animated: true, - }; - - const gui = renderer.inspector.createParameters('Volumetric Lighting'); - - gui.add(params, 'animated'); - gui.add(params, 'traa').name('TRAA').onChange(updatePostProcessing); - - const rayMarching = gui.addFolder('Ray Marching'); - rayMarching.add(volumetricMaterial, 'steps', 2, 16, 1).name('step count'); - - function updatePostProcessing() { - renderPipeline.outputNode = params.traa ? traaPass : scenePassColor; - renderPipeline.needsUpdate = true; - } - - const lighting = gui.addFolder('Lighting / Scene'); - lighting.add(pointLight, 'intensity', 0, 6).name('light intensity'); - lighting.add(spotLight, 'intensity', 0, 200).name('spot intensity'); - lighting.add(volumetricIntensity, 'value', 0, 2).name('volumetric intensity'); - lighting.add(smokeAmount, 'value', 0, 3).name('smoke amount'); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -let frameCount = 0; -let animationTime = 0; -let lastTime = performance.now(); - -function animate() { - const currentTime = performance.now(); - const delta = (currentTime - lastTime) * 0.001; - lastTime = currentTime; - - // Update temporal uniforms - synced with TRAA's Halton sequence for optimal accumulation - const haltonIndex = frameCount % 32; - temporalOffset.value = _haltonOffsets[haltonIndex][0]; - temporalRotation.value = _haltonOffsets[haltonIndex][1]; - frameCount++; - - if (params.animated) { - animationTime += delta; - } - - shaderTime.value = animationTime; - - const scale = 2.4; - - pointLight.position.x = Math.sin(animationTime * 0.7) * scale; - pointLight.position.y = Math.cos(animationTime * 0.5) * scale; - pointLight.position.z = Math.cos(animationTime * 0.3) * scale; - - spotLight.position.x = Math.cos(animationTime * 0.3) * scale; - spotLight.lookAt(0, 0, 0); - - teapot.rotation.y = animationTime * 0.2; - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_volume_perlin.ts b/examples-testing/examples/webgpu_volume_perlin.ts deleted file mode 100644 index fb6f2bbd5..000000000 --- a/examples-testing/examples/webgpu_volume_perlin.ts +++ /dev/null @@ -1,114 +0,0 @@ -import * as THREE from 'three/webgpu'; -import { Break, If, vec3, vec4, texture3D, uniform, Fn } from 'three/tsl'; - -import { RaymarchingBox } from 'three/addons/tsl/utils/Raymarching.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { ImprovedNoise } from 'three/addons/math/ImprovedNoise.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -let renderer, scene, camera; -let mesh; - -init(); - -function init() { - renderer = new THREE.WebGPURenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(0, 0, 2); - - new OrbitControls(camera, renderer.domElement); - - // Texture - - const size = 128; - const data = new Uint8Array(size * size * size); - - let i = 0; - const perlin = new ImprovedNoise(); - const vector = new THREE.Vector3(); - - for (let z = 0; z < size; z++) { - for (let y = 0; y < size; y++) { - for (let x = 0; x < size; x++) { - vector.set(x, y, z).divideScalar(size); - - const d = perlin.noise(vector.x * 6.5, vector.y * 6.5, vector.z * 6.5); - - data[i++] = d * 128 + 128; - } - } - } - - const texture = new THREE.Data3DTexture(data, size, size, size); - texture.format = THREE.RedFormat; - texture.minFilter = THREE.LinearFilter; - texture.magFilter = THREE.LinearFilter; - texture.unpackAlignment = 1; - texture.needsUpdate = true; - - // Shader - - const opaqueRaymarchingTexture = Fn(({ texture, steps, threshold }) => { - const finalColor = vec4(0).toVar(); - - RaymarchingBox(steps, ({ positionRay }) => { - const mapValue = texture.sample(positionRay.add(0.5)).r.toVar(); - - If(mapValue.greaterThan(threshold), () => { - const p = vec3(positionRay).add(0.5); - - finalColor.rgb.assign(texture.normal(p).mul(0.5).add(positionRay.mul(1.5).add(0.25))); - finalColor.a.assign(1); - Break(); - }); - }); - - return finalColor; - }); - - // - - const threshold = uniform(0.6); - const steps = uniform(200); - - const material = new THREE.NodeMaterial(); - material.colorNode = opaqueRaymarchingTexture({ - texture: texture3D(texture, null, 0), - steps, - threshold, - }); - material.side = THREE.BackSide; - material.transparent = true; - - mesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), material); - scene.add(mesh); - - // - - const gui = renderer.inspector.createParameters('Parameters'); - gui.add(threshold, 'value', 0, 1, 0.01).name('threshold'); - gui.add(steps, 'value', 0, 300, 1).name('steps'); - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_water.ts b/examples-testing/examples/webgpu_water.ts deleted file mode 100644 index 88a9e5f60..000000000 --- a/examples-testing/examples/webgpu_water.ts +++ /dev/null @@ -1,199 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { pass, mrt, output, emissive, renderOutput } from 'three/tsl'; -import { bloom } from 'three/addons/tsl/display/BloomNode.js'; -import { fxaa } from 'three/addons/tsl/display/FXAANode.js'; - -import { Inspector } from 'three/addons/inspector/Inspector.js'; - -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; - -import { WaterMesh } from 'three/addons/objects/Water2Mesh.js'; -import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -let scene, camera, renderer, water, renderPipeline, controls; - -const params = { - color: '#99e0ff', - scale: 2, - flowX: 1, - flowY: 1, -}; - -init(); - -async function init() { - scene = new THREE.Scene(); - - const loader = new UltraHDRLoader(); - loader.load('textures/equirectangular/moonless_golf_2k.hdr.jpg', function (texture) { - texture.mapping = THREE.EquirectangularReflectionMapping; - texture.needsUpdate = true; - - scene.background = texture; - scene.environment = texture; - }); - - // camera - - camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 200); - camera.position.set(-20, 6, -30); - - // asset loading - - const dracoLoader = new DRACOLoader(); - dracoLoader.setDecoderPath('jsm/libs/draco/gltf/'); - - const gltfLoader = new GLTFLoader(); - gltfLoader.setDRACOLoader(dracoLoader); - - const textureLoader = new THREE.TextureLoader(); - - const [gltf, normalMap0, normalMap1] = await Promise.all([ - gltfLoader.loadAsync('models/gltf/pool.glb'), - textureLoader.loadAsync('textures/water/Water_1_M_Normal.jpg'), - textureLoader.loadAsync('textures/water/Water_2_M_Normal.jpg'), - ]); - - gltf.scene.position.z = 2; - gltf.scene.scale.setScalar(0.1); - scene.add(gltf.scene); - - // water - - normalMap0.wrapS = normalMap0.wrapT = THREE.RepeatWrapping; - normalMap1.wrapS = normalMap1.wrapT = THREE.RepeatWrapping; - - const waterGeometry = new THREE.PlaneGeometry(30, 40); - - water = new WaterMesh(waterGeometry, { - color: params.color, - scale: params.scale, - flowDirection: new THREE.Vector2(params.flowX, params.flowY), - normalMap0: normalMap0, - normalMap1: normalMap1, - }); - - water.position.set(0, 0.2, -2); - water.rotation.x = Math.PI * -0.5; - water.renderOrder = Infinity; - scene.add(water); - - // floor - - const floorGeometry = new THREE.PlaneGeometry(1, 1); - floorGeometry.rotateX(-Math.PI * 0.5); - const floorMaterial = new THREE.MeshStandardMaterial({ - color: 0x444444, - roughness: 1, - metalness: 0, - side: THREE.DoubleSide, - }); - - { - const floor = new THREE.Mesh(floorGeometry, floorMaterial); - floor.position.set(20, 0, 0); - floor.scale.set(15, 1, 80); - scene.add(floor); - } - - { - const floor = new THREE.Mesh(floorGeometry, floorMaterial); - floor.position.set(-20, 0, 0); - floor.scale.set(15, 1, 80); - scene.add(floor); - } - - { - const floor = new THREE.Mesh(floorGeometry, floorMaterial); - floor.position.set(0, 0, 30); - floor.scale.set(30, 1, 20); - scene.add(floor); - } - - { - const floor = new THREE.Mesh(floorGeometry, floorMaterial); - floor.position.set(0, 0, -30); - floor.scale.set(30, 1, 20); - scene.add(floor); - } - - // renderer - - renderer = new THREE.WebGPURenderer(); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setAnimationLoop(animate); - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = 0.5; - renderer.inspector = new Inspector(); - document.body.appendChild(renderer.domElement); - - // postprocessing - - renderPipeline = new THREE.RenderPipeline(renderer); - renderPipeline.outputColorTransform = false; - - const scenePass = pass(scene, camera); - scenePass.setMRT( - mrt({ - output, - emissive, - }), - ); - - const beautyPass = scenePass.getTextureNode(); - const emissivePass = scenePass.getTextureNode('emissive'); - - const bloomPass = bloom(emissivePass, 2); - - const outputPass = renderOutput(beautyPass.add(bloomPass)); - - const fxaaPass = fxaa(outputPass); - renderPipeline.outputNode = fxaaPass; - - // gui - - const gui = renderer.inspector.createParameters('Water'); - const waterNode = water.material.colorNode; - - gui.addColor(params, 'color').onChange(function (value) { - waterNode.color.value.set(value); - }); - gui.add(params, 'scale', 1, 10).onChange(function (value) { - waterNode.scale.value = value; - }); - gui.add(params, 'flowX', -1, 1, 0.01).onChange(function (value) { - waterNode.flowDirection.value.x = value; - waterNode.flowDirection.value.normalize(); - }); - gui.add(params, 'flowY', -1, 1, 0.01).onChange(function (value) { - waterNode.flowDirection.value.y = value; - waterNode.flowDirection.value.normalize(); - }); - - // - - controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - controls.target.set(0, 0, -5); - controls.update(); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - controls.update(); - - renderPipeline.render(); -} diff --git a/examples-testing/examples/webgpu_xr_cubes.ts b/examples-testing/examples/webgpu_xr_cubes.ts deleted file mode 100644 index d164eb843..000000000 --- a/examples-testing/examples/webgpu_xr_cubes.ts +++ /dev/null @@ -1,216 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { BoxLineGeometry } from 'three/addons/geometries/BoxLineGeometry.js'; -import { XRButton } from 'three/addons/webxr/XRButton.js'; -import { XRControllerModelFactory } from 'three/addons/webxr/XRControllerModelFactory.js'; - -const timer = new THREE.Timer(); -timer.connect(document); - -let container; -let camera, scene, raycaster, renderer; - -let room; - -let controller, controllerGrip; -let INTERSECTED; - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x505050); - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 10); - camera.position.set(0, 1.6, 3); - scene.add(camera); - - room = new THREE.LineSegments( - new BoxLineGeometry(6, 6, 6, 10, 10, 10).translate(0, 3, 0), - new THREE.LineBasicMaterial({ color: 0xbcbcbc }), - ); - scene.add(room); - - scene.add(new THREE.HemisphereLight(0xa5a5a5, 0x898989, 3)); - - const light = new THREE.DirectionalLight(0xffffff, 3); - light.position.set(1, 1, 1).normalize(); - scene.add(light); - - const geometry = new THREE.BoxGeometry(0.15, 0.15, 0.15); - - for (let i = 0; i < 200; i++) { - const object = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({ color: Math.random() * 0xffffff })); - - object.position.x = Math.random() * 4 - 2; - object.position.y = Math.random() * 4; - object.position.z = Math.random() * 4 - 2; - - object.rotation.x = Math.random() * 2 * Math.PI; - object.rotation.y = Math.random() * 2 * Math.PI; - object.rotation.z = Math.random() * 2 * Math.PI; - - object.scale.x = Math.random() + 0.5; - object.scale.y = Math.random() + 0.5; - object.scale.z = Math.random() + 0.5; - - object.userData.velocity = new THREE.Vector3(); - object.userData.velocity.x = Math.random() * 0.01 - 0.005; - object.userData.velocity.y = Math.random() * 0.01 - 0.005; - object.userData.velocity.z = Math.random() * 0.01 - 0.005; - - room.add(object); - } - - raycaster = new THREE.Raycaster(); - - renderer = new THREE.WebGPURenderer({ - antialias: true, - forceWebGL: true, - outputBufferType: THREE.UnsignedByteType, - multiview: true, - }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.xr.enabled = true; - container.appendChild(renderer.domElement); - - // - - function onSelectStart() { - this.userData.isSelecting = true; - } - - function onSelectEnd() { - this.userData.isSelecting = false; - } - - controller = renderer.xr.getController(0); - controller.addEventListener('selectstart', onSelectStart); - controller.addEventListener('selectend', onSelectEnd); - controller.addEventListener('connected', function (event) { - const targetRayMode = event.data.targetRayMode; - - if (targetRayMode === 'tracked-pointer' || targetRayMode === 'gaze') { - this.add(buildController(event.data)); - } - }); - controller.addEventListener('disconnected', function () { - this.remove(this.children[0]); - }); - scene.add(controller); - - const controllerModelFactory = new XRControllerModelFactory(); - - controllerGrip = renderer.xr.getControllerGrip(0); - controllerGrip.add(controllerModelFactory.createControllerModel(controllerGrip)); - scene.add(controllerGrip); - - window.addEventListener('resize', onWindowResize); - - // - - document.body.appendChild(XRButton.createButton(renderer)); -} - -function buildController(data) { - let geometry, material; - - switch (data.targetRayMode) { - case 'tracked-pointer': - geometry = new THREE.BufferGeometry(); - geometry.setAttribute('position', new THREE.Float32BufferAttribute([0, 0, 0, 0, 0, -1], 3)); - geometry.setAttribute('color', new THREE.Float32BufferAttribute([0.5, 0.5, 0.5, 0, 0, 0], 3)); - - material = new THREE.LineBasicMaterial({ vertexColors: true, blending: THREE.AdditiveBlending }); - - return new THREE.Line(geometry, material); - - case 'gaze': - geometry = new THREE.RingGeometry(0.02, 0.04, 32).translate(0, 0, -1); - material = new THREE.MeshBasicMaterial({ opacity: 0.5, transparent: true }); - return new THREE.Mesh(geometry, material); - } -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - timer.update(); - - const delta = timer.getDelta() * 60; - - if (controller.userData.isSelecting === true) { - const cube = room.children[0]; - room.remove(cube); - - cube.position.copy(controller.position); - cube.userData.velocity.x = (Math.random() - 0.5) * 0.02 * delta; - cube.userData.velocity.y = (Math.random() - 0.5) * 0.02 * delta; - cube.userData.velocity.z = (Math.random() * 0.01 - 0.05) * delta; - cube.userData.velocity.applyQuaternion(controller.quaternion); - room.add(cube); - } - - // find intersections - - raycaster.setFromXRController(controller); - - const intersects = raycaster.intersectObjects(room.children, false); - - if (intersects.length > 0) { - if (INTERSECTED != intersects[0].object) { - if (INTERSECTED) INTERSECTED.material.emissive.setHex(INTERSECTED.currentHex); - - INTERSECTED = intersects[0].object; - INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex(); - INTERSECTED.material.emissive.setHex(0xff0000); - } - } else { - if (INTERSECTED) INTERSECTED.material.emissive.setHex(INTERSECTED.currentHex); - - INTERSECTED = undefined; - } - - // Keep cubes inside room - - for (let i = 0; i < room.children.length; i++) { - const cube = room.children[i]; - - cube.userData.velocity.multiplyScalar(1 - 0.001 * delta); - - cube.position.add(cube.userData.velocity); - - if (cube.position.x < -3 || cube.position.x > 3) { - cube.position.x = THREE.MathUtils.clamp(cube.position.x, -3, 3); - cube.userData.velocity.x = -cube.userData.velocity.x; - } - - if (cube.position.y < 0 || cube.position.y > 6) { - cube.position.y = THREE.MathUtils.clamp(cube.position.y, 0, 6); - cube.userData.velocity.y = -cube.userData.velocity.y; - } - - if (cube.position.z < -3 || cube.position.z > 3) { - cube.position.z = THREE.MathUtils.clamp(cube.position.z, -3, 3); - cube.userData.velocity.z = -cube.userData.velocity.z; - } - - cube.rotation.x += cube.userData.velocity.x * 2 * delta; - cube.rotation.y += cube.userData.velocity.y * 2 * delta; - cube.rotation.z += cube.userData.velocity.z * 2 * delta; - } - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_xr_native_layers.ts b/examples-testing/examples/webgpu_xr_native_layers.ts deleted file mode 100644 index cd7f3fb2d..000000000 --- a/examples-testing/examples/webgpu_xr_native_layers.ts +++ /dev/null @@ -1,638 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { BoxLineGeometry } from 'three/addons/geometries/BoxLineGeometry.js'; -import { VRButton } from 'three/addons/webxr/VRButton.js'; -import { XRControllerModelFactory } from 'three/addons/webxr/XRControllerModelFactory.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { InteractiveGroup } from 'three/addons/interactive/InteractiveGroup.js'; -import { - RollerCoasterGeometry, - RollerCoasterShadowGeometry, - RollerCoasterLiftersGeometry, - TreesGeometry, - SkyGeometry, -} from 'three/addons/misc/RollerCoaster.js'; -import { HTMLMesh } from 'three/addons/interactive/HTMLMesh.js'; -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - -let camera, scene, renderer; -let controller1, controller2; -let controllerGrip1, controllerGrip2; - -let room; - -let count = 0; -const radius = 0.08; -let normal = new THREE.Vector3(); -const relativeVelocity = new THREE.Vector3(); - -const timer = new THREE.Timer(); -timer.connect(document); -const funfairs = []; -const train = new THREE.Object3D(); -const rcdelta = timer.getDelta() * 0.8; // slow down simulation -const PI2 = Math.PI * 2; -let rccamera = null; -let rcscene = null; - -const tempMatrix = new THREE.Matrix4(); -let raycaster = null; - -const curve = (function () { - const vector = new THREE.Vector3(); - const vector2 = new THREE.Vector3(); - - return { - getPointAt: function (t) { - t = t * PI2; - - const x = Math.sin(t * 3) * Math.cos(t * 4) * 50; - const y = Math.sin(t * 10) * 2 + Math.cos(t * 17) * 2 + 5; - const z = Math.sin(t) * Math.sin(t * 4) * 50; - - return vector.set(x, y, z).multiplyScalar(2); - }, - - getTangentAt: function (t) { - const delta = 0.0001; - const t1 = Math.max(0, t - delta); - const t2 = Math.min(1, t + delta); - - return vector2.copy(this.getPointAt(t2)).sub(this.getPointAt(t1)).normalize(); - }, - }; -})(); - -let horseCamera = null; -let horseScene = null; -let horseMixer = null; -let horseTheta = 0; -let horseMesh = null; -const horseRadius = 600; - -let guiScene = null; -let guiCamera = null; -let guiGroup = null; - -let rollercoasterLayer = null; -let horseLayer = null; -let guiLayer = null; - -const parameters = { - radius: 0.6, - tube: 0.2, - tubularSegments: 150, - radialSegments: 20, - p: 2, - q: 3, - thickness: 0.5, -}; - -init(); - -function getIntersections(controller) { - tempMatrix.identity().extractRotation(controller.matrixWorld); - - raycaster.ray.origin.setFromMatrixPosition(controller.matrixWorld); - raycaster.ray.direction.set(0, 0, -1).applyMatrix4(tempMatrix); - - return raycaster.intersectObjects(scene.children, false); -} - -function init() { - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x505050); - - raycaster = new THREE.Raycaster(); - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 10); - camera.position.set(0, 1.6, 3); - - room = new THREE.LineSegments( - new BoxLineGeometry(6, 6, 6, 10, 10, 10), - new THREE.LineBasicMaterial({ color: 0x808080 }), - ); - room.geometry.translate(0, 3, 0); - scene.add(room); - - scene.add(new THREE.HemisphereLight(0x606060, 0x404040)); - - const light = new THREE.DirectionalLight(0xffffff); - light.position.set(1, 1, 1).normalize(); - scene.add(light); - - const geometry = new THREE.IcosahedronGeometry(radius, 3); - - for (let i = 0; i < 200; i++) { - const object = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({ color: Math.random() * 0xffffff })); - - object.position.x = Math.random() * 4 - 2; - object.position.y = Math.random() * 4; - object.position.z = Math.random() * 4 - 2; - - object.userData.velocity = new THREE.Vector3(); - object.userData.velocity.x = Math.random() * 0.01 - 0.005; - object.userData.velocity.y = Math.random() * 0.01 - 0.005; - object.userData.velocity.z = Math.random() * 0.01 - 0.005; - - room.add(object); - } - - // - - renderer = new THREE.WebGPURenderer({ - antialias: true, - forceWebGL: true, - outputBufferType: THREE.UnsignedByteType, - multiview: true, - }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(render); - renderer.xr.enabled = true; - document.body.appendChild(renderer.domElement); - - // - - document.body.appendChild(VRButton.createButton(renderer)); - - // controllers - - function onSqueezeStart() { - this.userData.isSelecting = true; - } - - function onSqueezeEnd() { - this.userData.isSelecting = false; - } - - function onSelectStart(event) { - const controller = event.target; - - const intersections = getIntersections(controller); - let hadSelection = false; - - for (let x = 0; x < intersections.length; x++) { - if (intersections[x].object == horseLayer) { - horseLayer.visible = false; - hadSelection = true; - } - - if (intersections[x].object == rollercoasterLayer) { - controller.attach(rollercoasterLayer); - hadSelection = true; - } - - if (intersections[x].object == guiLayer) { - const uv = intersections[x].uv; - guiGroup.children[0].dispatchEvent({ - type: 'mousedown', - data: { x: uv.x, y: 1 - uv.y }, - target: guiGroup, - }); - hadSelection = true; - } - } - - this.userData.isSelecting = hadSelection === false; - } - - function onSelectEnd() { - horseLayer.visible = true; - scene.attach(rollercoasterLayer); - guiGroup.children[0].dispatchEvent({ type: 'mouseup', data: { x: 0, y: 0 }, target: guiGroup }); - this.userData.isSelecting = false; - } - - controller1 = renderer.xr.getController(0); - controller1.addEventListener('selectstart', onSelectStart); - controller1.addEventListener('selectend', onSelectEnd); - controller1.addEventListener('squeezestart', onSqueezeStart); - controller1.addEventListener('squeezeend', onSqueezeEnd); - controller1.addEventListener('connected', function (event) { - this.add(buildController(event.data)); - }); - controller1.addEventListener('disconnected', function () { - this.remove(this.children[0]); - }); - scene.add(controller1); - - controller2 = renderer.xr.getController(1); - controller2.addEventListener('selectstart', onSelectStart); - controller2.addEventListener('selectend', onSelectEnd); - controller2.addEventListener('squeezestart', onSqueezeStart); - controller2.addEventListener('squeezeend', onSqueezeEnd); - controller2.addEventListener('connected', function (event) { - this.add(buildController(event.data)); - }); - controller2.addEventListener('disconnected', function () { - this.remove(this.children[0]); - }); - scene.add(controller2); - - // The XRControllerModelFactory will automatically fetch controller models - // that match what the user is holding as closely as possible. The models - // should be attached to the object returned from getControllerGrip in - // order to match the orientation of the held device. - - const controllerModelFactory = new XRControllerModelFactory(); - - controllerGrip1 = renderer.xr.getControllerGrip(0); - controllerGrip1.add(controllerModelFactory.createControllerModel(controllerGrip1)); - scene.add(controllerGrip1); - - controllerGrip2 = renderer.xr.getControllerGrip(1); - controllerGrip2.add(controllerModelFactory.createControllerModel(controllerGrip2)); - scene.add(controllerGrip2); - - // - - window.addEventListener('resize', onWindowResize); - - // set up rollercoaster - rollercoasterLayer = renderer.xr.createCylinderLayer( - 1, - Math.PI / 2, - 2, - new THREE.Vector3(0, 1.5, -0.5), - new THREE.Quaternion(), - 1500, - 1000, - renderRollercoaster, - ); - scene.add(rollercoasterLayer); - - rcscene = new THREE.Scene(); - rcscene.background = new THREE.Color(0xf0f0ff); - - const rclight = new THREE.HemisphereLight(0xfff0f0, 0x606066); - rclight.position.set(1, 1, 1); - rcscene.add(rclight); - - rcscene.add(train); - - rccamera = new THREE.PerspectiveCamera(50, 1, 0.1, 500); - train.add(rccamera); - - // environment - - let rcgeometry = new THREE.PlaneGeometry(500, 500, 15, 15); - rcgeometry.rotateX(-Math.PI / 2); - - const positions = rcgeometry.attributes.position.array; - const vertex = new THREE.Vector3(); - - for (let i = 0; i < positions.length; i += 3) { - vertex.fromArray(positions, i); - - vertex.x += Math.random() * 10 - 5; - vertex.z += Math.random() * 10 - 5; - - const distance = vertex.distanceTo(scene.position) / 5 - 25; - vertex.y = Math.random() * Math.max(0, distance); - - vertex.toArray(positions, i); - } - - rcgeometry.computeVertexNormals(); - - let rcmaterial = new THREE.MeshLambertMaterial({ - color: 0x407000, - }); - - let rcmesh = new THREE.Mesh(rcgeometry, rcmaterial); - rcscene.add(rcmesh); - - rcgeometry = new TreesGeometry(rcmesh); - rcmaterial = new THREE.MeshBasicMaterial({ - side: THREE.DoubleSide, - vertexColors: true, - }); - rcmesh = new THREE.Mesh(rcgeometry, rcmaterial); - rcscene.add(rcmesh); - - rcgeometry = new SkyGeometry(); - rcmaterial = new THREE.MeshBasicMaterial({ color: 0xffffff }); - rcmesh = new THREE.Mesh(rcgeometry, rcmaterial); - rcscene.add(rcmesh); - - // - - rcgeometry = new RollerCoasterGeometry(curve, 1500); - rcmaterial = new THREE.MeshPhongMaterial({ - vertexColors: true, - }); - rcmesh = new THREE.Mesh(rcgeometry, rcmaterial); - rcscene.add(rcmesh); - - rcgeometry = new RollerCoasterLiftersGeometry(curve, 100); - rcmaterial = new THREE.MeshPhongMaterial(); - rcmesh = new THREE.Mesh(rcgeometry, rcmaterial); - rcmesh.position.y = 0.1; - rcscene.add(rcmesh); - - rcgeometry = new RollerCoasterShadowGeometry(curve, 500); - rcmaterial = new THREE.MeshBasicMaterial({ - color: 0x305000, - depthWrite: false, - transparent: true, - }); - rcmesh = new THREE.Mesh(rcgeometry, rcmaterial); - rcmesh.position.y = 0.1; - rcscene.add(rcmesh); - - // - - rcgeometry = new THREE.CylinderGeometry(10, 10, 5, 15); - rcmaterial = new THREE.MeshLambertMaterial({ - color: 0xff8080, - }); - rcmesh = new THREE.Mesh(rcgeometry, rcmaterial); - rcmesh.position.set(-80, 10, -70); - rcmesh.rotation.x = Math.PI / 2; - rcscene.add(rcmesh); - - funfairs.push(rcmesh); - - rcgeometry = new THREE.CylinderGeometry(5, 6, 4, 10); - rcmaterial = new THREE.MeshLambertMaterial({ - color: 0x8080ff, - }); - rcmesh = new THREE.Mesh(rcgeometry, rcmaterial); - rcmesh.position.set(50, 2, 30); - rcscene.add(rcmesh); - - funfairs.push(rcmesh); - - // set up horse animation - horseLayer = renderer.xr.createQuadLayer( - 1, - 1, - new THREE.Vector3(-1.5, 1.5, -1.5), - new THREE.Quaternion(), - 800, - 800, - renderQuad, - ); - scene.add(horseLayer); - - horseLayer.geometry = new THREE.CircleGeometry(0.5, 64); - - horseCamera = new THREE.PerspectiveCamera(50, 1, 1, 10000); - horseCamera.position.y = 300; - - horseScene = new THREE.Scene(); - horseScene.background = new THREE.Color(0xf0f0f0); - - // - - const light1 = new THREE.DirectionalLight(0xefefff, 1.5); - light1.position.set(1, 1, 1).normalize(); - horseScene.add(light1); - - const light2 = new THREE.DirectionalLight(0xffefef, 1.5); - light2.position.set(-1, -1, -1).normalize(); - horseScene.add(light2); - - const loader = new GLTFLoader(); - loader.load('models/gltf/Horse.glb', function (gltf) { - horseMesh = gltf.scene.children[0]; - horseMesh.scale.set(1.5, 1.5, 1.5); - horseScene.add(horseMesh); - - horseMixer = new THREE.AnimationMixer(horseMesh); - - horseMixer.clipAction(gltf.animations[0]).setDuration(1).play(); - }); - - function onChange() {} - - function onThicknessChange() {} - - // set up ui - guiScene = new THREE.Scene(); - guiScene.background = new THREE.Color(0x0); - - guiCamera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1); - guiScene.add(guiCamera); - - const gui = new GUI({ width: 300 }); - gui.add(parameters, 'radius', 0.0, 1.0).onChange(onChange); - gui.add(parameters, 'tube', 0.0, 1.0).onChange(onChange); - gui.add(parameters, 'tubularSegments', 10, 150, 1).onChange(onChange); - gui.add(parameters, 'radialSegments', 2, 20, 1).onChange(onChange); - gui.add(parameters, 'p', 1, 10, 1).onChange(onChange); - gui.add(parameters, 'q', 0, 10, 1).onChange(onChange); - gui.add(parameters, 'thickness', 0, 1).onChange(onThicknessChange); - gui.domElement.style.visibility = 'hidden'; - - guiGroup = new InteractiveGroup(); - guiScene.add(guiGroup); - - const mesh = new HTMLMesh(gui.domElement); - guiGroup.add(mesh); - - const bbox = new THREE.Box3().setFromObject(guiScene); - - guiLayer = renderer.xr.createQuadLayer( - 1.2, - 0.8, - new THREE.Vector3(1.5, 1.5, -1.5), - new THREE.Quaternion(), - 1280, - 800, - renderGui, - ); - scene.add(guiLayer); - - guiCamera.left = bbox.min.x; - guiCamera.right = bbox.max.x; - guiCamera.top = bbox.max.y; - guiCamera.bottom = bbox.min.y; - guiCamera.updateProjectionMatrix(); -} - -function renderGui() { - renderer.render(guiScene, guiCamera); -} - -function renderQuad() { - horseTheta += 0.1; - - horseCamera.position.x = horseRadius * Math.sin(THREE.MathUtils.degToRad(horseTheta)); - horseCamera.position.z = horseRadius * Math.cos(THREE.MathUtils.degToRad(horseTheta)); - - horseCamera.lookAt(0, 150, 0); - - if (horseMixer) { - const time = Date.now(); - - horseMixer.update((time - prevTime) * 0.001); - - prevTime = time; - } - - renderer.render(horseScene, horseCamera); -} - -const rcposition = new THREE.Vector3(); -const tangent = new THREE.Vector3(); - -const lookAt = new THREE.Vector3(); - -let rcvelocity = 0; -let progress = 0; - -let prevTime = performance.now(); - -function renderRollercoaster() { - const time = performance.now(); - for (let i = 0; i < funfairs.length; i++) { - funfairs[i].rotation.y = time * 0.0004; - } - - // - - progress += rcvelocity; - progress = progress % 1; - - rcposition.copy(curve.getPointAt(progress)); - rcposition.y += 0.3; - - train.position.copy(rcposition); - - tangent.copy(curve.getTangentAt(progress)); - - rcvelocity -= tangent.y * 0.0000001 * rcdelta; - rcvelocity = Math.max(0.00004, Math.min(0.0002, rcvelocity)); - - train.lookAt(lookAt.copy(rcposition).sub(tangent)); - - // - - renderer.render(rcscene, rccamera); -} - -function buildController(data) { - let geometry, material; - - switch (data.targetRayMode) { - case 'tracked-pointer': - geometry = new THREE.BufferGeometry(); - geometry.setAttribute('position', new THREE.Float32BufferAttribute([0, 0, 0, 0, 0, -1], 3)); - geometry.setAttribute('color', new THREE.Float32BufferAttribute([0.5, 0.5, 0.5, 0, 0, 0], 3)); - - material = new THREE.LineBasicMaterial({ vertexColors: true, blending: THREE.AdditiveBlending }); - - return new THREE.Line(geometry, material); - - case 'gaze': - geometry = new THREE.RingGeometry(0.02, 0.04, 32).translate(0, 0, -1); - material = new THREE.MeshBasicMaterial({ opacity: 0.5, transparent: true }); - return new THREE.Mesh(geometry, material); - } -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function handleController(controller) { - if (controller.userData.isSelecting) { - const object = room.children[count++]; - - object.position.copy(controller.position); - object.userData.velocity.x = (Math.random() - 0.5) * 3; - object.userData.velocity.y = (Math.random() - 0.5) * 3; - object.userData.velocity.z = Math.random() - 9; - object.userData.velocity.applyQuaternion(controller.quaternion); - - if (count === room.children.length) count = 0; - } - - const intersections = getIntersections(controller); - for (let x = 0; x < intersections.length; x++) { - if (intersections[x].object == guiLayer) { - const uv = intersections[x].uv; - guiGroup.children[0].dispatchEvent({ type: 'mousemove', data: { x: uv.x, y: 1 - uv.y }, target: guiGroup }); - } - } -} - -// - -function render() { - timer.update(); - - renderer.xr.renderLayers(); - - handleController(controller1); - handleController(controller2); - - // rotate horse - horseLayer.rotation.y -= 0.02; - - // - const delta = timer.getDelta() * 0.8; - - const range = 3 - radius; - - for (let i = 0; i < room.children.length; i++) { - const object = room.children[i]; - - object.position.x += object.userData.velocity.x * delta; - object.position.y += object.userData.velocity.y * delta; - object.position.z += object.userData.velocity.z * delta; - - // keep objects inside room - - if (object.position.x < -range || object.position.x > range) { - object.position.x = THREE.MathUtils.clamp(object.position.x, -range, range); - object.userData.velocity.x = -object.userData.velocity.x; - } - - if (object.position.y < radius || object.position.y > 6) { - object.position.y = Math.max(object.position.y, radius); - - object.userData.velocity.x *= 0.98; - object.userData.velocity.y = -object.userData.velocity.y * 0.8; - object.userData.velocity.z *= 0.98; - } - - if (object.position.z < -range || object.position.z > range) { - object.position.z = THREE.MathUtils.clamp(object.position.z, -range, range); - object.userData.velocity.z = -object.userData.velocity.z; - } - - for (let j = i + 1; j < room.children.length; j++) { - const object2 = room.children[j]; - - normal.copy(object.position).sub(object2.position); - - const distance = normal.length(); - - if (distance < 2 * radius) { - normal.multiplyScalar(0.5 * distance - radius); - - object.position.sub(normal); - object2.position.add(normal); - - normal.normalize(); - - relativeVelocity.copy(object.userData.velocity).sub(object2.userData.velocity); - - normal = normal.multiplyScalar(relativeVelocity.dot(normal)); - - object.userData.velocity.sub(normal); - object2.userData.velocity.add(normal); - } - } - - object.userData.velocity.y -= 9.8 * delta; - } - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webgpu_xr_rollercoaster.ts b/examples-testing/examples/webgpu_xr_rollercoaster.ts deleted file mode 100644 index 6035d0687..000000000 --- a/examples-testing/examples/webgpu_xr_rollercoaster.ts +++ /dev/null @@ -1,216 +0,0 @@ -import * as THREE from 'three/webgpu'; - -import { - RollerCoasterGeometry, - RollerCoasterShadowGeometry, - RollerCoasterLiftersGeometry, - TreesGeometry, - SkyGeometry, -} from 'three/addons/misc/RollerCoaster.js'; -import { VRButton } from 'three/addons/webxr/VRButton.js'; - -let mesh, material, geometry; - -const renderer = new THREE.WebGPURenderer({ - antialias: true, - forceWebGL: true, - outputBufferType: THREE.UnsignedByteType, - multiview: false, -}); -renderer.setPixelRatio(window.devicePixelRatio); -renderer.setSize(window.innerWidth, window.innerHeight); -renderer.setAnimationLoop(animate); -renderer.xr.enabled = true; -renderer.xr.setReferenceSpaceType('local'); -document.body.appendChild(renderer.domElement); - -document.body.appendChild(VRButton.createButton(renderer)); - -// - -const scene = new THREE.Scene(); -scene.background = new THREE.Color(0xf0f0ff); - -const light = new THREE.HemisphereLight(0xfff0f0, 0x60606, 3); -light.position.set(1, 1, 1); -scene.add(light); - -const train = new THREE.Object3D(); -scene.add(train); - -const camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 500); -train.add(camera); - -// environment - -geometry = new THREE.PlaneGeometry(500, 500, 15, 15); -geometry.rotateX(-Math.PI / 2); - -const positions = geometry.attributes.position.array; -const vertex = new THREE.Vector3(); - -for (let i = 0; i < positions.length; i += 3) { - vertex.fromArray(positions, i); - - vertex.x += Math.random() * 10 - 5; - vertex.z += Math.random() * 10 - 5; - - const distance = vertex.distanceTo(scene.position) / 5 - 25; - vertex.y = Math.random() * Math.max(0, distance); - - vertex.toArray(positions, i); -} - -geometry.computeVertexNormals(); - -material = new THREE.MeshLambertMaterial({ - color: 0x407000, -}); - -mesh = new THREE.Mesh(geometry, material); -scene.add(mesh); - -geometry = new TreesGeometry(mesh); -material = new THREE.MeshBasicMaterial({ - side: THREE.DoubleSide, - vertexColors: true, -}); -mesh = new THREE.Mesh(geometry, material); -scene.add(mesh); - -geometry = new SkyGeometry(); -material = new THREE.MeshBasicMaterial({ color: 0xffffff }); -mesh = new THREE.Mesh(geometry, material); -scene.add(mesh); - -// - -const PI2 = Math.PI * 2; - -const curve = (function () { - const vector = new THREE.Vector3(); - const vector2 = new THREE.Vector3(); - - return { - getPointAt: function (t) { - t = t * PI2; - - const x = Math.sin(t * 3) * Math.cos(t * 4) * 50; - const y = Math.sin(t * 10) * 2 + Math.cos(t * 17) * 2 + 5; - const z = Math.sin(t) * Math.sin(t * 4) * 50; - - return vector.set(x, y, z).multiplyScalar(2); - }, - - getTangentAt: function (t) { - const delta = 0.0001; - const t1 = Math.max(0, t - delta); - const t2 = Math.min(1, t + delta); - - return vector2.copy(this.getPointAt(t2)).sub(this.getPointAt(t1)).normalize(); - }, - }; -})(); - -geometry = new RollerCoasterGeometry(curve, 1500); -material = new THREE.MeshPhongMaterial({ - vertexColors: true, -}); -mesh = new THREE.Mesh(geometry, material); -scene.add(mesh); - -geometry = new RollerCoasterLiftersGeometry(curve, 100); -material = new THREE.MeshPhongMaterial(); -mesh = new THREE.Mesh(geometry, material); -mesh.position.y = 0.1; -scene.add(mesh); - -geometry = new RollerCoasterShadowGeometry(curve, 500); -material = new THREE.MeshBasicMaterial({ - color: 0x305000, - depthWrite: false, - transparent: true, -}); -mesh = new THREE.Mesh(geometry, material); -mesh.position.y = 0.1; -scene.add(mesh); - -const funfairs = []; - -// - -geometry = new THREE.CylinderGeometry(10, 10, 5, 15); -material = new THREE.MeshLambertMaterial({ - color: 0xff8080, -}); -mesh = new THREE.Mesh(geometry, material); -mesh.position.set(-80, 10, -70); -mesh.rotation.x = Math.PI / 2; -scene.add(mesh); - -funfairs.push(mesh); - -geometry = new THREE.CylinderGeometry(5, 6, 4, 10); -material = new THREE.MeshLambertMaterial({ - color: 0x8080ff, -}); -mesh = new THREE.Mesh(geometry, material); -mesh.position.set(50, 2, 30); -scene.add(mesh); - -funfairs.push(mesh); - -// - -window.addEventListener('resize', onWindowResize); - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -const position = new THREE.Vector3(); -const tangent = new THREE.Vector3(); - -const lookAt = new THREE.Vector3(); - -let velocity = 0; -let progress = 0; - -let prevTime = performance.now(); - -function animate() { - const time = performance.now(); - const delta = time - prevTime; - - for (let i = 0; i < funfairs.length; i++) { - funfairs[i].rotation.y = time * 0.0004; - } - - // - - progress += velocity; - progress = progress % 1; - - position.copy(curve.getPointAt(progress)); - position.y += 0.3; - - train.position.copy(position); - - tangent.copy(curve.getTangentAt(progress)); - - velocity -= tangent.y * 0.0000001 * delta; - velocity = Math.max(0.00004, Math.min(0.0002, velocity)); - - train.lookAt(lookAt.copy(position).sub(tangent)); - - // - - renderer.render(scene, camera); - - prevTime = time; -} diff --git a/examples-testing/examples/webxr_ar_cones.ts b/examples-testing/examples/webxr_ar_cones.ts deleted file mode 100644 index 95eb34393..000000000 --- a/examples-testing/examples/webxr_ar_cones.ts +++ /dev/null @@ -1,66 +0,0 @@ -import * as THREE from 'three'; -import { ARButton } from 'three/addons/webxr/ARButton.js'; - -let camera, scene, renderer; -let controller; - -init(); - -function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 20); - - const light = new THREE.HemisphereLight(0xffffff, 0xbbbbff, 3); - light.position.set(0.5, 1, 0.25); - scene.add(light); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.xr.enabled = true; - container.appendChild(renderer.domElement); - - // - - document.body.appendChild(ARButton.createButton(renderer)); - - // - - const geometry = new THREE.CylinderGeometry(0, 0.05, 0.2, 32).rotateX(Math.PI / 2); - - function onSelect() { - const material = new THREE.MeshPhongMaterial({ color: 0xffffff * Math.random() }); - const mesh = new THREE.Mesh(geometry, material); - mesh.position.set(0, 0, -0.3).applyMatrix4(controller.matrixWorld); - mesh.quaternion.setFromRotationMatrix(controller.matrixWorld); - scene.add(mesh); - } - - controller = renderer.xr.getController(0); - controller.addEventListener('select', onSelect); - scene.add(controller); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webxr_ar_hittest.ts b/examples-testing/examples/webxr_ar_hittest.ts deleted file mode 100644 index 009b4b976..000000000 --- a/examples-testing/examples/webxr_ar_hittest.ts +++ /dev/null @@ -1,119 +0,0 @@ -import * as THREE from 'three'; -import { ARButton } from 'three/addons/webxr/ARButton.js'; - -let container; -let camera, scene, renderer; -let controller1, controller2; - -let reticle; - -let hitTestSource = null; -let hitTestSourceRequested = false; - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 20); - - const light = new THREE.HemisphereLight(0xffffff, 0xbbbbff, 3); - light.position.set(0.5, 1, 0.25); - scene.add(light); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.xr.enabled = true; - container.appendChild(renderer.domElement); - - // - - document.body.appendChild(ARButton.createButton(renderer, { requiredFeatures: ['hit-test'] })); - - // - - const geometry = new THREE.CylinderGeometry(0.1, 0.1, 0.2, 32).translate(0, 0.1, 0); - - function onSelect() { - if (reticle.visible) { - const material = new THREE.MeshPhongMaterial({ color: 0xffffff * Math.random() }); - const mesh = new THREE.Mesh(geometry, material); - reticle.matrix.decompose(mesh.position, mesh.quaternion, mesh.scale); - mesh.scale.y = Math.random() * 2 + 1; - scene.add(mesh); - } - } - - controller1 = renderer.xr.getController(0); - controller1.addEventListener('select', onSelect); - scene.add(controller1); - - controller2 = renderer.xr.getController(1); - controller2.addEventListener('select', onSelect); - scene.add(controller2); - - reticle = new THREE.Mesh( - new THREE.RingGeometry(0.15, 0.2, 32).rotateX(-Math.PI / 2), - new THREE.MeshBasicMaterial(), - ); - reticle.matrixAutoUpdate = false; - reticle.visible = false; - scene.add(reticle); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate(timestamp, frame) { - if (frame) { - const referenceSpace = renderer.xr.getReferenceSpace(); - const session = renderer.xr.getSession(); - - if (hitTestSourceRequested === false) { - session.requestReferenceSpace('viewer').then(function (referenceSpace) { - session.requestHitTestSource({ space: referenceSpace }).then(function (source) { - hitTestSource = source; - }); - }); - - session.addEventListener('end', function () { - hitTestSourceRequested = false; - hitTestSource = null; - }); - - hitTestSourceRequested = true; - } - - if (hitTestSource) { - const hitTestResults = frame.getHitTestResults(hitTestSource); - - if (hitTestResults.length) { - const hit = hitTestResults[0]; - - reticle.visible = true; - reticle.matrix.fromArray(hit.getPose(referenceSpace).transform.matrix); - } else { - reticle.visible = false; - } - } - } - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webxr_ar_lighting.ts b/examples-testing/examples/webxr_ar_lighting.ts deleted file mode 100644 index 10f49f0bf..000000000 --- a/examples-testing/examples/webxr_ar_lighting.ts +++ /dev/null @@ -1,124 +0,0 @@ -import * as THREE from 'three'; -import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js'; -import { ARButton } from 'three/addons/webxr/ARButton.js'; -import { XREstimatedLight } from 'three/addons/webxr/XREstimatedLight.js'; - -let camera, scene, renderer; -let controller; -let defaultEnvironment; - -init(); - -function init() { - const container = document.createElement('div'); - document.body.appendChild(container); - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 20); - - const defaultLight = new THREE.HemisphereLight(0xffffff, 0xbbbbff, 1); - defaultLight.position.set(0.5, 1, 0.25); - scene.add(defaultLight); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.xr.enabled = true; - container.appendChild(renderer.domElement); - - // Don't add the XREstimatedLight to the scene initially. - // It doesn't have any estimated lighting values until an AR session starts. - - const xrLight = new XREstimatedLight(renderer); - - xrLight.addEventListener('estimationstart', () => { - // Swap the default light out for the estimated one once we start getting some estimated values. - scene.add(xrLight); - scene.remove(defaultLight); - - // The estimated lighting also provides an environment cubemap, which we can apply here. - if (xrLight.environment) { - scene.environment = xrLight.environment; - } - }); - - xrLight.addEventListener('estimationend', () => { - // Swap the lights back when we stop receiving estimated values. - scene.add(defaultLight); - scene.remove(xrLight); - - // Revert back to the default environment. - scene.environment = defaultEnvironment; - }); - - // - - new UltraHDRLoader().setPath('textures/equirectangular/').load('royal_esplanade_2k.hdr.jpg', function (texture) { - texture.mapping = THREE.EquirectangularReflectionMapping; - - defaultEnvironment = texture; - - scene.environment = defaultEnvironment; - }); - - // - - // In order for lighting estimation to work, 'light-estimation' must be included as either an optional or required feature. - document.body.appendChild(ARButton.createButton(renderer, { optionalFeatures: ['light-estimation'] })); - - // - - const ballGeometry = new THREE.SphereGeometry(0.175, 32, 32); - const ballGroup = new THREE.Group(); - ballGroup.position.z = -2; - - const rows = 3; - const cols = 3; - - for (let i = 0; i < rows; i++) { - for (let j = 0; j < cols; j++) { - const ballMaterial = new THREE.MeshStandardMaterial({ - color: 0xdddddd, - roughness: i / rows, - metalness: j / cols, - }); - const ballMesh = new THREE.Mesh(ballGeometry, ballMaterial); - ballMesh.position.set((i + 0.5 - rows * 0.5) * 0.4, (j + 0.5 - cols * 0.5) * 0.4, 0); - ballGroup.add(ballMesh); - } - } - - scene.add(ballGroup); - - // - - function onSelect() { - ballGroup.position.set(0, 0, -2).applyMatrix4(controller.matrixWorld); - ballGroup.quaternion.setFromRotationMatrix(controller.matrixWorld); - } - - controller = renderer.xr.getController(0); - controller.addEventListener('select', onSelect); - scene.add(controller); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webxr_ar_plane_detection.ts b/examples-testing/examples/webxr_ar_plane_detection.ts deleted file mode 100644 index 841b6b04b..000000000 --- a/examples-testing/examples/webxr_ar_plane_detection.ts +++ /dev/null @@ -1,46 +0,0 @@ -import * as THREE from 'three'; -import { ARButton } from 'three/addons/webxr/ARButton.js'; -import { XRPlanes } from 'three/addons/webxr/XRPlanes.js'; - -// - -const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); -renderer.setPixelRatio(window.devicePixelRatio); -renderer.setSize(window.innerWidth, window.innerHeight); -renderer.setAnimationLoop(animate); -renderer.xr.enabled = true; -document.body.appendChild(renderer.domElement); - -document.body.appendChild( - ARButton.createButton(renderer, { - requiredFeatures: ['plane-detection'], - }), -); - -window.addEventListener('resize', onWindowResize); - -// - -const scene = new THREE.Scene(); - -const planes = new XRPlanes(renderer); -scene.add(planes); - -const camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 20); - -const light = new THREE.HemisphereLight(0xffffff, 0xbbbbff, 3); -light.position.set(0.5, 1, 0.25); -scene.add(light); - -// - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webxr_vr_handinput.ts b/examples-testing/examples/webxr_vr_handinput.ts deleted file mode 100644 index d746e4582..000000000 --- a/examples-testing/examples/webxr_vr_handinput.ts +++ /dev/null @@ -1,126 +0,0 @@ -import * as THREE from 'three'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { VRButton } from 'three/addons/webxr/VRButton.js'; -import { XRControllerModelFactory } from 'three/addons/webxr/XRControllerModelFactory.js'; -import { XRHandModelFactory } from 'three/addons/webxr/XRHandModelFactory.js'; - -let container; -let camera, scene, renderer; -let hand1, hand2; -let controller1, controller2; -let controllerGrip1, controllerGrip2; - -let controls; - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x444444); - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 10); - camera.position.set(0, 1.6, 3); - - controls = new OrbitControls(camera, container); - controls.target.set(0, 1.6, 0); - controls.update(); - - const floorGeometry = new THREE.PlaneGeometry(4, 4); - const floorMaterial = new THREE.MeshStandardMaterial({ color: 0x666666 }); - const floor = new THREE.Mesh(floorGeometry, floorMaterial); - floor.rotation.x = -Math.PI / 2; - floor.receiveShadow = true; - scene.add(floor); - - scene.add(new THREE.HemisphereLight(0xbcbcbc, 0xa5a5a5, 3)); - - const light = new THREE.DirectionalLight(0xffffff, 3); - light.position.set(0, 6, 0); - light.castShadow = true; - light.shadow.camera.top = 2; - light.shadow.camera.bottom = -2; - light.shadow.camera.right = 2; - light.shadow.camera.left = -2; - light.shadow.mapSize.set(4096, 4096); - scene.add(light); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.shadowMap.enabled = true; - renderer.xr.enabled = true; - - container.appendChild(renderer.domElement); - - const sessionInit = { - requiredFeatures: ['hand-tracking'], - }; - - document.body.appendChild(VRButton.createButton(renderer, sessionInit)); - - // controllers - - controller1 = renderer.xr.getController(0); - scene.add(controller1); - - controller2 = renderer.xr.getController(1); - scene.add(controller2); - - const controllerModelFactory = new XRControllerModelFactory(); - const handModelFactory = new XRHandModelFactory(); - - // Hand 1 - controllerGrip1 = renderer.xr.getControllerGrip(0); - controllerGrip1.add(controllerModelFactory.createControllerModel(controllerGrip1)); - scene.add(controllerGrip1); - - hand1 = renderer.xr.getHand(0); - hand1.add(handModelFactory.createHandModel(hand1)); - - scene.add(hand1); - - // Hand 2 - controllerGrip2 = renderer.xr.getControllerGrip(1); - controllerGrip2.add(controllerModelFactory.createControllerModel(controllerGrip2)); - scene.add(controllerGrip2); - - hand2 = renderer.xr.getHand(1); - hand2.add(handModelFactory.createHandModel(hand2)); - scene.add(hand2); - - // - - const geometry = new THREE.BufferGeometry().setFromPoints([ - new THREE.Vector3(0, 0, 0), - new THREE.Vector3(0, 0, -1), - ]); - - const line = new THREE.Line(geometry); - line.name = 'line'; - line.scale.z = 5; - - controller1.add(line.clone()); - controller2.add(line.clone()); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} -// - -function animate() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webxr_vr_panorama.ts b/examples-testing/examples/webxr_vr_panorama.ts deleted file mode 100644 index 535e1c937..000000000 --- a/examples-testing/examples/webxr_vr_panorama.ts +++ /dev/null @@ -1,92 +0,0 @@ -import * as THREE from 'three'; -import { VRButton } from 'three/addons/webxr/VRButton.js'; - -let camera; -let renderer; -let scene; - -init(); - -function init() { - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.xr.enabled = true; - renderer.xr.setReferenceSpaceType('local'); - document.body.appendChild(renderer.domElement); - - document.body.appendChild(VRButton.createButton(renderer)); - - // - - scene = new THREE.Scene(); - - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000); - camera.layers.enable(1); - - const geometry = new THREE.BoxGeometry(100, 100, 100); - geometry.scale(1, 1, -1); - - const textures = getTexturesFromAtlasFile('textures/cube/sun_temple_stripe_stereo.jpg', 12); - - const materials = []; - - for (let i = 0; i < 6; i++) { - materials.push(new THREE.MeshBasicMaterial({ map: textures[i] })); - } - - const skyBox = new THREE.Mesh(geometry, materials); - skyBox.layers.set(1); - scene.add(skyBox); - - const materialsR = []; - - for (let i = 6; i < 12; i++) { - materialsR.push(new THREE.MeshBasicMaterial({ map: textures[i] })); - } - - const skyBoxR = new THREE.Mesh(geometry, materialsR); - skyBoxR.layers.set(2); - scene.add(skyBoxR); - - window.addEventListener('resize', onWindowResize); -} - -function getTexturesFromAtlasFile(atlasImgUrl, tilesNum) { - const textures = []; - - for (let i = 0; i < tilesNum; i++) { - textures[i] = new THREE.Texture(); - } - - const loader = new THREE.ImageLoader(); - loader.load(atlasImgUrl, function (imageObj) { - let canvas, context; - const tileWidth = imageObj.height; - - for (let i = 0; i < textures.length; i++) { - canvas = document.createElement('canvas'); - context = canvas.getContext('2d'); - canvas.height = tileWidth; - canvas.width = tileWidth; - context.drawImage(imageObj, tileWidth * i, 0, tileWidth, tileWidth, 0, 0, tileWidth, tileWidth); - textures[i].colorSpace = THREE.SRGBColorSpace; - textures[i].image = canvas; - textures[i].needsUpdate = true; - } - }); - - return textures; -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webxr_vr_panorama_depth.ts b/examples-testing/examples/webxr_vr_panorama_depth.ts deleted file mode 100644 index ea3d76199..000000000 --- a/examples-testing/examples/webxr_vr_panorama_depth.ts +++ /dev/null @@ -1,93 +0,0 @@ -import * as THREE from 'three'; -import { VRButton } from 'three/addons/webxr/VRButton.js'; - -let camera, scene, renderer, sphere, timer; - -init(); - -function init() { - const container = document.getElementById('container'); - - timer = new THREE.Timer(); - timer.connect(document); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x101010); - - const light = new THREE.AmbientLight(0xffffff, 3); - scene.add(light); - - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 2000); - scene.add(camera); - - // Create the panoramic sphere geometry - const panoSphereGeo = new THREE.SphereGeometry(6, 256, 256); - - // Create the panoramic sphere material - const panoSphereMat = new THREE.MeshStandardMaterial({ - side: THREE.BackSide, - displacementScale: -4.0, - }); - - // Create the panoramic sphere mesh - sphere = new THREE.Mesh(panoSphereGeo, panoSphereMat); - - // Load and assign the texture and depth map - const manager = new THREE.LoadingManager(); - const loader = new THREE.TextureLoader(manager); - - loader.load('./textures/kandao3.jpg', function (texture) { - texture.colorSpace = THREE.SRGBColorSpace; - texture.minFilter = THREE.NearestFilter; - texture.generateMipmaps = false; - sphere.material.map = texture; - }); - - loader.load('./textures/kandao3_depthmap.jpg', function (depth) { - depth.minFilter = THREE.NearestFilter; - depth.generateMipmaps = false; - sphere.material.displacementMap = depth; - }); - - // On load complete add the panoramic sphere to the scene - manager.onLoad = function () { - scene.add(sphere); - }; - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.xr.enabled = true; - renderer.xr.setReferenceSpaceType('local'); - container.appendChild(renderer.domElement); - - document.body.appendChild(VRButton.createButton(renderer)); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - timer.update(); - - // If we are not presenting move the camera a little so the effect is visible - - if (renderer.xr.isPresenting === false) { - const time = timer.getElapsed(); - - sphere.rotation.y += 0.001; - sphere.position.x = Math.sin(time) * 0.2; - sphere.position.z = Math.cos(time) * 0.2; - } - - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webxr_vr_rollercoaster.ts b/examples-testing/examples/webxr_vr_rollercoaster.ts deleted file mode 100644 index b8c35a9e3..000000000 --- a/examples-testing/examples/webxr_vr_rollercoaster.ts +++ /dev/null @@ -1,211 +0,0 @@ -import * as THREE from 'three'; - -import { - RollerCoasterGeometry, - RollerCoasterShadowGeometry, - RollerCoasterLiftersGeometry, - TreesGeometry, - SkyGeometry, -} from 'three/addons/misc/RollerCoaster.js'; -import { VRButton } from 'three/addons/webxr/VRButton.js'; - -let mesh, material, geometry; - -const renderer = new THREE.WebGLRenderer({ antialias: true }); -renderer.setPixelRatio(window.devicePixelRatio); -renderer.setSize(window.innerWidth, window.innerHeight); -renderer.setAnimationLoop(animate); -renderer.xr.enabled = true; -renderer.xr.setReferenceSpaceType('local'); -document.body.appendChild(renderer.domElement); - -document.body.appendChild(VRButton.createButton(renderer)); - -// - -const scene = new THREE.Scene(); -scene.background = new THREE.Color(0xf0f0ff); - -const light = new THREE.HemisphereLight(0xfff0f0, 0x60606, 3); -light.position.set(1, 1, 1); -scene.add(light); - -const train = new THREE.Object3D(); -scene.add(train); - -const camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 500); -train.add(camera); - -// environment - -geometry = new THREE.PlaneGeometry(500, 500, 15, 15); -geometry.rotateX(-Math.PI / 2); - -const positions = geometry.attributes.position.array; -const vertex = new THREE.Vector3(); - -for (let i = 0; i < positions.length; i += 3) { - vertex.fromArray(positions, i); - - vertex.x += Math.random() * 10 - 5; - vertex.z += Math.random() * 10 - 5; - - const distance = vertex.distanceTo(scene.position) / 5 - 25; - vertex.y = Math.random() * Math.max(0, distance); - - vertex.toArray(positions, i); -} - -geometry.computeVertexNormals(); - -material = new THREE.MeshLambertMaterial({ - color: 0x407000, -}); - -mesh = new THREE.Mesh(geometry, material); -scene.add(mesh); - -geometry = new TreesGeometry(mesh); -material = new THREE.MeshBasicMaterial({ - side: THREE.DoubleSide, - vertexColors: true, -}); -mesh = new THREE.Mesh(geometry, material); -scene.add(mesh); - -geometry = new SkyGeometry(); -material = new THREE.MeshBasicMaterial({ color: 0xffffff }); -mesh = new THREE.Mesh(geometry, material); -scene.add(mesh); - -// - -const PI2 = Math.PI * 2; - -const curve = (function () { - const vector = new THREE.Vector3(); - const vector2 = new THREE.Vector3(); - - return { - getPointAt: function (t) { - t = t * PI2; - - const x = Math.sin(t * 3) * Math.cos(t * 4) * 50; - const y = Math.sin(t * 10) * 2 + Math.cos(t * 17) * 2 + 5; - const z = Math.sin(t) * Math.sin(t * 4) * 50; - - return vector.set(x, y, z).multiplyScalar(2); - }, - - getTangentAt: function (t) { - const delta = 0.0001; - const t1 = Math.max(0, t - delta); - const t2 = Math.min(1, t + delta); - - return vector2.copy(this.getPointAt(t2)).sub(this.getPointAt(t1)).normalize(); - }, - }; -})(); - -geometry = new RollerCoasterGeometry(curve, 1500); -material = new THREE.MeshPhongMaterial({ - vertexColors: true, -}); -mesh = new THREE.Mesh(geometry, material); -scene.add(mesh); - -geometry = new RollerCoasterLiftersGeometry(curve, 100); -material = new THREE.MeshPhongMaterial(); -mesh = new THREE.Mesh(geometry, material); -mesh.position.y = 0.1; -scene.add(mesh); - -geometry = new RollerCoasterShadowGeometry(curve, 500); -material = new THREE.MeshBasicMaterial({ - color: 0x305000, - depthWrite: false, - transparent: true, -}); -mesh = new THREE.Mesh(geometry, material); -mesh.position.y = 0.1; -scene.add(mesh); - -const funfairs = []; - -// - -geometry = new THREE.CylinderGeometry(10, 10, 5, 15); -material = new THREE.MeshLambertMaterial({ - color: 0xff8080, -}); -mesh = new THREE.Mesh(geometry, material); -mesh.position.set(-80, 10, -70); -mesh.rotation.x = Math.PI / 2; -scene.add(mesh); - -funfairs.push(mesh); - -geometry = new THREE.CylinderGeometry(5, 6, 4, 10); -material = new THREE.MeshLambertMaterial({ - color: 0x8080ff, -}); -mesh = new THREE.Mesh(geometry, material); -mesh.position.set(50, 2, 30); -scene.add(mesh); - -funfairs.push(mesh); - -// - -window.addEventListener('resize', onWindowResize); - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -const position = new THREE.Vector3(); -const tangent = new THREE.Vector3(); - -const lookAt = new THREE.Vector3(); - -let velocity = 0; -let progress = 0; - -let prevTime = performance.now(); - -function animate() { - const time = performance.now(); - const delta = time - prevTime; - - for (let i = 0; i < funfairs.length; i++) { - funfairs[i].rotation.y = time * 0.0004; - } - - // - - progress += velocity; - progress = progress % 1; - - position.copy(curve.getPointAt(progress)); - position.y += 0.3; - - train.position.copy(position); - - tangent.copy(curve.getTangentAt(progress)); - - velocity -= tangent.y * 0.0000001 * delta; - velocity = Math.max(0.00004, Math.min(0.0002, velocity)); - - train.lookAt(lookAt.copy(position).sub(tangent)); - - // - - renderer.render(scene, camera); - - prevTime = time; -} diff --git a/examples-testing/examples/webxr_vr_sandbox.ts b/examples-testing/examples/webxr_vr_sandbox.ts deleted file mode 100644 index 19108d589..000000000 --- a/examples-testing/examples/webxr_vr_sandbox.ts +++ /dev/null @@ -1,192 +0,0 @@ -import * as THREE from 'three'; - -import { HDRLoader } from 'three/addons/loaders/HDRLoader.js'; -import { Reflector } from 'three/addons/objects/Reflector.js'; -import { VRButton } from 'three/addons/webxr/VRButton.js'; - -import { HTMLMesh } from 'three/addons/interactive/HTMLMesh.js'; -import { InteractiveGroup } from 'three/addons/interactive/InteractiveGroup.js'; -import { XRControllerModelFactory } from 'three/addons/webxr/XRControllerModelFactory.js'; - -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; -import Stats from 'three/addons/libs/stats.module.js'; - -let camera, scene, renderer; -let reflector; -let stats, statsMesh; - -const parameters = { - radius: 0.6, - tube: 0.2, - tubularSegments: 150, - radialSegments: 20, - p: 2, - q: 3, - thickness: 0.5, -}; - -init(); - -function init() { - scene = new THREE.Scene(); - - new HDRLoader().setPath('textures/equirectangular/').load('moonless_golf_1k.hdr', function (texture) { - texture.mapping = THREE.EquirectangularReflectionMapping; - - scene.background = texture; - scene.environment = texture; - }); - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 10); - camera.position.set(0, 1.6, 1.5); - - // - - const torusGeometry = new THREE.TorusKnotGeometry(...Object.values(parameters)); - const torusMaterial = new THREE.MeshPhysicalMaterial({ - transmission: 1.0, - roughness: 0, - metalness: 0.25, - thickness: 0.5, - side: THREE.DoubleSide, - }); - const torus = new THREE.Mesh(torusGeometry, torusMaterial); - torus.name = 'torus'; - torus.position.y = 1.5; - torus.position.z = -2; - scene.add(torus); - - const cylinderGeometry = new THREE.CylinderGeometry(1, 1, 0.1, 50); - const cylinderMaterial = new THREE.MeshLambertMaterial(); - const cylinder = new THREE.Mesh(cylinderGeometry, cylinderMaterial); - cylinder.position.z = -2; - scene.add(cylinder); - - // - - reflector = new Reflector(new THREE.PlaneGeometry(2, 2), { - textureWidth: window.innerWidth, - textureHeight: window.innerHeight, - }); - reflector.position.x = 1; - reflector.position.y = 1.5; - reflector.position.z = -3; - reflector.rotation.y = -Math.PI / 4; - scene.add(reflector); - - const frameGeometry = new THREE.BoxGeometry(2.1, 2.1, 0.1); - const frameMaterial = new THREE.MeshLambertMaterial({ color: 0x888888 }); - const frame = new THREE.Mesh(frameGeometry, frameMaterial); - frame.position.z = -0.07; - reflector.add(frame); - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.autoClear = false; - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.xr.enabled = true; - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.toneMappingExposure = 1; - document.body.appendChild(renderer.domElement); - - document.body.appendChild(VRButton.createButton(renderer)); - - window.addEventListener('resize', onWindowResize); - - // - - const geometry = new THREE.BufferGeometry(); - geometry.setFromPoints([new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 0, -5)]); - - const controller1 = renderer.xr.getController(0); - controller1.add(new THREE.Line(geometry)); - scene.add(controller1); - - const controller2 = renderer.xr.getController(1); - controller2.add(new THREE.Line(geometry)); - scene.add(controller2); - - // - - const controllerModelFactory = new XRControllerModelFactory(); - - const controllerGrip1 = renderer.xr.getControllerGrip(0); - controllerGrip1.add(controllerModelFactory.createControllerModel(controllerGrip1)); - scene.add(controllerGrip1); - - const controllerGrip2 = renderer.xr.getControllerGrip(1); - controllerGrip2.add(controllerModelFactory.createControllerModel(controllerGrip2)); - scene.add(controllerGrip2); - - // GUI - - function onChange() { - torus.geometry.dispose(); - torus.geometry = new THREE.TorusKnotGeometry(...Object.values(parameters)); - } - - function onThicknessChange() { - torus.material.thickness = parameters.thickness; - } - - const gui = new GUI({ width: 300 }); - gui.add(parameters, 'radius', 0.0, 1.0).onChange(onChange); - gui.add(parameters, 'tube', 0.0, 1.0).onChange(onChange); - gui.add(parameters, 'tubularSegments', 10, 150, 1).onChange(onChange); - gui.add(parameters, 'radialSegments', 2, 20, 1).onChange(onChange); - gui.add(parameters, 'p', 1, 10, 1).onChange(onChange); - gui.add(parameters, 'q', 0, 10, 1).onChange(onChange); - gui.add(parameters, 'thickness', 0, 1).onChange(onThicknessChange); - gui.domElement.style.visibility = 'hidden'; - - const group = new InteractiveGroup(); - group.listenToPointerEvents(renderer, camera); - group.listenToXRControllerEvents(controller1); - group.listenToXRControllerEvents(controller2); - scene.add(group); - - const mesh = new HTMLMesh(gui.domElement); - mesh.position.x = -0.75; - mesh.position.y = 1.5; - mesh.position.z = -0.5; - mesh.rotation.y = Math.PI / 4; - mesh.scale.setScalar(2); - group.add(mesh); - - // Add stats.js - stats = new Stats(); - stats.dom.style.width = '80px'; - stats.dom.style.height = '48px'; - document.body.appendChild(stats.dom); - - statsMesh = new HTMLMesh(stats.dom); - statsMesh.position.x = -0.75; - statsMesh.position.y = 2; - statsMesh.position.z = -0.6; - statsMesh.rotation.y = Math.PI / 4; - statsMesh.scale.setScalar(2.5); - group.add(statsMesh); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - const time = performance.now() * 0.0002; - const torus = scene.getObjectByName('torus'); - torus.rotation.x = time * 0.4; - torus.rotation.y = time; - - renderer.render(scene, camera); - stats.update(); - - // Canvas elements doesn't trigger DOM updates, so we have to update the texture - statsMesh.material.map.update(); -} diff --git a/examples-testing/examples/webxr_vr_video.ts b/examples-testing/examples/webxr_vr_video.ts deleted file mode 100644 index 50a990412..000000000 --- a/examples-testing/examples/webxr_vr_video.ts +++ /dev/null @@ -1,92 +0,0 @@ -import * as THREE from 'three'; -import { VRButton } from 'three/addons/webxr/VRButton.js'; - -let camera, scene, renderer; - -init(); - -function init() { - const container = document.getElementById('container'); - container.addEventListener('click', function () { - video.play(); - }); - - camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 2000); - camera.layers.enable(1); // render left view when no stereo available - - // video - - const video = document.getElementById('video'); - video.play(); - - const texture = new THREE.VideoTexture(video); - texture.colorSpace = THREE.SRGBColorSpace; - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x101010); - - // left - - const geometry1 = new THREE.SphereGeometry(500, 60, 40); - // invert the geometry on the x-axis so that all of the faces point inward - geometry1.scale(-1, 1, 1); - - const uvs1 = geometry1.attributes.uv.array; - - for (let i = 0; i < uvs1.length; i += 2) { - uvs1[i] *= 0.5; - } - - const material1 = new THREE.MeshBasicMaterial({ map: texture }); - - const mesh1 = new THREE.Mesh(geometry1, material1); - mesh1.rotation.y = -Math.PI / 2; - mesh1.layers.set(1); // display in left eye only - scene.add(mesh1); - - // right - - const geometry2 = new THREE.SphereGeometry(500, 60, 40); - geometry2.scale(-1, 1, 1); - - const uvs2 = geometry2.attributes.uv.array; - - for (let i = 0; i < uvs2.length; i += 2) { - uvs2[i] *= 0.5; - uvs2[i] += 0.5; - } - - const material2 = new THREE.MeshBasicMaterial({ map: texture }); - - const mesh2 = new THREE.Mesh(geometry2, material2); - mesh2.rotation.y = -Math.PI / 2; - mesh2.layers.set(2); // display in right eye only - scene.add(mesh2); - - // - - renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.xr.enabled = true; - renderer.xr.setReferenceSpaceType('local'); - container.appendChild(renderer.domElement); - - document.body.appendChild(VRButton.createButton(renderer)); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function animate() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webxr_xr_controls_transform.ts b/examples-testing/examples/webxr_xr_controls_transform.ts deleted file mode 100644 index cd9b1290d..000000000 --- a/examples-testing/examples/webxr_xr_controls_transform.ts +++ /dev/null @@ -1,218 +0,0 @@ -import * as THREE from 'three'; -import { TransformControls } from 'three/addons/controls/TransformControls.js'; -import { XRButton } from 'three/addons/webxr/XRButton.js'; -import { XRControllerModelFactory } from 'three/addons/webxr/XRControllerModelFactory.js'; -import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js'; - -let container; -let camera, scene, renderer; -let bloomPass; -let controller1, controller2, line; -let controllerGrip1, controllerGrip2; - -let raycaster; - -let controls, group; - -init(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x808080); - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 10); - camera.position.set(0, 1.6, 0); - - const floorGeometry = new THREE.PlaneGeometry(6, 6); - const floorMaterial = new THREE.ShadowMaterial({ - opacity: 0.25, - blending: THREE.CustomBlending, - transparent: false, - }); - const floor = new THREE.Mesh(floorGeometry, floorMaterial); - floor.rotation.x = -Math.PI / 2; - floor.receiveShadow = true; - scene.add(floor); - - scene.add(new THREE.HemisphereLight(0xbcbcbc, 0xa5a5a5, 3)); - - const light = new THREE.DirectionalLight(0xffffff, 3); - light.position.set(0, 6, 0); - light.castShadow = true; - light.shadow.camera.top = 3; - light.shadow.camera.bottom = -3; - light.shadow.camera.right = 3; - light.shadow.camera.left = -3; - light.shadow.mapSize.set(4096, 4096); - scene.add(light); - - group = new THREE.Group(); - scene.add(group); - - const geometries = [ - new THREE.BoxGeometry(0.2, 0.2, 0.2), - new THREE.ConeGeometry(0.2, 0.4, 64), - new THREE.CylinderGeometry(0.2, 0.2, 0.2, 64), - new THREE.IcosahedronGeometry(0.2, 8), - new THREE.TorusGeometry(0.2, 0.04, 64, 32), - ]; - - for (let i = 0; i < 16; i++) { - const geometry = geometries[Math.floor(Math.random() * geometries.length)]; - const material = new THREE.MeshStandardMaterial({ - color: Math.random() * 0xffffff, - roughness: 0.7, - metalness: 0.0, - }); - - const object = new THREE.Mesh(geometry, material); - - object.position.x = Math.random() - 0.5; - object.position.y = Math.random() * 2 + 0.5; - object.position.z = Math.random() - 2.5; - - object.rotation.x = Math.random() * 2 * Math.PI; - object.rotation.y = Math.random() * 2 * Math.PI; - object.rotation.z = Math.random() * 2 * Math.PI; - - object.scale.setScalar(Math.random() + 0.5); - - object.castShadow = true; - object.receiveShadow = true; - - group.add(object); - } - - // - - renderer = new THREE.WebGLRenderer({ antialias: true, outputBufferType: THREE.HalfFloatType }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setAnimationLoop(animate); - renderer.shadowMap.enabled = true; - renderer.toneMapping = THREE.ACESFilmicToneMapping; - renderer.xr.enabled = true; - container.appendChild(renderer.domElement); - - // post-processing - - bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85); - renderer.setEffects([bloomPass]); - - document.body.appendChild(XRButton.createButton(renderer)); - - // controllers - - controller1 = renderer.xr.getController(0); - controller1.addEventListener('select', onSelect); - controller1.addEventListener('selectstart', onControllerEvent); - controller1.addEventListener('selectend', onControllerEvent); - controller1.addEventListener('move', onControllerEvent); - controller1.userData.active = false; - scene.add(controller1); - - controller2 = renderer.xr.getController(1); - controller2.addEventListener('select', onSelect); - controller2.addEventListener('selectstart', onControllerEvent); - controller2.addEventListener('selectend', onControllerEvent); - controller2.addEventListener('move', onControllerEvent); - controller2.userData.active = true; - scene.add(controller2); - - const controllerModelFactory = new XRControllerModelFactory(); - - controllerGrip1 = renderer.xr.getControllerGrip(0); - controllerGrip1.add(controllerModelFactory.createControllerModel(controllerGrip1)); - scene.add(controllerGrip1); - - controllerGrip2 = renderer.xr.getControllerGrip(1); - controllerGrip2.add(controllerModelFactory.createControllerModel(controllerGrip2)); - scene.add(controllerGrip2); - - // - - const geometry = new THREE.BufferGeometry().setFromPoints([ - new THREE.Vector3(0, 0, 0), - new THREE.Vector3(0, 0, -1), - ]); - - line = new THREE.Line(geometry); - line.name = 'line'; - line.scale.z = 5; - - raycaster = new THREE.Raycaster(); - - // controls - - controls = new TransformControls(camera, renderer.domElement); - controls.attach(group.children[0]); - scene.add(controls.getHelper()); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onSelect(event) { - const controller = event.target; - - controller1.userData.active = false; - controller2.userData.active = false; - - if (controller === controller1) { - controller1.userData.active = true; - controller1.add(line); - } - - if (controller === controller2) { - controller2.userData.active = true; - controller2.add(line); - } - - raycaster.setFromXRController(controller); - - const intersects = raycaster.intersectObjects(group.children); - - if (intersects.length > 0) { - controls.attach(intersects[0].object); - } -} - -function onControllerEvent(event) { - const controller = event.target; - - if (controller.userData.active === false) return; - - controls.getRaycaster().setFromXRController(controller); - - switch (event.type) { - case 'selectstart': - controls.pointerDown(null); - break; - - case 'selectend': - controls.pointerUp(null); - break; - - case 'move': - controls.pointerHover(null); - controls.pointerMove(null); - break; - } -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// - -function animate() { - renderer.render(scene, camera); -} diff --git a/examples-testing/examples/webxr_xr_dragging_custom_depth.ts b/examples-testing/examples/webxr_xr_dragging_custom_depth.ts deleted file mode 100644 index 2cd50ba4c..000000000 --- a/examples-testing/examples/webxr_xr_dragging_custom_depth.ts +++ /dev/null @@ -1,395 +0,0 @@ -import * as THREE from 'three'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { XRButton } from 'three/addons/webxr/XRButton.js'; -import { XRControllerModelFactory } from 'three/addons/webxr/XRControllerModelFactory.js'; - -let container; -let camera, scene, renderer; -let controller1, controller2; -let controllerGrip1, controllerGrip2; -let isDepthSupplied = false; - -let raycaster; - -const intersected = []; -const tempMatrix = new THREE.Matrix4(); - -let controls, group; - -init(); -animate(); - -function init() { - container = document.createElement('div'); - document.body.appendChild(container); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0x808080); - - camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 10); - camera.position.set(0, 1.6, 3); - - controls = new OrbitControls(camera, container); - controls.target.set(0, 1.6, 0); - controls.update(); - - const floorGeometry = new THREE.PlaneGeometry(6, 6); - const floorMaterial = new THREE.ShadowMaterial({ - opacity: 0.25, - blending: THREE.CustomBlending, - transparent: false, - }); - const floor = new THREE.Mesh(floorGeometry, floorMaterial); - floor.rotation.x = -Math.PI / 2; - floor.receiveShadow = true; - scene.add(floor); - - scene.add(new THREE.HemisphereLight(0xbcbcbc, 0xa5a5a5, 3)); - - const light = new THREE.DirectionalLight(0xffffff, 3); - light.position.set(0, 6, 0); - light.castShadow = true; - light.shadow.camera.top = 3; - light.shadow.camera.bottom = -3; - light.shadow.camera.right = 3; - light.shadow.camera.left = -3; - light.shadow.mapSize.set(4096, 4096); - scene.add(light); - - group = new THREE.Group(); - scene.add(group); - - const geometries = [ - new THREE.BoxGeometry(0.2, 0.2, 0.2), - new THREE.ConeGeometry(0.2, 0.2, 64), - new THREE.CylinderGeometry(0.2, 0.2, 0.2, 64), - new THREE.IcosahedronGeometry(0.2, 8), - new THREE.TorusGeometry(0.2, 0.04, 64, 32), - ]; - - for (let i = 0; i < 50; i++) { - const geometry = geometries[Math.floor(Math.random() * geometries.length)]; - const material = new THREE.ShaderMaterial({ - vertexShader: /* glsl */ ` - varying vec3 vNormal; - varying vec2 vUv; - void main() { - vNormal = normalize(normalMatrix * normal); - vUv = uv; - gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); - } - `, - - fragmentShader: /* glsl */ ` - uniform vec3 diffuseColor; - uniform float roughness; - uniform float metalness; - uniform float emissive; - varying vec3 vNormal; - varying vec2 vUv; - uniform sampler2DArray depthColor; - uniform float depthWidth; - uniform float depthHeight; - #define saturate( a ) clamp( a, 0.0, 1.0 ) - float Depth_GetCameraDepthInMeters(const sampler2DArray depthTexture, - const vec2 depthUv, int arrayIndex) { - return texture(depthColor, vec3(depthUv.x, depthUv.y, arrayIndex)).r; - } - float Depth_GetOcclusion(const sampler2DArray depthTexture, const vec2 depthUv, float assetDepthM, int arrayIndex) { - float depthMm = Depth_GetCameraDepthInMeters(depthTexture, depthUv, arrayIndex); - const float kDepthTolerancePerM = 0.001; - return clamp(1.0 - - 0.5 * (depthMm - assetDepthM) / - (kDepthTolerancePerM * assetDepthM) + - 0.5, 0.0, 1.0); - } - float Depth_GetBlurredOcclusionAroundUV(const sampler2DArray depthTexture, const vec2 uv, float assetDepthM, int arrayIndex) { - // Kernel used: - // 0 4 7 4 0 - // 4 16 26 16 4 - // 7 26 41 26 7 - // 4 16 26 16 4 - // 0 4 7 4 0 - const float kKernelTotalWeights = 269.0; - float sum = 0.0; - const float kOcclusionBlurAmount = 0.0005; - vec2 blurriness = - vec2(kOcclusionBlurAmount, kOcclusionBlurAmount /** u_DepthAspectRatio*/); - float current = 0.0; - current += Depth_GetOcclusion( - depthTexture, uv + vec2(-1.0, -2.0) * blurriness, assetDepthM, arrayIndex); - current += Depth_GetOcclusion( - depthTexture, uv + vec2(+1.0, -2.0) * blurriness, assetDepthM, arrayIndex); - current += Depth_GetOcclusion( - depthTexture, uv + vec2(-1.0, +2.0) * blurriness, assetDepthM, arrayIndex); - current += Depth_GetOcclusion( - depthTexture, uv + vec2(+1.0, +2.0) * blurriness, assetDepthM, arrayIndex); - current += Depth_GetOcclusion( - depthTexture, uv + vec2(-2.0, +1.0) * blurriness, assetDepthM, arrayIndex); - current += Depth_GetOcclusion( - depthTexture, uv + vec2(+2.0, +1.0) * blurriness, assetDepthM, arrayIndex); - current += Depth_GetOcclusion( - depthTexture, uv + vec2(-2.0, -1.0) * blurriness, assetDepthM, arrayIndex); - current += Depth_GetOcclusion( - depthTexture, uv + vec2(+2.0, -1.0) * blurriness, assetDepthM, arrayIndex); - sum += current * 4.0; - current = 0.0; - current += Depth_GetOcclusion( - depthTexture, uv + vec2(-2.0, -0.0) * blurriness, assetDepthM, arrayIndex); - current += Depth_GetOcclusion( - depthTexture, uv + vec2(+2.0, +0.0) * blurriness, assetDepthM, arrayIndex); - current += Depth_GetOcclusion( - depthTexture, uv + vec2(+0.0, +2.0) * blurriness, assetDepthM, arrayIndex); - current += Depth_GetOcclusion( - depthTexture, uv + vec2(-0.0, -2.0) * blurriness, assetDepthM, arrayIndex); - sum += current * 7.0; - current = 0.0; - current += Depth_GetOcclusion( - depthTexture, uv + vec2(-1.0, -1.0) * blurriness, assetDepthM, arrayIndex); - current += Depth_GetOcclusion( - depthTexture, uv + vec2(+1.0, -1.0) * blurriness, assetDepthM, arrayIndex); - current += Depth_GetOcclusion( - depthTexture, uv + vec2(-1.0, +1.0) * blurriness, assetDepthM, arrayIndex); - current += Depth_GetOcclusion( - depthTexture, uv + vec2(+1.0, +1.0) * blurriness, assetDepthM, arrayIndex); - sum += current * 16.0; - current = 0.0; - current += Depth_GetOcclusion( - depthTexture, uv + vec2(+0.0, +1.0) * blurriness, assetDepthM, arrayIndex); - current += Depth_GetOcclusion( - depthTexture, uv + vec2(-0.0, -1.0) * blurriness, assetDepthM, arrayIndex); - current += Depth_GetOcclusion( - depthTexture, uv + vec2(-1.0, -0.0) * blurriness, assetDepthM, arrayIndex); - current += Depth_GetOcclusion( - depthTexture, uv + vec2(+1.0, +0.0) * blurriness, assetDepthM, arrayIndex); - sum += current * 26.0; - sum += Depth_GetOcclusion(depthTexture, uv, assetDepthM, arrayIndex) * 41.0; - return sum / kKernelTotalWeights; - } - void main() { - vec3 normal = normalize(vNormal); - vec3 diffuse = diffuseColor; - float specularIntensity = pow(max(dot(normal, normalize(vec3(0, 0, 1))), 0.0), 64.0); - vec3 specular = vec3(specularIntensity) * mix(vec3(0.04), diffuse, metalness); - gl_FragColor = vec4(diffuse * (1.0 - specular) + specular, 1.0) * (1.0 + emissive); - - if (depthWidth > 0.0) { - int arrayIndex = 0; - vec2 depthUv; - if (gl_FragCoord.x>=depthWidth) { - arrayIndex = 1; - depthUv = vec2((gl_FragCoord.x-depthWidth)/depthWidth, gl_FragCoord.y/depthHeight); - } else { - depthUv = vec2(gl_FragCoord.x/depthWidth, gl_FragCoord.y/depthHeight); - } - float assetDepthM = gl_FragCoord.z; - - float occlusion = Depth_GetBlurredOcclusionAroundUV(depthColor, depthUv, assetDepthM, arrayIndex); - float depthMm = Depth_GetCameraDepthInMeters(depthColor, depthUv, arrayIndex); - - float absDistance = abs(assetDepthM - depthMm); - float v = 0.0025; - absDistance = saturate(v - absDistance) / v; - - gl_FragColor.rgb += vec3(absDistance * 2.0, absDistance * 2.0, absDistance * 12.0); - gl_FragColor = mix(gl_FragColor, vec4(0.0, 0.0, 0.0, 0.0), occlusion * 0.7); - } - } - `, - - uniforms: { - diffuseColor: { value: new THREE.Color(Math.random() * 0xffffff) }, - roughness: { value: 0.7 }, - metalness: { value: 0.0 }, - emissive: { value: 0.0 }, - depthWidth: { value: 0.0 }, - depthHeight: { value: 0.0 }, - depthColor: { value: new THREE.Texture() }, - }, - }); - - const object = new THREE.Mesh(geometry, material); - - object.position.x = Math.random() * 4 - 2; - object.position.y = Math.random() * 2; - object.position.z = Math.random() * 4 - 2; - - object.rotation.x = Math.random() * 2 * Math.PI; - object.rotation.y = Math.random() * 2 * Math.PI; - object.rotation.z = Math.random() * 2 * Math.PI; - - object.scale.setScalar(Math.random() + 0.5); - - group.add(object); - } - - // - - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.shadowMap.enabled = true; - renderer.xr.enabled = true; - container.appendChild(renderer.domElement); - - document.body.appendChild( - XRButton.createButton(renderer, { - optionalFeatures: ['depth-sensing'], - depthSensing: { usagePreference: ['gpu-optimized'], dataFormatPreference: [] }, - }), - ); - - // controllers - - controller1 = renderer.xr.getController(0); - controller1.addEventListener('selectstart', onSelectStart); - controller1.addEventListener('selectend', onSelectEnd); - scene.add(controller1); - - controller2 = renderer.xr.getController(1); - controller2.addEventListener('selectstart', onSelectStart); - controller2.addEventListener('selectend', onSelectEnd); - scene.add(controller2); - - const controllerModelFactory = new XRControllerModelFactory(); - - controllerGrip1 = renderer.xr.getControllerGrip(0); - controllerGrip1.add(controllerModelFactory.createControllerModel(controllerGrip1)); - scene.add(controllerGrip1); - - controllerGrip2 = renderer.xr.getControllerGrip(1); - controllerGrip2.add(controllerModelFactory.createControllerModel(controllerGrip2)); - scene.add(controllerGrip2); - - // - - const geometry = new THREE.BufferGeometry().setFromPoints([ - new THREE.Vector3(0, 0, 0), - new THREE.Vector3(0, 0, -1), - ]); - - const line = new THREE.Line(geometry); - line.name = 'line'; - line.scale.z = 5; - - controller1.add(line.clone()); - controller2.add(line.clone()); - - raycaster = new THREE.Raycaster(); - - // - - window.addEventListener('resize', onWindowResize); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight); -} - -function onSelectStart(event) { - const controller = event.target; - - const intersections = getIntersections(controller); - - if (intersections.length > 0) { - const intersection = intersections[0]; - - const object = intersection.object; - object.material.uniforms.emissive.value = 1; - controller.attach(object); - - controller.userData.selected = object; - } - - controller.userData.targetRayMode = event.data.targetRayMode; -} - -function onSelectEnd(event) { - const controller = event.target; - - if (controller.userData.selected !== undefined) { - const object = controller.userData.selected; - object.material.uniforms.emissive.value = 0; - group.attach(object); - - controller.userData.selected = undefined; - } -} - -function getIntersections(controller) { - controller.updateMatrixWorld(); - - tempMatrix.identity().extractRotation(controller.matrixWorld); - - raycaster.ray.origin.setFromMatrixPosition(controller.matrixWorld); - raycaster.ray.direction.set(0, 0, -1).applyMatrix4(tempMatrix); - - return raycaster.intersectObjects(group.children, false); -} - -function intersectObjects(controller) { - // Do not highlight in mobile-ar - - if (controller.userData.targetRayMode === 'screen') return; - - // Do not highlight when already selected - - if (controller.userData.selected !== undefined) return; - - const line = controller.getObjectByName('line'); - const intersections = getIntersections(controller); - - if (intersections.length > 0) { - const intersection = intersections[0]; - - const object = intersection.object; - object.material.uniforms.emissive.value = 1; - intersected.push(object); - - line.scale.z = intersection.distance; - } else { - line.scale.z = 5; - } -} - -function cleanIntersected() { - while (intersected.length) { - const object = intersected.pop(); - object.material.uniforms.emissive.value = 0; - } -} - -// - -function animate() { - renderer.setAnimationLoop(render); -} - -function render() { - if (renderer.xr.hasDepthSensing() && !isDepthSupplied) { - group.children.forEach(child => { - child.material.uniforms.depthColor.value = renderer.xr.getDepthTexture(); - child.material.uniforms.depthWidth.value = 1680; - child.material.uniforms.depthHeight.value = 1760; - - isDepthSupplied = true; - }); - } else if (!renderer.xr.hasDepthSensing() && isDepthSupplied) { - group.children.forEach(child => { - child.material.uniforms.depthWidth.value = 0; - child.material.uniforms.depthHeight.value = 0; - - isDepthSupplied = false; - }); - } - - cleanIntersected(); - - intersectObjects(controller1); - intersectObjects(controller2); - - renderer.render(scene, camera); -} From c45522c298b9d28056dfa123007aff6bc4e6dd99 Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Mon, 30 Mar 2026 22:13:23 -0400 Subject: [PATCH 7/7] Update three.js --- three.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/three.js b/three.js index 87c407b70..85f77d7f2 160000 --- a/three.js +++ b/three.js @@ -1 +1 @@ -Subproject commit 87c407b70235a6cdf64f96fa3f74d6fb9b0ccbbc +Subproject commit 85f77d7f2ad12ce724aa5624facb11b23233281d