Is math.smoothstep broken or am I doing something wrong?

I’m twisting a mesh and want to smooth the transition between the twisted and untwisted region. So I turned to smooth step. For the life of me I couldn’t figure out how to get it to work!

lerp() worked fine before and I wasn’t sure why smoothstep() would require different inputs. Eventually I decided to try Mathf.SmoothStep() to see if the problem was on my end. Bam. Mathf.SmoothStep worked like a charm. Is the implementation in the new mathematics library different or is it just broken?

Here’s the code, if you need context.

[BurstCompile (CompileSynchronously = COMPILE_SYNCHRONOUSLY)]
private struct LimitedTwistDeformJob : IJobParallelFor
{
    public float angle;
    public float top;
    public float bottom;
    public float4x4 axisSpace;
    public float4x4 inverseAxisSpace;
    public NativeMeshData data;

    public void Execute (int index)
    {
        var range = abs (top - bottom);
        if (range < MIN_RANGE)
            top += MIN_RANGE;

        var positionOnAxis = mul (axisSpace, float4 (data.vertices[index], 1f));
        // this is the relevant line
        var rotationAngle = Mathf.SmoothStep (0f, angle, (clamp (positionOnAxis.z, bottom, top) - bottom) / range);

        positionOnAxis.xy = rotate (positionOnAxis.xy, radians (rotationAngle) + (float)PI);

        data.vertices[index] = mul (inverseAxisSpace, positionOnAxis).xyz;
    }
}

[edit 1] and yes, I can just call “smoothstep” because I have using static Unity.Mathematics.math; at the top of the file.

[edit 2] ok so I got tired of waiting and recreated the smoothstep effect in the new mathematics library. it behaves exactly the same as Mathf.SmoothStep.

[MethodImpl (MethodImplOptions.AggressiveInlining)]
public static float smoothstep (float a, float b, float t)
{
    t = saturate (t);
    t = -2f * t * t * t + 3f * t * t;
    return b * t + a * (1f - t);
}
2 Likes

Standard implementation of smoothstep (in any shader language) is:

float smoothstep(float a, float b, float x)
{
    float t = saturate((x - a)/(b - a));
    return t*t*(3.0 - (2.0*t));
}

New math lib seems to match with this. IMO the old one shouldn’t been named SmoothStep as it’s not really the same thing.

2 Likes

How am I supposed to use the shader version of smoothstep? Is it not supposed to be treated like lerp, because that’s how I’m using it but it doesn’t work.

I have been tricked before, the definition of the last argument is not the same.

Mathf : It is clamped first, the t value is expected to be between 0~1 then maps smoothly with from~to
Unity.Mathematics : It is interpolated first before the clamp, the x value is expected to be between edge values a~b and not 0~1.

Shader language’s smoothstep works like the Unity.Mathematics version.

2 Likes

Thanks for the answer. I got it working now. I just need to multiply the 0~1 value that I was using for the lerp by the max value which is ‘angle’ in this instance. Then multiply the result by angle.

if (smooth)
    rotationAngle = smoothstep (0f, angle, ((clamp (distanceFromAxis, inner, outer) - inner) / range) * angle) * angle;
else
    rotationAngle = lerp (0f, angle, (clamp (distanceFromAxis, inner, outer) - inner) / range);

You can actually simplify that to:

rotationAngle = smoothstep (0f, range, clamp (distanceFromAxis, inner, outer) - inner) * angle;

I think it helps if you don’t think smoothstep as lerp but instead a smooth version of the regular step function. It returns 0 or 1 depending if the given value bigger than the limit. smoothstep does the same thing but has smooth transition from 0 to 1.

2 Likes