three
Version:
JavaScript 3D library
69 lines (49 loc) • 1.82 kB
JavaScript
export default /* glsl */`
/**
* See: https://casual-effects.com/research/Wyman2017Hashed/index.html
*/
const float ALPHA_HASH_SCALE = 0.05; // Derived from trials only, and may be changed.
float hash2D( vec2 value ) {
return fract( 1.0e4 * sin( 17.0 * value.x + 0.1 * value.y ) * ( 0.1 + abs( sin( 13.0 * value.y + value.x ) ) ) );
}
float hash3D( vec3 value ) {
return hash2D( vec2( hash2D( value.xy ), value.z ) );
}
float getAlphaHashThreshold( vec3 position ) {
// Find the discretized derivatives of our coordinates
float maxDeriv = max(
length( dFdx( position.xyz ) ),
length( dFdy( position.xyz ) )
);
float pixScale = 1.0 / ( ALPHA_HASH_SCALE * maxDeriv );
// Find two nearest log-discretized noise scales
vec2 pixScales = vec2(
exp2( floor( log2( pixScale ) ) ),
exp2( ceil( log2( pixScale ) ) )
);
// Compute alpha thresholds at our two noise scales
vec2 alpha = vec2(
hash3D( floor( pixScales.x * position.xyz ) ),
hash3D( floor( pixScales.y * position.xyz ) )
);
// Factor to interpolate lerp with
float lerpFactor = fract( log2( pixScale ) );
// Interpolate alpha threshold from noise at two scales
float x = ( 1.0 - lerpFactor ) * alpha.x + lerpFactor * alpha.y;
// Pass into CDF to compute uniformly distrib threshold
float a = min( lerpFactor, 1.0 - lerpFactor );
vec3 cases = vec3(
x * x / ( 2.0 * a * ( 1.0 - a ) ),
( x - 0.5 * a ) / ( 1.0 - a ),
1.0 - ( ( 1.0 - x ) * ( 1.0 - x ) / ( 2.0 * a * ( 1.0 - a ) ) )
);
// Find our final, uniformly distributed alpha threshold (ατ)
float threshold = ( x < ( 1.0 - a ) )
? ( ( x < a ) ? cases.x : cases.y )
: cases.z;
// Avoids ατ == 0. Could also do ατ =1-ατ
return clamp( threshold , 1.0e-6, 1.0 );
}
`;