#version 130 #define DEBUGGING 0 #define OUTLINE 1 #define MAX_BALLS 50 #define TARGET_INFLUENCE 0.5 #define BORDER 0.3 #define MIN_INFLUENCE (TARGET_INFLUENCE - BORDER) #define MAX_INFLUENCE (TARGET_INFLUENCE + (BORDER)) uniform vec3 v3Colours[MAX_BALLS]; uniform vec2 v2Positions[MAX_BALLS]; uniform float fMasses[MAX_BALLS]; uniform float fTotalBalls; in vec4 v4ScreenPosition; out vec4 v4FragColour; float metaball( int index ) { vec2 v2BallToScreen = v2Positions[index] - v4ScreenPosition.xy; float fBallToScreenLengthSq = dot( v2BallToScreen, v2BallToScreen ); float fRadiusSq = fMasses[index] * fMasses[index]; return pow( max( 0, 1.0 - ( fBallToScreenLengthSq / fRadiusSq ) ), 3 ); // Using this equation means the balls have a maximum range, meaning we can use bounding shapes if we want as an optimisation. //return fMasses[index] / length( v2Positions[index] - v4ScreenPosition.xy ); // The classic metaball equation. } void main() { vec4 v4Colour = vec4(0,0,0,1); float sum = 0; for ( int i = 0; i < fTotalBalls; ++i ) { float value = metaball( i ); sum += value; //value = pow( value, 5 ); v4Colour.xyz += value * v3Colours[i]; } float fadeIn = smoothstep( MIN_INFLUENCE, TARGET_INFLUENCE, sum ); #if OUTLINE float fadeOut = smoothstep( MAX_INFLUENCE, TARGET_INFLUENCE, sum ); #else float fadeOut = 1; #endif v4Colour.xyz = v4Colour.xyz / sum;//normalize( v4Colour.xyz ); v4Colour.a = pow( fadeIn * fadeOut, 32 ); #if DEBUGGING if ( v4Colour.a == 0 ) { v4Colour = vec4(0,0,0,0); } for ( int i = 0; i < fTotalBalls; ++i ) { if ( length( v2Positions[i] - v4ScreenPosition.xy ) < fMasses[i] ) { v4Colour.a += 3.0 / fTotalBalls; v4Colour.r += 3.0 / fTotalBalls; } } #endif if ( v4Colour.a == 0 ) { discard; } clamp( v4Colour.a, 0, 1 ); v4FragColour = v4Colour; }