texturas online normal linea crear crazy con c++ opengl graphics

c++ - online - ¿Generando un mapa normal desde un mapa de altura?



normal map photoshop (4)

Estoy trabajando en la producción de parches de suciedad mediante el uso de fractales aleatorios para un videojuego. Ya he generado un mapa de altura utilizando el algoritmo de desplazamiento del punto medio y lo guardé en una textura. Tengo algunas ideas de cómo convertir eso en una textura de normales, pero algunos comentarios serían muy apreciados.

Mi textura de altura es actualmente una imagen en escala de grises de 257 x 257 (los valores de altura se escalan para fines de visibilidad):

Mi opinión es que cada píxel de la imagen representa una coordenada reticular en una cuadrícula de 256 x 256 (de ahí, por qué hay 257 x 257 alturas). Eso significaría que lo normal en la coordenada (i, j) está determinado por las alturas en (i, j), (i, j + 1), (i + 1, j) y (i + 1, j + 1 ) (llame a los A, B, C y D, respectivamente).

Entonces, dadas las coordenadas 3D de A, B, C y D, tendría sentido:

  1. divide los cuatro en dos triángulos: ABC y BCD
  2. calcular las normales de esas dos caras a través de productos cruzados
  3. dividido en dos triángulos: ACD y ABD
  4. calcular las normales de esas dos caras
  5. promedio de las cuatro normales

... o hay un método mucho más fácil que me falta?


Mi opinión es que cada píxel de la imagen representa una coordenada reticular en una cuadrícula de 256 x 256 (de ahí, por qué hay 257 x 257 alturas). Eso significaría que lo normal en la coordenada (i, j) está determinado por las alturas en (i, j), (i, j + 1), (i + 1, j) y (i + 1, j + 1 ) (llame a los A, B, C y D, respectivamente).

No. Cada píxel de la imagen representa un vértice de la cuadrícula, por lo que intuitivamente, a partir de la simetría, su normalidad está determinada por las alturas de los píxeles vecinos (i-1, j), (i + 1, j), (i, j- 1), (i, j + 1).

Dada una función f: ℝ 2 → ℝ que describe una superficie en ℝ 3 , una unidad normal en (x, y) está dada por

v = (-∂f / ∂x, -∂f / ∂y, 1) yn = v / | v |.

Se puede demostrar que la mejor aproximación a ∂f / ∂x por dos muestras se archiva mediante:

∂f / ∂x (x, y) = (f (x + ε, y) - f (x-ε, y)) / (2ε)

Para obtener una mejor aproximación necesita usar al menos cuatro puntos, por lo tanto, agregar un tercer punto (es decir, (x, y)) no mejora el resultado.

Su hightmap es una muestra de alguna función f en una cuadrícula regular. Tomando ε = 1 obtienes:

2v = (f (x-1, y) - f (x + 1, y), f (x, y-1) - f (x, y + 1), 2)


Ejemplo de código GLSL de mi sombreador de representación de superficie de agua:

#version 130 uniform sampler2D unit_wave noperspective in vec2 tex_coord; const vec2 size = vec2(2.0,0.0); const ivec3 off = ivec3(-1,0,1); vec4 wave = texture(unit_wave, tex_coord); float s11 = wave.x; float s01 = textureOffset(unit_wave, tex_coord, off.xy).x; float s21 = textureOffset(unit_wave, tex_coord, off.zy).x; float s10 = textureOffset(unit_wave, tex_coord, off.yx).x; float s12 = textureOffset(unit_wave, tex_coord, off.yz).x; vec3 va = normalize(vec3(size.xy,s21-s01)); vec3 vb = normalize(vec3(size.yx,s12-s10)); vec4 bump = vec4( cross(va,vb), s11 );

El resultado es un vector de respuesta: xyz = normal, a = altura


Si piensas en cada píxel como un vértice en lugar de una cara, puedes generar una malla triangular simple.

+--+--+ |/ |/ | | /| /| +--+--+ |/ |/ | | /| /| +--+--+

Cada vértice tiene una coordenada xey que corresponde a la xey del píxel en el mapa. La coordenada z se basa en el valor en el mapa en esa ubicación. Los triángulos se pueden generar explícita o implícitamente por su posición en la grilla.

Lo que necesitas es lo normal en cada vértice .

Un vértice normal se puede calcular tomando un promedio ponderado de área de las normales de superficie para cada uno de los triángulos que se encuentran en ese punto.

Si tiene un triángulo con vértices v0 , v1 , v2 , puede usar un producto vectorial cruzado (de dos vectores que se encuentran en dos de los lados del triángulo) para calcular un vector en la dirección de la normal y escalar proporcionalmente a el área del triángulo

Vector3 contribution = Cross(v1 - v0, v2 - v1);

Cada uno de tus vértices que no están en el borde será compartido por seis triángulos. Puede recorrer esos triángulos, resumiendo la contribution s, y luego normalizar la suma del vector.

Nota: debe calcular los productos cruzados de manera consistente para asegurarse de que todas las normales apuntan en la misma dirección. Elija siempre dos lados en el mismo orden (en sentido horario o antihorario). Si mezclas algunos de ellos, esas contribuciones estarán apuntando en la dirección opuesta.

Para vértices en el borde, terminas con un ciclo más corto y muchos casos especiales. Probablemente sea más fácil crear un borde alrededor de su cuadrícula de vértices falsos y luego calcular las normales para las interiores y descartar las fronteras falsas.

for each interior vertex V { Vector3 sum(0.0, 0.0, 0.0); for each of the six triangles T that share V { const Vector3 side1 = T.v1 - T.v0; const Vector3 side2 = T.v2 - T.v1; const Vector3 contribution = Cross(side1, side2); sum += contribution; } sum.Normalize(); V.normal = sum; }

Si necesitas la normal en un punto particular de un triángulo (que no sea uno de los vértices), puedes interpolar al pesar las normales de los tres vértices mediante las coordenadas baricéntricas de tu punto. Así es como los rasterizadores gráficos tratan lo normal para sombrear. Permite que una malla triangular parezca una superficie lisa y curva en lugar de un grupo de triángulos planos adyacentes.

Consejo: Para su primera prueba, use una cuadrícula perfectamente plana y asegúrese de que todas las normales calculadas apuntan hacia arriba.


Un método común es usar un filtro Sobel para una derivada ponderada / suave en cada dirección.

Comience muestreando un área de alturas de 3x3 alrededor de cada téxel (aquí, [4] es el píxel para el que queremos que sea normal).

[6][7][8] [3][4][5] [0][1][2]

Entonces,

//float s[9] contains above samples vec3 n; n.x = scale * -(s[2]-s[0]+2*(s[5]-s[3])+s[8]-s[6]); n.y = scale * -(s[6]-s[0]+2*(s[7]-s[1])+s[8]-s[2]); n.z = 1.0; n = normalize(n);

Donde la scale se puede ajustar para que coincida con la profundidad del mapa de altura del mundo real en relación con su tamaño.