Last active
February 21, 2023 15:51
-
-
Save williamchange/f6b2630dac9d0be5935a520e41555d17 to your computer and use it in GitHub Desktop.
Triangle Voronoi Borders HLSL UE (Custom Node)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Based on "Triangle Voronoi Borders" by Thomas Hooper (@tdhooper) | |
// https://www.shadertoy.com/view/ss3fW4 | |
struct Functions | |
{ | |
float3 sdTriEdges(float2 p) { | |
return float3( | |
dot(p, float2(0.,-1.)), | |
dot(p, float2(.866025, .5)), | |
dot(p, float2(-.866025, .5)) | |
); | |
} | |
float sdTri(float2 p) { | |
float3 t = sdTriEdges(p); | |
return max(t.x, max(t.y, t.z)); | |
} | |
float3 primaryAxis(float3 p) { | |
float3 a = abs(p); | |
return (1.-step(a.xyz, a.yzx))*step(a.zxy, a.xyz)*sign(p); | |
} | |
float3 sdgBorder(float2 pt1, float2 pt2) { | |
float3 tbRel = sdTriEdges(pt2 - pt1); | |
float3 axis = primaryAxis(tbRel); | |
float2 gA = float2(0,-1); | |
float2 gB = float2(.866025, .5); | |
float2 gC = float2(-.866025, .5); | |
float2 norA = gC * axis.x + gA * axis.y + gB * axis.z; | |
float2 norB = gB * -axis.x + gC * -axis.y + gA * -axis.z; | |
float2 dir = gA * axis.x + gB * axis.y + gC * axis.z; | |
float2 corner = dir * dot(dir, pt1 - pt2) * 2./3.; | |
float2x2 r90 = {0,-1,1,0}; | |
bool isEdge = axis.x + axis.y + axis.z < 0.; | |
if (isEdge) { | |
corner = pt2 + corner; | |
float2 ca = corner + min(0., dot(corner, -norA)) * norA; | |
float2 cb = corner + max(0., dot(corner, -norB)) * norB; | |
float side = step(dot(corner, mul(r90,dir)), 0.); | |
corner = lerp(cb, ca, side); | |
} else { | |
corner = pt1 - corner; | |
float2 ca = corner + max(0., dot(corner, -norA)) * norA; | |
float2 cb = corner + min(0., dot(corner, -norB)) * norB; | |
float side = step(dot(corner, mul(r90,dir)), 0.); | |
corner = lerp(ca, cb, side); | |
} | |
float2 nor = normalize(corner); | |
float d = length(corner); | |
return float3(abs(d), nor); | |
} | |
float2 hash2(float2 p) | |
{ | |
return frac(sin(float2(dot(p,float2(127.1,311.7)),dot(p,float2(269.5,183.3))))*43758.5453); | |
} | |
float2 cellPoint(float2 n, float2 f, float2 cell, float offset) { | |
float2 coord = n + cell; | |
float2 o = hash2( n + cell ); | |
o = 0.5 + 0.5*sin( offset * 6.2831 + 6.2831 * o ); | |
float2 pt = cell + o - f; | |
return pt; | |
} | |
float3x3 voronoi(float2 x, float offset) | |
{ | |
float2 n = floor(x); | |
float2 f = frac(x); | |
//---------------------------------- | |
// first pass: regular voronoi | |
//---------------------------------- | |
float2 closestCell, closestPoint, id, nor; | |
const int reach = 2; | |
float closestDist = 8.0; | |
for( int j = -reach; j <= reach; j++ ) | |
for( int i = -reach; i <= reach; i++ ) | |
{ | |
float2 cell = float2(i, j); | |
float2 pt = cellPoint(n, f, cell,offset); | |
float dist = sdTri(pt); | |
if( dist < closestDist ) | |
{ | |
closestDist = dist; | |
closestPoint = pt; | |
closestCell = cell; | |
id = cell + n; | |
} | |
} | |
//---------------------------------- | |
// second pass: distance to borders | |
//---------------------------------- | |
closestDist = 8.0; | |
for( int j = -reach-1; j <= reach+1; j++ ) | |
for( int i = -reach-1; i <= reach+1; i++ ) | |
{ | |
float2 cell = closestCell + float2(i, j); | |
float2 pt = cellPoint(n, f, cell,offset); | |
float dist = sdTri(closestPoint - pt); | |
if( dist > 0.0001 ) { | |
float3 sdg = sdgBorder(closestPoint, pt); | |
if (sdg.x < closestDist) { | |
closestDist = sdg.x; | |
nor = sdg.yz; | |
} | |
} | |
} | |
return float3x3( closestDist, closestPoint, id, nor, 0, 0); | |
} | |
float3x3 Out(float2 p, float offset) { return voronoi(p,offset); } | |
}; | |
Functions f; | |
float3x3 vor = f.Out(uv,offset); | |
id = vor[1].xy; | |
nor = float2(vor[1].z, vor[2].x); | |
return vor[0].xyz; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment