Ptex
PtexFilters.cpp
Go to the documentation of this file.
1/*
2PTEX SOFTWARE
3Copyright 2014 Disney Enterprises, Inc. All rights reserved
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are
7met:
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
22Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
23CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
24BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
25FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
26IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
27CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
31THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33OF 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
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.
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.
virtual void release()
Release resources held by this pointer (pointer becomes invalid).
PtexPointFilterTri(PtexTexture *tx)
Point-sampling filter for rectangular textures.
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.
PtexTexture * _tx
PtexPointFilter(PtexTexture *tx)
virtual void release()
Release resources held by this pointer (pointer becomes invalid).
Interface for reading data from a ptex file.
Definition Ptexture.h:457
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 .
virtual const Ptex::FaceInfo & getFaceInfo(int faceid)=0
Access resolution and adjacency information about a face.
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