|
Sabes que has estado trazando demasiado tiempo cuando ... |
... Te descubres a ti mismo deseando haber prestado más atención en las clases de matemáticas a todas esas fórmulas que pensaste que no tendrían ninguna utilidad para ti en la vida real. |
-- Jeff Lee |
Las Isosuperficies son formas descritas por funciones matemáticas.
En contraste con otras formas con base matemática en POV-Ray, las isosuperficies son aproximadas durante el dibujado y por ello a veces son más difíciles de manejar. De todas formas, ofrecen interesantes posibilidades.
Tener algún conocimiento sobre funciones matemáticas y geometría es útil, pero no es requisito indispensable para trabajar con isosuperficies.
Para empezar, elegiremos la función más simple: x
.
El valor de esta función es exactamente la coordenada x actual.
El objeto isosuperficie toma esa función como una función definida por el usuario:
isosurface { function { x } contained_by { box { -2, 2 } } }
La forma resultante es realmente simple: un cubo.
El hecho de que sea un cubo está causado por el objeto contenedor, el cual es necesario para una isosuperficie. Podéis utilizar un cubo o una esfera para este propósito.
Por ello, de hecho sólo se construye una cara del cubo por la función. Esta superficie nacerá donde la coordenada x es 0, ya que 0 es el umbral (threshold) por defecto. Normalmente no hay razón para cambiarlo, ya que es el valor más común y sugestivo, pero puedes especificar algo diferente añadiendo
threshold 1
a la definición de isosuperficie.
Como podéis ver, la superficie está ahora en la coordenada x 1.
También podemos eliminar las superficies visibles del objeto contenedor añadiendo la palabra 'open' a la definición de la isosuperficie.
Para aclarar qué superficies son la isosuperficie real y cuáles son causadas por el objeto contenedor, el color será diferente en las imágenes siguientes.
Ahora reemplazamos la función utilizada con algo diferente:
function { x+y }
function { x+y+z }
Nota: aquí se añade 'max_gradient 4' a la definición de isosuperficie. Esto se explicará más adelante.
Todas estas funciones describen planos que pasan a través del origen. La función simplemente describe el vector de la normal de este plano.
Las dos funciones siguientes dan los mismos resultados:
function { abs(x)-1 }
function { sqrt(x*x)-1 }
Podéis ver que ahora hay dos planos. La razón es que ambas fórmulas
tienen las mismas dos soluciones (cuando el valor de la función es 0),
a saber x=-1
y x=1
.
Por mucho que mezclemos estos elementos en combinaciones diferentes, los resultados siempre consistirán en superficies planas:
function { abs(x)-1+y }
function { abs(x)+abs(y)+abs(z)-2 }
Se pueden conseguir muchos tipos diferentes de superficies curvadas con funciones no lineales.
function { pow(x,2) + y }
Podéis ver la forma parabólica causada por la función "elevado al cuadrado".
Para obtener una superficie cilíndrica podemos utilizar la siguiente función.
function { sqrt(pow(x,2) + pow(z,2)) - 1 }
Esto describe un círculo en 2 dimensiones, como la 3ª dimensión es constante, obtenemos un cilindro:
No es difícil transformarlo en un cono, simplemente necesitamos añadir un componente lineal en el la dirección y:
function { sqrt(pow(x,2) + pow(z,2)) + y }
Y por supuesto, también podemos hacer una esfera:
function { sqrt(pow(x,2) + pow(y,2) + pow(z,2)) - 2 }
Aquí el 2
especifica el radio.
Hay un montón de funciones internas disponibles en POV-Ray. Por
ejemplo, una esfera puede ser generada también con function {
f_sphere(x, y, z, 2) }
Estas funciones están declaradas en el
archivo de inclusión functions.inc
. La mayoría de ellas
son más complicadas y normalmente es más rápido utilizarlas en lugar de
su equivalente de código a mano. Para ver más detalles, mira la lista completa.
Lo siguiente hace un torus igual al objeto torus de POV-Ray:
#include "functions.inc" isosurface { function { f_torus(x, y, z, 1.6, 0.4) } contained_by { box { -2, 2 } } }
Los parámetros 4º y 5º son los radios mayor y menor, igual que los
valores correspondientes en el objeto torus{}
.
Los parámetros X, Y y Z son necesarios, porque es una función declarada. También podéis declarar funciones vosotros mismos como se explica en la sección de referencia.
También podemos simular en parte la CSG (Geometría Constructiva de Sólidos) con funciones de isosuperficies. Si no sabes nada de CSG te sugerimos que le eches un vistazo primero a "¿Qué es CSG?" o al apartado correspondiente de la sección de referencia.
Vamos a utilizar dos funciones: un cilindro y un cubo rotado:
#declare fn_A = function { sqrt(pow(y,2) + pow(z,2)) - 0.8 } #declare fn_B = function { abs(x)+abs(y)-1 }
Si los combinamos de esta manera, obtenemos una "fusión":
function { min(fn_A(x, y, z), fn_B(x, y, z)) }
Se puede obtener una "intersección" utilizando max()
en lugar de min()
:
function { max(fn_A(x, y, z), fn_B(x, y, z)) }
Por supuesto, también es posible una "diferencia", sólo tenemos que añadir un signo de menos (-) antes de la segunda función:
function { max(fn_A(x, y, z), -fn_B(x, y, z)) }
Aparte del CSG básico, podéis obtener transiciones suaves entre las diferentes superficies (como con el objeto blob)
#declare Blob_threshold=0.01; isosurface { function { (1+Blob_threshold) -pow(Blob_threshold, fn_A(x,y,z)) -pow(Blob_threshold, fn_B(x,y,z)) } max_gradient 4 contained_by { box { -2, 2 } } }
El valor Blob_threshold
influye en la suavidad de las
transición entre formas. Un valor bajo conlleva bordes más afilados.
Algunas de las funciones internas tienen una estructura aleatoria o parecida al ruido
Junto con las funciones de pigmento, son una de las herramientas más poderosas para el diseño de isosuperficies. Podemos añadir desplazamiento real de la superficie a los objetos en lugar de la usual perturbación ya conocida de la sentencia normal{}.
Las funciones internas relevantes son:
f_noise3d(x,y,z)
global_settings{}
y genera estructuras
como el patrón bozo.f_noise_generator(x,y,z, noise_generator)
f_ridged_mf(x,y,z, H, Lacunarity, Octaves, Offset, Gain,
noise_generator)
f_ridge(x,y,z, Lambda, Octaves, Omega, Offset, Ridge,
noise_generator)
f_hetero_mf(x,y,z, H, Lacunarity, Octaves, Offset, T,
noise_generator)
Utilizar ruido3d puro como una función da como resultado la siguiente imagen:
function { f_noise3d(x, y, z)-0.5 }
Nota: -0.5
está apuntado
aquí sólo para hacerlo casar con el valor de umbral 0, la función f_noise3d
retorna valores entre 0 y 1.
Con ésta y las otras funciones puedes generar objetos similares a campos de elevación (height_fields), teniendo la ventaja de que se puede conseguir una alta resolución sin grandes requisitos de memoria.
function { x+f_noise3d(0, y, z) }
Por supuesto, La función de ruido se puede restar, lo que da como resultado una versión 'invertida':
function { x-f_noise3d(0, y, z) }
En las últimas dos imágenes, añadimos la función de ruido a una función de plano. El parámetro x estaba fijado a 0 por lo que la función de ruido es constante en la dirección x. De esta forma conseguimos la estructura típica de un campo de elevación.
Por supuesto, podemos añadir ruido a cualquier otra función. Si la función de ruido es muy fuerte, ello puede dar como resultado varias superficies separadas.
function { f_sphere(x, y, z, 1.2)-f_noise3d(x, y, z) }
Ésta es una función de ruido aplicada a la superficie de una esfera, podemos influir en la intensidad del ruido multiplicándolo por un factor y cambiar el escalado multiplicando los parámetros de las coordenadas:
function { f_sphere(x, y, z, 1.6)-f_noise3d(x*5, y*5, z*5)*0.5
}
Como alternativa a las funciones de ruido, también podemos utilizar cualquier pigmento en una función:
#declare fn_Pigm=function { pigment { agate color_map { [0 color rgb 0] [1 color rgb 1] } } }
Esta función es una función de vector que retorna un vector (color). Para utilizarlo en una isosuperficie, tenemos que especificar el componente a utilizar (para tener más detalles ver la sección de referencia).
function { f_sphere(x, y, z, 1.6)-fn_Pigm(x/2, y/2,
z/2).gray*0.5 }
Hay un montón de posibilidades con las funciones de pigmento, pero probablemente os habréis dado cuenta de que esto se dibuja realmente despacio.
Para optimizar la aproximación de la isosuperficie y conseguir la máxima velocidad de dibujado, es importante adaptar ciertos valores (ver también "Mejorando la velocidad de las isosuperficies" en la sección de referencia).
precisión (accuracy)
El valor de precisión influye en cuan precisa es calculada la
geometría de la superficie. Valores bajos dan resultados más precisos,
pero también más lentos. El valor por defecto de 0.001
es
realmente bajo. Hemos utilizado este valor en todos los ejemplos
anteriores, pero normalmente podéis elevarlo bastante más y por tanto
obtener mayor velocidad.
max_gradient
Para que POV-Ray pueda encontrar la superficie real es importante
que conozca el gradiente máximo de la función, que significa cómo de
rápido cambia el valor de la función. Podemos especificar un valor con
el identificador max_gradient
. Valores bajos de
max_gradient producen un dibujado más rápido, pero si el valor
especificado está por debajo del gradiente máximo real de la función,
pueden aparecer agujeros u otros defectos en la superficie.
Por la misma razón, no deben usarse funciones con un gradiente
infinito. Esto es de aplicación a las funciones de pigmento con un
patrón de ladrillo (brick) o ajedrezado (checker) por ejemplo. Por ello
también debéis tener cuidado cuando utilicéis select()
en
la funciones de isosuperficie.
Si el gradiente máximo real difiere mucho del valor especificado,
POV-Ray emite una advertencia junto con el valor hallado para el
gradiente máximo. Habitualmente es suficiente con usar este número para
el parámetro max_gradient
para obtener resultados rápidos
y correctos.
POV-Ray también puede cambiar dinámicamente el max_gradient
cuando especifiquéis evaluate
con 3 parámetros en la
definición de la isosuperficie. Respecto a los detalles sobre esto y
otras cosas, ver la sección de
referencia.
|