655 lines
25 KiB
C++
655 lines
25 KiB
C++
/*
|
|
This file is part of Mitsuba, a physically based rendering system.
|
|
|
|
Copyright (c) 2007-2011 by Wenzel Jakob and others.
|
|
|
|
Mitsuba is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License Version 3
|
|
as published by the Free Software Foundation.
|
|
|
|
Mitsuba is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <mitsuba/core/plugin.h>
|
|
#include <mitsuba/hw/vpl.h>
|
|
#include <mitsuba/hw/gpuprogram.h>
|
|
#include <mitsuba/hw/gputexture.h>
|
|
#include "../shapes/instance.h"
|
|
|
|
MTS_NAMESPACE_BEGIN
|
|
|
|
VPLShaderManager::VPLShaderManager(const Scene *scene, Renderer *renderer)
|
|
: m_scene(scene), m_renderer(renderer), m_clamping(0.1f),
|
|
m_maxClipDist(std::numeric_limits<Float>::infinity()), m_initialized(false),
|
|
m_shadowMapResolution(512), m_singlePass(false),
|
|
m_diffuseSources(false), m_diffuseReceivers(false) {
|
|
}
|
|
|
|
VPLShaderManager::~VPLShaderManager() {
|
|
if (m_initialized)
|
|
cleanup();
|
|
}
|
|
|
|
void VPLShaderManager::init() {
|
|
if (m_renderer->getCapabilities()->isSupported(RendererCapabilities::EGeometryShaders)) {
|
|
m_shadowProgram = m_renderer->createGPUProgram("Shadow Program");
|
|
|
|
m_shadowProgram->setSource(GPUProgram::EVertexProgram,
|
|
"void main() {\n"
|
|
" gl_Position = gl_ModelViewMatrix * gl_Vertex;\n"
|
|
"}"
|
|
);
|
|
|
|
m_shadowProgram->setSource(GPUProgram::EGeometryProgram,
|
|
"#version 120\n"
|
|
"#extension GL_EXT_geometry_shader4 : enable\n"
|
|
"\n"
|
|
"uniform mat4 cubeMapTransform[6];\n"
|
|
"uniform vec4 depthVec[6];\n"
|
|
"varying float depth;\n"
|
|
"\n"
|
|
"void main() {\n"
|
|
" depth = 0;\n" // avoid an (incorrect?) warning
|
|
" for (int side = 0; side < 6; side++) {\n"
|
|
" gl_Layer = side;\n"
|
|
" for (int i = 0; i < gl_VerticesIn; i++) {\n"
|
|
" gl_Position = cubeMapTransform[side] * gl_PositionIn[i];\n"
|
|
" depth = dot(depthVec[side], gl_PositionIn[i]);\n"
|
|
" EmitVertex();\n"
|
|
" }\n"
|
|
" EndPrimitive();\n"
|
|
" }\n"
|
|
"}\n"
|
|
);
|
|
|
|
m_shadowProgram->setSource(GPUProgram::EFragmentProgram,
|
|
"#version 120\n"
|
|
"varying float depth;\n"
|
|
"void main() {\n"
|
|
" float dx = dFdx(depth), dy = dFdy(depth);"
|
|
" gl_FragDepth = depth + sqrt(dx*dx + dy*dy);"
|
|
"}\n"
|
|
);
|
|
|
|
/* Six output triangles per input triangle */
|
|
m_shadowProgram->setMaxVertices(18);
|
|
m_shadowProgram->init();
|
|
|
|
for (int i=0; i<6; ++i) {
|
|
m_shadowProgramParam_cubeMapTransform[i] =
|
|
m_shadowProgram->getParameterID(formatString("cubeMapTransform[%i]", i));
|
|
m_shadowProgramParam_depthVec[i] =
|
|
m_shadowProgram->getParameterID(formatString("depthVec[%i]", i));
|
|
}
|
|
}
|
|
|
|
m_altShadowProgram = m_renderer->createGPUProgram("Alternative Shadow Program");
|
|
m_altShadowProgram->setSource(GPUProgram::EVertexProgram,
|
|
"uniform mat4 cubeMapTransform;\n"
|
|
"uniform vec4 depthVec;\n"
|
|
"varying float depth;\n"
|
|
"void main() {\n"
|
|
" gl_Position = gl_ModelViewMatrix * (cubeMapTransform * gl_Vertex);\n"
|
|
" depth = dot(depthVec, gl_Vertex);\n"
|
|
"}\n"
|
|
);
|
|
|
|
m_altShadowProgram->setSource(GPUProgram::EFragmentProgram,
|
|
"#version 120\n"
|
|
"varying float depth;\n"
|
|
"void main() {\n"
|
|
" float dx = dFdx(depth), dy = dFdy(depth);"
|
|
" gl_FragDepth = depth + sqrt(dx*dx + dy*dy);"
|
|
"}\n"
|
|
);
|
|
|
|
m_altShadowProgram->init();
|
|
|
|
m_altShadowProgramParam_cubeMapTransform =
|
|
m_altShadowProgram->getParameterID("cubeMapTransform");
|
|
m_altShadowProgramParam_depthVec =
|
|
m_altShadowProgram->getParameterID("depthVec");
|
|
|
|
const std::vector<Shape *> shapes = m_scene->getShapes();
|
|
const std::vector<Luminaire *> luminaires = m_scene->getLuminaires();
|
|
|
|
for (size_t i=0; i<shapes.size(); ++i) {
|
|
ref<TriMesh> triMesh = shapes[i]->createTriMesh();
|
|
if (!triMesh) {
|
|
std::string shapeClass = shapes[i]->getClass()->getName();
|
|
if (shapeClass == "Instance") {
|
|
const Instance *instance = static_cast<const Instance *>(shapes[i]);
|
|
const std::vector<const Shape *> &subShapes =
|
|
instance->getShapeGroup()->getKDTree()->getShapes();
|
|
for (size_t j=0; j<subShapes.size(); ++j) {
|
|
triMesh = const_cast<Shape *>(subShapes[j])->createTriMesh();
|
|
if (!triMesh)
|
|
continue;
|
|
GPUGeometry *gpuGeo = m_renderer->registerGeometry(triMesh);
|
|
Shader *shader = triMesh->hasBSDF() ?
|
|
m_renderer->registerShaderForResource(triMesh->getBSDF()) : NULL;
|
|
if (shader != NULL && !shader->isComplete()) {
|
|
m_renderer->unregisterShaderForResource(triMesh->getBSDF());
|
|
} else if (shader != NULL && shader->getFlags() & Shader::ETransparent) {
|
|
m_transparentMeshes.push_back(std::make_pair(triMesh.get(), instance->getWorldTransform()));
|
|
continue;
|
|
}
|
|
m_meshes.push_back(std::make_pair(triMesh.get(), instance->getWorldTransform()));
|
|
if (gpuGeo)
|
|
m_drawList.push_back(std::make_pair(gpuGeo, instance->getWorldTransform()));
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
GPUGeometry *gpuGeo = m_renderer->registerGeometry(triMesh);
|
|
Shader *shader = triMesh->hasBSDF() ?
|
|
m_renderer->registerShaderForResource(triMesh->getBSDF()) : NULL;
|
|
if (shader != NULL && !shader->isComplete()) {
|
|
m_renderer->unregisterShaderForResource(triMesh->getBSDF());
|
|
} else if (shader != NULL && shader->getFlags() & Shader::ETransparent) {
|
|
m_transparentMeshes.push_back(std::make_pair(triMesh.get(), Transform()));
|
|
continue;
|
|
}
|
|
m_meshes.push_back(std::make_pair(triMesh.get(), Transform()));
|
|
if (gpuGeo)
|
|
m_drawList.push_back(std::make_pair(gpuGeo, Transform()));
|
|
}
|
|
|
|
for (size_t i=0; i<luminaires.size(); ++i)
|
|
m_renderer->registerShaderForResource(luminaires[i]);
|
|
|
|
if (m_scene->hasBackgroundLuminaire() &&
|
|
m_renderer->getShaderForResource(m_scene->getBackgroundLuminaire()) != NULL) {
|
|
Shader *shader = m_renderer->getShaderForResource(m_scene->getBackgroundLuminaire());
|
|
m_backgroundDependencies = VPLDependencyNode(shader);
|
|
int id = 0;
|
|
std::ostringstream oss;
|
|
std::string evalName = m_backgroundDependencies.recursiveGenerateCode(oss, id);
|
|
|
|
m_backgroundProgram = m_renderer->createGPUProgram("Background program");
|
|
m_backgroundProgram->setSource(GPUProgram::EVertexProgram,
|
|
"uniform mat4 clipToWorld;\n"
|
|
"varying vec3 d;\n"
|
|
"void main() {\n"
|
|
" gl_Position = ftransform();\n"
|
|
" vec4 tmp = clipToWorld * (gl_ModelViewProjectionMatrix * gl_Vertex);\n"
|
|
" d = tmp.xyz/tmp.w;"
|
|
"}\n"
|
|
);
|
|
|
|
oss << "varying vec3 d;" << endl
|
|
<< "uniform vec3 camPos;" << endl
|
|
<< "uniform float scale;" << endl
|
|
<< "void main() {" << endl
|
|
<< " gl_FragColor.rgb = scale * " << evalName << "_background(normalize(d - camPos));" << endl
|
|
<< " gl_FragColor.a = 1.0;" << endl
|
|
<< "}" << endl;
|
|
|
|
m_backgroundProgram->setSource(GPUProgram::EFragmentProgram, oss.str());
|
|
m_backgroundProgram->init();
|
|
|
|
id = 0;
|
|
m_backgroundDependencies.recursiveResolve(m_backgroundProgram, id);
|
|
}
|
|
|
|
m_initialized = true;
|
|
}
|
|
|
|
void VPLShaderManager::cleanup() {
|
|
for (std::map<std::string, ProgramAndConfiguration>::iterator it = m_programs.begin();
|
|
it != m_programs.end(); ++it) {
|
|
(*it).second.program->cleanup();
|
|
(*it).second.program->decRef();
|
|
}
|
|
|
|
if (m_shadowMap)
|
|
m_shadowMap->cleanup();
|
|
|
|
if (m_backgroundProgram) {
|
|
m_backgroundProgram->cleanup();
|
|
m_backgroundProgram = NULL;
|
|
}
|
|
|
|
const std::vector<Luminaire *> luminaires = m_scene->getLuminaires();
|
|
|
|
for (size_t i=0; i<m_meshes.size(); ++i) {
|
|
m_renderer->unregisterGeometry(m_meshes[i].first);
|
|
m_renderer->unregisterShaderForResource(m_meshes[i].first->getBSDF());
|
|
}
|
|
|
|
m_meshes.clear();
|
|
m_drawList.clear();
|
|
|
|
for (size_t i=0; i<luminaires.size(); ++i)
|
|
m_renderer->unregisterShaderForResource(luminaires[i]);
|
|
m_initialized = false;
|
|
}
|
|
|
|
|
|
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();
|
|
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);
|
|
m_shadowProgram->unbind();
|
|
} else {
|
|
/* Old-fashioned: render 6 times, once for each cube map face */
|
|
m_altShadowProgram->bind();
|
|
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);
|
|
}
|
|
|
|
m_altShadowProgram->unbind();
|
|
}
|
|
m_shadowMap->releaseTarget();
|
|
}
|
|
|
|
void VPLShaderManager::configure(const VPL &vpl, const BSDF *bsdf,
|
|
const Luminaire *luminaire, const Point &camPos, bool faceNormals) {
|
|
Shader *bsdfShader = m_renderer->getShaderForResource(bsdf);
|
|
Shader *vplShader = (vpl.type == ELuminaireVPL)
|
|
? m_renderer->getShaderForResource(vpl.luminaire)
|
|
: m_renderer->getShaderForResource(vpl.its.shape->getBSDF());
|
|
Shader *lumShader = (luminaire == NULL) ? NULL
|
|
: m_renderer->getShaderForResource(luminaire);
|
|
std::ostringstream oss;
|
|
|
|
if (bsdfShader == NULL || vplShader == NULL ||
|
|
(luminaire != NULL && lumShader == NULL)) {
|
|
/* Unsupported! */
|
|
m_renderer->setColor(Spectrum(0.0f));
|
|
return;
|
|
}
|
|
|
|
bool anisotropic = bsdf->getType() & BSDF::EAnisotropic;
|
|
|
|
m_targetConfig = VPLProgramConfiguration(vplShader, bsdfShader,
|
|
lumShader, faceNormals);
|
|
m_targetConfig.toString(oss);
|
|
std::string configName = oss.str();
|
|
std::map<std::string, ProgramAndConfiguration>::iterator it =
|
|
m_programs.find(configName);
|
|
GPUProgram *program = NULL;
|
|
|
|
if (it != m_programs.end()) {
|
|
/* A program for this configuration has been created previously */
|
|
m_current = (*it).second;
|
|
program = m_current.program;
|
|
} else {
|
|
/* No program for this particular combination exists -- create one */
|
|
program = m_renderer->createGPUProgram(configName);
|
|
|
|
if (faceNormals) {
|
|
/* Generate face normals in a geometry shader */
|
|
|
|
if (!m_renderer->getCapabilities()->isSupported(
|
|
RendererCapabilities::EGeometryShaders))
|
|
Log(EError, "Face normals require geometry shader support!");
|
|
if (anisotropic)
|
|
Log(EError, "Anisotropy and face normals can't be combined at the moment");
|
|
|
|
oss.str("");
|
|
oss << "#version 120" << endl
|
|
<< "#extension GL_EXT_geometry_shader4 : enable" << endl
|
|
<< "varying in vec3 lightVec_vertex[3], camVec_vertex[3];" << endl
|
|
<< "varying in vec2 uv_vertex[3];" << endl
|
|
<< "varying in vec3 vertexColor_vertex[3];" << endl
|
|
<< "varying out vec3 normal;" << endl
|
|
<< "varying out vec3 lightVec, camVec;" << endl
|
|
<< "varying out vec2 uv;" << endl
|
|
<< "varying out vec3 vertexColor;" << endl
|
|
<< endl
|
|
<< "void main() {" << endl
|
|
<< " vec3 edge1 = camVec_vertex[0]-camVec_vertex[1];" << endl
|
|
<< " vec3 edge2 = camVec_vertex[0]-camVec_vertex[2];" << endl
|
|
<< " normal = normalize(cross(edge1, edge2));" << endl
|
|
<< " gl_Position = vec4(0.0);" << endl
|
|
<< " lightVec = camVec = vec3(0.0);" << endl
|
|
<< " for (int i=0; i<gl_VerticesIn; ++i) {" << endl
|
|
<< " gl_Position = gl_PositionIn[i];" << endl
|
|
<< " uv = uv_vertex[i];" << endl
|
|
<< " vertexColor = vertexColor_vertex[i];" << endl
|
|
<< " lightVec = lightVec_vertex[i];" << endl
|
|
<< " camVec = camVec_vertex[i];" << endl
|
|
<< " EmitVertex();" << endl
|
|
<< " }" << endl
|
|
<< " EndPrimitive();" << endl
|
|
<< "}" << endl;
|
|
|
|
program->setMaxVertices(3);
|
|
program->setSource(GPUProgram::EGeometryProgram, oss.str());
|
|
}
|
|
|
|
/* Vertex program */
|
|
oss.str("");
|
|
oss << "#version 120" << endl;
|
|
if (anisotropic)
|
|
oss << "varying vec3 tangent;" << endl;
|
|
oss << "uniform vec3 vplPos, camPos;" << endl;
|
|
|
|
if (!faceNormals) {
|
|
oss << "varying vec3 lightVec, camVec;" << endl
|
|
<< "varying vec2 uv;" << endl
|
|
<< "varying vec3 normal;" << endl
|
|
<< "varying vec3 vertexColor;" << endl
|
|
<< endl
|
|
<< "void main() {" << endl
|
|
<< " uv = gl_MultiTexCoord0.xy;" << endl
|
|
<< " camVec = camPos - gl_Vertex.xyz;" << endl
|
|
<< " lightVec = vplPos - gl_Vertex.xyz;" << endl
|
|
<< " gl_Position = ftransform();" << endl
|
|
<< " vertexColor = gl_Color.rgb;" << endl
|
|
<< " normal = gl_Normal;" << endl;
|
|
} else {
|
|
oss << "varying vec3 lightVec_vertex, camVec_vertex;" << endl
|
|
<< "varying vec2 uv_vertex;" << endl
|
|
<< "varying vec3 vertexColor_vertex;" << endl
|
|
<< endl
|
|
<< "void main() {" << endl
|
|
<< " uv_vertex = gl_MultiTexCoord0.xy;" << endl
|
|
<< " camVec_vertex = camPos - gl_Vertex.xyz;" << endl
|
|
<< " lightVec_vertex = vplPos - gl_Vertex.xyz;" << endl
|
|
<< " gl_Position = ftransform();" << endl
|
|
<< " vertexColor_vertex = gl_Color.rgb;" << endl;
|
|
}
|
|
if (anisotropic)
|
|
oss << " tangent = gl_MultiTexCoord1.xyz;" << endl;
|
|
oss << "}" << endl;
|
|
|
|
program->setSource(GPUProgram::EVertexProgram, oss.str());
|
|
oss.str("");
|
|
|
|
oss << "#version 120" << endl
|
|
<< endl
|
|
<< "/* Uniform inputs */" << endl
|
|
<< "uniform samplerCube shadowMap;" << endl
|
|
<< "uniform vec3 vplPower, vplS, vplT, vplN, vplWi;" << endl
|
|
<< "uniform float nearClip, invClipRange, minDist, alpha;" << endl
|
|
<< "uniform vec2 vplUV;" << endl
|
|
<< "uniform bool diffuseSources, diffuseReceivers;" << endl
|
|
<< "varying vec3 vertexColor;" << endl
|
|
<< endl
|
|
<< "/* Inputs <- Vertex program */" << endl
|
|
<< "varying vec3 normal, lightVec, camVec;" << endl
|
|
<< "varying vec2 uv;" << endl;
|
|
if (anisotropic)
|
|
oss << "varying vec3 tangent;" << endl;
|
|
|
|
oss << endl
|
|
<< "/* Some helper functions for BSDF implementations */" << endl
|
|
<< "float cosTheta(vec3 v) { return v.z; }" << endl
|
|
<< "float sinTheta2(vec3 v) { return 1.0-v.z*v.z; }" << endl
|
|
<< "float sinTheta(vec3 v) { float st2 = sinTheta2(v); if (st2 <= 0) return 0.0; else return sqrt(sinTheta2(v)); }" << endl
|
|
<< "float tanTheta(vec3 v) { return sinTheta(v)/cosTheta(v); }" << endl
|
|
<< "float sinPhi(vec3 v) { return v.y/sinTheta(v); }" << endl
|
|
<< "float cosPhi(vec3 v) { return v.x/sinTheta(v); }" << endl
|
|
<< "const float pi = 3.141592653589;" << endl
|
|
<< endl;
|
|
|
|
std::string vplEvalName, bsdfEvalName, lumEvalName;
|
|
m_targetConfig.generateCode(oss, vplEvalName, bsdfEvalName, lumEvalName);
|
|
|
|
oss << "void main() {" << endl
|
|
<< " /* Set up an ONB */" << endl
|
|
<< " vec3 N = normalize(normal);" << endl;
|
|
if (anisotropic) {
|
|
oss << " vec3 S = normalize(tangent - dot(tangent, N)*N);" << endl;
|
|
} else {
|
|
oss << " vec3 S;" << endl
|
|
<< " if (abs(N.x) > abs(N.y)) {" << endl
|
|
<< " float invLen = 1.0 / sqrt(N.x*N.x + N.z*N.z);" << endl
|
|
<< " S = vec3(-N.z * invLen, 0.0, N.x * invLen);" << endl
|
|
<< " } else {" << endl
|
|
<< " float invLen = 1.0 / sqrt(N.y*N.y + N.z*N.z);" << endl
|
|
<< " S = vec3(0.0, -N.z * invLen, N.y * invLen);" << endl
|
|
<< " }" << endl;
|
|
}
|
|
oss << " vec3 T = cross(N, S);" << endl
|
|
<< endl
|
|
<< " /* Compute shadows */" << endl
|
|
<< " float d = length(lightVec);" << endl
|
|
<< " vec3 nLightVec = lightVec/d, absLightVec = abs(lightVec);" << endl
|
|
<< " float depth = max(max(absLightVec.x, absLightVec.y), absLightVec.z);" << endl
|
|
<< " depth = (depth-nearClip) * invClipRange - 0.005;" << endl
|
|
<< " float shadow = textureCube(shadowMap, nLightVec).r > depth ? 1.0 : 0.0;" << endl
|
|
<< endl
|
|
<< " /* Shading */" << endl
|
|
<< " vec3 nCamVec = normalize(camVec);" << endl
|
|
<< " vec3 wo = vec3(dot(S, nLightVec)," << endl
|
|
<< " dot(T, nLightVec)," << endl
|
|
<< " dot(N, nLightVec));" << endl
|
|
<< " vec3 wi = vec3(dot(S, nCamVec)," << endl
|
|
<< " dot(T, nCamVec)," << endl
|
|
<< " dot(N, nCamVec));" << endl
|
|
<< " vec3 vplWo = -vec3(dot(vplS, nLightVec)," << endl
|
|
<< " dot(vplT, nLightVec)," << endl
|
|
<< " dot(vplN, nLightVec));" << endl
|
|
<< " vec3 contrib = vplPower;" << endl
|
|
<< " if (!diffuseSources)" << endl
|
|
<< " contrib *= " << vplEvalName;
|
|
if (vpl.type == ESurfaceVPL)
|
|
oss << "(vplUV, vplWi, vplWo);" << endl;
|
|
else
|
|
oss << "_dir(vplWo);" << endl;
|
|
if (vpl.type == ESurfaceVPL)
|
|
oss << " else contrib *= abs(cosTheta(vplWo));" << endl;
|
|
oss << " if (d < minDist) d = minDist;" << endl
|
|
<< " if (!diffuseReceivers)" << endl
|
|
<< " contrib *= "<< bsdfEvalName << "(uv, wi, wo);" << endl
|
|
<< " else" << endl
|
|
<< " contrib *= " << bsdfEvalName << "_diffuse(uv, wi, wo);" << endl
|
|
<< " gl_FragColor.rgb = contrib";
|
|
if (vpl.type == ELuminaireVPL
|
|
&& (vpl.luminaire->getType() & Luminaire::EOnSurface))
|
|
oss << " * (shadow * abs(cosTheta(vplWo)) / (d*d))";
|
|
else
|
|
oss << " * (shadow / (d*d))";
|
|
if (luminaire != NULL) {
|
|
oss << endl;
|
|
oss << " + " << lumEvalName << "_area(uv)"
|
|
<< " * " << lumEvalName << "_dir(wi);" << endl;
|
|
} else {
|
|
oss << ";" << endl;
|
|
}
|
|
oss << " gl_FragColor.a = alpha;" << endl
|
|
<< "}" << endl;
|
|
|
|
program->setSource(GPUProgram::EFragmentProgram, oss.str());
|
|
try {
|
|
program->init();
|
|
} catch (const std::exception &) {
|
|
Log(EWarn, "Unable to compile the following VPL program:\n%s", oss.str().c_str());
|
|
throw;
|
|
}
|
|
|
|
m_targetConfig.resolve(program);
|
|
m_targetConfig.param_shadowMap = program->getParameterID("shadowMap", false);
|
|
m_targetConfig.param_vplPos = program->getParameterID("vplPos", false);
|
|
m_targetConfig.param_camPos = program->getParameterID("camPos", false);
|
|
m_targetConfig.param_vplPower = program->getParameterID("vplPower", false);
|
|
m_targetConfig.param_vplN = program->getParameterID("vplN", false);
|
|
m_targetConfig.param_vplS = program->getParameterID("vplS", false);
|
|
m_targetConfig.param_vplT = program->getParameterID("vplT", false);
|
|
m_targetConfig.param_vplWi = program->getParameterID("vplWi", false);
|
|
m_targetConfig.param_vplUV = program->getParameterID("vplUV", false);
|
|
m_targetConfig.param_nearClip = program->getParameterID("nearClip", false);
|
|
m_targetConfig.param_invClipRange = program->getParameterID("invClipRange", false);
|
|
m_targetConfig.param_minDist = program->getParameterID("minDist", false);
|
|
m_targetConfig.param_diffuseSources = program->getParameterID("diffuseSources", false);
|
|
m_targetConfig.param_diffuseReceivers = program->getParameterID("diffuseReceivers", false);
|
|
m_targetConfig.param_alpha = program->getParameterID("alpha", false);
|
|
m_current.program = program;
|
|
m_current.config = m_targetConfig;
|
|
m_programs[configName] = m_current;
|
|
program->incRef();
|
|
}
|
|
|
|
program->bind();
|
|
m_shadowMap->bind(0);
|
|
|
|
const VPLProgramConfiguration &config = m_current.config;
|
|
|
|
program->setParameter(config.param_shadowMap, m_shadowMap);
|
|
program->setParameter(config.param_vplPos, vpl.its.p);
|
|
program->setParameter(config.param_camPos, camPos);
|
|
program->setParameter(config.param_vplN, vpl.its.shFrame.n);
|
|
program->setParameter(config.param_vplS, vpl.its.shFrame.s);
|
|
program->setParameter(config.param_vplT, vpl.its.shFrame.t);
|
|
|
|
program->setParameter(config.param_alpha,
|
|
bsdfShader->getFlags() & Shader::ETransparent ? 0.5f : 1.0f);
|
|
|
|
if (vpl.type == ESurfaceVPL) {
|
|
program->setParameter(config.param_vplWi, vpl.its.wi);
|
|
program->setParameter(config.param_vplUV, vpl.its.uv);
|
|
program->setParameter(config.param_diffuseSources, m_diffuseSources);
|
|
}
|
|
|
|
Spectrum power = vpl.P;
|
|
if (m_diffuseSources && vpl.type == ESurfaceVPL)
|
|
power *= vpl.its.shape->getBSDF()->getDiffuseReflectance(vpl.its) * INV_PI;
|
|
program->setParameter(config.param_vplPower, power);
|
|
program->setParameter(config.param_diffuseReceivers, m_diffuseReceivers);
|
|
program->setParameter(config.param_nearClip, m_nearClip);
|
|
program->setParameter(config.param_invClipRange, m_invClipRange);
|
|
program->setParameter(config.param_minDist, m_minDist);
|
|
|
|
int textureUnitOffset = 1;
|
|
m_targetConfig.bind(program, config, textureUnitOffset);
|
|
}
|
|
|
|
void VPLShaderManager::drawBackground(const Transform &clipToWorld, const Point &camPos, Float scaleFactor) {
|
|
if (m_backgroundProgram == NULL)
|
|
return;
|
|
int textureUnitOffset = 0;
|
|
m_backgroundProgram->bind();
|
|
m_backgroundDependencies.recursiveBind(m_backgroundProgram,
|
|
m_backgroundDependencies, textureUnitOffset);
|
|
m_backgroundProgram->setParameter("clipToWorld", clipToWorld, false);
|
|
m_backgroundProgram->setParameter("camPos", camPos, false);
|
|
m_backgroundProgram->setParameter("scale", scaleFactor);
|
|
m_renderer->blitQuad(false);
|
|
m_backgroundProgram->unbind();
|
|
m_backgroundDependencies.recursiveUnbind();
|
|
}
|
|
|
|
void VPLShaderManager::unbind() {
|
|
if (m_current.program && m_current.program->isBound()) {
|
|
m_targetConfig.unbind();
|
|
m_current.program->unbind();
|
|
m_shadowMap->unbind();
|
|
}
|
|
}
|
|
|
|
MTS_IMPLEMENT_CLASS(VPLShaderManager, false, Object)
|
|
MTS_NAMESPACE_END
|