@@ -441,82 +441,170 @@ if constexpr (std::is_same_v<decltype(VALUE),bool>) \
441441 ASSERT_VALUE (forest->addMaterial (layerH,logger),true ," ThinDielectric" );
442442 }
443443
444- // plastics with IOR 1.5
444+ // compled materials with coatings with IOR 1.5
445445 {
446- // make the node everyone shares
446+ // make the nodes everyone shares
447+ const auto roughDiffuseH = forest->_new <CFrontendIR::CMul>();
448+ {
449+ auto * mul = forest->deref (roughDiffuseH);
450+ {
451+ const auto orenNayarH = forest->_new <CFrontendIR::COrenNayar>();
452+ auto * orenNayar = forest->deref (orenNayarH);
453+ orenNayar->ndParams .getRougness ()[0 ].scale = orenNayar->ndParams .getRougness ()[1 ].scale = 0 .2f ;
454+ mul->lhs = orenNayarH;
455+ }
456+ {
457+ spectral_var_t ::SCreationParams<3 > params = {};
458+ params.getSemantics () = spectral_var_t ::Semantics::Fixed3_SRGB;
459+ params.knots .params [0 ].scale = 0 .9f ;
460+ params.knots .params [1 ].scale = 0 .6f ;
461+ params.knots .params [2 ].scale = 0 .01f ;
462+ const auto albedoH = forest->_new <CFrontendIR::CSpectralVariable>(std::move (params));
463+ forest->deref (albedoH)->debugInfo = forest->_new <CNodePool::CDebugInfo>(" Albedo" );
464+ mul->rhs = albedoH;
465+ }
466+ }
447467 const auto fresnelH = forest->_new <CFrontendIR::CFresnel>();
448468 {
449469 auto * fresnel = forest->deref (fresnelH);
450470 spectral_var_t ::SCreationParams<1 > params = {};
451471 params.knots .params [0 ].scale = 1 .5f ;
452472 fresnel->orientedRealEta = forest->_new <CFrontendIR::CSpectralVariable>(std::move (params));
453473 }
474+ // the delta layering should optimize out nicely due to the sampling property
475+ const auto transH = forest->_new <CFrontendIR::CMul>();
476+ {
477+ auto * mul = forest->deref (transH);
478+ mul->lhs = forest->_new <CFrontendIR::CDeltaTransmission>();
479+ mul->rhs = fresnelH;
480+ }
481+ // can't attach a copy of the top layer because we'll have a cycle, also the BRDF needs to be on the other side
482+ const auto bottomH = forest->_new <CFrontendIR::CLayer>();
483+ {
484+ auto * bottomLayer = forest->deref (bottomH);
485+ bottomLayer->debugInfo = forest->_new <CNodePool::CDebugInfo>(" Rough Coating Copy" );
486+ // no brdf on the top of last layer, kill multiscattering
487+ bottomLayer->btdf = transH;
488+ bottomLayer->brdfBottom = dielectricH;
489+ }
454490
455- // rough plastic
491+ // twosided rough plastic
456492 {
457493 const auto rootH = forest->_new <CFrontendIR::CLayer>();
458494 auto * topLayer = forest->deref (rootH);
459- topLayer->debugInfo = forest->_new <CNodePool::CDebugInfo>(" Rough Plastic" );
495+ topLayer->debugInfo = forest->_new <CNodePool::CDebugInfo>(" Twosided Rough Plastic" );
460496
461497 topLayer->brdfTop = dielectricH;
462- // the delta layering should optimize out nicely due to the sampling property
463- const auto transH = forest->_new <CFrontendIR::CMul>();
464- {
465- auto * mul = forest->deref (transH);
466- mul->lhs = forest->_new <CFrontendIR::CDeltaTransmission>();
467- mul->rhs = fresnelH;
468- }
469498 topLayer->btdf = transH;
499+ // no brdf on the bottom of first layer, kill multiscattering
470500
471501 const auto diffuseH = forest->_new <CFrontendIR::CLayer>();
472502 auto * midLayer = forest->deref (diffuseH);
473503 {
474- const auto mulH = forest->_new <CFrontendIR::CMul>();
475- {
476- auto * mul = forest->deref (mulH);
477- {
478- const auto orenNayarH = forest->_new <CFrontendIR::COrenNayar>();
479- auto * orenNayar = forest->deref (orenNayarH);
480- orenNayar->ndParams .getRougness ()[0 ].scale = orenNayar->ndParams .getRougness ()[1 ].scale = 0 .2f ;
481- mul->lhs = orenNayarH;
482- }
483- {
484- spectral_var_t ::SCreationParams<3 > params = {};
485- params.getSemantics () = spectral_var_t ::Semantics::Fixed3_SRGB;
486- params.knots .params [0 ].scale = 0 .9f ;
487- params.knots .params [1 ].scale = 0 .6f ;
488- params.knots .params [2 ].scale = 0 .01f ;
489- const auto albedoH = forest->_new <CFrontendIR::CSpectralVariable>(std::move (params));
490- forest->deref (albedoH)->debugInfo = forest->_new <CNodePool::CDebugInfo>(" Albedo" );
491- mul->rhs = albedoH;
492- }
493- }
494- midLayer->brdfTop = mulH;
504+ midLayer->brdfTop = roughDiffuseH;
495505 // no transmission in the mid-layer, the backend needs to decompose into separate front/back materials
496- midLayer->brdfBottom = mulH;
497-
498- // can't attach a copy of the top layer because we'll have a cycle, also the BRDF needs to be on the other side
499- const auto bottomH = forest->_new <CFrontendIR::CLayer>();
500- {
501- auto * bottomLayer = forest->deref (bottomH);
502- bottomLayer->debugInfo = forest->_new <CNodePool::CDebugInfo>(" Rough Plastic Copy" );
503- bottomLayer->brdfBottom = dielectricH;
504- }
506+ midLayer->brdfBottom = roughDiffuseH;
505507 midLayer->coated = bottomH;
506508 }
507509 topLayer->coated = diffuseH;
508510
509511 ASSERT_VALUE (forest->addMaterial (rootH,logger),true ," Twosided Rough Plastic" );
510512 }
511513
514+ // Diffuse transmitter normalized to whoel sphere
515+ const auto roughDiffTransH = forest->_new <CFrontendIR::CMul>();
516+ {
517+ // normalize the Oren Nayar over the full sphere
518+ auto * mul = forest->deref (roughDiffTransH);
519+ mul->lhs = roughDiffuseH;
520+ {
521+ spectral_var_t ::SCreationParams<1 > params = {};
522+ params.knots .params [0 ].scale = 0 .5f ;
523+ mul->rhs = forest->_new <CFrontendIR::CSpectralVariable>(std::move (params));
524+ }
525+ }
526+
512527 // coated diffuse transmitter
513528 {
514- //
529+ const auto rootH = forest->_new <CFrontendIR::CLayer>();
530+ auto * topLayer = forest->deref (rootH);
531+ topLayer->debugInfo = forest->_new <CNodePool::CDebugInfo>(" Coated Diffuse Transmitter" );
532+
533+ topLayer->brdfTop = dielectricH;
534+ topLayer->btdf = transH;
535+ // no brdf on the bottom of first layer, kill multiscattering
536+
537+ const auto midH = forest->_new <CFrontendIR::CLayer>();
538+ auto * midLayer = forest->deref (midH);
539+ {
540+ midLayer->brdfTop = roughDiffTransH;
541+ midLayer->btdf = roughDiffTransH;
542+ midLayer->brdfBottom = roughDiffTransH;
543+
544+ // we could even have a BSDF with a different Fresnel and Roughness on the bottom layer!
545+ midLayer->coated = bottomH;
546+ }
547+ topLayer->coated = midH;
548+
549+ ASSERT_VALUE (forest->addMaterial (rootH,logger),true ," Coated Diffuse Transmitter" );
515550 }
516551
517- // same thing but with subsurface beer scattering
552+ // same thing but with subsurface beer absorption
518553 {
519- //
554+ const auto rootH = forest->_new <CFrontendIR::CLayer>();
555+ auto * topLayer = forest->deref (rootH);
556+ topLayer->debugInfo = forest->_new <CNodePool::CDebugInfo>(" Coated Diffuse Extinction Transmitter" );
557+
558+ // we have a choice of where to stick the Beer Absorption:
559+ // - on the BTDF of the outside layer, means that it will be applied to the transmission so twice according to VdotN and LdotN
560+ // (but delta transmission makes special weight nodes behave in a special and only once because `L=-V` is forced in a single scattering)
561+ // - inner layer BRDF or BTDF but thats intractable for most compiler backends because the `L` and `V` in the internal layers are not trivially known
562+ // unless the previous layers are delta distributions (in which case we can equivalently hoist beer to the previous layer).
563+ const auto beerH = forest->_new <CFrontendIR::CBeer>();
564+ {
565+ auto * beer = forest->deref (beerH);
566+ spectral_var_t ::SCreationParams<3 > params = {};
567+ params.getSemantics () = spectral_var_t ::Semantics::Fixed3_SRGB;
568+ params.knots .params [0 ].scale = 0 .3f ;
569+ params.knots .params [1 ].scale = 0 .9f ;
570+ params.knots .params [2 ].scale = 0 .7f ;
571+ beer->perpTransparency = forest->_new <spectral_var_t >(std::move (params));
572+ }
573+
574+ topLayer->brdfTop = dielectricH;
575+ // simplest/recommended
576+ {
577+ const auto transAbsorbH = forest->_new <CFrontendIR::CMul>();
578+ auto * transAbsorb = forest->deref (transAbsorbH);
579+ transAbsorb->lhs = transH;
580+ {
581+ transAbsorb->rhs = beerH;
582+ }
583+ topLayer->btdf = transAbsorbH;
584+ }
585+
586+ const auto midH = forest->_new <CFrontendIR::CLayer>();
587+ auto * midLayer = forest->deref (midH);
588+ {
589+ midLayer->brdfTop = roughDiffTransH;
590+ midLayer->btdf = roughDiffTransH;
591+ // making extra work for our canonicalizer
592+ {
593+ const auto roughAbsorbH = forest->_new <CFrontendIR::CMul>();
594+ auto * transAbsorb = forest->deref (roughAbsorbH);
595+ transAbsorb->lhs = roughDiffTransH;
596+ {
597+ transAbsorb->rhs = beerH;
598+ }
599+ midLayer->brdfBottom = roughAbsorbH;
600+ }
601+
602+ // we could even have a BSDF with a different Fresnel and Roughness on the bottom layer!
603+ midLayer->coated = bottomH;
604+ }
605+ topLayer->coated = midH;
606+
607+ ASSERT_VALUE (forest->addMaterial (rootH,logger),true ," Coated Diffuse Extinction Transmitter" );
520608 }
521609 }
522610
0 commit comments