Comment créer un shader «Surface humide» / «Flaque d'eau peu profonde» dans Unity?


71

Dans mon jeu, j'ai besoin de créer des flaques d'eau dynamiques, mais je ne trouve pas de tutoriel qui montre comment créer un tel effet (un exemple est présenté ci-dessous). Comment puis-je le faire?

Rupture quantique


4
Ennuyeux de voir une question aussi votée élevée et une réponse votée plus élevée ne pas être clôturée. C'est bien de choisir sa propre réponse, mais un peu idiot de réclamer cette prime pour soi-même :)
Tim Holt le

@ Timholt Sur quelle base fermerions-nous une question comme celle-ci? Il semble parfaitement sur le sujet.
Josh

Je dis que la personne qui l'a demandé devrait accepter sa propre réponse. Pardon mon mauvais usage de l'anglais.
Tim Holt

Réponses:


121

Réflexion

Pour créer un shader humide, vous devez d’abord avoir une réflexion.

SimpleRoad

Vous pouvez utiliser une sonde de réflexion ou un miroirReflection3, mais j'utilise ici une fausse réflexion (Cube Map) car le shader peut ensuite être utilisé sur mobile.

Réflexion

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

Distorsion

Pour ajouter de la distorsion à votre reflet, vous pouvez multiplier la carte normale et le worldRefl:

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

Distorsion

Forme procédurale

Vous pouvez utiliser le bruit pour créer une forme procédurale :

Capturer

Voici un tutoriel 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
}
}
}

La FBM ci-dessus ne doit pas être utilisée directement dans votre shader, car elle comporte de nombreux calculs de GPU et diminue les performances. Au lieu d'utiliser directement, vous pouvez rendre le résultat en une texture avec RenderTexture .

Shadertoy utilise plusieurs passes , une par "tampon". Comme son nom l’indique, cette passe stocke les résultats dans un tampon, qui n’est qu’une texture. Unity vous permettra également de rendre des textures.

2018-01-26_10-18-20

Créer un masque

Vous pouvez faire un masque épais et lisse avec ces fonctions:

Étape

étape

Les sorties 1 si [A]est inférieur ou égal à [B], sinon, les sorties 0.

Smoothstep

étape douce

Les mélanges entre deux valeurs se font en douceur, en fonction du lieu où une troisième valeur se situe dans cette plage, générant des valeurs comprises entre 0 et 1. Considérez-le comme un lerp inverse verrouillé avec une valeur de sortie lissée.

Résultat

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

image

Utiliser des cartes

shaderToy Ombrage Physique

Voici une définition utile:

Rugosité Décrit la microsurface de l'objet. Le blanc 1.0 est rugueux et le noir 0.0 est lisse. Si elle est rugueuse, la microsurface peut disperser les rayons lumineux et rendre la surbrillance plus sombre et plus large. La même quantité d'énergie lumineuse est reflétée sortant de la surface. Cette carte a la plus grande liberté artistique. Il n'y a pas de mauvaise réponse ici. Cette carte donne à l’actif le plus de caractère possible car il décrit véritablement la surface, par exemple des rayures, des empreintes de doigt, des taches, de la crasse, etc.

Brillance Cette carte est l'inverse de la carte de rugosité. Le blanc 1,0 est lisse et 0,0 noir est rugueux. Décrit la microsurface de l'objet. Si elle est rugueuse, la microsurface peut disperser les rayons lumineux et rendre la surbrillance plus sombre et plus large. La même quantité d'énergie lumineuse est reflétée sortant de la surface. Cette carte a la plus grande liberté artistique. Il n'y a pas de mauvaise réponse ici. Cette carte donne à l’actif le plus de caractère possible car il décrit véritablement la surface, par exemple des rayures, des empreintes de doigt, des taches, de la crasse, etc.

Spéculaire Cette carte contient les informations de réflectance pour les surfaces métalliques et diélectriques (non métalliques). Il s'agit d'une différence essentielle entre les flux de travail métal / rugueux et spéc / brillant. Les mêmes règles s'appliquent. Vous devez utiliser les valeurs mesurées pour les métaux et la plupart des diélectriques tomberont dans la plage de 0,04 à 4%. S'il y a de la saleté sur le métal, la valeur de réflectance doit également être abaissée. Cependant, vous pouvez ajouter différentes valeurs dans la carte spéculaire pour les matériaux diélectriques, car vous avez le contrôle pour créer la carte.

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

Rugosité

image

Je ne sais pas pourquoi, mais le shader standard d'Unity n'a pas de carte de finesse. J'ai donc écrit un shader de base et ajouté cette carte.

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

Je pense qu'Unity n'a pas de rugosité, il n'a que des métaux, mais le canal alpha est destiné à la rugosité et le canal rouge est destiné aux métaux. Vous pouvez modifier l'intensité avec douceur.

Source sur GitHub .

Liens utiles

boue-sphère-1024x576

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

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


39
Wow, vous avez fait toute une série de tutoriels sur les shader sur le site de questions / réponses.
Ocelot

6
@Ocelot J'adore la façon dont Seyed continue d'en ajouter de plus en plus ici. J'aime bricoler avec des shaders et ceux-ci sont vraiment utiles pour plus d'idées et comme tutoriels. Il peut continuer à les afficher pour toujours, à mon avis.
John Hamilton

7
Réponse étonnante. Il est très difficile de travailler avec des shaders et il me faut des heures de violon, de recherche, d’essais et d’erreur et l’examen d’autres shaders pour obtenir les effets souhaités. Et ici, vous faites cela, pour quelqu'un d'autre, gratuitement.
Draco18s

1
Pour les matériaux standard, il est généralement préférable d’intégrer la rugosité dans la carte métallique ou normale (la première semble être la valeur par défaut). Je recommanderais d'utiliser Photo Shop, Paint Shop ou Gimp pour créer un métal adéquat qui incorpore une rugosité. Alternativement, si vous avez Substance Painter ou similaire, vous pouvez exporter votre rugosité exactement comme Unity le désire et avoir l’avantage de visualiser vos matériaux avant de les placer dans Unity.
David Peterson

un érudit et un monsieur
Bas Smit
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.