Displacement Mapping y aproximaciones Prof. Rhadamés Carmona Universidad Central de Venezuela Última revisión: 08-Oct-2009 Contenido • Introducción • Displacement Mapping • Subdividir y Desplazar, Ray Tracing, Volume Rendering Slicing • Bump Mapping, Normal Mapping • Parallax Mapping, Steep Parallax Mapping • Otros: Relief Mapping, Interval Mapping, View Dependent Displacement Mapping… Displacement Mapping • Mediante una textura, hace que los vértices de la geometría se desplace en dirección de su normal. • Requiere de un alto nivel de tesselation de los polígonos para obtener “micropolígonos” cuyo tamaño se ajuste idealmente a un píxel de la imagen. • Se puede utilizar para generar animaciones en batch, pero no para aplicaciones real time. Displacement Mapping • La geometría se desplaza en dirección a la normal. Por ejemplo, el gris 128 significa “no desplazamiento”. Valores inferiores a 128 hacen el efecto de hundido. Valores sobre 128 hace el efecto de chichones. Para mejorar la precisión, se pude utilizar 16 bits y no 8 bits. Displacement Mapping Se usa también para crear terrenos con una mapa de alturas y un plano Tesselado en muchos triángulos. En este caso se le denomina vertex displacement mapping, y puede ser implementado trivialmente en el vertex shader. Displacement Mapping struct vertex { float4 position : POSITION; float3 normal : NORMAL; float4 color0 : COLOR0; float2 texcoord0 : TEXCOORD0; }; fragment main( vertex IN, uniform sampler2D displacementTexture : TEXUNIT1, uniform float4 displacementScaler ) { fragment OUT; // Perform a texture look-up for the displacement value. float4 displacement = tex2D( displacementTexture, IN.texcoord0 ); // Now, use the displacment value to move or displace the current vertex // along the surface normal. We'll also throw in a scaling factor so we'll // have better control over the effect. float4 displacedPosition = float4( IN.normal * (displacement.x * displacementScaler.x), 0.0 ) + IN.position; struct fragment { float4 position : POSITION; float4 color0 : COLOR0; float2 texcoord0 : TEXCOORD0; }; OUT.position = mul( modelViewProj, displacedPosition ); OUT.color0 = IN.color0; OUT.texcoord0 = IN.texcoord0; return OUT; uniform float4x4 modelViewProj : state.matrix.mvp; } Displacement Mapping Mapa de bump Creado con Nurbs Mapa de Normales Geometría Desplazada Displacement Mapping Mapa sin Suavizar Mapa Suavizado Displacement Mapping • Es un problema tanto obtener un mallado muy fino, como su procesamiento. • Aproximaciones – – – – – – – – – Subdividir y Desplazar Ray Tracing Volume Slice Rendering Parallax Mapping, Steep Parallax Mapping Bump Mapping Normal Mapping Relief Mapping Interval Mapping View Dependent Displacement Mapping Displacement Mapping • Subdividir y Desplazar: se subdivide cada triángulo en 4 triángulos recursivamente hasta llegar a la resolución deseada. Esto podría realizarse con ayuda del geometric shader. Luego se aplica el vertex displacement mapping con el vertex shader. Displacement Mapping • Ray Tracing: la idea es intersectar los rayos con el mapa de alturas “on the fly” sin generar un mallado intermedio. Esta técnica está relacionada con Steep Parallax Mapping. Plano de referencia Desplazamiento máximo Displacement Mapping • Volume Rendering Slicing – Partimos de un plano y un mapa de desp. – Hacemos un strude del polígono en dirección a su normal – Por cada slice se dibujan sólo los píxeles con altura >= a la altura del slice – Esto se logra con el alpha test, donde el =altura en [0,1] – Requiere mucho “fill rate” pero poca geometría Displacement Mapping • Volume Rendering Slicing Jan Kautz, Hans-Peter Seidel. “Hardware accelerated displacement mapping for image based rendering”. No description on Graphics interface 2001. pp. 61-70. Canadian Information Processing Society. Bump Mapping • No perturba la geometría. Sólo las normales del objeto. • Las normales se perturban en función de un mapa de bump. • Blinn, en 1978, define la normal perturbada como: n' n Fu (n Pv) Fv(n Pu ) • • • • • P(u,v) = (X(u,v), Y(u,v), Z(u,v)) es la ecuación paramétrica de un objeto 3D dP(u,v)/du = Pu es la derivada parcial de la superficie en (u,v) respecto de u dP(u,v)/dv = Pv es la derivada parcial de la superficie en (u,v) respecto de v Fu(u,v)=Fu es la diferencia finita (derivada parcial) del mapa de bump respecto de u Fv(u,v)=Fv es la diferencia finita (derivada parcial) del mapa de bump respecto de v n(u, v) n P (u ,v ) P (u ,v ) u v P (u ,v ) P (u ,v ) u v Bump Mapping • Si la función de perturbación es una imagen en escala de grises, entonces: F (u, v) F (u 1, v) F (u 1, v) u 2 F (u, v) F (u, v 1) F (u, v 1) v 2 Bump Mapping • Ejemplo de ecuaciones paramétricas 3D: 0<=u,v<=6.28319 X(u,v)=COS(u)*u*(1.0+COS(v)/2.0) Y(u,v)=u/2.0*SIN(v) Z(u,v)=SIN(u)*u*(1.0+COS(v)/2.0) [0,2], [-, ] P( , ) (rCosSen , rSenSen , rCos ) P( , ) ( rSenSen , rCosSen ,0) P( , ) ( rCosCos , rSenCos , rSen ) Displacement vs. Bump Displacement Mapping Bump Mapping Displacement vs. Bump Displacement Mapping Bump Mapping Normal Mapping • Para mallados 3D, es más usual utilizar un mapa de normales. • La normal de cada punto de la superficie puede definirse en el espacio tangente. • Por cada punto de la superficie necesitamos el vector normal N, y el vector tangente T que va en dirección a la coordenada s de textura. El vector binormal o binomial se obtiene en el shader como B=NxT. Similarmente, se puede obtener N conociendo a T y B como N=TxB. Normal Mapping • Un punto (x,y,z) en coordenadas objetos, tiene asociado una coordenada de textura (s,t) y un espacio tangente. Muestreando sobre el normal map en (s,t) sabemos la normal del objeto en el espacio tangente. Mediante la matrix TBN, podemos llevar esta normal al espacio objeto, donde está la luz. Con la normal y la luz en el mismo espacio, procedemos a calcular un modelo de iluminación. Equivalentemente se podría llevar la luz al espacio tangente para calcular el modelo de iluminación, pero requiere de la inversa de la TBN. Normal Mapping • Sistemas de coordenadas involucrados en el normal mapping: Normal Mapping • Si el objeto fuese paramétrico, el valor de T sencillamente sería dP(u,v)/du, el valor de B sería dP(u,v)/dv, y el gradiente sería el producto cruz de ambos vectores. Al normalizar el gradiente obtenemos N. • Si el objeto es una malla, se calcula N en un vértice como el promedio de las normales de sus polígonos adyacentes. Para los fragmentos se interpola la normal de los vértices con el GPU. Normal Mapping • Los vectores T, B que definen el espacio tangente en un punto (x,y,z) del objeto corresponden con los vectores (1,0) y (0,1) del espacio de textura. • Debido a esta correspondencia, los vectores T y B pueden ser deducidos mediante la relación entre vértices en el espacio objeto y sus coordenadas de textura. Normal Mapping • Relación entre es espacio objeto, el espacio tangente y el espacio de textura. • Como tenemos unas coordenadas (ui,vi) para cada vértice del triángulo pi, podemos establecer 3 ecuaciones de la forma: pi ui *T vi * B Normal Mapping Lo cual resulta en las siguientes ecuaciones: p1 u1 *T v1 * B p2 u2 *T v2 * B p3 u3 *T v3 * B Restando las ecuaciones (2) y (3) con (1): p2 p1 (u2 u1) *T (v2 v1) * B p3 p1 (u3 u1) *T (v3 v1) * B Normal Mapping Multiplico la primera ecuación por (v3-v1) y la segunda por (v2-v1) ( p2 p1) * (v3 v1) (u2 u1) * (v3 v1) *T (v2 v1) * (v3 v1) * B ( p3 p1) * (v2 v1) (u3 u1) * (v2 v1) *T (v3 v1) * (v2 v1) * B T (v3 v1 ) * ( p2 p1 ) (v2 v1 ) * ( p3 p1 ) (u2 u1 ) * (v3 v1 ) (v2 v1 ) * (u3 u1 ) Multiplico la primera ecuación por (u3-u1) y la segunda por (u2-u1) ( p2 p1) * (u3 u1) (u2 u1) * (u3 u1) *T (v2 v1) * (u3 u1) * B ( p3 p1) * (u2 u1) (u3 u1) * (u2 u1) *T (v3 v1) * (u2 u1) * B B (u3 u1 ) * ( p2 p1 ) (u2 u1 ) * ( p3 p1 ) (v2 v1 ) * (u3 u1 ) (u2 u1 ) * (v3 v1 ) Normal Mapping Con T y B podemos calcular N=TxB. Con los 3 vectores TBN formamos una base. Mientras N es a T y B, T no necesariamente es a B. Por lo tanto la matriz TBN no es ortonormal. Mediante la matriz TBN podemos mapear un vector del espacio tangente (expresado en función de T,B,N) a su equivalente vector en coordenadas objeto, expresado en términos de los vectores canónicos (1,0,0), (0,1,0), (0,0,1). Para calcular el modelo de iluminación de lambert, la luz y la normal deben estar en le mismo espacio. Nos interesa llevar la luz del espacio objeto al espacio tangente. Equivalentemente podríamos llevar la normal del mapa del espacio tangente al espacio objeto. Sin embargo, es más económico llevar la luz del espacio objeto al espacio tangente puesto que esa multiplicación matriz-vector se haría una sola vez por triángulo, mientras que llevar la normal al espacio tangente sería por fragmento (luz en el infinito). En este caso se necesita la inversa de la TBN. Si la luz es puntual, entonces ambos enfoques son equivalentes, pues requieren mapear un vector de un espacio a otro por fragmento. Normal Mapping Calculando la inversa de la TBN: una alternativa es darle la TBN a OpenGL en una matriz (por ejemplo la matriz de textura) , y el vertex shader recibirá la inversa ya precalculada. La otra alternativa es invertir la TBN en el CPU, y darle los vectores TBN a OpenGL pero correspondiendo a la matriz inversa. Normal Mapping //Vertex program con luz en Inf. varying vec4 passcolor; //The vertex color passed varying vec3 LightDir; //The transformed light direction, to pass to the fragment shader attribute vec3 tangent; //The inverse tangent to the geometry attribute vec3 binormal; //The inverse binormal to the geometry uniform vec3 lightdir; //The direction the light is shining void main() { //Put the color in a varying variable passcolor = gl_Color; //Put the vertex in the position passed gl_Position = ftransform(); //Construct a 3x3 matrix from the geometry’s inverse tangent, binormal, and normal mat3 rotmat = mat3(tangent,binormal,gl_Normal); //Rotate the light into tangent space LightDir = rotmat * normalize(lightdir); //Normalize the light normalize(LightDir); //Use the first set of texture coordinates in the fragment shader gl_TexCoord[0] = gl_MultiTexCoord0; } Normal Mapping // Fragment program con luz en Inf. uniform sampler2D BumpTex; //The bump-map uniform sampler2D DecalTex; //The texture varying vec4 passcolor; //Receiving the vertex color from the vertex shader varying vec3 LightDir; //Receiving the transformed light direction void main() { //Get the color of the bump-map vec3 BumpNorm = vec3(texture2D(BumpTex, gl_TexCoord[0].xy)); //Get the color of the texture vec3 DecalCol = vec3(texture2D(DecalTex, gl_TexCoord[0].xy)); //Expand the bump-map into a normalized signed vector BumpNorm = (BumpNorm -0.5) * 2.0; //Find the dot product between the light direction and the normal float NdotL = max(dot(BumpNorm, LightDir), 0.0); //Calculate the final color gl_FragColor vec3 diffuse = NdotL * passcolor.xyz * DecalCol; //Set the color of the fragment... If you want specular lighting or other types add it here gl_FragColor = vec4(diffuse, passcolor.w); } Normal Mapping //Vertex program con luz puntual. varying vec4 passcolor; //The vertex color passed attribute vec3 tangent; //The tangent to the geometry attribute vec3 binormal; //The binormal to the geometry uniform vec3 lightPos; //The direction the light is shining void main() { //Put the color in a varying variable passcolor = gl_Color; //Put the vertex in the position passed gl_Position = ftransform(); //Use the first set of texture coordinates in the fragment shader gl_TexCoord[0] = gl_MultiTexCoord0; // light direction per vertex. It will be interpolated per fragment gl_TexCoord[1] = normalize(lightPos – gl_Vertex); } Normal Mapping // Fragment program con luz puntual uniform sampler2D BumpTex; //The bump-map uniform sampler2D DecalTex; //The texture varying vec4 passcolor; //Receiving the vertex color from the vertex shader uniform vec3 lightPos; //Receiving the light direction attribute vec3 tangent; //The tangent to the geometry attribute vec3 binormal; //The binormal to the geometry void main() { //Construct a 3x3 matrix from the geometry’s tangent, binormal, and interpolated normal mat3 rotmat = mat3(tangent,binormal,gl_Normal); //Get the color of the bump-map vec3 BumpNorm = vec3(texture2D(BumpTex, gl_TexCoord[0].xy)); //Get the color of the texture vec3 DecalCol = vec3(texture2D(DecalTex, gl_TexCoord[0].xy)); //Expand the bump-map into a normalized signed vector BumpNorm = rotmat* ((BumpNorm -0.5) * 2.0); //Find the dot product between the light direction and the normal float NdotL = max(dot(BumpNorm, normalize(gl_TexCoord[1]), 0.0); //Calculate the final color gl_FragColor vec3 diffuse = NdotL * passcolor.xyz * DecalCol; //Set the color of the fragment... If you want specular lighting or other types add it here gl_FragColor = vec4(diffuse, passcolor.w); } Parallax Mapping • Otros nombres: Virtual Displacement Mapping, offset mapping • Desplaza las coordenadas de textura en función del ángulo de visión en es espacio tangente y el mapa de alturas en cada punto. En ángulos de visión empinados, las coordenadas de textura se desplazan más dando la ilusión de profundidad por el efecto de paralaje por cambios del punto de vista. • Generalmente acompaña a las técnicas de bump mapping y normal mapping. Parallax Mapping Corrección de coordenada de textura en A Parallax Mapping • Aplicando un offset a las coordenada de textura adecuadamente, se produce el efecto deseado. • Inputs por fragmento: la coordenada de textura original T0, el valor de altura h de la superficie en ese punto (en 0,1), y un vector de visualización V normalizado del pixel al ojo en el espacio tangente. Parallax Mapping • La altura del mapa es primero llevado al rango deseado mediante hsb=h.s+b • El offset es calculado trazando un vector paralelo al polígono desde el punto de la superficie directamente sobre P hacia el vector de ojo Tn = To + ( hsb ∙ V{x, y} / V{z} ) Parallax Mapping Normal Mapping Normal + Parallax Mapping Steep Parallax Mapping • Utiliza trazado de rayos, y un mapa de alturas. La idea es caminar en el rayo, entrar en el volumen de alturas, y encontrar el punto de intersección del rayo con el mapa. La intersección más cercana es la visible realmente. • Produce self-oclussion y self-shadowing Steep Parallax Mapping Steep Parallax Mapping float step = 1.0 / n vec2 dt = E.xy * bumpScale / (n * E.z) float height = 1.0; vec2 t = texCoord.xy; vec4 nb = texture2DLod(NB, t, LOD); while (nb.a < height) { height -= step; t += dt; nb = texture2DLod(NB, t, LOD); } // ... Shade using N = nb.rgb Otros… • Relief mapping …. • Interval mapping …. • Dependent Displacement mapping…