Come posso creare uno shader “superficie bagnata” / “pozza superficiale” in Unity?


71

Nel mio gioco ho bisogno di creare pozze d'acqua dinamiche ma non riesco a trovare un tutorial che mostri come posso ottenere un tale effetto (un esempio di cui è mostrato di seguito). Come posso farlo?

Pausa quantistica


4
Fastidioso di vedere una domanda così votata e una risposta più votata non chiusa. Va bene scegliere la tua risposta come migliore, anche se un po 'sciocca per rivendicare la grazia per te :)
Tim Holt,

@TimHolt Su quali basi chiuderemmo una domanda come questa? Sembra perfettamente in tema.
Josh

Sto dicendo che la persona che lo ha chiesto dovrebbe accettare la propria risposta. Perdonate il mio uso improprio dell'inglese.
Tim Holt,

Risposte:


121

Riflessione

Per creare uno shader bagnato, devi prima avere un riflesso.

SimpleRoad

È possibile utilizzare una sonda di riflessione o una MirrorReflection3 ma, qui uso una riflessione falsa (Mappa cubo) perché lo shader può quindi essere utilizzato su dispositivi mobili.

Riflessione

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

Distorsione

Per aggiungere distorsione al tuo riflesso, puoi moltiplicare la mappa normale e il worldRefl:

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

distorsione

Forma procedurale

Puoi usare il rumore per creare una forma procedurale :

catturare

Ecco un tutorial su 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
}
}
}

L'FBM sopra non dovrebbe essere usato direttamente nel tuo shader perché ha molti calcoli GPU e diminuisce le prestazioni. Invece di utilizzare direttamente, puoi renderizzare il risultato su una trama con RenderTexture .

Shadertoy utilizza più passaggi , uno per "buffer". Come indica il nome, questo passaggio memorizza i risultati in un buffer, che è solo una trama. Unity ti permetterà di renderizzare anche le trame.

2018-01-26_10-18-20

Creazione della maschera

Puoi creare una maschera spessa e liscia con queste funzioni:

Passo

passo

Emette 1 se [A]è inferiore o uguale a [B], altrimenti emette 0.

smoothstep

smoothstep

Si fonde uniformemente tra due valori, in base a dove si trova un terzo valore in quell'intervallo, producendo valori compresi tra 0 e 1. Consideralo come un lerp inverso bloccato con un valore di output livellato.

Risultato

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

Immagine

Utilizzando le mappe

shaderToy Ombreggiatura fisica

Ecco una definizione utile:

Rugosità Descrive la microsuperficie dell'oggetto. Il bianco 1.0 è ruvido e il nero 0.0 è liscio. La microsuperficie se ruvida può causare la dispersione dei raggi di luce e rendere la luce più fioca e più ampia. La stessa quantità di energia luminosa viene riflessa uscendo dalla superficie. Questa mappa ha la massima libertà artistica. Non ci sono risposte sbagliate qui. Questa mappa conferisce alla risorsa il maggior carattere in quanto descrive veramente la superficie, ad esempio graffi, impronte digitali, macchie, sporcizia, ecc.

Lucidità Questa mappa è l'inverso della mappa di rugosità. Il bianco 1.0 è liscio e il nero 0.0 è ruvido. Descrive la microsuperficie dell'oggetto. La microsuperficie se ruvida può causare la dispersione dei raggi di luce e rendere la luce più fioca e più ampia. La stessa quantità di energia luminosa viene riflessa uscendo dalla superficie. Questa mappa ha la massima libertà artistica. Non ci sono risposte sbagliate qui. Questa mappa conferisce alla risorsa il maggior carattere in quanto descrive veramente la superficie, ad esempio graffi, impronte digitali, macchie, sporcizia, ecc.

Speculare Questa mappa contiene le informazioni sulla riflettanza sia per le superfici metalliche che per quelle dielettriche (non metalliche). Questa è una differenza fondamentale nei flussi di lavoro metal / rough e spec / gloss. Si applicano le stesse regole. È necessario utilizzare i valori misurati per i metalli e la maggior parte di tutti i dielettrici cadrà nell'intervallo 0,04 - 4%. Se il metallo è sporco, è necessario ridurre anche il valore di riflettanza. Tuttavia, è possibile aggiungere valori diversi nella mappa speculare per i materiali dielettrici poiché si ha il controllo sull'autore della mappa.

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

Rugosità

Immagine

Non so perché, ma lo shader standard Unity non ha una mappa di fluidità, quindi ho scritto uno shader di base e ho aggiunto questa mappa.

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

Penso che Unity non abbia rugosità, abbia solo metallizzato, ma il canale alfa è per rugosità e il canale rosso per metallizzato. È possibile modificare l'intensità con fluidità.

Fonte su GitHub .

Link utili

fango-sfera-1024x576

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

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


39
Wow, hai realizzato un'intera serie di tutorial sugli shader sul sito di domande e risposte.
Ocelot,

6
@Ocelot Adoro come Seyed continui ad aggiungerne sempre di più qui. Mi piace armeggiare con gli shader e questi sono davvero utili per altre idee e anche come tutorial. Può continuare a pubblicarli per sempre secondo me.
John Hamilton,

7
Risposta incredibile. Gli shader sono molto difficili con cui lavorare e mi ci vogliono ore di manipolazione, ricerca, prove ed errori ed esame di altri shader per ottenere gli effetti che desidero. E qui lo stai facendo, per qualcun altro, gratuitamente.
Draco18s

1
Per i materiali standard di solito è meglio incorporare la rugosità nella mappa metallica o normale (la prima sembra essere l'impostazione predefinita). Consiglierei di usare Photo Shop, Paint Shop o Gimp per creare un metallo che incorpori la rugosità. In alternativa, se hai un pittore di sostanze o simili, puoi esportare la tua rugosità esattamente come Unity li desidera e avere il vantaggio di visualizzare i tuoi materiali prima di metterli in Unity.
David Peterson,

uno studioso e un gentiluomo
Bas Smit,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.