pouët.net

Inaccuracies strolling along a ray in GLSL. -.-'

category: code [glöplog]
Errate: you can totally use just a "noise value" as a good distance estimation

abs( ) is not necessary here, it must be signed
added on the 2015-08-21 18:47:34 by tomkh tomkh
Yet another way to look at it (by turning things around):

If want to design your approxSDF(p) the way it has max. "steepness" <= 1.0.

Then you are sure: exactSDF(p) >= approxSDF(p), so you may need for iterations, but you will have no artifacts.
added on the 2015-08-21 19:05:16 by tomkh tomkh
Yet another way to look at it (by turning things around):

If want to design your approxSDF(p) the way it has max. "steepness" <= 1.0.

Then you are sure: exactSDF(p) >= approxSDF(p), so you may need more iterations, but you will have no artifacts.

[edit]
added on the 2015-08-21 19:05:55 by tomkh tomkh
If want = You want

[pouet really need post correction feature!]
added on the 2015-08-21 19:07:40 by tomkh tomkh
Solecist, I'm not entirely sure but I get the impression that it is the basic principle of raymarching distance fields you have to understand first ... before going into details.

The most efficient way to learn the basics is to read and understand working code by playing with it.
Here for example (yes, it is one of mine and it is nor perfect but anyway): https://www.shadertoy.com/view/XsBSRV
It covers, distance primitives, domain operations like circular domain, domain repetition and distance operations like subtract and also things that are not ideal because they create discontinuous distance fields ("spiral" effect is made simply by increasing an angle depending on distance to center) and is using distance*0.9 to intentionally understep (as tomkh already explained above) to visually "cure" the discontinuity, shadows (two types), fakes and tricks.

Here is an important bit of information that might be new to you (and answers one of your questions):
When you feed a position to your distance function, it always returns the distance to the nearest "object" of your function.
You can see that effect by decreasing the number of iterations of the ray marching loop.

There wont be overstepping or understepping if you simply add the distance you got to the next iteration of your ray marching loop because the distance will get very very small so it does not change the final result but makes it even more precise. It will in most cases miss the point of intersection if you just stupidly add a fixed distance for the next iteration.

Here's the ray marching loop from the example shader (h is a struct, h.d is the distance):
Code:for(int i=0; i < 60; i++) { h = scene(ray); if(h.d < 0.0001) break; dist += h.d; ray += dir * h.d * 0.9; if(dist > far) { dist = far; break; } }


If you look closely, you will see that it is just what I posted before only without early-out mechanism:
Code:for(int i=0; i < 60; i++) { dist += scene(startPos + dir*dist); } pointOfIntersection = startPos + dir*dist;


You can get the normal at the point of intersection (still an approximation) by sampling distances around the current pixel:
Code:vec3 normal(vec3 p) { float c = scene(p).d; vec2 h = vec2(0.01, 0.0); return normalize(vec3(scene(p + h.xyy).d - c, scene(p + h.yxy).d - c, scene(p + h.yyx).d - c)); }


A similar method can be used to find edges for example.

One last suggestion:
Try raymarching a cube or sphere (clean) first. If that works, 3D noise will work too and if you want to make fog or clouds, there are many examples to look at and learn from but first make sure the basics are there.

I hope that helped and please don't be mad at me if I was wrong with my assumption or if this was way below your level.
added on the 2015-08-21 22:19:12 by movAX13h movAX13h
My advice: take it to absolute basics first. Forget noise functions as they complicate everything. Forget fancy shapes too. Just ray march a few spheres, because spheres are easy.

Distance from a sphere = distance from centre of sphere minus radius, i.e. length( rayPosition - spherePosition) - sphereRadius.

If you have several spheres, find the distance to each, then use the minimum. I.e. min(sphereDist1, min(sphereDist2, sphereDist3))

You can always step along the ray by that minimum distance without worrying.

Once you get that working, you've got a working SDF raymarcher :)

From there, try some other basic shapes (see the functions on IQ's site), and boolean operations (the typical 'sphere cut out of cube' etc.), and add normals / basic lighting.

Somewhere in that process, everything will make a ton more sense, and you can go back to what tomkh is saying and make sense of it :)
added on the 2015-08-22 01:52:08 by psonice psonice
I hate this crap, it's making me depressive and sad.

Gonna report back in a few more hours.

Sorry for the delays, I'm not ignoring the advise.
The biggest issue I have is that most explanations do not help me visualize how to solve the issue.

Like, okay, I am supposed to calculate gradients.
Points around a point to estimate a gradient.

For what? So I know how close or far I am from what?
How do I reach a ppint to calculate a gradient in the first place?
I googled "estimating length to a gradient" but lol, it's just a blank image in my head.

Based on lack of understanding of the process I can not imagine it. I constantly feel like I am missing a part that's being left out. And I understand enough of raymarching to understand how to do spheres, but it does not fit to this at all.

It's all about points I want to find, but to find them I need these points I haven't found yet.

That's what I am understanding so far...

Talk to you later...
Sorry for ranting. I usually solve my issues alone and in unconventional ways,
avoiding math as much as I can by visualizing what's necessary,
which allows me to figure out what I need to do.

Shaders make this harder, because I can't print out values to experiment with.

Anyhow... too much ranting, gotta do work instead.
Solecist:
oh no! Now I see that you should actually totally ignore my ramblings at first.

As movAX13h or psonice said - you don't need this stuff at the begining.
The only reason why you need to calculate gradient at first is to estimate the normal: like movAX13h wrote in his vec3 normal(vec3 p) function (although it is not using central difference, but some faster hack, but since it's still just an approximation it should be just fine).

In general what you want to calculate is obviously (again) just a distance to the closest surface. How? If you really want to trace pure 3d noise, you can simply use distance = noise value * some factor, and this factor can be a constant for all points and you can find it by trial&error until you will have no visible artifacts, start with .6 or something.
As you can see movAX13h is using factor = .9 in his loop by default (I'm actually curious why it is there by default).

Now, the point is - if you have properly designed SDF, you don't need this factor (as it should be 1.0). For this you need extra math background unfortunately. Like study "Functional analysis" deeper and so on... You can still get away with it if you have proper SDF debugging tools, like "cupe" is showing in his seminar - which is really worth watching.
added on the 2015-08-22 14:08:59 by tomkh tomkh
movAX13h's normal calculation method is afaik called one-sided finite difference method. It doesn't work in all cases and required some finetuned parameters (you can get quite a bit of artifacts), but can be quite a bit faster since it requires far fewer evaluations of the geometry. I myself never noticed too big of a performance difference honestly (but artifacts can get troublesome with it).
added on the 2015-08-22 14:39:06 by noby noby
You guys really are the best, but I know that for two decades already. ^_^

"distance = noise value * some factor" no it's not that simple,
else I would see what's being meant. :p

I do have a suspicion to what it's about, though.
It was something that hit me as a possibility when I was in the elevator.

I accidentially might have crippled my thought process from the early on,
because I'm filtering out values below 0.1 and don't really see the whole thing.

I will tinker around with values and look at the visuals ...
... and hope that I can find what I am missing.

And please believe me when I tell you that I looked at sample code on shader toy.
Tried finding the most easiest. I understand how it works, basically,
but it doesn't necessarily make sense in my head.

The thing with all these shaders is ... hm ... like knowing a few chinese symbols.
You know the symbols, but when you look into a book there are SO FUCKING MANY
more symbols and connections in between these symbols that you don't understand,
that the symbols you DO understand just drown out and blur.

Anyhow ... finally at home ... finally able to code.
I'll try to make use of what you awesome people posted.

Somehow.

And I'll check out that seminar. ^_^
Okay, this seminar definitely isn't made for me, but I'll keep it bookmarked.
"Try raymarching a cube or sphere (clean) first. If that works, 3D noise will work too and if you want to make fog or clouds, there are many examples to look at and learn from but first make sure the basics are there."

A sphere or a cube, as object, has a vector that defines it's spot in existence.
A 3d noise function does not have that. Nowhere inside the noise is a way to
know where points are, except by looking for them in the first place.

That's a huge hole in my head. I feel like people constantly tell me to just measure the distance to an object I do not have the coordinates of. It's not comparable to a sphere or cube, because there are no coordinates I can use to measure the distance.

I think I've managed better how to express one of my issues. There is just nothing to "look at" or "find out" in regards to understanding ray marching when it comes to this, because I have nothing to look at in the first place!

Soooo ... I would need to find coordinates of points inside the noise function I want to march towards to, which is silly, because I need to march through the noise to find these coordinates in the first place!

Unless, of course, I can somehow use the values (s < 0.1) inside the noise to determine approximate distances to the values I want to render (s > 0.1) !

Did I understand this correctly? I feel like it's what I'm being told. I just now slowly start to be able to put it into a language that makes sense for anyone else. -.-

Also sorry for blogging.
I lack a social environment and this really helps sorting my thoughts. :p
It shows my dedication. :p
I think that you should experiment with displaying distance function in 2D using glslsandbox.com or shadertoy.com
http://glslsandbox.com/e#27272.0
Does this help ?
added on the 2015-08-22 17:16:49 by kloumpt kloumpt
Hm ... so I changed my python program to allow me to dynamically change the filter,
which makes it "while (s < n)" and indeed it seems that the lower n gets,
the more matter is there around me.

Not sure yet if I can use that to jump distances ......
@kloumpt:

Here I know the coordinates of the discs and thus can measure the distance.
In my noise function I do not have any coordinates I could measure the distance to.

Isn't that my issue?
Also omg I just noticed the green dots showing who's online! :O
Here: https://www.shadertoy.com/view/MtlSWX

Try dragging mouse up/down to see what artifacts appear when you change the factor I'm talking about.
If it is in the middle of the screen (vertically) it should be exactly 1.0.
I have found out that .98 is already fine (not perfect in all cases though).

I took some hacky IQ's noise, which is not really a gradient noise, but since values are bounded (0..1) and you just interpolate between them, obviously |gradient| is bounded as well - it has Lipschitz continuity ;-) as "cupe" was bringing up this term in his serminar as well.
added on the 2015-08-22 17:46:10 by tomkh tomkh
Okay.

So now I manually change StepSize and IgnoreBelow.

Code:vec3 rb = rd * (StepSize/length(vec3(q,1.0))); while (s < IgnoreBelow) { rs += rb; s = snoise(rs); }


I see now that, when I decrease IgnoreBelow, matter gets thicker. That's interesting, because it looks like it would allow me to make bigger steps without stepping through any matter.

This seems to work in theory, but I also see that whatever matter is close to the camera would give horribly wrong results. What I mean is that ... hm ...

Decreasing IgnoreBelow increases thickness of matter.
That allows me to increase StepSize.
Though if I then want to use "optimal" IgnoreBelow and use smaller steps,
I might end up going much further, because at "optimal" IgnoreBelow the matter isn't there anymore.

It seems to work at greater distances, but not close to the camera.


Hmhmhm.


Am I annoying you guys?
Solecist: the thing is you are not doing sphere-tracing, just ray-marching with fixed step. Maybe that's why all the confusion?
added on the 2015-08-22 17:59:11 by tomkh tomkh
@tomkh: Wow ......

Uhm ... how in hell does this work.
You take the noise value, subtract and multiply and it works.

That makes no sense at all, assuming that the noise function does nothing more than output some value for a specific coordinate in space. What is this sorcery?
Oh and this doesn't work with simplex, but I feel like that was obvious ...

I feel like this thread has gone way differently than I intended ...................... D:
@tomkh:

"Solecist: the thing is you are not doing sphere-tracing, just ray-marching with fixed step. Maybe that's why all the confusion?"

I'm not sure what to think anymore. Someone said I should consider sphere tracing instead of what I'm doing, which wasn't my idea, I had no idea that it wouldn't even work with what I am doing, now I feel like I've wasted everyone's time ... D:

D:
Solecist: well yeah, if you have proper distance function, sphere tracing gives you guarantee that you will not miss the surface (which is where you distance function is equal to 0).
With fixed step you can miss it easily as @noby pointed out with his illustration.
So we are indeed going in circles ;-) But it's fine. Tracing noise and approximating distance function with good properties is still an interesting topic.
added on the 2015-08-22 18:20:57 by tomkh tomkh
@tomkh: Honestly I did not mean to mislead anyone. Someone suggested sphere-tracing.
I thought "why not" and knew that there's holes in my understanding about it.

Now I realise that how I'm doing this, there's no distance function for it ...
... at least not with that noise function. I really hope no one's mad at me now. D:

...

ON THE UPSIDE THOUGH did all this talk help me figure out other solutions!

You all did not waste your time on me ... my first test gave me a 10fps boost ....

Code: vec3 rb; rb = rd * (0.07/length(vec3(q,1.0))); while (s < 0.03) { rs += rb; s = snoise(rs); } rb = rd * (0.02/length(vec3(q,1.0))); while (s < 0.1) { rs += rb; s = snoise(rs); }


High resolution image:
BB Image


Made with the above code:
BB Image


Btw, this is downsampled.
The window actually is 1600x1000.


What do you think? :/

login