@@ -134,30 +134,34 @@ struct SCookTorrance
134
134
template<class Interaction, class MicrofacetCache>
135
135
spectral_type __eval (NBL_CONST_REF_ARG (sample_type) _sample, NBL_CONST_REF_ARG (Interaction) interaction, NBL_CONST_REF_ARG (MicrofacetCache) cache)
136
136
{
137
- fresnel_type _f = impl::getOrientedFresnel<fresnel_type, IsBSDF>:: __call (fresnel, interaction. getNdotV ()) ;
138
- const bool notTIR = impl::check_TIR_helper< fresnel_type, IsBSDF>::template __call<MicrofacetCache>(_f, cache) ;
139
- if (( IsBSDF && notTIR) || (!IsBSDF && _sample. getNdotL () > numeric_limits<scalar_type>:: min && interaction. getNdotV () > numeric_limits<scalar_type>:: min ) )
137
+ bool valid ;
138
+ fresnel_type _f = fresnel ;
139
+ NBL_IF_CONSTEXPR ( IsBSDF)
140
140
{
141
- using quant_query_type = typename ndf_type::quant_query_type;
142
- using g2g1_query_type = typename ndf_type::g2g1_query_type;
143
-
144
- scalar_type dummy;
145
- quant_query_type qq = ndf.template createQuantQuery<MicrofacetCache>(cache, dummy);
146
-
147
- quant_type D = ndf.template D<sample_type, Interaction, MicrofacetCache>(qq, _sample, interaction, cache);
148
- scalar_type DG = D.projectedLightMeasure;
149
- if (D.microfacetMeasure < bit_cast<scalar_type>(numeric_limits<scalar_type>::infinity))
150
- {
151
- g2g1_query_type gq = ndf.template createG2G1Query<sample_type, Interaction>(_sample, interaction);
152
- DG *= ndf.template correlated<sample_type, Interaction>(gq, _sample, interaction);
153
- }
154
- NBL_IF_CONSTEXPR (IsBSDF)
155
- return impl::__implicit_promote<spectral_type, typename fresnel_type::vector_type>::__call (_f (hlsl::abs (cache.getVdotH ()))) * DG;
156
- else
157
- return impl::__implicit_promote<spectral_type, typename fresnel_type::vector_type>::__call (_f (cache.getVdotH ())) * DG;
141
+ _f = impl::getOrientedFresnel<fresnel_type, IsBSDF>::__call (fresnel, interaction.getNdotV ());
142
+ valid = impl::check_TIR_helper<fresnel_type, IsBSDF>::template __call<MicrofacetCache>(_f, cache);
158
143
}
159
144
else
145
+ valid = _sample.getNdotL () > numeric_limits<scalar_type>::min && interaction.getNdotV () > numeric_limits<scalar_type>::min ;
146
+ if (!valid)
160
147
return hlsl::promote<spectral_type>(0.0 );
148
+
149
+ using quant_query_type = typename ndf_type::quant_query_type;
150
+ scalar_type dummy;
151
+ quant_query_type qq = ndf.template createQuantQuery<MicrofacetCache>(cache, dummy);
152
+
153
+ quant_type D = ndf.template D<sample_type, Interaction, MicrofacetCache>(qq, _sample, interaction, cache);
154
+ scalar_type DG = D.projectedLightMeasure;
155
+ if (D.microfacetMeasure < bit_cast<scalar_type>(numeric_limits<scalar_type>::infinity))
156
+ {
157
+ using g2g1_query_type = typename ndf_type::g2g1_query_type;
158
+ g2g1_query_type gq = ndf.template createG2G1Query<sample_type, Interaction>(_sample, interaction);
159
+ DG *= ndf.template correlated<sample_type, Interaction>(gq, _sample, interaction);
160
+ }
161
+ scalar_type clampedVdotH = cache.getVdotH ();
162
+ NBL_IF_CONSTEXPR (IsBSDF)
163
+ clampedVdotH = hlsl::abs (clampedVdotH);
164
+ return impl::__implicit_promote<spectral_type, typename fresnel_type::vector_type>::__call (_f (clampedVdotH)) * DG;
161
165
}
162
166
template<typename C=bool_constant<!IsAnisotropic> >
163
167
enable_if_t<C::value && !IsAnisotropic, spectral_type> eval (NBL_CONST_REF_ARG (sample_type) _sample, NBL_CONST_REF_ARG (isotropic_interaction_type) interaction, NBL_CONST_REF_ARG (isocache_type) cache)
@@ -172,66 +176,100 @@ struct SCookTorrance
172
176
template<typename C=bool_constant<!IsBSDF> >
173
177
enable_if_t<C::value && !IsBSDF, sample_type> generate (NBL_CONST_REF_ARG (anisotropic_interaction_type) interaction, const vector2_type u, NBL_REF_ARG (anisocache_type) cache)
174
178
{
175
- ray_dir_info_type localV_raydir = interaction.getV ().transform (interaction.getToTangentSpace ());
179
+ if (interaction.getNdotV () > numeric_limits<scalar_type>::min )
180
+ {
181
+ ray_dir_info_type invalidL;
182
+ invalidL.makeInvalid ();
183
+ return sample_type::createFromTangentSpace (invalidL, interaction.getFromTangentSpace ());
184
+ }
185
+
176
186
const vector3_type localV = interaction.getTangentSpaceV ();
177
187
const vector3_type localH = ndf.generateH (localV, u);
188
+ const scalar_type VdotH = hlsl::dot (localV, localH);
189
+ const vector3_type H = hlsl::mul (interaction.getFromTangentSpace (), localH);
178
190
179
- cache = anisocache_type:: createForReflection (localV, localH) ;
180
- struct reflect_wrapper
191
+ ray_dir_info_type L ;
192
+ if ( scalar_type ( 2.0 ) * VdotH * localH.z > localV.z) // NdotL>0, compiler's Common Subexpression Elimination pass should re-use 2*VdotH later
181
193
{
182
- vector3_type operator ()() NBL_CONST_MEMBER_FUNC
194
+ assert (VdotH >= scalar_type (0.0 ));
195
+ ray_dir_info_type V = interaction.getV ();
196
+ struct reflect_wrapper // so we don't recalculate VdotH
183
197
{
184
- return r (VdotH);
185
- }
186
- bxdf::Reflect<scalar_type> r;
187
- scalar_type VdotH;
188
- };
189
- reflect_wrapper r;
190
- r.r = bxdf::Reflect<scalar_type>::create (localV, localH);
191
- r.VdotH = cache.getVdotH ();
192
- ray_dir_info_type localL = localV_raydir.template reflect<reflect_wrapper>(r);
198
+ vector3_type operator ()() NBL_CONST_MEMBER_FUNC
199
+ {
200
+ return r (VdotH);
201
+ }
202
+ bxdf::Reflect<scalar_type> r;
203
+ scalar_type VdotH;
204
+ };
205
+ reflect_wrapper rw;
206
+ rw.r = bxdf::Reflect<scalar_type>::create (V.getDirection (), H);
207
+ rw.VdotH = VdotH;
208
+ L = V.template reflect<reflect_wrapper>(rw);
209
+
210
+ cache = anisocache_type::createForReflection (localV, localH);
211
+ }
212
+ else // fail if samples have invalid paths
213
+ L.makeInvalid (); // should check if sample direction is invalid
193
214
194
- // fail if samples have invalid paths
195
- if (localL. getDirection ().z < scalar_type ( 0.0 )) // NdotL<0
196
- localL. makeInvalid (); // should check if sample direction is invalid
215
+ const vector3_type T = interaction. getT ();
216
+ const vector3_type B = interaction. getB ();
217
+ const vector3_type _N = interaction. getN ();
197
218
198
- return sample_type::createFromTangentSpace (localL, interaction. getFromTangentSpace () );
219
+ return sample_type::create (L, T, B, _N );
199
220
}
200
221
template<typename C=bool_constant<IsBSDF> >
201
222
enable_if_t<C::value && IsBSDF, sample_type> generate (NBL_CONST_REF_ARG (anisotropic_interaction_type) interaction, const vector3_type u, NBL_REF_ARG (anisocache_type) cache)
202
223
{
203
- fresnel_type _f = impl::getOrientedFresnel<fresnel_type, IsBSDF>::__call (fresnel, interaction.getNdotV ());
224
+ const vector3_type localV = interaction.getTangentSpaceV ();
225
+ const scalar_type NdotV = localV.z;
226
+
227
+ fresnel_type _f = impl::getOrientedFresnel<fresnel_type, IsBSDF>::__call (fresnel, NdotV);
204
228
fresnel::OrientedEtaRcps<monochrome_type> rcpEta = _f.getOrientedEtaRcps ();
205
229
206
- ray_dir_info_type V = interaction.getV ();
207
- const vector3_type localV = interaction.getTangentSpaceV ();
208
- const vector3_type upperHemisphereV = ieee754::flipSignIfRHSNegative<vector3_type>(localV, hlsl::promote<vector3_type>(interaction.getNdotV ()));
230
+ const vector3_type upperHemisphereV = ieee754::flipSignIfRHSNegative<vector3_type>(localV, hlsl::promote<vector3_type>(NdotV));
209
231
const vector3_type localH = ndf.generateH (upperHemisphereV, u.xy);
232
+ const scalar_type VdotH = hlsl::dot (localV, localH);
210
233
const vector3_type H = hlsl::mul (interaction.getFromTangentSpace (), localH);
211
234
212
- const scalar_type VdotH = hlsl:: dot (V. getDirection (), H );
235
+ assert (NdotV * VdotH > scalar_type ( 0.0 ) );
213
236
const scalar_type reflectance = _f (hlsl::abs (VdotH))[0 ];
214
237
215
238
scalar_type rcpChoiceProb;
216
239
scalar_type z = u.z;
217
240
bool transmitted = math::partitionRandVariable (reflectance, z, rcpChoiceProb);
218
241
242
+ ray_dir_info_type V = interaction.getV ();
243
+ const vector3_type _N = interaction.getN ();
219
244
Refract<scalar_type> r = Refract<scalar_type>::create (V.getDirection (), H);
245
+ const scalar_type LdotH = hlsl::mix (VdotH, r.getNdotT (rcpEta.value2[0 ]), transmitted);
246
+ cache = anisocache_type::createPartial (VdotH, LdotH, hlsl::dot (_N, H), transmitted, rcpEta);
247
+
248
+ struct reflect_refract_wrapper // so we don't recalculate LdotH
249
+ {
250
+ vector3_type operator ()(const bool doRefract, const scalar_type rcpOrientedEta) NBL_CONST_MEMBER_FUNC
251
+ {
252
+ return rr (NdotTorR, rcpOrientedEta);
253
+ }
254
+ bxdf::ReflectRefract<scalar_type> rr;
255
+ scalar_type NdotTorR;
256
+ };
220
257
bxdf::ReflectRefract<scalar_type> rr;
221
258
rr.refract = r;
222
- ray_dir_info_type L = V.reflectRefract (rr, transmitted, rcpEta.value[0 ]);
259
+ reflect_refract_wrapper rrw;
260
+ rrw.rr = rr;
261
+ rrw.NdotTorR = LdotH;
262
+ ray_dir_info_type L = V.template reflectRefract<reflect_refract_wrapper>(rrw, transmitted, rcpEta.value[0 ]);
223
263
224
264
const vector3_type T = interaction.getT ();
225
265
const vector3_type B = interaction.getB ();
226
- const vector3_type _N = interaction.getN ();
227
266
228
267
// fail if samples have invalid paths
229
- const vector3_type Ldir = L.getDirection ();
230
- const scalar_type LdotH = hlsl::dot (Ldir, H);
231
- if ((ComputeMicrofacetNormal<scalar_type>::isTransmissionPath (VdotH, LdotH) != transmitted) || (LdotH * hlsl::dot (_N, Ldir) < scalar_type (0.0 )))
268
+ const scalar_type NdotL = scalar_type (2.0 ) * VdotH * localH.z - localV.z;
269
+ if ((ComputeMicrofacetNormal<scalar_type>::isTransmissionPath (NdotV, NdotL) != transmitted))
232
270
L.makeInvalid (); // should check if sample direction is invalid
233
271
else
234
- cache = anisocache_type:: create (VdotH, Ldir, H, T, B, _N, transmitted );
272
+ cache. fillTangents ( T, B, H );
235
273
236
274
return sample_type::create (L, T, B, _N);
237
275
}
0 commit comments