@@ -7,7 +7,7 @@ use rand_pcg::Pcg64Mcg;
77
88use basis:: OrthonormalBasis ;
99use results:: { HitResult , ScatterResult } ;
10- use ggx:: { ggx_distribution, ggx_geometry, ggx_sample_vndf } ;
10+ use ggx:: { ggx_distribution, ggx_geometry} ;
1111use pdf:: PDF ;
1212use ray:: { find_offset_point, Ray } ;
1313use sampling:: pick_sphere_point;
@@ -541,10 +541,7 @@ impl Plastic {
541541
542542 /// Generate either a specular or diffuse response to a surface hit, dependent
543543 /// on a random reflect probability.
544- fn generate_response ( & self , ray : & Ray , result : & HitResult , rng : & mut Pcg64Mcg ) -> Option < ScatterResult > {
545- let cos_theta_i = ( -ray. direction ) . dot ( result. shading_normal ) . max ( 0.0 ) ;
546- let fresnel = schlick_from_ior ( cos_theta_i, self . ior ) ;
547-
544+ fn generate_response ( & self , ray : & Ray , result : & HitResult , _rng : & mut Pcg64Mcg ) -> Option < ScatterResult > {
548545 let geometric_normal = if ray. direction . dot ( result. geometric_normal ) < 0.0 {
549546 result. geometric_normal
550547 } else {
@@ -557,49 +554,56 @@ impl Plastic {
557554 result. shading_normal
558555 } ;
559556
560- let offset_point = find_offset_point ( result. point , geometric_normal) ;
561-
562- // probabilistically pick specular or diffuse based on Fresnel.
563- // this material has two lobes, specular and diffuse. in the future
564- // we need to work on returning both lobes rather than picking one randomly.
565- if rng. random :: < f32 > ( ) < fresnel {
566- // this is the specular path
567- let alpha = self . roughness ;
568- let microfacet_normal = ggx_sample_vndf ( & shading_normal, & -ray. direction , & alpha, rng) ;
569- let reflected = reflect ( ray. direction , microfacet_normal) ;
570-
571- if shading_normal. dot ( reflected) <= 0.0 {
572- return None ;
573- }
557+ let cos_theta_i = ( -ray. direction ) . dot ( result. shading_normal ) . max ( 0.0 ) ;
558+ let fresnel = schlick_from_ior ( cos_theta_i, self . ior ) ;
574559
575- let specular_ray = Ray :: new ( offset_point, reflected) ;
576- let pdf = PDF :: GGX { wi : -ray. direction , normal : shading_normal, alpha } ;
560+ let offset_point = find_offset_point ( result. point , geometric_normal) ;
561+ let scattered_ray = Ray :: new ( offset_point, ray. direction ) ;
562+
563+ let pdf = PDF :: Composite {
564+ uvw : OrthonormalBasis :: new ( & shading_normal) ,
565+ wi : -ray. direction ,
566+ normal : shading_normal,
567+ alpha : self . roughness ,
568+ specular_weight : fresnel,
569+ } ;
577570
578- Some ( ScatterResult :: new ( specular_ray, Vec3A :: ONE , pdf, true , true ) )
579- } else {
580- // this is the diffuse path
581- // even though ray.direction is given, a new ray with offset is generated in the integrator
582- let scattered_ray = Ray :: new ( offset_point, ray. direction ) ;
583- let contribution = self . albedo . sample_texture ( result. u , result. v , & result. point ) ;
584- let pdf = PDF :: Cosine { uvw : OrthonormalBasis :: new ( & result. shading_normal ) } ;
585- Some ( ScatterResult :: new ( scattered_ray, contribution, pdf, false , false ) )
586- }
571+ Some ( ScatterResult :: new ( scattered_ray, Vec3A :: ONE , pdf, false , false ) )
587572 }
588573
589574 /// Compute how the Plastic material handles reflectance.
590- fn compute_reflectance ( & self , wo : & Ray , result : & HitResult , wi : & Ray ) -> Vec3A {
591- // we only need to compute the reflectance for the diffuse branch since
592- // the specular branch is pre_weighted and this method is only called
593- // for non-pre_weighted branches
575+ fn compute_reflectance ( & self , ray : & Ray , result : & HitResult , scattered : & Ray ) -> Vec3A {
576+ if self . roughness == 0.0 { return Vec3A :: ZERO ; }
577+
578+ let wi = -ray. direction ;
579+ let wo = scattered. direction ;
594580 let n = result. shading_normal ;
595581
596- let cos_o = n. dot ( wi. direction ) ;
597- let cos_theta_i = ( -wo . direction ) . dot ( n ) ;
582+ let cos_i = n. dot ( wi) ;
583+ let cos_o = n . dot ( wo ) ;
598584
599- if cos_o <= 0.0 || cos_theta_i < 0.0 {
585+ if cos_i <= 0.0 || cos_o <= 0.0 {
600586 return Vec3A :: ZERO ;
601587 }
602588
603- Vec3A :: splat ( cos_o / PI )
589+ let h = ( wi + wo) . normalize ( ) ;
590+ let cos_h = n. dot ( h) ;
591+
592+ if cos_h <= 0.0 {
593+ return Vec3A :: ZERO ;
594+ }
595+
596+ let v_dot_h = wi. dot ( h) . max ( 0.0 ) ;
597+ let micro_fresnel = schlick_from_ior ( v_dot_h, self . ior ) ;
598+
599+ let d = ggx_distribution ( cos_h, self . roughness ) ;
600+ let g = ggx_geometry ( cos_i, cos_o, self . roughness ) ;
601+ let specular = Vec3A :: splat ( micro_fresnel * d * g / ( 4.0 * cos_i) ) ;
602+
603+ let macro_fresnel = schlick_from_ior ( cos_i, self . ior ) ;
604+ let albedo = self . albedo . sample_texture ( result. u , result. v , & result. point ) ;
605+ let diffuse = albedo * ( 1.0 - macro_fresnel) * cos_o / PI ;
606+
607+ specular + diffuse
604608 }
605609}
0 commit comments