Как я могу сделать шейдер «влажная поверхность» / «мелкая лужа» в Unity?

71

В моей игре мне нужно создавать динамические водяные лужи, но я не могу найти учебник, который показывает, как я могу создать такой эффект (пример которого показан ниже). Как я могу это сделать?

Квантовый разлом

Сейед Мортеза Камали
источник
4
Раздражает, что закрытый вопрос с таким большим количеством голосов и ответом выше. Хорошо выбрать свой собственный ответ как лучший, хотя и немного глупо требовать награду за себя :)
Тим Холт
@TimHolt На каком основании мы бы закрыли такой вопрос? Вроде идеально по теме.
Джош
Я говорю, что человек, который спросил это, должен принять его собственный ответ. Извините за неправильное использование английского языка.
Тим Холт

Ответы:

121

отражение

Чтобы создать мокрый шейдер, сначала нужно иметь отражение.

SimpleRoad

Вы можете использовать Reflection Probe или MirrorReflection3, но здесь я использую поддельное отражение (Cube Map), потому что шейдер можно использовать на мобильном устройстве.

отражение

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

Искажение

Чтобы добавить искажение к вашему отражению, вы можете умножить карту нормалей на worldRefl:

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

искажение

Процедурная форма

Вы можете использовать шум, чтобы сделать процедурную форму:

захватить

Вот учебник по 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
}
}
}

Приведенную выше FBM не следует использовать непосредственно в своем шейдере, потому что она имеет много вычислений на GPU и снижает производительность. Вместо непосредственного использования вы можете визуализировать результат в текстуру с помощью RenderTexture .

Shadertoy использует несколько проходов , по одному на «буфер». Как видно из названия, этот проход сохраняет результаты в буфере, который является просто текстурой. Unity также позволит вам визуализировать текстуры.

2018-01-26_10-18-20

Создание Маски

Вы можете сделать толстую и гладкую маску с этими функциями:

шаг

шаг

Выходы 1, если [A]меньше или равно [B], в противном случае выводит 0.

Smoothstep

smoothstep

Смешивается плавно между двумя значениями, в зависимости от того, где находится третье значение в этом диапазоне, и выводит значения в диапазоне от 0 до 1. Думайте об этом как о фиксированном обратном скачке с сглаженным выходным значением.

Результат

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

образ

Использование карт

shaderToy Физически основанное затенение

Вот полезные определения:

Шероховатость Описывает микроповерхность объекта. Белый 1.0 - грубый, а черный 0.0 - гладкий. Микроповерхность, если она шероховатая, может вызвать рассеивание световых лучей и сделать подсветку более тусклой и широкой. Такое же количество энергии света отражается при выходе на поверхность. Эта карта обладает наибольшей художественной свободой. Здесь нет неправильных ответов. Эта карта дает активу больше всего символов, поскольку она действительно описывает поверхность, например, царапины, отпечатки пальцев, пятна, грязь и т. Д.

Глянцевость Эта карта является обратной к карте шероховатости. Белый 1.0 - гладкий, а 0.0 - грубый. Описывает микроповерхность объекта. Микроповерхность, если она шероховатая, может вызвать рассеивание световых лучей и сделать подсветку более тусклой и широкой. Такое же количество энергии света отражается при выходе на поверхность. Эта карта обладает наибольшей художественной свободой. Здесь нет неправильных ответов. Эта карта дает активу больше всего символов, поскольку она действительно описывает поверхность, например, царапины, отпечатки пальцев, пятна, грязь и т. Д.

Specular Эта карта содержит информацию об отражательной способности как металлических, так и диэлектрических (неметаллических) поверхностей. Это ключевое различие в рабочих процессах металл / шероховатый и спец / глянец. Применяются те же правила. Вам необходимо использовать измеренные значения для металлов, и большинство всех диэлектриков будут падать в диапазоне 0,04 - 4%. Если на металле есть грязь, значение отражательной способности также необходимо уменьшить. Тем не менее, вы можете добавить различные значения в карту отражений для диэлектрических материалов, так как вы можете создавать карту.

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

неровность

образ

Я не знаю почему, но стандартный шейдер Unity не имеет карты гладкости, поэтому я написал базовый шейдер и добавил эту карту.

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

Я думаю, что у Unity нет шероховатости, у него есть только металлик, но альфа-канал - для шероховатости, а красный - для металлика. Вы можете изменить интенсивность с плавностью.

Источник на GitHub .

Полезные ссылки

грязевая сфера-1024x576

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

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

Сейед Мортеза Камали
источник
39
Вау, вы сделали целую серию учебников по шейдерам на сайте вопросов и ответов.
Оцелот
6
@Ocelot Мне нравится, как Seyed продолжает добавлять сюда все больше и больше. Мне нравится возиться с шейдерами, и они действительно полезны для новых идей, а также для обучения. Он может продолжать публиковать их навсегда, по моему мнению.
Джон Гамильтон
7
Удивительный ответ. С шейдерами очень трудно работать, и мне требуются часы возни, исследований, проб и ошибок и изучения других шейдеров, чтобы получить нужные мне эффекты. И здесь вы делаете это для кого-то, бесплатно.
Draco18s
1
Для стандартных материалов обычно лучше встраивать шероховатости в металлическую карту или карту нормалей (первая, по-видимому, используется по умолчанию). Я бы порекомендовал использовать Photo Shop, Paint Shop или Gimp, чтобы создать правильный металлик, который вмещает шероховатость. В качестве альтернативы, если у вас есть Substance Painter или тому подобное, вы можете экспортировать ваши шероховатости в точности так, как того желает Unity, и вы можете визуализировать ваши материалы перед тем, как поместить их в Unity.
Дэвид Петерсон
ученый и джентльмен
Бас Смит