P3D: Texturas en Mallas
En el mundo 3D podemos utilizar imágenes de mapa de bits como texturas de nuestras mallas.
Hay múltiples formas de asignar una imagen como textura de una malla. El sistema de coordenadas UV nos permitirá determinar con precisión qué puntos de nuestra textura corresponden a qué vértices de nuestra malla.
1 Coordenadas UV
Se les llaman coordenadas UV a las que nos permiten ubicar puntos en una textura. El eje U es el horizontal y comienza en 0 en el borde izquierdo, y el eje V es el eje vertical que comienza en 0 en el borde superior.
1.1 Modos: IMAGE
o NORMAL
El valor máximo de cada coordenada UV va a depender del modo que escojamos.
La función para asignarlo es textureMode( )
, que puede tener alguno de los siguientes dos argumentos:
IMAGE
: Las coordenadas UV corresponden a las coordenadas de los pixeles de la imagen original.NORMAL
: Las coordenadas UV se normalizan: la U va de 0 (borde izquierdo) a 1 (borde derecho), y la V de 0 (borde superior) a 1 (borde inferior)
Es recomendable trabajar con el modo NORMAL
pues nos permite pensar en términos de porcentajes, sin importar las dimensiones de la imagen original:
// normaliza las coordenadas UV de las texturas
textureMode( NORMAL );
1.2 Wrapping: CLAMP
o REPEAT
Con el modo de wrapping indicamos qué queremos que suceda al utilizar coordenadas UV que excedan los límites de la textura.
La función para asignarlo es textureWrap( )
, que puede tener alguno de los siguientes dos argumentos:
CLAMP
: La textura original solo existe dentro de sus límites, y fuera de ellos se asigna el color más cercano a los bordesREPEAT
: La textura se repite tanto en el eje U como en V.
En general puede convenir utilizar la modalidad de REPEAT
:
// repite las texturas en sus ejes U y V
textureWrap( REPEAT );
1.3 Mapeo de coordenadas
Para utilizar una textura en una malla, necesitamos:
- Indicar una textura de tipo
PImage
oPGraphics
con la funcióntexture( )
- Asignar a cada vértice, no solo sus coordenadas x,y,z, sino también las coordenadas u,v que le corresponderían de la textura.
A continuación algunos ejemplos donde se ve en acción.
La textura utilizada es la siguiente imagen:
2 Ejemplos
2.1 Plano con textura de imagen
En este ejemplo se carga una imagen y se utiliza como textura de un plano en XY.
Observa la correspondencia entre las coordenadas x,y,z, u,v
de los cuatro vértices.
Experimenta cambiando las coordenadas de los vértices para observar qué pasa con la malla y su textura.
import peasy.PeasyCam;
PeasyCam cam;
// declara variable para textura
PImage textura;
void setup() {
size(800, 600, P3D);
new PeasyCam(this, 40);
cam = perspective(PI/3, 1.0*width/height, 1, 100);
// carga imagen en la variable
loadImage("ladrillos.jpg");
textura =
// configura modos de textura
textureMode( NORMAL ); // coords normalizadas
textureWrap( REPEAT ); // repite textura
}
void draw() {
background(255);
lights();
beginShape(QUADS);
texture( textura ); //asigna textura a malla
// coordenadas x,y,z, u,v:
vertex( 0, 0, 0, 0, 0); // superior izquierda
vertex( 5, 0, 0, 1, 0); // superior derecha
vertex( 5, 5, 0, 1, 1); // inferior derecha
vertex( 0, 5, 0, 0, 1); // inferior izquierda
endShape();
}
Importante: la imagen debe estar en el mismo directorio que el sketch.
Aquí más notas sobre cargar imágenes:
[Imágenes: Abrir, mostrar, leer]
2.2 Plano con textura generada (PGraphics
)
Este ejemplo es similar al anterior, pero con una imagen generada algorítmicamente dentro de un canvas virtual o “capa” de tipo PGraphics
.
Nota cómo las funciones de dibujo que ya conoces se utilizan para dibujar dentro de la capa.
import peasy.PeasyCam;
PeasyCam cam;
// declara variable para capa
PGraphics capa;
void setup() {
size(800, 600, P3D);
new PeasyCam(this, 40);
cam = perspective(PI/3, 1.0*width/height, 1, 100);
createGraphics( 400, 400 );
capa =
// configura modos de textura
textureMode( NORMAL ); // coords normalizadas
textureWrap( REPEAT ); // repite textura
}
void draw() {
background(255);
lights();
// dibuja en la capa dos círculos que crecen
beginDraw();
capa.background( 0 ); // fondo negro
capa.stroke( 255 );
capa.noFill();
capa.strokeWeight(10);
capa.circle( 200, 200, frameCount%400);
capa.circle( 200, 200, (frameCount+200)%400);
capa.endDraw();
capa.
beginShape(QUADS);
texture( capa ); //asigna textura a malla
// coordenadas x,y,z, u,v:
vertex( 0, 0, 0, 0, 0); // superior izquierda
vertex( 5, 0, 0, 1, 0); // superior derecha
vertex( 5, 5, 0, 1, 1); // inferior derecha
vertex( 0, 5, 0, 0, 1); // inferior izquierda
endShape();
}
Notas sobre capas:
La técnica de animación utilizada en el ejemplo se aborda en estas notas:
[Animación en función del tiempo (frameCount)]
2.3 Cilindro con textura de imagen
Este ejemplo parte de un cilindro tradicional, asignando a cada cara cuadrangular una fracción proporcional de la textura en U.
Mira qué pasa si cambias el factor que multiplica a la cantidad correspondiente a la coordenada U.
import peasy.PeasyCam;
PeasyCam cam;
// declara variable para textura
PImage textura;
void setup() {
size(800, 600, P3D);
new PeasyCam(this, 40);
cam = perspective(PI/3, 1.0*width/height, 1, 100);
// carga imagen en la variable
loadImage("ladrillos.jpg");
textura =
// configura modos de textura
textureMode( NORMAL ); // coords normalizadas
textureWrap( REPEAT ); // repite textura
}
void draw() {
background(255);
lights();
noStroke();
rotateX( PI/2 );
cilindro(5, 10, 20);
}
void cilindro(float r, float h, int N){
// dibuja un cilindro tradicional
// r: radio de la base
// h: altura
// N: número de divisiones en la base
// variables auxiliares
float x,y,angulo;
beginShape(QUAD_STRIP);
texture( textura ); // asigna textura
for(int i=0; i<=N; i=i+1){
angulo = i*TWO_PI/N;cos(angulo);
x = r*sin(angulo);
y = r*// vértice en la base
vertex(x,y,0, 1.0*i, 1); // uv: 1.0*i, 1
// vértice en la tapa
vertex(x,y,h, 1.0*i, 0); // uv: 1.0*i, 0
}endShape();
}