Como posso fazer um shader de “superfície molhada” / “poça rasa” no Unity?

71

No meu jogo, preciso criar poças de água dinâmicas, mas não consigo encontrar um tutorial que mostre como posso fazer esse efeito (um exemplo é mostrado abaixo). Como eu posso fazer isso?

Quebra quântica

Seyed Morteza Kamali
fonte
4
É irritante ver uma pergunta tão votada e uma resposta votada mais alta não sendo fechadas. É bom para escolher a sua própria resposta como melhor, embora um pouco bobo para reivindicar a recompensa para si mesmo :)
Tim Holt
@ TimHolt Em que base encerraríamos uma pergunta como essa? Parece perfeitamente no tópico.
Josh
Estou dizendo que a pessoa que pediu deve aceitar sua própria resposta. Perdoe meu mau uso do inglês.
Tim Holt

Respostas:

121

Reflexão

Para criar um shader úmido, primeiro você precisa refletir.

SimpleRoad

Você pode usar uma sonda de reflexão ou uma MirrorReflection3 , mas eu uso uma reflexão falsa (mapa do cubo) aqui porque o shader pode ser usado no celular.

Reflexão

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"
}

Distorção

Para adicionar distorção ao seu reflexo, você pode multiplicar o mapa normal e worldRefl:

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

distorção

Forma processual

Você pode usar ruído para criar uma forma processual :

capturar

Aqui está um tutorial do 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
}
}
}

O FBM acima não deve ser usado diretamente no seu sombreador, pois possui muitos cálculos de GPU e diminui o desempenho. Em vez de usar diretamente, você pode renderizar o resultado em uma textura com RenderTexture .

Shadertoy usa várias passagens , uma por "buffer". Como o nome indica, esse passe armazena os resultados em um buffer, que é apenas uma textura. O Unity também permitirá que você renderize texturas.

2018-01-26_10-18-20

Criando máscara

Você pode fazer uma máscara grossa e suave com estas funções:

Degrau

degrau

Emite 1 se [A]for menor ou igual a [B], caso contrário, emite 0.

Smoothstep

passo suave

Combina suavemente entre dois valores, com base em onde um terceiro valor está nesse intervalo, produzindo valores entre 0 e 1. Pense nele como um lerp inverso fixo com um valor de saída 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"
}

imagem

Usando mapas

shaderToy Sombreamento fisicamente baseado

Aqui estão algumas definições úteis:

Rugosidade Descreve a micro-superfície do objeto. O branco 1.0 é áspero e o preto 0.0 é suave. A microssuperfície, se rugosa, pode causar a dispersão dos raios de luz e fazer com que o destaque pareça mais escuro e mais amplo. A mesma quantidade de energia luminosa é refletida saindo da superfície. Este mapa tem a maior liberdade artística. Não há respostas erradas aqui. Este mapa dá ao recurso o máximo de caracteres, pois realmente descreve a superfície, como arranhões, impressões digitais, manchas, sujeira, etc.

Brilho Este mapa é o inverso do mapa de rugosidade. O branco 1.0 é suave e o preto 0.0 é áspero. Descreve a micro-superfície do objeto. A microssuperfície, se rugosa, pode causar a dispersão dos raios de luz e fazer com que o destaque pareça mais escuro e mais amplo. A mesma quantidade de energia luminosa é refletida saindo da superfície. Este mapa tem a maior liberdade artística. Não há respostas erradas aqui. Este mapa dá ao recurso o máximo de caracteres, pois realmente descreve a superfície, como arranhões, impressões digitais, manchas, sujeira, etc.

Especular Este mapa contém as informações de refletância para superfícies metálicas e dielétricas (não metálicas). Essa é uma diferença importante nos fluxos de trabalho de metal / bruto e de especificação / brilho. As mesmas regras se aplicam. Você precisa usar valores medidos para metais e quase todos os dielétricos cairão na faixa de 0,04 a 4%. Se houver sujeira no metal, o valor da refletância também precisará ser reduzido. No entanto, você pode adicionar valores diferentes no mapa especular para materiais dielétricos, já que você tem controle para criar o mapa.

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

Rugosidade

imagem

Não sei por que, mas o sombreador padrão do Unity não tem um mapa de suavidade, então escrevi um sombreador básico e adicionei esse 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"
}

Eu acho que o Unity não tem rugosidade, só tem metal, mas o canal alfa é para rugosidade e o canal vermelho é para metal. Você pode alterar a intensidade com suavidade.

Fonte no GitHub .

Links Úteis

esfera-de-lama-1024x576

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

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

Seyed Morteza Kamali
fonte
39
Uau, você fez uma série de tutoriais completos sobre shader no site de perguntas e respostas.
Ocelot
6
@Ocelot Eu amo como Seyed continua adicionando mais e mais desses aqui. Eu gosto de mexer com shaders e eles são realmente úteis para mais idéias e também para tutoriais. Ele pode continuar postando isso para sempre na minha opinião.
John Hamilton
7
Resposta incrível. Os shaders são muito difíceis de trabalhar e levo horas de mexer, pesquisa, tentativa e erro e exame de outros shaders para obter os efeitos desejados. E aqui você está fazendo isso, para outra pessoa, de graça.
precisa saber é o seguinte
11
Para materiais padrão, geralmente é melhor incorporar a rugosidade no mapa metálico ou normal (o primeiro parece ser o padrão). Eu recomendaria usar o Photo Shop, Paint Shop ou Gimp para criar um metal adequado que incorpore rugosidade. Como alternativa, se você tiver o Substance Painter ou algo semelhante, poderá exportar sua rugosidade exatamente como o Unity deseja e ter o benefício de visualizar seus materiais antes de colocá-los no Unity.
David Peterson
um estudioso e um cavalheiro
Bas Smit