Primitivas 3D

Facebooktwittergoogle_pluslinkedinmailFacebooktwittergoogle_pluslinkedinmail
Primitivas 3D

Primitivas 3D

En el artículo anterior aprendimos a crear un escenario de Processing y dibujar en él primitivas en 2D. Se trata de un proceso muy simple, pero que nos permite vislumbrar el enorme mundo de posibilidades gráficas que este entorno nos ofrece. Por supuesto, el verdadero poder de Processing viene de la mano de su conectividad con Arduino, pero a eso ya llegaremos.

En este artículo vamos a aprender a dibujar algunas primitivas 3D, que, en su momento, incorporaremos a nuestros sketches.

EMPEZANDO EN 3D

Lo primero, al igual que hacíamos en el artículo anterior, es inicializar el escenario, estableciendo las dimensiones del lienzo con la instrucción size. Sin embargo, en esta ocasión debemos agregar un tercer parámetro, una constante de Processing llamada P3D, que le indica al sketch que vamos a trabajar con objetos 3D. Si no establecemos esta constante, no podremos dibujar en 3D. Así pues, inicializaremos este primer sketch así:

size (200, 200, P3D);

El siguiente paso, cómo no, es establecer, un color de fondo, un color de línea y un color de relleno. Esto es igual que lo que aprendimos con las primitivas 2D, por lo que reproducimos directamente las tres líneas, sin entrar en mas detalles.

background(255);
stroke(0);
fill(255, 150, 100);

Ahora sí, vamos a empezar a dibujar. En este ejemplo dibujaremos una caja en 3D, vista en perspectiva, con los colores que hemos establecido. El siguiente paso es establecer el punto central de la caja que vamos a dibujar. Este punto se establece, inicialmente, con respecto a las coordenadas 0, 0, es decir, la esquina superior izquierda del lienzo. Lo haremos mediante la instrucción translate(), así:

translate(100, 100, -50);

Tu punto de vista, desde el eje Z.

Tu punto de vista, desde el eje Z.

Esta instrucción nos introduce algunos conceptos importantes. En primer lugar, los dos primeros argumentos indican los píxeles que nos desplazamos desde el punto de origen (en este caso la esquina superior izquierda) para establecer el centro de la figura que vamos a dibujar. Cómo el lienzo tiene, en nuestro ejemplo, 200 x 200 píxeles, el centro de la figura coincidirá con el centro del lienzo.

Lo del tercer parámetro tiene más enjundia. A partir de aquí tienes que empezar a pensar en tres dimensiones. Tu ya no estás viendo un lienzo de dibujo. Estás viendo un plano en el espacio, y lo estás viendo “desde arriba”, desde el eje Z. El tercer parámetro te dice en que posición quedará el objeto con respecto a tu punto de observación. Cuanto más pequeño sea este valor, más lejos de tí quedará el objeto, y más cerca del plano o, por expresarlo del mejor modo posible, del fondo de la pantalla. Por lo tanto, cuanto más reduzcas este valor, más pequeño verás el objeto final. El gráfico de tu izquierda revela tu posición de observador viendo, desde arriba el plano X-Y.

Si ahora dibujásemos una caja, no la veríamos en perspectiva, si no que sólo veríamos la cara superior. Por esta razón, nos parecería estar viendo sólo un cuadrado (o un rectángulo; en todo caso, un objeto 2D, no 3D). Antes de dibujar la caja, vamos a imponer alguna rotación, para tener una vista en perspectiva. Las rotaciones pueden establecerse en uno, dos o los tres ejes, mediante las instrucciones rotateX(), rotateY() y rotateZ(). Estas tres actuan rotando el objeto, cdad uno sobre su eje. Para ello, reciben un argumento que es el ángulo de rotación, expresado en radianes. Si te acuerdas, en el artículo anterior hablamos de los ángulos en grados y radianes. Todo lo que dijimos al respecto entonces es válido aquí también. Vamos a darle a nuestra caja un poco de giro en los ejes X e Y, de momento.

rotateY(0.8);
rotateX(0.8);

primera_cajaY, por fín, vamos a dibujar la caja. Para ello recurrimos a la instrucción box(). Esta recibe, normalmente, tres parámetros, que indican la anchura (eje X), altura (eje Y) y profundidad (eje Z) de la caja, en píxeles. Alternativamente (y es lo que haremos aquí), se puede poner un sólo valor. En ese caso, este valor será para las tres dimensiones, y la caja será completamente cuadrada:

box (100);

Y ya está. Ejecuta tu sketch de Processing y verás una caja en perspectiva, similar a la de la derecha de este texto.

Antes de seguir adelante, experimenta un poco. Cambia algunos valores, y observa el resultado al ejecutar de nuevo. De esta forma, te familiarizarás un poco con el punto de vista 3D. A continuación, te dejo este sketch completo para que lo copies y pegues en tu IDE Processing.

ATENCIÓN. La función translate(), que hemos usado para posicionar el centro del objeto que vamos a dibujar usa valores relativo, no absolutos. Eso quiere decir que si en el mismo sketch vamos a dibujar otro objeto 3d y usamos, por ejemplo, translate (10, 10, 0);, el centro se desplazará 10 píxeles hacia la derecha y hacia abajo del centro del último objeto dibujado, manteniendo la misma distancia en el eje Z, dónde hemos puesto 0. Sólo se toman valores absolutos la primera vez que se usa esta función en el sketch (en realidad, no son absolutos, sino relativos a 0, 0, 0 lo que, en la práctica, es lo mismo, claro).

EL MUNDO ES REDONDO

Así que vamos a aprender a dibujar esferas 3D. Observa el sketch primera_esfera.pde:

Esfera triangulada

Esfera triangulada

Las cuatro primeras líneas ya no ofrecen misterio. La función sphere() recibe, cçomo argumento, el radio de la esfera y la dibuja partiendo de las coordenadas indicadas. El problema es que el resultado a lo mejor no es lo que esperábamos, cómo puedes ver en la imagen de la izquierda.

Una esfera con iluminación.

Una esfera con iluminación.

Los programas con capacidades gráficas 3D renderizan los modelos a partir de una malla de triángulos, que son, internamente, fáciles de manejar desde un punto de vista matemático, así que, para los autores de estos programas, es la solución perfecta. Eso está muy bien, pero nosotros queremos una esfera “límpia”, dónde no se aprecie la triangulación.

Podríamos pensar que la solución pasa por quitarle el borde. En el sketch, sustituimos la línea stroke(0); por noStroke(). La idea es buena, pero el resultado no tanto. Si lo pruebas, verás que ya no se ve ningún triángulo, pero la esfera se ve cómo un círculo plano, sin ningún efecto de 3D. Hay que buscar otra solución. Y esta consiste en iluminar la esfera. Antes de dibujarla, vamos a llamar a la función lights(). La iluminación en un escenario 3D es muy importante, y da mucho juego. De momento, sólo “encendemos la luz” y alejamos un poco la perspectiva, logrando el resultdo que ves a la derecha. El skecth primera_iluminacion.pde aparece a continuación (y, por cierto, sí: hemos quitado los contornos, para evitar visualizar la malla).

La configuración de la iluminación ambiente, y todas las posibilidades que ofrece es un tema que da mucho juego, y que desarrollaremos en otro artículo. De momento, nos quedamos con este poquito, para empezar.

ACERCA DE LA MALLA

Hemos aprendido a ocultar la malla, para que no sea visible una superficie triangulada, que “hace feo”. Sin embargo, aunque no la veamos, la malla sigue estando ahí. El objeto se renderizará siempre a partir de una malla de triángulos, tanto si la vemos cómo si no.

La densidad de esta triangulación determinará la resolución del objeto final. A mayor número de triángulos, tendremos una malla más densa y, por tanto, una esfera más definida. Cómo contrapartida, si aumentamos mucho el número de triángulos, serán necesarios más cálculos por parte de Processing para renderizar la esfera, lo que puede ralentizar el funcionamiento. Lo ideal es buscar una solución de compromiso, de modo que

Dicotomía en la resolución.

Dicotomía en la resolución.

obtengamos un resultado aceptable a la vista, pero sin sobrecargar el equipo. La renderización 3D consume muchos recursos y, aunque la mayoría de cosas que hagamos con Processing, podremos llevarlas a cabo en un PC “normalito”, si alguna vez nos metemos en “palabras mayores” quizá necesitemos más memoria, mejor tarjeta gráfica… Bueno, los jugones empedernidos saben de que va el tema. De momento, no debemos preocuparnos.

Para establecer la densidad de la malla contamos con la función sphereDetail(). Esta recibe un argumento, que indica el número de triángulos que se usan para renderizar los 360º de una esfera. El valor por defecto (que nos sirve en la mayoría de las ocasiones) es de 30. Tambiién podemos invocar a esta función con dos argumentos. En ese caso, el primero establece la resolución en horizontal, y el segundo en vertical. Para entender cómo puede haber una dicotomía en la resolución, observa el sketch distintas_resoluciones.pde, listado a continuación, y que nos da un resultado cómo el que vemos a la derecha de estas líneas.

En artículos posteriores aprenderemos a crear otras formas tridimensionales más complejas, cómo conos, cilinros, toros, pirámides, etc, así como distintas combinaciones de unos y otros. De momento lo dejamos aquí, para que vayas probando distintos valores.

     

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *