Open TLC123 opened 6 years ago
Woah!!!!!!!! That is exciting!!!!
On Sat, May 19, 2018 at 5:10 AM, TLC123 notifications@github.com wrote:
When creating textures (x,y,z,t) is available to plug in to some function. In Shadertoy there are many examples where, in addition to the (x,y,z,t) coordinate, the normal of the field at that (x,y,z,t) point is made available. This is of course very useful for texture generation I hacked up a thing like this [image: alt text] https://github.com/TLC123/Curv_stuff/blob/master/rgbn.png?raw=true
the code let
RGB_normal shape =
make_shape { dist p : shape.dist(p), colour p : let n= ( ( normalize( (shape.dist(p) - shape.dist(p+(0.001,0,0,0)) ) , (shape.dist(p) - shape.dist(p+(0,0.001,0,0)) ) , (shape.dist(p) - shape.dist(p+(0,0,0.001,0)) ) ))); in max(0,n[X])*[0,1,1]+max(0,n[Y])*[0,1,0]+max(0,n[Z])*[1,1,0]+ -min(0,n[X])*[1,0,0]+-min(0,n[Y])*[1,0,1]+-min(0,n[Z])*[0,0,1] , bbox : shape.bbox, is_2d : shape.is_2d, is_3d : shape.is_3d,
};
in
union( smooth 0.2 .union( union( ellipsoid (1,1.4,2) >>move(1,1,1) , cone {d:1, h:2}>>move(-1,1,0.5), cylinder {d:1, h:1.5}>>move(1,-1,1), cube 1>>move(-1,-1,1) ), morph 0.75 ( sphere 2 , cube 1.45>>offset 0.05), ), torus {major:4, minor:1}>>move(0,0,-1)
)
RGB_normal
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/doug-moen/curv/issues/19, or mute the thread https://github.com/notifications/unsubscribe-auth/AQWpJX7p9JrgSSMzs4PifdYzZr9ZGL4cks5tz-GFgaJpZM4UFo9J .
-- Do you want to make something? Here are the tools. Go
Diyode is the single most awesome toy under the tree at Christmas that I had to grow up to get
That's a cool demo!
Have you heard the good news about automatic differentiation?
Since Curv has full control over the evaluation and rendering pipeline, I'm curious if there are plans to get the normals at a language level, without needing to manually evaluate partial differences.
@mkeeter: Thanks for the link, Matt. I did not know about automatic differentiation.
Based on the article, I'd consider implementing forward accumulation, and also allow the user to directly specify the gradient of the distance function within a shape.
(I did have plans to provide normals at the language level, but that was years ago, and higher priority work kept getting in the way. Automatic differentiation looks easier than the symbolic differentiation that I had previously considered.)
Hi @TLC123, I decided to speed up your code.
First, in RGB_normal, in make_shape, in colour, I cached the value of shape.dist(p)
in the variable d
, and that got me from 12 to 15 FPS on my 2010 MacBook Air. This shows that the Curv compiler ought to be doing common subexpression optimization (future enhancement).
Next, I fixed a performance bug in the Curv GLSL compiler, and that got me from 15 to 30 FPS. See: https://github.com/doug-moen/curv/commit/878cf2b43825c7c873f03298ff48de4a76857839
Finally, the colour
function returns linear RGB, which isn't as nice to work with as the more familiar sRGB colour space. So I added a call to sRGB
to your colour
function, just to see if the colours look any better. sRGB
converts from sRGB coordinates to linear RGB coordinates.
Here's my revision of your code:
let
RGB_normal shape =
make_shape {
dist : shape.dist,
colour p :
let d = shape.dist p;
n = normalize(
d - shape.dist(p+(0.001,0,0,0)),
d - shape.dist(p+(0,0.001,0,0)),
d - shape.dist(p+(0,0,0.001,0)));
in
sRGB(max(0,n[X])*[0,1,1]+max(0,n[Y])*[0,1,0]+max(0,n[Z])*[1,1,0]+
-min(0,n[X])*[1,0,0]+-min(0,n[Y])*[1,0,1]+-min(0,n[Z])*[0,0,1]),
bbox : shape.bbox,
is_2d : shape.is_2d,
is_3d : shape.is_3d,
};
in
union(
smooth 0.2 .union(
union(
ellipsoid (1,1.4,2) >>move(1,1,1) ,
cone {d:1, h:2}>>move(-1,1,0.5),
cylinder {d:1, h:1.5}>>move(1,-1,1),
cube 1>>move(-1,-1,1)),
morph 0.75 ( sphere 2 , cube 1.45>>offset 0.05)),
torus {major:4, minor:1} >> move(0,0,-1))
>> RGB_normal
Thanks. Assuming Automatic differentiation is cheap shouldn't that gradient slope help determine a truer step-size in sphere tracing of unruly distance fields? There might be more benefits than mere textures.
I compute multiple gradients per pixel in the lighting model, so this could speed up rendering.
Sphere tracing works on distance fields that aren't differentiable. For something like a cube, the distance field isn't differentiable at edges and corners. For something like the Mandelbulb, the DF is not differentiable anywhere.
However, Sphere tracing requires the distance field to be Lipschitz(1) continuous. There are some distance fields that are not Lipschitz continuous, but which are C1-continuous (differentiable everywhere, and the derivative is continuous). For these distance fields, I could use a root finding technique that uses the derivative for ray-casting. The problem is the limited applicability. What if I union a C1-continuous DF with another shape? The output of union is not C1-continuous, so how do I render it?
What I would like is a ray-casting method that works on any continuous distance field, even if it is not Lipschitz-continuous. This method does not require differentiability, so it works on Mandelbulb. It's expected to be slower than sphere tracing. Then I can use this alternate method instead of sphere tracing if the DF is not Lipschitz.
I think this started as a feature request for a high level interface for making textures that have access to the normal. Okay, here's my initial idea for this.
let
gtexture f shape =
make_shape {
dist : shape.dist,
colour p :
let d = shape.dist p;
n = normalize(
d - shape.dist(p+(0.001,0,0,0)),
d - shape.dist(p+(0,0.001,0,0)),
d - shape.dist(p+(0,0,0.001,0)));
in f(p, n),
bbox : shape.bbox,
is_2d : shape.is_2d,
is_3d : shape.is_3d,
};
RGB_normal (p, n) = sRGB(
max(0,n[X])*[0,1,1]
+ max(0,n[Y])*[0,1,0]
+ max(0,n[Z])*[1,1,0]
- min(0,n[X])*[1,0,0]
- min(0,n[Y])*[1,0,1]
- min(0,n[Z])*[0,0,1]);
in
union(
smooth 0.2 .union(
union(
ellipsoid (1,1.4,2) >>move(1,1,1) ,
cone {d:1, h:2}>>move(-1,1,0.5),
cylinder {d:1, h:1.5}>>move(1,-1,1),
cube 1>>move(-1,-1,1)),
morph 0.75 ( sphere 2 , cube 1.45>>offset 0.05)),
torus {major:4, minor:1} >> move(0,0,-1))
>> gtexture RGB_normal
I was trying to get rid of the sharp colour banding. I realized that the output of min
and max
in RGB_normal is not smooth, so I tried this instead:
let
gtexture f shape =
make_shape {
dist : shape.dist,
colour p :
let d = shape.dist p;
n = normalize(
d - shape.dist(p+(0.001,0,0,0)),
d - shape.dist(p+(0,0.001,0,0)),
d - shape.dist(p+(0,0,0.001,0)));
in f(p, n),
bbox : shape.bbox,
is_2d : shape.is_2d,
is_3d : shape.is_3d,
};
RGB_normal (p, n) = sRGB(
max(0,n[X])*[0,1,1]
+ max(0,n[Y])*[0,1,0]
+ max(0,n[Z])*[1,1,0]
- min(0,n[X])*[1,0,0]
- min(0,n[Y])*[1,0,1]
- min(0,n[Z])*[0,0,1]);
smooth_rgbn (p, n) = sRGB(
smooth_max(0,n[X],1)*[0,1,1]
+ smooth_max(0,n[Y],1)*[0,1,0]
+ smooth_max(0,n[Z],1)*[1,1,0]
- smooth_min(0,n[X],1)*[1,0,0]
- smooth_min(0,n[Y],1)*[1,0,1]
- smooth_min(0,n[Z],1)*[0,0,1]);
in
union(
smooth 0.2 .union(
union(
ellipsoid (1,1.4,2) >>move(1,1,1) ,
cone {d:1, h:2}>>move(-1,1,0.5),
cylinder {d:1, h:1.5}>>move(1,-1,1),
cube 1>>move(-1,-1,1)),
morph 0.75 ( sphere 2 , cube 1.45>>offset 0.05)),
torus {major:4, minor:1} >> move(0,0,-1))
>> gtexture smooth_rgbn
Brilliant. Works like a charm.
Just one question. I tried and failed. What if i need to pass say two arguments a and b or maybe a list to my f function. Some times i do, sometimes i don't RGB_normal a b (p, n) = sRGB(... ... ... ...>> gtexture RGB_normal 10 .7
@TLC123
The short answer is: use gtexture (RGB_normal 10 .7)
.
The reason that gtexture RGB_normal 10 .7
doesn't work is: you are passing 3 arguments to gtexture
. The first argument is RGB_normal
, the second argument is 10
, the third argument is .7
. But gtexture
takes 2 arguments, a function and a shape.
A more detailed answer is: if you call a function, passing fewer arguments than are required, then this is legal, and what you get back is another function that consumes the remaining arguments. This is called 'Currying', and Curv functions with 2 or more arguments are called 'Curried functions'.
In the example, you define
RGB_normal a b (p,n) = ...;
So RGB_normal
takes 3 arguments: the third argument is the pair (p,n). If you write (RGB_normal 10 .7)
, this is called partial application, and you get back another function that consumes a single argument (p,n)
.
More generally, if f
takes 3 arguments, then you call it by writing f a b c
. This is equivalent to writing
((f a) b) c
When creating textures (x,y,z,t) is available to plug in to some function. In Shadertoy there are many examples where, in addition to the (x,y,z,t) coordinate, the normal of the field at that (x,y,z,t) point is made available. This is of course very useful for texture generation I hacked up a thing like this
https://www.shadertoy.com/view/ld3BDj
the code
let