@@ -1184,6 +1184,91 @@ make_texture_impl(ImageBufAlgo::MakeTextureMode mode, const ImageBuf* input,
1184
1184
mode = ImageBufAlgo::MakeTxTexture;
1185
1185
src = bumpslopes;
1186
1186
}
1187
+
1188
+ if (configspec.get_int_attribute (" maketx:cdf" )) {
1189
+ // Writes Gaussian CDF and Inverse Gaussian CDF as per-channel metadata. We provide both
1190
+ // the inverse transfrom and forward transfrom, so in theory we're free to change
1191
+ // the distribution.
1192
+ //
1193
+ // References:
1194
+ //
1195
+ // Brent Burley, On Histogram-Preserving Blending for Randomized Texture Tiling,
1196
+ // Journal of Computer Graphics Techniques (JCGT), vol. 8, no. 4, 31-53, 2019
1197
+ //
1198
+ // Eric Heitz and Fabrice Neyret, High-Performance By-Example Noise using a Histogram-Preserving Blending Operator,
1199
+ // https://hal.inria.fr/hal-01824773},
1200
+ // Proceedings of the ACM on Computer Graphics and Interactive Techniques,
1201
+ // ACM SIGGRAPH / Eurographics Symposium on High-Performance Graphics 2018,
1202
+ //
1203
+ // Benedikt Bitterli
1204
+ // https://benedikt-bitterli.me/histogram-tiling/
1205
+
1206
+ const float cdf_sigma = configspec.get_float_attribute (
1207
+ " maketx:cdfsigma" );
1208
+ const int cdf_bits = configspec.get_int_attribute (" maketx:cdfbits" );
1209
+ const uint64_t bins = 1 << cdf_bits;
1210
+
1211
+ // Normalization coefficient for the truncated normal distribution
1212
+ const float c_sigma_inv = fast_erf (1 .0f / (2 .0f * M_SQRT2 * cdf_sigma));
1213
+
1214
+ // If there are channels other than R,G,B,A, we probably shouldn't do anything
1215
+ // to them.
1216
+
1217
+ const int channels = std::min (4 , src->spec ().nchannels );
1218
+
1219
+ std::vector<float > invCDF (bins);
1220
+ std::vector<float > CDF (bins);
1221
+ std::vector<imagesize_t > hist;
1222
+
1223
+ for (int i = 0 ; i < channels; i++) {
1224
+ hist = ImageBufAlgo::histogram (*src, i, bins, 0 .0f , 1 .0f );
1225
+
1226
+ // Turn the histogram into a non-normalized CDF
1227
+ for (int j = 1 ; j < bins; j++) {
1228
+ hist[j] += hist[j - 1 ];
1229
+ }
1230
+
1231
+ // Store the inverse CDF as a lookup-table which we'll use to transform
1232
+ // the image data to a Guassian distribution. As mentioned in Burley [2019]
1233
+ // we're combining two steps here when using the invCDF lookup
1234
+ // table: we first "look up" the image value through its CDF (the normalized histogram)
1235
+ // which gives us a uniformly distributed value, which we're then feeding in to the
1236
+ // Gaussian inverse CDF to transfrom the unifrom distribution to Gaussian.
1237
+
1238
+ for (int j = 0 ; j < bins; j++) {
1239
+ float u = float (hist[j]) / hist[bins - 1 ];
1240
+ float g = 0.5
1241
+ + cdf_sigma * M_SQRT2
1242
+ * fast_ierf (c_sigma_inv * (2 * u - 1 ));
1243
+ invCDF[j] = std::min (1 .0f , std::max (0 .0f , g));
1244
+ }
1245
+ configspec.attribute (" invCDF_" + std::to_string (i),
1246
+ TypeDesc (TypeDesc::FLOAT, bins),
1247
+ invCDF.data ());
1248
+
1249
+
1250
+ // Store the forward CDF as a lookup table to transform back
1251
+ // to the original image distribution from a Gaussian distribution.
1252
+
1253
+ std::vector<float >::iterator upper;
1254
+ for (int j = 0 ; j < bins; j++) {
1255
+ upper = std::upper_bound (invCDF.begin (), invCDF.end (),
1256
+ float (j) / (float (bins - 1 )));
1257
+ CDF[j] = std::min (1 .0f ,
1258
+ std::max (0 .0f , float (upper - invCDF.begin ())
1259
+ / float (bins - 1 )));
1260
+ }
1261
+
1262
+
1263
+ configspec.attribute (" CDF_" + std::to_string (i),
1264
+ TypeDesc (TypeDesc::FLOAT, bins), CDF.data ());
1265
+ }
1266
+
1267
+ configspec[" CDF_bits" ] = cdf_bits;
1268
+
1269
+ mode = ImageBufAlgo::MakeTxTexture;
1270
+ }
1271
+
1187
1272
double misc_time_2 = alltime.lap ();
1188
1273
STATUS (" misc2" , misc_time_2);
1189
1274
0 commit comments