// experimental shader to animate a sphere with vertex math // christopher rogers rogersc@fb.com // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' // Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject' Shader "Unlit/EggShader" { Properties { // _MainTex ("Texture", 2D) = "white" {} // these need to match the number of row/cols in MakeEgg.cs _U("U",Range(2,140)) = 20 _V("V",Range(2,140)) = 20 // sweep less than 360 to get a pac-man shape _TU("pie shape",Range(-1,1)) = 0 // main axis start and end _TV("row start",Range(-1,1)) = 0 _TVe("row end",Range(-1,1)) = 0 // radius of sphere radius("radius",Range(0.1,10)) = 1 // zig zag the row start for a star shape _star("star amount",Range(-18,18)) = 0 // squeeze in uv space _squeeze("sqeeuze face in uv", Range(0,10)) = 0 // color speed _colorSpeed("color speed", Range(-2,2)) = .1 // mix between these 2 colors along main axis _col1("color1",Color) = (1,1,1,1) _col2("color2",Color) = (1,1,1,1) // amount of noise for all 3 channels _noise("noise amount",Range(0,2)) = 0.01 // per channel noise freqency nxyz("noise freq",Vector) = (1,1,1,1) // offsets noise, doesnt do anything except be the same as _Time.y for now axyz("noise speed x y z w=all",Vector) = (1,1,1,1) } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Cull Off Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag // make fog work #pragma multi_compile_fog #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float4 color : COLOR; float4 uv : TEXCOORD0; // float4 tangent : TANGENT; // float4 binormal : binormal; }; struct v2f { float2 uv : TEXCOORD0; // UNITY_FOG_COORDS(1) float4 vertex : SV_POSITION; float4 color : COLOR; }; // sampler2D _MainTex; // float4 _MainTex_ST; float _U; float _V; float _TU; float _TV, _TVe; float radius; float _noise; float4 nxyz; float4 axyz; float4 _col1, _col2; float _star; float _squeeze; float _colorSpeed; //https://www.shadertoy.com/view/XsX3zB /* skew constants for 3d simplex functions */ const float F3 = 0.3333333; const float G3 = 0.1666667; const float M_PI = 3.1415927; /* discontinuous pseudorandom uniformly distributed in [-0.5, +0.5]^3 */ float3 random3(float3 c) { float j = 4096.0 * sin(dot(c, float3(17.0, 59.4, 15.0))); float3 r; r.z = frac(512.0 * j); j *= .125; r.x = frac(512.0 * j); j *= .125; r.y = frac(512.0 * j); return r - 0.5; } /* 3d simplex noise */ float simplex3d(float3 p) { /* 1. find current tetrahedron T and it's four vertices */ /* s, s+i1, s+i2, s+1.0 - absolute skewed (integer) coordinates of T vertices */ /* x, x1, x2, x3 - unskewed coordinates of p relative to each of T vertices*/ /* calculate s and x */ float3 s = floor(p + dot(p, float3(F3, F3, F3))); float3 x = p - s + dot(s, float3(G3, G3, G3)); /* calculate i1 and i2 */ float3 e = step(float3(0, 0, 0), x - x.yzx); float3 i1 = e * (1.0 - e.zxy); float3 i2 = 1.0 - e.zxy * (1.0 - e); /* x1, x2, x3 */ float3 x1 = x - i1 + G3; float3 x2 = x - i2 + 2.0 * G3; float3 x3 = x - 1.0 + 3.0 * G3; /* 2. find four surflets and store them in d */ float4 w, d; /* calculate surflet weights */ w.x = dot(x, x); w.y = dot(x1, x1); w.z = dot(x2, x2); w.w = dot(x3, x3); /* w fades from 0.6 at the center of the surflet to 0.0 at the margin */ w = max(0.6 - w, 0.0); /* calculate surflet components */ d.x = dot(random3(s), x); d.y = dot(random3(s + i1), x1); d.z = dot(random3(s + i2), x2); d.w = dot(random3(s + 1.0), x3); /* multiply d by w^4 */ w *= w; w *= w; d *= w; /* 3. return the sum of the four surflets */ return dot(d, float4(52.0, 52., 52., 52.)); } // given a polar coordinate convert to cartesian float3 polToCart(float row, float col) { float3 cart = {0, 0, 0}; // TODO this doesnt look right and use PI float u = col / _U * 6.28 * _TU; float v = row / _V * 3.14 * _TV; cart.x = radius * sin(v) * cos(u); cart.y = radius * sin(v) * sin(u); cart.z = radius * cos(v); return cart; } // doesnt get used but math is in comment at bottom float3 cartToPol(float3 cart) { float3 pol = {0, 0, 0}; return pol; } // main vert program v2f vert(appdata v) { v2f o; o.vertex = (v.vertex); o.uv = v.uv; o.color = v.color; UNITY_TRANSFER_FOG(o, o.vertex); // the uv coords are 0,1,2,3,4 etc float row = int(o.uv.x); float col = int(o.uv.y); // mix color along main axis float f = frac((_Time.y * _colorSpeed) + (o.uv.x / _U)); o.color = smoothstep(_col1, _col2, f); // min max for start/stop of rows on sphere row = min(row, _TV * _U); row = max(row, _TVe * _V); // make it jaggy row += (col % 2) * _star; // f = 1 - (f*f); // this pushes each vert towards the center of the quad row += v.uv.z * (_squeeze); // *f for pulsatiions col += v.uv.w * (_squeeze); o.vertex.xyz = polToCart(row, col); // hacky animation TODO make better axyz.z += _Time.y; axyz.x += _Time.x * 0.1; // add noise to the position o.vertex.xyz += _noise * simplex3d(axyz.xyz + (o.vertex.xyz * nxyz.xyz)); // i dont understand what to do here. Unity puts this is if i mul(MVP) // end goal is the object to move in space like any other object, // but this way its really tough/impossile to see in scene view // and stays the same screen size no matter how far away camera is o.vertex.xyz = UnityObjectToClipPos(o.vertex.xyz); return o; } fixed4 frag(v2f i) : SV_Target { // sample the texture fixed4 col = i.color; // apply fog UNITY_APPLY_FOG(i.fogCoord, col); return col; } ENDCG } } } /* float radius = sqrt(@P.x*@P.x + @P.y*@P.y + @P.z*@P.z); float u = atan2(@P.y, @P.x) + M_PI; float v = acos(@P.z/radius); @P.x = radius * sin(v)*cos(u); @P.y = radius * sin(v)*sin(u); @P.z = radius * cos(v); // Convert object space to world space float4 NewCoord = mul(_Object2World, CoordToConvert); // Convert worldspace to object space float4 NewCoord = mul(_World2Object, CoordToConvert); quick explanation: c# script creates a grid of unconnected quads (they do not share a vertex) and assigns the UV to be the integer grid position, ie: UV.xy = (3.0,2.0) also, the script stuffs another vector into the UV.wz that is a direction from that vertex to the center of the quad, so the effect is they all shrink when applied to a vertex position the vertex shader uses the polarCoordinateToCartesian formula to transform this grid into a sphere. the row end, row start variables control the start and end of the sphere to chop the ends off, to make it a tube per-channel noise frequency should have per-channel noise animation */