392 lines
14 KiB
C++
392 lines
14 KiB
C++
#include <mitsuba/render/preview.h>
|
|
#include <mitsuba/render/imageproc_wu.h>
|
|
|
|
MTS_NAMESPACE_BEGIN
|
|
|
|
void PreviewWorker::serialize(Stream *stream, InstanceManager *manager) const {
|
|
Log(EError, "Serialization is not supported!");
|
|
}
|
|
|
|
ref<WorkUnit> PreviewWorker::createWorkUnit() const {
|
|
return new RectangularWorkUnit();
|
|
}
|
|
|
|
ref<WorkResult> PreviewWorker::createWorkResult() const {
|
|
/* Only pixel data, nothing else */
|
|
return new ImageBlock(Vector2i(m_blockSize, m_blockSize),
|
|
0, false, false, false, false);
|
|
}
|
|
|
|
void PreviewWorker::prepare() {
|
|
m_scene = static_cast<Scene *>(getResource("scene"));
|
|
m_kdtree = m_scene->getKDTree();
|
|
m_shapes = &m_kdtree->getShapes();
|
|
}
|
|
|
|
void PreviewWorker::process(const WorkUnit *workUnit, WorkResult *workResult,
|
|
const bool &stop) {
|
|
if (m_coherent)
|
|
processCoherent(workUnit, workResult, stop);
|
|
else
|
|
processIncoherent(workUnit, workResult, stop);
|
|
}
|
|
|
|
void PreviewWorker::processIncoherent(const WorkUnit *workUnit, WorkResult *workResult,
|
|
const bool &stop) {
|
|
const RectangularWorkUnit *rect = static_cast<const RectangularWorkUnit *>(workUnit);
|
|
ImageBlock *block = static_cast<ImageBlock *>(workResult);
|
|
|
|
block->setOffset(rect->getOffset());
|
|
block->setSize(rect->getSize());
|
|
|
|
const int sx = rect->getOffset().x, sy = block->getOffset().y;
|
|
const int ex = sx + rect->getSize().x, ey = sy + rect->getSize().y;
|
|
|
|
/* Some local variables */
|
|
int pos = 0;
|
|
Intersection its;
|
|
Spectrum value, bsdfVal;
|
|
Vector toIts;
|
|
Ray primary, secondary;
|
|
int numRays = 0;
|
|
|
|
for (int y=sy; y<ey; ++y) {
|
|
for (int x=sx; x<ex; ++x) {
|
|
/* Generate a camera ray without normalization */
|
|
primary = Ray(m_cameraO, m_cameraTL + m_cameraDx*x + m_cameraDy * y);
|
|
|
|
++numRays;
|
|
if (!m_kdtree->rayIntersect(primary, its)) {
|
|
block->setPixel(pos++, m_scene->LeBackground(primary));
|
|
continue;
|
|
}
|
|
|
|
if (its.shape->isLuminaire())
|
|
value = its.Le(-primary.d);
|
|
else
|
|
value = Spectrum(0.0f);
|
|
|
|
toIts = its.p - m_vpl.its.p;
|
|
secondary = Ray(m_vpl.its.p, toIts, 0.001, 0.999);
|
|
++numRays;
|
|
if (m_kdtree->rayIntersect(secondary)) {
|
|
block->setPixel(pos++, value);
|
|
continue;
|
|
}
|
|
|
|
Float length = toIts.length();
|
|
toIts /= length;
|
|
|
|
BSDFQueryRecord rr(its, -its.toLocal(toIts));
|
|
rr.wi = normalize(rr.wi);
|
|
bsdfVal = its.shape->getBSDF()->fCos(rr);
|
|
length = std::max(length, m_minDist);
|
|
|
|
if (m_vpl.type == ESurfaceVPL) {
|
|
BSDFQueryRecord bRec(m_vpl.its, m_vpl.its.toLocal(toIts));
|
|
bRec.quantity = EImportance;
|
|
value += m_vpl.P * bsdfVal * m_vpl.its.shape->getBSDF()->fCos(bRec) / (length*length);
|
|
} else {
|
|
EmissionRecord eRec(m_vpl.luminaire,
|
|
ShapeSamplingRecord(m_vpl.its.p, m_vpl.its.shFrame.n), toIts);
|
|
eRec.type = EmissionRecord::EPreview;
|
|
value += m_vpl.P * bsdfVal * m_vpl.luminaire->f(eRec) *
|
|
((m_vpl.luminaire->getType() == Luminaire::EOnSurface ?
|
|
(Float) 1 : dot(m_vpl.its.shFrame.n, toIts)) / (length*length));
|
|
}
|
|
block->setPixel(pos++, value);
|
|
}
|
|
}
|
|
block->setExtra(numRays);
|
|
}
|
|
|
|
void PreviewWorker::processCoherent(const WorkUnit *workUnit, WorkResult *workResult,
|
|
const bool &stop) {
|
|
#if defined(MTS_HAS_COHERENT_RT)
|
|
const RectangularWorkUnit *rect = static_cast<const RectangularWorkUnit *>(workUnit);
|
|
ImageBlock *block = static_cast<ImageBlock *>(workResult);
|
|
|
|
block->setOffset(rect->getOffset());
|
|
block->setSize(rect->getSize());
|
|
|
|
/* Some constants */
|
|
const int sx = rect->getOffset().x, sy = block->getOffset().y;
|
|
const int ex = sx + rect->getSize().x, ey = sy + rect->getSize().y;
|
|
const int width = rect->getSize().x;
|
|
const SSEVector MM_ALIGN16 xOffset(0.0f, 1.0f, 0.0f, 1.0f);
|
|
const SSEVector MM_ALIGN16 yOffset(0.0f, 0.0f, 1.0f, 1.0f);
|
|
const int pixelOffset[] = {0, 1, width, width+1};
|
|
const __m128 clamping = _mm_set1_ps(1/(m_minDist*m_minDist));
|
|
|
|
const __m128 camTL[3] = {
|
|
_mm_set1_ps(m_cameraTL.x),
|
|
_mm_set1_ps(m_cameraTL.y),
|
|
_mm_set1_ps(m_cameraTL.z)
|
|
};
|
|
const __m128 camDx[3] = {
|
|
_mm_set1_ps(m_cameraDx.x),
|
|
_mm_set1_ps(m_cameraDx.y),
|
|
_mm_set1_ps(m_cameraDx.z)
|
|
};
|
|
const __m128 camDy[3] = {
|
|
_mm_set1_ps(m_cameraDy.x),
|
|
_mm_set1_ps(m_cameraDy.y),
|
|
_mm_set1_ps(m_cameraDy.z)
|
|
};
|
|
const __m128 lumPos[3] = {
|
|
_mm_set1_ps(m_vpl.its.p.x),
|
|
_mm_set1_ps(m_vpl.its.p.y),
|
|
_mm_set1_ps(m_vpl.its.p.z)
|
|
};
|
|
const __m128 lumDir[3] = {
|
|
_mm_set1_ps(m_vpl.its.shFrame.n.x),
|
|
_mm_set1_ps(m_vpl.its.shFrame.n.y),
|
|
_mm_set1_ps(m_vpl.its.shFrame.n.z)
|
|
};
|
|
|
|
/* Some local variables */
|
|
int pos = 0;
|
|
int numRays = 0;
|
|
RayPacket4 MM_ALIGN16 primRay4, secRay4;
|
|
Intersection4 MM_ALIGN16 its4, secIts4;
|
|
RayInterval4 MM_ALIGN16 itv4, secItv4;
|
|
SSEVector MM_ALIGN16 nSecD[3], cosThetaLight, invLengthSquared;
|
|
Spectrum emitted[4], direct[4];
|
|
Intersection its;
|
|
LuminaireSamplingRecord lRec;
|
|
Vector wo, wi;
|
|
its.hasUVPartials = false;
|
|
|
|
bool diffuseVPL = false, vplOnSurface = false;
|
|
Spectrum vplWeight;
|
|
|
|
if (m_vpl.type == ESurfaceVPL && m_vpl.its.shape->getBSDF()->getType() == BSDF::EDiffuseReflection) {
|
|
diffuseVPL = true;
|
|
vplOnSurface = true;
|
|
vplWeight = m_vpl.its.shape->getBSDF()->getDiffuseReflectance(m_vpl.its) * m_vpl.P / M_PI;
|
|
} else if (m_vpl.type == ELuminaireVPL) {
|
|
vplOnSurface = m_vpl.luminaire->getType() & Luminaire::EOnSurface;
|
|
diffuseVPL = m_vpl.luminaire->getType() & Luminaire::EDiffuseDirection;
|
|
EmissionRecord eRec(m_vpl.luminaire,
|
|
ShapeSamplingRecord(m_vpl.its.p, m_vpl.its.shFrame.n), m_vpl.its.shFrame.n);
|
|
vplWeight = m_vpl.P * m_vpl.luminaire->f(eRec);
|
|
}
|
|
|
|
primRay4.o[0].ps = _mm_set1_ps(m_cameraO.x);
|
|
primRay4.o[1].ps = _mm_set1_ps(m_cameraO.y);
|
|
primRay4.o[2].ps = _mm_set1_ps(m_cameraO.z);
|
|
secItv4.mint.ps = _mm_set1_ps(0.001);
|
|
|
|
/* Work on 2x2 sub-blocks */
|
|
for (int y=sy; y<ey; y += 2, pos += width) {
|
|
for (int x=sx; x<ex; x += 2, pos += 2) {
|
|
/* Generate camera rays without normalization */
|
|
const __m128
|
|
xPixel = _mm_add_ps(xOffset.ps, _mm_set1_ps((float) x)),
|
|
yPixel = _mm_add_ps(yOffset.ps, _mm_set1_ps((float) y));
|
|
|
|
primRay4.d[0].ps = _mm_add_ps(camTL[0], _mm_add_ps(
|
|
_mm_mul_ps(xPixel, camDx[0]), _mm_mul_ps(yPixel, camDy[0])));
|
|
primRay4.d[1].ps = _mm_add_ps(camTL[1], _mm_add_ps(
|
|
_mm_mul_ps(xPixel, camDx[1]), _mm_mul_ps(yPixel, camDy[1])));
|
|
primRay4.d[2].ps = _mm_add_ps(camTL[2], _mm_add_ps(
|
|
_mm_mul_ps(xPixel, camDx[2]), _mm_mul_ps(yPixel, camDy[2])));
|
|
|
|
primRay4.dRcp[0].ps = _mm_div_ps(SSEConstants::one.ps, primRay4.d[0].ps);
|
|
primRay4.dRcp[1].ps = _mm_div_ps(SSEConstants::one.ps, primRay4.d[1].ps);
|
|
primRay4.dRcp[2].ps = _mm_div_ps(SSEConstants::one.ps, primRay4.d[2].ps);
|
|
|
|
/* Ray coherence test */
|
|
const int primSignsX = _mm_movemask_ps(primRay4.d[0].ps);
|
|
const int primSignsY = _mm_movemask_ps(primRay4.d[1].ps);
|
|
const int primSignsZ = _mm_movemask_ps(primRay4.d[2].ps);
|
|
|
|
const bool primCoherent =
|
|
(primSignsX == 0 || primSignsX == 0xF)
|
|
&& (primSignsY == 0 || primSignsY == 0xF)
|
|
&& (primSignsZ == 0 || primSignsZ == 0xF);
|
|
|
|
/* Trace the primary rays */
|
|
its4.t = SSEConstants::p_inf;
|
|
if (EXPECT_TAKEN(primCoherent)) {
|
|
primRay4.signs[0][0] = primSignsX ? 1 : 0;
|
|
primRay4.signs[1][0] = primSignsY ? 1 : 0;
|
|
primRay4.signs[2][0] = primSignsZ ? 1 : 0;
|
|
m_kdtree->rayIntersectPacket(primRay4, itv4, its4);
|
|
} else {
|
|
m_kdtree->rayIntersectPacketIncoherent(primRay4, itv4, its4);
|
|
}
|
|
numRays += 4;
|
|
|
|
/* Generate secondary rays */
|
|
secRay4.o[0].ps = _mm_add_ps(primRay4.o[0].ps, _mm_mul_ps(its4.t.ps, primRay4.d[0].ps));
|
|
secRay4.o[1].ps = _mm_add_ps(primRay4.o[1].ps, _mm_mul_ps(its4.t.ps, primRay4.d[1].ps));
|
|
secRay4.o[2].ps = _mm_add_ps(primRay4.o[2].ps, _mm_mul_ps(its4.t.ps, primRay4.d[2].ps));
|
|
secRay4.d[0].ps = _mm_sub_ps(lumPos[0], secRay4.o[0].ps);
|
|
secRay4.d[1].ps = _mm_sub_ps(lumPos[1], secRay4.o[1].ps);
|
|
secRay4.d[2].ps = _mm_sub_ps(lumPos[2], secRay4.o[2].ps);
|
|
|
|
/* Normalization */
|
|
const __m128
|
|
lengthSquared = _mm_add_ps(_mm_add_ps(
|
|
_mm_mul_ps(secRay4.d[0].ps, secRay4.d[0].ps),
|
|
_mm_mul_ps(secRay4.d[1].ps, secRay4.d[1].ps)),
|
|
_mm_mul_ps(secRay4.d[2].ps, secRay4.d[2].ps)),
|
|
invLength = _mm_rsqrt_ps(lengthSquared);
|
|
|
|
invLengthSquared.ps = _mm_min_ps(_mm_rcp_ps(lengthSquared), clamping);
|
|
|
|
nSecD[0].ps = _mm_mul_ps(secRay4.d[0].ps, invLength);
|
|
nSecD[1].ps = _mm_mul_ps(secRay4.d[1].ps, invLength);
|
|
nSecD[2].ps = _mm_mul_ps(secRay4.d[2].ps, invLength);
|
|
|
|
secRay4.dRcp[0].ps = _mm_div_ps(SSEConstants::one.ps, secRay4.d[0].ps);
|
|
secRay4.dRcp[1].ps = _mm_div_ps(SSEConstants::one.ps, secRay4.d[1].ps);
|
|
secRay4.dRcp[2].ps = _mm_div_ps(SSEConstants::one.ps, secRay4.d[2].ps);
|
|
|
|
cosThetaLight.ps = _mm_sub_ps(_mm_setzero_ps(),
|
|
_mm_add_ps(_mm_add_ps(
|
|
_mm_mul_ps(nSecD[0].ps, lumDir[0]),
|
|
_mm_mul_ps(nSecD[1].ps, lumDir[1])),
|
|
_mm_mul_ps(nSecD[2].ps, lumDir[2])));
|
|
secItv4.maxt.ps = _mm_set1_ps(0.999);
|
|
|
|
/* Shading (scalar) --- this is way too much work and should be
|
|
rewritten to be smarter in special cases */
|
|
for (int idx=0; idx<4; ++idx) {
|
|
if (EXPECT_NOT_TAKEN(its4.t.f[idx] == std::numeric_limits<float>::infinity())) {
|
|
/* Don't trace a secondary ray */
|
|
secItv4.maxt.f[idx] = 0;
|
|
emitted[idx] = m_scene->LeBackground(Ray(
|
|
Point(primRay4.o[0].f[idx], primRay4.o[1].f[idx], primRay4.o[2].f[idx]),
|
|
Vector(primRay4.d[0].f[idx], primRay4.d[1].f[idx], primRay4.d[2].f[idx])
|
|
));
|
|
memset(&direct[idx], 0, sizeof(Spectrum));
|
|
continue;
|
|
}
|
|
const unsigned int primIndex = its4.primIndex.i[idx];
|
|
const Shape *shape = (*m_shapes)[its4.shapeIndex.i[idx]];
|
|
const BSDF *bsdf = shape->getBSDF();
|
|
|
|
|
|
if (EXPECT_TAKEN(primIndex != KNoTriangleFlag)) {
|
|
const TriMesh *mesh = static_cast<const TriMesh *>(shape);
|
|
const Vertex *vb = mesh->getVertexBuffer();
|
|
const Triangle &t = mesh->getTriangles()[primIndex];
|
|
const Vertex &v0 = vb[t.idx[0]], &v1 = vb[t.idx[1]], &v2 = vb[t.idx[2]];
|
|
const Float beta = its4.u.f[idx],
|
|
gamma = its4.v.f[idx],
|
|
alpha = 1.0f - beta - gamma;
|
|
its.shFrame.n = normalize(v0.n * alpha + v1.n * beta + v2.n * gamma);
|
|
its.uv = v0.uv * alpha + v1.uv * beta + v2.uv * gamma;
|
|
|
|
if (EXPECT_NOT_TAKEN(bsdf->getType() != BSDF::EDiffuseReflection) || !diffuseVPL) {
|
|
its.dpdu = v0.dpdu * alpha + v1.dpdu * beta + v2.dpdu * gamma;
|
|
its.dpdv = v0.dpdv * alpha + v1.dpdv * beta + v2.dpdv * gamma;
|
|
}
|
|
} else {
|
|
Ray ray(
|
|
Point(primRay4.o[0].f[idx], primRay4.o[1].f[idx], primRay4.o[2].f[idx]),
|
|
Vector(primRay4.d[0].f[idx], primRay4.d[1].f[idx], primRay4.d[2].f[idx])
|
|
);
|
|
shape->rayIntersect(ray, its);
|
|
}
|
|
|
|
wo.x = nSecD[0].f[idx]; wo.y = nSecD[1].f[idx]; wo.z = nSecD[2].f[idx];
|
|
|
|
if (EXPECT_TAKEN(!shape->isLuminaire())) {
|
|
memset(&emitted[idx], 0, sizeof(Spectrum));
|
|
} else {
|
|
lRec.d = -Vector(primRay4.d[0].f[idx], primRay4.d[1].f[idx], primRay4.d[2].f[idx]);
|
|
lRec.sRec.n = its.shFrame.n;
|
|
emitted[idx] = shape->getLuminaire()->Le(lRec);
|
|
}
|
|
|
|
if (EXPECT_TAKEN(bsdf->getType() == BSDF::EDiffuseReflection) && diffuseVPL) {
|
|
/* Fast path */
|
|
direct[idx] = (bsdf->getDiffuseReflectance(its) * vplWeight)
|
|
* (std::max((Float) 0.0f, dot(wo, its.shFrame.n))
|
|
* (vplOnSurface ? std::max(cosThetaLight.f[idx], (Float) 0.0f) * INV_PI : 0.0f)
|
|
* invLengthSquared.f[idx]);
|
|
} else {
|
|
wi.x = -primRay4.d[0].f[idx];
|
|
wi.y = -primRay4.d[1].f[idx];
|
|
wi.z = -primRay4.d[2].f[idx];
|
|
its.p.x = secRay4.o[0].f[idx];
|
|
its.p.y = secRay4.o[1].f[idx];
|
|
its.p.z = secRay4.o[2].f[idx];
|
|
its.shFrame.s = normalize(its.dpdu - its.shFrame.n
|
|
* dot(its.shFrame.n, its.dpdu));
|
|
its.shFrame.t = cross(its.shFrame.n, its.shFrame.s);
|
|
const Float ctLight = cosThetaLight.f[idx];
|
|
wi = normalize(wi);
|
|
|
|
its.wi = its.toLocal(wi);
|
|
wo = its.toLocal(wo);
|
|
|
|
if (!diffuseVPL) {
|
|
if (m_vpl.type == ESurfaceVPL) {
|
|
BSDFQueryRecord bRec(m_vpl.its, m_vpl.its.toLocal(wi));
|
|
bRec.quantity = EImportance;
|
|
vplWeight = m_vpl.its.shape->getBSDF()->fCos(bRec) * m_vpl.P;
|
|
} else {
|
|
EmissionRecord eRec(m_vpl.luminaire,
|
|
ShapeSamplingRecord(m_vpl.its.p, m_vpl.its.shFrame.n), wi);
|
|
eRec.type = EmissionRecord::EPreview;
|
|
vplWeight = m_vpl.luminaire->f(eRec) * m_vpl.P;
|
|
}
|
|
}
|
|
|
|
if (EXPECT_TAKEN(ctLight > 0)) {
|
|
direct[idx] = (bsdf->fCos(BSDFQueryRecord(its, wo)) * vplWeight
|
|
* ((vplOnSurface ? std::max(ctLight, (Float) 0.0f) : 1.0f) * invLengthSquared.f[idx]));
|
|
} else {
|
|
memset(&direct[idx], 0, sizeof(Spectrum));
|
|
}
|
|
}
|
|
++numRays;
|
|
}
|
|
|
|
/* Shoot the secondary rays */
|
|
const int secSignsX = _mm_movemask_ps(secRay4.d[0].ps);
|
|
const int secSignsY = _mm_movemask_ps(secRay4.d[1].ps);
|
|
const int secSignsZ = _mm_movemask_ps(secRay4.d[2].ps);
|
|
|
|
const bool secCoherent =
|
|
(secSignsX == 0 || secSignsX == 0xF)
|
|
&& (secSignsY == 0 || secSignsY == 0xF)
|
|
&& (secSignsZ == 0 || secSignsZ == 0xF);
|
|
|
|
/* Shoot the secondary rays */
|
|
secIts4.t = SSEConstants::p_inf;
|
|
if (EXPECT_TAKEN(secCoherent)) {
|
|
secRay4.signs[0][0] = secSignsX ? 1 : 0;
|
|
secRay4.signs[1][0] = secSignsY ? 1 : 0;
|
|
secRay4.signs[2][0] = secSignsZ ? 1 : 0;
|
|
m_kdtree->rayIntersectPacket(secRay4, secItv4, secIts4);
|
|
} else {
|
|
m_kdtree->rayIntersectPacketIncoherent(secRay4, secItv4, secIts4);
|
|
}
|
|
|
|
for (int idx=0; idx<4; ++idx) {
|
|
if (EXPECT_TAKEN(secIts4.t.f[idx] == std::numeric_limits<float>::infinity()))
|
|
block->setPixel(pos+pixelOffset[idx], direct[idx]+emitted[idx]);
|
|
else
|
|
block->setPixel(pos+pixelOffset[idx], emitted[idx]);
|
|
}
|
|
}
|
|
}
|
|
block->setExtra(numRays);
|
|
#else
|
|
Log(EError, "Coherent raytracing support was not compiled into this binary!");
|
|
#endif
|
|
}
|
|
|
|
ref<WorkProcessor> PreviewWorker::clone() const {
|
|
return new PreviewWorker(m_blockSize, m_cameraO, m_cameraTL,
|
|
m_cameraDx, m_cameraDy, m_vpl, m_minDist, m_coherent);
|
|
}
|
|
|
|
MTS_IMPLEMENT_CLASS(PreviewWorker, false, WorkProcessor)
|
|
MTS_NAMESPACE_END
|