Shader "Unlit/DOMAIN_MAPPING_01" { Properties { _MainTex ("Texture", 2D) = "white" {} } 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; }; sampler2D _MainTex; float4 _MainTex_ST; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); UNITY_TRANSFER_FOG(o,o.vertex); return o; } // Author: Rigel // licence: https://creativecommons.org/licenses/by/4.0/ // link: https://www.shadertoy.com/view/ltjczK /* When you graph a real function y=f(x) you need a plane (2D). When you graph a complex function w=f(z), to see the relationship between the input complex plane (2D) and the output complex plane (2D), you need 4D. Because humans are not able to see in 4D one technique to visualize these functions is to use color hue, and saturation as the two extra dimensions. This technique is called domain coloring. https://en.wikipedia.org/wiki/Domain_coloring In case you know nothing about complex numbers, or need a refresher this youtube serie is very good ! */ #define speed _Time.y*.05 float2 toCarte(float2 z) { return z.x*float2(cos(z.y),sin(z.y)); } float2 toPolar(float2 z) { return float2(length(z),atan2(z.x,z.y)); } // All the following complex operations are defined for the polar form // of a complex number. So, they expect a complex number with the // format float2(radius,theta) -> radius*eˆ(i*theta). // The polar form makes the operations *,/,pow,log very light and simple // The price to pay is that +,- become costly :/ // So I switch back to cartesian in those cases. float2 zmul(float2 z1,float2 z2) { return float2(z1.x*z2.x,z1.y+z2.y); } float2 zdiv(float2 z1, float2 z2) { return float2(z1.x/z2.x,z1.y-z2.y); } float2 zlog(float2 z) { return toPolar(float2(log(z.x),z.y)); } float2 zpow(float2 z, float n) { return float2(exp(log(z.x)*n),z.y*n); } float2 zpow(float n, float2 z) { return float2(exp(log(n)*z.x*cos(z.y)),log(n)*z.x*sin(z.y)); } float2 zpow(float2 z1, float2 z2) { return zpow(exp(1.),zmul(zlog(z1),z2)); } float2 zadd(float2 z1, float2 z2) { return toPolar(toCarte(z1) + toCarte(z2)); } float2 zsub(float2 z1, float2 z2) { return toPolar(toCarte(z1) - toCarte(z2)); } //sinz, cosz and tanz came from -> https://www.shadertoy.com/view/Mt2GDV float2 zsin(float2 z) { z = toCarte(z); float e1 = exp(z.y); float e2 = exp(-z.y); float sinh = (e1-e2)*.5; float cosh = (e1+e2)*.5; return toPolar(float2(sin(z.x)*cosh,cos(z.x)*sinh)); } float2 zcos(float2 z) { z = toCarte(z); float e1 = exp(z.y); float e2 = exp(-z.y); float sinh = (e1-e2)*.5; float cosh = (e1+e2)*.5; return toPolar(float2(cos(z.x)*cosh,-sin(z.x)*sinh)); } float2 ztan(float2 z) { z = toCarte(z); float e1 = exp(z.y); float e2 = exp(-z.y); float cosx = cos(z.x); float sinh = (e1 - e2)*0.5; float cosh = (e1 + e2)*0.5; return toPolar(float2(sin(z.x)*cosx, sinh*cosh)/(cosx*cosx + sinh*sinh)); } float2 zeta(float2 z) { float2 sum = float2(.0,0.); for (int i=1; i<20; i++) sum += toCarte(zpow(float(i),-z)); return toPolar(sum); } float2 lambert(float2 z) { float2 sum = float2(.0,0.); for (int i=1; i<15; i++) sum += toCarte(zdiv(zpow(z,float(i)),zsub(float2(1.,.0),zpow(z,float(i))))); return toPolar(sum); } float2 mandelbrot(float2 z) { float2 sum = float2(.0,0.); float2 zc = toCarte(z); for (int i=1; i<11; i++) sum += toCarte(zpow(toPolar(sum),2.)) + zc; return toPolar(sum); } float2 julia(float2 z) { float2 sum = toCarte(zpow(z,2.)); // the julia set is connected if C is in the mandelbrot set and disconnected otherwise // to make it interesting, C is animated on the boundary of the main bulb // the formula for the boundary is 0.5*eˆ(i*theta) - 0.25*eˆ(i*2*theta) and came from iq // http://iquilezles.org/www/articles/mset_1bulb/mset1bulb.htm float theta = frac(speed)*2.*6.2830; float2 c = toCarte(float2(0.5,.5*theta)) - toCarte(float2(0.25,theta)) - float2(.25,.0); for (int i=0; i<7; i++) sum += toCarte(zpow(toPolar(sum),2.)) + c; return toPolar(sum); } float2 map(float2 uv) { float t = floor(speed-10.*floor(speed/10.));//x-y*floor(x/y);//floor(mod(speed,10.)); //t = 7.; float s = t == 1.? 4. : t==5.? .6: t==4.? 6. : t==5.? 2.5 : t== 9. ? 13. : 3.; uv *= s + s*.2*cos(frac(speed)*6.2830); float2 fz; float2 z = toPolar(uv); // z + 1 / z - 1 fz = t == 0. ? zdiv(zadd(z,float2(1.0,1.0)),zsub(z,float2(1.0,.0)) ) : // formula from wikipedia https://en.m.wikipedia.org/wiki/Complex_analysis // fz = (zˆ2 - 1)(z + (2-i))ˆ2 / zˆ2 + (2+2i) t == 1. ? zdiv(zmul(zsub(zpow(z,2.),float2(1.,0)),zpow(zadd(z,toPolar(float2(2.,-1.))),2.)),zadd(zpow(z,2.),toPolar(float2(2.,-2.)))) : // z^(3-i) + 1. t == 2. ? zadd(zpow(z,float2(3.,acos(-1.))),float2(1.,.0)) : // tan(z^3) / z^2 t == 3. ? zdiv(ztan(zpow(z,3.)),zpow(z,2.)) : // tan ( sin (z) ) t == 4. ? ztan(zsin(z)) : // sin ( 1 / z ) t == 5. ? zsin(zdiv(float2(1.,.0),z)) : // the usual coloring methods for the mandelbrot show the outside. // this technique allows to see the structure of the inside. t == 6. ? mandelbrot(zsub(z,float2(1.,.0))) : // the julia set t == 7. ? julia(z) : //https://en.m.wikipedia.org/wiki/Lambert_series t == 8. ? lambert(z) : // this is the Riemman Zeta Function (well, at least part of it... :P) // if you can prove that all the zeros of this function are // in the 0.5 + iy line, you will win: // a) a million dollars ! (no, really...) // b) eternal fame and your name will be worshiped in history books // c) you will uncover the deep and misterious connection between PI and the primes // https://en.m.wikipedia.org/wiki/Riemann_hypothesis // https://www.youtube.com/watch?v=rGo2hsoJSbo zeta(zadd(z,float2(8.,.0))); return toCarte(fz); } float3 color(float2 uv) { float a = atan2(uv.x,uv.y); float r = length(uv); float3 c = .5 * ( cos(a*float3(2.,2.,1.) + float3(.0,1.4,.4)) + 1. ); return c * smoothstep(1.,0.,abs(frac(log(r)-_Time.y*.1)-.5)) // modulus lines * smoothstep(1.,0.,abs(frac((a*7.)/3.14+(_Time.y*.1))-.5)) // phase lines * smoothstep(11.,0.,log(r)) // infinity fades to black * smoothstep(.5,.4,abs(frac(speed)-.5)); // scene switch } fixed4 frag (v2f i) : SV_Target { float2 UV = i.uv - .5; float2 uv = UV; fixed4 col = float4( color(map(uv)), 1.0 ); return col; } ENDCG } } }