better medium integration + a two-sided BRDF adapter
parent
cd6d3f8a65
commit
77bcff3550
|
@ -106,8 +106,6 @@ class RENDERENGINE_mitsuba(bpy.types.RenderEngine):
|
|||
output_dir = scene_path
|
||||
else:
|
||||
output_dir = os.path.dirname(scene_path)
|
||||
if output_dir[-1] != '/':
|
||||
output_dir += '/'
|
||||
efutil.export_path = output_dir
|
||||
os.chdir(output_dir)
|
||||
|
||||
|
@ -245,4 +243,3 @@ class RENDERENGINE_mitsuba(bpy.types.RenderEngine):
|
|||
framebuffer_thread.kick(render_end=True)
|
||||
framebuffer_thread.shutdown()
|
||||
|
||||
|
||||
|
|
|
@ -401,7 +401,13 @@ class MtsExporter:
|
|||
if not hasattr(mat, 'name') or mat.name in self.exported_materials:
|
||||
return
|
||||
self.exported_materials += [mat.name]
|
||||
params = mat.mitsuba_material.get_params()
|
||||
mmat = mat.mitsuba_material
|
||||
params = mmat.get_params()
|
||||
twosided = False
|
||||
|
||||
if mmat.twosided and mmat.type in ['lambertian', 'phong', 'ward',
|
||||
'mirror', 'roughmetal', 'microfacet', 'composite']:
|
||||
twosided = True
|
||||
|
||||
for p in params:
|
||||
if p.type == 'reference_material':
|
||||
|
@ -409,14 +415,22 @@ class MtsExporter:
|
|||
elif p.type == 'reference_texture':
|
||||
self.exportTexture(self.findTexture(p.value))
|
||||
|
||||
self.openElement('bsdf', {'id' : '%s-material' % translate_id(mat.name), 'type' : mat.mitsuba_material.type})
|
||||
if twosided:
|
||||
self.openElement('bsdf', {'id' : '%s-material' % translate_id(mat.name), 'type' : 'twosided'})
|
||||
self.openElement('bsdf', {'type' : mmat.type})
|
||||
else:
|
||||
self.openElement('bsdf', {'id' : '%s-material' % translate_id(mat.name), 'type' : mmat.type})
|
||||
|
||||
params.export(self)
|
||||
self.closeElement()
|
||||
|
||||
if twosided:
|
||||
self.closeElement()
|
||||
|
||||
def exportEmission(self, obj):
|
||||
mult = lamp.intensity
|
||||
lamp = obj.data.materials[0].mitsuba_emission
|
||||
name = translate_id(obj.data.name)
|
||||
mult = lamp.intensity
|
||||
self.openElement('append', { 'id' : '%s-mesh_0' % name})
|
||||
self.openElement('luminaire', { 'id' : '%s-emission' % name, 'type' : 'area'})
|
||||
self.parameter('float', 'samplingWeight', {'value' : '%f' % lamp.samplingWeight})
|
||||
|
@ -426,23 +440,21 @@ class MtsExporter:
|
|||
self.closeElement()
|
||||
|
||||
def exportPreviewMesh(self, material):
|
||||
self.out.write('\t\t<shape id="Exterior-mesh_0" type="serialized">\n')
|
||||
self.out.write('\t\t\t<string name="filename" value="matpreview.serialized"/>\n')
|
||||
self.out.write('\t\t\t<integer name="shapeIndex" value="1"/>\n')
|
||||
self.out.write('\t\t\t<transform name="toWorld">\n')
|
||||
self.out.write('\t\t\t\t<matrix value="0.614046 0.614047 0 -1.78814e-07 -0.614047 0.614046 0 2.08616e-07 0 0 0.868393 1.02569 0 0 0 1"/>\n')
|
||||
self.out.write('\t\t\t</transform>\n')
|
||||
self.out.write('\t\t\t<ref id="%s-material" name="bsdf"/>\n' % translate_id(material.name))
|
||||
self.openElement('shape', {'id' : 'Exterior-mesh_0', 'type' : 'serialized'})
|
||||
self.parameter('string', 'filename', {'value' : 'matpreview.serialized'})
|
||||
self.parameter('integer', 'shapeIndex', {'value' : '1'})
|
||||
self.openElement('transform', {'name' : 'toWorld'})
|
||||
self.element('matrix', {'value' : '0.614046 0.614047 0 -1.78814e-07 -0.614047 0.614046 0 2.08616e-07 0 0 0.868393 1.02569 0 0 0 1'})
|
||||
self.closeElement()
|
||||
self.element('ref', {'name' : 'bsdf', 'id' : '%s-material' % translate_id(material.name)})
|
||||
lamp = material.mitsuba_emission
|
||||
if lamp and lamp.use_emission:
|
||||
mult = lamp.intensity
|
||||
self.out.write('\t\t\t<luminaire type="area">\n')
|
||||
self.out.write('\t\t\t\t<rgb name="intensity" value="%f %f %f"/>\n'
|
||||
% (lamp.color.r*mult, lamp.color.g*mult, lamp.color.b*mult))
|
||||
self.out.write('\t\t\t\t<float name="samplingWeight" value="%f"/>\n' % lamp.samplingWeight)
|
||||
self.out.write('\t\t\t</luminaire>\n')
|
||||
self.out.write('\t\t</shape>\n')
|
||||
self.out.write('\n')
|
||||
self.openElement('luminaire', {'type' : 'area'})
|
||||
self.parameter('rgb', 'intensity', { 'value' : "%f %f %f"
|
||||
% (lamp.color.r*mult, lamp.color.g*mult, lamp.color.b*mult)})
|
||||
self.closeElement()
|
||||
self.closeElement()
|
||||
|
||||
def exportCameraSettings(self, scene, camera):
|
||||
if scene.mitsuba_integrator.motionblur:
|
||||
|
@ -450,10 +462,20 @@ class MtsExporter:
|
|||
shuttertime = scene.mitsuba_integrator.shuttertime
|
||||
shutterOpen = (scene.frame_current - shuttertime/2) * frameTime
|
||||
shutterClose = (scene.frame_current + shuttertime/2) * frameTime
|
||||
self.out.write('\t<prepend id="%s-camera">\n' % translate_id(camera.name))
|
||||
self.out.write('\t\t<float name="shutterOpen" value="%f"/>\n' % shutterOpen)
|
||||
self.out.write('\t\t<float name="shutterClose" value="%f"/>\n' % shutterClose)
|
||||
self.out.write('\t</prepend>\n')
|
||||
self.openElement('prepend', {'id' : '%s-camera' % translate_id(camera.name)})
|
||||
self.parameter('float', 'shutterOpen', {'value' : str(shutterOpen)})
|
||||
self.parameter('float', 'shutterClose', {'value' : str(shutterClose)})
|
||||
self.closeElement()
|
||||
|
||||
def exportMedium(self, medium):
|
||||
self.openElement('medium', {'id' : medium.name, 'type' : medium.type})
|
||||
if medium.g == 0:
|
||||
self.element('phase', {'type' : 'isotropic'})
|
||||
else:
|
||||
self.openElement('phase', {'type' : 'hg'})
|
||||
self.parameter('float', 'g', {'value' : str(medium.g)})
|
||||
self.closeElement()
|
||||
self.closeElement()
|
||||
|
||||
def export(self, scene):
|
||||
if scene.mitsuba_engine.binary_path == '':
|
||||
|
@ -463,7 +485,6 @@ class MtsExporter:
|
|||
idx = 0
|
||||
# Force scene update; NB, scene.update() doesn't work
|
||||
scene.frame_set(scene.frame_current)
|
||||
|
||||
efutil.export_path = self.xml_filename
|
||||
try:
|
||||
os.mkdir(self.meshes_dir)
|
||||
|
@ -477,6 +498,8 @@ class MtsExporter:
|
|||
self.writeHeader()
|
||||
self.exportIntegrator(scene.mitsuba_integrator)
|
||||
self.exportSampler(scene.mitsuba_sampler)
|
||||
for medium in scene.mitsuba_media.media:
|
||||
self.exportMedium(medium)
|
||||
for obj in scene.objects:
|
||||
if obj.type == 'LAMP':
|
||||
self.exportLamp(obj, idx)
|
||||
|
@ -490,7 +513,7 @@ class MtsExporter:
|
|||
idx = idx+1
|
||||
self.writeFooter()
|
||||
(width, height) = resolution(scene)
|
||||
|
||||
|
||||
MtsLog("MtsBlend: Launching mtsimport")
|
||||
command = ['mtsimport', '-r', '%dx%d' % (width, height),
|
||||
'-n', '-l', 'pngfilm', self.dae_filename, self.xml_filename, self.adj_filename]
|
||||
|
|
|
@ -52,7 +52,7 @@ public:
|
|||
m_points.reserve(m_size.x*m_size.y);
|
||||
m_size = size; m_pos = PointType(0);
|
||||
generate(
|
||||
log2i(std::max(m_size.x, m_size.y)),
|
||||
log2i((uint32_t) std::max(m_size.x, m_size.y)),
|
||||
ENorth, EEast, ESouth, EWest);
|
||||
}
|
||||
|
||||
|
|
|
@ -104,17 +104,35 @@ extern MTS_EXPORT_CORE std::string formatString(const char *pFmt, ...);
|
|||
/// Base-2 logarithm
|
||||
extern MTS_EXPORT_CORE Float log2(Float value);
|
||||
|
||||
/// Base-2 logarithm (integer version)
|
||||
extern MTS_EXPORT_CORE int log2i(int value);
|
||||
/// Base-2 logarithm (32-bit integer version)
|
||||
extern MTS_EXPORT_CORE int log2i(uint32_t value);
|
||||
|
||||
/// Base-2 logarithm (64-bit integer version)
|
||||
extern MTS_EXPORT_CORE int log2i(uint64_t value);
|
||||
|
||||
/// Friendly modulo function (always positive)
|
||||
extern MTS_EXPORT_CORE int modulo(int a, int b);
|
||||
|
||||
/// Check if an integer is a power of two
|
||||
extern MTS_EXPORT_CORE bool isPowerOfTwo(unsigned int i);
|
||||
/// Check if an integer is a power of two (32 bit version)
|
||||
inline bool isPowerOfTwo(uint32_t i) {
|
||||
return (i & (i-1)) == 0;
|
||||
}
|
||||
|
||||
/// Check if an integer is a power of two (signed 32 bit version)
|
||||
inline bool isPowerOfTwo(int32_t i) {
|
||||
return i > 0 && (i & (i-1)) == 0;
|
||||
}
|
||||
|
||||
/// Check if an integer is a power of two (64 bit version)
|
||||
inline bool isPowerOfTwo(uint64_t i) {
|
||||
return (i & (i-1)) == 0;
|
||||
}
|
||||
|
||||
/// Round an integer to the next power of two
|
||||
extern MTS_EXPORT_CORE unsigned int roundToPowerOfTwo(unsigned int i);
|
||||
extern MTS_EXPORT_CORE uint32_t roundToPowerOfTwo(uint32_t i);
|
||||
|
||||
/// Round an integer to the next power of two (64 bit version)
|
||||
extern MTS_EXPORT_CORE uint64_t roundToPowerOfTwo(uint64_t i);
|
||||
|
||||
//// Windowed sinc filter (Lanczos envelope, tau=number of cycles)
|
||||
extern MTS_EXPORT_CORE Float lanczosSinc(Float t, Float tau = 2);
|
||||
|
|
|
@ -923,7 +923,7 @@ protected:
|
|||
KDLog(EError, "The exact primitive threshold must be >= 0");
|
||||
if (m_minMaxBins <= 1)
|
||||
KDLog(EError, "The number of min-max bins must be > 2");
|
||||
|
||||
|
||||
size_type primCount = cast()->getPrimitiveCount();
|
||||
if (primCount == 0) {
|
||||
KDLog(EWarn, "kd-tree contains no geometry!");
|
||||
|
@ -933,6 +933,9 @@ protected:
|
|||
return;
|
||||
}
|
||||
|
||||
if (primCount <= m_exactPrimThreshold)
|
||||
m_parallelBuild = false;
|
||||
|
||||
BuildContext ctx(primCount, m_minMaxBins);
|
||||
|
||||
/* Establish an ad-hoc depth cutoff value (Formula from PBRT) */
|
||||
|
|
|
@ -389,6 +389,7 @@ protected:
|
|||
|
||||
if (EXPECT_TAKEN(!vertexTangents)) {
|
||||
its.shFrame = Frame(normalize(n0 * b.x + n1 * b.y + n2 * b.z));
|
||||
its.dpdu = its.dpdv = Vector(0.0f);
|
||||
} else {
|
||||
const TangentSpace &t0 = vertexTangents[idx0];
|
||||
const TangentSpace &t1 = vertexTangents[idx1];
|
||||
|
@ -403,6 +404,7 @@ protected:
|
|||
}
|
||||
} else {
|
||||
its.shFrame = its.geoFrame;
|
||||
its.dpdu = its.dpdv = Vector(0.0f);
|
||||
}
|
||||
|
||||
if (EXPECT_TAKEN(vertexTexcoords)) {
|
||||
|
|
|
@ -12,5 +12,6 @@ plugins += env.SharedLibrary('#plugins/microfacet', ['microfacet.cpp'])
|
|||
plugins += env.SharedLibrary('#plugins/roughglass', ['roughglass.cpp'])
|
||||
plugins += env.SharedLibrary('#plugins/roughmetal', ['roughmetal.cpp'])
|
||||
plugins += env.SharedLibrary('#plugins/composite', ['composite.cpp'])
|
||||
plugins += env.SharedLibrary('#plugins/twosided', ['twosided.cpp'])
|
||||
|
||||
Export('plugins')
|
||||
|
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
This file is part of Mitsuba, a physically based rendering system.
|
||||
|
||||
Copyright (c) 2007-2010 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/render/bsdf.h>
|
||||
#include <mitsuba/render/consttexture.h>
|
||||
#include <mitsuba/hw/gpuprogram.h>
|
||||
|
||||
MTS_NAMESPACE_BEGIN
|
||||
|
||||
class TwoSidedBRDF : public BSDF {
|
||||
public:
|
||||
TwoSidedBRDF(const Properties &props)
|
||||
: BSDF(props) { }
|
||||
|
||||
TwoSidedBRDF(Stream *stream, InstanceManager *manager)
|
||||
: BSDF(stream, manager) {
|
||||
m_nestedBRDF = static_cast<BSDF *>(manager->getInstance(stream));
|
||||
configure();
|
||||
}
|
||||
|
||||
virtual ~TwoSidedBRDF() {
|
||||
if (m_type)
|
||||
delete m_type;
|
||||
}
|
||||
|
||||
void serialize(Stream *stream, InstanceManager *manager) const {
|
||||
BSDF::serialize(stream, manager);
|
||||
|
||||
manager->serialize(stream, m_nestedBRDF.get());
|
||||
}
|
||||
|
||||
void configure() {
|
||||
if (!m_nestedBRDF)
|
||||
Log(EError, "TwoSidedBRDF: A child BRDF instance is required");
|
||||
m_combinedType = m_nestedBRDF->getType();
|
||||
if (m_combinedType & BSDF::ETransmission)
|
||||
Log(EError, "TwoSidedBRDF: only BRDF child instances (without "
|
||||
"transmission) are supported");
|
||||
m_usesRayDifferentials = m_nestedBRDF->usesRayDifferentials();
|
||||
m_componentCount = m_nestedBRDF->getComponentCount();
|
||||
m_type = new unsigned int[m_componentCount];
|
||||
for (int i=0; i<m_nestedBRDF->getComponentCount(); ++i)
|
||||
m_type[i] = m_nestedBRDF->getType(i);
|
||||
}
|
||||
|
||||
Spectrum getDiffuseReflectance(const Intersection &its) const {
|
||||
return m_nestedBRDF->getDiffuseReflectance(its);
|
||||
}
|
||||
|
||||
Spectrum f(const BSDFQueryRecord &bRec) const {
|
||||
BSDFQueryRecord b(bRec);
|
||||
if (b.wi.z < 0) {
|
||||
b.wi.z *= -1;
|
||||
b.wo.z *= -1;
|
||||
}
|
||||
return m_nestedBRDF->f(b);
|
||||
}
|
||||
|
||||
|
||||
Float pdf(const BSDFQueryRecord &bRec) const {
|
||||
BSDFQueryRecord b(bRec);
|
||||
if (b.wi.z < 0) {
|
||||
b.wi.z *= -1;
|
||||
b.wo.z *= -1;
|
||||
}
|
||||
return m_nestedBRDF->pdf(b);
|
||||
}
|
||||
|
||||
|
||||
Spectrum sample(BSDFQueryRecord &bRec, const Point2 &sample) const {
|
||||
bool flip = false;
|
||||
if (bRec.wi.z < 0) {
|
||||
bRec.wi.z *= -1;
|
||||
flip = true;
|
||||
}
|
||||
Spectrum result = m_nestedBRDF->sample(bRec, sample);
|
||||
if (bRec.wi.z < 0 && !result.isZero()) {
|
||||
bRec.wi.z *= -1;
|
||||
bRec.wo.z *= -1;
|
||||
flip = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Spectrum sample(BSDFQueryRecord &bRec, Float &pdf, const Point2 &sample) const {
|
||||
bool flip = false;
|
||||
if (bRec.wi.z < 0) {
|
||||
bRec.wi.z *= -1;
|
||||
flip = true;
|
||||
}
|
||||
Spectrum result = m_nestedBRDF->sample(bRec, pdf, sample);
|
||||
if (bRec.wi.z < 0 && !result.isZero()) {
|
||||
bRec.wi.z *= -1;
|
||||
bRec.wo.z *= -1;
|
||||
flip = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void addChild(const std::string &name, ConfigurableObject *child) {
|
||||
if (child->getClass()->derivesFrom(BSDF::m_theClass)) {
|
||||
m_nestedBRDF = static_cast<BSDF *>(child);
|
||||
} else {
|
||||
BSDF::addChild(name, child);
|
||||
}
|
||||
}
|
||||
|
||||
std::string toString() const {
|
||||
std::ostringstream oss;
|
||||
oss << "TwoSided[" << endl
|
||||
<< " nestedBRDF = " << indent(m_nestedBRDF->toString()) << endl
|
||||
<< "]";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
|
||||
Shader *createShader(Renderer *renderer) const;
|
||||
|
||||
MTS_DECLARE_CLASS()
|
||||
protected:
|
||||
ref<BSDF> m_nestedBRDF;
|
||||
};
|
||||
|
||||
|
||||
// ================ Hardware shader implementation ================
|
||||
|
||||
class TwoSidedShader : public Shader {
|
||||
public:
|
||||
TwoSidedShader(Renderer *renderer,
|
||||
const BSDF *nestedBRDF) : Shader(renderer, EBSDFShader),
|
||||
m_nestedBRDF(nestedBRDF) {
|
||||
m_nestedBRDFShader = renderer->registerShaderForResource(nestedBRDF);
|
||||
}
|
||||
|
||||
bool isComplete() const {
|
||||
return m_nestedBRDFShader.get() != NULL;
|
||||
}
|
||||
|
||||
void putDependencies(std::vector<Shader *> &deps) {
|
||||
deps.push_back(m_nestedBRDFShader.get());
|
||||
}
|
||||
|
||||
void cleanup(Renderer *renderer) {
|
||||
renderer->unregisterShaderForResource(m_nestedBRDF);
|
||||
}
|
||||
|
||||
void generateCode(std::ostringstream &oss,
|
||||
const std::string &evalName,
|
||||
const std::vector<std::string> &depNames) const {
|
||||
oss << "vec3 " << evalName << "(vec2 uv, vec3 wi, vec3 wo) {" << endl
|
||||
<< " if (wi.z <= 0.0) {" << endl
|
||||
<< " wi.z *= -1; wo.z *= -1;" << endl
|
||||
<< " }" << endl
|
||||
<< " return " << depNames[0] << "(uv, wi, wo);" << endl
|
||||
<< "}" << endl
|
||||
<< "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl
|
||||
<< " if (wi.z <= 0.0) {" << endl
|
||||
<< " wi.z *= -1; wo.z *= -1;" << endl
|
||||
<< " }" << endl
|
||||
<< " return " << depNames[0] << "_diffuse(uv, wi, wo);" << endl
|
||||
<< "}" << endl;
|
||||
}
|
||||
|
||||
MTS_DECLARE_CLASS()
|
||||
private:
|
||||
const BSDF *m_nestedBRDF;
|
||||
ref<Shader> m_nestedBRDFShader;
|
||||
bool m_complete;
|
||||
};
|
||||
|
||||
Shader *TwoSidedBRDF::createShader(Renderer *renderer) const {
|
||||
return new TwoSidedShader(renderer, m_nestedBRDF.get());
|
||||
}
|
||||
|
||||
MTS_IMPLEMENT_CLASS(TwoSidedShader, false, Shader)
|
||||
MTS_IMPLEMENT_CLASS_S(TwoSidedBRDF, false, BSDF)
|
||||
MTS_EXPORT_PLUGIN(TwoSidedBRDF, "Two-sided BRDF adapter");
|
||||
MTS_NAMESPACE_END
|
|
@ -224,6 +224,7 @@ VertexData *fetchVertexData(Transform transform,
|
|||
result->typeToOffset[EUV] = offset;
|
||||
result->typeToOffsetInStream[EUV] = offsetInStream;
|
||||
result->typeToCount[EUV] = size;
|
||||
cout << "Got texture coordinates.." << endl;
|
||||
} else {
|
||||
SLog(EWarn, "Found multiple sets of texture coordinates - ignoring!");
|
||||
}
|
||||
|
|
|
@ -244,7 +244,7 @@ bool enableFPExceptions() {
|
|||
bool exceptionsWereEnabled = false;
|
||||
#if defined(WIN32)
|
||||
_clearfp();
|
||||
unsigned int cw = _controlfp(0, 0);
|
||||
uint32_t cw = _controlfp(0, 0);
|
||||
exceptionsWereEnabled = ~cw & (_EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW);
|
||||
cw &= ~(_EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW);
|
||||
_controlfp(cw, _MCW_EM);
|
||||
|
@ -268,7 +268,7 @@ bool disableFPExceptions() {
|
|||
bool exceptionsWereEnabled = false;
|
||||
#if defined(WIN32)
|
||||
_clearfp();
|
||||
unsigned int cw = _controlfp(0, 0);
|
||||
uint32_t cw = _controlfp(0, 0);
|
||||
exceptionsWereEnabled = ~cw & (_EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW);
|
||||
cw |= _EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW;
|
||||
_controlfp(cw, _MCW_EM);
|
||||
|
@ -288,7 +288,7 @@ bool disableFPExceptions() {
|
|||
void restoreFPExceptions(bool oldState) {
|
||||
bool currentState;
|
||||
#if defined(WIN32)
|
||||
unsigned int cw = _controlfp(0, 0);
|
||||
uint32_t cw = _controlfp(0, 0);
|
||||
currentState = ~cw & (_EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW);
|
||||
#elif defined(__OSX__)
|
||||
currentState = query_fpexcept_sse() != 0;
|
||||
|
@ -381,11 +381,18 @@ std::string formatString(const char *fmt, ...) {
|
|||
return std::string(tmp);
|
||||
}
|
||||
|
||||
int log2i(int value) {
|
||||
int r = 0;
|
||||
while ((value >> r) != 0)
|
||||
r++;
|
||||
return r-1;
|
||||
int log2i(uint32_t value) {
|
||||
int r = 0;
|
||||
while ((value >> r) != 0)
|
||||
r++;
|
||||
return r-1;
|
||||
}
|
||||
|
||||
int log2i(uint64_t value) {
|
||||
int r = 0;
|
||||
while ((value >> r) != 0)
|
||||
r++;
|
||||
return r-1;
|
||||
}
|
||||
|
||||
int modulo(int a, int b) {
|
||||
|
@ -394,11 +401,7 @@ int modulo(int a, int b) {
|
|||
}
|
||||
|
||||
/* Fast rounding & power-of-two test algorithms from PBRT */
|
||||
bool isPowerOfTwo(unsigned int i) {
|
||||
return (i & (i-1)) == 0;
|
||||
}
|
||||
|
||||
unsigned int roundToPowerOfTwo(unsigned int i) {
|
||||
uint32_t roundToPowerOfTwo(uint32_t i) {
|
||||
i--;
|
||||
i |= i >> 1; i |= i >> 2;
|
||||
i |= i >> 4; i |= i >> 8;
|
||||
|
@ -406,6 +409,13 @@ unsigned int roundToPowerOfTwo(unsigned int i) {
|
|||
return i+1;
|
||||
}
|
||||
|
||||
uint64_t roundToPowerOfTwo(uint64_t i) {
|
||||
i--;
|
||||
i |= i >> 1; i |= i >> 2;
|
||||
i |= i >> 4; i |= i >> 8;
|
||||
i |= i >> 16; i |= i >> 32;
|
||||
return i+1;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Numerical utility functions
|
||||
|
|
|
@ -32,8 +32,8 @@ MIPMap::MIPMap(int width, int height, Spectrum *pixels,
|
|||
Spectrum *texture = pixels;
|
||||
|
||||
if (!isPowerOfTwo(width) || !isPowerOfTwo(height)) {
|
||||
m_width = roundToPowerOfTwo(width);
|
||||
m_height = roundToPowerOfTwo(height);
|
||||
m_width = (int) roundToPowerOfTwo((uint32_t) width);
|
||||
m_height = (int) roundToPowerOfTwo((uint32_t) height);
|
||||
|
||||
/* The texture needs to be up-sampled */
|
||||
Spectrum *texture1 = new Spectrum[m_width*height];
|
||||
|
@ -87,7 +87,7 @@ MIPMap::MIPMap(int width, int height, Spectrum *pixels,
|
|||
delete[] texture1;
|
||||
}
|
||||
|
||||
m_levels = 1 + log2i(std::max(width, height));
|
||||
m_levels = 1 + log2i((uint32_t) std::max(width, height));
|
||||
m_pyramid = new Spectrum*[m_levels];
|
||||
m_pyramid[0] = texture;
|
||||
m_levelWidth = new int[m_levels];
|
||||
|
|
|
@ -285,7 +285,7 @@ void TriMesh::configure() {
|
|||
}
|
||||
|
||||
if (hasBSDF() && ((m_bsdf->getType() & BSDF::EAnisotropicMaterial)
|
||||
|| m_bsdf->usesRayDifferentials()) && !m_tangents)
|
||||
|| m_bsdf->usesRayDifferentials()) && !m_tangents)
|
||||
computeTangentSpaceBasis();
|
||||
}
|
||||
|
||||
|
@ -546,10 +546,12 @@ void TriMesh::computeNormals() {
|
|||
bool TriMesh::computeTangentSpaceBasis() {
|
||||
int zeroArea = 0, zeroNormals = 0;
|
||||
if (!m_texcoords) {
|
||||
if (hasBSDF() && m_bsdf->getType() & BSDF::EAnisotropicMaterial)
|
||||
Log(EError, "\"%s\": computeTangentSpace(): texture coordinates are required "
|
||||
"to generate tangent vectors. If you want to render with an anisotropic "
|
||||
"material, make sure that all assigned objects have texture coordinates.");
|
||||
bool anisotropic = hasBSDF() && m_bsdf->getType() & BSDF::EAnisotropicMaterial;
|
||||
if (anisotropic)
|
||||
Log(EError, "\"%s\": computeTangentSpace(): texture coordinates "
|
||||
"are required to generate tangent vectors. If you want to render with an anisotropic "
|
||||
"material, please make sure that all associated shapes have valid texture coordinates.",
|
||||
getName().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -470,7 +470,7 @@
|
|||
</action>
|
||||
<action name="actionSceneDescription">
|
||||
<property name="text">
|
||||
<string>Describe Scene..</string>
|
||||
<string>Scene Information ..</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
|
|
|
@ -183,17 +183,10 @@ void RenderSettingsDialog::onTreeSelectionChange(const QItemSelection &selected,
|
|||
|
||||
void RenderSettingsDialog::update() {
|
||||
int index = ui->integratorBox->currentIndex();
|
||||
Properties integratorProps;
|
||||
Properties integratorProps, samplerProps;
|
||||
|
||||
int sampleCount = -1;
|
||||
if (sender() == ui->samplerBox) {
|
||||
Properties samplerProps;
|
||||
if (sender() == ui->samplerBox)
|
||||
m_samplerNode->putProperties(samplerProps);
|
||||
if (samplerProps.hasProperty("sampleCount"))
|
||||
sampleCount = samplerProps.getInteger("sampleCount");
|
||||
else if (samplerProps.hasProperty("resolution"))
|
||||
sampleCount = (int) std::pow((Float) samplerProps.getInteger("resolution"), (Float) 2);
|
||||
}
|
||||
|
||||
if (sender() == ui->integratorBox)
|
||||
m_integratorNode->putProperties(integratorProps);
|
||||
|
@ -224,18 +217,6 @@ void RenderSettingsDialog::update() {
|
|||
m_aiNode = m_model->updateClass(m_aiNode, "", "");
|
||||
}
|
||||
|
||||
if (sender() == ui->samplerBox && sampleCount != -1) {
|
||||
std::string samplerPlugin = getPluginName(ui->samplerBox);
|
||||
for (int i=0; i<m_samplerNode->childCount(); ++i) {
|
||||
TreeItem *treeItem = m_samplerNode->child(i);
|
||||
if (treeItem->getName() == "sampleCount") {
|
||||
treeItem->setValue(sampleCount);
|
||||
} else if (treeItem->getName() == "resolution") {
|
||||
treeItem->setValue((int) std::sqrt((Float) sampleCount));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sender() == ui->integratorBox) {
|
||||
for (int i=0; i<m_integratorNode->childCount(); ++i) {
|
||||
TreeItem *treeItem = m_integratorNode->child(i);
|
||||
|
@ -243,6 +224,14 @@ void RenderSettingsDialog::update() {
|
|||
m_integratorNode->setProperty(treeItem->getName().toStdString(), integratorProps);
|
||||
}
|
||||
}
|
||||
|
||||
if (sender() == ui->samplerBox) {
|
||||
for (int i=0; i<m_samplerNode->childCount(); ++i) {
|
||||
TreeItem *treeItem = m_samplerNode->child(i);
|
||||
if (samplerProps.hasProperty(treeItem->getName().toStdString()))
|
||||
m_samplerNode->setProperty(treeItem->getName().toStdString(), samplerProps);
|
||||
}
|
||||
}
|
||||
|
||||
ui->treeView->expandAll();
|
||||
dataChanged();
|
||||
|
|
|
@ -1044,19 +1044,16 @@
|
|||
</plugin>
|
||||
|
||||
<plugin type="sampler" name="independent" readableName="Independent sampler" show="true" className="IndependentSampler" extends="Sampler">
|
||||
<descr>Independent sample generator - returns independent uniformly distributed random numbers on <tt>[0,1)</tt> and <tt>[0, 1)x[0, 1)</tt>.</descr>
|
||||
<param name="sampleCount" readableName="Samples per pixel" type="integer" default="1">Number of generated samples / samples per pixel.</param>
|
||||
<descr>Independent sample generator - computes independent uniformly distributed random numbers without any kind of stratification.</descr>
|
||||
<param name="sampleCount" readableName="Samples per pixel" type="integer" default="4">Number of samples per pixel</param>
|
||||
</plugin>
|
||||
|
||||
<plugin type="sampler" name="stratified" readableName="Stratified sampler" show="true" className="StratifiedSampler" extends="Sampler">
|
||||
<descr>
|
||||
Stratified sample generator. Given a resolution <tt>R</tt> and a depth <tt>D</tt>, it
|
||||
generates <tt>R*R</tt> samples, each of which can be queried for up to <tt>D</tt> 1-
|
||||
or 2-dimensional vectors by an integrator. The returned 1D/2D-vectors of a particular depth
|
||||
have the property of being stratified over all <tt>R*R</tt> samples. When
|
||||
the maximum depth is exceeded, this implementation reverts to independent sampling.
|
||||
<descr>Stratified sample generator - computes stratified samples in 1 and 2 dimensions.
|
||||
This only works up to a specified maximum depth, after which
|
||||
the implementation falls back to independent sampling.
|
||||
</descr>
|
||||
<param name="resolution" readableName="Stratification resolution" type="integer" default="2">Stratification resolution of the sample space. The default results in 2x2=4 samples per pixel</param>
|
||||
<param name="sampleCount" readableName="Samples per pixel" type="integer" default="4">Number of samples per pixel (will be rounded up to the next perfect square)</param>
|
||||
<param name="depth" readableName="Stratification depth" type="integer" default="3">Depth, up to which which stratified samples are guaranteed to be available.</param>
|
||||
</plugin>
|
||||
|
||||
|
@ -1066,7 +1063,7 @@
|
|||
Provides samples up to a specified depth, after which independent
|
||||
sampling takes over.
|
||||
</descr>
|
||||
<param name="sampleCount" readableName="Samples per pixel" type="integer" default="4">Number of generated samples / samples per pixel</param>
|
||||
<param name="sampleCount" readableName="Samples per pixel" type="integer" default="4">Number of samples per pixel (will be rounded up to the next power of two)</param>
|
||||
<param name="depth" readableName="Depth" type="integer" default="3">Depth, up to which which low discrepancy samples are guaranteed to be available.</param>
|
||||
</plugin>
|
||||
|
||||
|
|
|
@ -49,6 +49,9 @@
|
|||
<pointsize>8</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="lineWrapMode">
|
||||
<enum>QTextEdit::NoWrap</enum>
|
||||
</property>
|
||||
<property name="html">
|
||||
<string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
|
|
|
@ -31,7 +31,7 @@ public:
|
|||
|
||||
IndependentSampler(const Properties &props) : Sampler(props) {
|
||||
/* Number of samples per pixel when used with a sampling-based integrator */
|
||||
m_sampleCount = props.getSize("sampleCount", 1);
|
||||
m_sampleCount = props.getSize("sampleCount", 4);
|
||||
m_random = new Random();
|
||||
}
|
||||
|
||||
|
|
|
@ -27,8 +27,7 @@ MTS_NAMESPACE_BEGIN
|
|||
*/
|
||||
class LowDiscrepancySampler : public Sampler {
|
||||
public:
|
||||
LowDiscrepancySampler() : Sampler(Properties()) {
|
||||
}
|
||||
LowDiscrepancySampler() : Sampler(Properties()) { }
|
||||
|
||||
LowDiscrepancySampler(Stream *stream, InstanceManager *manager)
|
||||
: Sampler(stream, manager) {
|
||||
|
@ -44,23 +43,24 @@ public:
|
|||
}
|
||||
|
||||
LowDiscrepancySampler(const Properties &props) : Sampler(props) {
|
||||
/* Number of samples per pixel when used with a sampling-based integrator */
|
||||
/* Sample count (will be rounded up to the next power of two) */
|
||||
m_sampleCount = props.getSize("sampleCount", 4);
|
||||
|
||||
/* Depth, up to which which low discrepancy samples are guaranteed to be available. */
|
||||
m_depth = props.getInteger("depth", 3);
|
||||
|
||||
if (!isPowerOfTwo((int) m_sampleCount)) {
|
||||
m_sampleCount = roundToPowerOfTwo((int) m_sampleCount);
|
||||
Log(EWarn, "Sample count should be a power of two - rounding to %i", m_sampleCount);
|
||||
if (!isPowerOfTwo((uint64_t) m_sampleCount)) {
|
||||
m_sampleCount = (size_t) roundToPowerOfTwo((uint64_t) m_sampleCount);
|
||||
Log(EWarn, "Sample count should be a power of two -- rounding to "
|
||||
SIZE_T_FMT, m_sampleCount);
|
||||
}
|
||||
|
||||
m_samples1D = new Float*[m_depth];
|
||||
m_samples2D = new Point2*[m_depth];
|
||||
|
||||
for (int i=0; i<m_depth; i++) {
|
||||
m_samples1D[i] = new Float[(size_t) m_sampleCount];
|
||||
m_samples2D[i] = new Point2[(size_t) m_sampleCount];
|
||||
m_samples1D[i] = new Float[m_sampleCount];
|
||||
m_samples2D[i] = new Point2[m_sampleCount];
|
||||
}
|
||||
|
||||
m_random = new Random();
|
||||
|
@ -90,8 +90,8 @@ public:
|
|||
sampler->m_samples1D = new Float*[m_depth];
|
||||
sampler->m_samples2D = new Point2*[m_depth];
|
||||
for (int i=0; i<m_depth; i++) {
|
||||
sampler->m_samples1D[i] = new Float[(size_t) m_sampleCount];
|
||||
sampler->m_samples2D[i] = new Point2[(size_t) m_sampleCount];
|
||||
sampler->m_samples1D[i] = new Float[m_sampleCount];
|
||||
sampler->m_samples2D[i] = new Point2[m_sampleCount];
|
||||
}
|
||||
for (size_t i=0; i<m_req1D.size(); ++i)
|
||||
sampler->request2DArray(m_req1D[i]);
|
||||
|
|
|
@ -34,20 +34,31 @@ public:
|
|||
}
|
||||
|
||||
StratifiedSampler(const Properties &props) : Sampler(props) {
|
||||
/* Stratification resolution of the sample space. Default: 2,
|
||||
resulting in 2x2=4 samples per pixel */
|
||||
m_resolution = props.getInteger("resolution", 2);
|
||||
/* Sample count (will be rounded up to the next perfect square) */
|
||||
size_t desiredSampleCount = props.getSize("sampleCount", 4);
|
||||
|
||||
size_t i = 1;
|
||||
while (i * i < desiredSampleCount)
|
||||
++i;
|
||||
m_sampleCount = i*i;
|
||||
|
||||
if (m_sampleCount != desiredSampleCount) {
|
||||
Log(EWarn, "Sample count should be a perfect square -- rounding to "
|
||||
SIZE_T_FMT, m_sampleCount);
|
||||
}
|
||||
|
||||
m_resolution = (int) i;
|
||||
|
||||
/* Depth, up to which which stratified samples are guaranteed to be available. */
|
||||
m_depth = props.getInteger("depth", 3);
|
||||
|
||||
m_sampleCount = m_resolution*m_resolution;
|
||||
m_permutations1D = new unsigned int*[m_depth];
|
||||
m_permutations2D = new unsigned int*[m_depth];
|
||||
m_permutations1D = new uint32_t*[m_depth];
|
||||
m_permutations2D = new uint32_t*[m_depth];
|
||||
|
||||
for (int i=0; i<m_depth; i++) {
|
||||
m_permutations1D[i] = new unsigned int[m_sampleCount];
|
||||
m_permutations2D[i] = new unsigned int[m_sampleCount];
|
||||
m_permutations1D[i] = new uint32_t[m_sampleCount];
|
||||
m_permutations2D[i] = new uint32_t[m_sampleCount];
|
||||
}
|
||||
|
||||
m_invResolution = 1 / (Float) m_resolution;
|
||||
|
@ -60,11 +71,11 @@ public:
|
|||
m_depth = stream->readInt();
|
||||
m_resolution = stream->readInt();
|
||||
m_random = static_cast<Random *>(manager->getInstance(stream));
|
||||
m_permutations1D = new unsigned int*[m_depth];
|
||||
m_permutations2D = new unsigned int*[m_depth];
|
||||
m_permutations1D = new uint32_t*[m_depth];
|
||||
m_permutations2D = new uint32_t*[m_depth];
|
||||
for (int i=0; i<m_depth; i++) {
|
||||
m_permutations1D[i] = new unsigned int[m_sampleCount];
|
||||
m_permutations2D[i] = new unsigned int[m_sampleCount];
|
||||
m_permutations1D[i] = new uint32_t[m_sampleCount];
|
||||
m_permutations2D[i] = new uint32_t[m_sampleCount];
|
||||
}
|
||||
m_invResolution = 1.0f / m_resolution;
|
||||
m_invResolutionSquare = 1.0f / m_sampleCount;
|
||||
|
@ -96,11 +107,11 @@ public:
|
|||
sampler->m_invResolutionSquare = m_invResolutionSquare;
|
||||
sampler->m_random = new Random(m_random);
|
||||
|
||||
sampler->m_permutations1D = new unsigned int*[m_depth];
|
||||
sampler->m_permutations2D = new unsigned int*[m_depth];
|
||||
sampler->m_permutations1D = new uint32_t*[m_depth];
|
||||
sampler->m_permutations2D = new uint32_t*[m_depth];
|
||||
for (int i=0; i<m_depth; i++) {
|
||||
sampler->m_permutations1D[i] = new unsigned int[m_sampleCount];
|
||||
sampler->m_permutations2D[i] = new unsigned int[m_sampleCount];
|
||||
sampler->m_permutations1D[i] = new uint32_t[m_sampleCount];
|
||||
sampler->m_permutations2D[i] = new uint32_t[m_sampleCount];
|
||||
}
|
||||
for (size_t i=0; i<m_req1D.size(); ++i)
|
||||
sampler->request2DArray(m_req1D[i]);
|
||||
|
@ -112,11 +123,11 @@ public:
|
|||
void generate() {
|
||||
for (int i=0; i<m_depth; i++) {
|
||||
for (size_t j=0; j<m_sampleCount; j++)
|
||||
m_permutations1D[i][j] = (unsigned int) j;
|
||||
m_permutations1D[i][j] = (uint32_t) j;
|
||||
m_random->shuffle(&m_permutations1D[i][0], &m_permutations1D[i][m_sampleCount]);
|
||||
|
||||
for (size_t j=0; j<m_sampleCount; j++)
|
||||
m_permutations2D[i][j] = (unsigned int) j;
|
||||
m_permutations2D[i][j] = (uint32_t) j;
|
||||
m_random->shuffle(&m_permutations2D[i][0], &m_permutations2D[i][m_sampleCount]);
|
||||
}
|
||||
|
||||
|
@ -200,7 +211,7 @@ private:
|
|||
int m_resolution;
|
||||
int m_depth;
|
||||
Float m_invResolution, m_invResolutionSquare;
|
||||
unsigned int **m_permutations1D, **m_permutations2D;
|
||||
uint32_t **m_permutations1D, **m_permutations2D;
|
||||
int m_sampleDepth1D, m_sampleDepth2D;
|
||||
};
|
||||
|
||||
|
|
|
@ -38,7 +38,8 @@ public:
|
|||
/// When the file contains multiple meshes, this index specifies which one to load
|
||||
int shapeIndex = props.getInteger("shapeIndex", 0);
|
||||
|
||||
m_name = (props.getID() != "unnamed") ? props.getID() : formatString("%s@%i", filePath.stem().c_str(), shapeIndex);
|
||||
m_name = (props.getID() != "unnamed") ? props.getID()
|
||||
: formatString("%s@%i", filePath.stem().c_str(), shapeIndex);
|
||||
|
||||
/* Load the geometry */
|
||||
Log(EInfo, "Loading shape %i from \"%s\" ..", shapeIndex, filePath.leaf().c_str());
|
||||
|
|
Loading…
Reference in New Issue