









 
 




 
 


In my development of rounded objects I want to define two versions of a complex
curvy shape, one bigger than the other, and take the smaller one away from the
larger to leave a shell with (almost) uniform thickness.
I have tried to do this with CSG difference, but it appears that the smaller
isosurface is transparent to rays meeting the inside surface. How can this
surface be made visable? I think this could be a useful extension of this
technique.
Here is a simple demo of the effect:
#version 3.7 ;
global_settings {assumed_gamma 1.0 }
camera {location <30,20,40> angle 7 look_at <0,0,0> }
light_source {<5,20,20> colour rgb<1,1,1>}
background { color rgb<0.2, 0.4, 0.8> }
#difference {
isosurface {
function {
pow(x,2)+pow(y,2)+pow(z,2)pow(1.1,2)
}
threshold 1
max_gradient 60
contained_by { box {<3.2,3.2, 3.2>, <3.2,3.2,3.2>} }
texture {pigment {color rgb < 1, 0.9, 0.65>}}
}
isosurface {
function {
pow(x,2)+pow(y,2)+pow(z,2)pow(.9,2)
}
threshold 1
max_gradient 60
contained_by { box {<3.2,3.2, 3.2>, <3.2,3.2,3.2>} }
texture {pigment {color rgb < 1, 0.9, 0.65>}}
}
box {<5,0,0>,<5,5,5>}
}
Post a reply to this message


 
 




 
 


Am 12.11.2016 um 11:57 schrieb John Greenwood:
> In my development of rounded objects I want to define two versions of a complex
> curvy shape, one bigger than the other, and take the smaller one away from the
> larger to leave a shell with (almost) uniform thickness.
>
> I have tried to do this with CSG difference, but it appears that the smaller
> isosurface is transparent to rays meeting the inside surface. How can this
> surface be made visable? I think this could be a useful extension of this
> technique.
Computing isosurfaces is quite labourintensive, and therefore the
algorithm is lazy by default: When computing where a ray intersects the
object's surface, it only searches for the intersection closest to the
observer, and ignores intersections further away.
As long as the isosurface is a standalone object, this goes unnoticed,
since the closest intersection point will translate into a surface,
hiding anything behind it.
However, when objects are used in a nonunion CSG, some intersection
points may be discarded at a later stage, depending on whether they are
inside or outside the other objects in the CSG, and thus not translate
into a surface, leaving anything behind it exposed  and in case of an
isosurface that "anything" may be the big gaping hole caused by the lazy
algorithm.
You can increase the number of intersections the isosurface algorithm
should search for before getting lazy, by specifying "max_trace NUMBER"
in the isosurface. Alternatively, you can specify "all_intersections" to
set the parameter to the highest value allowed (which in POVRay 3.7 is
equivalent to "max_trace 10".
Another approach to solve the problem would be to implement the
differencing operation in the isosurface function, rather than via CSG.
I reckon this might be faster.
In essence, the CSG difference between A and B is equivalent to an
intersection of A and the inverse of B. You can invert isosurface B by
flipping the sign of both the function and the threshold; you can then
adjust the threshold to match that of A by adding a constant to both the
function and threshold. And if I'm not mistaken you can intersect two
isosurfaces with identical thresholds and functions FnA() and FnB() by
using min(FnA(),FnB()) [or max(FnA(),FnB()), can't say off the top of my
head] as the function of a single isosurface.
Post a reply to this message


 
 




 
 


I didn't try Clipka's method, did get the intended result with max_trace 5 (or
all_intersections). It was lack of all_intersections I was thinking might be the
problem. I'm not so good with isosurfaces.
Also textured only the CSG difference to slim the SDL down, and bounding boxes
trimmed down in size too.
difference {
isosurface {
function {
pow(x,2)+pow(y,2)+pow(z,2)pow(1.1,2)
}
threshold 1
max_gradient 5
contained_by { box {<2,2,2>, <2,2,2>} }
}
isosurface {
function {
(pow(x,2)+pow(y,2)+pow(z,2)pow(0.9,2))
}
threshold 1
max_gradient 5
contained_by { box {<2,2,2>, <2,2,2>} }
max_trace 2
//all_intersections
}
box {<2,0,0>,<2,2,2>}
texture {pigment {color rgb < 1, 0.9, 0.65>}}
}
Post a reply to this message


 
 




 
 


"omniverse" <omn### [at] charternet> wrote:
> did get the intended result with max_trace 5
um, mistake. Was max_trace 2 and max_gradient 5.
Post a reply to this message


 
 




 
 


On 11/12/2016 07:50 AM, clipka wrote:
> Am 12.11.2016 um 11:57 schrieb John Greenwood:
>> In my development of rounded objects I want to define two versions of a complex
>> curvy shape, one bigger than the other, and take the smaller one away from the
>> larger to leave a shell with (almost) uniform thickness.
>>
>
> Another approach to solve the problem would be to implement the
> differencing operation in the isosurface function, rather than via CSG.
> I reckon this might be faster.
>
> In essence, the CSG difference between A and B is equivalent to an
> intersection of A and the inverse of B. You can invert isosurface B by
> flipping the sign of both the function and the threshold; you can then
> adjust the threshold to match that of A by adding a constant to both the
> function and threshold. And if I'm not mistaken you can intersect two
> isosurfaces with identical thresholds and functions FnA() and FnB() by
> using min(FnA(),FnB()) [or max(FnA(),FnB()), can't say off the top of my
> head] as the function of a single isosurface.
>
In this vein of thinking, I often use a set up like that below to get an
isosurface shell.
Bill P.
//
#version 3.7;
global_settings {
assumed_gamma 1
ambient_light srgb <1,1,1>
}
#default { finish {ambient 0.005 diffuse 0.45} }
#declare Grey50 = srgbft <0.5,0.5,0.5,0,0>;
background { color Grey50 }
#declare Camera00 = camera {
perspective
location <3,3,3.001>
sky <0,1,0>
angle 35
right x*(image_width/image_height)
look_at <0,0,0>
}
#declare White = srgbft <1,1,1,0,0>;
#declare Light00 = light_source { <50,150,250>, White }
#declare Red = srgbft <1,0,0,0,0>;
#declare CylinderX = cylinder {
<1,0,0>, <1,0,0>, 0.01
pigment { color Red }
}
#declare Green = srgbft <0,1,0,0,0>;
#declare CylinderY = cylinder {
<0,1,0>, <0,1,0>, 0.01
pigment { color Green }
}
#declare Blue = srgbft <0,0,1,0,0>;
#declare CylinderZ = cylinder {
<0,0,1>, <0,0,1>, 0.01
pigment { color Blue }
}
#declare VarShellThickness = 0.04;
#include "functions.inc"
#declare Fn00 = function (x,y,z) {
f_sphere(x,y,z,0.5)(VarShellThickness/2.0)
}
#declare Fn00_inv = function (x,y,z) {
(f_sphere(x,y,z,0.5))(VarShellThickness/2.0)
}
#declare FnShell = function (x,y,z) {
max(Fn00(x,y,z),Fn00_inv(x,y,z))
}
#declare Azure = srgbft <0,0.498,1,0,0>;
#declare Iso99 = isosurface {
function { FnShell(x,y,z) }
contained_by { box { <1,1,1>,<1,1,1> } }
threshold 0
accuracy 0.0005
max_gradient 2.1
max_trace 3
pigment { color Azure }
}
#declare Rose = srgbft <1,0,0.5,0,0>;
#declare Box00 = box {
<1,1,1>,<0,1,1>
pigment { color Rose }
}
#declare Inter00 = intersection {
object { Box00 }
object { Iso99 }
//
camera { Camera00 }
light_source { Light00 }
object { CylinderX }
object { CylinderY }
object { CylinderZ }
object { Inter00 }
//
Post a reply to this message


 
 




 
 


William F Pokorny <ano### [at] anonymousorg> wrote:
> #declare FnShell = function (x,y,z) {
> max(Fn00(x,y,z),Fn00_inv(x,y,z))
> }
Goes to show just how little I understand, I don't see how max(V1,V2) produces
anything but a single value for the next function. Hence, how could it create
both outer and inner surfaces?
Not sure I'm looking at this the right way but I tried to output the value(s) to
message stream to find out more:
#local Maxim=function{FnShell(x,y,z)};
#local Posit=function{Fn00(x,y,z)};
#local Minus=function{Fn00_inv(x,y,z)};
#warning concat(str(Maxim(1,1,1),3,3)," = maxim()")
#warning concat(str(Posit(1,1,1),3,3)," = posit()")
#warning concat(str(Minus(1,1,1),3,3)," = minus()")
Which displays the numbers (my #debug does not show, #warning does):
1.212
1.212
1.252
Sorry to post this here but as I was about to start a new post I thought the
originator of this thread might want to know about this too.
Bob
Post a reply to this message


 
 




 
 


On 11/12/2016 12:39 PM, omniverse wrote:
> William F Pokorny <ano### [at] anonymousorg> wrote:
>> #declare FnShell = function (x,y,z) {
>> max(Fn00(x,y,z),Fn00_inv(x,y,z))
>> }
>
> Goes to show just how little I understand, I don't see how max(V1,V2) produces
> anything but a single value for the next function. Hence, how could it create
> both outer and inner surfaces?
>
I think about this particular set up as follows.
In FnShell(), max() is returning the most positive value for all sampled
points within the isosurface container. Further, these samples are
always along the rays being traced(1) within the container.
The maximum value of the two functions has been set up to be negative(2)
only in the region where you see the shell. Creating the shell of
negative values is achieved by setting up the right kind of return value
overlap for the regular and inverted forms of the function used in
max(). Specifically, by making both regular and inverted forms of the
function slightly more negative by half a "thickness" value.
Bill P.
(1) When looking at function values as used in an isosurface I find it
easier sample as if following along some ray withing the container. Or
to look at a set of samples within a plane of values within the
container. There are vector analysis functions in math.inc, complements
of Christoph Hormann and Tor Olav Kristensen, which can be of help here.
(2) Surfaces form on the transition from positive values to negative and
visa versa due the threshold of 0. We get a surface entering the
negative shell of values and another on exit while moving along each ray
passing through the negative region of values.
Post a reply to this message


 
 




 
 


William F Pokorny <ano### [at] anonymousorg> wrote:
> On 11/12/2016 12:39 PM, omniverse wrote:
> > William F Pokorny <ano### [at] anonymousorg> wrote:
> >> #declare FnShell = function (x,y,z) {
> >> max(Fn00(x,y,z),Fn00_inv(x,y,z))
> >> }
> >
> > Goes to show just how little I understand, I don't see how max(V1,V2) produces
> > anything but a single value for the next function. Hence, how could it create
> > both outer and inner surfaces?
> >
> I think about this particular set up as follows.
>
> In FnShell(), max() is returning the most positive value for all sampled
> points within the isosurface container. Further, these samples are
> always along the rays being traced(1) within the container.
>
> The maximum value of the two functions has been set up to be negative(2)
> only in the region where you see the shell. Creating the shell of
> negative values is achieved by setting up the right kind of return value
> overlap for the regular and inverted forms of the function used in
> max(). Specifically, by making both regular and inverted forms of the
> function slightly more negative by half a "thickness" value.
>
> Bill P.
>
> (1) When looking at function values as used in an isosurface I find it
> easier sample as if following along some ray withing the container. Or
> to look at a set of samples within a plane of values within the
> container. There are vector analysis functions in math.inc, complements
> of Christoph Hormann and Tor Olav Kristensen, which can be of help here.
>
> (2) Surfaces form on the transition from positive values to negative and
> visa versa due the threshold of 0. We get a surface entering the
> negative shell of values and another on exit while moving along each ray
> passing through the negative region of values.
I feel dumb. Well of course the function evaluates to more than a single point,
or vector, as the ray is traced. But I'm still going to have to wrap my head
around what you're saying. Thanks Bill!
Post a reply to this message


 
 




 
 


"omniverse" <omn### [at] charternet> wrote:
> I didn't try Clipka's method, did get the intended result with max_trace 5 (or
> all_intersections). It was lack of all_intersections I was thinking might be the
> problem. I'm not so good with isosurfaces.
> Also textured only the CSG difference to slim the SDL down, and bounding boxes
> trimmed down in size too.
>
>
> difference {
>
> isosurface {
> function {
>
> pow(x,2)+pow(y,2)+pow(z,2)pow(1.1,2)
> }
> threshold 1
> max_gradient 5
> contained_by { box {<2,2,2>, <2,2,2>} }
> }
>
> isosurface {
> function {
>
> (pow(x,2)+pow(y,2)+pow(z,2)pow(0.9,2))
>
> }
> threshold 1
> max_gradient 5
> contained_by { box {<2,2,2>, <2,2,2>} }
> max_trace 2
> //all_intersections
> }
>
> box {<2,0,0>,<2,2,2>}
>
> texture {pigment {color rgb < 1, 0.9, 0.65>}}
> }
That does the trick. I will go with max_trace 2 and watch out for problems as
things get more comlicated!
Thanks for the help.
Post a reply to this message


 
 




 
 


Le 161112 à 12:39, omniverse a écrit :
> William F Pokorny <ano### [at] anonymousorg> wrote:
>> #declare FnShell = function (x,y,z) {
>> max(Fn00(x,y,z),Fn00_inv(x,y,z))
>> }
>
> Goes to show just how little I understand, I don't see how max(V1,V2) produces
> anything but a single value for the next function. Hence, how could it create
> both outer and inner surfaces?
>
> Not sure I'm looking at this the right way but I tried to output the value(s) to
> message stream to find out more:
>
> #local Maxim=function{FnShell(x,y,z)};
> #local Posit=function{Fn00(x,y,z)};
> #local Minus=function{Fn00_inv(x,y,z)};
>
> #warning concat(str(Maxim(1,1,1),3,3)," = maxim()")
> #warning concat(str(Posit(1,1,1),3,3)," = posit()")
> #warning concat(str(Minus(1,1,1),3,3)," = minus()")
>
> Which displays the numbers (my #debug does not show, #warning does):
>
> 1.212
> 1.212
> 1.252
>
> Sorry to post this here but as I was about to start a new post I thought the
> originator of this thread might want to know about this too.
>
> Bob
>
>
>
#debug print out only full lines. Add "\n" in the #debug string:
#debug concat(str(Maxim(1,1,1),3,3)," = maxim()\n")
Alain
Post a reply to this message


 
 




 

