Ptex
PtexFilters.cpp
Go to the documentation of this file.
1 /*
2 PTEX SOFTWARE
3 Copyright 2014 Disney Enterprises, Inc. All rights reserved
4 
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are
7 met:
8 
9  * Redistributions of source code must retain the above copyright
10  notice, this list of conditions and the following disclaimer.
11 
12  * Redistributions in binary form must reproduce the above copyright
13  notice, this list of conditions and the following disclaimer in
14  the documentation and/or other materials provided with the
15  distribution.
16 
17  * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
18  Studios" or the names of its contributors may NOT be used to
19  endorse or promote products derived from this software without
20  specific prior written permission from Walt Disney Pictures.
21 
22 Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
23 CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
24 BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
25 FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
26 IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
27 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
31 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
34 */
35 
36 #include "PtexPlatform.h"
37 #include "Ptexture.h"
38 #include "PtexSeparableFilter.h"
39 #include "PtexSeparableKernel.h"
40 #include "PtexTriangleFilter.h"
41 
43 
46 {
47  public:
49  virtual void release() { delete this; }
50  virtual void eval(float* result, int firstchan, int nchannels,
51  int faceid, float u, float v,
52  float /*uw1*/, float /*vw1*/, float /*uw2*/, float /*vw2*/,
53  float /*width*/, float /*blur*/)
54  {
55  if (!_tx || nchannels <= 0) return;
56  if (faceid < 0 || faceid >= _tx->numFaces()) return;
57  const FaceInfo& f = _tx->getFaceInfo(faceid);
58  int resu = f.res.u(), resv = f.res.v();
59  int ui = PtexUtils::clamp(int(u*(float)resu), 0, resu-1);
60  int vi = PtexUtils::clamp(int(v*(float)resv), 0, resv-1);
61  _tx->getPixel(faceid, ui, vi, result, firstchan, nchannels);
62  }
63 
64  private:
66 };
67 
68 
71 {
72  public:
74  virtual void release() { delete this; }
75  virtual void eval(float* result, int firstchan, int nchannels,
76  int faceid, float u, float v,
77  float /*uw1*/, float /*vw1*/, float /*uw2*/, float /*vw2*/,
78  float /*width*/, float /*blur*/)
79  {
80  if (!_tx || nchannels <= 0) return;
81  if (faceid < 0 || faceid >= _tx->numFaces()) return;
82  const FaceInfo& f = _tx->getFaceInfo(faceid);
83  int res = f.res.u();
84  int resm1 = res - 1;
85  float ut = u * (float)res, vt = v * (float)res;
86  int ui = PtexUtils::clamp(int(ut), 0, resm1);
87  int vi = PtexUtils::clamp(int(vt), 0, resm1);
88  float uf = ut - (float)ui, vf = vt - (float)vi;
89 
90  if (uf + vf <= 1.0f) {
91  // "even" triangles are stored in lower-left half-texture
92  _tx->getPixel(faceid, ui, vi, result, firstchan, nchannels);
93  }
94  else {
95  // "odd" triangles are stored in upper-right half-texture
96  _tx->getPixel(faceid, resm1-vi, resm1-ui, result, firstchan, nchannels);
97  }
98  }
99 
100  private:
102 };
103 
104 
117 {
118  public:
119  typedef float KernelFn(float x, const float* c);
120 
121  PtexWidth4Filter(PtexTexture* tx, const PtexFilter::Options& opts, KernelFn k, const float* c = 0)
122  : PtexSeparableFilter(tx, opts), _k(k), _c(c) {}
123 
124  virtual void buildKernel(PtexSeparableKernel& k, float u, float v, float uw, float vw,
125  Res faceRes)
126  {
127  buildKernelAxis(k.res.ulog2, k.u, k.uw, k.ku, u, uw, faceRes.ulog2);
128  buildKernelAxis(k.res.vlog2, k.v, k.vw, k.kv, v, vw, faceRes.vlog2);
129  }
130 
131  private:
132 
133  float blur(float x)
134  {
135  // 2-unit (x in -1..1) cubic hermite kernel
136  // this produces a blur roughly 1.5 times that of the 4-unit b-spline kernel
137  x = PtexUtils::abs(x);
138  return x < 1.0f ? (2.0f*x-3.0f)*x*x+1.0f : 0.0f;
139  }
140 
141  void buildKernelAxis(int8_t& k_ureslog2, int& k_u, int& k_uw, float* ku,
142  float u, float uw, int f_ureslog2)
143  {
144  // build 1 axis (note: "u" labels may repesent either u or v axis)
145 
146  // clamp filter width to no smaller than a texel
147  uw = PtexUtils::max(uw, PtexUtils::reciprocalPow2(f_ureslog2));
148 
149  // compute desired texture res based on filter width
150  k_ureslog2 = (int8_t)PtexUtils::calcResFromWidth(uw);
151  int resu = 1 << k_ureslog2;
152  float uwlo = 1.0f/(float)resu; // smallest filter width for this res
153 
154  // compute lerp weights (amount to blend towards next-lower res)
155  float lerp2 = _options.lerp ? (uw-uwlo)/uwlo : 0;
156  float lerp1 = 1.0f-lerp2;
157 
158  // adjust for large filter widths
159  if (uw >= .25f) {
160  if (uw < .5f) {
161  k_ureslog2 = 2;
162  float upix = u * 4.0f - 0.5f;
163  int u1 = int(PtexUtils::ceil(upix - 2)), u2 = int(PtexUtils::ceil(upix + 2));
164  u1 = u1 & ~1; // round down to even pair
165  u2 = (u2 + 1) & ~1; // round up to even pair
166  k_u = u1;
167  k_uw = u2-u1;
168  float x1 = (float)u1-upix;
169  for (int i = 0; i < k_uw; i+=2) {
170  float xa = x1 + (float)i, xb = xa + 1.0f, xc = (xa+xb)*0.25f;
171  // spread the filter gradually to approach the next-lower-res width
172  // at uw = .5, s = 1.0; at uw = 1, s = 0.8
173  float s = 1.0f/(uw + .75f);
174  float ka = _k(xa, _c), kb = _k(xb, _c), kc = blur(xc*s);
175  ku[i] = ka * lerp1 + kc * lerp2;
176  ku[i+1] = kb * lerp1 + kc * lerp2;
177  }
178  return;
179  }
180  else if (uw < 1) {
181  k_ureslog2 = 1;
182  float upix = u * 2.0f - 0.5f;
183  k_u = int(PtexUtils::floor(u - .5f))*2;
184  k_uw = 4;
185  float x1 = (float)k_u-upix;
186  for (int i = 0; i < k_uw; i+=2) {
187  float xa = x1 + (float)i, xb = xa + 1.0f, xc = (xa+xb)*0.5f;
188  // spread the filter gradually to approach the next-lower-res width
189  // at uw = .5, s = .8; at uw = 1, s = 0.5
190  float s = 1.0f/(uw*1.5f + .5f);
191  float ka = blur(xa*s), kb = blur(xb*s), kc = blur(xc*s);
192  ku[i] = ka * lerp1 + kc * lerp2;
193  ku[i+1] = kb * lerp1 + kc * lerp2;
194  }
195  return;
196  }
197  else {
198  // use res 0 (1 texel per face) w/ no lerping
199  // (future: use face-blended values for filter > 2)
200  k_ureslog2 = 0;
201  float upix = u - .5f;
202  k_uw = 2;
203  float ui = PtexUtils::floor(upix);
204  k_u = int(ui);
205  ku[0] = blur(upix-ui);
206  ku[1] = 1-ku[0];
207  return;
208  }
209  }
210 
211  // convert from normalized coords to pixel coords
212  float upix = u * (float)resu - 0.5f;
213  float uwpix = uw * (float)resu;
214 
215  // find integer pixel extent: [u,v] +/- [2*uw,2*vw]
216  // (kernel width is 4 times filter width)
217  float dupix = 2.0f*uwpix;
218  int u1 = int(PtexUtils::ceil(upix - dupix)), u2 = int(PtexUtils::ceil(upix + dupix));
219 
220  if (lerp2) {
221  // lerp kernel weights towards next-lower res
222  // extend kernel width to cover even pairs
223  u1 = u1 & ~1;
224  u2 = (u2 + 1) & ~1;
225  k_u = u1;
226  k_uw = u2-u1;
227 
228  // compute kernel weights
229  float step = 1.0f/uwpix, x1 = ((float)u1-upix)*(float)step;
230  for (int i = 0; i < k_uw; i+=2) {
231  float xa = x1 + (float)i*step, xb = xa + step, xc = (xa+xb)*0.5f;
232  float ka = _k(xa, _c), kb = _k(xb, _c), kc = _k(xc, _c);
233  ku[i] = ka * lerp1 + kc * lerp2;
234  ku[i+1] = kb * lerp1 + kc * lerp2;
235  }
236  }
237  else {
238  k_u = u1;
239  k_uw = u2-u1;
240  // compute kernel weights
241  float x1 = ((float)u1-upix)/uwpix, step = 1.0f/uwpix;
242  for (int i = 0; i < k_uw; i++) ku[i] = _k(x1 + (float)i*step, _c);
243  }
244  }
245 
246  KernelFn* _k; // kernel function
247  const float* _c; // kernel coefficients (if any)
248 };
249 
250 
253 {
254  public:
255  PtexBicubicFilter(PtexTexture* tx, const PtexFilter::Options& opts, float sharpness)
256  : PtexWidth4Filter(tx, opts, kernelFn, _coeffs)
257  {
258  // compute Cubic filter coefficients:
259  // abs(x) < 1:
260  // 1/6 * ((12 - 9*B - 6*C)*x^3 + (-18 + 12*B + 6*C)*x^2 + (6 - 2*B))
261  // == c[0]*x^3 + c[1]*x^2 + c[2]
262  // abs(x) < 2:
263  // 1/6 * ((-B - 6*C)*x^3 + (6*B + 30*C)*x^2 + (-12*B - 48*C)*x + (8*B + 24*C))
264  // == c[3]*x^3 + c[4]*x^2 + c[5]*x + c[6]
265  // else: 0
266 
267  float B = 1.0f - sharpness; // choose C = (1-B)/2
268  _coeffs[0] = 1.5f - B;
269  _coeffs[1] = 1.5f * B - 2.5f;
270  _coeffs[2] = 1.0f - float(1.0/3.0) * B;
271  _coeffs[3] = float(1.0/3.0) * B - 0.5f;
272  _coeffs[4] = 2.5f - 1.5f * B;
273  _coeffs[5] = 2.0f * B - 4.0f;
274  _coeffs[6] = 2.0f - float(2.0/3.0) * B;
275  }
276 
277  private:
278  static float kernelFn(float x, const float* c)
279  {
280  x = PtexUtils::abs(x);
281  if (x < 1.0f) return (c[0]*x + c[1])*x*x + c[2];
282  else if (x < 2.0f) return ((c[3]*x + c[4])*x + c[5])*x + c[6];
283  else return 0.0f;
284  }
285 
286  float _coeffs[7]; // filter coefficients for current sharpness
287 };
288 
289 
290 
293 {
294  public:
296  : PtexWidth4Filter(tx, opts, kernelFn) {}
297 
298  private:
299  static float kernelFn(float x, const float*)
300  {
301  return (float)exp(-2.0f*x*x);
302  }
303 };
304 
305 
306 
312 {
313  public:
315  : PtexSeparableFilter(tx, opts) {}
316 
317  protected:
318  virtual void buildKernel(PtexSeparableKernel& k, float u, float v, float uw, float vw,
319  Res faceRes)
320  {
321  // clamp filter width to no larger than 1.0
322  uw = PtexUtils::min(uw, 1.0f);
323  vw = PtexUtils::min(vw, 1.0f);
324 
325  // clamp filter width to no smaller than a texel
326  uw = PtexUtils::max(uw, PtexUtils::reciprocalPow2(faceRes.ulog2));
327  vw = PtexUtils::max(vw, PtexUtils::reciprocalPow2(faceRes.vlog2));
328 
329  // compute desired texture res based on filter width
330  uint8_t ureslog2 = (uint8_t)PtexUtils::calcResFromWidth(uw);
331  uint8_t vreslog2 = (uint8_t)PtexUtils::calcResFromWidth(vw);
332  Res res(ureslog2, vreslog2);
333  k.res = res;
334 
335  // convert from normalized coords to pixel coords
336  u = u * (float)k.res.u();
337  v = v * (float)k.res.v();
338  uw *= (float)k.res.u();
339  vw *= (float)k.res.v();
340 
341  // find integer pixel extent: [u,v] +/- [uw/2,vw/2]
342  // (box is 1 unit wide for a 1 unit filter period)
343  float u1 = u - 0.5f*uw, u2 = u + 0.5f*uw;
344  float v1 = v - 0.5f*vw, v2 = v + 0.5f*vw;
345  float u1floor = PtexUtils::floor(u1), u2ceil = PtexUtils::ceil(u2);
346  float v1floor = PtexUtils::floor(v1), v2ceil = PtexUtils::ceil(v2);
347  k.u = int(u1floor);
348  k.v = int(v1floor);
349  k.uw = int(u2ceil)-k.u;
350  k.vw = int(v2ceil)-k.v;
351 
352  // compute kernel weights along u and v directions
353  computeWeights(k.ku, k.uw, 1.0f-(u1-u1floor), 1.0f-(u2ceil-u2));
354  computeWeights(k.kv, k.vw, 1.0f-(v1-v1floor), 1.0f-(v2ceil-v2));
355  }
356 
357  private:
358  void computeWeights(float* kernel, int size, float f1, float f2)
359  {
360  assert(size >= 1 && size <= 3);
361 
362  if (size == 1) {
363  kernel[0] = f1 + f2 - 1.0f;
364  }
365  else {
366  kernel[0] = f1;
367  for (int i = 1; i < size-1; i++) kernel[i] = 1.0f;
368  kernel[size-1] = f2;
369  }
370  }
371 };
372 
373 
376 {
377  public:
379  : PtexSeparableFilter(tx, opts) {}
380 
381  protected:
382  virtual void buildKernel(PtexSeparableKernel& k, float u, float v, float uw, float vw,
383  Res faceRes)
384  {
385  // clamp filter width to no larger than 1.0
386  uw = PtexUtils::min(uw, 1.0f);
387  vw = PtexUtils::min(vw, 1.0f);
388 
389  // clamp filter width to no smaller than a texel
390  uw = PtexUtils::max(uw, PtexUtils::reciprocalPow2(faceRes.ulog2));
391  vw = PtexUtils::max(vw, PtexUtils::reciprocalPow2(faceRes.vlog2));
392 
393  uint8_t ureslog2 = (uint8_t)PtexUtils::calcResFromWidth(uw);
394  uint8_t vreslog2 = (uint8_t)PtexUtils::calcResFromWidth(vw);
395  Res res(ureslog2, vreslog2);
396  k.res = res;
397 
398  // convert from normalized coords to pixel coords
399  float upix = u * (float)k.res.u() - 0.5f;
400  float vpix = v * (float)k.res.v() - 0.5f;
401 
402  float ufloor = PtexUtils::floor(upix);
403  float vfloor = PtexUtils::floor(vpix);
404  k.u = int(ufloor);
405  k.v = int(vfloor);
406  k.uw = 2;
407  k.vw = 2;
408 
409  // compute kernel weights
410  float ufrac = upix-ufloor, vfrac = vpix-vfloor;
411  k.ku[0] = 1.0f - ufrac;
412  k.ku[1] = ufrac;
413  k.kv[0] = 1.0f - vfrac;
414  k.kv[1] = vfrac;
415  }
416 };
417 
418 
420 {
421  switch (tex->meshType()) {
422  case Ptex::mt_quad:
423  switch (opts.filter) {
424  case f_point: return new PtexPointFilter(tex);
425  case f_bilinear: return new PtexBilinearFilter(tex, opts);
426  default:
427  case f_box: return new PtexBoxFilter(tex, opts);
428  case f_gaussian: return new PtexGaussianFilter(tex, opts);
429  case f_bicubic: return new PtexBicubicFilter(tex, opts, opts.sharpness);
430  case f_bspline: return new PtexBicubicFilter(tex, opts, 0.f);
431  case f_catmullrom: return new PtexBicubicFilter(tex, opts, 1.f);
432  case f_mitchell: return new PtexBicubicFilter(tex, opts, 2.f/3.f);
433  }
434  break;
435 
436  case Ptex::mt_triangle:
437  switch (opts.filter) {
438  case f_point: return new PtexPointFilterTri(tex);
439  default: return new PtexTriangleFilter(tex, opts);
440  }
441  break;
442  }
443  return 0;
444 }
445 
Platform-specific classes, functions, and includes.
#define PTEX_NAMESPACE_END
Definition: PtexVersion.h:62
Public API classes for reading, writing, caching, and filtering Ptex files.
Separable bicubic filter.
static float kernelFn(float x, const float *c)
PtexBicubicFilter(PtexTexture *tx, const PtexFilter::Options &opts, float sharpness)
Bilinear filter (for rectangular textures)
PtexBilinearFilter(PtexTexture *tx, const PtexFilter::Options &opts)
virtual void buildKernel(PtexSeparableKernel &k, float u, float v, float uw, float vw, Res faceRes)
Rectangular box filter.
PtexBoxFilter(PtexTexture *tx, const PtexFilter::Options &opts)
void computeWeights(float *kernel, int size, float f1, float f2)
virtual void buildKernel(PtexSeparableKernel &k, float u, float v, float uw, float vw, Res faceRes)
Interface for filtered sampling of ptex data files.
Definition: Ptexture.h:937
static PtexFilter * getFilter(PtexTexture *tx, const Options &opts)
@ f_bicubic
General bi-cubic filter (uses sharpness option)
Definition: Ptexture.h:949
@ f_bspline
BSpline (equivalent to bi-cubic w/ sharpness=0)
Definition: Ptexture.h:950
@ f_bilinear
Bi-linear interpolation.
Definition: Ptexture.h:946
@ f_catmullrom
Catmull-Rom (equivalent to bi-cubic w/ sharpness=1)
Definition: Ptexture.h:951
@ f_mitchell
Mitchell (equivalent to bi-cubic w/ sharpness=2/3)
Definition: Ptexture.h:952
@ f_box
Box filter.
Definition: Ptexture.h:947
@ f_gaussian
Gaussian filter.
Definition: Ptexture.h:948
@ f_point
Point-sampled (no filtering)
Definition: Ptexture.h:945
Separable gaussian filter.
PtexGaussianFilter(PtexTexture *tx, const PtexFilter::Options &opts)
static float kernelFn(float x, const float *)
Point-sampling filter for triangular textures.
Definition: PtexFilters.cpp:71
PtexTexture * _tx
virtual void eval(float *result, int firstchan, int nchannels, int faceid, float u, float v, float, float, float, float, float, float)
Apply filter to a ptex data file.
Definition: PtexFilters.cpp:75
virtual void release()
Release resources held by this pointer (pointer becomes invalid).
Definition: PtexFilters.cpp:74
PtexPointFilterTri(PtexTexture *tx)
Definition: PtexFilters.cpp:73
Point-sampling filter for rectangular textures.
Definition: PtexFilters.cpp:46
virtual void eval(float *result, int firstchan, int nchannels, int faceid, float u, float v, float, float, float, float, float, float)
Apply filter to a ptex data file.
Definition: PtexFilters.cpp:50
PtexTexture * _tx
Definition: PtexFilters.cpp:65
PtexPointFilter(PtexTexture *tx)
Definition: PtexFilters.cpp:48
virtual void release()
Release resources held by this pointer (pointer becomes invalid).
Definition: PtexFilters.cpp:49
Interface for reading data from a ptex file.
Definition: Ptexture.h:457
virtual const Ptex::FaceInfo & getFaceInfo(int faceid)=0
Access resolution and adjacency information about a face.
virtual Ptex::MeshType meshType()=0
Type of mesh for which texture data is defined.
virtual int numFaces()=0
Number of faces stored in file.
virtual void getPixel(int faceid, int u, int v, float *result, int firstchan, int nchannels)=0
Access a single texel from the highest resolution texture .
Separable filter with width=4 support.
float blur(float x)
PtexWidth4Filter(PtexTexture *tx, const PtexFilter::Options &opts, KernelFn k, const float *c=0)
const float * _c
void buildKernelAxis(int8_t &k_ureslog2, int &k_u, int &k_uw, float *ku, float u, float uw, int f_ureslog2)
float KernelFn(float x, const float *c)
virtual void buildKernel(PtexSeparableKernel &k, float u, float v, float uw, float vw, Res faceRes)
float reciprocalPow2(int power)
Definition: PtexUtils.h:92
T clamp(T x, T lo, T hi)
Definition: PtexUtils.h:154
T abs(T x)
Definition: PtexUtils.h:134
T min(T a, T b)
Definition: PtexUtils.h:148
int calcResFromWidth(float w)
Definition: PtexUtils.h:103
T max(T a, T b)
Definition: PtexUtils.h:151
@ mt_triangle
Mesh is triangle-based.
Definition: Ptexture.h:67
@ mt_quad
Mesh is quad-based.
Definition: Ptexture.h:68
Choose filter options.
Definition: Ptexture.h:956
FilterType filter
Filter type.
Definition: Ptexture.h:958
float sharpness
Filter sharpness, 0..1 (for general bi-cubic filter only).
Definition: Ptexture.h:960
bool lerp
Interpolate between mipmap levels.
Definition: Ptexture.h:959
Res res
Resolution of face.
Definition: Ptexture.h:230
int u() const
U resolution in texels.
Definition: Ptexture.h:173