mmp / pbrt-v3

Source code for pbrt, the renderer described in the third edition of "Physically Based Rendering: From Theory To Implementation", by Matt Pharr, Wenzel Jakob, and Greg Humphreys.
http://pbrt.org
BSD 2-Clause "Simplified" License
4.89k stars 1.19k forks source link

bssrdf problem when shape's normal is reverted #269

Open wangchi87 opened 4 years ago

wangchi87 commented 4 years ago

Hi, I am learning the bssrdf code of PBRT, and possibly found a problem.

The problem occurs when I was trying to render a sqare with BSSRDF material. The original scene file is from the material test ball scene from Tungsten rendering resource page. And I remove the all of objects except a floor.

The strange thing is when I set subsurface material to the floor, the rendering result is totaly black. The original material for the floor is a lambert, which, of course, show something in the result.

Then, I tried to look into the shape of the floor, the floor actually has a normal facing downwards , i.e. the normal is [0 0 -1] . The rendering result looks OK when material is Lamber and floor facing downwards, which means pbrt works with "two sided shading", right? But, it seems that bssdf has a problem here. (bssrdf works, if I revert the floor's normal manually, i.e. make the normal [0 0 1])

I guess, the reason for this "black floor" is as following: The bssrdf has a bsdf to sample reflection or transmission at the boudary at po, i.e. bssrdf will only happen when a transmission is sampled. The reason for being black is that transmission actually can NOT be sampled at boundary.

I guess, there might be an inconsistency regarding the direction defined for wo in the following code.

Spectrum FresnelSpecular::Sample_f(
              const Vector3f &wo, 
              Vector3f *const Point2f &u, 
              Float *BxDFType *sampledType) const {

Float F = FrDielectric(CosTheta(wo), etaA, etaB);        <---- here F is always 1

if (u[0] < F) {
   ……

when the floor is facing downwards, wo here has z value that is less than zero; but here, FrDielectric() use the sign of wo.z to judge if the ray is entering or exiting the boundary.

The "black floor" is due to that for the case wo.z < 0, and the program thought the ray is exiting the boudary, and then total reflection happens here (F =1), and no transmission sampled for bssrdf! so the image is "all black".

the following the scene file that trigger this problem, uncomment the mesh to see the problem

--------------- scene file ------------

Integrator "path" "integer maxdepth" [ 10 ] Transform [ 0.721367 -0.373123 -0.583445 -0 -0 0.842456 -0.538765 -0 -0.692553 -0.388647 -0.60772 -0 0.0258668 -0.29189 5.43024 1] Sampler "random" "integer pixelsamples" [ 16 ] PixelFilter "triangle" "float xwidth" [ 1.000000 ] "float ywidth" [ 1.000000 ] Film "image" "integer xresolution" [ 512 ] "integer yresolution" [ 512 ] "string filename" [ "sss_problem.exr" ] Camera "perspective" "float fov" [ 20.114292 ]

WorldBegin

    MakeNamedMaterial "sss_1" 
    "string type" [ "subsurface" ]  
    "float g" [0.0]
    "float eta" [1.5]
    "string name" ["Skin1"]
    "float scale" [1]
    "rgb Kr" [0 0 0]

MakeNamedMaterial "sss_10" 
    "string type" [ "subsurface" ]  
    "float g" [0.0]
    "float eta" [1.5]
    "string name" ["Skin1"]
    "float scale" [10]

LightSource "point" "point from" [0 5 0] "rgb I" [100 100 100]

NamedMaterial "sss_1" 

# bssrdf does NOT works
#Shape "trianglemesh" "integer indices" [ 0 1 2 0 2 3 ] "point P" [ -0.785994 0 3.11108 -4.55196 -4.75246e-007 -0.80933 -0.63155 0 -4.57529 3.13441 4.75246e-007 -0.654886 ] "normal N" [ 0 -1 0 0 -1 0 0 -1 0 0 -1 0 ] "float uv" [ 0 0 1 0 1 1 0 1 ] 

# bssrdf works, if I revert the floor's normal manually
Shape "trianglemesh" "integer indices" [ 0 1 2 0 2 3 ] "point P" [ -0.785994 0 3.11108 -4.55196 -4.75246e-007 -0.80933 -0.63155 0 -4.57529 3.13441 4.75246e-007 -0.654886 ] "normal N" [ 0 1 0 0 1 0 0 1 0 0 1 0 ] "float uv" [ 0 0 1 0 1 1 0 1 ] 

WorldEnd