【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]