TA159

Notas, resueltos y trabajos practicos de la materia Sistemas Gráficos
Index Commits Files Refs Submodules README LICENSE
tp/src/terrain.js (5712B)
   1 import * as THREE from 'three';
   2 
   3 const widthSegments   = 100;
   4 const heightSegments  = 100;
   5 const amplitude       = 8;
   6 const amplitudeBottom = -1.00;
   7 
   8 import rocaUrl             from './assets/roca.jpg'
   9 import pastoUrl            from './assets/pasto.jpg'
  10 import tierraUrl           from './assets/tierra.jpg'
  11 import elevationMapUrl     from './assets/elevation_map_wider_river.png'
  12 
  13 const textures = {
  14     tierra:       { url: tierraUrl,       object: null },
  15     roca:         { url: rocaUrl,         object: null },
  16     pasto:        { url: pastoUrl,        object: null },
  17     elevationMap: { url: elevationMapUrl, object: null },
  18 };
  19 
  20 // La funcion devuelve una geometria de Three.js
  21 // width: Ancho del plano
  22 // height: Alto del plano
  23 // amplitude: Amplitud de la elevacion
  24 // widthSegments: Numero de segmentos en el ancho
  25 // heightSegments: Numero de segmentos en el alto
  26 // texture: Textura que se usara para la elevacion
  27 export function elevationGeometry(width, height, amplitude, widthSegments, heightSegments, texture) {
  28     console.log('Generating terrain geometry');
  29     let geometry = new THREE.BufferGeometry();
  30 
  31     const positions = [];
  32     const indices = [];
  33     const normals = [];
  34     const uvs = [];
  35 
  36     // Creamos un canvas para poder leer los valores de los píxeles de la textura
  37     let canvas = document.createElement('canvas');
  38     let ctx = canvas.getContext('2d');
  39     let img = texture.image;
  40 
  41     // Ajustamos el tamaño del canvas segun la cantidad de segmentos horizontales y verticales
  42     canvas.width = widthSegments;
  43     canvas.height = heightSegments;
  44 
  45     // Dibujamos la textura en el canvas en la escala definida por widthSegments y heightSegments
  46     ctx.drawImage(img, 0, 0, widthSegments, heightSegments);
  47 
  48     // Obtenemos los valores de los píxeles de la textura
  49     let imageData = ctx.getImageData(0, 0, widthSegments, heightSegments);
  50     let data = imageData.data; // Este es un array con los valores de los píxeles
  51 
  52     const quadsPerRow = widthSegments - 1;
  53 
  54     // Recorremos los segmentos horizontales y verticales
  55     for (let i = 0; i < widthSegments - 1; i++) {
  56         for (let j = 0; j < heightSegments - 1; j++) {
  57             // Obtenemos los valores de los píxeles de los puntos adyacentes
  58             let xPrev = undefined;
  59             let xNext = undefined;
  60             let yPrev = undefined;
  61             let yNext = undefined;
  62 
  63             // Obtenemos el valor del pixel en la posicion i, j
  64             // console.log('getting elevation map value at: (' + i + ',' + j + ')');
  65             let z0 = data[(i + j * widthSegments) * 4] / 255;
  66 
  67             // Obtenemos los valores de los píxeles adyacentes
  68             xPrev = i > 0 ? data[(i - 1 + j * widthSegments) * 4] / 255 : undefined;
  69             xNext = i < widthSegments - 1 ? (xNext = data[(i + 1 + j * widthSegments) * 4] / 255) : undefined;
  70 
  71             yPrev = j > 0 ? data[(i + (j - 1) * widthSegments) * 4] / 255 : undefined;
  72             yNext = j < heightSegments - 1 ? data[(i + (j + 1) * widthSegments) * 4] / 255 : undefined;
  73 
  74             // calculamos la diferencia entre los valores de los píxeles adyacentes
  75             // en el eje `x` y en el eje `y` de la imagen (en el espacio de la textura
  76             // Ojo no confundir con el espacio 3D del modelo 3D donde Y es la altura)
  77             let deltaX;
  78             if (xPrev == undefined) {
  79                 deltaX = xNext - z0;
  80             } else if (yNext == undefined) {
  81                 deltaX = xPrev - z0;
  82             } else {
  83                 deltaX = (xNext - xPrev) / 2;
  84             }
  85 
  86             let deltaY;
  87             if (yPrev == undefined) {
  88                 deltaY = yNext - z0;
  89             } else if (yNext == undefined) {
  90                 deltaY = yPrev - z0;
  91             } else {
  92                 deltaY = (yNext - yPrev) / 2;
  93             }
  94 
  95             // Calculamos la altura del punto en el espacio 3D
  96             const z = amplitude * z0;
  97 
  98             // Añadimos los valores de los puntos al array de posiciones
  99             positions.push((width * i) / widthSegments - width / 2);
 100             positions.push(z);
 101             positions.push((height * j) / heightSegments - height / 2);
 102 
 103             // Calculamos los vectores tangentes a la superficie en el ejex y en el eje y
 104             let tanX = new THREE.Vector3(width / widthSegments, deltaX * amplitude, 0).normalize();
 105             let tanY = new THREE.Vector3(0, deltaY * amplitude, height / heightSegments).normalize();
 106 
 107             // Calculamos el vector normal a la superficie
 108             let n = new THREE.Vector3();
 109             n.crossVectors(tanY, tanX);
 110 
 111             // Añadimos los valores de los vectores normales al array de normales
 112             normals.push(n.x);
 113             normals.push(n.y);
 114             normals.push(n.z);
 115 
 116             uvs.push(i / (widthSegments - 1));
 117             uvs.push(j / (heightSegments - 1));
 118 
 119             if (i == widthSegments - 2 || j == heightSegments - 2) continue;
 120 
 121             // Ensamblamos los triangulos
 122             indices.push(i + j * quadsPerRow);
 123             indices.push(i + 1 + j * quadsPerRow);
 124             indices.push(i + 1 + (j + 1) * quadsPerRow);
 125 
 126             indices.push(i + j * quadsPerRow);
 127             indices.push(i + 1 + (j + 1) * quadsPerRow);
 128             indices.push(i + (j + 1) * quadsPerRow);
 129         }
 130     }
 131 
 132     geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
 133     geometry.setAttribute('normal', new THREE.Float32BufferAttribute(normals, 3));
 134     geometry.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2));
 135     geometry.setIndex(indices);
 136 
 137     return geometry;
 138 }
 139 
 140 function onTextureLoaded(key, texture) {
 141     texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
 142     textures[key].object = texture;
 143     console.log('Texture `' + key + '` loaded');
 144 }
 145 
 146 function loadTextures(callback) {
 147     const loadingManager = new THREE.LoadingManager();
 148 
 149     loadingManager.onLoad = () => {
 150         console.log('All textures loaded');
 151         callback();
 152     };
 153 
 154     for (const key in textures) {
 155         console.log("Loading textures");
 156         const loader = new THREE.TextureLoader(loadingManager);
 157         const texture = textures[key];
 158         texture.object = loader.load(
 159             texture.url,
 160             onTextureLoaded.bind(this, key),
 161             null,
 162             (error) => {
 163                 console.error(error);
 164             }
 165         );
 166     }
 167 }
 168 
 169 function main() {
 170 }
 171 
 172 loadTextures(main);