void test01_shRotation() { /* Generate a random SH expansion, rotate it and spot-check 100 times against the original evaluated at appropriately rotated positions */ ref<Random> random = new Random(); int bands = 8; SHVector vec1(bands); for (int l=0; l<bands; ++l) for (int m=-l; m<=l; ++m) vec1(l, m) = random->nextFloat(); Vector axis(squareToSphere(Point2(random->nextFloat(), random->nextFloat()))); Transform trafo = Transform::rotate(axis, random->nextFloat()*360); Transform inv = trafo.inverse(); SHRotation rot(vec1.getBands()); SHVector::rotation(trafo, rot); SHVector vec2(bands); rot(vec1, vec2); for (int i=0; i<100; ++i) { Vector dir1(squareToSphere(Point2(random->nextFloat(), random->nextFloat()))), dir2; trafo(dir1, dir2); Float value1 = vec1.eval(dir2); Float value2 = vec2.eval(dir1); assertEqualsEpsilon(value1, value2, Epsilon); } }
Vector sampleDirection(Point2 sample, Float &pdf, Spectrum &value) const { #if defined(SAMPLE_UNIFORMLY) pdf = 1.0f / (4*M_PI); Vector d = squareToSphere(sample); value = Le(-d); return d; #endif }
void sampleEmission(EmissionRecord &eRec, const Point2 &sample1, const Point2 &sample2) const { eRec.sRec.p = m_position; eRec.d = squareToSphere(sample2); eRec.pdfDir = 1.0f / (4 * M_PI); eRec.pdfArea = 1; eRec.value = m_intensity; }
void sampleEmissionArea(EmissionRecord &eRec, const Point2 &sample) const { if (eRec.type == EmissionRecord::ENormal) { Vector d = squareToSphere(sample); eRec.sRec.p = m_bsphere.center + d * m_bsphere.radius; eRec.sRec.n = Normal(-d); eRec.pdfArea = 1.0f / (4 * M_PI * m_bsphere.radius * m_bsphere.radius); eRec.value = Spectrum(M_PI); } else { /* Preview mode, which is more suitable for VPL-based rendering: approximate the infinitely far-away source with set of diffuse point sources */ const Float radius = m_bsphere.radius * 1.5f; Vector d = squareToSphere(sample); eRec.sRec.p = m_bsphere.center + d * radius; eRec.sRec.n = Normal(-d); eRec.pdfArea = 1.0f / (4 * M_PI * radius * radius); eRec.value = Le(d) * M_PI; } }
void sampleEmissionArea(EmissionRecord &eRec, const Point2 &sample) const { Float radius = m_bsphere.radius; if (eRec.type == EmissionRecord::EPreview) { /* This is more suitable for VPL-based rendering */ radius *= 1.5; } Vector d = squareToSphere(sample); eRec.sRec.p = m_bsphere.center + d * radius; eRec.sRec.n = Normal(-d); eRec.pdfArea = 1.0f / (4 * M_PI * radius * radius); eRec.value = m_intensity * M_PI; }
/** * This is the tricky bit - we want to sample a ray that * has uniform density over the set of all rays passing * through the scene. * For more detail, see "Using low-discrepancy sequences and * the Crofton formula to compute surface areas of geometric models" * by Li, X. and Wang, W. and Martin, R.R. and Bowyer, A. * (Computer-Aided Design vol 35, #9, pp. 771--782) */ void sampleEmission(EmissionRecord &eRec, const Point2 &sample1, const Point2 &sample2) const { Assert(eRec.type == EmissionRecord::ENormal); /* Chord model - generate the ray passing through two uniformly distributed points on a sphere containing the scene */ Vector d = squareToSphere(sample1); eRec.sRec.p = m_bsphere.center + d * m_bsphere.radius; eRec.sRec.n = Normal(-d); Point p2 = m_bsphere.center + squareToSphere(sample2) * m_bsphere.radius; eRec.d = p2 - eRec.sRec.p; Float length = eRec.d.length(); if (length == 0) { eRec.value = Spectrum(0.0f); eRec.pdfArea = eRec.pdfDir = 1.0f; return; } eRec.d /= length; eRec.pdfArea = 1.0f / (4 * M_PI * m_bsphere.radius * m_bsphere.radius); eRec.pdfDir = INV_PI * dot(eRec.sRec.n, eRec.d); eRec.value = Le(-eRec.d); }
void sample(const Point &p, LuminaireSamplingRecord &lRec, const Point2 &sample) const { Vector d = squareToSphere(sample); Float nearHit, farHit; if (m_bsphere.contains(p) && m_bsphere.rayIntersect(Ray(p, d, 0.0f), nearHit, farHit)) { lRec.sRec.p = p + d * nearHit; lRec.pdf = 1.0f / (4*M_PI); lRec.sRec.n = normalize(m_bsphere.center - lRec.sRec.p); lRec.d = -d; lRec.value = m_intensity; } else { lRec.pdf = 0.0f; } }
Spectrum sampleEmissionDirection(EmissionRecord &eRec, const Point2 &sample) const { Float radius = m_bsphere.radius; if (eRec.type == EmissionRecord::EPreview) radius *= 1.5f; Point p2 = m_bsphere.center + squareToSphere(sample) * radius; eRec.d = p2 - eRec.sRec.p; Float length = eRec.d.length(); if (length == 0.0f) { eRec.pdfDir = 1.0f; return Spectrum(0.0f); } eRec.d /= length; eRec.pdfDir = INV_PI * dot(eRec.sRec.n, eRec.d); return Spectrum(INV_PI); }
void VPLShaderManager::setVPL(const VPL &vpl) { Point p = vpl.its.p + vpl.its.shFrame.n * 0.01; Intersection its; /* Estimate good near and far plane locations by tracing some rays */ Float nearClip = std::numeric_limits<Float>::infinity(), farClip = -std::numeric_limits<Float>::infinity(); Ray ray; ray.o = p; if (m_shadowMap == NULL || m_shadowMapResolution != m_shadowMap->getSize().x) { m_shadowMap = m_renderer->createGPUTexture("Shadow cube map", NULL); m_shadowMap->setSize(Point3i(m_shadowMapResolution, m_shadowMapResolution, 1)); m_shadowMap->setFrameBufferType(GPUTexture::EDepthBuffer); m_shadowMap->setType(GPUTexture::ETextureCubeMap); m_shadowMap->setWrapType(GPUTexture::EClampToEdge); m_shadowMap->setFilterType(GPUTexture::ENearest); m_shadowMap->setDepthMode(GPUTexture::ENormal); m_shadowMap->init(); } const int sampleCount = 200; const Float invSampleCount = 1.0f/sampleCount; for (int i=1; i<=sampleCount; ++i) { Vector dir; Point2 seed(i*invSampleCount, radicalInverse(2, i)); // Hammersley seq. if (vpl.type == ESurfaceVPL || vpl.luminaire->getType() & Luminaire::EOnSurface) dir = vpl.its.shFrame.toWorld(squareToHemispherePSA(seed)); else dir = squareToSphere(seed); ray.setDirection(dir); if (m_scene->rayIntersect(ray, its)) { nearClip = std::min(nearClip, its.t); farClip = std::max(farClip, its.t); } } m_minDist = nearClip + (farClip - nearClip) * m_clamping; nearClip = std::min(nearClip, (Float) 0.001f); farClip = std::min(farClip * 1.5f, m_maxClipDist); if (farClip < 0 || nearClip >= farClip) { /* Unable to find any surface - just default values based on the scene size */ nearClip = 1e-3f * m_scene->getBSphere().radius; farClip = 2 * m_scene->getBSphere().radius; m_minDist = 0; } farClip = std::min(farClip, 5.0f*m_scene->getBSphere().radius); m_nearClip = nearClip; m_invClipRange = 1/(farClip-nearClip); Transform lightViewTrafo, lightProjTrafo = Transform::glPerspective(90.0f, nearClip, farClip); Matrix4x4 identity; identity.setIdentity(); m_renderer->setCamera(identity, identity); m_shadowMap->activateTarget(); if (m_singlePass && m_shadowProgram != NULL) { /* "Fancy": render the whole cube map in a single pass using a geometry program. On anything but brand-new hardware, this is actually slower. */ m_shadowMap->activateSide(-1); m_shadowMap->clear(); m_shadowProgram->bind(); try { for (int i=0; i<6; ++i) { switch (i) { case 0: lightViewTrafo = Transform::lookAt(p, p + Vector(1, 0, 0), Vector(0, 1, 0)).inverse(); break; case 1: lightViewTrafo = Transform::lookAt(p, p + Vector(-1, 0, 0), Vector(0, 1, 0)).inverse(); break; case 2: lightViewTrafo = Transform::lookAt(p, p + Vector(0, 1, 0), Vector(0, 0, -1)).inverse(); break; case 3: lightViewTrafo = Transform::lookAt(p, p + Vector(0, -1, 0), Vector(0, 0, 1)).inverse(); break; case 4: lightViewTrafo = Transform::lookAt(p, p + Vector(0, 0, 1), Vector(0, 1, 0)).inverse(); break; case 5: lightViewTrafo = Transform::lookAt(p, p + Vector(0, 0, -1), Vector(0, 1, 0)).inverse(); break; } lightViewTrafo = Transform::scale(Vector(-1, 1, 1)) * lightViewTrafo; const Matrix4x4 &viewMatrix = lightViewTrafo.getMatrix(); m_shadowProgram->setParameter(m_shadowProgramParam_cubeMapTransform[i], lightProjTrafo * lightViewTrafo); m_shadowProgram->setParameter(m_shadowProgramParam_depthVec[i], Vector4( -viewMatrix.m[2][0] * m_invClipRange, -viewMatrix.m[2][1] * m_invClipRange, -viewMatrix.m[2][2] * m_invClipRange, (-viewMatrix.m[2][3] - m_nearClip) * m_invClipRange )); } m_renderer->drawAll(m_drawList); } catch (const std::exception &ex) { m_shadowProgram->unbind(); throw ex; } m_shadowProgram->unbind(); } else { /* Old-fashioned: render 6 times, once for each cube map face */ m_altShadowProgram->bind(); try { for (int i=0; i<6; ++i) { switch (i) { case 0: lightViewTrafo = Transform::lookAt(p, p + Vector(1, 0, 0), Vector(0, 1, 0)).inverse(); break; case 1: lightViewTrafo = Transform::lookAt(p, p + Vector(-1, 0, 0), Vector(0, 1, 0)).inverse(); break; case 2: lightViewTrafo = Transform::lookAt(p, p + Vector(0, 1, 0), Vector(0, 0, -1)).inverse(); break; case 3: lightViewTrafo = Transform::lookAt(p, p + Vector(0, -1, 0), Vector(0, 0, 1)).inverse(); break; case 4: lightViewTrafo = Transform::lookAt(p, p + Vector(0, 0, 1), Vector(0, 1, 0)).inverse(); break; case 5: lightViewTrafo = Transform::lookAt(p, p + Vector(0, 0, -1), Vector(0, 1, 0)).inverse(); break; } lightViewTrafo = Transform::scale(Vector(-1, 1, 1)) * lightViewTrafo; const Matrix4x4 &viewMatrix = lightViewTrafo.getMatrix(); m_altShadowProgram->setParameter(m_altShadowProgramParam_cubeMapTransform, lightProjTrafo * lightViewTrafo); m_altShadowProgram->setParameter(m_altShadowProgramParam_depthVec, Vector4( -viewMatrix.m[2][0] * m_invClipRange, -viewMatrix.m[2][1] * m_invClipRange, -viewMatrix.m[2][2] * m_invClipRange, (-viewMatrix.m[2][3] - m_nearClip) * m_invClipRange )); m_shadowMap->activateSide(i); m_shadowMap->clear(); m_renderer->drawAll(m_drawList); } } catch (std::exception &ex) { m_altShadowProgram->unbind(); throw ex; } m_altShadowProgram->unbind(); } m_shadowMap->releaseTarget(); }
Float sample(PhaseFunctionQueryRecord &pRec, Float &pdf, Sampler *sampler) const { pRec.wo = squareToSphere(sampler->next2D()); pdf = 1/(4 * M_PI); return f(pRec); }
Spectrum sampleEmissionDirection(EmissionRecord &eRec, const Point2 &sample) const { eRec.d = squareToSphere(sample); eRec.pdfDir = 1.0f / (4 * M_PI); return Spectrum(1.0f / (4*M_PI)); }