Skip to main content
Phraseology
Source Link
DMGregory
  • 136.3k
  • 22
  • 247
  • 373

Alex F's solution is correct; I'd like to just explain why it's necessary in this case.

The way you're raymarching assumes that map is a true distance function, or at least a conservative one: because you try to step forward by the distance returned by map in each iteration, that return value must always be less than or equal to the true distance to your surface.

But your addition of sine breaks that rule.

Let's look at the curve map(p) == 1.0 (in blue), the level set of points your map function claims are 1.0 units from the surface (the level set map(p) == 0.0 in black):

Diagram of stacked sine waves

Because map(p) increases straight vertically, not perpendicular to the surface, it gives the correct distance at the peaks, but massively over-estimates the distance inside the troughs.

This means that a ray heading close to perpendicularly into the side of a trough will be given an estimate larger than its true distance to the surface, and on the next step it will tunnel through.

I'm not aware of an analytical function for computing the distance to a sine wave, so correcting the estimate itself may not be viable here.

Instead, you can modify your raymarcher to understand that it might sometimes get a bad estimate and overshoot. It can detect this if it lands in a region where map(p) returns a negative value, corresponding to a distance "inside" the shape. You can then backtrack based on this estimate. Your ray will then do something similar to a binary search in regions with inaccurate estimates, hopping between points on the near and far side of the surface, a little closer each time, until it zeroes-in on something close enough.

This is what Alex F's proposed modification accomplishes.

Note that for this to work, your step size needs to be small enough that you don't tunnel through the surface and out the other side in one step - otherwise your raymarcher won't see the negative value and detect the overshoot - and when. When it does overshoot, it needs to move forwardback again by less than its last step distance - otherwise it could oscillate endlessly or diverge away from the true intersection point.

Tuning your step size multiplier can help achieve this in trouble spots, though it does make your raymarching slower overall since even areas with well-behaved estimates will start taking more iterations.

Alex F's solution is correct; I'd like to just explain why it's necessary in this case.

The way you're raymarching assumes that map is a true distance function, or at least a conservative one: because you try to step forward by the distance returned by map in each iteration, that return value must always be less than or equal to the true distance to your surface.

But your addition of sine breaks that rule.

Let's look at the curve map(p) == 1.0 (in blue), the level set of points your map function claims are 1.0 units from the surface (the level set map(p) == 0.0 in black):

Diagram of stacked sine waves

Because map(p) increases straight vertically, not perpendicular to the surface, it gives the correct distance at the peaks, but massively over-estimates the distance inside the troughs.

This means that a ray heading close to perpendicularly into the side of a trough will be given an estimate larger than its true distance to the surface, and on the next step it will tunnel through.

I'm not aware of an analytical function for computing the distance to a sine wave, so correcting the estimate itself may not be viable here.

Instead, you can modify your raymarcher to understand that it might sometimes get a bad estimate and overshoot. It can detect this if it lands in a region where map(p) returns a negative value, corresponding to a distance "inside" the shape. You can then backtrack based on this estimate. Your ray will then do something similar to a binary search in regions with inaccurate estimates, hopping between points on the near and far side of the surface, a little closer each time, until it zeroes-in on something close enough.

This is what Alex F's proposed modification accomplishes.

Note that for this to work, your step size needs to be small enough that you don't tunnel through the surface and out the other side in one step - otherwise your raymarcher won't see the negative value and detect the overshoot - and when it does overshoot, it needs to move forward again by less than its last step distance - otherwise it could oscillate endlessly or diverge away from the true intersection point.

Tuning your step size multiplier can help achieve this in trouble spots, though it does make your raymarching slower overall since even areas with well-behaved estimates will start taking more iterations.

Alex F's solution is correct; I'd like to just explain why it's necessary in this case.

The way you're raymarching assumes that map is a true distance function, or at least a conservative one: because you try to step forward by the distance returned by map in each iteration, that return value must always be less than or equal to the true distance to your surface.

But your addition of sine breaks that rule.

Let's look at the curve map(p) == 1.0 (in blue), the level set of points your map function claims are 1.0 units from the surface (the level set map(p) == 0.0 in black):

Diagram of stacked sine waves

Because map(p) increases straight vertically, not perpendicular to the surface, it gives the correct distance at the peaks, but massively over-estimates the distance inside the troughs.

This means that a ray heading close to perpendicularly into the side of a trough will be given an estimate larger than its true distance to the surface, and on the next step it will tunnel through.

I'm not aware of an analytical function for computing the distance to a sine wave, so correcting the estimate itself may not be viable here.

Instead, you can modify your raymarcher to understand that it might sometimes get a bad estimate and overshoot. It can detect this if it lands in a region where map(p) returns a negative value, corresponding to a distance "inside" the shape. You can then backtrack based on this estimate. Your ray will then do something similar to a binary search in regions with inaccurate estimates, hopping between points on the near and far side of the surface, a little closer each time, until it zeroes-in on something close enough.

This is what Alex F's proposed modification accomplishes.

Note that for this to work, your step size needs to be small enough that you don't tunnel through the surface and out the other side in one step - otherwise your raymarcher won't see the negative value and detect the overshoot. When it does overshoot, it needs to move back again by less than its last step distance - otherwise it could oscillate endlessly or diverge away from the true intersection point.

Tuning your step size multiplier can help achieve this in trouble spots, though it does make your raymarching slower overall since even areas with well-behaved estimates will start taking more iterations.

Source Link
DMGregory
  • 136.3k
  • 22
  • 247
  • 373

Alex F's solution is correct; I'd like to just explain why it's necessary in this case.

The way you're raymarching assumes that map is a true distance function, or at least a conservative one: because you try to step forward by the distance returned by map in each iteration, that return value must always be less than or equal to the true distance to your surface.

But your addition of sine breaks that rule.

Let's look at the curve map(p) == 1.0 (in blue), the level set of points your map function claims are 1.0 units from the surface (the level set map(p) == 0.0 in black):

Diagram of stacked sine waves

Because map(p) increases straight vertically, not perpendicular to the surface, it gives the correct distance at the peaks, but massively over-estimates the distance inside the troughs.

This means that a ray heading close to perpendicularly into the side of a trough will be given an estimate larger than its true distance to the surface, and on the next step it will tunnel through.

I'm not aware of an analytical function for computing the distance to a sine wave, so correcting the estimate itself may not be viable here.

Instead, you can modify your raymarcher to understand that it might sometimes get a bad estimate and overshoot. It can detect this if it lands in a region where map(p) returns a negative value, corresponding to a distance "inside" the shape. You can then backtrack based on this estimate. Your ray will then do something similar to a binary search in regions with inaccurate estimates, hopping between points on the near and far side of the surface, a little closer each time, until it zeroes-in on something close enough.

This is what Alex F's proposed modification accomplishes.

Note that for this to work, your step size needs to be small enough that you don't tunnel through the surface and out the other side in one step - otherwise your raymarcher won't see the negative value and detect the overshoot - and when it does overshoot, it needs to move forward again by less than its last step distance - otherwise it could oscillate endlessly or diverge away from the true intersection point.

Tuning your step size multiplier can help achieve this in trouble spots, though it does make your raymarching slower overall since even areas with well-behaved estimates will start taking more iterations.