Commit b57b56ca authored by Matt Pharr's avatar Matt Pharr

Add support for Ptex textures

parent f6edb5f1
......@@ -7,3 +7,6 @@
[submodule "src/ext/glog"]
path = src/ext/glog
url = https://github.com/google/glog.git
[submodule "src/ext/ptex"]
path = src/ext/ptex
url = https://github.com/wdas/ptex.git
......@@ -27,6 +27,13 @@ if(NOT IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/src/ext/glog")
"\"git submodule update --init --recursive\"")
endif()
if(NOT IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/src/ext/ptex")
message(FATAL_ERROR "The ptex submodule directory is missing! "
"You probably did not clone the project with --recursive, or first checked out "
"pbrt before it was added. It is possible to recover by running "
"\"git submodule update --init --recursive\"")
endif()
IF(CMAKE_COMPILER_IS_GNUCXX)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-conversion-null")
......@@ -147,6 +154,13 @@ INCLUDE_DIRECTORIES (
${CMAKE_BINARY_DIR}/src/ext/glog
)
SET(CMAKE_MACOSX_RPATH 1)
ADD_SUBDIRECTORY(src/ext/ptex)
SET_PROPERTY(TARGET Ptex_static PROPERTY FOLDER "dependencies")
INCLUDE_DIRECTORIES (
src/ext/ptex/src/ptex
)
SET ( PBRT_CORE_SOURCE
src/core/api.cpp
src/core/bssrdf.cpp
......@@ -281,6 +295,7 @@ TARGET_LINK_LIBRARIES ( pbrt_exe
${CMAKE_THREAD_LIBS_INIT}
${OPENEXR_LIBS}
glog
Ptex_static
)
......@@ -310,6 +325,7 @@ TARGET_LINK_LIBRARIES ( bsdftest
${CMAKE_THREAD_LIBS_INIT}
${OPENEXR_LIBS}
glog
Ptex_static
)
TARGET_LINK_LIBRARIES ( imgtool
......@@ -317,6 +333,7 @@ TARGET_LINK_LIBRARIES ( imgtool
${CMAKE_THREAD_LIBS_INIT}
${OPENEXR_LIBS}
glog
Ptex_static
)
# Unit test
......@@ -336,6 +353,7 @@ TARGET_LINK_LIBRARIES ( pbrt_test
${CMAKE_THREAD_LIBS_INIT}
${OPENEXR_LIBS}
glog
Ptex_static
)
ADD_TEST ( pbrt_unit_test
......
......@@ -107,6 +107,7 @@
#include "textures/imagemap.h"
#include "textures/marble.h"
#include "textures/mix.h"
#include "textures/ptex.h"
#include "textures/scale.h"
#include "textures/uv.h"
#include "textures/windy.h"
......@@ -503,6 +504,8 @@ std::shared_ptr<Texture<Float>> MakeFloatTexture(const std::string &name,
tex = CreateMarbleFloatTexture(tex2world, tp);
else if (name == "windy")
tex = CreateWindyFloatTexture(tex2world, tp);
else if (name == "ptex")
tex = CreatePtexFloatTexture(tex2world, tp);
else
Warning("Float texture \"%s\" unknown.", name.c_str());
tp.ReportUnused();
......@@ -537,6 +540,8 @@ std::shared_ptr<Texture<Spectrum>> MakeSpectrumTexture(
tex = CreateMarbleSpectrumTexture(tex2world, tp);
else if (name == "windy")
tex = CreateWindySpectrumTexture(tex2world, tp);
else if (name == "ptex")
tex = CreatePtexSpectrumTexture(tex2world, tp);
else
Warning("Spectrum texture \"%s\" unknown.", name.c_str());
tp.ReportUnused();
......
......@@ -44,7 +44,8 @@ namespace pbrt {
SurfaceInteraction::SurfaceInteraction(
const Point3f &p, const Vector3f &pError, const Point2f &uv,
const Vector3f &wo, const Vector3f &dpdu, const Vector3f &dpdv,
const Normal3f &dndu, const Normal3f &dndv, Float time, const Shape *shape)
const Normal3f &dndu, const Normal3f &dndv, Float time, const Shape *shape,
int faceIndex)
: Interaction(p, Normal3f(Normalize(Cross(dpdu, dpdv))), pError, wo, time,
nullptr),
uv(uv),
......@@ -52,7 +53,8 @@ SurfaceInteraction::SurfaceInteraction(
dpdv(dpdv),
dndu(dndu),
dndv(dndv),
shape(shape) {
shape(shape),
faceIndex(faceIndex) {
// Initialize shading geometry from true geometry
shading.n = n;
shading.dpdu = dpdu;
......
......@@ -122,7 +122,8 @@ class SurfaceInteraction : public Interaction {
const Point2f &uv, const Vector3f &wo,
const Vector3f &dpdu, const Vector3f &dpdv,
const Normal3f &dndu, const Normal3f &dndv, Float time,
const Shape *sh);
const Shape *sh,
int faceIndex = 0);
void SetShadingGeometry(const Vector3f &dpdu, const Vector3f &dpdv,
const Normal3f &dndu, const Normal3f &dndv,
bool orientationIsAuthoritative);
......@@ -148,6 +149,11 @@ class SurfaceInteraction : public Interaction {
BSSRDF *bssrdf = nullptr;
mutable Vector3f dpdx, dpdy;
mutable Float dudx = 0, dvdx = 0, dudy = 0, dvdy = 0;
// Added after book publication. Shapes can optionally provide a face
// index with an intersection point for use in Ptex texture lookups.
// If Ptex isn't being used, then this value is ignored.
int faceIndex = 0;
};
} // namespace pbrt
......
......@@ -181,6 +181,7 @@ enum class Prof {
GetSample,
TexFiltTrilerp,
TexFiltEWA,
TexFiltPtex,
NumProfCategories
};
......@@ -235,6 +236,7 @@ static const char *ProfNames[] = {
"Sampler::GetSample[12]D()",
"MIPMap::Lookup() (trilinear)",
"MIPMap::Lookup() (EWA)",
"Ptex lookup",
};
static_assert((int)Prof::NumProfCategories ==
......
Subproject commit 59eac370e52af48a3f4dab77a60f91523c3e70ac
......@@ -55,7 +55,8 @@ TriangleMesh::TriangleMesh(
const Transform &ObjectToWorld, int nTriangles, const int *vertexIndices,
int nVertices, const Point3f *P, const Vector3f *S, const Normal3f *N,
const Point2f *UV, const std::shared_ptr<Texture<Float>> &alphaMask,
const std::shared_ptr<Texture<Float>> &shadowAlphaMask)
const std::shared_ptr<Texture<Float>> &shadowAlphaMask,
const int *fIndices)
: nTriangles(nTriangles),
nVertices(nVertices),
vertexIndices(vertexIndices, vertexIndices + 3 * nTriangles),
......@@ -84,6 +85,9 @@ TriangleMesh::TriangleMesh(
s.reset(new Vector3f[nVertices]);
for (int i = 0; i < nVertices; ++i) s[i] = ObjectToWorld(S[i]);
}
if (fIndices)
faceIndices = std::vector<int>(fIndices, fIndices + nTriangles);
}
std::vector<std::shared_ptr<Shape>> CreateTriangleMesh(
......@@ -91,10 +95,11 @@ std::vector<std::shared_ptr<Shape>> CreateTriangleMesh(
bool reverseOrientation, int nTriangles, const int *vertexIndices,
int nVertices, const Point3f *p, const Vector3f *s, const Normal3f *n,
const Point2f *uv, const std::shared_ptr<Texture<Float>> &alphaMask,
const std::shared_ptr<Texture<Float>> &shadowAlphaMask) {
const std::shared_ptr<Texture<Float>> &shadowAlphaMask,
const int *faceIndices) {
std::shared_ptr<TriangleMesh> mesh = std::make_shared<TriangleMesh>(
*ObjectToWorld, nTriangles, vertexIndices, nVertices, p, s, n, uv,
alphaMask, shadowAlphaMask);
alphaMask, shadowAlphaMask, faceIndices);
std::vector<std::shared_ptr<Shape>> tris;
tris.reserve(nTriangles);
for (int i = 0; i < nTriangles; ++i)
......@@ -322,7 +327,7 @@ bool Triangle::Intersect(const Ray &ray, Float *tHit, SurfaceInteraction *isect,
// Fill in _SurfaceInteraction_ from triangle hit
*isect = SurfaceInteraction(pHit, pError, uvHit, -ray.d, dpdu, dpdv,
Normal3f(0, 0, 0), Normal3f(0, 0, 0), ray.time,
this);
this, faceIndex);
// Override surface normal in _isect_ for triangle
isect->n = isect->shading.n = Normal3f(Normalize(Cross(dp02, dp12)));
......@@ -667,6 +672,14 @@ std::vector<std::shared_ptr<Shape>> CreateTriangleMeshShape(
return std::vector<std::shared_ptr<Shape>>();
}
int nfi;
const int *faceIndices = params.FindInt("faceIndices", &nfi);
if (faceIndices && nfi != nvi / 3) {
Error("Number of face indices, %d, doesn't match number of faces, %d",
nfi, nvi / 3);
faceIndices = nullptr;
}
std::shared_ptr<Texture<Float>> alphaTex;
std::string alphaTexName = params.FindTexture("alpha");
if (alphaTexName != "") {
......@@ -692,7 +705,7 @@ std::vector<std::shared_ptr<Shape>> CreateTriangleMeshShape(
shadowAlphaTex.reset(new ConstantTexture<Float>(0.f));
return CreateTriangleMesh(o2w, w2o, reverseOrientation, nvi / 3, vi, npi, P,
S, N, uvs, alphaTex, shadowAlphaTex);
S, N, uvs, alphaTex, shadowAlphaTex, faceIndices);
}
} // namespace pbrt
......@@ -54,7 +54,8 @@ struct TriangleMesh {
const int *vertexIndices, int nVertices, const Point3f *P,
const Vector3f *S, const Normal3f *N, const Point2f *uv,
const std::shared_ptr<Texture<Float>> &alphaMask,
const std::shared_ptr<Texture<Float>> &shadowAlphaMask);
const std::shared_ptr<Texture<Float>> &shadowAlphaMask,
const int *faceIndices);
// TriangleMesh Data
const int nTriangles, nVertices;
......@@ -64,6 +65,7 @@ struct TriangleMesh {
std::unique_ptr<Vector3f[]> s;
std::unique_ptr<Point2f[]> uv;
std::shared_ptr<Texture<Float>> alphaMask, shadowAlphaMask;
std::vector<int> faceIndices;
};
class Triangle : public Shape {
......@@ -75,6 +77,7 @@ class Triangle : public Shape {
: Shape(ObjectToWorld, WorldToObject, reverseOrientation), mesh(mesh) {
v = &mesh->vertexIndices[3 * triNumber];
triMeshBytes += sizeof(*this);
faceIndex = mesh->faceIndices.size() ? mesh->faceIndices[triNumber] : 0;
}
Bounds3f ObjectBound() const;
Bounds3f WorldBound() const;
......@@ -107,6 +110,7 @@ class Triangle : public Shape {
// Triangle Private Data
std::shared_ptr<TriangleMesh> mesh;
const int *v;
int faceIndex;
};
std::vector<std::shared_ptr<Shape>> CreateTriangleMesh(
......@@ -114,7 +118,8 @@ std::vector<std::shared_ptr<Shape>> CreateTriangleMesh(
int nTriangles, const int *vertexIndices, int nVertices, const Point3f *p,
const Vector3f *s, const Normal3f *n, const Point2f *uv,
const std::shared_ptr<Texture<Float>> &alphaTexture,
const std::shared_ptr<Texture<Float>> &shadowAlphaTexture);
const std::shared_ptr<Texture<Float>> &shadowAlphaTexture,
const int *faceIndices = nullptr);
std::vector<std::shared_ptr<Shape>> CreateTriangleMeshShape(
const Transform *o2w, const Transform *w2o, bool reverseOrientation,
const ParamSet &params,
......
/*
pbrt source code is Copyright(c) 1998-2016
Matt Pharr, Greg Humphreys, and Wenzel Jakob.
This file is part of pbrt.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// textures/ptex.cpp*
#include "textures/ptex.h"
#include "error.h"
#include "interaction.h"
#include "paramset.h"
#include "stats.h"
#include <Ptexture.h>
namespace pbrt {
namespace {
// Reference count for the cache. Note: we assume that PtexTextures aren't
// being created/destroyed concurrently by multiple threads.
int nActiveTextures;
Ptex::PtexCache *cache;
STAT_COUNTER("Texture/Ptex lookups", nLookups);
STAT_COUNTER("Texture/Ptex files accessed", nFilesAccessed);
STAT_COUNTER("Texture/Ptex block reads", nBlockReads);
STAT_MEMORY_COUNTER("Memory/Ptex peak memory used", peakMemoryUsed);
struct : public PtexErrorHandler {
void reportError(const char *error) override { Error("%s", error); }
} errorHandler;
} // anonymous namespace
// PtexTexture Method Definitions
template <typename T>
PtexTexture<T>::PtexTexture(const std::string &filename) : filename(filename) {
if (!cache) {
CHECK_EQ(nActiveTextures, 0);
int maxFiles = 100;
size_t maxMem = 0; // unlimited. TODO: FIXME?
bool premultiply = true;
cache = Ptex::PtexCache::create(maxFiles, maxMem, premultiply, nullptr,
&errorHandler);
// TODO? cache->setSearchPath(...);
}
++nActiveTextures;
// Issue an error if the texture doesn't exist or has an unsupported
// number of channels.
valid = false;
Ptex::String error;
Ptex::PtexTexture *texture = cache->get(filename.c_str(), error);
if (!texture)
Error("%s", error.c_str());
else {
if (texture->numChannels() != 1 && texture->numChannels() != 3)
Error("%s: only one and three channel ptex textures are supported",
filename.c_str());
else {
valid = true;
LOG(INFO) << filename << ": added ptex texture";
}
texture->release();
}
}
template <typename T>
PtexTexture<T>::~PtexTexture() {
if (--nActiveTextures == 0) {
LOG(INFO) << "Releasing ptex cache";
Ptex::PtexCache::Stats stats;
cache->getStats(stats);
nFilesAccessed += stats.filesAccessed;
nBlockReads += stats.blockReads;
peakMemoryUsed = stats.peakMemUsed;
cache->release();
cache = nullptr;
}
}
template <typename T>
inline T fromResult(int nc, float *result) {
return T::unimplemented;
}
template <>
inline Float fromResult<Float>(int nc, float *result) {
if (nc == 1)
return result[0];
else
return (result[0] + result[1] + result[2]) / 3;
}
template <>
inline Spectrum fromResult<Spectrum>(int nc, float *result) {
if (nc == 1)
return Spectrum(result[0]);
else
return Spectrum::FromRGB(result);
}
template <typename T>
T PtexTexture<T>::Evaluate(const SurfaceInteraction &si) const {
ProfilePhase _(Prof::TexFiltPtex);
if (!valid) return T{};
++nLookups;
Ptex::String error;
Ptex::PtexTexture *texture = cache->get(filename.c_str(), error);
CHECK_NOTNULL(texture);
// TODO: make the filter an option?
Ptex::PtexFilter::Options opts(Ptex::PtexFilter::FilterType::f_bspline);
Ptex::PtexFilter *filter = Ptex::PtexFilter::getFilter(texture, opts);
int nc = texture->numChannels();
float result[3];
int firstChan = 0;
filter->eval(result, firstChan, nc, si.faceIndex, si.uv[0],
si.uv[1], si.dudx, si.dvdx, si.dudy, si.dvdy);
filter->release();
texture->release();
return fromResult<T>(nc, result);
}
PtexTexture<Float> *CreatePtexFloatTexture(const Transform &tex2world,
const TextureParams &tp) {
std::string filename = tp.FindFilename("filename");
return new PtexTexture<Float>(filename);
}
PtexTexture<Spectrum> *CreatePtexSpectrumTexture(const Transform &tex2world,
const TextureParams &tp) {
std::string filename = tp.FindFilename("filename");
return new PtexTexture<Spectrum>(filename);
}
} // namespace pbrt
/*
pbrt source code is Copyright(c) 1998-2016
Matt Pharr, Greg Humphreys, and Wenzel Jakob.
This file is part of pbrt.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#if defined(_MSC_VER)
#define NOMINMAX
#pragma once
#endif
#ifndef PBRT_TEXTURES_PTEX_H
#define PBRT_TEXTURES_PTEX_H
// textures/ptex.h*
#include "pbrt.h"
#include "texture.h"
#include <string>
namespace pbrt {
// PtexTexture Declarations
template <typename T>
class PtexTexture : public Texture<T> {
public:
// PtexTexture Public Methods
PtexTexture(const std::string &filename);
~PtexTexture();
T Evaluate(const SurfaceInteraction &) const;
private:
bool valid;
const std::string filename;
};
PtexTexture<Float> *CreatePtexFloatTexture(const Transform &tex2world,
const TextureParams &tp);
PtexTexture<Spectrum> *CreatePtexSpectrumTexture(const Transform &tex2world,
const TextureParams &tp);
} // namespace pbrt
#endif // PBRT_TEXTURES_PTEX_H
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment