136 lines
5 KiB
C++
136 lines
5 KiB
C++
// Copyright 2009-2021 Intel Corporation
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
#include "light.h"
|
|
#include <sampling.hpp>
|
|
#include <math/linearspace3.h>
|
|
#include "helper.hpp"
|
|
|
|
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
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
Light_SampleRes SpotLight_sample(const Light* super,
|
|
const Sample& 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;
|
|
}
|
|
|
|
Light_EvalRes SpotLight_eval(const Light* super,
|
|
const Sample& 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
|