¿Cómo puedo hacer que el agua parezca más oscura con la profundidad, como en Minecraft?

24

En Minecraft cuando miras el agua, cuanto más profunda ves, más oscura se vuelve. ¿Alguien sabe cómo codificar algo así?

Minecraft con efecto Minecraft con efecto

juego similar sin efecto juego similar sin efecto

Javier
fuente
18
¿No se hace esto automáticamente, ya que el material del cubo de agua es semitransparente?
Pek
No lo creo. Agregué una imagen sin el efecto de comparación.
Xavier
2
¿Tal vez es un efecto de mezcla aditiva aplicado solo en los cubos de agua? Nuevamente, esto debería ser fácil ya que el material es semitransparente.
Pek
1
También puede cambiar el color de los cuadros según la profundidad.
Ali1S232

Respuestas:

51

Básicamente, existen dos enfoques diferentes para iluminar el agua según la profundidad:

Voxel-Lighting

Minecraft utiliza iluminación basada en vóxel, que funciona mediante la propagación de la luz a los cubos adyacentes, reduciendo el brillo según el tipo de bloque. Los océanos oscuros son un efecto secundario de este sistema.

El agua bloquea la luz solar y reduce la luz en 3 niveles por bloque (en lugar del nivel predeterminado de 1), lo que significa que el brillo en un océano para cada distancia de la superficie es:

0 (surface): 15 (direct sunlight)
1:           12
2:            9
3:            6
4:            3
5 and below:  0 (darkness)

Fuente: Minecraft Wiki - Light

Sombreado a distancia

En los juegos con un modelo de iluminación tradicional, este efecto puede crearse midiendo la cantidad de agua que se encuentra entre la fuente de luz y el fondo del océano. La luz se desvanece en función de esta distancia. Hay algunos métodos para hacer esto:

Cálculo directo

Si tiene una superficie plana, puede calcular fácilmente la distancia que recorre la luz en el agua si pasa la superficie normal lejos del cuerpo de agua \ vec {n}y el producto escalar de esta normal y una posición de superficie sen el sombreador de geometría.

La distancia efectiva del agua es

\ max (\ left (s - \ vec {n} \ cdot \ vec {p} \ right), 0) \ cdot \ left (1 + \ tan (\ alpha) \ right)

donde \ vec {p}es la posición del vértice y alfaes el ángulo entre la dirección de la luz debajo de la superficie y la superficie del agua normal hacia el cuerpo de agua.

Al atardecer, alfasolo alcanza un poco menos de 50 ° porque la luz se refracta al ingresar al agua.
Aquí hay una publicación de blog con una buena explicación: La cámara digital: reflexión interna total
Otra publicación con más detalles: La cámara digital: Ley de refracción de Snell

Si está utilizando un mapa de altura en una superficie paralela al agua, se \ left (s - \ vec {n} \ cdot \ vec {p} \ right)convierte en \ left (s - h \ right). El factor correcto es igual a 1 si el sol está directamente sobre la superficie del agua.
Con un punto de luz, debe calcular alfapara cada vértice en función de la posición relativa a la fuente de luz.

Con un nivel de agua fijo o una dirección de luz fija, partes de la ecuación son constantes y no deben calcularse en el sombreador por razones de rendimiento.

Pros:

  • Rápido y preciso

Contras:

  • Solo funciona para superficies planas de agua o solo para luz directamente desde arriba, ya que normalmente solo se tiene en cuenta una superficie. (La combinación de una superficie rugosa y luz inclinada podría funcionar hasta cierto punto con el mapeo de paralaje).
  • Sin cáusticos

Mapeo de sombras

Si representa la superficie del agua en un mapa de profundidad separado (como se ve desde la fuente de luz), puede usar esa textura de profundidad para calcular la distancia que recorre la luz en el agua antes de tocar la superficie.
Para hacer esto, proyecta cada vértice en la proyección de vista de la fuente de luz en el sombreador de vértices y realiza la búsqueda de textura en el sombreador de píxeles.

Si la superficie es relativamente plana, debe usar un origen de luz refractada para obtener mejores resultados.

Pros:

  • Funciona con geometría de agua relativamente compleja, siempre que no se ocluya. *
  • Se puede reutilizar para casi cualquier tipo de volumen parcialmente transparente.

Contras:

  • Más lento que el cálculo directo.
  • Necesita VRAM adicional para el mapa de profundidad.
  • No es 100% exacto.

* Puede determinar la cantidad de agua frente a la superficie sólida más cercana contando la profundidad desde el punto de vista de la luz de la siguiente manera:

  1. Renderice la geometría sólida en su escena de manera normal. Para cada fragmento, agrega el valor de profundidad a la textura resultante.
  2. Renderice las caras frontales del agua sin actualizar el búfer de profundidad y reste las profundidades de los fragmentos del resultado.
  3. Renderice las caras posteriores de la misma manera, pero agregue la profundidad del fragmento al resultado.

La textura resultante ahora contiene la cantidad de agua frente a la luz en el espacio de visualización de la luz, por lo que el valor debe transformarse antes de usarlo. Este método funciona para calcular la luz direccional (menos la refracción), pero conducirá a una luz ambiental incorrecta si las superficies son muy irregulares y hay una gran cantidad de aire entre los cuerpos de agua que afectan los mismos fragmentos.
Los pros y los contras son los mismos que para el mapeo de sombras normal, excepto que necesita un búfer más al calcular la profundidad y el rendimiento es peor porque tiene que dibujar más geometría.

Trazado de rayos

El trazado de rayos es, con mucho, la solución más precisa pero también la más costosa para renderizar volúmenes transparentes. Hay dos formas de hacer esto: 1. Trazar desde el fondo del océano hacia la superficie y 2. Trazar desde la fuente de luz hacia el agua. Se necesitan múltiples rayos para cada punto en el piso para calcular el brillo.

Pros:

  • Funciona correctamente con cada geometría.
  • ¡Cáusticos correctos!

Contras:

  • ¡Lento!

Efectos adicionales

Hay algunas cosas más a tener en cuenta al procesar el agua:

Niebla

La luz en el agua se dispersa nuevamente mientras viaja hacia el observador, por lo que debe mezclarla hacia un color sólido.

Si el observador está sumergido , solo puede generar niebla en función del resultado final del búfer de profundidad. ¡El color de la niebla, pero no su densidad, debería cambiar con la distancia del observador desde la superficie! (Minecraft solo usa esta parte del efecto).

Si el observador mira el agua desde arriba , debe calcular la niebla en función de la diferencia de profundidad entre la superficie y la geometría bajo el agua. El color de la niebla debería oscurecerse un poco con mayores diferencias de profundidad, pero solo debería cambiar al punto donde la niebla es completamente opaca.

El color de la niebla también debe depender de la dirección de la vista para cada píxel, por lo que es ligeramente más oscuro cuando se mira hacia abajo en ambos casos.

Falsificación de cáusticos

Si utiliza una textura 3D de mosaico sin costuras en lugar de una calcomanía para cáusticos falsos, puede evitar estirarse en superficies verticales. La intensidad de la luz dispersa cerca de la superficie varía en tres dimensiones, por lo que el uso de una textura 2D generalmente produce estiramiento en algún lugar de la escena. Puede modelar los ángulos de luz cambiantes proyectando las posiciones de vértice del piso en un sistema de coordenadas diferente.

Otra posibilidad es calcular la densidad de la luz en función de la posición de la superficie en el sistema de coordenadas de la luz, aunque lo más probable es que cueste algo de rendimiento.

Los cáusticos deberían desvanecerse más rápido que la luz difusa al aumentar la profundidad.

Gradiente de color

Los colores se dispersan de manera diferente, por lo que el color claro debería cambiar con el aumento de la profundidad. Esto también evita bordes abruptos donde, por ejemplo, una playa se cruza con la superficie del agua.

Ángulo de incidencia

Debido a la refracción, la luz golpea el fondo del océano mucho más empinada de lo normal. El artículo de Wikipedia sobre la ley de Snell tiene fórmulas para ángulos y vectores.

Tamschi
fuente
6

Creo que el efecto de iluminación del cielo en Minecraft es directo: las cosas se sombrean por lo que está encima de ellos, sin importar dónde esté el sol. Luego, la iluminación local de antorchas, etc., se aplica con un efecto de caída: cuanto más lejos de la fuente de luz, menos luz obtiene un cubo.

Si se hace de esta manera, cada capa de agua sombreará acumulativamente la capa debajo de ella, por lo que cada una se volverá progresivamente más oscura. El follaje de los árboles proporciona sombra como esta, sin embargo, no es acumulativa. Obtiene la misma sombra debajo de un árbol, ya sea 1 o 100 cubos de follaje.

Una pista de que este es el método que se utiliza es que el agua no se oscurece cuando se aleja del espectador, solo a medida que baja. Sí, el efecto de niebla se activa a distancia, pero no el efecto de oscuridad del agua.

Entonces, la fórmula básica para calcular la iluminación sería algo así en pseudocódigo ...

light_on_cube = 1.0
for each cube above target cube, from lowest to highest {
   if cube being examined is tree foliage
      light_on_cube = 0.5
   else if cube being examined is water
      light_on_cube = light_on_cube - 0.1
   else if cube being examined is solid 
      light_on_cube = 0
}

Esto no es perfecto para calcular la iluminación debajo de voladizos o en cuevas, ya que con este método se oscurecería bajo un voladizo. Pero uno podría agregar tanto fuentes de luz locales (antorchas, fuegos, etc.) como tratar los bloques iluminados por el sol como fuentes de luz. Algo como esto podría hacerlo ...

  1. Calcule la luz del sol directamente desde arriba (a través del pseudocódigo anterior) para cada cubo.
  2. Si un cubo tiene una fuente de luz al lado, considérelo completamente encendido (1.0)
  3. Si un cubo no recibe sol directamente desde arriba, bríndele algo de luz según lo lejos que esté de un cubo completamente iluminado. Más cerca significa más luz, más lejos significa menos (hasta cero).

La idea aquí es que si un cubo está iluminado por el sol o una antorcha, el cubo al lado también se encenderá de alguna manera. Y cuanto más lejos esté de ese cubo iluminado, menos luz habrá. Es una especie de mala manera de estimar la iluminación difusa, pero creo que (?) Funcionaría.

Tim Holt
fuente
1
Sí, estoy bastante seguro de que ese es el boleto. He hecho algo similar en mi juego.
MichaelHouse
Por cierto, acabo de agregar su blog a mi lista de lectores de Google Byte56 - blogs de desarrolladores FTW!
Tim Holt
Oh, porque gracias. Todavía estoy fuera del tema de esta pregunta, pero acabo de leer su blog sobre la clase del profesor Bailey. ¡Estuve en esa clase el año pasado! Estoy bastante seguro de que también diste esa presentación el año pasado. Pensé que tu nombre era familiar. Pequeño mundo :)
MichaelHouse
3

Tal vez estoy malinterpretando la pregunta, pero ¿por qué no puedes cambiar el color de los bloques dependiendo de su profundidad?

Si tiene la profundidad d (en bloques, comenzando en 0), una ecuación razonable para el brillo sería:

L = (1- m ) e - kd + m

Código: L = (1.0 - m) * exp(-k * d) + m;

k controla qué tan rápido se oscurece (más alto = más rápido). Un valor razonable sería 0.5.
m es el brillo mínimo que desea.
L varía de 0 a 1.

Si no sabe cómo cambiar el color de los bloques en cualquier API de gráficos que esté utilizando, hágalo como una pregunta por separado (indicando qué API utiliza y si está utilizando sombreadores).

Peter Alexander
fuente
Simplemente no pensé en hacer eso. Solo por curiosidad, ¿de dónde sacaste esa ecuación?
Xavier
1
@ Xavier: Acabo de inventarlo. El e^-kdbit es solo una disminución exponencial, que es una función estándar para cosas que gradualmente tienden a cero sobre algún valor (profundidad). La multiplicación por (1-m)y la suma de mson solo para escalar y compensar la desintegración de modo que termine en un mínimo de mpero aún comience en 1. en.wikipedia.org/wiki/Exponential_decay
Peter Alexander
La cosa es que los bloques de un tono más profundo solo se verán si los bloques tienen colores alfa; en cuyo caso no hay necesidad de cambiar el color del bloque, el alfa creará el efecto automáticamente.
Jonathan Connell
@ Jonathan: No renderizas los bloques de agua, los bloques en el fondo del mar con el color oscuro y luego solo tienes una capa alfa en la superficie del agua.
Peter Alexander
@ Peter Alexander Ok, supuse que en estos juegos tipo bloque, incluso el agua estaba hecha de bloques.
Jonathan Connell