2011-06-28 8 views
8

Estoy generando terreno en el sombreador de geometría Opengl y tengo problemas para calcular las normales para la iluminación. Estoy generando el terreno dinámicamente en cada cuadro con una función de ruido perlin implementada en el sombreador de geometría. Debido a esto, necesito una forma eficiente de calcular las normales por vértice en función de la función de ruido (sin textura ni nada). Puedo tomar el producto cruzado de 2 lados para obtener las caras normales, pero se generan dinámicamente con la geometría, por lo que no puedo retroceder y suavizar las normales de la cara para las normales de los vértices. ¿Cómo puedo obtener las normales de los vértices sobre la marcha simplemente usando la función de ruido que genera la altura de mi terreno en el plano y (por lo tanto, la altura está entre 1 y -1). Creo que tengo para probar la función de ruido 4 veces para cada vértice, pero intentado algo así como lo siguiente y no funcionó ...Per-Vertex ¿Normals del ruido perlin?

vec3 xP1 = vertex + vec3(1.0, 0.0, 0.0); 
vec3 xN1 = vertex + vec3(-1.0, 0.0, 0.0); 
vec3 zP1 = vertex + vec3(0.0, 0.0, 1.0); 
vec3 zN1 = vertex + vec3(0.0, 0.0, -1.0); 

float sx = snoise(xP1) - snoise(xN1); 
float sz = snoise(zP1) - snoise(zN1); 

vec3 n = vec3(-sx, 1.0, sz); 
normalize(n); 

return n; 

La realidad sobre la iluminación generada que se movía alrededor como el ruido Perlin! Entonces, ¿algún consejo sobre cómo puedo obtener las normales por vértice correctamente?

Respuesta

5

No dijo exactamente cómo estaba generando las posiciones. Por lo tanto, supongo que está utilizando el ruido Perlin para generar valores de altura en un mapa de altura. Por lo tanto, para cualquier posición X, Y en el jeroglífico, utiliza una función de ruido 2D para generar el valor Z.

Por lo tanto, vamos a suponer que su posición se calcula de la siguiente manera:

vec3 CalcPosition(in vec2 loc) { 
    float height = MyNoiseFunc2D(loc); 
    return vec3(loc, height); 
} 

Esto genera una posición 3D. Pero en qué espacio es esta posición? Esa es la pregunta.

La mayoría de las funciones de ruido esperan que loc sean dos valores en un determinado rango de coma flotante. Qué tan buena es su función de ruido determinará en qué rango puede pasar los valores. Ahora, si las posiciones 2D de su espacio modelo no están garantizadas dentro del rango de la función de ruido, entonces necesita transformarlas a ese rango, hacer los cálculos, y luego, transfórmalo como espacio modelo.

Al hacerlo, ahora tiene una posición 3D. La transformación para los valores X e Y es simple (el inverso de la transformación al espacio de la función de ruido), pero ¿qué ocurre con la Z? Aquí, debes aplicar algún tipo de escala a la altura. La función de ruido devolverá un número en el rango [0, 1), por lo que necesita escalar este rango al mismo espacio modelo al que van sus valores X e Y. Esto se hace típicamente seleccionando una altura máxima y escalando la posición de manera apropiada. Por lo tanto, nuestra posición Calc revisada se ve algo como esto:

vec3 CalcPosition(in vec2 modelLoc, const in mat3 modelToNoise, const in mat4 noiseToModel) 
{ 
    vec2 loc = modelToNoise * vec3(modelLoc, 1.0); 
    float height = MyNoiseFunc2D(loc); 
    vec4 modelPos = noiseToModel * vec4(loc, height, 1.0); 
    return modelPos.xyz; 
} 

Las dos matrices se transforman en el espacio de la función de ruido, y luego se transforman de vuelta. Su código real podría usar estructuras menos complicadas, dependiendo de su caso de uso, pero una transformación afín completa es simple de describir.

OK, ahora que hemos establecido eso, lo que debe tener en cuenta es esto: nada tiene sentido a menos que sepa en qué espacio está. Su normal, sus posiciones, nada importa hasta que establezca el espacio que es en.

Esta función devuelve las posiciones en el espacio modelo. Necesitamos calcular normales en espacio modelo. Para hacer eso, necesitamos 3 posiciones: la posición actual del vértice y dos posiciones que están ligeramente desplazadas de la posición actual. Las posiciones que obtenemos deben estar en el espacio modelo, o nuestra normalidad no será.

Por lo tanto, tenemos que tener la siguiente función:

void CalcDeltas(in vec2 modelLoc, const in mat3 modelToNoise, const in mat4 noiseToModel, out vec3 modelXOffset, out vec3 modelYOffset) 
{ 
    vec2 loc = modelToNoise * vec3(modelLoc, 1.0); 
    vec2 xOffsetLoc = loc + vec2(delta, 0.0); 
    vec2 yOffsetLoc = loc + vec2(0.0, delta); 
    float xOffsetHeight = MyNoiseFunc2D(xOffsetLoc); 
    float yOffsetHeight = MyNoiseFunc2D(yOffsetLoc); 
    modelXOffset = (noiseToModel * vec4(xOffsetLoc, xOffsetHeight, 1.0)).xyz; 
    modelYOffset = (noiseToModel * vec4(yOffsetLoc, yOffsetHeight, 1.0)).xyz; 
} 

Obviamente, puede combinar estas dos funciones en una sola.

El valor delta es un pequeño desplazamiento en el espacio de la entrada de la textura del ruido. El tamaño de este desplazamiento depende de su función de ruido; necesita ser lo suficientemente grande como para devolver una altura que sea significativamente diferente de la que devuelve la posición actual real. Pero tiene que ser small suficiente para que no esté tirando de partes aleatorias de la distribución de ruido.

Debe conocer su función de ruido.

Ahora que tiene las tres posiciones (la posición actual, el X-offset, y la ordenada en el offset) en el espacio modelo, se puede calcular el vértice normal en el espacio modelo:

vec3 modelXGrad = modelXOffset - modelPosition; 
vec3 modelYGrad = modelYOffset - modelPosition; 

vec3 modelNormal = normalize(cross(modelXGrad, modelYGrad)); 

Desde aquí haz lo de siempre Pero nunca olvide hacer un seguimiento de los espacios de sus diversos vectores.

Ah, y una cosa más: esto se debe hacer en el vértice shader. No hay ninguna razón para hacer esto en un sombreador de geometría, ya que ninguno de los cálculos afecta a otros vértices. Deje que el paralelismo de la GPU funcione para usted.

+0

Pensé en un enfoque como este, lo intenté y fallé. Después de leer su respuesta, probablemente no tenga todo en el espacio correcto y esté causando errores. Estoy trabajando con vértices, geometría y sombreadores de fragmentos, así que me doy cuenta de que tengo que tener mucho cuidado con el espacio en el que guardo las cosas. Volveré a intentarlo mañana con un enfoque claro al usar este método y ver qué sucede. – Nitrex88

+0

Lo tengo trabajando con este enfoque. Sin embargo, para que funcione, necesitaba crear 4 vectores de compensación y usar los gradientes entre los desplazamientos (no el degradado del desplazamiento con la posición del modelo). Y para obtener la iluminación en la dirección correcta, tuve que cambiar de orden de tomar el producto cruzado del modelo XGrad y el modelo YGrad (solo menciono para otros que se encuentran con esta solución). Gracias por explicarlo tan bien con el espacio de coordenadas ... que ayudó. Además, quiero mencionar que estaba haciendo esto en el sombreador de geometría porque estoy teselando dinámicamente el terreno en el sombreador de geometría – Nitrex88

8

La normal es el vector perpendicular a la tangente (también conocida como pendiente). La pendiente de una función es su derivada; para n dimensiones, n derivadas parciales. Entonces muestrea el ruido alrededor de un punto central P y en P ± (δx, 0) y P ± (0, δy), con δx, δy elegido para ser lo más pequeño posible, pero lo suficientemente grande para la estabilidad numérica. Esto te da las tangentes en cada dirección. Luego toma el producto cruzado de ellos, normaliza el resultado y obtiene el normal en P.

+1

+1: Es el tipo correcto de enfoque, aunque no sé qué tan práctico es hacerlo en un sombreador. –

+0

Esta respuesta dice lo mismo que la otra respuesta más larga (básicamente), ¿verdad? En cualquier caso, intentaré mañana y veré si funciona. – Nitrex88

+2

@ Nitrex88: la respuesta de @Nicol Bolas te dice lo mismo que yo, pero en una forma más detallada y elaborada. Tiene razón, por supuesto, en que debe hacer un seguimiento del espacio en el que se encuentra. Sin embargo, si observamos esto desde la perspectiva de las funciones, entonces se reduce a derivadas parciales, es decir, gradientes. Usted "mueve" la coordenada de muestreo de ruido, y el valor de ruido resultante fluctúa en consecuencia. Entonces solo se trata de relacionar los vectores de espacio de movimiento de entrada con el espacio de movimiento de salida. – datenwolf