Menu

Caustics go through walls?

Help
ma0mpt
2023-04-25
2023-07-17
  • ma0mpt

    ma0mpt - 2023-04-25

    Hi Folks,

    I've not posted for a few years re AoI. I missed playing with it so have resurrected my interest.

    I have noticed a perplexing thing.

    I cannot block light sources from producing caustics in a glass material. I was glad I got the effect I was wanting, nice caustics, but then I noticed that even if I blocked the light to the glass object it was still producing caustics.

    Included a scene file and a couple of images.

    I am using AoI 3.2 on Windows 10, Java 17. Same thing happens on an older version (2.9). Have not tested older ones.

    Of course I could be doing something obviously (or not so obvious to me) stupid.

    Any ideas?

     
  • ma0mpt

    ma0mpt - 2023-04-25

    attachments

     
  • Pete

    Pete - 2023-04-25

    Hi!

    By a quick test, there seems to be a bug there. Or actually at least two:
    1) Directional light travels through walls to create caustics . (However at least Spot light does not, but the result is not necessarily that clean.)
    2) The DL caustics seem to be unrealistically strong, like the reflected part of the ray was not subtracted from the part that went through.
    3) When you use a different type of light and entirely block it from the reflective/transparent object, building (or trying to build) a caustics map takes a very long time, as there is nothing to build caustics from. It does survive the process though.

    Sadly this is not a new bug. Directional lights shone through walls, when I was new to AoI many years ago and the problem was addressed at the time. (As I recall it took a few rounds to fix.) I have no idea why or when it has stopped working but at least there have been some major rearrangements to the code since. For example that rays can be used on other things than tracing only light...

     
  • ma0mpt

    ma0mpt - 2023-04-25

    Hi Pete,

    Thanks for the response. The forum looked waaaaaay too quiet and I thought I would be lucky if anyone replied.

    True, it only seems to happen with a DL.

    The DL direct lighting seems to get blocked by geometry, but somehow the light gets into the Photon Map despite the DL being obscured from the object by the blocker.

    I tried PL and SL too and yes, when blocked, the PM takes longer to build. And the lights are attenuated/blocked.

    I messed a bit with the source for RaytracerRenderer.java and noticed that caustic light contribution gets added before the lights are looped over, checking for blocks. However, I could not get my head around how/where to try experimenting. The following snippet (see ~line 1742) looks like it has to be involved somehow:

    if (caustics) {
                workspace.causticsMap.getLight(pos, spec, normal, viewDir, front, lightColor);
                finalColor.add(lightColor);
    
            }
    

    Might be barking up entirely the wrong tree though...

     
  • Pete

    Pete - 2023-04-26

    That would at least be one marker, where to start. It is usually pretty complex to follow the program path to find the exact point where things go wrong and thenwhat exactly goes wrong.

    I'll have a look at it in a couple of days (got somethig else on for the nex couple of days). And anybody else, if you know where to look, please do. :)

     

    Last edit: Pete 2023-04-26
  • ma0mpt

    ma0mpt - 2023-04-26

    Cheers.

    If anyone can provide an initial good steer then I will delve in too :-)

     
  • ma0mpt

    ma0mpt - 2023-04-27

    Been doing some more testing. I think there is a fairly serious issue with photon mapping:

    • caustic photon mapping
      environment lights (solid colour, emissive, diffuse) and directional lights leak through multiple walls to pollute a caustics photon map inside a sealed box (double-wall).

    • final gather photon mapping
      environment, point, spot, directional (and procedural versions) lights leak through multiple walls to pollute a final-gather global photon map inside a sealed box (double-wall).

    • direct photon mapping
      environment, point, spot, directional (and procedural versions) lights leak through multiple walls to pollute a direct photon map inside a sealed box (double-wall).

     
  • Pete

    Pete - 2023-04-27

    I managed to trace down at least one problem, just follow what I picked from the code:

    RaytracerRenderer.java:
    803 run()
    831 buildPhotonMap...
    
    // The method:
    660 buildPhotonMap()
    
    // And here we go with caustics
    677 if(caustics)
    
    // This will have an effect later 
    881 BoundingBox bounds
    682 for...
    
    // "bounds" will enclose all reflective or refractive objects, in this
    // case only one. The plan is that photon tracing rays would be aimed
    // only at the part of the space, where the object that can produce
    // caustics are.
    
    // The "bounds" travelling forward with the map.
    667 causticsMap = new PhotonMap() 
    
    668 generatePhotons(causticsMap) -->
    727 generatePhotons()
    
    // The "causticsMap" that carries the "bounds" is sent to 
    // the photon source..
    748 sources.add(new DirectionalPhotonSource(.......)) 
    
    // In our case "map" is the PhotonMap named "causticsMap".
    797 map.generatePhotons() --> 
    
    PhotonMap.java:
    112 generatePhotons()
    
    // One source[i] will be the "DirectionalPhotonSource()" above.
    140 source[i].generatePhotons() --> 
    
    DirectionalPhotonSource.java:
    // At creation reading in the box around the reflective object
    // and the center of the box.
     32 BoundingBox bounds = map.getBounds()
     37 center = ... 
    
    // And the program continues here:
     64 generatePhotons()
    
    // This ray will find the spot, where the photon should land.
     92 Ray r = new Ray() 
    
    // Staring point of the ray, undefined at this point.
     93 Vec3 orig = r.getOrigin(); 
    
     // And this is where things go wrong:
     96 orig.set(center.x+x*xdir.x+y*ydir.x, 
                 center.y+x*xdir.y+y*ydir.y,
                 center.z+x*xdir.z+y*ydir.z);
    
    // The origin of the ray is set at the bounding box of the reflective 
    // object, effectively making a single object appear to radiate 
    // itself rather than reflecting light from outside.
    

    You can work around this particular issue by adding another reflective or refractive object to the scene outside the "shelter" on the side where the directional light is shining from. That makes the bounding box larger, so the photon tracing rays will be sent from outside. (Tested & worked. Minor light leak happend, where the othe object's caustic hit the joint of the wall and floor.... The "neighboring photons" don't seem to be too sensitive to boundaries)

    The correction in the code is basically rather simple: Just move the ray origin farther away, but I'm not sure what effect that would have on soft caustics ... Should study further but a quick and dirty orig.subtract(coords.getZDirection().times(100)); for line 97 helped the main problem.

     

    Last edit: Pete 2023-04-28
  • ma0mpt

    ma0mpt - 2023-04-27

    Pete, you genius!

    Now I have something to get my teeth into. Lots of playing coming up...

     
  • Luke S

    Luke S - 2023-04-28

    I'm glad that there's a workaround for the time being.

    Regarding bleed through multiple layers in the non-caustics maps, unfortunately that's a more-or-less inevitable side effect of how the PM works. When sampling, the lighting algorithm grabs the nearest X photons, with no regard for whether they are actually visible from the location being lit.

     
  • Pete

    Pete - 2023-04-29

    Somehow the process logic of creating caustics seems a kind of unfinished to me.

    Now the light sources are bombarding the approximate direction where they believe any "causticable" objects might be, without really knowing if they have any hope of hitting the objects. The caustics BoundingBox might just as well be mostly empty space or the objects might be obscured by something else...

    What I'm thinking is that the first ray should be initiated on the surface of the object and aimed at the light source. If it hits, then it should continue from the starting point to the opposite direction (reflected or refracted) to find the landing point of the photon.

    And to clarify: The first ray should not be traced through a transparent object. The purpose is only to make sure that only the light rays (from the light source) that actually can hit the object would be used. Also the first rays should not be aimed at other caustic objects. It is the other direction of the ray that takes care of photons bouncing from other objects.

    What this would take code wise, I have no idea. There is quite a bit of it....

     

    Last edit: Pete 2023-04-29
  • ma0mpt

    ma0mpt - 2023-04-29

    Thanks to your help Pete I am starting to understand how the directional light source photon mapping stuff works with caustics. Although I am having problems visualizing (in my head) how the stratified sampling works. But I have a feeling that it shoots rays into the bounding box from distributed random positions on one end of the box, in the direction of the light. Which kind of sound sensible. And where it hits a refractive/specular object it creates photons.

    Directional Lights, Point Lights, Spot Lights. Env Light don't have geometry so it might be tricky to reverse-shoot to a light from an object (and where on the object would you shoot from?). I think this is almost bi-directional path tracing - to which PM is an alternative? I get where you are coming from and I agree it currently seems overkill to shoot rays from every part of the bounding box face.

    In the case of the directional source might it be possible to somehow project the caustic-generators' 2D shape back onto the bounding box face and just trace rays from the areas where the 2D Projections landed? Same principle you suggest but done in bounding box space rather than world space. I think the code for this (although totally beyond me) should not be too difficult for a seasoned guru. There, I've painted a target on my back...

     

    Last edit: ma0mpt 2023-04-29
  • Pete

    Pete - 2023-04-30

    problems visualizing (in my head) how the stratified sampling works

    I haven't gotten familiar with that part either but the principle is pretty much what you described. Building a clear image of how everything happens by following the code takes time and energy... (This is not only true with raytracing, but all visualization.)

    it might be tricky to reverse-shoot to a light from an object

    Not really. Most of raytracing rendering happens from camera to object to light(s) except for AmbientOcclusion that shoots rays to all directions, not just lights, from the object surfaces. Though lights are not rendered, they still exist, their position and orientation is known and their properties are there for the purpose of rendering.

    and where on the object would you shoot from?

    Everywhere. The ray with a direction can detect if it is on the obscured side of the object or if it can see a light. It is all bout deciding what to do with that information. Most 3D-objects exist only as sets of RTTriangles on the raytracing scene. The Raytracer actually builds a parallel copy of the modelling scene out of RT-objects, that can interact with rays. The needed tools are in there.

    project the caustic-generators' 2D shape

    Playing with this idea a bit...

    At first thought, creating the projection shape would seem kind of an unnecessary step: While you spend time on projecting triangles on a plane between the object and the light, you might just as well check the direction of the light in relation to the orientation of the surface. Just like in all other parts of raytracing rendering.

    Having said that, if you have a flat (or spherical to that matter) projection of an object, then you'd avoid recalculating some parameters again for every ray.... And there should be millions of them for photon mapping.

    I have thought of different variants of projections too for a couple of different purposes. A model for the math is hidden in the code already: The interactive rendering during modelling just projects triangles on a "screen" inside a "camera". Actually I never thought of it but it'd probably be easiest to configure a Camera to create projection silhouettes of objects... Can't remember how fixed the Camera is with views, but that should be easy to overcome ... like public class Projector extends Camera{} -- And if you have a flat (or spherical to that matter) projection of an object, then you'd avoid recalculating some parameters again for every ray....

    Back to Earth... The ideas are intriguing but realistically, I don't see myself digging very deep into the code any time soon. :)

     
  • ma0mpt

    ma0mpt - 2023-05-02

    In the case of the directional light source, generating photons for the photon map, I am mulling over an idea that I think has potential to be more efficient than blasting rays from every part of the the bounding box face. Just a baby idea gurgling at me from its cot at the moment...

     
  • ma0mpt

    ma0mpt - 2023-05-08

    OK so the gurgling baby idea became an infant.

    I have played around a lot with the DirectionalPhotonSource while trying to find out how it worked. This class uses a large bounding box passed to it with the PhotonMap. I discovered that if you place 4 caustic generating spheres at positions in the scene so as to create a huge bounding box then the stratified sampling algorithm bogs down while shooting photons. It becomes unusable. The sample scene has 4 such spheres in it. If you try rendering with caustics enabled I think you will be waiting forever... so hide these objects before rendering in an unmodified version of the application.

    My goal was to follow up on the projection idea by only shooting photons at places where there were caustic generators, and avoiding shooting into empty space. I managed to achieve this by passing a list of caustic generator bounding boxes to the DirectionalPhotonSource and making it iterate over them. I had to adjust the number of photons per object according to relative area of the bounding box faces. So the DirectionalPhotonSource is more efficient as it does not shoot into empty space, and it will cope easily with the dummy caustic spheres in the scene.

    I modified the PhotonMap class to contain a list of bounding boxes which was populated by the buildPhotonMap() method of RaytracerRenderer. This list was then automatically passed to the
    DirectionalPhotonSource which then extracts the bounding boxes and shoots photons through them. I added a couple of utility methods to recalculate bounds and ray origins.

    So I made changes to to PhotonMap, RaytracerRenderer, and DirectionalPhotonSource. These changes effectively enable caustic generators to be placed at very large distances from each other without causing the stratified sampling algorithm to choke due to a single huge bounding box size. NB, this affects directional lighting. I have not looked at other light types.

    The changed files and a sample scene are attached.

    Obviously just a POC, and the way of selecting caustic generators relies upon an object containing the word 'caustics' in its name. Been a good learning experience though.

     

    Last edit: ma0mpt 2023-05-08
  • Peter Eastman

    Peter Eastman - 2023-05-08

    Good idea. Be sure you do it in a way that doesn't alter the light distribution. Equal numbers of photons should be sent from every point on the source, even if some of the objects you're shooting toward are larger than others, and even if they overlap.

     
  • Maksim Khramov

    Maksim Khramov - 2023-05-10

    Without applying this code patches this test scene is load my processor with 100% for very long time at calculating photon map stage. Not finished but I just killed process.

     
  • ma0mpt

    ma0mpt - 2023-05-11

    Equal numbers of photons should be sent from every point on the source, even if some of the objects you're shooting toward are larger than others, and even if they overlap.

    Ok, so what I have done probably violates that criterion, because if two objects are in-line as seen from the light source they will get two lots of photons shot at them. I think I can fix that though.

     

    Last edit: ma0mpt 2023-05-11

Log in to post a comment.