/// Loads the mesh from the thread-local file stream cache void loadCompressed(const fs::path& filePath, const int idx) { if (EXPECT_NOT_TAKEN(idx < 0)) { Log(EError, "Unable to unserialize mesh, " "shape index is negative! (requested %i out of 0..%i)", idx); } // Get the thread local cache; create it if this is the first time FileStreamCache* cache = m_cache.get(); if (EXPECT_NOT_TAKEN(cache == NULL)) { cache = new FileStreamCache(); m_cache.set(cache); mitsuba::pushSceneCleanupHandler(&SerializedMesh::flushCache); } boost::shared_ptr<MeshLoader> meshLoader = cache->get(filePath); Assert(meshLoader != NULL); TriMesh::loadCompressed(meshLoader->seekStream((size_t) idx)); }
Vector Warp::squareToCosineHemisphere(const Point2 &sample) { Point2 p = Warp::squareToUniformDiskConcentric(sample); Float z = math::safe_sqrt(1.0f - p.x*p.x - p.y*p.y); /* Guard against numerical imprecisions */ if (EXPECT_NOT_TAKEN(z == 0)) z = 1e-10f; return Vector(p.x, p.y, z); }
bool develop(const Point2i &sourceOffset, const Vector2i &size, const Point2i &targetOffset, Bitmap *target) const { const Bitmap *source = m_storage->getBitmap(); const FormatConverter *cvt = FormatConverter::getInstance( std::make_pair(Bitmap::EFloat, target->getComponentFormat()) ); size_t sourceBpp = source->getBytesPerPixel(); size_t targetBpp = target->getBytesPerPixel(); const uint8_t *sourceData = source->getUInt8Data() + (sourceOffset.x + sourceOffset.y * source->getWidth()) * sourceBpp; uint8_t *targetData = target->getUInt8Data() + (targetOffset.x + targetOffset.y * target->getWidth()) * targetBpp; if (EXPECT_NOT_TAKEN(m_pixelFormats.size() != 1)) { /* Special case for general multi-channel images -- just develop the first component(s) */ for (int i=0; i<size.y; ++i) { for (int j=0; j<size.x; ++j) { Float weight = *((Float *) (sourceData + (j+1)*sourceBpp - sizeof(Float))); Float invWeight = weight != 0 ? ((Float) 1 / weight) : (Float) 0; cvt->convert(Bitmap::ESpectrum, 1.0f, sourceData + j*sourceBpp, target->getPixelFormat(), target->getGamma(), targetData + j * targetBpp, 1, invWeight); } sourceData += source->getWidth() * sourceBpp; targetData += target->getWidth() * targetBpp; } } else if (size.x == m_cropSize.x && target->getWidth() == m_storage->getWidth()) { /* Develop a connected part of the underlying buffer */ cvt->convert(source->getPixelFormat(), 1.0f, sourceData, target->getPixelFormat(), target->getGamma(), targetData, size.x*size.y); } else { /* Develop a rectangular subregion */ for (int i=0; i<size.y; ++i) { cvt->convert(source->getPixelFormat(), 1.0f, sourceData, target->getPixelFormat(), target->getGamma(), targetData, size.x); sourceData += source->getWidth() * sourceBpp; targetData += target->getWidth() * targetBpp; } } return true; }
Vector squareToHemispherePSA(const Point2 &sample) { Float r = std::sqrt(sample.x); Float phi = 2.0f * M_PI * sample.y; Float dirX = r * std::cos(phi); Float dirY = r * std::sin(phi); Float z = std::sqrt(1 - std::min((Float) 1, dirX*dirX + dirY*dirY)); if (EXPECT_NOT_TAKEN(z == 0)) { /* Guard against numerical imprecisions */ return normalize(Vector( dirX, dirY, Epsilon)); } return Vector( dirX, dirY, z ); }
MTS_NAMESPACE_BEGIN void Intersection::computePartials(const RayDifferential &ray) { Float A[2][2], Bx[2], By[2], x[2]; int axes[2]; /* Compute the texture coordinates partials wrt. changes in the screen-space position. Based on PBRT */ if (hasUVPartials) return; hasUVPartials = true; if (!ray.hasDifferentials || (dpdu.isZero() && dpdv.isZero())) { dudx = dvdx = dudy = dvdy = 0.0f; return; } /* Offset of the plane passing through the surface */ const Float d = -dot(geoFrame.n, Vector(p)); const Float txRecip = dot(geoFrame.n, ray.rxDirection), tyRecip = dot(geoFrame.n, ray.ryDirection); if (EXPECT_NOT_TAKEN(txRecip == 0 || tyRecip == 0)) { dudx = dvdx = dudy = dvdy = 0.0f; return; } /* Ray distances traveled */ const Float tx = -(dot(geoFrame.n, Vector(ray.rxOrigin)) + d) / txRecip; const Float ty = -(dot(geoFrame.n, Vector(ray.ryOrigin)) + d) / tyRecip; /* Calculate the U and V partials by solving two out of a set of 3 equations in an overconstrained system */ Float absX = std::abs(geoFrame.n.x), absY = std::abs(geoFrame.n.y), absZ = std::abs(geoFrame.n.z); if (absX > absY && absX > absZ) { axes[0] = 1; axes[1] = 2; } else if (absY > absZ) { axes[0] = 0; axes[1] = 2; } else { axes[0] = 0; axes[1] = 1; } A[0][0] = dpdu[axes[0]]; A[0][1] = dpdv[axes[0]]; A[1][0] = dpdu[axes[1]]; A[1][1] = dpdv[axes[1]]; /* Auxilary intersection point of the adjacent rays */ Point px = ray.rxOrigin + ray.rxDirection * tx, py = ray.ryOrigin + ray.ryDirection * ty; Bx[0] = px[axes[0]] - p[axes[0]]; Bx[1] = px[axes[1]] - p[axes[1]]; By[0] = py[axes[0]] - p[axes[0]]; By[1] = py[axes[1]] - p[axes[1]]; if (EXPECT_TAKEN(solveLinearSystem2x2(A, Bx, x))) { dudx = x[0]; dvdx = x[1]; } else { dudx = 1; dvdx = 0; } if (EXPECT_TAKEN(solveLinearSystem2x2(A, By, x))) { dudy = x[0]; dvdy = x[1]; } else { dudy = 0; dudy = 1; } }
Float lookupFloat(const Point &_p) const { const Point p = m_worldToGrid.transformAffine(_p); int x = (int) p.x, y = (int) p.y, z = (int) p.z; if (EXPECT_NOT_TAKEN( x < 0 || x >= m_cellCount.x || y < 0 || y >= m_cellCount.y || z < 0 || z >= m_cellCount.z)) return 0.0f; BlockCache *cache = m_cache.get(); if (EXPECT_NOT_TAKEN(cache == NULL)) { cache = new BlockCache(m_blocksPerCore, boost::bind(&CachingDataSource::renderBlock, this, _1), boost::bind(&CachingDataSource::destroyBlock, this, _1)); m_cache.set(cache); } #if defined(VOLCACHE_DEBUG) if (cache->isFull()) { /* For debugging: when the cache is full, dump locations of all cache records into an OBJ file and exit */ std::vector<Vector3i> keys; cache->get_keys(std::back_inserter(keys)); std::ofstream os("keys.obj"); os << "o Keys" << endl; for (size_t i=0; i<keys.size(); i++) { Vector3i key = keys[i]; key = key * m_blockSize + Vector3i(m_blockSize/2); Point p(key.x * m_voxelWidth + m_aabb.min.x, key.y * m_voxelWidth + m_aabb.min.y, key.z * m_voxelWidth + m_aabb.min.z); os << "v " << p.x << " " << p.y << " " << p.z << endl; } /// Need to generate some fake geometry so that blender will import the points for (size_t i=3; i<=keys.size(); i++) os << "f " << i << " " << i-1 << " " << i-2 << endl; os.close(); _exit(-1); } #endif bool hit = false; float *blockData = cache->get(Vector3i( (x & m_blockMask) >> m_blockShift, (y & m_blockMask) >> m_blockShift, (z & m_blockMask) >> m_blockShift), hit); statsHitRate.incrementBase(); if (hit) ++statsHitRate; if (blockData == NULL) return 0.0f; const int x1 = x & m_voxelMask, y1 = y & m_voxelMask, z1 = z & m_voxelMask, x2 = x1 + 1, y2 = y1 + 1, z2 = z1 + 1; const Float fx = p.x - x, fy = p.y - y, fz = p.z - z, _fx = 1.0f - fx, _fy = 1.0f - fy, _fz = 1.0f - fz; const float &d000 = blockData[(z1*m_blockRes + y1)*m_blockRes + x1], &d001 = blockData[(z1*m_blockRes + y1)*m_blockRes + x2], &d010 = blockData[(z1*m_blockRes + y2)*m_blockRes + x1], &d011 = blockData[(z1*m_blockRes + y2)*m_blockRes + x2], &d100 = blockData[(z2*m_blockRes + y1)*m_blockRes + x1], &d101 = blockData[(z2*m_blockRes + y1)*m_blockRes + x2], &d110 = blockData[(z2*m_blockRes + y2)*m_blockRes + x1], &d111 = blockData[(z2*m_blockRes + y2)*m_blockRes + x2]; float result = ((d000*_fx + d001*fx)*_fy + (d010*_fx + d011*fx)*fy)*_fz + ((d100*_fx + d101*fx)*_fy + (d110*_fx + d111*fx)*fy)*fz; return result; }
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)); uint8_t temp[MTS_KD_INTERSECTION_TEMP*4]; 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; Vector wo, wi; its.hasUVPartials = false; bool diffuseVPL = false, vplOnSurface = false; Spectrum vplWeight; if (m_vpl.type == ESurfaceVPL && (m_diffuseSources || 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->evalDirection(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(ShadowEpsilon); /* 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, temp); } else { m_kdtree->rayIntersectPacketIncoherent(primRay4, itv4, its4, temp); } 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(1-ShadowEpsilon); /* 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]), 0.0f )) * m_backgroundScale; 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_NOT_TAKEN(!bsdf)) { memset(&emitted[idx], 0, sizeof(Spectrum)); memset(&direct[idx], 0, sizeof(Spectrum)); continue; } if (EXPECT_TAKEN(primIndex != KNoTriangleFlag)) { const TriMesh *mesh = static_cast<const TriMesh *>(shape); const Triangle &t = mesh->getTriangles()[primIndex]; const Normal *normals = mesh->getVertexNormals(); const Point2 *texcoords = mesh->getVertexTexcoords(); const Spectrum *colors = mesh->getVertexColors(); const TangentSpace * tangents = mesh->getVertexTangents(); const Float beta = its4.u.f[idx], gamma = its4.v.f[idx], alpha = 1.0f - beta - gamma; const uint32_t idx0 = t.idx[0], idx1 = t.idx[1], idx2 = t.idx[2]; if (EXPECT_TAKEN(normals)) { const Normal &n0 = normals[idx0], &n1 = normals[idx1], &n2 = normals[idx2]; its.shFrame.n = normalize(n0 * alpha + n1 * beta + n2 * gamma); } else { const Point *positions = mesh->getVertexPositions(); const Point &p0 = positions[idx0], &p1 = positions[idx1], &p2 = positions[idx2]; Vector sideA = p1 - p0, sideB = p2 - p0; Vector n = cross(sideA, sideB); Float nLengthSqr = n.lengthSquared(); if (nLengthSqr != 0) n /= std::sqrt(nLengthSqr); its.shFrame.n = Normal(n); } if (EXPECT_TAKEN(texcoords)) { const Point2 &t0 = texcoords[idx0], &t1 = texcoords[idx1], &t2 = texcoords[idx2]; its.uv = t0 * alpha + t1 * beta + t2 * gamma; } else { its.uv = Point2(0.0f); } if (EXPECT_NOT_TAKEN(colors)) { const Spectrum &c0 = colors[idx0], &c1 = colors[idx1], &c2 = colors[idx2]; its.color = c0 * alpha + c1 * beta + c2 * gamma; } if (EXPECT_NOT_TAKEN(tangents)) { const TangentSpace &t0 = tangents[idx0], &t1 = tangents[idx1], &t2 = tangents[idx2]; its.dpdu = t0.dpdu * alpha + t1.dpdu * beta + t2.dpdu * gamma; its.dpdv = t0.dpdv * alpha + t1.dpdv * beta + t2.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]), 0.0f ); its.t = its4.t.f[idx]; shape->fillIntersectionRecord(ray, temp + idx * MTS_KD_INTERSECTION_TEMP + 8, its); bsdf = its.shape->getBSDF(); } 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 { Vector d(-primRay4.d[0].f[idx], -primRay4.d[1].f[idx], -primRay4.d[2].f[idx]); emitted[idx] = shape->getLuminaire()->Le(ShapeSamplingRecord(its.p, its.shFrame.n), d); } 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) : INV_PI) * 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]; if (EXPECT_NOT_TAKEN(bsdf->getType() & BSDF::EAnisotropic)) { 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); } else { coordinateSystem(its.shFrame.n, its.shFrame.s, its.shFrame.t); } 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()->eval(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->evalDirection(eRec) * m_vpl.P; } } if (EXPECT_TAKEN(ctLight >= 0)) { direct[idx] = (bsdf->eval(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, temp); } else { m_kdtree->rayIntersectPacketIncoherent(secRay4, secItv4, secIts4, temp); } 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 }
Spectrum Li(const RayDifferential &r, RadianceQueryRecord &rRec) const { /* Some aliases and local variables */ const Scene *scene = rRec.scene; Intersection &its = rRec.its, bsdfIts; RayDifferential ray(r); LuminaireSamplingRecord lRec; Spectrum Li(0.0f); Point2 sample; /* Perform the first ray intersection (or ignore if the intersection has already been provided). */ if (!rRec.rayIntersect(ray)) { /* If no intersection could be found, possibly return radiance from a background luminaire */ if (rRec.type & RadianceQueryRecord::EEmittedRadiance) return scene->LeBackground(ray); else return Spectrum(0.0f); } /* Possibly include emitted radiance if requested */ if (its.isLuminaire() && (rRec.type & RadianceQueryRecord::EEmittedRadiance)) Li += its.Le(-ray.d); /* Include radiance from a subsurface integrator if requested */ if (its.hasSubsurface() && (rRec.type & RadianceQueryRecord::ESubsurfaceRadiance)) Li += its.LoSub(scene, rRec.sampler, -ray.d, rRec.depth); const BSDF *bsdf = its.getBSDF(ray); if (EXPECT_NOT_TAKEN(!bsdf)) { /* The direct illumination integrator doesn't support surfaces without a BSDF (e.g. medium transitions) -- give up. */ return Li; } /* Leave here if direct illumination was not requested */ if (!(rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance)) return Li; /* ==================================================================== */ /* Luminaire sampling */ /* ==================================================================== */ bool adaptiveQuery = (rRec.extra & RadianceQueryRecord::EAdaptiveQuery); Point2 *sampleArray; size_t numLuminaireSamples = m_luminaireSamples, numBSDFSamples = m_bsdfSamples; Float fracLum = m_fracLum, fracBSDF = m_fracBSDF, weightLum = m_weightLum, weightBSDF = m_weightBSDF; if (rRec.depth > 1 || adaptiveQuery) { /* This integrator is used recursively by another integrator. Be less accurate as this sample will not directly be observed. */ numBSDFSamples = numLuminaireSamples = 1; fracLum = fracBSDF = .5f; weightLum = weightBSDF = 1.0f; } if (numLuminaireSamples > 1) { sampleArray = rRec.sampler->next2DArray(numLuminaireSamples); } else { sample = rRec.nextSample2D(); sampleArray = &sample; } for (size_t i=0; i<numLuminaireSamples; ++i) { /* Estimate the direct illumination if this is requested */ if (scene->sampleLuminaire(its.p, ray.time, lRec, sampleArray[i])) { /* Allocate a record for querying the BSDF */ BSDFQueryRecord bRec(its, its.toLocal(-lRec.d)); /* Evaluate BSDF * cos(theta) */ const Spectrum bsdfVal = bsdf->eval(bRec); if (!bsdfVal.isZero()) { /* Calculate prob. of having sampled that direction using BSDF sampling */ Float bsdfPdf = (lRec.luminaire->isIntersectable() || lRec.luminaire->isBackgroundLuminaire()) ? bsdf->pdf(bRec) : 0; /* Weight using the power heuristic */ const Float weight = miWeight(lRec.pdf * fracLum, bsdfPdf * fracBSDF) * weightLum; Li += lRec.value * bsdfVal * weight; } } } /* ==================================================================== */ /* BSDF sampling */ /* ==================================================================== */ if (numBSDFSamples > 1) { sampleArray = rRec.sampler->next2DArray(numBSDFSamples); } else { sample = rRec.nextSample2D(); sampleArray = &sample; } for (size_t i=0; i<numBSDFSamples; ++i) { /* Sample BSDF * cos(theta) */ BSDFQueryRecord bRec(its, rRec.sampler, ERadiance); Float bsdfPdf; Spectrum bsdfVal = bsdf->sample(bRec, bsdfPdf, sampleArray[i]); if (bsdfVal.isZero()) continue; /* Trace a ray in this direction */ Ray bsdfRay(its.p, its.toWorld(bRec.wo), ray.time); if (scene->rayIntersect(bsdfRay, bsdfIts)) { /* Intersected something - check if it was a luminaire */ if (bsdfIts.isLuminaire()) { lRec = LuminaireSamplingRecord(bsdfIts, -bsdfRay.d); lRec.value = bsdfIts.Le(-bsdfRay.d); } else { continue; } } else { /* No intersection found. Possibly, there is a background luminaire such as an environment map? */ if (scene->hasBackgroundLuminaire()) { lRec.luminaire = scene->getBackgroundLuminaire(); lRec.d = -bsdfRay.d; lRec.value = lRec.luminaire->Le(bsdfRay); } else { continue; } } const Float lumPdf = (!(bRec.sampledType & BSDF::EDelta)) ? scene->pdfLuminaire(its.p, lRec) : 0; const Float weight = miWeight(bsdfPdf * fracBSDF, lumPdf * fracLum) * weightBSDF; Li += lRec.value * bsdfVal * weight; } return Li; }
Spectrum Li(const RayDifferential &r, RadianceQueryRecord &rRec) const { /* Some aliases and local variables */ const Scene *scene = rRec.scene; Intersection &its = rRec.its; RayDifferential ray(r); Spectrum Li(0.0f); /* Perform the first ray intersection (or ignore if the intersection has already been provided). */ rRec.rayIntersect(ray); ray.mint = Epsilon; Spectrum pathThroughput(1.0f); while (rRec.depth <= m_maxDepth || m_maxDepth < 0) { if (!its.isValid()) { /* If no intersection could be found, potentially return radiance from a background luminaire if it exists */ if (rRec.type & RadianceQueryRecord::EEmittedRadiance) Li += pathThroughput * scene->LeBackground(ray); break; } const BSDF *bsdf = its.getBSDF(ray); if (EXPECT_NOT_TAKEN(bsdf == NULL)) { /* The MI path tracer doesn't support surfaces without a BSDF (e.g. medium transitions) -- give up. */ break; } /* Possibly include emitted radiance if requested */ if (its.isLuminaire() && (rRec.type & RadianceQueryRecord::EEmittedRadiance)) Li += pathThroughput * its.Le(-ray.d); /* Include radiance from a subsurface integrator if requested */ if (its.hasSubsurface() && (rRec.type & RadianceQueryRecord::ESubsurfaceRadiance)) Li += pathThroughput * its.LoSub(scene, rRec.sampler, -ray.d, rRec.depth); if (m_maxDepth > 0 && rRec.depth >= m_maxDepth) break; /* ==================================================================== */ /* Luminaire sampling */ /* ==================================================================== */ /* Prevent light leaks due to the use of shading normals */ Float wiDotGeoN = -dot(its.geoFrame.n, ray.d), wiDotShN = Frame::cosTheta(its.wi); if (wiDotGeoN * wiDotShN < 0 && m_strictNormals) break; /* Estimate the direct illumination if this is requested */ LuminaireSamplingRecord lRec; if (rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance && scene->sampleLuminaire(its.p, ray.time, lRec, rRec.nextSample2D())) { /* Allocate a record for querying the BSDF */ const Vector wo = -lRec.d; const BSDFQueryRecord bRec(its, its.toLocal(wo)); /* Evaluate BSDF * cos(theta) */ const Spectrum bsdfVal = bsdf->fCos(bRec); Float woDotGeoN = dot(its.geoFrame.n, wo); /* Prevent light leaks due to the use of shading normals */ if (!bsdfVal.isZero() && (!m_strictNormals || woDotGeoN * Frame::cosTheta(bRec.wo) > 0)) { /* Calculate prob. of having sampled that direction using BSDF sampling */ Float bsdfPdf = (lRec.luminaire->isIntersectable() || lRec.luminaire->isBackgroundLuminaire()) ? bsdf->pdf(bRec) : 0; /* Weight using the power heuristic */ const Float weight = miWeight(lRec.pdf, bsdfPdf); Li += pathThroughput * lRec.value * bsdfVal * weight; } } /* ==================================================================== */ /* BSDF sampling */ /* ==================================================================== */ /* Sample BSDF * cos(theta) */ BSDFQueryRecord bRec(its); Float bsdfPdf; Spectrum bsdfVal = bsdf->sampleCos(bRec, bsdfPdf, rRec.nextSample2D()); if (bsdfVal.isZero()) break; bsdfVal /= bsdfPdf; /* Prevent light leaks due to the use of shading normals */ const Vector wo = its.toWorld(bRec.wo); Float woDotGeoN = dot(its.geoFrame.n, wo); if (woDotGeoN * Frame::cosTheta(bRec.wo) <= 0 && m_strictNormals) break; /* Trace a ray in this direction */ ray = Ray(its.p, wo, ray.time); bool hitLuminaire = false; if (scene->rayIntersect(ray, its)) { /* Intersected something - check if it was a luminaire */ if (its.isLuminaire()) { lRec = LuminaireSamplingRecord(its, -ray.d); lRec.value = its.Le(-ray.d); hitLuminaire = true; } } else { /* No intersection found. Possibly, there is a background luminaire such as an environment map? */ if (scene->hasBackgroundLuminaire()) { lRec.luminaire = scene->getBackgroundLuminaire(); lRec.value = lRec.luminaire->Le(ray); lRec.d = -ray.d; hitLuminaire = true; } else { rRec.depth++; break; } } /* If a luminaire was hit, estimate the local illumination and weight using the power heuristic */ if (hitLuminaire && (rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance)) { /* Prob. of having generated this sample using luminaire sampling */ const Float lumPdf = (!(bRec.sampledType & BSDF::EDelta)) ? scene->pdfLuminaire(ray.o, lRec) : 0; const Float weight = miWeight(bsdfPdf, lumPdf); Li += pathThroughput * lRec.value * bsdfVal * weight; } /* ==================================================================== */ /* Indirect illumination */ /* ==================================================================== */ /* Set the recursive query type */ /* Stop if no surface was hit by the BSDF sample or if indirect illumination was not requested */ if (!its.isValid() || !(rRec.type & RadianceQueryRecord::EIndirectSurfaceRadiance)) break; rRec.type = RadianceQueryRecord::ERadianceNoEmission; /* Russian roulette - Possibly stop the recursion. Don't do this when dealing with a transmission component, since solid angle compression factors cause problems with the heuristic below */ if (rRec.depth >= m_rrDepth && !(bRec.sampledType & BSDF::ETransmission)) { /* Assuming that BSDF importance sampling is perfect, 'bsdfVal.max()' should equal the maximum albedo over all spectral samples */ Float approxAlbedo = std::min((Float) 0.9f, bsdfVal.max()); if (rRec.nextSample1D() > approxAlbedo) break; else pathThroughput /= approxAlbedo; } pathThroughput *= bsdfVal; rRec.depth++; } /* Store statistics */ avgPathLength.incrementBase(); avgPathLength += rRec.depth; return Li; }