Skip to content

Commit

Permalink
Initial work on Histogram-preserving Blending for Randomized Texture …
Browse files Browse the repository at this point in the history
…Tiling (aka Procedural stochastic texturing, issue #228)
  • Loading branch information
Dade916 committed Aug 26, 2020
1 parent f313993 commit 8a4e736
Show file tree
Hide file tree
Showing 9 changed files with 321 additions and 7 deletions.
12 changes: 10 additions & 2 deletions include/slg/textures/imagemaptex.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ namespace slg {

class ImageMapTexture : public Texture {
public:
ImageMapTexture(const ImageMap *img, const TextureMapping2D *mp, const float g);
virtual ~ImageMapTexture() { delete mapping; }
ImageMapTexture(const ImageMap *img, const TextureMapping2D *mp, const float g,
const bool rt);
virtual ~ImageMapTexture();

virtual TextureType GetType() const { return IMAGEMAP; }
virtual float GetFloatValue(const HitPoint &hitPoint) const;
Expand All @@ -50,9 +51,16 @@ class ImageMapTexture : public Texture {
virtual luxrays::Properties ToProperties(const ImageMapCache &imgMapCache, const bool useRealFileName) const;

private:
luxrays::Spectrum SampleTile(const luxrays::UV &vertex, const luxrays::UV &offset) const;

const ImageMap *imageMap;
const TextureMapping2D *mapping;
float gain;
bool randomizedTiling;

// Used for randomized tiling
ImageMap *randomizedTilingInvLUT;
ImageMap *randomMap;
};

}
Expand Down
Binary file added scenes/randomizedtiling/pattern-11.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added scenes/randomizedtiling/pattern-3.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added scenes/randomizedtiling/pattern-7.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added scenes/randomizedtiling/pattern-9.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions scenes/randomizedtiling/rt.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
film.width = 1200
film.height = 900
scene.file = rt.scn
# Film image pipeline plug-ins
film.imagepipeline.0.type = TONEMAP_LINEAR
film.imagepipeline.1.type = GAMMA_CORRECTION
film.imagepipeline.1.value = 2.2
29 changes: 29 additions & 0 deletions scenes/randomizedtiling/rt.scn
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#scene.camera.lookat = 7.481132 -6.50764 5.343665 0.932513 -0.400984 0.891212
##
scene.camera.lookat.orig = 2.8718164 -5.47710514 4.63370657
scene.camera.lookat.target = -0.600622892 1.50124407 -1.6308527
################################################################################
scene.textures.img.file = pattern-3.jpg
scene.textures.img.gamma = 1.0
scene.textures.img.randomizedtiling = 1
scene.textures.img.mapping.uvscale = 2 2
################################################################################
scene.materials.mat_light.type = matte
scene.materials.mat_light.emission = 20. 20. 20.
scene.materials.mat_light.kd = 0.0 0.0 0.0
##
scene.materials.mat_red.type = matte
scene.materials.mat_red.kd = img
##
scene.materials.mat_white.type = matte
scene.materials.mat_white.kd = img
################################################################################
scene.objects.obj_light.ply = scenes/bump/mat_light.ply
scene.objects.obj_light.material = mat_light
#scene.objects.obj_red.ply = scenes/bump/mat_red.ply
#scene.objects.obj_red.material = mat_red
scene.objects.obj_white.ply = scenes/bump/mat_white_multi_uv.ply
scene.objects.obj_white.material = mat_white
################################################################################
scene.lights.infinitelight.type = constantinfinite
scene.lights.infinitelight.gain = 0.2 0.2 0.2
4 changes: 3 additions & 1 deletion src/slg/scene/parsetextures.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,11 @@ Texture *Scene::CreateTexture(const string &texName, const Properties &props) {

const ImageMapStorage::WrapType wrapType = ImageMapStorage::String2WrapType(
props.Get(Property(propName + ".wrap")("repeat")).Get<string>());

const bool randomizedTiling = props.Get(Property(propName + ".randomizedtiling")(false)).Get<bool>();

ImageMap *im = imgMapCache.GetImageMap(name, gamma, selectionType, storageType, wrapType);
tex = new ImageMapTexture(im, CreateTextureMapping2D(propName + ".mapping", props), gain);
tex = new ImageMapTexture(im, CreateTextureMapping2D(propName + ".mapping", props), gain, randomizedTiling);
} else if (texType == "constfloat1") {
const float v = props.Get(Property(propName + ".value")(1.f)).Get<float>();
tex = new ConstFloatTexture(v);
Expand Down
276 changes: 272 additions & 4 deletions src/slg/textures/imagemaptex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,293 @@
* limitations under the License. *
***************************************************************************/

#include "luxrays/core/geometry/matrix3x3.h"

#include "slg/textures/imagemaptex.h"
#include "luxrays/core/randomgen.h"

using namespace std;
using namespace luxrays;
using namespace slg;

//------------------------------------------------------------------------------
// Histogram-preserving Blending for Randomized Texture Tiling functions
//------------------------------------------------------------------------------

#define RT_HISTOGRAM_SIZE 256
#define RT_LUT_SIZE (RT_HISTOGRAM_SIZE * 4)
#define RT_RANDOM_IMAGE_SIZE 512

static inline Spectrum RGBToYCbCr(const Spectrum &rgb) {
// ITU-R BT.601 from https://en.wikipedia.org/wiki/YCbCr
static const float RGBToYCbCrOff[3] = {
16.f / 255.f,
128.f / 255.f,
128.f / 255.f
};
static const float RGBToYCbCrMat[3][3] = {
{ 65.481f / 255.f, 128.553f / 255.f, 24.966f / 255.f },
{ -37.797f / 255.f, -74.203f / 255.f, 112.f / 255.f },
{ 112.f / 255.f, -93.786f / 255.f, -18.214f / 255.f }
};

// RGB to YCbCr color space transformation
const float Y = RGBToYCbCrOff[0] + RGBToYCbCrMat[0][0] * rgb.c[0] + RGBToYCbCrMat[0][1] * rgb.c[1] + RGBToYCbCrMat[0][2] * rgb.c[2];
const float Cb = RGBToYCbCrOff[1] + RGBToYCbCrMat[1][0] * rgb.c[0] + RGBToYCbCrMat[1][1] * rgb.c[1] + RGBToYCbCrMat[1][2] * rgb.c[2];
const float Cr = RGBToYCbCrOff[2] + RGBToYCbCrMat[2][0] * rgb.c[0] + RGBToYCbCrMat[2][1] * rgb.c[1] + RGBToYCbCrMat[2][2] * rgb.c[2];

return Spectrum(Y, Cb, Cr).Clamp(0.f, 1.f);
}

static inline Spectrum YCbCrToRGB(const Spectrum &YCbCr) {
// ITU-R BT.601 from https://en.wikipedia.org/wiki/YCbCr
static const float YCbCrToRGBOff[3] = {
-222.921f / 256.f,
135.576f / 256.f,
-276.836f / 256.f
};
static const float YCbCrToRGBMat[3][3] = {
{ 298.082f / 256.f, 0.f / 256.f, 408.583f / 256.f },
{ 298.082f / 256.f, -100.291f / 256.f, -208.120f / 256.f },
{ 298.082f / 256.f, 516.412f / 256.f, 0.f / 256.f }
};

// YCbCr to RGB color space transformation
const float r = YCbCrToRGBOff[0] + YCbCrToRGBMat[0][0] * YCbCr.c[0] + YCbCrToRGBMat[0][1] * YCbCr.c[1] + YCbCrToRGBMat[0][2] * YCbCr.c[2];
const float g = YCbCrToRGBOff[1] + YCbCrToRGBMat[1][0] * YCbCr.c[0] + YCbCrToRGBMat[1][1] * YCbCr.c[1] + YCbCrToRGBMat[1][2] * YCbCr.c[2];
const float b = YCbCrToRGBOff[2] + YCbCrToRGBMat[2][0] * YCbCr.c[0] + YCbCrToRGBMat[2][1] * YCbCr.c[1] + YCbCrToRGBMat[2][2] * YCbCr.c[2];

return Spectrum(r, g, b).Clamp(0.f, 1.f);
}

static inline float Erf(float x) {
static const float a1 = .254829592f;
static const float a2 = -0.284496736f;
static const float a3 = 1.421413741f;
static const float a4 = -1.453152027f;
static const float a5 = 1.061405429f;
static const float p = .3275911f;

const float sign = (x < 0.f) ? -1.f : 1.f;
x = fabsf(x);

const float t = 1.f / (1.f + p * x);
const float y = 1.f - ((((a5 * t + a4) * t + a3) * t + a2) * t + a1) * t * expf(-x * x);

return sign * y;
}

static inline float Derf(const float x) {
return 2.f / sqrtf(M_PI) * expf(-x * x);
}

static inline float ErfInv(const float x) {
float y = 0.f;
float err;

do {
err = Erf(y) - x;
y -= err / Derf(y);
} while (fabsf(err) > 1e-5f);

return y;
}

static inline float C(const float sigma) {
return 1.f / Erf(.5f / (sigma * sqrtf(2.f)));
}

static inline float TruncCDFInv(const float x, const float sigma) {
const float cdfInv = .5f + sqrtf(2.f) * sigma * ErfInv((2.f * x - 1.f) / C(sigma));

return Clamp(cdfInv, 0.f, 1.f);
}

static inline float SoftClipContrast(float x, float W) {
const float u = (x > .5f) ? (1.f - x) : x;

float result;
if (u >= .5f - .25f * W)
result = (u - .5f) / W + .5f;
else if (W >= 2.f / 3.f)
result = 8.f * (1.f / W - 1.f) * Sqr(u / (2.f - W)) + (3.f - 2.f / W) * u / (2.f - W);
else if (u >= .5f - .75f * W)
result = Sqr((u - (.5f - .75f * W)) / W);
else
result = 0.f;

if (x > .5f)
result = 1.f - result;

return result;
}

//------------------------------------------------------------------------------
// ImageMap texture
//------------------------------------------------------------------------------

ImageMapTexture::ImageMapTexture(const ImageMap *img, const TextureMapping2D *mp, const float g) :
imageMap(img), mapping(mp), gain(g) {
ImageMapTexture::ImageMapTexture(const ImageMap *img, const TextureMapping2D *mp,
const float g, const bool rt) :
imageMap(img), mapping(mp), gain(g), randomizedTiling(rt),
randomizedTilingInvLUT(nullptr), randomMap(nullptr) {
if (!randomizedTiling)
return;

// Preprocessing work for Histogram-preserving Blending for Randomized Texture Tiling
//
// From Benedikt Bitterli's "Histogram-preserving Blending for Randomized Texture Tiling"
// (http://www.jcgt.org/published/0008/04/02/paper.pdf)
// and WebGL demo: https://benedikt-bitterli.me/histogram-tiling

vector<float> histogram(RT_HISTOGRAM_SIZE, 0.f);

const u_int width = img->GetWidth();
const u_int height = img->GetHeight();
const u_int pixelsCount = width * height;

for (u_int i = 0; i < pixelsCount; ++i) {
// Read the pixel RGB
const Spectrum rgb = imageMap->GetStorage()->GetSpectrum(i);

// RGB to YCbCr color space transformation
const Spectrum ycbcr = RGBToYCbCr(rgb);

// Fill the histogram
const u_int histogramIndex = Min<u_int>(Floor2UInt(ycbcr.c[0] * RT_HISTOGRAM_SIZE), RT_HISTOGRAM_SIZE - 1);
histogram[histogramIndex]++;
}

// Transform the histogram in a CDF
for (u_int i = 1; i < RT_HISTOGRAM_SIZE; ++i)
histogram[i] += histogram[i - 1];

vector<float> lut(RT_HISTOGRAM_SIZE);
for (u_int i = 0; i < RT_HISTOGRAM_SIZE; ++i)
lut[i] = Clamp(TruncCDFInv(histogram[i] / histogram[RT_HISTOGRAM_SIZE - 1], 1.f / 6.f), 0.f, 1.f);

// TODO: preprocess imageMap

// Initialize randomized tiling LUT
randomizedTilingInvLUT = ImageMap::AllocImageMap<float>(1.f, 1, RT_LUT_SIZE, 1, ImageMapStorage::REPEAT);
float *randomizedTilingInvLUTData = (float *)randomizedTilingInvLUT->GetStorage()->GetPixelsData();
for (u_int i = 0; i < RT_LUT_SIZE; ++i) {
const float f = (i + .5f) / RT_LUT_SIZE;

randomizedTilingInvLUTData[i] = 1.f;
for (u_int j = 0; j < RT_HISTOGRAM_SIZE; ++j) {
if (f < histogram[j]) {
randomizedTilingInvLUTData[i] = j / (RT_HISTOGRAM_SIZE - 1.f);
break;
}
}
}

// Initialized the random image map
randomMap = ImageMap::AllocImageMap<float>(1.f, 3, RT_RANDOM_IMAGE_SIZE, RT_RANDOM_IMAGE_SIZE, ImageMapStorage::REPEAT);

RandomGenerator rndGen(123);
float *randomMapData = (float *)randomMap->GetStorage()->GetPixelsData();
for (u_int i = 0; i < 3 * RT_RANDOM_IMAGE_SIZE * RT_RANDOM_IMAGE_SIZE; ++i)
randomMapData[i] = rndGen.floatValue();
}

ImageMapTexture::~ImageMapTexture() {
delete mapping;
delete randomizedTilingInvLUT;
delete randomMap;
}

float ImageMapTexture::GetFloatValue(const HitPoint &hitPoint) const {
return gain * imageMap->GetFloat(mapping->Map(hitPoint));
const UV mappedUV = mapping->Map(hitPoint);

float value;
if (randomizedTiling) {
// TODO
value = 0.f;
} else
value = imageMap->GetFloat(mappedUV);

return gain * value;
}

Spectrum ImageMapTexture::SampleTile(const UV &vertex, const UV &offset) const {
const Spectrum noise = randomMap->GetSpectrum(vertex / RT_HISTOGRAM_SIZE);

const UV pos = UV(.25f, .25f) + UV(noise.c[0], noise.c[1]) * .5f + offset;

return RGBToYCbCr(imageMap->GetSpectrum(pos));
}

Spectrum ImageMapTexture::GetSpectrumValue(const HitPoint &hitPoint) const {
return gain * imageMap->GetSpectrum(mapping->Map(hitPoint));
static const float triangleSize = .25f;
static const float a = triangleSize * cosf(M_PI / 3.f);
static const float b = triangleSize;
static const float c = triangleSize * sinf(M_PI / 3.f);
static const float d = 0.f;
static const float det = a * d - c * b;
static const float latticeToSurface[2][2] = {
{ a, b },
{ c, d }
};
static const float surfaceToLattice[2][2] = {
{ d / det, -b / det },
{ -c / det, a / det }
};

const UV pos = mapping->Map(hitPoint);

Spectrum value;
if (randomizedTiling) {
const UV lattice(
surfaceToLattice[0][0] * pos.u + surfaceToLattice[0][1] * pos.v,
surfaceToLattice[1][0] * pos.u + surfaceToLattice[1][1] * pos.v
);
const UV cell = UV(floorf(lattice.u), floorf(lattice.v));
UV uv = lattice - cell;

UV v0 = cell;
if (uv.u + uv.v >= 1.f) {
v0 += UV(1.f, 1.f);
uv = UV(1.f, 1.f) - UV(uv.v, uv.u);
}
UV v1 = cell + UV(1.f, 0.f);
UV v2 = cell + UV(0.f, 1.f);

const UV v0offset(
pos.u - (latticeToSurface[0][0] * v0.u + latticeToSurface[0][1] * v0.v),
pos.v - (latticeToSurface[1][0] * v0.u + latticeToSurface[1][1] * v0.v)
);
const UV v1offset(
pos.u - (latticeToSurface[0][0] * v1.u + latticeToSurface[0][1] * v1.v),
pos.v - (latticeToSurface[1][0] * v1.u + latticeToSurface[1][1] * v1.v)
);
const UV v2offset(
pos.u - (latticeToSurface[0][0] * v2.u + latticeToSurface[0][1] * v2.v),
pos.v - (latticeToSurface[1][0] * v2.u + latticeToSurface[1][1] * v2.v)
);

const Spectrum color0 = SampleTile(v0, v0offset);
const Spectrum color1 = SampleTile(v1, v1offset);
const Spectrum color2 = SampleTile(v2, v2offset);

Vector uvWeights(1.f - uv.u - uv.v, uv.u, uv.v);

uvWeights.x = uvWeights.x * uvWeights.x * uvWeights.x;
uvWeights.y = uvWeights.y * uvWeights.y * uvWeights.y;
uvWeights.z = uvWeights.z * uvWeights.z * uvWeights.z;

uvWeights /= uvWeights.x + uvWeights.y + uvWeights.z;

Spectrum YCbCr = uvWeights.x * color0 + uvWeights.y * color1 + uvWeights.z * color2;

YCbCr.c[0] = SoftClipContrast(YCbCr.c[0], uvWeights.x + uvWeights.y + uvWeights.z);
//YCbCr.c[0] = randomizedTilingInvLUT->GetFloat(UV(YCbCr.c[0], .5f));

value = YCbCrToRGB(YCbCr);
} else
value = imageMap->GetSpectrum(pos);

return gain * value;
}

Normal ImageMapTexture::Bump(const HitPoint &hitPoint, const float sampleDistance) const {
Expand Down Expand Up @@ -67,6 +334,7 @@ Properties ImageMapTexture::ToProperties(const ImageMapCache &imgMapCache,
props.Set(Property("scene.textures." + name + ".gain")(gain));
props.Set(imageMap->ToProperties("scene.textures." + name, false));
props.Set(mapping->ToProperties("scene.textures." + name + ".mapping"));
props.Set(Property("scene.textures." + name + ".randomizedtiling")(randomizedTiling));

return props;
}

0 comments on commit 8a4e736

Please sign in to comment.