Our current media method 3 jitter code doesn't bound even the smallest jitter within the end points of the sub-intervals. This is most obvious when the media density has a relatively high value as it exits the containing object and we are in fact sampling half the time outside the container.
See the images: NoJitter.png vs VeryTinyJitter.png. The two results should be nearly identical and they're not. The bounding issue happens any time abs (jitter value) >0 but depending on sample counts, density and so on it will be more or less visible in practice.
(Note: There is too a sub issue with jitter worth fixing with this one. On adaptive descent we are I think correctly passing the non-jitter 'distance' value to the recursive call, but we are incorrectly not re-calculating the color and optical distance before that call. The result is we are corrupting downstream tests against aa_threshold and downstream optical distance calculations. Perhaps none of this matters if the only intent is to give the user a noisy result. This just for noise aim is however different than how jitter is used with method 1 & 2 and the photon media depositing code where jitter tends to resolve the media's density with application.)
Environment
Ubuntu 16.04 linux. v3.7.0 onward at least. Attached code written and tested against release/v3.8.0 as of commit 5b99fdc Sun Jan 14 13:15:29 2018 +0100.
Steps to Reproduce
1) povray +w800 +h600 MediaMethod3JitterSubIntervalBounding.pov
2) Note noise in image where lit media exits container. See: VeryTinyJitter.png
3) Edit source file and change VarJitter to 0.
4) povray +w800 +h600 MediaMethod3JitterSubIntervalBounding.pov
5) Result clean and it's what we should produce in (1). See: NoJitter.png
Expected Behavior
Media method 3 jitter should be bound within the active interval so long as the absolute value is >=0 and <=1.0. With larger value it will of course jump intervals. Other media methods look to keep the jitter inside the 'interval's' end points.
Actual Behavior
The jitter isn't sub-sample interval bound though on recursive descent the issue is I think more the lack of recalculation of color and optical distance prior to decent.
Scene
//
// See VarJitter setting below. At 0 OK. At abs(jitter)>0 && <=1 not.
//
// Our current media method 3 jitter code doesn't bound even the smallest
// jitter within the end points of the sub-intervals. This is most obvious
// and most noisy when the media density has a relatively high value and
// exits the containing object.
//
// See the images: NoJitter.png vs VeryTinyJitter.png. The two results
// should be nearly identical and they're not.
//
#version 3.8;
global_settings { assumed_gamma 1 }
#default { finish {ambient 0.0 diffuse 1.0} }
#declare Grey40 = srgb <0.4,0.4,0.4>;
background { color Grey40 }
#declare VarOrthoMult = 1;
#declare Camera01z = camera {
orthographic
location <0,0,-2>
direction z
right VarOrthoMult*x*max(1,image_width/image_height)
up VarOrthoMult*y*max(1,image_height/image_width)
translate <0.5,0.5,-2>
}
#declare VarMediaR = 0.48;
#declare VarDistRes = 0.04;
#declare VarDist = VarMediaR-(VarDistRes/2);
#declare VarDistPos = VarDist+(VarDistRes/2);
#declare VarDistNeg = VarDist-(VarDistRes/2);
#declare White = srgb <1,1,1>;
#declare Light00 = light_source {
<0.5,0.5,-0.5>, White
fade_distance VarDistPos
fade_power 1000
media_attenuation on
}
#declare Red = srgb <1,0,0>;
#declare CylinderX = cylinder { -2*x, 2*x, 0.01 pigment { Red } }
#declare Green = srgb <0,1,0>;
#declare CylinderY = cylinder { -2*y, 2*y, 0.01 pigment { Green } }
#declare Blue = srgb <0,0,1>;
#declare CylinderZ = cylinder { -2*z, 2*z, 0.01 pigment { Blue } }
#declare Box00 = box {
<-1.001,-1.001,-1.001>,<1.022,0.499,1.022>
pigment { color White }
}
#declare Box01 = box { <-1,-1,-1>,<1,0.5,1> hollow }
#include "functions.inc"
#declare FnctSph = function (x,y,z) { sqrt(x*x+y*y+z*z)>0.46 }
#declare DensityOne = density { function { FnctSph(x-0.5,y-0.5,z+0.5) } }
#declare Sphere01 = sphere {
<0,0,0>, 0.23
scale <2.2,1,2.2>
translate <0.5,0.75,-0.5>
hollow
}
#declare Difference01 = difference { object { Sphere01 } object { Box01 } }
#declare VarSamples = 1;
#declare VarJitter = 0.0; // OK.
#declare VarJitter = -0.001; // Even smallest bleeds outside sub-interval.
// #declare VarJitter = +0.001;
#declare VarAA_level = 1;
#declare MediaSC = media {
method 3
intervals 1
samples VarSamples
jitter VarJitter
aa_level VarAA_level
aa_threshold 0.1
absorption rgb <0,0,0>
emission rgb <0,0,0>
scattering { 1, rgb <1,1,1> extinction 1 }
density { DensityOne } // Avoids method 3, constant density, method 1, fast path.
}
#declare InteriorSC = interior { ior 1 media { MediaSC } }
#declare Clear90 = srgbt <1,1,1,0.9>;
#declare PigmClear = pigment { color Clear90 }
#declare TxtrClear = texture { pigment { PigmClear } }
#declare MatrSC = material { texture { TxtrClear } interior { InteriorSC } }
#declare Obj02SC = object { Difference01 material { MatrSC } hollow }
//--- scene ---
camera { Camera01z }
light_source { Light00 }
object { Box00 }
object { CylinderX }
object { CylinderY }
object { CylinderZ }
object { Obj02SC }
Output
See attached NoJitter.png and VeryTinyJitter.png
Workaround
None. Recommend media method 3 jitter be avoided unless a noisy result is the actual aim.
Suggested Solution
The up front solutions would be to bound jitter to sub-sample / interval end point range as done elsewhere in media methods. Recalculate color (C2) and distance (od2) before recursive call in ComputeOneMediaSampleRecursive. However, these are likely to be expensive to the normal user as in my own use and review of media scenes - most do not use jitter. Splitting the code into jitter and no jitter trees is an option offering a substantial performance gain to jitter=0 users and limiting the jitter fix impacts to those using jitter. Further, I think it will likely make performance optimization in the jitter=0 tree much easier to accomplish. I'm playing with such split code as I write this but nothing is anywhere near ready.
We probably have to keep the existing distance based jitter given it's been around so long, but maybe we could think too about a new media { method 3 aa_jitter ... } option that would be applied at the aa_threshold test for recursion. That sort of jitter would tend to resolve media density in a way more friendly to AA, area lights and such.
Summary
Our current media method 3 jitter code doesn't bound even the smallest jitter within the end points of the sub-intervals. This is most obvious when the media density has a relatively high value as it exits the containing object and we are in fact sampling half the time outside the container.
See the images: NoJitter.png vs VeryTinyJitter.png. The two results should be nearly identical and they're not. The bounding issue happens any time abs (jitter value) >0 but depending on sample counts, density and so on it will be more or less visible in practice.
(Note: There is too a sub issue with jitter worth fixing with this one. On adaptive descent we are I think correctly passing the non-jitter 'distance' value to the recursive call, but we are incorrectly not re-calculating the color and optical distance before that call. The result is we are corrupting downstream tests against aa_threshold and downstream optical distance calculations. Perhaps none of this matters if the only intent is to give the user a noisy result. This just for noise aim is however different than how jitter is used with method 1 & 2 and the photon media depositing code where jitter tends to resolve the media's density with application.)
Environment
Ubuntu 16.04 linux. v3.7.0 onward at least. Attached code written and tested against release/v3.8.0 as of commit 5b99fdc Sun Jan 14 13:15:29 2018 +0100.
Steps to Reproduce
1) povray +w800 +h600 MediaMethod3JitterSubIntervalBounding.pov 2) Note noise in image where lit media exits container. See: VeryTinyJitter.png 3) Edit source file and change VarJitter to 0. 4) povray +w800 +h600 MediaMethod3JitterSubIntervalBounding.pov 5) Result clean and it's what we should produce in (1). See: NoJitter.png
Expected Behavior
Media method 3 jitter should be bound within the active interval so long as the absolute value is >=0 and <=1.0. With larger value it will of course jump intervals. Other media methods look to keep the jitter inside the 'interval's' end points.
Actual Behavior
The jitter isn't sub-sample interval bound though on recursive descent the issue is I think more the lack of recalculation of color and optical distance prior to decent.
Scene
Output
See attached NoJitter.png and VeryTinyJitter.png
Workaround
None. Recommend media method 3 jitter be avoided unless a noisy result is the actual aim.
Suggested Solution
The up front solutions would be to bound jitter to sub-sample / interval end point range as done elsewhere in media methods. Recalculate color (C2) and distance (od2) before recursive call in ComputeOneMediaSampleRecursive. However, these are likely to be expensive to the normal user as in my own use and review of media scenes - most do not use jitter. Splitting the code into jitter and no jitter trees is an option offering a substantial performance gain to jitter=0 users and limiting the jitter fix impacts to those using jitter. Further, I think it will likely make performance optimization in the jitter=0 tree much easier to accomplish. I'm playing with such split code as I write this but nothing is anywhere near ready.
We probably have to keep the existing distance based jitter given it's been around so long, but maybe we could think too about a new media { method 3 aa_jitter ... } option that would be applied at the aa_threshold test for recursion. That sort of jitter would tend to resolve media density in a way more friendly to AA, area lights and such.