ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/checkBSDF.c
Revision: 2.7
Committed: Fri Jan 28 02:25:28 2022 UTC (2 years, 4 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.6: +8 -4 lines
Log Message:
fix(checkBSDF): Fixed another divide-by-zero and improved output consistency

File Contents

# User Rev Content
1 greg 2.1 #ifndef lint
2 greg 2.7 static const char RCSid[] = "$Id: checkBSDF.c,v 2.6 2022/01/26 17:30:07 greg Exp $";
3 greg 2.1 #endif
4     /*
5     * checkBSDF.c
6     *
7     * Load BSDF XML file and check Helmholtz reciprocity
8     */
9    
10     #define _USE_MATH_DEFINES
11     #include <math.h>
12     #include "rtio.h"
13 greg 2.2 #include "random.h"
14 greg 2.1 #include "bsdf.h"
15     #include "bsdf_m.h"
16     #include "bsdf_t.h"
17    
18     #define F_IN_COLOR 0x1
19     #define F_ISOTROPIC 0x2
20     #define F_MATRIX 0x4
21     #define F_TTREE 0x8
22    
23 greg 2.2 typedef struct {
24     double vmin, vmax; /* extrema */
25     double vsum; /* straight sum */
26     long nvals; /* number of values */
27     } SimpleStats;
28    
29     const SimpleStats SSinit = {FHUGE, -FHUGE, .0, 0};
30    
31     /* relative difference formula */
32 greg 2.6 #define rdiff(a,b) ((a)>(b) ? ((a)-(b))/((a)+FTINY) : ((b)-(a))/((b)+FTINY))
33 greg 2.2
34 greg 2.1 /* Figure out BSDF type (and optionally determine if in color) */
35     const char *
36     getBSDFtype(const SDData *bsdf, int *flags)
37     {
38     const SDSpectralDF *df = bsdf->tb;
39    
40     if (flags) *flags = 0;
41     if (!df) df = bsdf->tf;
42     if (!df) df = bsdf->rf;
43     if (!df) df = bsdf->rb;
44     if (!df) return "Pure_Lambertian";
45     if (df->comp[0].func == &SDhandleMtx) {
46     const SDMat *m = (const SDMat *)df->comp[0].dist;
47     if (flags) {
48     *flags |= F_MATRIX;
49     *flags |= F_IN_COLOR*(m->chroma != NULL);
50     }
51     switch (m->ninc) {
52     case 145:
53     return "Klems_Full";
54     case 73:
55     return "Klems_Half";
56     case 41:
57     return "Klems_Quarter";
58     }
59     return "Unknown_Matrix";
60     }
61     if (df->comp[0].func == &SDhandleTre) {
62     const SDTre *t = (const SDTre *)df->comp[0].dist;
63     if (flags) {
64     *flags |= F_TTREE;
65     *flags |= F_IN_COLOR*(t->stc[1] != NULL);
66     }
67     switch (t->stc[0]->ndim) {
68     case 4:
69     return "Anisotropic_Tensor_Tree";
70     case 3:
71     if (flags) *flags |= F_ISOTROPIC;
72     return "Isotropic_Tensor_Tree";
73     }
74     return "Unknown_Tensor_Tree";
75     }
76     return "Unknown";
77     }
78    
79     /* Report details related to one hemisphere distribution */
80     void
81     detailComponent(const char *nm, const SDValue *lamb, const SDSpectralDF *df)
82     {
83 greg 2.7 fputs(nm, stdout);
84     if (lamb->spec.flags)
85     printf("\t%4.1f %4.1f %4.1f\t\t",
86 greg 2.2 100.*lamb->cieY*lamb->spec.cx/lamb->spec.cy,
87 greg 2.1 100.*lamb->cieY,
88     100.*lamb->cieY*(1.f - lamb->spec.cx - lamb->spec.cy)/lamb->spec.cy);
89 greg 2.7 else
90     fputs("\t 0 0 0\t\t", stdout);
91 greg 2.1 if (df)
92     printf("%5.1f%%\t\t%.2f deg\n", 100.*df->maxHemi,
93     sqrt(df->minProjSA/M_PI)*(360./M_PI));
94     else
95 greg 2.7 puts(" 0%\t\t180 deg");
96 greg 2.1 }
97    
98 greg 2.2 /* Add a value to stats */
99     void
100     addStat(SimpleStats *ssp, double v)
101     {
102     if (v < ssp->vmin) ssp->vmin = v;
103     if (v > ssp->vmax) ssp->vmax = v;
104     ssp->vsum += v;
105     ssp->nvals++;
106     }
107    
108     /* Sample a BSDF hemisphere with callback (quadtree recursion) */
109     int
110     qtSampBSDF(double xleft, double ytop, double siz,
111     const SDData *bsdf, const int side, const RREAL *v0,
112     int (*cf)(const SDData *b, const FVECT v1, const RREAL *v0, void *p),
113     void *cdata)
114     {
115     if (siz < 0.124) { /* make sure we subdivide, first */
116     FVECT vsmp;
117     double sa;
118     square2disk(vsmp, xleft + frandom()*siz, ytop + frandom()*siz);
119     vsmp[2] = 1. - vsmp[0]*vsmp[0] - vsmp[1]*vsmp[1];
120     if (vsmp[2] <= 0) return 0;
121     vsmp[2] = side * sqrt(vsmp[2]);
122     if (SDreportError( SDsizeBSDF(&sa, vsmp, v0, SDqueryMin, bsdf), stderr))
123     return 0;
124     if (sa >= M_PI*siz*siz - FTINY) /* no further division needed */
125     return (*cf)(bsdf, vsmp, v0, cdata);
126     }
127     siz *= .5; /* 4-branch recursion */
128     return( qtSampBSDF(xleft, ytop, siz, bsdf, side, v0, cf, cdata) &&
129     qtSampBSDF(xleft+siz, ytop, siz, bsdf, side, v0, cf, cdata) &&
130     qtSampBSDF(xleft, ytop+siz, siz, bsdf, side, v0, cf, cdata) &&
131     qtSampBSDF(xleft+siz, ytop+siz, siz, bsdf, side, v0, cf, cdata) );
132     }
133    
134     #define sampBSDFhemi(b,s,v0,cf,cd) qtSampBSDF(0,0,1,b,s,v0,cf,cd)
135    
136     /* Call-back to compute reciprocity difference */
137     int
138     diffRecip(const SDData *bsdf, const FVECT v1, const RREAL *v0, void *p)
139     {
140     SDValue sdv;
141     double otherY;
142    
143     if (SDreportError( SDevalBSDF(&sdv, v0, v1, bsdf), stderr))
144     return 0;
145     otherY = sdv.cieY;
146     if (SDreportError( SDevalBSDF(&sdv, v1, v0, bsdf), stderr))
147     return 0;
148    
149     addStat((SimpleStats *)p, rdiff(sdv.cieY, otherY));
150     return 1;
151     }
152    
153     /* Call-back to compute reciprocity over reflected hemisphere */
154     int
155     reflHemi(const SDData *bsdf, const FVECT v1, const RREAL *v0, void *p)
156     {
157     return sampBSDFhemi(bsdf, 1 - 2*(v1[2]<0), v1, &diffRecip, p);
158     }
159    
160     /* Call-back to compute reciprocity over transmitted hemisphere */
161     int
162     transHemi(const SDData *bsdf, const FVECT v1, const RREAL *v0, void *p)
163     {
164     return sampBSDFhemi(bsdf, 1 - 2*(v1[2]>0), v1, &diffRecip, p);
165     }
166    
167 greg 2.1 /* Report reciprocity errors for the given directions */
168     void
169     checkReciprocity(const char *nm, const int side1, const int side2,
170     const SDData *bsdf, const int fl)
171     {
172 greg 2.2 SimpleStats myStats = SSinit;
173 greg 2.1 const SDSpectralDF *df = bsdf->tf;
174    
175     if (side1 == side2) {
176     df = (side1 > 0) ? bsdf->rf : bsdf->rb;
177     if (!df) goto nothing2do;
178     } else if (!bsdf->tf | !bsdf->tb)
179     goto nothing2do;
180    
181     if (fl & F_MATRIX) { /* special case for matrix BSDF */
182     const SDMat *m = (const SDMat *)df->comp[0].dist;
183     int i = m->ninc;
184 greg 2.3 double diffuseY;
185 greg 2.1 FVECT vin, vout;
186 greg 2.2 double fwdY;
187     SDValue rev;
188 greg 2.3 if (side1 == side2)
189     diffuseY = (side1 > 0) ? bsdf->rLambFront.cieY : bsdf->rLambBack.cieY;
190     else
191     diffuseY = (side1 > 0) ? bsdf->tLambFront.cieY : bsdf->tLambBack.cieY;
192     diffuseY /= M_PI;
193 greg 2.1 while (i--) {
194     int o = m->nout;
195     if (!mBSDF_incvec(vin, m, i+.5))
196     continue;
197     while (o--) {
198     if (!mBSDF_outvec(vout, m, o+.5))
199     continue;
200 greg 2.3 fwdY = mBSDF_value(m, o, i) + diffuseY;
201 greg 2.2 if (fwdY <= 1e-4)
202 greg 2.1 continue;
203 greg 2.2 if (SDreportError( SDevalBSDF(&rev, vout, vin, bsdf), stderr))
204 greg 2.1 return;
205 greg 2.2 if (rev.cieY > 1e-4)
206     addStat(&myStats, rdiff(fwdY, rev.cieY));
207 greg 2.1 }
208     }
209 greg 2.3 } else if (fl & F_ISOTROPIC) { /* isotropic case */
210 greg 2.2 const double stepSize = sqrt(df->minProjSA/M_PI);
211     FVECT vin;
212     vin[1] = 0;
213     for (vin[0] = 0.5*stepSize; vin[0] < 1; vin[0] += stepSize) {
214     vin[2] = side1*sqrt(1. - vin[0]*vin[0]);
215     if (!sampBSDFhemi(bsdf, side2, vin, &diffRecip, &myStats))
216     return;
217     }
218     } else if (!sampBSDFhemi(bsdf, side1, NULL,
219     (side1==side2) ? &reflHemi : &transHemi, &myStats))
220     return;
221     if (myStats.nvals) {
222     printf("%s\t%5.1f\t%5.1f\t%5.1f\n", nm,
223     100.*myStats.vmin,
224     100.*myStats.vsum/(double)myStats.nvals,
225     100.*myStats.vmax);
226 greg 2.1 return;
227     }
228     nothing2do:
229 greg 2.7 printf("%s\t 0\t 0\t 0\n", nm);
230 greg 2.1 }
231    
232     /* Report on the given BSDF XML file */
233     int
234     checkXML(char *fname)
235     {
236     int flags;
237     SDData myBSDF;
238     char *pth;
239    
240 greg 2.2 puts("=====================================================");
241 greg 2.1 printf("File: '%s'\n", fname);
242     SDclearBSDF(&myBSDF, fname);
243 greg 2.5 pth = getpath(fname, getrlibpath(), 0);
244 greg 2.1 if (!pth) {
245     fprintf(stderr, "Cannot find file '%s'\n", fname);
246     return 0;
247     }
248 greg 2.2 if (SDreportError( SDloadFile(&myBSDF, pth), stderr))
249     return 0;
250 greg 2.1 printf("Manufacturer: '%s'\n", myBSDF.makr);
251     printf("BSDF Name: '%s'\n", myBSDF.matn);
252     printf("Dimensions (W x H x Thickness): %g x %g x %g cm\n", 100.*myBSDF.dim[0],
253     100.*myBSDF.dim[1], 100.*myBSDF.dim[2]);
254     printf("Type: %s\n", getBSDFtype(&myBSDF, &flags));
255     printf("Color: %d\n", (flags & F_IN_COLOR) != 0);
256     printf("Has Geometry: %d\n", (myBSDF.mgf != NULL));
257     puts("Component\tLambertian XYZ %\tMax. Dir\tMin. Angle");
258 greg 2.4 detailComponent("Interior Refl", &myBSDF.rLambFront, myBSDF.rf);
259     detailComponent("Exterior Refl", &myBSDF.rLambBack, myBSDF.rb);
260 greg 2.1 detailComponent("Int->Ext Trans", &myBSDF.tLambFront, myBSDF.tf);
261     detailComponent("Ext->Int Trans", &myBSDF.tLambBack, myBSDF.tb);
262 greg 2.2 puts("Component\tReciprocity Error (min avg max %)");
263 greg 2.4 checkReciprocity("Interior Refl", 1, 1, &myBSDF, flags);
264     checkReciprocity("Exterior Refl", -1, -1, &myBSDF, flags);
265 greg 2.1 checkReciprocity("Transmission", -1, 1, &myBSDF, flags);
266     SDfreeBSDF(&myBSDF);
267     return 1;
268     }
269    
270     int
271     main(int argc, char *argv[])
272     {
273     int i;
274    
275     if (argc < 2) {
276     fprintf(stderr, "Usage: %s bsdf.xml ..\n", argv[0]);
277     return 1;
278     }
279 greg 2.2 for (i = 1; i < argc; i++)
280 greg 2.1 if (!checkXML(argv[i]))
281     return 1;
282     return 0;
283     }