File: QuadLight.ispc

package info (click to toggle)
ospray 3.2.0-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 10,040 kB
  • sloc: cpp: 80,569; ansic: 951; sh: 805; makefile: 171; python: 69
file content (279 lines) | stat: -rw-r--r-- 8,185 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
// Copyright 2009 Intel Corporation
// SPDX-License-Identifier: Apache-2.0

#include "IntensityDistribution.ih"
#include "SphericalQuadSampling.ih"
#include "common/DifferentialGeometry.ih"
#include "common/Instance.ih"
// c++ shared
#include "QuadLightShared.h"

OSPRAY_BEGIN_ISPC_NAMESPACE

// Implementation
//////////////////////////////////////////////////////////////////////////////
inline void Transform(const QuadLight *uniform self,
    const uniform affine3f &xfm,
    uniform QuadLightDynamic &dyn)
{
  // transform light into the world space
  dyn.position = xfmPoint(xfm, self->pre.position);
  dyn.edge1 = xfmVector(xfm, self->pre.edge1);
  dyn.edge2 = xfmVector(xfm, self->pre.edge2);

  // calculate quad normal vector
  const uniform vec3f ndirection = cross(dyn.edge2, dyn.edge1);
  dyn.ppdf = rcp(length(ndirection)); // 1/area
  dyn.nnormal = ndirection * dyn.ppdf; // normalize

  if (self->intensityDistribution.lid) {
    dyn.c90 = normalize(cross(dyn.nnormal, xfmVector(xfm, self->pre.c0)));
    dyn.c0 = cross(dyn.c90, dyn.nnormal);
  }
}

export void QuadLight_Transform(
    const void *uniform self, const void *uniform xfm, void *uniform dyn)
{
  Transform((QuadLight * uniform) self,
      *((affine3f * uniform) xfm),
      *((QuadLightDynamic * uniform) dyn));
}

inline Light_SampleRes SampleArea(const QuadLight *uniform self,
    const uniform QuadLightDynamic &dyn,
    const DifferentialGeometry &dg,
    const vec2f &s)
{
  Light_SampleRes res;
  const vec3f p = dyn.position + dyn.edge1 * s.x + dyn.edge2 * s.y;

  // extant light vector from the hit point
  const vec3f dir = p - dg.P;
  const float dist = length(dir);

  // normalized light vector
  res.dir = dir / dist;
  res.dist = dist;

  // convert to pdf wrt. solid angle
  const float cosd = dot(dyn.nnormal, res.dir);
  res.pdf = dyn.ppdf * sqr(dist) / abs(cosd);

  float weight;
  if (self->intensityDistribution.lid) {
    weight = IntensityDistribution_eval(
        &self->intensityDistribution, dyn.c0, dyn.c90, cosd, res.dir);
    // when an light distribution function is used we want to
    // remove the cosine term. To avoid numerical issues
    // at cosineAngle = 0 we use the fact that the division
    // of radiance with the cosine cancels out.
    weight /= dyn.ppdf * sqr(dist);
  } else {
    // emit only to one side
    weight = cosd > 0.f ? rcp(res.pdf) : 0.f;
  }
  res.weight = self->radiance * weight;

  return res;
}

inline Light_SampleRes Sample(const QuadLight *uniform self,
    const uniform QuadLightDynamic &dyn,
    const DifferentialGeometry &dg,
    const vec2f &s)
{
  Light_SampleRes res;

  if (s.x == 0.0f && s.y == 0.0f) { // XXX SciVis
    vec2f ss = make_vec2f(0.5f);
    return SampleArea(self, dyn, dg, ss);
  }

  // create spherical quad for solid angle sampling
  SphericalQuad quad = SphericalQuad_create(
      dyn.position, dyn.edge1, dyn.edge2, neg(dyn.nnormal), dg.P);

  // bilinear warped cosine weight approximation
  const vec3f cosW = computeCosineWeightedRNG(
      dyn.position, dyn.edge1, dyn.edge2, dg.P, dg.Ng, s);

  // sample quad
  const vec3f dir = sampleSphericalQuad(quad, make_vec2f(cosW.x, cosW.y));
  const float dist = length(dir);

  // normalized light vector
  res.dir = dir / dist;
  res.dist = dist;

  // convert to pdf wrt. solid angle
  const float cosd = dot(dyn.nnormal, res.dir);
  res.pdf = quad.S == 0.f ? 0.f : cosW.z / quad.S;

  if (self->intensityDistribution.lid) {
    if (abs(cosd) < 0.005f) // handle numerical edge case
      return SampleArea(self, dyn, dg, s);
    else {
      float weight = IntensityDistribution_eval(
          &self->intensityDistribution, dyn.c0, dyn.c90, cosd, res.dir);
      // when an light distribution function is used we want to
      // remove the cosine term. To avoid numerical issues
      // at cosineAngle = 0 we use the fact that the division
      // of radiance with the cosine cancels out.
      weight /= abs(cosd) * res.pdf;
      res.weight = res.pdf != 0.f ? self->radiance * weight : make_vec3f(0.f);
    }
  } else {
    // emit only to one side
    res.weight = (cosd > 0.f) && (res.pdf != 0.f)
        ? self->radiance * rcp(res.pdf)
        : make_vec3f(0.f);
  }
  return res;
}

SYCL_EXTERNAL Light_SampleRes QuadLight_sample(const Light *uniform super,
    const DifferentialGeometry &dg,
    const vec2f &sp,
    const float,
    const uniform FeatureFlagsHandler &)
{
  const QuadLight *uniform self = (QuadLight * uniform) super;
  assert(self);
  return Sample(self, self->pre, dg, sp);
}

SYCL_EXTERNAL Light_SampleRes QuadLight_sample_instanced(
    const Light *uniform super,
    const DifferentialGeometry &dg,
    const vec2f &sp,
    const float time,
    const uniform FeatureFlagsHandler &)
{
  const QuadLight *uniform self = (QuadLight * uniform) super;
  assert(self);

  const Instance *uniform instance = self->super.instance;
  assert(instance);

  Light_SampleRes res;
  foreach_unique (utime in time) {
    const uniform affine3f xfm = Instance_getTransform(instance, utime);
    uniform QuadLightDynamic dyn;
    Transform(self, xfm, dyn);
    res = Sample(self, dyn, dg, sp);
  }
  return res;
}

inline Light_EvalRes Eval(const QuadLight *uniform self,
    const uniform QuadLightDynamic &dyn,
    const DifferentialGeometry &dg,
    const vec3f &dir,
    const float minDist,
    const float maxDist)
{
  Light_EvalRes res;
  res.radiance = make_vec3f(0.f);

  // backfacing?
  const float cosd = dot(dyn.nnormal, dir);
  // denominator = dot(cross(edge1, edge2), dir) == cosd/ppdf
  if (cosd <= 0.f && !self->intensityDistribution.lid)
    return res;

  const vec3f c = dyn.position - dg.P;
  const vec3f r = cross(c, dir);
  const float rcosd = rcp(cosd);
  const float u = dot(r, dyn.edge1) * rcosd;
  const float v = -dot(r, dyn.edge2) * rcosd;

  // u/denominator > 1?
  if (min(u, v) < 0.f || max(u, v) * dyn.ppdf > 1.0f)
    return res;

  const float dist = dot(dyn.nnormal, c) * rcosd;
  if (dist <= minDist || dist > maxDist)
    return res;

  SphericalQuad quad = SphericalQuad_create(
      dyn.position, dyn.edge1, dyn.edge2, neg(dyn.nnormal), dg.P);
  if (quad.S > 0.f) {
    res.radiance = self->radiance;
    if (self->intensityDistribution.lid) {
      // convert from intensity to radiance by canceling the the cosine
      // term introduced by the Lambertian area light
      res.radiance = res.radiance
          * (IntensityDistribution_eval(
                 &self->intensityDistribution, dyn.c0, dyn.c90, cosd, dir)
              / abs(cosd));
    }
    res.pdf = rcp(quad.S);
  }

  return res;
}

SYCL_EXTERNAL Light_EvalRes QuadLight_eval(const Light *uniform super,
    const DifferentialGeometry &dg,
    const vec3f &dir,
    const float minDist,
    const float maxDist,
    const float)
{
  const QuadLight *uniform self = (QuadLight * uniform) super;
  assert(self);
  return Eval(self, self->pre, dg, dir, minDist, maxDist);
}

SYCL_EXTERNAL Light_EvalRes QuadLight_eval_instanced(const Light *uniform super,
    const DifferentialGeometry &dg,
    const vec3f &dir,
    const float minDist,
    const float maxDist,
    const float time)
{
  const QuadLight *uniform self = (QuadLight * uniform) super;
  assert(self);

  const Instance *uniform instance = self->super.instance;
  assert(instance);

  Light_EvalRes res;
  foreach_unique (utime in time) {
    const uniform affine3f xfm = Instance_getTransform(instance, utime);
    uniform QuadLightDynamic dyn;
    Transform(self, xfm, dyn);
    res = Eval(self, dyn, dg, dir, minDist, maxDist);
  }
  return res;
}

// Exports (called from C++)
//////////////////////////////////////////////////////////////////////////////

export void *uniform QuadLight_sample_addr()
{
  return (void *uniform)QuadLight_sample;
}

#ifndef OSPRAY_TARGET_SYCL
export void *uniform QuadLight_sample_instanced_addr()
{
  return (void *uniform)QuadLight_sample_instanced;
}
#endif

export void *uniform QuadLight_eval_addr()
{
  return (void *uniform)QuadLight_eval;
}

#ifndef OSPRAY_TARGET_SYCL
export void *uniform QuadLight_eval_instanced_addr()
{
  return (void *uniform)QuadLight_eval_instanced;
}
#endif

OSPRAY_END_ISPC_NAMESPACE