如何在Unity中制作“湿表面” /“浅水坑”着色器?


71

在我的游戏中,我需要创建动态水坑,但是找不到能说明如何实现这种效果的教程(下面显示了一个示例)。我该怎么做?

量子断裂


4
看到如此高投票的问题和高投票的答案没有被关闭很烦。最好选择自己的答案,尽管为自己争取赏金有点愚蠢:)
蒂姆·霍尔特

@TimHolt我们将在什么基础上结束这样的问题?似乎完全是话题。
乔什

我是说要求它的人应该接受自己的回答。请原谅我滥用英语。
蒂姆·霍尔特

Answers:


121

反射

若要创建湿着色器,首先需要进行反射。

简单路

您可以使用Reflection ProbeMirrorReflection3,但是我在这里使用了虚假的反射(立方体贴图),因为可以在移动设备上使用着色器。

反射

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

失真

程序形状

您可以使用噪声来制作程序形状:

捕获

这是分形布朗运动(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

创建蒙版

您可以使用以下功能制作厚而光滑的面膜:

步

如果[A]小于或等于1,则输出1 [B];否则,输出0。

平稳步伐

平稳的步伐

根据第三个值在该范围内的位置,在两个值之间平滑混合,输出0到1之间的值。将其视为具有平滑输出值的钳位逆lerp。

结果

/* 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黑色则粗糙。描述对象的微表面。微观表面如果粗糙,则可能导致光线散射,并使高光显得更暗,更宽。反射出的光能量与进入表面的能量相同。该地图具有最大的艺术自由度。这里没有错误的答案。这张地图为资产提供了最多的特征,因为它真正地描述了表面,例如划痕,指纹,污迹,污垢等。

镜面反射 此贴图包含金属和电介质(非金属)表面的反射率信息。这是金属/毛坯和规格/光泽工作流程中的关键区别。相同的规则适用。您需要使用金属的测量值,并且大多数所有电介质会落在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不具有粗糙度,它仅具有金属性,但是alpha通道用于粗糙度,红色通道用于金属性。您可以通过平滑度更改强度。

来源GitHub

有用的链接

泥球-1024x576

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

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


39
哇,您已经在Q&A网站上完成了整个着色器教程系列。
豹猫

6
@Ocelot我喜欢Seyed如何在这里不断添加更多这些东西。我喜欢修补着色器,这些着色器对于获得更多想法和教程非常有帮助。我认为他可以永远将其发布。
约翰·汉密尔顿

7
惊人的答案。着色器非常难以使用,需要花费数小时的时间进行摆弄,研究,反复试验以及检查其他着色器才能获得所需的效果。在这里,您可以免费为其他人这样做。
Draco18s

1
对于标准材料,通常最好将粗糙度嵌入到金属贴图或法线贴图中(前者似乎是默认设置)。我建议使用Photo Shop,Paint Shop或Gimp创建合适的金属材料,以嵌入粗糙度。或者,如果您具有Substance Painter或类似工具,则可以完全按照Unity的要求导出粗糙度,并可以在将其放入Unity之前可视化您的材料。
大卫·彼得森

学者和绅士
Bas Smit
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.