Photon Engine 2.0.0-beta
A physically based renderer.
Loading...
Searching...
No Matches
TDirectLightEstimator.ipp
Go to the documentation of this file.
2#include "World/Scene.h"
6#include "Core/SurfaceHit.h"
14#include "Core/HitProbe.h"
15#include "Core/HitDetail.h"
16#include "Core/Ray.h"
17#include "Core/LTA/lta.h"
19#include "Core/LTA/TMIS.h"
21#include "Math/TVector3.h"
22
23#include <Common/assertion.h>
24
25#include <algorithm>
26#include <limits>
27
28namespace ph::lta
29{
30
31template<ESidednessPolicy POLICY>
33 : m_scene(scene)
34{
35 PH_ASSERT(scene);
36}
37
38template<ESidednessPolicy POLICY>
40 BsdfSampleQuery& bsdfSample,
41 SampleFlow& sampleFlow,
42 math::Spectrum* const out_Le,
43 std::optional<SurfaceHit>* const out_X) const
44{
45 SurfaceHit nextX;
46 const bool foundNextX = SurfaceTracer{m_scene}.bsdfSampleNextSurface(bsdfSample, sampleFlow, &nextX);
47 if(!bsdfSample.outputs.isMeasurable())
48 {
49 return false;
50 }
51
52 math::Spectrum Le(0);
53 if(foundNextX)
54 {
55 const Emitter* nextEmitter = nextX.getSurfaceEmitter();
56 if(nextEmitter && nextEmitter->getFeatureSet().has(EEmitterFeatureSet::BsdfSample))
57 {
58 nextX.getSurfaceEmitter()->evalEmittedEnergy(nextX, &Le);
59 }
60 }
61
62 PH_ASSERT_IN_RANGE(bsdfSample.outputs.getL().lengthSquared(), 0.9_r, 1.1_r);
63
64 if(out_Le) { *out_Le = Le; }
65 if(out_X) { *out_X = foundNextX ? std::make_optional(nextX) : std::nullopt; }
66
67 return true;
68}
69
70template<ESidednessPolicy POLICY>
72 DirectEnergySampleQuery& directSample,
73 SampleFlow& sampleFlow,
74 SurfaceHit* const out_Xe) const
75{
76 PH_ASSERT(isNeeSamplable(directSample.inputs.getX()));
77
78 const SidednessAgreement sidedness{POLICY};
79 const SurfaceHit& X = directSample.inputs.getX();
80
81 HitProbe probe;
82 getScene().genDirectSample(directSample, sampleFlow, probe);
83 if(!directSample.outputs || !sidedness.isSidednessAgreed(X, directSample.getTargetToEmit()))
84 {
85 return false;
86 }
87
89 const SurfaceHit Xe(directSample.outputs.getObservationRay(), probe, reason);
90 const auto optVisibilityRay = SurfaceHitRefinery{X}.tryEscape(Xe);
91 if(!optVisibilityRay || getScene().isOccluding(*optVisibilityRay))
92 {
93 return false;
94 }
95
96 PH_ASSERT_IN_RANGE(optVisibilityRay->getDir().lengthSquared(), 0.9_r, 1.1_r);
97 PH_ASSERT(Xe.getSurfaceEmitter());
98
99 if(out_Xe) { *out_Xe = Xe; }
100
101 return true;
102}
103
104template<ESidednessPolicy POLICY>
106 BsdfSampleQuery& bsdfSample,
107 SampleFlow& sampleFlow,
108 math::Spectrum* const out_Lo,
109 std::optional<SurfaceHit>* const out_X) const
110{
111 using MIS = TMIS<EMISStyle::Power>;
112
113 const SurfaceHit& X = bsdfSample.inputs.getX();
114 const math::Vector3R V = X.getIncidentRay().getDir().mul(-1);
115 const math::Vector3R N = X.getShadingNormal();
116 math::Spectrum sampledLo(0);
117
118 // BSDF sample
119 {
120 math::Spectrum bsdfLe;
121 std::optional<SurfaceHit> nextX;
122 if(bsdfSampleEmission(bsdfSample, sampleFlow, &bsdfLe, &nextX) &&
123 bsdfSample.outputs.isMeasurable() &&
124 nextX)
125 {
126 const SurfaceOptics* optics = X.getSurfaceOptics();
127 PH_ASSERT(optics);
128
129 const auto pdfAppliedBsdfCos = bsdfSample.outputs.getPdfAppliedBsdfCos();
130
131 // If NEE cannot sample the same light from `X` (due to delta BSDF, etc.), then we
132 // cannot use MIS weighting to remove NEE contribution as BSDF sampling may not
133 // always have an explicit PDF term.
134
135 // MIS
136 if(isNeeSamplable(X) && nextX->getSurfaceEmitter())
137 {
138 // No need to test occlusion again as `bsdfSampleEmission()` already done that
139 const real neePdfW = neeSamplePdfWUnoccluded(X, *nextX);
140
141 BsdfPdfQuery bsdfPdfQuery{bsdfSample.context};
142 bsdfPdfQuery.inputs.set(bsdfSample);
143 optics->calcBsdfPdf(bsdfPdfQuery);
144
145 // `isNeeSamplable()` is already checked, but BSDF PDF can still be empty or 0
146 // (e.g., sidedness policy or by the distribution itself)
147 if(bsdfPdfQuery.outputs)
148 {
149 const real bsdfSamplePdfW = bsdfPdfQuery.outputs.getSampleDirPdfW();
150 const real misWeighting = MIS{}.weight(bsdfSamplePdfW, neePdfW);
151
152 math::Spectrum weight(pdfAppliedBsdfCos * misWeighting);
153
154 // Avoid excessive, negative weight and possible NaNs
155 weight.safeClampLocal(0.0_r, 1e9_r);
156
157 sampledLo += bsdfLe * weight;
158 }
159 }
160 // BSDF sample only
161 else
162 {
163 sampledLo += bsdfLe * pdfAppliedBsdfCos;
164 }
165 }
166
167 // If BSDF sampling failed for whatever reason, we cannot simply return `false`
168 // as NEE could still sample a non-zero outgoing energy
169 if(out_X) { *out_X = nextX; }
170 }
171
172 // NEE
173 if(isNeeSamplable(X))
174 {
175 DirectEnergySampleQuery directSample;
176 directSample.inputs.set(bsdfSample.inputs.getX());
177 if(neeSampleEmission(directSample, sampleFlow) &&
178 directSample.outputs)
179 {
180 // Always do MIS. If NEE can sample a light from `X`, then BSDF light sample should have
181 // no problem doing the same. No need to consider delta light sources as Photon do not
182 // have them.
183
184 const SurfaceOptics* optics = X.getSurfaceOptics();
185 PH_ASSERT(optics);
186
187 BsdfEvalQuery bsdfEval{bsdfSample.context};
188 bsdfEval.inputs.set(X, directSample.getTargetToEmit().normalize(), V);
189 optics->calcBsdf(bsdfEval);
190 if(bsdfEval.outputs.isMeasurable())
191 {
192 BsdfPdfQuery bsdfPdfQuery{bsdfSample.context};
193 bsdfPdfQuery.inputs.set(bsdfEval.inputs);
194 optics->calcBsdfPdf(bsdfPdfQuery);
195 if(bsdfPdfQuery.outputs)
196 {
197 const auto L = bsdfEval.inputs.getL();
198 const real neePdfW = directSample.outputs.getPdfW();
199 const real bsdfSamplePdfW = bsdfPdfQuery.outputs.getSampleDirPdfW();
200 const real misWeighting = MIS{}.weight(neePdfW, bsdfSamplePdfW);
201
202 math::Spectrum weight(bsdfEval.outputs.getBsdf() * N.absDot(L) * misWeighting / neePdfW);
203
204 // Avoid excessive, negative weight and possible NaNs
205 weight.safeClampLocal(0.0_r, 1e9_r);
206
207 sampledLo += directSample.outputs.getEmittedEnergy() * weight;
208 }
209 }
210 }
211 }
212
213 if(out_Lo) { *out_Lo = sampledLo; }
214
215 return true;
216}
217
218template<ESidednessPolicy POLICY>
220 const SurfaceHit& X,
221 const SurfaceHit& Xe) const
222{
223 PH_ASSERT(isNeeSamplable(X));
224 PH_ASSERT(Xe.getSurfaceEmitter());
225
226 DirectEnergyPdfQuery pdfQuery;
227 pdfQuery.inputs.set(X, Xe);
228 getScene().calcDirectPdf(pdfQuery);
229 return pdfQuery.outputs ? pdfQuery.outputs.getPdfW() : 0;
230}
231
232template<ESidednessPolicy POLICY>
234{
235 const SurfaceOptics* optics = X.getSurfaceOptics();
236 return optics && optics->getAllPhenomena().hasNone(DELTA_SURFACE_PHENOMENA);
237}
238
239template<ESidednessPolicy POLICY>
241{
242 PH_ASSERT(m_scene);
243
244 return *m_scene;
245}
246
247}// end namespace ph::lta
Information for obtaining a sample value from BSDF.
Definition BsdfEvalQuery.h:90
Information for the probability of generating a specific BSDF sample.
Definition BsdfPdfQuery.h:66
const SurfaceHit & getX() const
Definition BsdfSampleQuery.h:179
const math::Vector3R & getL() const
Definition BsdfSampleQuery.h:215
const math::Spectrum & getPdfAppliedBsdfCos() const
Definition BsdfSampleQuery.h:237
bool isMeasurable() const
Tells whether this sample has potential to contribute. All sampled data should be usable if true is r...
Definition BsdfSampleQuery.h:260
Information for generating a BSDF sample.
Definition BsdfSampleQuery.h:141
Input inputs
Definition BsdfSampleQuery.h:147
BsdfQueryContext context
Definition BsdfSampleQuery.h:146
Output outputs
Definition BsdfSampleQuery.h:148
void set(const SurfaceHit &X, const SurfaceHit &Xe)
Definition DirectEnergyPdfQuery.h:92
real getPdfW() const
Definition DirectEnergyPdfQuery.h:149
Information for the probability of generating a specific sample for direct energy estimation.
Definition DirectEnergyPdfQuery.h:81
Output outputs
Definition DirectEnergyPdfQuery.h:87
Input inputs
Definition DirectEnergyPdfQuery.h:86
const SurfaceHit & getX() const
Definition DirectEnergySampleQuery.h:112
void set(const SurfaceHit &X)
Definition DirectEnergySampleQuery.h:103
const math::Spectrum & getEmittedEnergy() const
The sampled emitted energy of. Does not contain any path weighting.
Definition DirectEnergySampleQuery.h:154
const Ray & getObservationRay() const
Get the ray from target position to sampled emitting position. If target position or emitting positio...
Definition DirectEnergySampleQuery.h:179
real getPdfW() const
Definition DirectEnergySampleQuery.h:160
Information for generating a sample for direct energy estimation.
Definition DirectEnergySampleQuery.h:89
math::Vector3R getTargetToEmit() const
Definition DirectEnergySampleQuery.h:190
Output outputs
Definition DirectEnergySampleQuery.h:95
Input inputs
Definition DirectEnergySampleQuery.h:94
An electromagnetic energy emitting source. The emitted energy can be captured by a Receiver.
Definition Emitter.h:68
EmitterFeatureSet getFeatureSet() const
Definition Emitter.h:129
virtual void evalEmittedEnergy(const SurfaceHit &Xe, math::Spectrum *out_energy) const =0
Evaluate emitted energy from a point on the surface.
Lightweight ray intersection testing and reporting object. If an intersection is found,...
Definition HitProbe.h:27
const math::Vector3R & getDir() const
Definition Ray.h:214
A sample with arbitrary dimensions with fine-grained sampling control.
Definition SampleFlow.h:19
A unified interface for accessing cooked content in a visual world.
Definition Scene.h:27
General information about a ray-surface intersection event.
Definition SurfaceHit.h:59
math::Vector3R getShadingNormal() const
Definition SurfaceHit.h:191
const Ray & getIncidentRay() const
Convenient method for getRay() where getReason() contains ESurfaceHitReason::IncidentRay.
Definition SurfaceHit.h:175
const Emitter * getSurfaceEmitter() const
Definition SurfaceHit.cpp:61
const SurfaceOptics * getSurfaceOptics() const
Definition SurfaceHit.cpp:67
Describes how light interacts with a surface.
Definition SurfaceOptics.h:17
void calcBsdf(BsdfEvalQuery &eval) const
Executes a BSDF evaluation query. Respects sidedness policy.
Definition SurfaceOptics.cpp:17
void calcBsdfPdf(BsdfPdfQuery &pdfQuery) const
Executes a BSDF sample PDF query. Respects sidedness policy.
Definition SurfaceOptics.cpp:54
SurfacePhenomena getAllPhenomena() const
Get all phenomena that exist in this surface.
Definition SurfaceOptics.h:94
Manipulate a value type where each bit is a binary flag.
Definition TBitFlags.h:17
constexpr bool has(Input singleFlag) const
Checks whether this single flag is fully contained.
Definition TBitFlags.ipp:133
constexpr bool hasNone(const FlagsSet &flagsSet) const
Checks whether this instance contains no specified flags.
Definition TBitFlags.ipp:81
Definition SidednessAgreement.h:32
Algorithms for various hit point adjustments. For surface escaping routines, the generated ray is not...
Definition SurfaceHitRefinery.h:31
Common operations for surface tracing. This class also handles many subtle cases for surface tracing....
Definition SurfaceTracer.h:35
bool bsdfSampleNextSurface(BsdfSampleQuery &bsdfSample, SampleFlow &sampleFlow, SurfaceHit *out_X) const
Uses BSDF sample to trace the next surface.
Definition SurfaceTracer.h:158
Estimate direct lighting for a surface point. This is a lightweight helper type for estimating direct...
Definition TDirectLightEstimator.h:31
bool bsdfSamplePathWithNee(BsdfSampleQuery &bsdfSample, SampleFlow &sampleFlow, math::Spectrum *out_Lo=nullptr, std::optional< SurfaceHit > *out_X=nullptr) const
Sample lighting by combining the techniques used by bsdfSample() and neeSample(). A light sampling te...
Definition TDirectLightEstimator.ipp:105
bool bsdfSampleEmission(BsdfSampleQuery &bsdfSample, SampleFlow &sampleFlow, math::Spectrum *out_Le=nullptr, std::optional< SurfaceHit > *out_X=nullptr) const
Sample lighting using BSDF's suggestion. A light sampling technique that is always valid.
Definition TDirectLightEstimator.ipp:39
TDirectLightEstimator(const Scene *scene)
Definition TDirectLightEstimator.ipp:32
bool isNeeSamplable(const SurfaceHit &X) const
Definition TDirectLightEstimator.ipp:233
bool neeSampleEmission(DirectEnergySampleQuery &directSample, SampleFlow &sampleFlow, SurfaceHit *out_Xe=nullptr) const
Sample lighting using next-event estimation. This light sampling technique may not always be valid....
Definition TDirectLightEstimator.ipp:71
real neeSamplePdfWUnoccluded(const SurfaceHit &X, const SurfaceHit &Xe) const
Get the solid angle domain PDF of an next-event estimation lighting sample. Surface occlusion is not ...
Definition TDirectLightEstimator.ipp:219
Static helper for Multiple Importance Sampling (MIS). See the paper by Veach et al....
Definition TMIS.h:18
Derived & safeClampLocal(T lowerBound, T upperBound)
Definition TArithmeticArrayBase.ipp:307
Definition TTristimulusSpectrum.h:11
Derived mul(const Derived &rhs) const
Definition TArithmeticArrayBase.ipp:98
Derived normalize() const
Normalize the vector. Notice that normalizing a integer typed vector will result in 0-vector most of ...
Definition TVectorNBase.ipp:50
T absDot(const Derived &rhs) const
Definition TVectorNBase.ipp:26
T lengthSquared() const
Definition TVectorNBase.ipp:44
Light transport algorithms.
Definition enums.h:6
constexpr ESurfacePhenomenon DELTA_SURFACE_PHENOMENA
Definition surface_optics_fwd.h:48