2D SDF距离函数(一)

Number of views 155

作者:iquilezles

引言

3D SDF函数的文章相当受欢迎,因此我决定为2D基本图形写一篇类似的文章,因为大多数3D基本图形都是通过这些2D形状的拉伸或旋转生成的。所以,正确处理这些2D图形非常重要。特别地,下面的函数使用了尽可能少的平方根和除法运算,并且相比通过其他基本图形构建(在可能的情况下),能够产生更好/更快的结果。

请注意,在这里列出的所有基本图形都可以实时预览与修改。它们引用该链接上的Shadertoy示例,所以不要错过。

最后,如同本网站上的所有内容一样,所有的公式和代码都是由我自己推导得出的(除非另有说明),因此可能存在错误或更好的实现方法。如果你认为有这种情况,请告诉我。

基本图形

所有基本图形都以原点为中心;你需要变换点来获得任意旋转、平移和缩放的目标图形对象(见下文)。dot2(v)函数返回一个向量与自身的点积(或其长度的平方)。

Circle

image1739096055144.png

float sdCircle( vec2 p, float r )
{
    return length(p) - r;
}
float sdCircle( in vec2 p, in float r ) 
{
    return length(p)-r;
}


void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
	vec2 p = (2.0*fragCoord-iResolution.xy)/iResolution.y;
    vec2 m = (2.0*iMouse.xy-iResolution.xy)/iResolution.y;

	float d = sdCircle(p,0.5);
  
	// coloring
    vec3 col = (d>0.0) ? vec3(0.9,0.6,0.3) : vec3(0.65,0.85,1.0);
    col *= 1.0 - exp(-6.0*abs(d));
	col *= 0.8 + 0.2*cos(150.0*d);
	col = mix( col, vec3(1.0), 1.0-smoothstep(0.0,0.01,abs(d)) );

    if( iMouse.z>0.001 )
    {
    d = sdCircle(m,0.5);
    col = mix(col, vec3(1.0,1.0,0.0), 1.0-smoothstep(0.0, 0.005, abs(length(p-m)-abs(d))-0.0025));
    col = mix(col, vec3(1.0,1.0,0.0), 1.0-smoothstep(0.0, 0.005, length(p-m)-0.015));
    }

	fragColor = vec4(col,1.0);
}

Rounded Box

image1739096446477.png

float sdRoundedBox( in vec2 p, in vec2 b, in vec4 r )
{
    r.xy = (p.x>0.0)?r.xy : r.zw;
    r.x  = (p.y>0.0)?r.x  : r.y;
    vec2 q = abs(p)-b+r.x;
    return min(max(q.x,q.y),0.0) + length(max(q,0.0)) - r.x;
}
// b.x = half width
// b.y = half height
// r.x = roundness top-right  
// r.y = roundness boottom-right
// r.z = roundness top-left
// r.w = roundness bottom-left
float sdRoundBox( in vec2 p, in vec2 b, in vec4 r ) 
{
    r.xy = (p.x>0.0)?r.xy : r.zw;
    r.x  = (p.y>0.0)?r.x  : r.y;
    vec2 q = abs(p)-b+r.x;
    return min(max(q.x,q.y),0.0) + length(max(q,0.0)) - r.x;
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
	vec2 p = (2.0*fragCoord-iResolution.xy)/iResolution.y;
    vec2 m = (2.0*iMouse.xy-iResolution.xy)/iResolution.y;

	vec2 si = vec2(0.9,0.6) + 0.3*cos(iTime+vec2(0,2));
    vec4 ra = 0.3 + 0.3*cos( 2.0*iTime + vec4(0,1,2,3) );
    ra = min(ra,min(si.x,si.y));

	float d = sdRoundBox( p, si, ra );

    vec3 col = (d>0.0) ? vec3(0.9,0.6,0.3) : vec3(0.65,0.85,1.0);
	col *= 1.0 - exp(-6.0*abs(d));
	col *= 0.8 + 0.2*cos(150.0*d);
	col = mix( col, vec3(1.0), 1.0-smoothstep(0.0,0.01,abs(d)) );

    if( iMouse.z>0.001 )
    {
    d = sdRoundBox(m, si, ra );
    col = mix(col, vec3(1.0,1.0,0.0), 1.0-smoothstep(0.0, 0.005, abs(length(p-m)-abs(d))-0.0025));
    col = mix(col, vec3(1.0,1.0,0.0), 1.0-smoothstep(0.0, 0.005, length(p-m)-0.015));
    }

	fragColor = vec4(col,1.0);
}

Box

image1739096602230.png

float sdBox( in vec2 p, in vec2 b )
{
    vec2 d = abs(p)-b;
    return length(max(d,0.0)) + min(max(d.x,d.y),0.0);
}

Oriented Box

image1739096787529.png

float sdOrientedBox( in vec2 p, in vec2 a, in vec2 b, float th )
{
    float l = length(b-a);
    vec2  d = (b-a)/l;
    vec2  q = (p-(a+b)*0.5);
          q = mat2(d.x,-d.y,d.y,d.x)*q;
          q = abs(q)-vec2(l,th)*0.5;
    return length(max(q,0.0)) + min(max(q.x,q.y),0.0);  
}
float sdOrientedBox( in vec2 p, in vec2 a, in vec2 b, float th )
{
    float l = length(b-a);
    vec2  d = (b-a)/l;
    vec2  q = p-(a+b)*0.5;
          q = mat2(d.x,-d.y,d.y,d.x)*q;
          q = abs(q)-vec2(l*0.5,th);
    return length(max(q,0.0)) + min(max(q.x,q.y),0.0);  
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    // normalized pixel coordinates
    vec2 p = (2.0*fragCoord-iResolution.xy)/iResolution.y;
    vec2 m = (2.0*iMouse.xy-iResolution.xy)/iResolution.y;
    p *= 1.4;
    m *= 1.4;
  
    // animation
    vec2 v1 = cos( iTime*0.5 + vec2(0.0,1.00) + 0.0 );
	vec2 v2 = cos( iTime*0.5 + vec2(0.0,3.00) + 1.5 );
    float th = 0.3*(0.5+0.5*cos(iTime*1.1+1.0));

	float d = sdOrientedBox( p, v1, v2, th );

    // distance
    vec3 col = vec3(1.0) - sign(d)*vec3(0.1,0.4,0.7);
	col *= 1.0 - exp(-6.0*abs(d));
	col *= 0.8 + 0.2*cos(120.0*d);
	col = mix( col, vec3(1.0), 1.0-smoothstep(0.0,0.015,abs(d)) );

    if( iMouse.z>0.001 )
    {
    d = sdOrientedBox(m, v1, v2, th);
    col = mix(col, vec3(1.0,1.0,0.0), 1.0-smoothstep(0.0, 0.005, abs(length(p-m)-abs(d))-0.0025));
    col = mix(col, vec3(1.0,1.0,0.0), 1.0-smoothstep(0.0, 0.005, length(p-m)-0.015));
    }
  

	fragColor = vec4(col, 1.0);
}

Segment:

image1739096872181.png

float sdSegment( in vec2 p, in vec2 a, in vec2 b )
{
    vec2 pa = p-a, ba = b-a;
    float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 );
    return length( pa - ba*h );
}
float udSegment( in vec2 p, in vec2 a, in vec2 b )
{
    vec2 ba = b-a;
    vec2 pa = p-a;
    float h =clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 );
    return length(pa-h*ba);
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
	vec2 p = (2.0*fragCoord-iResolution.xy)/iResolution.y;
    vec2 m = (2.0*iMouse.xy-iResolution.xy)/iResolution.y;
    p *= 1.4;
    m *= 1.4;
  
    vec2 v1 = cos( iTime*0.5 + vec2(0.0,1.00) + 0.0 );
	vec2 v2 = cos( iTime*0.5 + vec2(0.0,3.00) + 1.5 );
    float th = 0.3*(0.5+0.5*cos(iTime*1.1+1.0));
  
	float d = udSegment( p, v1, v2 ) - th;
  
    vec3 col = vec3(1.0) - sign(d)*vec3(0.1,0.4,0.7);
	col *= 1.0 - exp(-3.0*abs(d));
	col *= 0.8 + 0.2*cos(120.0*d);
	col = mix( col, vec3(1.0), 1.0-smoothstep(0.0,0.015,abs(d)) );

    if( iMouse.z>0.001 )
    {
    d = udSegment(m, v1, v2) - th;
    col = mix(col, vec3(1.0,1.0,0.0), 1.0-smoothstep(0.0, 0.005, abs(length(p-m)-abs(d))-0.0025));
    col = mix(col, vec3(1.0,1.0,0.0), 1.0-smoothstep(0.0, 0.005, length(p-m)-0.015));
    }
  
	fragColor = vec4(col,1.0);
}

Rhombus:

image1739096996784.png

float ndot(vec2 a, vec2 b ) { return a.x*b.x - a.y*b.y; }
float sdRhombus( in vec2 p, in vec2 b ) 
{
    p = abs(p);
    float h = clamp( ndot(b-2.0*p,b)/dot(b,b), -1.0, 1.0 );
    float d = length( p-0.5*b*vec2(1.0-h,1.0+h) );
    return d * sign( p.x*b.y + p.y*b.x - b.x*b.y );
}
float ndot(vec2 a, vec2 b ) { return a.x*b.x - a.y*b.y; }

float sdRhombus( in vec2 p, in vec2 b ) 
{
    p = abs(p);

    float h = clamp( ndot(b-2.0*p,b)/dot(b,b), -1.0, 1.0 );
    float d = length( p-0.5*b*vec2(1.0-h,1.0+h) );

	return d * sign( p.x*b.y + p.y*b.x - b.x*b.y );
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
	vec2 p = (2.0*fragCoord-iResolution.xy)/iResolution.y;
    vec2 m = (2.0*iMouse.xy-iResolution.xy)/iResolution.y;

	vec2 ra = 0.4 + 0.3*cos( iTime + vec2(0.0,1.57) + 0.0 );

	float d = sdRhombus( p, ra );

    vec3 col = vec3(1.0) - sign(d)*vec3(0.1,0.4,0.7);
	col *= 1.0 - exp(-2.0*abs(d));
	col *= 0.8 + 0.2*cos(140.0*d);
	col = mix( col, vec3(1.0), 1.0-smoothstep(0.0,0.02,abs(d)) );

    if( iMouse.z>0.001 )
    {
    d = sdRhombus(m, ra);
    col = mix(col, vec3(1.0,1.0,0.0), 1.0-smoothstep(0.0, 0.005, abs(length(p-m)-abs(d))-0.0025));
    col = mix(col, vec3(1.0,1.0,0.0), 1.0-smoothstep(0.0, 0.005, length(p-m)-0.015));
    }

	fragColor = vec4(col,1.0);
}

Isosceles Trapezoid

image1739097171952.png

float sdTrapezoid( in vec2 p, in float r1, float r2, float he )
{
    vec2 k1 = vec2(r2,he);
    vec2 k2 = vec2(r2-r1,2.0*he);
    p.x = abs(p.x);
    vec2 ca = vec2(p.x-min(p.x,(p.y<0.0)?r1:r2), abs(p.y)-he);
    vec2 cb = p - k1 + k2*clamp( dot(k1-p,k2)/dot2(k2), 0.0, 1.0 );
    float s = (cb.x<0.0 && ca.y<0.0) ? -1.0 : 1.0;
    return s*sqrt( min(dot2(ca),dot2(cb)) );
}
float dot2(in vec2 v ) { return dot(v,v); }

// trapezoid / capped cone, specialized for Y alignment
float sdTrapezoid( in vec2 p, in float r1, float r2, float he )
{
    vec2 k1 = vec2(r2,he);
    vec2 k2 = vec2(r2-r1,2.0*he);

	p.x = abs(p.x);
    vec2 ca = vec2(max(0.0,p.x-((p.y<0.0)?r1:r2)), abs(p.y)-he);
    vec2 cb = p - k1 + k2*clamp( dot(k1-p,k2)/dot2(k2), 0.0, 1.0 );
  
    float s = (cb.x < 0.0 && ca.y < 0.0) ? -1.0 : 1.0;
  
    return s*sqrt( min(dot2(ca),dot2(cb)) );
}

// trapezoid / capped cone
float sdTrapezoid( in vec2 p, in vec2 a, in vec2 b, in float ra, float rb )
{
    float rba  = rb-ra;
    float baba = dot(b-a,b-a);
    float papa = dot(p-a,p-a);
    float paba = dot(p-a,b-a)/baba;
    float x = sqrt( papa - paba*paba*baba );
    float cax = max(0.0,x-((paba<0.5)?ra:rb));
    float cay = abs(paba-0.5)-0.5;
    float k = rba*rba + baba;
    float f = clamp( (rba*(x-ra)+paba*baba)/k, 0.0, 1.0 );
    float cbx = x-ra - f*rba;
    float cby = paba - f;
    float s = (cbx < 0.0 && cay < 0.0) ? -1.0 : 1.0;
    return s*sqrt( min(cax*cax + cay*cay*baba,
                       cbx*cbx + cby*cby*baba) );
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
	vec2 p = (2.0*fragCoord-iResolution.xy)/iResolution.y;
    vec2 m = (2.0*iMouse.xy-iResolution.xy)/iResolution.y;
  
    float ra = 0.2+0.15*sin(iTime*1.3+0.0);
    float rb = 0.2+0.15*sin(iTime*1.4+1.1);
    vec2  pa = vec2(-0.6,0.0)+0.4*sin(iTime*1.1+vec2(0.0,2.0));
    vec2  pb = vec2(-0.6,0.0)+0.4*sin(iTime*1.2+vec2(1.0,2.5));
    vec2  pc = vec2(0.8,0.0);

    // axis aligned trapezoid
	float d = sdTrapezoid( p-pc, ra, rb, 0.5+0.2*sin(1.3*iTime) );
    // aribitrary trapezoid
    d = min( d, sdTrapezoid( p, pa, pb , ra, rb ) );

  
    vec3 col = vec3(1.0) - sign(d)*vec3(0.1,0.4,0.7);
	col *= 1.0 - exp(-4.0*abs(d));
	col *= 0.8 + 0.2*cos(140.0*d);
	col = mix( col, vec3(1.0), 1.0-smoothstep(0.0,0.015,abs(d)) );
    col = mix( col, vec3(1.0), 1.0-smoothstep(0.0,0.005,abs(d)) );

    if( iMouse.z>0.001 )
    {
    d = sdTrapezoid( m, pa, pb , ra, rb );
    d = min( d, sdTrapezoid( m-pc, ra, rb, 0.5+0.2*sin(1.3*iTime) ) );
    col = mix(col, vec3(1.0,1.0,0.0), 1.0-smoothstep(0.0, 0.005, abs(length(p-m)-abs(d))-0.0025));
    col = mix(col, vec3(1.0,1.0,0.0), 1.0-smoothstep(0.0, 0.005, length(p-m)-0.015));
    }
  
	fragColor = vec4(col,1.0);
}

Parallelogram

image1739097294397.png

float sdParallelogram( in vec2 p, float wi, float he, float sk )
{
    vec2 e = vec2(sk,he);
    p = (p.y<0.0)?-p:p;
    vec2  w = p - e; w.x -= clamp(w.x,-wi,wi);
    vec2  d = vec2(dot(w,w), -w.y);
    float s = p.x*e.y - p.y*e.x;
    p = (s<0.0)?-p:p;
    vec2  v = p - vec2(wi,0); v -= e*clamp(dot(v,e)/dot(e,e),-1.0,1.0);
    d = min( d, vec2(dot(v,v), wi*he-abs(s)));
    return sqrt(d.x)*sign(-d.y);
}
// Distance to a parallelogram, I implemented three methods:
// Method 1: computed by two edges, by symmetry, single square root
// Method 2: computed by interior/exterior, optimization of Pentan's idea
// Method 3: computed by zones

#define METHOD 1

// List of some other 2D distances: https://www.shadertoy.com/playlist/MXdSRf
//
// and iquilezles.org/articles/distfunctions2d



// signed distance to a 2D parallelogram (width, height, skew)
float sdParallelogram_1( in vec2 p, float wi, float he, float sk )
{
    vec2  e  = vec2(sk,he);
    float e2 = sk*sk + he*he;

    p = (p.y<0.0)?-p:p;
    // horizontal edge
    vec2  w = p - e; w.x -= clamp(w.x,-wi,wi);
    vec2  d = vec2(dot(w,w), -w.y);
    // vertical edge
    float s = p.x*e.y - p.y*e.x;
    p = (s<0.0)?-p:p;
    vec2  v = p - vec2(wi,0); v -= e*clamp(dot(v,e)/e2,-1.0,1.0);
    d = min( d, vec2(dot(v,v), wi*he-abs(s)));
    return sqrt(d.x)*sign(-d.y);
}

float sdParallelogram_2( in vec2 p, float wi, float he, float sk )
{
    vec2  e  = vec2(sk,he);
    float e2 = sk*sk + he*he;

    float da = abs(p.x*e.y-p.y*e.x)-wi*he;
    float db = abs(p.y)-e.y;
    if( max(da,db)<0.0 ) // interior
    {
        return max( da*inversesqrt(e2), db );
    }
    else                 // exterior
    {
       float f = clamp(p.y/e.y,-1.0,1.0);
       float g = clamp(p.x-e.x*f, -wi, wi);
       float h = clamp(((p.x-g)*e.x+p.y*e.y)/e2,-1.0,1.0);
       return length(p-vec2(g+e.x*h,e.y*h));
    }
}

float sdParallelogram_3( in vec2 p, float wi, float he, float sk )
{
    // above
    float db = abs(p.y)-he;
    if( db>0.0 && abs(p.x-sk*sign(p.y))<wi )
        return db;
    
    // inside
    float e2 = sk*sk + he*he;
    float h  = p.x*he - p.y*sk;
    float da = (abs(h)-wi*he)*inversesqrt(e2);
    if( da<0.0 && db<0.0 )
        return max( da, db );

    // sides
    vec2 q = (h<0.0)?-p:p; q.x -= wi;
    float v = abs(q.x*sk+q.y*he);
    if( v<e2 )
        return da;
  
    // exterior
    return sqrt( dot(q,q)+e2-2.0*v );
}

float sdParallelogram( in vec2 p, float wi, float he, float sk )
{
    #if METHOD==1
    return sdParallelogram_1(p,wi,he,sk);
    #endif
    #if METHOD==2
    return sdParallelogram_2(p,wi,he,sk);
    #endif
    #if METHOD==3
    return sdParallelogram_3(p,wi,he,sk);
    #endif
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
	vec2 p = (2.0*fragCoord-iResolution.xy)/iResolution.y;
    vec2 m = (2.0*iMouse.xy-iResolution.xy)/iResolution.y;

    // animate
    float sk = 0.5*sin(iTime);
    float ra = 0.0;//0.1 + 0.1*sin(1.3*iTime);
    
    //p.x -= sk; // enable to lock base in place

    // distance
	float d = sdParallelogram(p,0.4,0.6,sk)-ra;

    // colorize
    vec3 col = (d>0.0) ? vec3(0.9,0.6,0.3) : vec3(0.65,0.85,1.0);
	col *= 1.0 - exp(-6.0*abs(d));
	col *= 0.8 + 0.2*cos(120.0*d);
	col = mix( col, vec3(1.0), 1.0-smoothstep(0.0,0.01,abs(d)) );

    if( iMouse.z>0.001 )
    {
    d = sdParallelogram(m,0.4,0.6,sk)-ra;
    col = mix(col, vec3(1.0,1.0,0.0), 1.0-smoothstep(0.0, 0.005, abs(length(p-m)-abs(d))-0.0025));
    col = mix(col, vec3(1.0,1.0,0.0), 1.0-smoothstep(0.0, 0.005, length(p-m)-0.015));
    }
  
    fragColor = vec4(col,1.0);
}

Equilateral Triangle

image1739097396102.png

float sdEquilateralTriangle( in vec2 p, in float r )
{
    const float k = sqrt(3.0);
    p.x = abs(p.x) - r;
    p.y = p.y + r/k;
    if( p.x+k*p.y>0.0 ) p = vec2(p.x-k*p.y,-k*p.x-p.y)/2.0;
    p.x -= clamp( p.x, -2.0*r, 0.0 );
    return -length(p)*sign(p.y);
}
// signed distance to an equilateral triangle
float sdEquilateralTriangle(  in vec2 p, in float r )
{
    const float k = sqrt(3.0);
#if 0
    // form 1
    p.x = abs(p.x) - r;
    p.y = p.y + r/k;
    if( p.x+k*p.y>0.0 ) p=vec2(p.x-k*p.y,-k*p.x-p.y)/2.0;
    p.x -= clamp( p.x, -2.0*r, 0.0 );
    return -length(p)*sign(p.y);
#else
    // form 2
    p.x = abs(p.x);
    p -= vec2(0.5,0.5*k)*max(p.x+k*p.y,0.0);
    p.x =  p.x - clamp(p.x,-r,r);
    p.y = -p.y - r*(1.0/k);
    return length(p)*sign(p.y);
#endif
}

// not an SDF
float fEquilateralTriangle(  in vec2 p, in float r )
{
    const float k = sqrt(3.0);
    return -p.y + 0.5*k*max(abs(p.x)+k*p.y,0.0) - r*(1.0/k);
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
	vec2 p = (2.0*fragCoord.xy-iResolution.xy)/iResolution.y;
    vec2 m = (2.0*iMouse.xy-iResolution.xy)/iResolution.y;
    p *= 2.0;
    m *= 2.0;
  
	float d = sdEquilateralTriangle( p, 1.0 );

    vec3 col = (d>0.0) ? vec3(0.9,0.6,0.3) : vec3(0.65,0.85,1.0);
	col *= 1.0 - exp(-4.0*abs(d));
	col *= 0.8 + 0.2*cos(60.0*d);
	col = mix( col, vec3(1.0), 1.0-smoothstep(0.0,0.02,abs(d)) );

    if( iMouse.z>0.001 )
    {
    d = sdEquilateralTriangle( m, 1.0 );
    col = mix(col, vec3(1.0,1.0,0.0), 1.0-smoothstep(0.0, 0.01, abs(length(p-m)-abs(d))-0.005));
    col = mix(col, vec3(1.0,1.0,0.0), 1.0-smoothstep(0.0, 0.01, length(p-m)-0.03));
    }
  
	fragColor = vec4(col,1.0);
}

Isosceles Triangle

image1739097544583.png

float sdTriangleIsosceles( in vec2 p, in vec2 q )
{
    p.x = abs(p.x);
    vec2 a = p - q*clamp( dot(p,q)/dot(q,q), 0.0, 1.0 );
    vec2 b = p - q*vec2( clamp( p.x/q.x, 0.0, 1.0 ), 1.0 );
    float s = -sign( q.y );
    vec2 d = min( vec2( dot(a,a), s*(p.x*q.y-p.y*q.x) ),
                  vec2( dot(b,b), s*(p.y-q.y)  ));
    return -sqrt(d.x)*sign(d.y);
}
// signed distance to a 2D triangle
float sdTriangleIsosceles( in vec2 p, in vec2 q )
{
    p.x = abs(p.x);
	vec2 a = p - q*clamp( dot(p,q)/dot(q,q), 0.0, 1.0 );
    vec2 b = p - q*vec2( clamp( p.x/q.x, 0.0, 1.0 ), 1.0 );
    float k = sign( q.y );
    float d = min(dot( a, a ),dot(b, b));
    float s = max( k*(p.x*q.y-p.y*q.x),k*(p.y-q.y)  );
	return sqrt(d)*sign(s);
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
	vec2 p = (2.0*fragCoord-iResolution.xy)/iResolution.y;
    vec2 m = (2.0*iMouse.xy-iResolution.xy)/iResolution.y;
    p *= 1.1;
    m *= 1.1;
  

	vec2 tri = vec2(0.3,-1.1); // width, height

	float d = sdTriangleIsosceles( p-vec2(0.0,0.5), tri );

    vec3 col = vec3(1.0) - sign(d)*vec3(0.1,0.4,0.7);
	col *= 1.0 - exp(-2.0*abs(d));
	col *= 0.8 + 0.2*cos(140.0*d);
	col = mix( col, vec3(1.0), 1.0-smoothstep(0.0,0.02,abs(d)) );

    if( iMouse.z>0.001 )
    {
    d = sdTriangleIsosceles(m-vec2(0.0,0.5), tri );
    col = mix(col, vec3(1.0,1.0,0.0), 1.0-smoothstep(0.0, 0.005, abs(length(p-m)-abs(d))-0.0025));
    col = mix(col, vec3(1.0,1.0,0.0), 1.0-smoothstep(0.0, 0.005, length(p-m)-0.015));
    }

	fragColor = vec4(col*1.2,1.0);
}

Triangle:

image1739097635806.png

float sdTriangle( in vec2 p, in vec2 p0, in vec2 p1, in vec2 p2 )
{
    vec2 e0 = p1-p0, e1 = p2-p1, e2 = p0-p2;
    vec2 v0 = p -p0, v1 = p -p1, v2 = p -p2;
    vec2 pq0 = v0 - e0*clamp( dot(v0,e0)/dot(e0,e0), 0.0, 1.0 );
    vec2 pq1 = v1 - e1*clamp( dot(v1,e1)/dot(e1,e1), 0.0, 1.0 );
    vec2 pq2 = v2 - e2*clamp( dot(v2,e2)/dot(e2,e2), 0.0, 1.0 );
    float s = sign( e0.x*e2.y - e0.y*e2.x );
    vec2 d = min(min(vec2(dot(pq0,pq0), s*(v0.x*e0.y-v0.y*e0.x)),
                     vec2(dot(pq1,pq1), s*(v1.x*e1.y-v1.y*e1.x))),
                     vec2(dot(pq2,pq2), s*(v2.x*e2.y-v2.y*e2.x)));
    return -sqrt(d.x)*sign(d.y);
}
// Other triangle functions:
//
// Distance:   https://www.shadertoy.com/view/XsXSz4
// Gradient:   https://www.shadertoy.com/view/tlVyWh
// Boundaries: https://www.shadertoy.com/view/tlKcDz


// signed distance to a 2D triangle
float dot2( in vec2 v ) { return dot(v,v); }
float sdTriangle( in vec2 p, in vec2 p0, in vec2 p1, in vec2 p2 )
{
	vec2 e0=p1-p0, v0=p-p0; float d0=dot2(v0-e0*clamp(dot(v0,e0)/dot(e0,e0),0.0,1.0));
	vec2 e1=p2-p1, v1=p-p1; float d1=dot2(v1-e1*clamp(dot(v1,e1)/dot(e1,e1),0.0,1.0));
	vec2 e2=p0-p2, v2=p-p2; float d2=dot2(v2-e2*clamp(dot(v2,e2)/dot(e2,e2),0.0,1.0));
  
    float o = e0.x*e2.y-e0.y*e2.x;
    vec2 d = min(min(vec2(d0,o*(v0.x*e0.y-v0.y*e0.x)),
                     vec2(d1,o*(v1.x*e1.y-v1.y*e1.x))),
                     vec2(d2,o*(v2.x*e2.y-v2.y*e2.x)));
	return -sqrt(d.x)*sign(d.y);
}

//---- everything below this line is NOT the triangle formula, and it's there just to produce the picture ---

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
	vec2 p = (2.0*fragCoord.xy-iResolution.xy)/iResolution.y;
    vec2 m = (2.0*iMouse.xy-iResolution.xy)/iResolution.y;
	p *= 1.5;
    m *= 1.5;
  
    // animate
	vec2 v1 = vec2(1.4,1.0)*cos( iTime + vec2(0.0,2.00) + 0.0 );
	vec2 v2 = vec2(1.4,1.0)*cos( iTime + vec2(0.0,1.50) + 1.5 );
	vec2 v3 = vec2(1.4,1.0)*cos( iTime + vec2(0.0,3.00) + 4.0 );

    // distance
	float d = sdTriangle( p, v1, v2, v3 );
  
    // color
#if 1
    vec3 col = vec3(1.0) - sign(d)*vec3(0.1,0.4,0.7);
	col *= 1.0 - exp(-2.0*abs(d));
	col *= 0.8 + 0.2*cos(120.0*d);
	col = mix( col, vec3(1.0), 1.0-smoothstep(0.0,0.02,abs(d)) );
#else
    vec3 col = (d<0.0) ? vec3(0.2,0.8,0.8) : vec3(0.8,0.2,0.8);
	col *= exp2(-2.0*abs(d));
    col = mix(col,vec3(1.20),exp2(-22.0*abs(d)));
	col = mix( col, vec3(0.0), 1.0-smoothstep(0.01,0.02,abs(d)) );
    col *= 1.0 + 0.5*smoothstep(-0.4,0.4,cos(100.0*d));
#endif

    // interactivity
    if( iMouse.z>0.001 )
    {
    d = sdTriangle(m, v1, v2, v3 );
    col = mix(col, vec3(1.0,1.0,0.0), 1.0-smoothstep(0.0, 0.005, abs(length(p-m)-abs(d))-0.0025));
    col = mix(col, vec3(1.0,1.0,0.0), 1.0-smoothstep(0.0, 0.005, length(p-m)-0.015));
    }

	fragColor = vec4(col,1.0);
}
0 Answers