¿Cómo puedo hacer un sombreador de "superficie húmeda" / "charco poco profundo" en Unity?

71

En mi juego, necesito crear charcos de agua dinámicos, pero no puedo encontrar un tutorial que muestre cómo puedo hacer ese efecto (un ejemplo del cual se muestra a continuación). ¿Cómo puedo hacerlo?

Pausa cuántica

Seyed Morteza Kamali
fuente
44
Molesto de ver una pregunta tan votada y una respuesta votada más alta no se cierra. Está bien elegir tu propia respuesta como mejor, aunque un poco tonto para reclamar la recompensa por ti mismo :)
Tim Holt
@TimHolt ¿Sobre qué base cerraríamos una pregunta como esta? Parece perfectamente sobre el tema.
Josh
Estoy diciendo que la persona que lo preguntó debería aceptar su propia respuesta. Perdón por mi mal uso del inglés.
Tim Holt

Respuestas:

121

Reflexión

Para crear un sombreador húmedo, primero debe tener un reflejo.

SimpleRoad

Puede usar una Sonda de reflexión o una Reflexión de espejo3 pero, aquí uso una reflexión falsa (Mapa de cubos) porque el sombreador se puede usar en dispositivos móviles.

Reflexión

Shader "Smkgames/TransparentCubeMap" {
Properties {
_Color("Color",Color) = (1,1,1,1)
_Cube ("Cubemap", CUBE) = "" {}
_Metallic("Metallic",Range(0,1)) = 1
_Smoothness("Smoothness",Range(0,1)) = 1
_Alpha("Alpha",Range(0,1)) = 1
}
SubShader {
Tags {"RenderType"="Transparent" "Queue"="Transparent"}
LOD 200
Pass {
ColorMask 0
}
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
ColorMask RGB

CGPROGRAM
#pragma surface surf Standard fullforwardshadows alpha:fade

struct Input {
float2 uv_MainTex;
float3 worldRefl;
};
sampler2D _MainTex;
samplerCUBE _Cube;
float4 _Color;
float _Metallic;
float _Smoothness;
float4 _EmissionColor;
float _Alpha;
void surf (Input IN, inout SurfaceOutputStandard o) {
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;

o.Albedo = c.rgb * 0.5 * _Color;
o.Emission = texCUBE (_Cube, IN.worldRefl).rgb*_Color;
o.Metallic = _Metallic;
o.Smoothness = _Smoothness;
o.Alpha = _Alpha;

}
ENDCG
} 
Fallback "Diffuse"
}

Distorsión

Para agregar distorsión a su reflejo, puede multiplicar el mapa normal y el worldRefl:

float3 distortion = tex2D(_Distortion, IN.uv_Distortion);
o.Emission = texCUBE(_Cube, IN.worldRefl*distortion).rgb

distorsión

Forma procesal

Puede usar el ruido para crear una forma de procedimiento :

capturar

Aquí hay un tutorial de Fractal Brownian Motion (FBM) .

Shader "Smkgames/FbmNoise"
{
Properties
{
_TileAndOffset("Tile and Offset",Vector) = (1,1,0,0)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100

Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog

#include "UnityCG.cginc"

struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};

struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};


float4 _TileAndOffset;
float _Step,_Min,_Ma;

v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv*_TileAndOffset.xy+_TileAndOffset.zw;
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}

// Author @patriciogv - 2015
// http://patriciogonzalezvivo.com

float random (in float2 st) {
return frac(sin(dot(st.xy,
                    float2(12.9898,78.233)))*
    43758.5453123);
}

// Based on Morgan McGuire @morgan3d
// https://www.shadertoy.com/view/4dS3Wd
float noise (in float2 st) {
float2 i = floor(st);
float2 f = frac(st);

// Four corners in 2D of a tile
float a = random(i);
float b = random(i + float2(1.0, 0.0));
float c = random(i + float2(0.0, 1.0));
float d = random(i + float2(1.0, 1.0));

float2 u = f * f * (3.0 - 2.0 * f);

return lerp(a, b, u.x) +
        (c - a)* u.y * (1.0 - u.x) +
        (d - b) * u.x * u.y;
}

#define OCTAVES 6
float fbm (in float2 st) {
// Initial values
float value = 0.0;
float amplitude = .5;
float frequency = 0.;
//
// Loop of octaves
for (int i = 0; i < OCTAVES; i++) {
    value += amplitude * noise(st);
    st *= 2.;
    amplitude *= .5;
}
return value;
}

        fixed4 frag (v2f i) : SV_Target
        {


float2 st =i.uv;

float3 color = float3(0,0,0);
color += fbm(st*3.0);
return float4(color,1.0);

        }
ENDCG
}
}
}

El FBM anterior no debe usarse directamente en su sombreador porque tiene muchos cálculos de GPU y disminuye el rendimiento. En lugar de usar directamente, puede renderizar el resultado a una textura con RenderTexture .

Shadertoy utiliza múltiples pases , uno por "buffer". Como su nombre indica, este pase almacena los resultados en un búfer, que es solo una textura. La unidad también te permitirá renderizar texturas.

2018-01-26_10-18-20

Creando máscara

Puede hacer una máscara gruesa y suave con estas funciones:

Paso

paso

Emite 1 si [A]es menor o igual que [B], de lo contrario, emite 0.

Smoothstep

paso suave

Se mezcla suavemente entre dos valores, en función de dónde se encuentra un tercer valor en ese rango, generando valores entre 0 y 1. Piense en ello como un lerp inverso fijo con un valor de salida suavizado.

Resultado

/* Warning: don't use this shader because this is for preview only.
It has many GPU calculations so if you want use this in your game you should 
remove the FBM noise functions or render it to texture, or you can use an FBM texture
*/
//Created By Seyed Morteza Kamaly
Shader "Smkgames/WetShader" {
Properties{
_MainTex("MainTex",2D) = "white"{}
_Distortion("Distortion",2D) = "bump"{}
_Cube("Cubemap", CUBE) = "" {}
_BumpMap("Bumpmap", 2D) = "bump" {}
_Metallic("Metallic",Range(0,1)) = 0
_Smoothness("Smoothness",Range(0,1)) = 1
_ReflectAlpha("ReflectAlpha",Range(0,1)) = 1
scaleX("UV.X scale",Float) = 10.0
scaleY("UV.Y scale",Float) = 10.0
_Smooth("Smooth",Float) = 0.4
_Intensity("Intensity",Float) = 1
}
SubShader{
Tags{ "RenderType" = "Transparent" "Queue" = "Transparent" }
LOD 200
Pass{
ColorMask 0
}
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
ColorMask RGB

CGPROGRAM
#pragma surface surf Standard fullforwardshadows alpha:fade

struct Input {
float2 uv_MainTex;
float2 uv_Distortion;
float3 worldRefl;
float2 uv_BumpMap;
INTERNAL_DATA
};
sampler2D _MainTex, _Distortion;
samplerCUBE _Cube;
float _Metallic,_Smoothness;
float4 _EmissionColor;
sampler2D _NormalMap;
uniform fixed scaleX, scaleY, _Smooth, _Intensity,_Alpha,_ReflectAlpha;

static const float2x2 m = float2x2(-0.5, 0.8, 1.7, 0.2);

float hash(float2 n)
{
return frac(sin(dot(n, float2(95.43583, 93.323197))) * 65536.32);
}

float noise(float2 p)
{
float2 i = floor(p);
float2 u = frac(p);
u = u*u*(3.0 - 2.0*u);
float2 d = float2 (1.0, 0.0);
float r = lerp(lerp(hash(i), hash(i + d.xy), u.x), lerp(hash(i + d.yx), hash(i + d.xx), u.x), u.y);
return r*r;
}

float fbm(float2 p)
{
float f = 0.0;
f += 0.500000*(0.5 + 0.5*noise(p));
return f;
}

float fbm2(float2 p)
{
float f = 0.0;
f += 0.500000*(0.6 + 0.45*noise(p)); p = p*2.02; p = mul(p, m);
f += 0.250000*(0.6 + 0.36*noise(p));
return f;
}


void surf(Input IN, inout SurfaceOutputStandard o) {
fixed4 c = tex2D(_MainTex, IN.uv_MainTex);

o.Metallic = _Metallic;
o.Smoothness = _Smoothness;
o.Alpha = 1;

float t = fbm2(float2(IN.uv_MainTex.x*scaleX, IN.uv_MainTex.y*scaleY));

float fbmMask = step(t, _Smooth)*_Intensity;
float3 distortion = tex2D(_Distortion, IN.uv_Distortion);
o.Emission = texCUBE(_Cube, IN.worldRefl*distortion).rgb*_ReflectAlpha*fbmMask;

o.Albedo = float4(1.0, 1.0, 1.0, 1.0)*tex2Dlod(_MainTex, float4(IN.uv_MainTex, 0.0, 0.0));


}
ENDCG
}
Fallback "Diffuse"
}

imagen

Usar mapas

shaderToy Sombreado basado físicamente

Aquí hay algunas definiciones útiles:

Rugosidad Describe la micro-superficie del objeto. El blanco 1.0 es rugoso y el negro 0.0 es suave. La micro-superficie, si es rugosa, puede hacer que los rayos de luz se dispersen y que el resaltado parezca más tenue y más amplio. La misma cantidad de energía luminosa se refleja al salir a la superficie. Este mapa tiene la mayor libertad artística. No hay respuestas incorrectas aquí. Este mapa le da al activo el mayor carácter, ya que realmente describe la superficie, por ejemplo, rasguños, huellas digitales, manchas, mugre, etc.

Brillo Este mapa es el inverso del mapa de rugosidad. El blanco 1.0 es suave y el negro 0.0 es rugoso. Describe la micro-superficie del objeto. La micro-superficie, si es rugosa, puede hacer que los rayos de luz se dispersen y que el resaltado parezca más tenue y más amplio. La misma cantidad de energía luminosa se refleja al salir a la superficie. Este mapa tiene la mayor libertad artística. No hay respuestas incorrectas aquí. Este mapa le da al activo el mayor carácter, ya que realmente describe la superficie, por ejemplo, rasguños, huellas dactilares, manchas, mugre, etc.

Especular Este mapa contiene la información de reflectancia para superficies metálicas y dieléctricas (no metálicas). Esta es una diferencia clave en los flujos de trabajo metal / rugoso y spec / gloss. Se aplican las mismas reglas. Debe usar valores medidos para metales y la mayoría de todos los dieléctricos caerán con el rango de 0.04 - 4%. Si hay suciedad en el metal, el valor de reflectancia también debe reducirse. Sin embargo, puede agregar diferentes valores en el mapa especular para materiales dieléctricos ya que tiene el control para crear el mapa.

https://forum.allegorithmic.com/index.php?topic=3243.0

Aspereza

imagen

No sé por qué, pero el sombreador estándar de Unity no tiene un mapa de suavidad, así que escribí un sombreador básico y agregué este mapa.

Shader "Smkgames/SimpleSurface" {
    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _GlossMap("GlossMap",2D) = "white"{}
        _Glossiness ("Smoothness", Float) = 1.5
        _Metallic ("Metallic", Float) = 0.5
        _MetallicMap("MetallicMap",2D) = "white"{}
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        #pragma surface surf Standard fullforwardshadows

        #pragma target 3.0

        sampler2D _MainTex;

        struct Input {
            float2 uv_MainTex;
        };

        half _Glossiness,_Metallic;
        fixed4 _Color;
        sampler2D _GlossMap,_MetallicMap;

        UNITY_INSTANCING_CBUFFER_START(Props)
        UNITY_INSTANCING_CBUFFER_END

        void surf (Input IN, inout SurfaceOutputStandard o) {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            o.Metallic = _Metallic*tex2D(_MetallicMap,IN.uv_MainTex);
            o.Smoothness = _Glossiness*tex2D(_GlossMap,IN.uv_MainTex);
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

Creo que Unity no tiene rugosidad, solo tiene metal, pero el canal alfa es para rugosidad y el canal rojo es para metálico. Puedes cambiar la intensidad con suavidad.

Fuente en GitHub .

Enlaces útiles

esfera de barro-1024x576

https://80.lv/articles/how-to-create-wet-mud-in-substance-designer/

https://www.fxguide.com/featured/game-environments-partc/

Seyed Morteza Kamali
fuente
39
Wow, has hecho una serie completa de tutoriales sobre sombreadores en el sitio de preguntas y respuestas.
Ocelot
66
@Ocelot Me encanta cómo Seyed sigue agregando más y más de estos aquí. Me gusta jugar con sombreadores y estos son realmente útiles para obtener más ideas y también como tutoriales. Él puede seguir publicando estos para siempre en mi opinión.
John Hamilton
77
Respuesta asombrosa Es muy difícil trabajar con los sombreadores y me lleva horas de violín, investigación, prueba y error, y el examen de otros sombreadores para obtener los efectos que quiero. Y aquí estás haciendo eso, para otra persona, gratis.
Draco18s el
1
Para los materiales estándar, generalmente es mejor incrustar la rugosidad en el mapa metálico o normal (el primero parece ser el predeterminado). Recomendaría usar Photo Shop, Paint Shop o Gimp para crear un metal adecuado que incruste la aspereza. Alternativamente, si tiene Substance Painter o similar, puede exportar su aspereza exactamente como Unity los desea y tener la ventaja de visualizar sus materiales antes de ponerlos en Unity.
David Peterson el
un erudito y un caballero
Bas Smit