tp/src/standalone/trees.js (6283B)
1 import * as THREE from 'three'; 2 import * as dat from 'dat.gui'; 3 import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; 4 import { vertexShader, fragmentShader } from '/src/treesShaders.js'; 5 6 let scene, camera, renderer, container, terrainMaterial, instancedTrees; 7 8 import tierraUrl from '../assets/tierra.jpg' 9 import rocaUrl from '../assets/roca.jpg' 10 import pastoUrl from '../assets/pasto.jpg' 11 12 const textures = { 13 tierra: { url: tierraUrl, object: null }, 14 roca: { url: rocaUrl, object: null }, 15 pasto: { url: pastoUrl, object: null }, 16 }; 17 18 function onResize() { 19 camera.aspect = container.offsetWidth / container.offsetHeight; 20 camera.updateProjectionMatrix(); 21 renderer.setSize(container.offsetWidth, container.offsetHeight); 22 } 23 24 function setupThreeJs() { 25 scene = new THREE.Scene(); 26 container = document.getElementById('mainContainer'); 27 28 renderer = new THREE.WebGLRenderer(); 29 renderer.setClearColor(0x606060); 30 container.appendChild(renderer.domElement); 31 32 camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 0.1, 1000); 33 camera.position.set(-50, 60, 50); 34 camera.lookAt(0, 0, 0); 35 36 const controls = new OrbitControls(camera, renderer.domElement); 37 38 const ambientLight = new THREE.AmbientLight(0xffffff); 39 scene.add(ambientLight); 40 41 const hemisphereLight = new THREE.HemisphereLight(0xffffff, 0x000000, 0.25); 42 scene.add(hemisphereLight); 43 44 const directionalLight = new THREE.DirectionalLight(0xffffff, 1); 45 directionalLight.position.set(100, 100, 100); 46 scene.add(directionalLight); 47 48 const directionalLightHelper = new THREE.DirectionalLightHelper(directionalLight, 5); 49 // scene.add(directionalLightHelper); 50 51 //const gridHelper = new THREE.GridHelper(50, 20); 52 //scene.add(gridHelper); 53 54 const axesHelper = new THREE.AxesHelper(5); 55 scene.add(axesHelper); 56 57 window.addEventListener('resize', onResize); 58 onResize(); 59 } 60 61 function createInstancedTrees(count) { 62 console.log('Generating `' + count + '` instances of tree'); 63 64 let logHeight = 4.0; 65 66 const treeLogGeometry = new THREE.CylinderGeometry(0.30, 0.30, logHeight, 40, 40); 67 const treeLeavesGeometry = new THREE.SphereGeometry(1.75,40,40); 68 69 treeLogGeometry.translate(0, logHeight/2.0, 0); 70 71 const instancedTreeLogGeometry = new THREE.InstancedBufferGeometry(); 72 instancedTreeLogGeometry.copy(treeLogGeometry); 73 74 const instancedTreeLeavesGeometry = new THREE.InstancedBufferGeometry(); 75 instancedTreeLeavesGeometry.copy(treeLeavesGeometry); 76 77 const treeLogMaterial = new THREE.MeshPhongMaterial({color: 0x7c3f00}); 78 const instancedTreeLogs = new THREE.InstancedMesh(instancedTreeLogGeometry, treeLogMaterial, count); 79 80 const treeLeavesMaterial = new THREE.MeshPhongMaterial({color: 0x365829}); 81 const instancedTreeLeaves = new THREE.InstancedMesh(instancedTreeLeavesGeometry, treeLeavesMaterial, count); 82 83 const rotMatrix = new THREE.Matrix4(); 84 85 const translationMatrix = new THREE.Matrix4(); 86 const treeLogMatrix = new THREE.Matrix4(); 87 const treeLeavesMatrix = new THREE.Matrix4(); 88 89 //let origin = new THREE.Vector3(); 90 const RANGE = 50 - 4/2; 91 92 for (let i = 0; i < count; i++) { 93 let position = new THREE.Vector3( 94 (Math.random() - 0.5) * RANGE, 95 0, 96 (Math.random() - 0.5) * RANGE 97 ); 98 99 translationMatrix.makeTranslation(position); 100 101 //rotMatrix.lookAt(0, 0, new THREE.Vector3(0, 1, 0)); 102 treeLogMatrix.identity(); 103 treeLeavesMatrix.identity(); 104 105 let scale = 0.5 + (Math.random()*(logHeight/3)); 106 treeLogMatrix.makeScale(1, scale, 1); 107 //matrix.premultiply(rotMatrix); 108 109 treeLogMatrix.premultiply(translationMatrix); 110 111 position.y = scale*logHeight; 112 translationMatrix.makeTranslation(position); 113 treeLeavesMatrix.premultiply(translationMatrix); 114 115 instancedTreeLogs.setMatrixAt(i, treeLogMatrix); 116 instancedTreeLeaves.setMatrixAt(i, treeLeavesMatrix); 117 } 118 119 scene.add(instancedTreeLogs); 120 scene.add(instancedTreeLeaves); 121 } 122 123 function buildScene() { 124 console.log('Building scene'); 125 126 console.log('Generating terrain'); 127 const terrainGeometry = new THREE.PlaneGeometry(50, 50); 128 //const terrainMaterial = new THREE.MeshPhongMaterial( {color: 0x365829, side: THREE.DoubleSide} ); 129 terrainMaterial = new THREE.RawShaderMaterial({ 130 uniforms: { 131 tierraSampler: { type: 't', value: textures.tierra.object }, 132 rocaSampler: { type: 't', value: textures.roca.object }, 133 pastoSampler: { type: 't', value: textures.pasto.object }, 134 scale1: { type: 'f', value: 2.0 }, 135 136 mask1low: { type: 'f', value: -0.38 }, 137 mask1high: { type: 'f', value: 0.1 }, 138 139 mask2low: { type: 'f', value: 0.05 }, 140 mask2high: { type: 'f', value: -0.70 }, 141 }, 142 vertexShader: vertexShader, 143 fragmentShader: fragmentShader, 144 side: THREE.DoubleSide, 145 }); 146 terrainMaterial.needsUpdate = true; 147 148 const terrain = new THREE.Mesh(terrainGeometry, terrainMaterial); 149 terrain.rotateX(Math.PI/2); 150 terrain.position.set(0, 0, 0); 151 scene.add(terrain); 152 153 console.log('Generating trees'); 154 createInstancedTrees(35); 155 } 156 157 function onTextureLoaded(key, texture) { 158 texture.wrapS = texture.wrapT = THREE.RepeatWrapping; 159 textures[key].object = texture; 160 console.log('Texture `' + key + '` loaded'); 161 } 162 163 function loadTextures(callback) { 164 const loadingManager = new THREE.LoadingManager(); 165 166 loadingManager.onLoad = () => { 167 console.log('All textures loaded'); 168 callback(); 169 }; 170 171 for (const key in textures) { 172 console.log("Loading textures"); 173 const loader = new THREE.TextureLoader(loadingManager); 174 const texture = textures[key]; 175 texture.object = loader.load( 176 texture.url, 177 onTextureLoaded.bind(this, key), 178 null, 179 (error) => { 180 console.error(error); 181 } 182 ); 183 } 184 } 185 186 187 function createMenu() { 188 const gui = new dat.GUI({ width: 400 }); 189 gui.add(terrainMaterial.uniforms.scale1, 'value', 0, 10).name('Texture scale'); 190 gui.add(terrainMaterial.uniforms.mask1low, 'value', -1, 1).name('Mask1 Low'); 191 gui.add(terrainMaterial.uniforms.mask1high, 'value', -1, 1).name('Mask1 High'); 192 gui.add(terrainMaterial.uniforms.mask2low, 'value', -1, 1).name('Mask2 Low'); 193 gui.add(terrainMaterial.uniforms.mask2high, 'value', -1, 1).name('Mask2 High'); 194 } 195 196 function mainLoop() { 197 requestAnimationFrame(mainLoop); 198 renderer.render(scene, camera); 199 } 200 201 function main() { 202 buildScene(); 203 createMenu(); 204 mainLoop(); 205 } 206 207 setupThreeJs(); 208 loadTextures(main);