Mapping y Hammer

Agua en mapas de Source: configúrala bien y corrige errores

Agua en mapas de Source y GMod: cómo se construye el brush de agua, agua cara y barata, water_lod_control, cubemaps y soluciones a fallos de renderizado.

En Source Engine, el agua es un world brush cuya cara superior lleva un material de agua, mientras que todos los demás lados reciben tools/toolsnodraw. Además necesita un water_lod_control, una envolvente sellada de brushes sólidos y entities env_cubemap para la vía de renderizado barata. El agua suele renderizarse mal por una de estas cuatro razones: el brush está ligado a una entity, el mapa tiene un leak, dos superficies de agua a distinta altura se ven desde el mismo sitio, o faltan los cubemaps.

Cómo se construye un brush de agua

  1. Crea un brush que llene todo el volumen de agua, desde el fondo hasta la superficie.
  2. Aplica tools/toolsnodraw a todo el brush.
  3. Dale un material de agua solo a la cara superior, por ejemplo nature/water_canals_water2.
  4. Deja el brush como world brush. No lo ligues a func_detail ni a ninguna otra entity.
  5. Rodea el estanque con world brushes sólidos y coloca un water_lod_control en el mapa.

Estas reglas se derivan de un mecanismo. Los materiales de agua llevan el parámetro %compilewater 1. VBSP corta entonces los visleafs a lo largo de la superficie del agua y guarda exactamente una altura por cada volumen de agua. Por eso la cara superior tiene que ser horizontal, y por eso esto solo funciona con world brushes: con un brush de agua ligado a func_detail, el VBSP de GMod no escribió ningún volumen de agua en el BSP en una prueba (leafwaterdata 0).

Además, el agua no es sólida. Si un brush de agua sustituye a una pared exterior, VBSP informa de **** leaked ****. Cómo encontrar el hueco se explica en solucionar el error de leak del mapa.

Los lados se quedan en nodraw porque, si no, se compilan como caras. Con un material de agua en los seis lados, la prueba produjo 60 caras en lugar de 40. La vista desde abajo la crea el propio VBSP a partir del parámetro $bottommaterial del material.

Agua cara y agua barata

La misma agua se puede dibujar de tres maneras:

RenderizadoReflejoVista bajo la superficiePasadas de renderizado adicionales
CaroTiempo realRefracciónDos, para el reflejo y la refracción
MedioCubemapRefracciónUna
BaratoCubemapSin refracciónNinguna

El motor decide en cada fotograma qué variante se aplica. La wiki de Valve Developer Community (VDC) solo distingue entre caro y barato; «medio» es nuestro nombre para el estado intermedio. El orden siguiente procede del código de cliente publicado de Source SDK 2013. GMod usa su propia rama del motor, así que los detalles pueden diferir.

  1. Si el material define $forcecheap 1, el agua siempre es barata. Un ejemplo es nature/water_canals_cheap001.
  2. Un reflejo en tiempo real necesita una $reflecttexture en el material y, además, o bien $forceexpensive 1 o bien el ajuste del jugador r_waterforceexpensive 1. La mayoría de los materiales de agua que trae HL2 definen $forceexpensive 1, incluido nature/water_canals_water2.
  3. Sin reflejo en tiempo real, decide la distancia. A partir de la distancia final del water_lod_control el agua se dibuja barata, y por debajo de ella con refracción y un cubemap.

Así que el renderizado lo fijas sobre todo con el material que eliges. Aun así, el reflejo por cubemap tiene que funcionar en todo momento, porque el agua media y la barata recurren a él. El agua cara dibuja grandes partes de la escena hasta tres veces. Cómo medir esos costes se muestra en optimizar el rendimiento de un mapa de GMod.

Usar bien water_lod_control

Esta entity controla, para toda el agua del mapa, la distancia a la cámara a la que el motor cambia a agua barata.

KeyvalueNombre en HammerValor por defecto
cheapwaterstartdistanceStart Transition to Cheap Water1000
cheapwaterenddistanceEnd Transition to Cheap Water2000

Los dos valores están en unidades. Entre el inicio y el final el material hace un fundido suave, siempre que contenga el proxy WaterLOD, como ocurre con los materiales de agua de serie.

Si falta la entity, la compilación no se aborta. VBSP crea una con los valores por defecto y escribe:

Water found with no water_lod_control entity, creating a default one.

Aun así, coloca una tú mismo para ajustar las distancias a tu mapa. Pero no esperes demasiado: según el código del SDK y la VDC, la distancia no se aplica a los materiales con reflejo en tiempo real.

Agua a distintas alturas

La VDC enuncia la regla: con agua cara solo puede haber una altura de agua en un PVS, el conjunto de zonas que se pueden ver desde un lugar. El código del SDK muestra por qué: en cada fotograma el cliente determina un único volumen de agua visible y renderiza el reflejo y la refracción exactamente para esa altura. Una segunda superficie a otra altura recibe entonces imágenes calculadas para el plano equivocado. No probamos dentro del juego qué aspecto tiene eso exactamente. No hay ningún aviso: un mapa de prueba con dos estanques a distinta altura compiló sin ningún mensaje.

  • Una altura por zona visible. Separa con world brushes los estanques que estén a distinta altura para que nunca acaben en el mismo PVS.
  • Usa la misma altura. La regla solo afecta a alturas distintas. Varios estanques a la misma altura comparten el plano de reflexión.
  • No mezcles los tipos. Según la VDC, el agua cara y la barata tampoco deben compartir un PVS. Si dos alturas tienen que seguir viéndose juntas, dale a toda esa agua un material con $forcecheap 1. Refleja a través de su cubemap.

Cubemaps, skybox 3D y displacements

Cubemaps. Los materiales de agua contienen $envmap env_cubemap. El agua media y la barata lo usan para reflejar el env_cubemap más cercano. Coloca al menos uno en cada zona con agua y ejecuta buildcubemaps después de la compilación final. Hasta entonces el BSP solo contiene un sustituto hecho a partir de la textura del skybox. El procedimiento se describe en arreglar los reflejos de los cubemaps, y los fundamentos del HDR están en la guía de iluminación de Source Engine.

Skybox 3D. Según el código del SDK, el skybox 3D se dibuja en una sola pasada, sin reflejo ni refracción propios. Usa ahí un material con $forcecheap 1. Según la VDC, el agua barata en el skybox funciona junto al agua cara en el mapa. La altura sale de la escala, porque la sky_camera representa el origen del mapa principal: si el agua está allí en Z = 128 y la sky_camera tiene una escala de 16, la superficie en el skybox va 8 unidades por encima de la sky_camera. A la inversa, según el código del SDK y la VDC, el reflejo en tiempo real del mapa principal muestra el skybox 2D y no el skybox 3D. La VDC recoge para CS:GO y Garry's Mod un parámetro de material $reflect3dskybox que debería cambiar esto. No lo probamos. Más sobre el tema en la guía de skybox 3D.

Displacements. Una orilla hecha de displacements sella el mapa tan poco como la propia agua. Prolonga el brush de agua por debajo del terreno y pon un world brush sólido debajo de ambos. Usa nodraw solo donde el terreno lo tape: la VDC advierte de un efecto de estela cuando es nodraw lo que sella el agua. Más sobre el tema en displacements en Hammer.

Errores típicos y su causa

SíntomaCausaSolución
Agua invisible o mal renderizadaLeak en el mapa, o brush de agua ligado a una entityCierra el leak, recupera el brush con Tools → Move to World
Agua opaca o con un reflejo rotoSegunda altura de agua en el mismo PVS, o un func_viscluster o un areaportal cruza la superficie del aguaSepara las alturas, mantén func_viscluster y los areaportals lejos de la superficie
Reflejo negro, de cuadros morados y negros o excesivamente brillanteFaltan los cubemaps o no se generaron para el modo activoColoca env_cubemap, ejecuta buildcubemaps para LDR y HDR
El agua cambia de aspecto de golpe a lo lejosEl agua sin reflejo en tiempo real pasa a barata a partir de la distancia finalSube las distancias en water_lod_control, genera los cubemaps
**** leaked ****, la línea atraviesa el aguaAgua usada como límite exteriorWorld brushes sólidos alrededor del estanque

Si el agua desaparece por completo a distancia, no hay una única causa documentada. Comprueba primero si entra en el campo de visión una segunda altura de agua. También puedes pegar el compile log en el Map Scanner. Detecta causas raíz, errores derivados y soluciones adecuadas en logs de VBSP, VVIS y VRAD.

Preguntas frecuentes

¿Puede subir o bajar el nivel del agua?

Sí, con func_water_analog. La entity mueve un brush de agua mediante los inputs Open, Close y SetPosition. El shader de agua normal no funciona ahí, porque necesita visleafs fijos. Usa un material como nature/water_movingplane. Solo refleja a través del cubemap.

¿El agua también refleja a los jugadores y los props?

Solo si el material define $reflectentities 1 o el jugador ha activado r_waterforcereflectentities 1. En caso contrario, el reflejo en tiempo real solo contiene el mundo y el cielo.

¿Qué necesita un material de agua custom?

Como mínimo %compilewater 1, $abovewater, $fogcolor, $bottommaterial y $envmap env_cubemap. Si falta $abovewater o $fogcolor, el shader avisa en la consola. Si falta $bottommaterial, avisa VBSP. Para la transición suave a distancia, añade el proxy WaterLOD.