Jak mogę stworzyć shader „mokrej powierzchni” / „płytkiej kałuży” w Unity?


71

W mojej grze muszę tworzyć dynamiczne kałuże wodne, ale nie mogę znaleźć samouczka, który pokazuje, jak mogę uzyskać taki efekt (którego przykład pokazano poniżej). Jak mogę to zrobić?

Przerwa kwantowa


4
Irytujące, że tak wysoko głosowane pytanie i wyżej głosowana odpowiedź nie są zamknięte. Dobrze jest wybrać własną odpowiedź jako najlepszą, choć trochę głupio jest ubiegać się o nagrodę za siebie :)
Tim Holt

@TimHolt Na jakiej podstawie zamknęlibyśmy takie pytanie? Wydaje się idealnie na temat.
Josh

Mówię, że osoba, która o to poprosiła, powinna zaakceptować własną odpowiedź. Przepraszam za niewłaściwe użycie angielskiego.
Tim Holt

Odpowiedzi:


121

Odbicie

Aby utworzyć moduł cieniujący na mokro, najpierw musisz mieć odbicie.

SimpleRoad

Możesz użyć sondy refleksyjnej lub MirrorReflection3, ale używam tutaj fałszywej refleksji (mapa kostki), ponieważ moduł cieniujący może być następnie używany na urządzeniach mobilnych.

Odbicie

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

Zniekształcenie

Aby dodać zniekształcenie do swojego odbicia, możesz pomnożyć normalną mapę i worldRefl:

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

zniekształcenie

Kształt proceduralny

Za pomocą hałasu można uzyskać kształt proceduralny :

zdobyć

Oto samouczek 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
}
}
}

Powyższego FBM nie należy stosować bezpośrednio w module cieniującym, ponieważ ma wiele obliczeń na GPU i zmniejsza wydajność. Zamiast używać bezpośrednio, możesz renderować wynik do tekstury za pomocą RenderTexture .

Shadertoy używa wielu przejść , po jednym na „bufor”. Jak wskazuje nazwa, to przejście przechowuje wyniki w buforze, który jest tylko teksturą. Unity pozwoli ci także renderować tekstury.

2018-01-26_10-18-20

Tworzenie maski

Za pomocą tych funkcji możesz wykonać grubą i gładką maskę:

Krok

krok

Wyjścia 1, jeśli [A]jest mniejsze lub równe [B], w przeciwnym razie wyjścia 0.

Smoothstep

gładki krok

Łączy płynnie między dwiema wartościami, w zależności od tego, gdzie trzecia wartość znajduje się w tym zakresie, generując wartości od 0 do 1. Pomyśl o tym jak o zaciśniętej odwrotnej lerpie z wygładzoną wartością wyjściową.

Wynik

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

wizerunek

Korzystanie z map

shaderToy Cieniowanie fizyczne

Oto przydatne definicje:

Chropowatość Opisuje mikropowierzchnię obiektu. Biały 1.0 jest szorstki, a czarny 0.0 jest gładki. Mikropowierzchnia, jeśli jest szorstka, może powodować rozpraszanie promieni świetlnych i powodować, że podświetlenie wydaje się ciemniejsze i szersze. Ta sama ilość energii świetlnej jest odbijana podczas wychodzenia na powierzchnię. Ta mapa ma największą swobodę artystyczną. Tutaj nie ma błędnych odpowiedzi. Ta mapa nadaje zasobowi najwięcej charakteru, ponieważ naprawdę opisuje powierzchnię, np. Zadrapania, odciski palców, smugi, brud itp.

Połysk Ta mapa jest odwrotnością mapy chropowatości. Biały 1.0 jest gładki, a 0.0 czarny jest szorstki. Opisuje mikropowierzchnię obiektu. Mikropowierzchnia, jeśli jest szorstka, może powodować rozpraszanie promieni świetlnych i powodować, że podświetlenie wydaje się ciemniejsze i szersze. Ta sama ilość energii świetlnej jest odbijana podczas wychodzenia na powierzchnię. Ta mapa ma największą swobodę artystyczną. Tutaj nie ma błędnych odpowiedzi. Ta mapa nadaje zasobowi najwięcej charakteru, ponieważ naprawdę opisuje powierzchnię, np. Zadrapania, odciski palców, smugi, brud itp.

Lusterko Ta mapa zawiera informacje o współczynniku odbicia zarówno dla powierzchni metalowych, jak i dielektrycznych (niemetalowych). Jest to kluczowa różnica w przepływach pracy metal / szorstki i spec / połysk. Obowiązują te same zasady. Musisz użyć zmierzonych wartości dla metali i większość wszystkich dielektryków spadnie w zakresie 0,04 - 4%. Jeśli metal jest zabrudzony, należy również obniżyć wartość współczynnika odbicia. Możesz jednak dodać różne wartości do mapy zwierciadlanej dla materiałów dielektrycznych, ponieważ masz kontrolę nad tworzeniem mapy.

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

Chropowatość

wizerunek

Nie wiem dlaczego, ale standardowy moduł cieniujący Unity nie ma mapy płynności, więc napisałem podstawowy moduł cieniujący i dodałem tę mapę.

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

Myślę, że Unity nie ma szorstkości, ma tylko metal, ale kanał alfa służy do szorstkości, a czerwony kanał do metalu. Możesz płynnie zmieniać intensywność.

Źródło na GitHub .

Przydatne linki

mud-sphere-1024x576

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

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


39
Wow, zrobiłeś całą serię ćwiczeń z shaderem na stronie pytań i odpowiedzi.
Ocelot

6
@Ocelot Uwielbiam sposób, w jaki Seyed dodaje tutaj coraz więcej. Lubię majstrować przy shaderach, a te są naprawdę pomocne w przypadku dodatkowych pomysłów, a także samouczków. Moim zdaniem może nadal publikować je na zawsze.
John Hamilton

7
Niesamowita odpowiedź. Shadery są bardzo trudne do pracy i zajmuje mi wiele godzin zabawy, badań, prób i błędów oraz sprawdzania innych shaderów, aby uzyskać pożądane efekty. A tutaj robisz to, dla kogoś innego, za darmo.
Draco18s

1
W przypadku materiałów standardowych najlepiej jest osadzić chropowatość na mapie metalicznej lub normalnej (ta pierwsza wydaje się być domyślna). Poleciłbym użyć Photo Shop, Paint Shop lub Gimp, aby stworzyć odpowiedni metal, który osadzi szorstkość. Alternatywnie, jeśli masz malarza substancji lub tym podobne, możesz wyeksportować swoją szorstkość dokładnie tak, jak tego pragnie Unity, i skorzystaj z wizualizacji swoich materiałów, zanim umieścisz je w Unity.
David Peterson

uczony i dżentelmen
Bas Smit
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.