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
|
// Copyright 2009-2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
#include "light.h"
#include "../math/sampling.h"
#include "../math/linearspace.h"
namespace embree {
struct SpotLight
{
Light super; //!< inherited light fields
Vec3fa position; //!< Position of the SpotLight
LinearSpace3fa frame; //!< coordinate frame, with vz == direction that the SpotLight is emitting
Vec3fa power; //!< RGB color and intensity of the SpotLight
float cosAngleMax; //!< Angular limit of the spot in an easier to use form: cosine of the half angle in radians
float cosAngleScale; //!< 1/(cos(border of the penumbra area) - cosAngleMax); positive
float radius; //!< defines the size of the (extended) SpotLight
float diskPdf; //!< pdf of disk with radius
};
// Implementation
//////////////////////////////////////////////////////////////////////////////
SYCL_EXTERNAL Light_SampleRes SpotLight_sample(const Light* super,
const DifferentialGeometry& dg,
const Vec2f& s)
{
const SpotLight* self = (SpotLight*)super;
Light_SampleRes res;
// extant light vector from the hit point
res.dir = self->position - dg.P;
if (self->radius > 0.0f)
res.dir = self->frame * uniformSampleDisk(self->radius, s) + res.dir;
const float dist2 = dot(res.dir, res.dir);
const float invdist = rsqrt(dist2);
// normalized light vector
res.dir = res.dir * invdist;
res.dist = dist2 * invdist;
// cosine of the negated light direction and light vector.
const float cosAngle = -dot(self->frame.vz, res.dir);
const float angularAttenuation = clamp((cosAngle - self->cosAngleMax) * self->cosAngleScale);
if (self->radius > 0.0f)
res.pdf = self->diskPdf * dist2 * abs(cosAngle);
else
res.pdf = inf; // we always take this res
// convert from power to radiance by attenuating by distance^2; attenuate by angle
res.weight = self->power * (sqr(invdist) * angularAttenuation);
return res;
}
SYCL_EXTERNAL Light_EvalRes SpotLight_eval(const Light* super,
const DifferentialGeometry& dg,
const Vec3fa& dir)
{
const SpotLight* self = (SpotLight*)super;
Light_EvalRes res;
res.value = Vec3fa(0.0f);
res.dist = inf;
res.pdf = 0.0f;
if (self->radius > 0.0f) {
// intersect disk
const float cosAngle = -dot(dir, self->frame.vz);
if (cosAngle > self->cosAngleMax) { // inside illuminated cone?
const Vec3fa vp = dg.P - self->position;
const float dp = dot(vp, self->frame.vz);
if (dp > 0.0f) { // in front of light?
const float t = dp*rcp(cosAngle);
const Vec3fa vd = vp + t * dir;
if (dot(vd, vd) < sqr(self->radius)) { // inside disk?
const float angularAttenuation = min((cosAngle - self->cosAngleMax) * self->cosAngleScale, 1.f);
const float pdf = self->diskPdf * cosAngle;
res.value = self->power * (angularAttenuation * pdf); // *sqr(t)/sqr(t) cancels
res.dist = t;
res.pdf = pdf * sqr(t);
}
}
}
}
return res;
}
// Exports (called from C++)
//////////////////////////////////////////////////////////////////////////////
//! Set the parameters of an ispc-side SpotLight object
extern "C" void SpotLight_set(void* super,
const Vec3fa& position,
const Vec3fa& direction,
const Vec3fa& power,
float cosAngleMax,
float cosAngleScale,
float radius)
{
SpotLight* self = (SpotLight*)super;
self->position = position;
self->frame = frame(direction);
self->power = power;
self->cosAngleMax = cosAngleMax;
self->cosAngleScale = cosAngleScale;
self->radius = radius;
self->diskPdf = uniformSampleDiskPDF(radius);
}
//! Create an ispc-side SpotLight object
extern "C" void* SpotLight_create()
{
SpotLight* self = (SpotLight*) alignedUSMMalloc(sizeof(SpotLight),16);
Light_Constructor(&self->super);
//self->super.sample = GET_FUNCTION_POINTER(SpotLight_sample);
//self->super.eval = GET_FUNCTION_POINTER(SpotLight_eval);
self->super.type = LIGHT_SPOT;
SpotLight_set(self,
Vec3fa(0.f),
Vec3fa(0.f, 0.f, 1.f),
Vec3fa(1.f),
0.f,
100.f,
0.f);
return self;
}
} // namespace embree
|