DomDomm 发表于 2022-6-6 15:33

【Unity】将 Shadertoy 上的 Shader 移植到 Unity 里

Shadertoy 上有许多惊人的 Shader,不过要让 Unity 用上需要进行一系列的改写。

本文并不完整,是自学时的一些记录,仅供参考。
shader 代码框架


在 Unity 中任意创建一个 XXX.shader 文件,内容大致如下:
Shader "Custom/Test"{    Properties    {      _MainTex ("Albedo (RGB)", 2D) = "white" {}      _BackTex ("BackTex", 2D) = "white" {}      _Mouse("Mouse", Vector) = (0.5,0.5,0.5,0.5)      _iTime("iTime", Float) = 20.0    }    SubShader    {      Pass         {            cull off            CGPROGRAM            // 这里将放入 shader 代码                        ENDCG      }    }    FallBack "Diffuse"}定义一系列宏衔接变量与函数


Shadertoy 上的 Shader 使用 GLSL 书写,需要给原 Shader 代码添加一系列宏,从而衔接到 HLSL:
#define vec2 float2#define vec3 float3#define vec4 float4#define mat2 float2x2#define mat3 float3x3#define mat4 float4x4#define iGlobalTime _Time.y#define mod fmod#define mix lerp#define fract frac#define texture2D tex2D#define iResolution _ScreenParams#define gl_FragCoord ((_iParam.scrPos.xy/_iParam.scrPos.w) * _ScreenParams.xy)#define PI2 6.28318530718#define pi 3.14159265358979#define halfpi (pi * 0.5)#define oneoverpi (1.0 / pi)实例


对于这个 Shader,原代码如下:
// Heartfelt - by Martijn Steinrucken aka BigWings - 2017// Email:countfrolic@gmail.com Twitter:@The_ArtOfCode// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.#define S(a, b, t) smoothstep(a, b, t)//#define CHEAP_NORMALS#define HAS_HEART#define USE_POST_PROCESSINGvec3 N13(float p) {    //from DAVE HOSKINS   vec3 p3 = fract(vec3(p) * vec3(.1031,.11369,.13787));   p3 += dot(p3, p3.yzx + 19.19);   return fract(vec3((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y, (p3.y+p3.z)*p3.x));}vec4 N14(float t) {    return fract(sin(t*vec4(123., 1024., 1456., 264.))*vec4(6547., 345., 8799., 1564.));}float N(float t) {    return fract(sin(t*12345.564)*7658.76);}float Saw(float b, float t) {    return S(0., b, t)*S(1., b, t);}vec2 DropLayer2(vec2 uv, float t) {    vec2 UV = uv;      uv.y += t*0.75;    vec2 a = vec2(6., 1.);    vec2 grid = a*2.;    vec2 id = floor(uv*grid);      float colShift = N(id.x);   uv.y += colShift;      id = floor(uv*grid);    vec3 n = N13(id.x*35.2+id.y*2376.1);    vec2 st = fract(uv*grid)-vec2(.5, 0);      float x = n.x-.5;      float y = UV.y*20.;    float wiggle = sin(y+sin(y));    x += wiggle*(.5-abs(x))*(n.z-.5);    x *= .7;    float ti = fract(t+n.z);    y = (Saw(.85, ti)-.5)*.9+.5;    vec2 p = vec2(x, y);      float d = length((st-p)*a.yx);      float mainDrop = S(.4, .0, d);      float r = sqrt(S(1., y, st.y));    float cd = abs(st.x-x);    float trail = S(.23*r, .15*r*r, cd);    float trailFront = S(-.02, .02, st.y-y);    trail *= trailFront*r*r;      y = UV.y;    float trail2 = S(.2*r, .0, cd);    float droplets = max(0., (sin(y*(1.-y)*120.)-st.y))*trail2*trailFront*n.z;    y = fract(y*10.)+(st.y-.5);    float dd = length(st-vec2(x, y));    droplets = S(.3, 0., dd);    float m = mainDrop+droplets*r*trailFront;      //m += st.x>a.y*.45 || st.y>a.x*.165 ? 1.2 : 0.;    return vec2(m, trail);}float StaticDrops(vec2 uv, float t) {    uv *= 40.;      vec2 id = floor(uv);    uv = fract(uv)-.5;    vec3 n = N13(id.x*107.45+id.y*3543.654);    vec2 p = (n.xy-.5)*.7;    float d = length(uv-p);      float fade = Saw(.025, fract(t+n.z));    float c = S(.3, 0., d)*fract(n.z*10.)*fade;    return c;}vec2 Drops(vec2 uv, float t, float l0, float l1, float l2) {    float s = StaticDrops(uv, t)*l0;   vec2 m1 = DropLayer2(uv, t)*l1;    vec2 m2 = DropLayer2(uv*1.85, t)*l2;      float c = s+m1.x+m2.x;    c = S(.3, 1., c);      return vec2(c, max(m1.y*l0, m2.y*l1));}void mainImage( out vec4 fragColor, in vec2 fragCoord ){    vec2 uv = (fragCoord.xy-.5*iResolution.xy) / iResolution.y;    vec2 UV = fragCoord.xy/iResolution.xy;    vec3 M = iMouse.xyz/iResolution.xyz;    float T = iTime+M.x*2.;      #ifdef HAS_HEART    T = mod(iTime, 102.);    T = mix(T, M.x*102., M.z>0.?1.:0.);    #endif            float t = T*.2;      float rainAmount = iMouse.z>0. ? M.y : sin(T*.05)*.3+.7;      float maxBlur = mix(3., 6., rainAmount);    float minBlur = 2.;      float story = 0.;    float heart = 0.;      #ifdef HAS_HEART    story = S(0., 70., T);      t = min(1., T/70.);                     // remap drop time so it goes slower when it freezes    t = 1.-t;    t = (1.-t*t)*70.;      float zoom= mix(.3, 1.2, story);      // slowly zoom out    uv *=zoom;    minBlur = 4.+S(.5, 1., story)*3.;       // more opaque glass towards the end    maxBlur = 6.+S(.5, 1., story)*1.5;      vec2 hv = uv-vec2(.0, -.1);             // build heart    hv.x *= .5;    float s = S(110., 70., T);            // heart gets smaller and fades towards the end    hv.y-=sqrt(abs(hv.x))*.5*s;    heart = length(hv);    heart = S(.4*s, .2*s, heart)*s;    rainAmount = heart;                     // the rain is where the heart is      maxBlur-=heart;                         // inside the heart slighly less foggy    uv *= 1.5;                              // zoom out a bit more    t *= .25;    #else    float zoom = -cos(T*.2);    uv *= .7+zoom*.3;    #endif    UV = (UV-.5)*(.9+zoom*.1)+.5;      float staticDrops = S(-.5, 1., rainAmount)*2.;    float layer1 = S(.25, .75, rainAmount);    float layer2 = S(.0, .5, rainAmount);            vec2 c = Drops(uv, t, staticDrops, layer1, layer2);   #ifdef CHEAP_NORMALS      vec2 n = vec2(dFdx(c.x), dFdy(c.x));// cheap normals (3x cheaper, but 2 times shittier ;))    #else      vec2 e = vec2(.001, 0.);      float cx = Drops(uv+e, t, staticDrops, layer1, layer2).x;      float cy = Drops(uv+e.yx, t, staticDrops, layer1, layer2).x;      vec2 n = vec2(cx-c.x, cy-c.x);      // expensive normals    #endif            #ifdef HAS_HEART    n *= 1.-S(60., 85., T);    c.y *= 1.-S(80., 100., T)*.8;    #endif      float focus = mix(maxBlur-c.y, minBlur, S(.1, .2, c.x));    vec3 col = textureLod(iChannel0, UV+n, focus).rgb;            #ifdef USE_POST_PROCESSING    t = (T+3.)*.5;                                    // make time sync with first lightnoing    float colFade = sin(t*.2)*.5+.5+story;    col *= mix(vec3(1.), vec3(.8, .9, 1.3), colFade);   // subtle color shift    float fade = S(0., 10., T);                         // fade in at the start    float lightning = sin(t*sin(t*10.));                // lighting flicker    lightning *= pow(max(0., sin(t+sin(t))), 10.);      // lightning flash    col *= 1.+lightning*fade*mix(1., .1, story*story);// composite lightning    col *= 1.-dot(UV-=.5, UV);                        // vignette                                                    #ifdef HAS_HEART      col = mix(pow(col, vec3(1.2)), col, heart);      fade *= S(102., 97., T);    #endif      col *= fade;                                        // composite start and end fade    #endif      //col = vec3(heart);    fragColor = vec4(col, 1.);}
改写后结果如下:
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'Shader "Custom/rainWindowTest"{    Properties    {      _BackTex ("BackTex", 2D) = "white" {}      _Mouse("Mouse", Vector) = (0.5,0.5,0.5,0.5)      _iTime("Time", Float) = 20.0    }    SubShader    {      Pass         {            cull off            Tags { "RenderType"="Opaque" }            CGPROGRAM            #pragma target 3.0            #pragma vertex vert            #pragma fragment mainImage            #include "UnityCG.cginc"            #define vec2 float2            #define vec3 float3            #define vec4 float4            #define mat2 float2x2            #define mat3 float3x3            #define mat4 float4x4            #define iGlobalTime _Time.y            #define mod fmod            #define mix lerp            #define fract frac            #define texture2D tex2D            #define iResolution _ScreenParams            // #define iResolution float4(800,450,1+(1/800),1+(1/450))            #define gl_FragCoord ((_iParam.scrPos.xy/_iParam.scrPos.w) * _ScreenParams.xy)            #define PI2 6.28318530718            #define pi 3.14159265358979            #define halfpi (pi * 0.5)            #define oneoverpi (1.0 / pi)            #define S(a, b, t) smoothstep(a, b, t)            //#define CHEAP_NORMALS            // #define HAS_HEART            #define USE_POST_PROCESSING            // #define iMouse _Mouse            #define iMouse float4(_Mouse.x,_Mouse.y*_ScreenParams.y/450,_Mouse.z,_Mouse.w)            float4 _Mouse;            // #define iTime _iTime            #define iTime _Time.y            float _iTime;            // #define iChannel0 _MainTex            Texture2D _MainTex;            float4 _MainTex_ST;// _MainTex_ST.xy 缩放量,_MainTex_ST.zw 偏移值            #define iChannel0 _BackTex            Texture2D _BackTex;            // float4 _BackTex_ST;            SamplerState sampler_MainTex; // 对纹理进行采样            SamplerState sampler_BackTex; // 对纹理进行采样            struct v2f{                float4 position : SV_POSITION;            };            v2f vert(float4 v:POSITION) : SV_POSITION {                v2f o;                o.position = UnityObjectToClipPos (v);                return o;            }            vec3 N13(float p) {                //from DAVE HOSKINS                vec3 p3 = fract(vec3(p,p,p) * vec3(.1031,.11369,.13787));                p3 += dot(p3, p3.yzx + 19.19);                return fract(vec3((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y, (p3.y+p3.z)*p3.x));            }            vec4 N14(float t) {                return fract(sin(t*vec4(123., 1024., 1456., 264.))*vec4(6547., 345., 8799., 1564.));            }            float N(float t) {                return fract(sin(t*12345.564)*7658.76);            }            float Saw(float b, float t) {                return S(0., b, t)*S(1., b, t);            }            vec2 DropLayer2(vec2 uv, float t) {                vec2 UV = uv;                              uv.y += t*0.75;                vec2 a = vec2(6., 1.);                vec2 grid = a*2.;                vec2 id = floor(uv*grid);                              float colShift = N(id.x);               uv.y += colShift;                              id = floor(uv*grid);                vec3 n = N13(id.x*35.2+id.y*2376.1);                vec2 st = fract(uv*grid)-vec2(.5, 0);                              float x = n.x-.5;                              float y = UV.y*20.;                float wiggle = sin(y+sin(y));                x += wiggle*(.5-abs(x))*(n.z-.5);                x *= .7;                float ti = fract(t+n.z);                y = (Saw(.85, ti)-.5)*.9+.5;                vec2 p = vec2(x, y);                              float d = length((st-p)*a.yx);                              float mainDrop = S(.4, .0, d);                              float r = sqrt(S(1., y, st.y));                float cd = abs(st.x-x);                float trail = S(.23*r, .15*r*r, cd);                float trailFront = S(-.02, .02, st.y-y);                trail *= trailFront*r*r;                              y = UV.y;                float trail2 = S(.2*r, .0, cd);                float droplets = max(0., (sin(y*(1.-y)*120.)-st.y))*trail2*trailFront*n.z;                y = fract(y*10.)+(st.y-.5);                float dd = length(st-vec2(x, y));                droplets = S(.3, 0., dd);                float m = mainDrop+droplets*r*trailFront;                              //m += st.x>a.y*.45 || st.y>a.x*.165 ? 1.2 : 0.;                return vec2(m, trail);            }            float StaticDrops(vec2 uv, float t) {                uv *= 40.;                              vec2 id = floor(uv);                uv = fract(uv)-.5;                vec3 n = N13(id.x*107.45+id.y*3543.654);                vec2 p = (n.xy-.5)*.7;                float d = length(uv-p);                              float fade = Saw(.025, fract(t+n.z));                float c = S(.3, 0., d)*fract(n.z*10.)*fade;                return c;            }            vec2 Drops(vec2 uv, float t, float l0, float l1, float l2) {                float s = StaticDrops(uv, t)*l0;               vec2 m1 = DropLayer2(uv, t)*l1;                vec2 m2 = DropLayer2(uv*1.85, t)*l2;                              float c = s+m1.x+m2.x;                c = S(.3, 1., c);                              return vec2(c, max(m1.y*l0, m2.y*l1));            }            vec4 mainImage(v2f i): SV_Target            {                vec2 fragCoord = i.position;                vec2 uv = (fragCoord.xy-.5*iResolution.xy) / iResolution.y;                vec2 UV = fragCoord.xy/iResolution.xy;                vec3 M = iMouse.xyz/iResolution.xyz;                float T = iTime+M.x*2.;                              #ifdef HAS_HEART                  T = mod(iTime, 102.);                  T = mix(T, M.x*102., M.z>0.?1.:0.);                #endif                                                float t = T*.2;                              float rainAmount = iMouse.z>0. ? M.y : sin(T*.05)*.3+.7;                              float maxBlur = mix(3., 6., rainAmount);                float minBlur = 2.;                              float story = 0.;                float heart = 0.;                              #ifdef HAS_HEART                  story = S(0., 70., T);                                        t = min(1., T/70.);                     // remap drop time so it goes slower when it freezes                  t = 1.-t;                  t = (1.-t*t)*70.;                                        float zoom= mix(.3, 1.2, story);      // slowly zoom out                  uv *=zoom;                  minBlur = 4.+S(.5, 1., story)*3.;       // more opaque glass towards the end                  maxBlur = 6.+S(.5, 1., story)*1.5;                                        vec2 hv = uv-vec2(.0, -.1);             // build heart                  hv.x *= .5;                  float s = S(110., 70., T);            // heart gets smaller and fades towards the end                  hv.y-=sqrt(abs(hv.x))*.5*s;                  heart = length(hv);                  heart = S(.4*s, .2*s, heart)*s;                  rainAmount = heart;                     // the rain is where the heart is                                        maxBlur-=heart;                         // inside the heart slighly less foggy                  uv *= 1.5;                              // zoom out a bit more                  t *= .25;                #else                  float zoom = -cos(T*.2);                  // uv *= .7+zoom*.3;// 不缩放镜头                #endif                UV = (UV-.5)*(.9)+.5;                              float staticDrops = S(-.5, 1., rainAmount)*2.;                float layer1 = S(.25, .75, rainAmount);                float layer2 = S(.0, .5, rainAmount);                                                vec2 c = Drops(uv, t, staticDrops, layer1, layer2);                #ifdef CHEAP_NORMALS                  vec2 n = vec2(dFdx(c.x), dFdy(c.x));// cheap normals (3x cheaper, but 2 times shittier ;))                #else                  vec2 e = vec2(.001, 0.);                  float cx = Drops(uv+e, t, staticDrops, layer1, layer2).x;                  float cy = Drops(uv+e.yx, t, staticDrops, layer1, layer2).x;                  vec2 n = vec2(cx-c.x, cy-c.x);      // expensive normals                #endif                                                #ifdef HAS_HEART                  n *= 1.-S(60., 85., T);                  c.y *= 1.-S(80., 100., T)*.8;                #endif                              float focus = mix(maxBlur-c.y, minBlur, S(.1, .2, c.x));                // vec3 col = tex2Dlod(iChannel0, UV+n, focus).rgb;                vec3 col = _MainTex.SampleLevel(sampler_MainTex, UV+n, focus).rgb;                                                #ifdef USE_POST_PROCESSING                  t = (T+3.)*.5;                                    // make time sync with first lightnoing                  float colFade = sin(t*.2)*.5+.5+story;                  col *= mix(vec3(1.,1.,1.), vec3(.8, .9, 1.3), colFade); // subtle color shift                  float fade = S(0., 10., T);                         // fade in at the start                  float lightning = sin(t*sin(t*10.));                // lighting flicker                  lightning *= pow(max(0., sin(t+sin(t))), 10.);      // lightning flash                  col *= 1.+lightning*fade*mix(1., .1, story*story);// composite lightning                  col *= 1.-dot(UV-=.5, UV);                        // vignette                                        #ifdef HAS_HEART                        col = mix(pow(col, vec3(1.2,1.2,1.2)), col, heart);                        fade *= S(102., 97., T);                  #endif                                        col *= fade;                                        // composite start and end fade                #endif                UV+=float2(0.5,0.5);                col=_BackTex.SampleLevel(sampler_BackTex, UV+n, focus).rgb;                              //col = vec3(heart);                vec4 fragColor = vec4(col, 1.);                return fragColor;            }                        ENDCG      }    }    FallBack "Diffuse"}
页: [1]
查看完整版本: 【Unity】将 Shadertoy 上的 Shader 移植到 Unity 里