Shadertoy 上有許多驚人的 Shader,不過要讓 Unity 用上需要進(jìn)行一系列的改寫锚烦。
本文并不完整,是自學(xué)時(shí)的一些記錄帝雇,僅供參考涮俄。
shader 代碼框架
在 Unity 中任意創(chuàng)建一個(gè) XXX.shader
文件,內(nèi)容大致如下:
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"
}
定義一系列宏銜接變量與函數(shù)
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)
實(shí)例
對于這個(gè) 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_PROCESSING
vec3 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.);
}
改寫后結(jié)果如下:
// 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; // 對紋理進(jìn)行采樣
SamplerState sampler_BackTex; // 對紋理進(jìn)行采樣
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"
}