#ifndef AVATAR_CLONE_LIGHTING #define AVATAR_CLONE_LIGHTING #include "AutoLight.cginc" #include "iq_sdf.cginc" #include "math.cginc" #include "motion.cginc" #include "pbr.cginc" #include "poi.cginc" #include "shadertoy.cginc" sampler2D _BaseColor; sampler2D _Emission; float _Emission_Strength; sampler2D _Normal; sampler2D _Metallic; sampler2D _Roughness; bool _Disable_Normal_Texture; bool _Disable_Normal_Recalc; sampler2D _Height; float _Height_LOD; float _Height_Exponent; float _Height_Scale; float _Height_Speed_X; float _Height_Speed_Y; float _Height_AA_Sample_Scale; sampler2D _Height_Mask; float _Height_Mask_Exponent; float _Center_Out_Speed; float _Center_Out_Sharpness; float _Center_Out_Min_Radius; float _Center_Out_Max_Radius; float _Num_Clones; float _Clone_dx; float _Clone_dx_cents; float _Clone_dy; float _Clone_dz; float _Clone_Scale; float _Clone_Enable_Av_Height_Fix; float _Clone_Av_Height_Fix; float _Clone_Enable_Rotation; struct appdata { float4 position : POSITION; float2 uv : TEXCOORD0; float3 normal : NORMAL; }; void getVertexLightColor(inout v2f i) { #if defined(VERTEXLIGHT_ON) float3 light_pos = float3(unity_4LightPosX0.x, unity_4LightPosY0.x, unity_4LightPosZ0.x); float3 light_float = light_pos - i.worldPos; float3 light_dir = normalize(light_float); float ndotl = DotClamped(i.normal, light_dir); // Light fills an expanding sphere with surface area 4 * pi * r^2. // By conservation of energy, this means that at distance r, light intensity // is proportional to 1/(r^2). float attenuation = 1 / (1 + dot(light_float, light_float) * unity_4LightAtten0.x); i.vertexLightColor = unity_LightColor[0].rgb * ndotl * attenuation; i.vertexLightColor = Shade4PointLights( unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0, unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb, unity_4LightAtten0, i.worldPos, i.normal ); #endif } v2f vert(appdata v) { v2f o; o.objPos = v.position; o.clipPos = UnityObjectToClipPos(v.position); o.worldPos = mul(unity_ObjectToWorld, v.position); o.normal = UnityObjectToWorldNormal(v.normal); o.uv = v.uv; getVertexLightColor(o); return o; } float getDisplacement(float2 uv) { // Manipulate vertex in world space. float height = 0; float2 traveling_uv = uv; traveling_uv.x += _Time[0] * _Height_Speed_X; traveling_uv.y += _Time[0] * _Height_Speed_Y; traveling_uv = glsl_mod(traveling_uv, 1.0); const float duv = _Height_AA_Sample_Scale; float z0 = tex2Dlod(_Height, float4(traveling_uv.x, traveling_uv.y, _Height_LOD, 0)); // Trivial optimization. No need to keep going if no displacement is desired. if (z0 == 0) { return 0; } #if HEIGHT_AA_LEVEL >= 1 float z1 = tex2Dlod(_Height, float4(traveling_uv.x + duv, traveling_uv.y + duv, _Height_LOD, 0)); float z2 = tex2Dlod(_Height, float4(traveling_uv.x + duv, traveling_uv.y - duv, _Height_LOD, 0)); float z3 = tex2Dlod(_Height, float4(traveling_uv.x - duv, traveling_uv.y - duv, _Height_LOD, 0)); float z4 = tex2Dlod(_Height, float4(traveling_uv.x - duv, traveling_uv.y + duv, _Height_LOD, 0)); #endif #if HEIGHT_AA_LEVEL >= 2 float z5 = tex2Dlod(_Height, float4(traveling_uv.x + duv, traveling_uv.y, _Height_LOD, 0)); float z6 = tex2Dlod(_Height, float4(traveling_uv.x - duv, traveling_uv.y, _Height_LOD, 0)); float z7 = tex2Dlod(_Height, float4(traveling_uv.x, traveling_uv.y + duv, _Height_LOD, 0)); float z8 = tex2Dlod(_Height, float4(traveling_uv.x, traveling_uv.y - duv, _Height_LOD, 0)); #endif #if HEIGHT_AA_LEVEL == 0 height += z0; #elif HEIGHT_AA_LEVEL == 1 height += (z0 + z1 + z2 + z3 + z4) / 5.0; #elif HEIGHT_AA_LEVEL == 2 height += (z0 + z1 + z2 + z3 + z4 + z5 + z6 + z7 + z8) / 9.0; #endif height = pow(height, _Height_Exponent); height *= _Height_Scale; { float mask = tex2Dlod(_Height_Mask, float4(uv, _Height_LOD, 0)); mask = pow(mask, _Height_Mask_Exponent); height *= mask; } // 0 at middle, 1 or -1 at edges if (_Center_Out_Speed > 0.0) { float2 middle_out_uv = uv * 2.0 - 1.0; float center_dist2 = length2(middle_out_uv); float ring_radius = fmod(_Time[1] * _Center_Out_Speed, _Center_Out_Max_Radius - _Center_Out_Min_Radius) + _Center_Out_Min_Radius; // How far am I from the desired ring? float ring_dist = dabs(center_dist2 - ring_radius, _Center_Out_Sharpness); float ring_scale = exp(-1.0 * ring_dist); float middle_out_height = ring_scale; height *= middle_out_height; } return height; } void displace(inout v2f vert) { float height = getDisplacement(vert.uv); vert.worldPos += height * vert.normal; vert.objPos = mul(unity_WorldToObject, float4(vert.worldPos, 1.0)); vert.clipPos = UnityObjectToClipPos(vert.objPos); } float3 getNormal( v2f v0, v2f v1, v2f v2, float duv_scale) { float2 duv_v0v1 = v1.uv - v0.uv; float2 duv_v0v2 = v2.uv - v0.uv; float h0 = getDisplacement(v0.uv); float h1 = getDisplacement(v0.uv + duv_v0v1 * duv_scale); float h2 = getDisplacement(v0.uv + duv_v0v2 * duv_scale); float dh_v0v1 = h1 - h0; float dh_v0v2 = h2 - h0; float3 dpos_v0v1 = v1.worldPos - v0.worldPos; float3 dpos_v0v2 = v2.worldPos - v0.worldPos; float3 p0 = v0.worldPos + h0 * v0.normal; float3 p1 = v0.worldPos + dpos_v0v1 * duv_scale + lerp(h0, h1, duv_scale) * v1.normal; float3 p2 = v0.worldPos + dpos_v0v2 * duv_scale + lerp(h0, h2, duv_scale) * v2.normal; float3 tangent = normalize(p1 - p0); float3 bitangent = normalize(p2 - p0); float3 new_normal = normalize(cross(tangent, bitangent)); return new_normal; } // maxvertexcount == the number of vertices we create [maxvertexcount(15)] void geom(triangle v2f tri_in[3], uint pid: SV_PrimitiveID, inout TriangleStream tri_out) { float dx = 0.5; v2f t0 = tri_in[0]; v2f t1 = tri_in[1]; v2f t2 = tri_in[2]; displace(t0); displace(t1); displace(t2); // Math from here: // http://tonfilm.blogspot.com/2007/01/calculate-normals-in-shader.html if (!_Disable_Normal_Recalc) { for (uint i = 0; i < 3; i++) { v2f v0 = tri_in[(i + 0) % 3]; v2f v1 = tri_in[(i + 1) % 3]; v2f v2 = tri_in[(i + 2) % 3]; float3 aa_normal = 0; aa_normal += getNormal(v0, v1, v2, 0.3); aa_normal += getNormal(v0, v1, v2, 0.4); aa_normal += getNormal(v0, v1, v2, 0.5); aa_normal += getNormal(v0, v1, v2, 0.6); aa_normal += getNormal(v0, v1, v2, 0.7); aa_normal = normalize(aa_normal); if (i == 0) { t0.normal = aa_normal; } else if (i == 1) { t1.normal = aa_normal; } else if (i == 2) { t2.normal = aa_normal; } } } tri_out.Append(t0); tri_out.Append(t1); tri_out.Append(t2); tri_out.RestartStrip(); float4x4 object_rotate = float4x4( unity_ObjectToWorld[0].xyz, 0, 0, 1, 0, 0, unity_ObjectToWorld[2].xyz, 0, 0, 0, 0, 1 ); float3 object_offset = transpose(unity_ObjectToWorld)[3].xyz; const int num_clones = max((int) floor(_Num_Clones), 0); for (int i = 2; i < num_clones + 2; i++) { v2f t0p = t0; v2f t1p = t1; v2f t2p = t2; float3 dpos = 0; if (_Clone_Enable_Av_Height_Fix) { dpos.y += _Clone_Av_Height_Fix * pow(_Clone_Scale, i / 2) - object_offset.y; } dpos.x += (_Clone_dx + _Clone_dx_cents * 0.01) * (i / 2) * (i % 2 == 0 ? 1.0 : -1.0); dpos.y += _Clone_dy * (i / 2); dpos.z += _Clone_dz * (i / 2); if (_Clone_Enable_Rotation) { dpos = mul(object_rotate, float4(dpos, 1.0)); } t0p.worldPos += dpos; t1p.worldPos += dpos; t2p.worldPos += dpos; #if 1 t0p.worldPos = (t0p.worldPos - (dpos + object_offset)) * pow(_Clone_Scale, i / 2) + (dpos + object_offset); t1p.worldPos = (t1p.worldPos - (dpos + object_offset)) * pow(_Clone_Scale, i / 2) + (dpos + object_offset); t2p.worldPos = (t2p.worldPos - (dpos + object_offset)) * pow(_Clone_Scale, i / 2) + (dpos + object_offset); #endif t0p.objPos = mul(unity_WorldToObject, float4(t0p.worldPos, 1.0)); t0p.clipPos = UnityObjectToClipPos(t0p.objPos); t1p.objPos = mul(unity_WorldToObject, float4(t1p.worldPos, 1.0)); t1p.clipPos = UnityObjectToClipPos(t1p.objPos); t2p.objPos = mul(unity_WorldToObject, float4(t2p.worldPos, 1.0)); t2p.clipPos = UnityObjectToClipPos(t2p.objPos); tri_out.Append(t0p); tri_out.Append(t1p); tri_out.Append(t2p); tri_out.RestartStrip(); } } float getWorldSpaceDepth(in const float3 worldPos) { float4 clip_pos = mul(UNITY_MATRIX_VP, float4(worldPos, 1.0)); return clip_pos.z / clip_pos.w; } float4 effect(inout v2f i, out float depth) { depth = -1000.0; float4 albedo = tex2D(_BaseColor, i.uv); float3 emission = tex2D(_Emission, i.uv); float3 normal = _Disable_Normal_Texture ? i.normal : tex2D(_Normal, i.uv); float metallic = tex2D(_Metallic, i.uv); float roughness = tex2D(_Roughness, i.uv); if (albedo.a > 0) { depth = getWorldSpaceDepth(i.worldPos); } float4 lit_color = getLitColor(i, albedo, i.worldPos, normal, metallic, 1.0 - roughness, /*custom_cubemap=*/true); lit_color.rgb += emission * _Emission_Strength; return lit_color; } fixed4 frag(v2f i, out float depth : SV_DepthLessEqual) : SV_Target { return effect(i, depth); } #endif // AVATAR_CLONE_LIGHTING