Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: Fix specification clarity of smoothstep #1851

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 21 additions & 20 deletions src/doc/languagespec.tex
Original file line number Diff line number Diff line change
Expand Up @@ -3488,42 +3488,43 @@ \section{Pattern generation}
performed component-by-component (separately for $x$, $y$, and $z$).
\apiend

\apiitem{float {\ce linearstep} (float edge0, float edge1, float x) \\
\emph{type} {\ce linearstep} (\emph{type} edge0, \emph{type} edge1, \emph{type} x)}
\apiitem{float {\ce linearstep} (float low, float high, float x) \\
\emph{type} {\ce linearstep} (\emph{type} low, \emph{type} high, \emph{type} x)}
\indexapi{linearstep()}
Returns 0 if $x \le {\mathit edge0}$, and 1 if $x \ge {\mathit edge1}$,
Returns 0 if $x \le {\mathit low}$, and 1 if $x \ge {\mathit high}$,
and performs a linear
interpolation between 0 and 1 when ${\mathit edge0} < x < {\mathit edge1}$.
This is equivalent to {\cf step(edge0, x)} when {\cf edge0 == edge1}.
interpolation between 0 and 1 when ${\mathit low} < x < {\mathit high}$.
This is equivalent to {\cf step(low, x)} when {\cf low == high}.
For \color and \point-like types, the computations are
performed component-by-component (separately for $x$, $y$, and $z$).
\apiend

\apiitem{float {\ce smoothstep} (float edge0, float edge1, float x) \\
\emph{type} {\ce smoothstep} (\emph{type} edge0, \emph{type} edge1, \emph{type} x)}
\apiitem{float {\ce smoothstep} (float low, float high, float x) \\
\emph{type} {\ce smoothstep} (\emph{type} low, \emph{type} high, \emph{type} x)}
\indexapi{smoothstep()}
Returns 0 if $x \le {\mathit edge0}$, and 1 if $x \ge {\mathit edge1}$,
Returns 0 if $x \le {\mathit low}$, and 1 if $x \ge {\mathit high}$,
and performs a smooth Hermite
interpolation between 0 and 1 when ${\mathit edge0} < x < {\mathit edge1}$.
interpolation between 0 and 1 when ${\mathit low} < x < {\mathit high}$.
This is useful in cases where you would want a thresholding function
with a smooth transition.
with a smooth transition. In the degenerate case where ${\mathit high} \le
{\mathit low}$, the return value will be 0 if $x < {\mathit high}$ and 1 if $x
\ge {\mathit high}$, making the behavior identical to `step(high, x)`.

The \emph{type} may be any of of \float, \color, \point, \vector, or
\normal. For \color and \point-like types, the computations are
performed component-by-component.
\apiend

\apiitem{float {\ce smooth_linearstep} (float edge0, float edge1, float x, float eps) \\
\emph{type} {\ce smooth_linearstep} (\emph{type} edge0, \emph{type} edge1, \emph{type} x, \emph{type} eps)}
\apiitem{float {\ce smooth_linearstep} (float low, float high, float x, float eps) \\
\emph{type} {\ce smooth_linearstep} (\emph{type} low, \emph{type} high, \emph{type} x, \emph{type} eps)}
\indexapi{smooth_linearstep()}
This function is strictly linear between ${\mathit edge0}+{\mathit eps}$ and ${\mathit edge1}-{\mathit eps}$
but smoothly ramps to 0 between ${\mathit edge0}-{\mathit eps}$ and ${\mathit edge0}+{\mathit eps}$
and smoothly ramps to 1 between ${\mathit edge1}-{\mathit eps}$ and ${\mathit edge1}+{\mathit eps}$.
It is 0 when $x \le {\mathit edge0}-{\mathit eps}$, and 1 if $x \ge {\mathit edge1}+{\mathit eps}$,
and performs a linear
interpolation between 0 and 1 when ${\mathit edge0} < x < {\mathit edge1}$.
For \color and \point-like types, the computations are
performed component-by-component.
This function returns 0 if $x < {\mathit low}-{\mathit eps}$, and 1 if
$x \ge {\mathit high}+{\mathit eps}$, is strictly linear between
${\mathit low}+{\mathit eps}$ and ${\mathit high}-{\mathit eps}$ but smoothly
ramps to 0 between ${\mathit low}-{\mathit eps}$ and ${\mathit low}+{\mathit eps}$
and smoothly ramps to 1 between ${\mathit high}-{\mathit eps}$ and
${\mathit high}+{\mathit eps}$. For \color and \point-like types, the
computations are performed component-by-component.
\apiend


Expand Down
34 changes: 17 additions & 17 deletions src/doc/stdlib.md
Original file line number Diff line number Diff line change
Expand Up @@ -458,36 +458,36 @@ the computations are performed component-by-component (separately for `x`,
performed component-by-component (separately for $x$, $y$, and $z$).


`float` **`linearstep`** `(float edge0, float edge1, float x)` <br> *`type`* **`linearstep`** (*`type`* `edge0`, *`type`* `edge1`, *`type`* `x`)
`float` **`linearstep`** `(float low, float high, float x)` <br> *`type`* **`linearstep`** (*`type`* `low`, *`type`* `high`, *`type`* `x`)

: Returns 0 if `x` $\le$ `edge0`, and 1 if `x` $\ge$ `edge1`, and performs a
linear interpolation between 0 and 1 when `edge0` $<$ `x` $<$ `edge1`.
This is equivalent to `step(edge0, x)` when `edge0 == edge1`. For `color`
: Returns 0 if `x` $<$ `low`, and 1 if `x` $\ge$ `high`, and performs a
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be x $\le$ low to match the .tex

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still feel the need to point out that any phasing of the form " returns 0 if x something low, and 1 if x something high" is going to remain ambiguous, because these are 2 separate checks against 2 separate inputs(!) which necessarily makes it possible for both conditions to be true - regardless of what comparison is used - at which point the phrasing is not specifying what should happen (which of the 2 satisfied conditions should determine the output).

linear interpolation between 0 and 1 when `low` $<$ `x` $<$ `high`.
This is equivalent to `step(low, x)` when `low == high`. For `color`
and `point`-like types, the computations are performed
component-by-component (separately for $x$, $y$, and $z$).


`float` **`smoothstep`** `(float edge0, float edge1, float x)` <br> *`type`* **`smoothstep`** (*`type`* `edge0`, *`type`* `edge1`, *`type`* `x`)
`float` **`smoothstep`** `(float low, float high, float x)` <br> *`type`* **`smoothstep`** (*`type`* `low`, *`type`* `high`, *`type`* `x`)

: Returns 0 if `x` $\le$ `edge0`, and 1 if `x` $\ge$ `edge1`, and performs a
smooth Hermite interpolation between 0 and 1 when `edge0` $<$ `x` $<$
`edge1`. This is useful in cases where you would want a thresholding
function with a smooth transition.
: Returns 0 if `x` $\le$ `low`, and 1 if `x` $\ge$ `high`, and performs a
smooth Hermite interpolation between 0 and 1 when `low` $<$ `x` $<$
`high`. This is useful in cases where you would want a thresholding
function with a smooth transition. In the degenerate case where
`high` $\le$ `low`, the return value will be 0 if `x` $<$ `high` and 1 if
`x` $\ge$ `high`, making the behavior identical to `step(high, x)`.

The *`type`* may be any of of `float`, `color`, `point`, `vector`, or
`normal`. For `color` and `point`-like types, the computations are
performed component-by-component.


`float` **`smooth_linearstep`** `(float edge0, float edge1, float x, float eps)` <br> *`type`* **`smooth_linearstep`** (*`type`* `edge0`, *`type`* `edge1`, *`type`* `x`, *`type`* eps)
`float` **`smooth_linearstep`** `(float low, float high, float x, float eps)` <br> *`type`* **`smooth_linearstep`** (*`type`* `low`, *`type`* `high`, *`type`* `x`, *`type`* eps)

: This function is strictly linear between `edge0 + eps` and `edge1 - eps`
but smoothly ramps to 0 between `edge0 - eps` and `edge0 + eps`
and smoothly ramps to 1 between `edge1 - eps` and `edge1 + eps`.
It is 0 when `x` $\le$ `edge0-eps,` and 1 if `x` $\ge$ `edge1 + eps`,
and performs a linear interpolation between 0 and 1 when
`edge0` < x < `edge1`. For `color` and `point`-like types, the
computations are performed component-by-component.
: This function returns 0 if `x` $<$ `low-eps`, and 1 if `x` $\ge$ `high + eps`,
is strictly linear between `low + eps` and `high - eps`, but smoothly
ramps to 0 between `low - eps` and `low + eps` and smoothly ramps to 1
between `high - eps` and `high + eps`. For `color` and `point`-like types,
the computations are performed component-by-component.


%## Noise functions
Expand Down
32 changes: 17 additions & 15 deletions src/include/OSL/dual.h
Original file line number Diff line number Diff line change
Expand Up @@ -1311,27 +1311,29 @@ safe_fmod (const Dual<T,P>& a, const Dual<T,P>& b)



OSL_HOSTDEVICE OSL_FORCEINLINE float smoothstep(float e0, float e1, float x) {
if (x < e0) return 0.0f;
else if (x >= e1) return 1.0f;
else {
float t = (x - e0)/(e1 - e0);
OSL_HOSTDEVICE OSL_FORCEINLINE float smoothstep(float low, float high, float x) {
if (x >= high) {
return 1.0f;
} else if (x <= low) {
return 0.0f;
} else {
float t = (x - low) / (high - low);
return (3.0f-2.0f*t)*(t*t);
}
}

// f(t) = (3-2t)t^2, t = (x-e0)/(e1-e0)
// f(t) = (3-2t)t^2, t = (x-low)/(high-low)
template<class T, int P>
OSL_HOSTDEVICE OSL_FORCEINLINE Dual<T,P> smoothstep (const Dual<T,P> &e0, const Dual<T,P> &e1, const Dual<T,P> &x)
OSL_HOSTDEVICE OSL_FORCEINLINE Dual<T,P> smoothstep (const Dual<T,P> &low, const Dual<T,P> &high, const Dual<T,P> &x)
{
if (x.val() < e0.val()) {
return Dual<T,P> (T(0));
}
else if (x.val() >= e1.val()) {
return Dual<T,P> (T(1));
}
Dual<T,P> t = (x - e0)/(e1-e0);
return (T(3) - T(2)*t)*t*t;
if (x.val() >= high.val()) {
return Dual<T,P>(T(1));
} else if (x.val() <= low.val()) {
return Dual<T,P>(T(0));
} else {
Dual<T,P> t = (x - low) / (high - low);
return (T(3) - T(2)*t)*t*t;
}
}


Expand Down
70 changes: 35 additions & 35 deletions src/shaders/stdosl.h
Original file line number Diff line number Diff line change
Expand Up @@ -319,73 +319,73 @@ point step (point edge, point x) BUILTIN;
vector step (vector edge, vector x) BUILTIN;
normal step (normal edge, normal x) BUILTIN;
float step (float edge, float x) BUILTIN;
float smoothstep (float edge0, float edge1, float x) BUILTIN;
float smoothstep (float low, float high, float x) BUILTIN;

color smoothstep (color edge0, color edge1, color x)
color smoothstep (color low, color high, color x)
{
return color (smoothstep(edge0[0], edge1[0], x[0]),
smoothstep(edge0[1], edge1[1], x[1]),
smoothstep(edge0[2], edge1[2], x[2]));
return color (smoothstep(low[0], high[0], x[0]),
smoothstep(low[1], high[1], x[1]),
smoothstep(low[2], high[2], x[2]));
}
vector smoothstep (vector edge0, vector edge1, vector x)
vector smoothstep (vector low, vector high, vector x)
{
return vector (smoothstep(edge0[0], edge1[0], x[0]),
smoothstep(edge0[1], edge1[1], x[1]),
smoothstep(edge0[2], edge1[2], x[2]));
return vector (smoothstep(low[0], high[0], x[0]),
smoothstep(low[1], high[1], x[1]),
smoothstep(low[2], high[2], x[2]));
}

float linearstep (float edge0, float edge1, float x) {
float linearstep (float low, float high, float x) {
float result;
if (edge0 != edge1) {
float xclamped = clamp (x, edge0, edge1);
result = (xclamped - edge0) / (edge1 - edge0);
if (low != high) {
float xclamped = clamp (x, low, high);
result = (xclamped - low) / (high - low);
} else { // special case: edges coincide
result = step (edge0, x);
result = step (low, x);
}
return result;
}
color linearstep (color edge0, color edge1, color x)
color linearstep (color low, color high, color x)
{
return color (linearstep(edge0[0], edge1[0], x[0]),
linearstep(edge0[1], edge1[1], x[1]),
linearstep(edge0[2], edge1[2], x[2]));
return color (linearstep(low[0], high[0], x[0]),
linearstep(low[1], high[1], x[1]),
linearstep(low[2], high[2], x[2]));
}
vector linearstep (vector edge0, vector edge1, vector x)
vector linearstep (vector low, vector high, vector x)
{
return vector (linearstep(edge0[0], edge1[0], x[0]),
linearstep(edge0[1], edge1[1], x[1]),
linearstep(edge0[2], edge1[2], x[2]));
return vector (linearstep(low[0], high[0], x[0]),
linearstep(low[1], high[1], x[1]),
linearstep(low[2], high[2], x[2]));
}

float smooth_linearstep (float edge0, float edge1, float x_, float eps_) {
float smooth_linearstep (float low, float high, float x_, float eps_) {
float result;
if (edge0 != edge1) {
if (low != high) {
float rampup (float x, float r) { return 0.5/r * x*x; }
float width_inv = 1.0 / (edge1 - edge0);
float width_inv = 1.0 / (high - low);
float eps = eps_ * width_inv;
float x = (x_ - edge0) * width_inv;
float x = (x_ - low) * width_inv;
if (x <= -eps) result = 0;
else if (x >= eps && x <= 1.0-eps) result = x;
else if (x >= 1.0+eps) result = 1;
else if (x < eps) result = rampup (x+eps, 2.0*eps);
else /* if (x < 1.0+eps) */ result = 1.0 - rampup (1.0+eps - x, 2.0*eps);
} else {
result = step (edge0, x_);
result = step (low, x_);
}
return result;
}

color smooth_linearstep (color edge0, color edge1, color x, color eps)
color smooth_linearstep (color low, color high, color x, color eps)
{
return color (smooth_linearstep(edge0[0], edge1[0], x[0], eps[0]),
smooth_linearstep(edge0[1], edge1[1], x[1], eps[1]),
smooth_linearstep(edge0[2], edge1[2], x[2], eps[2]));
return color (smooth_linearstep(low[0], high[0], x[0], eps[0]),
smooth_linearstep(low[1], high[1], x[1], eps[1]),
smooth_linearstep(low[2], high[2], x[2], eps[2]));
}
vector smooth_linearstep (vector edge0, vector edge1, vector x, vector eps)
vector smooth_linearstep (vector low, vector high, vector x, vector eps)
{
return vector (smooth_linearstep(edge0[0], edge1[0], x[0], eps[0]),
smooth_linearstep(edge0[1], edge1[1], x[1], eps[1]),
smooth_linearstep(edge0[2], edge1[2], x[2], eps[2]));
return vector (smooth_linearstep(low[0], high[0], x[0], eps[0]),
smooth_linearstep(low[1], high[1], x[1], eps[1]),
smooth_linearstep(low[2], high[2], x[2], eps[2]));
}

float aastep (float edge, float s, float dedge, float ds) {
Expand Down
Loading