diff --git a/data/tests/test_bsdf.xml b/data/tests/test_bsdf.xml index fdd4d760..97428d7f 100644 --- a/data/tests/test_bsdf.xml +++ b/data/tests/test_bsdf.xml @@ -5,6 +5,9 @@ + + + diff --git a/doc/format.tex b/doc/format.tex index d3673b93..ce204bea 100644 --- a/doc/format.tex +++ b/doc/format.tex @@ -189,11 +189,10 @@ The \texttt{reflectance} intent is used by default, so remember to set it to \texttt{illuminant} when defining the brightness of a light source with the \texttt{} tag. -When spectral power distributions are obtained from measurements +When spectral power or reflectance distributions are obtained from measurements (e.g. at 10$nm$ intervals), they are usually quite unwiedy and can clutter the scene description. For this reason, there is yet another way to pass -a spectrum by loading it from an external -file: +a spectrum by loading it from an external file: \begin{xml} \end{xml} @@ -201,7 +200,7 @@ The file should contain a single measurement per line, with the corresponding wavelength in nanometers and the measured value separated by a space. Comments are allowed. Here is an example: \begin{xml} -# This file contains a measured spectral power distribution +# This file contains a measured spectral power/reflectance distribution 406.13 0.703313 413.88 0.744563 422.03 0.791625 diff --git a/doc/images/bsdf_overview.pdf b/doc/images/bsdf_overview.pdf new file mode 100644 index 00000000..7f456f36 --- /dev/null +++ b/doc/images/bsdf_overview.pdf @@ -0,0 +1,855 @@ +%PDF-1.4 % +1 0 obj <> endobj 2 0 obj <>stream + + + + + application/pdf + + + bsdf_overview + + + + + 2011-07-11T01:23:08+02:00 + 2011-07-11T01:23:08+02:00 + 2011-07-11T01:23:08+02:00 + Adobe Illustrator CS5 + + + + 256 + 184 + JPEG + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAuAEAAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A7sdDjext7nTbeMQukcra Pdjnbn4P2eQYwygGnJRQ/tAnfK5RkDcfl+OTl4suMx4Mg26SH1D3/wA4e/fuPRMLC50u/umlNusO q2/wyxTIouYxuOu9UNTRlJU9jhhkB97DPppY6POJ5SHI/t8juO5LNf17y3oVhfSGyWeNBW8jgij4 Fq0VZGPFSeTdNyPDMjFilM0HAzaiOOJMuiW6h5w0iz02yvL3S7ZtPLILURtzZRIRukbwxhadTUr9 +Wx0syaDTLXQiATdFk+n3OlXlw9xFCsWoBaSiRFWcL03IryX3BI+7McxI5uXGQPJXXStLWBrdbOA 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 5OeIqdu564FReKuxV//Z + + + + + + uuid:ada10ad2-f23d-ae4f-aa34-79ea7f6566ff + xmp.did:02801174072068118083EF5DDCA14462 + uuid:5D20892493BFDB11914A8590D31508C8 + proof:pdf + + xmp.iid:01801174072068118083EF5DDCA14462 + xmp.did:01801174072068118083EF5DDCA14462 + uuid:5D20892493BFDB11914A8590D31508C8 + proof:pdf + + + + + saved + xmp.iid:F77F1174072068118083E6A74DDECC2A + 2011-07-08T18:43:48+02:00 + Adobe Illustrator CS5 + / + + + saved + xmp.iid:01801174072068118083EF5DDCA14462 + 2011-07-11T01:12:40+02:00 + Adobe Illustrator CS5 + / + + + saved + xmp.iid:02801174072068118083EF5DDCA14462 + 2011-07-11T01:23:04+02:00 + Adobe Illustrator CS5 + / + + + + + + Print + + + False + False + 1 + + 1004.000000 + 715.000000 + Points + + + + + Courier + Courier + Medium + Type 1 + 001.003 + False + cour.pfa + + + MinionPro-Regular + Minion Pro + Regular + Open Type + Version 2.030;PS 2.000;hotconv 1.0.51;makeotf.lib2.0.18671 + False + MinionPro-Regular.otf + + + MinionPro-Bold + Minion Pro + Bold + Open Type + Version 2.030;PS 2.000;hotconv 1.0.51;makeotf.lib2.0.18671 + False + MinionPro-Bold.otf + + + Myriad-Roman + Myriad + Roman + Type 1 + 001.001 + False + MyriaRom; Myriad + + + + + + Cyan + Magenta + Yellow + Black + + + + + + Default Swatch Group + 0 + + + + White + CMYK + PROCESS + 0.000000 + 0.000000 + 0.000000 + 0.000000 + + + Black + CMYK + PROCESS + 0.000000 + 0.000000 + 0.000000 + 100.000000 + + + CMYK Red + CMYK + PROCESS + 0.000000 + 100.000000 + 100.000000 + 0.000000 + + + CMYK Yellow + CMYK + PROCESS + 0.000000 + 0.000000 + 100.000000 + 0.000000 + + + CMYK Green + CMYK + PROCESS + 100.000000 + 0.000000 + 100.000000 + 0.000000 + + + CMYK Cyan + CMYK + PROCESS + 100.000000 + 0.000000 + 0.000000 + 0.000000 + + + CMYK Blue + CMYK + PROCESS + 100.000000 + 100.000000 + 0.000000 + 0.000000 + + + CMYK Magenta + CMYK + PROCESS + 0.000000 + 100.000000 + 0.000000 + 0.000000 + + + C=15 M=100 Y=90 K=10 + CMYK + PROCESS + 14.999998 + 100.000000 + 90.000000 + 10.000002 + + + C=0 M=90 Y=85 K=0 + CMYK + PROCESS + 0.000000 + 90.000000 + 85.000000 + 0.000000 + + + C=0 M=80 Y=95 K=0 + CMYK + PROCESS + 0.000000 + 80.000000 + 95.000000 + 0.000000 + + + C=0 M=50 Y=100 K=0 + CMYK + PROCESS + 0.000000 + 50.000000 + 100.000000 + 0.000000 + + + C=0 M=35 Y=85 K=0 + CMYK + PROCESS + 0.000000 + 35.000004 + 85.000000 + 0.000000 + + + C=5 M=0 Y=90 K=0 + CMYK + PROCESS + 5.000001 + 0.000000 + 90.000000 + 0.000000 + + + C=20 M=0 Y=100 K=0 + CMYK + PROCESS + 19.999998 + 0.000000 + 100.000000 + 0.000000 + + + C=50 M=0 Y=100 K=0 + CMYK + PROCESS + 50.000000 + 0.000000 + 100.000000 + 0.000000 + + + C=75 M=0 Y=100 K=0 + CMYK + PROCESS + 75.000000 + 0.000000 + 100.000000 + 0.000000 + + + C=85 M=10 Y=100 K=10 + CMYK + PROCESS + 85.000000 + 10.000002 + 100.000000 + 10.000002 + + + C=90 M=30 Y=95 K=30 + CMYK + PROCESS + 90.000000 + 30.000002 + 95.000000 + 30.000002 + + + C=75 M=0 Y=75 K=0 + CMYK + PROCESS + 75.000000 + 0.000000 + 75.000000 + 0.000000 + + + C=80 M=10 Y=45 K=0 + CMYK + PROCESS + 80.000000 + 10.000002 + 45.000000 + 0.000000 + + + C=70 M=15 Y=0 K=0 + CMYK + PROCESS + 70.000000 + 14.999998 + 0.000000 + 0.000000 + + + C=85 M=50 Y=0 K=0 + CMYK + PROCESS + 85.000000 + 50.000000 + 0.000000 + 0.000000 + + + C=100 M=95 Y=5 K=0 + CMYK + PROCESS + 100.000000 + 95.000000 + 5.000001 + 0.000000 + + + C=100 M=100 Y=25 K=25 + CMYK + PROCESS + 100.000000 + 100.000000 + 25.000000 + 25.000000 + + + C=75 M=100 Y=0 K=0 + CMYK + PROCESS + 75.000000 + 100.000000 + 0.000000 + 0.000000 + + + C=50 M=100 Y=0 K=0 + CMYK + PROCESS + 50.000000 + 100.000000 + 0.000000 + 0.000000 + + + C=35 M=100 Y=35 K=10 + CMYK + PROCESS + 35.000004 + 100.000000 + 35.000004 + 10.000002 + + + C=10 M=100 Y=50 K=0 + CMYK + PROCESS + 10.000002 + 100.000000 + 50.000000 + 0.000000 + + + C=0 M=95 Y=20 K=0 + CMYK + PROCESS + 0.000000 + 95.000000 + 19.999998 + 0.000000 + + + C=25 M=25 Y=40 K=0 + CMYK + PROCESS + 25.000000 + 25.000000 + 39.999996 + 0.000000 + + + C=40 M=45 Y=50 K=5 + CMYK + PROCESS + 39.999996 + 45.000000 + 50.000000 + 5.000001 + + + C=50 M=50 Y=60 K=25 + CMYK + PROCESS + 50.000000 + 50.000000 + 60.000004 + 25.000000 + + + C=55 M=60 Y=65 K=40 + CMYK + PROCESS + 55.000000 + 60.000004 + 65.000000 + 39.999996 + + + C=25 M=40 Y=65 K=0 + CMYK + PROCESS + 25.000000 + 39.999996 + 65.000000 + 0.000000 + + + C=30 M=50 Y=75 K=10 + CMYK + PROCESS + 30.000002 + 50.000000 + 75.000000 + 10.000002 + + + C=35 M=60 Y=80 K=25 + CMYK + PROCESS + 35.000004 + 60.000004 + 80.000000 + 25.000000 + + + C=40 M=65 Y=90 K=35 + CMYK + PROCESS + 39.999996 + 65.000000 + 90.000000 + 35.000004 + + + C=40 M=70 Y=100 K=50 + CMYK + PROCESS + 39.999996 + 70.000000 + 100.000000 + 50.000000 + + + C=50 M=70 Y=80 K=70 + CMYK + PROCESS + 50.000000 + 70.000000 + 80.000000 + 70.000000 + + + C=24 M=4 Y=4 K=0 1 + CMYK + PROCESS + 24.435801 + 3.955102 + 3.839201 + 0.003099 + + + C=87 M=44 Y=14 K=0 1 + CMYK + PROCESS + 86.691101 + 43.988697 + 13.937598 + 0.424200 + + + + + + Grays + 1 + + + + C=0 M=0 Y=0 K=100 + CMYK + PROCESS + 0.000000 + 0.000000 + 0.000000 + 100.000000 + + + C=0 M=0 Y=0 K=90 + CMYK + PROCESS + 0.000000 + 0.000000 + 0.000000 + 89.999405 + + + C=0 M=0 Y=0 K=80 + CMYK + PROCESS + 0.000000 + 0.000000 + 0.000000 + 79.998795 + + + C=0 M=0 Y=0 K=70 + CMYK + PROCESS + 0.000000 + 0.000000 + 0.000000 + 69.999702 + + + C=0 M=0 Y=0 K=60 + CMYK + PROCESS + 0.000000 + 0.000000 + 0.000000 + 59.999104 + + + C=0 M=0 Y=0 K=50 + CMYK + PROCESS + 0.000000 + 0.000000 + 0.000000 + 50.000000 + + + C=0 M=0 Y=0 K=40 + CMYK + PROCESS + 0.000000 + 0.000000 + 0.000000 + 39.999401 + + + C=0 M=0 Y=0 K=30 + CMYK + PROCESS + 0.000000 + 0.000000 + 0.000000 + 29.998802 + + + C=0 M=0 Y=0 K=20 + CMYK + PROCESS + 0.000000 + 0.000000 + 0.000000 + 19.999701 + + + C=0 M=0 Y=0 K=10 + CMYK + PROCESS + 0.000000 + 0.000000 + 0.000000 + 9.999103 + + + C=0 M=0 Y=0 K=5 + CMYK + PROCESS + 0.000000 + 0.000000 + 0.000000 + 4.998803 + + + + + + Brights + 1 + + + + C=0 M=100 Y=100 K=0 + CMYK + PROCESS + 0.000000 + 100.000000 + 100.000000 + 0.000000 + + + C=0 M=75 Y=100 K=0 + CMYK + PROCESS + 0.000000 + 75.000000 + 100.000000 + 0.000000 + + + C=0 M=10 Y=95 K=0 + CMYK + PROCESS + 0.000000 + 10.000002 + 95.000000 + 0.000000 + + + C=85 M=10 Y=100 K=0 + CMYK + PROCESS + 85.000000 + 10.000002 + 100.000000 + 0.000000 + + + C=100 M=90 Y=0 K=0 + CMYK + PROCESS + 100.000000 + 90.000000 + 0.000000 + 0.000000 + + + C=60 M=90 Y=0 K=0 + CMYK + PROCESS + 60.000004 + 90.000000 + 0.003099 + 0.003099 + + + + + + + + + Adobe PDF library 9.90 + + + + + + + + + + + + + + + + + + + + + + + + + endstream endobj 3 0 obj <> endobj 5 0 obj <>/Font<>/ProcSet[/PDF/Text]/Properties<>>>>>/TrimBox[0.0 0.0 1004.0 715.0]/Type/Page>> endobj 6 0 obj <>stream +HWnWЭzו>` \ yHkA`fUȌG`w;' >\u?&oG}j?v{q1b/t ?8W)!mgWn}Mm7e&QB.lYbw's8g1mǨ۵ٝfvW]vo&_jDooӓbO6O6ם߮Υ҇S){6<p78| )o4܎^n8>e7<}SobXmņ%y<+9 o>~7d3KqsǏ{[)y &"cg s= qc,Z55昶Xg"W͂О3Is2YSnN0"F`k:>.G#&9bٚ6CVL08[pQe5HN +#`$(AdX-"jY> wgCw" +bf˺IfHTaǖ(-)5BN3ZBt80UΧYl w/;[,DVMrhrkZ+voeF.'|r![z@j".sF&vlWBA(H7gSnbX%>X}yuW3FoMG{'/}~#P!eAe Aȹ@ds;yB6jW6҃d U ɗF[ؐb9NZM~9Ek'13ԍ(؎zɔ35xo e3*YcF?\6wd[t] f*I_yrDZh6smہ=6 B%ztG4n>}6R9QrP[Ҝ7c@ՉTddoJ1g؆<0<& d6X.IKqLcy dãR?FXi>^\Λ":!xf=DO3nA:4MT8 (B\Kb-etX?FzocS^xGBg JŴ X|@z#$ ܬ nj,|Sά20S՗B0# +BH-2 9SKJ2hԠ-Ǡ!Nl?j3VB"QJk@ }U0֠zU26̆ DAP\lµ2;FĎŲdfYJ|@"RH;Va\ `N?XV'a\8*dVc Z!aDt!4e6)/Or4В_N='7{ΚkQ%0 =p:uo*Vct:ªV&h>]5~?{ZNi*iE}~Yxf<聪$bM{4`V".024?!U?Kuόb KlEDB"svV:fPgL[^pH]aAd + ûrz9JtgwĿZ:[ +k%7\^i~෪^}ȡS;1OMh#/i@L6]B-ks=9Nnx7 6ԘWd`G#}9ڦNfL y/ +VH=%Xӽ8,8D|Q:Ox0 [)kk{p,*C'e%IHL0a, LY/ȅ<\EFR^20)|#Eֹ]UAċ{=R!뷎c!;Z]BsIzIb"i,S^P1h"]K)X6(st :ƉRh+h@JݗTO˵o'Ly2Z;X M0_0;-.ɤR +8 +;l/ڞ*g䳿Ep4k,E1gkD7MN]"kGsg;iDzԷ^S2{H7}[ړm+#>OZ0QJիJ)ʖ\]fMcE[ѷޫTR<'O9!Z 1ES[9c]LYfzb3#K^iJj hξ|e&ۆMcA+P({BD) FVV5T2ґ%#`%yqB&$[H; glIi4V'-ܕR +vd!j "„N' +ie4׳e `HdK lJM"fW̲!r|3HgJs<1'~DA˨߰9w#&o4Ls&lπ"YE!7%j=y#(&. (wRl#}V nG S 78H(`i2.5%CʦV3Ҵ"[.%odlw BHJUTwo(Ŕ&dmEOR܁,S8àeUXv +yW[h +TשyL 8_WLRUct@(RJ]{ۥ72x E'g0HSeCSi銮(FGd!I_eO, >a9L&mPG]*22r/Osz2Ʃ,]MDt ^:EF͍ehѬ@L;Vřf2瞯uI,SLfmht9bzԦC!j! ˑc^{Zu + F:N/@x]H1Ξʻnٛ]|{:R9#Y`eS,-۳P{̮-0U??smv +yA1?' a/0"wa=C⅛J O'ru)A,#@zżTkp N鲑0@iW}9 KGӒA"b$NĝfD x(_qGhCٿ/LEh_bZ9}&aJDfqe|iڊ)g+">5{Ҥ1͗3σdG +dsXI8)I8!" >^М^XBt!1~V瞖7`*H<@KJ|zJ %Q>-п[VM FcLkI!2,(rP*z&7 Dz"@@崋0sZO#(hAԈEO*ܘSPV`ESQ0ϗp> FExG#KL$",hx۪zL$&G *:$̫{3aCXOs3;NUz4F0>ŋ\DWomt +26~rTW.~{7qek|8a̭7mKun [׸7׷Za5^m^,SHUi8fo_nW9~ F`V ~tK;N ϲNxf[ZV:T:ٜUh&F(N)$ܦdUw0Թ%* +Aq!yPΰ:>,_xioN͘x/ +T$%˵h!b:5!ź-6^і:ǻ3S˒ GT51s-#ǹP+;93\" + - K,(-gO*`򠀃U!Eij|v[ 9b BLhB +, +Fq 饭ԆEc{(j#>CS`_nSQI8V Igfk޾_ۊrKXfLZW~uɌcjՕhY0m514ؠ:j-[mUq_-ˍG8ww]AIaFU86}Ȼ+ά g0 CtWWeee +> +aPшLAމ>d-2kL( լ-!$8 +=@\!LR25@Lt /$&.7.]v+BfBM,!5mଖPmTC-֞LXl +Ą3kcW=TLfz12hr8*V 5J{5> 1ECs {M[&Z&*r<}uO*ÆL`^1Nć]9RntU{AD0j e_Hncn?;o·!Vt؎C5AWbAFu%$6`KTݰ)p{fJ_=' X6ݘMB' SAћ&j <ʠt(~\|wӍcy,RɡUh]|Xۺ>z#zSV.7Y|'nW=IV緋/?NrnD&JPiр5nDgz*?=<10y噉3Qpa <\v?8 3S D|7J{`%y4k*Pl*Xb%~ɫE!jPư|aW%n__޼}rhӛ7/`uH}d +"A,E$Lхr)A?2kAbn??ZfWxyℜ{$jj{@p4cd&S*'`C@w{Oc=[q'y"5P=n76S܀33[8uAmqD#./ЩyA248Aq&(N\(GZVRSEה(K'CEf: +V(8nFq + +mGJgAQ*>Z\CP܊RLPTG9(WK^1eATb?TkϼXq7ěҾܚ>epu [[iwq.Qxw΂¢rSݬE"ggF(jwT*,SZ$*m?Nܤ[KKj* E% w5ވ0!_D5fޒW3(>ez]٠R1 O^ס81gTM0i!*$ oIL[vQS5*Τ^BwrWnb;`RQ$ iV + /u[Nkk|p{nnq*>3)g).ǭ(>arW)zɵ$xMxb38'p B`|έjWK F!J&W0X:ʭg[LFWnQ}$$(B0-*GFEb 8zWRe=#=|Wi*<.FmIJϧQQ(އrT*>FE5CK^@f3!*x>2*^R+qSxa3Z<3oK4)F,bT|Nl0M΂¢qwѩ`zpwJz:-׫vw2ESЧiy%M5^GpTsiNp-.jT&DAޒ1QWR\j,*>̍j,W6\IJa%a<^Xq'X0&yXdk)Ejwg|[Srn;RR$* P/GYR4n -vǕ/YǑb{(O*B/7.!\(JO_K)I/‚;f +).I>dRbJ<҅ s-VGw1wݍtInҬڬ[Tq&7:w^T{aIP4ʻہҬ52^inFpW1Ǐszb{2~܌W}B^K;^L!SLqϹSV)(J&W0X:ʭ-g[̴G/8p7NRҷ@o;Zqo!ۻvOnW5{s{zw3me΅^)4/w6[a7O_"lmvg63}^77?tJU,[ ʽjl`F(*q6/ +y?Q/۰ydyWՁy} 4P6?l5\sf\!gCvb>D)*cw# !Ƌ$RZ[l50%idsr"_Ӈg#)pY,Zn07JZ( N%V%A{xg ׭<<=~hyHA|U`~_cL ;9Q?b˷۝~]Z#x/[ 9!xl~B7{ zӽ|IcBGishq0~G[]3Etw䕱E$N3!z }f#w9(L2hτ=WYJEyidk QA]Aow^)۠2%qx'$a +Ez?v 'NNHc?Ǵ,( ^BopER{XvYl0@p4HqG9TK(GetkWZ_JOj =тC'&Ѻ]x|<a܃t6xkMPVHrBl{KӞZk?|4U(AN7G&+\#NΊ~}f,vSJFZ|u4 "SCCS ;mCI" A(vT2߫tST9u0 ]`'&ՀB;cE6T[MrplŽ 8s2:L$<]7G1,Q͸5Y(=2%CHOk"vy}kT&\pA!$dW7ӎI +ܦW{FAx,ۃ#[a>ӡÉ =xxTgKGcezV9D@3q}hڲ|j>~/&>]mI:}~2 {!{X +q?<|{yz!,TkJSn@ÁL+(>~ +(5\6N ~р"m}0X}px1 T +οSŋ5G4M;XZZg)L=6CQOzEo "zv(blWdgG xICM^0Ev:@ד/Ȭjbu kLMp:F +6zm_MC#ۯ䜪=r!*cN~XWf*o5z&:b RA`(lTj]+0Kzmf(ay4=v:hk"=bRR?”ZQ6hy99:L,%kz^6Y)҉}tM KcZC[=EeuI}DE^IO$GM#X8<=0J-r ;vV`x5FYVwBGSQ`)}0U6$RjjNU͗ phA7' &XC{oz: +P$=xT>F=݁j=x%eWVeiA߅Xo_b`M;d $jb"4d3Rn;i'%8)LLQ!Y + UuAÁFj$KR$z@L2#qAOyGEا8b1 eӧ9ۦ"DP=6ޤS9v A2䄡uNT517K]\}-Xb{.`b6>FH,9nIRʒbVRG~E3{ 5ݢM$()ߤWE6,nHNWRvfŝ˳U97Ǚ}!F,9afh8IuoaGF Fݷ>qO?>uû\sz3̟?Cٿ9?Lz|> Ö?}m>r?_\.#-rƸ۷~{s\<-%bY}|f:]hrͩQi㘍XvՉOy+䬧ohB|{fMKedžQ畵6(bEV \}H/5US/_ٲ$\NCjkuu9- 23I0a ]iCgMNc7“(' ,|$ܐ%d)U鞁PRrQf`aMD=.y1&-.c c,K Ì<6w:M=Y5p뇢)/Į +^6X" I %gY-GUj>L3LgY4pbdth@I1F6 +u鸠pX)Z)H'mp{$fTK2˓~ՖhNzl$Xpp=H͝'MjFPVt +KGv:=|RQ#Yy8b3.3Ar*IvO+r5"FQʽ㘭Kj+1l@nUc͓'踩qzZ A)"SfK5~MlQxXŖ|a|JN]f|OR+!@4F&*Xj@Wl.XGXFf$j'D/kDV,ӈ")vF1zPThފE*;At'!1r}^`_?hTxd9K<:/n 0cnRV_.1Ñ$ˏܬz&b /W7~N$[w<@c?@{)%= f?{_!iZl*S9Ol')5y4bNPLKb``b1t-˴!$|'Dnfw-?²0C:y{uxCn g{*޲pvp83eQtn 8%~dBTdmb8r~*M%];`P; +Js4B>²Gc]wQ^` SF.^)/N퐫z XPNB[7/;g6NW2"j:|f]5k[u5?UIO/.i쿬p+]#2_5=֑;=j^x^cX=;ZƵ>01X˳;j<kkz +J rܸ}?07j 9Y@k 搭!zȶJi4r~?Ƨ@Z麩}Lz"1bYbyifFMyy'?>U%DӧStk5R%+ 7yI7y<2)ojXR*"a[:w#c:a]nԪIƠ9Olf 1J%7ԬyReW̬yҪ΋ܬ/_$A5? JŰ찇7ê5? ZA)#> YwXsFvqv;-pS7@k"n&:6Ozn81F6Xkأsh<m:goIu2~r?m2"Ŭ{xl] *rad UYok3%PBȴ%J֣)1`_J;cң!o1nT%Th 4W?/nT5j2+`bp\NZ|XlcmzFQEĜIFmb(U.U C͚5/%XyE70ČHͺQ60tzx$Zj,(²޳.Zb,hU,`dacycq켔3p S75X$=ps|dDĩ¢3ڷʈAUqĠmLZU=I!Z8? ʺZwԅȐrlT0-[qUUQz>)GzJԣ0XJ@rƤgC"]odK )shlFZ^%]k'u=)dap.->e"-ƶF=gI^QXzvZE"5Ԥ˔#PJcP1fK8/ RuL-Zɕ57Je쭇뺷*2f@(t T#jof o8;xh-qh&롑l:"6SOؘb|7.F5l˹[9^l6FLk5>wAY7UK`z𼱺pRE.8,j-Ca_Eyn3ncĠ9;A!rgBW\gQnIP;.G 3r6}8~>~O@%VLV5]:]f5W%ַt&07nv2ogSlJԲ[[bXf5QoH˹Qv Rycˊƨm~ʛ hחkxux=TzW`I=8n_N( މ˗yhs|(_A^wYPv[G{pðN}^6ٙ^^T%tRS (D!N^Ug ßJ/+ %뭾S`cM.GjDB0d(p;!i .(h%Lmg=nMV]Sn2*7tGM6XCk$(c +5jy+I-+wk$x /^.h&{   +gB +=>ز=|P8p^Mw.өQ6(S:1@8/jz@h4|}߀ ɀrdү/LpP'<%c + +:+~U'vs1zc0!E34whԣa¹X<šƩ ;o?5z6^nD6@>5-W @⹥=Iv4Vu!uu)uu%u57l +:%E7:` +(%.^Ŀ5/>[*/;)̀vǍ_>|ͳ}Y?|!W?/?>_ ??|-goyx/_/DşXs^jxO/MSlY\s_ +|RTnaHiVˢGߡN\&ٶY0Ύ.ބkx~#prt#eQ?NR9JQN+W/P:t^ݣ@X1l):@/ccs`e` +@i֌ )C`;X Y+PPb5D.4RH>RK`&7d.M]#s `2HO4Kg`uuV0 0 endstream endobj 8 0 obj <> endobj 9 0 obj <> endobj 10 0 obj <> endobj 11 0 obj <> endobj 16 0 obj <> endobj 17 0 obj <>stream +H|P[PUpJHta6;mB!ܡP, $4B[JrKJ E +te@_)RQY<8}?G! <'+譼sZNם|SW+$+6"~+={QD`&HJ[#1Ƈ>a#XBXƓi*]EZ F@fk+uz^iTQdZm-6z@xHUruBGr +׭z2b"RRN\bLR¿8Eϊ@Rjc 'is^FRE)RWCgDR%9/XY R\tJIkԫ)CT"K=E&* +I$9d!yKX-@y ++lg^~_(G`0:a*p<13g|'Ďw0Y,YTon@V{a]%\[\Am Mjo#jf95H{e~_l7Oa71j[L=8-m9m&еƵbF%н& *`0Ul랐oN^XYM-]{K D>j쀄`_ PU?:} 5|t+!s; J&,W|Yl~.ou*.v^K`̋S aY$Fٰ))!O4^*;.J""=ZuV%0 wt,ohK~`FBHKVYdnxZ6gxρb44wMrNqy;Mxo > endobj 18 0 obj <>stream +Hbd`ab`ddpq,LL Mf!ׯYe~#_! @]% +ɚ +F: DGX1%?)U!$5X3/9 ($5EOA1'G!X!(8 ( +qBfBBIQbJjnbQB~r+rE@"HfBrjQI"*-,NL.+c&FF}|? h";O_^=XsiǾ۷}xsR/f .ʔ[:iOΈ/J [(!b8P4=0-cqZyi?ͦ]oڞilgN6` +܂צy88gΜu1<> endobj 19 0 obj <>stream +H\{PW7 Qs]uX͠ZF<|((UTj@- "y2hѱR88URUZ(V*>j=\:ӍWٝs3+#L&p!&<ې8m;}rC;C]sMM&&;1)쵚MNj/O%'JbADʉh1Rb%zl쐬K!/˟(|9<~&ǑImMi<|#WD@Y³B²V dof ]˭*+b9t!~[z>]Lg %}xycjXؽIag#rHT`n5heBb..pMQ˒UM (zn<ϭ?K.ׇc?t]ó3l\m$j4TUz;S!WM;IrP+K s7L`i\s>le(?~蠖 HJSf3;~y[ŏ>!R0Iv]!zAoL1xj:P&NQڊ{c/Ca9N4m `,ͺ"NeVغI wi+`L0H8jyaP+?\KlB-OzJ)K"!ȚX2h<&A0(`0ƺ Hu-OYH^ +ϧj)?Yg`sS +hf<8 i 4+laJ$i}wKBhYu pH+]GEK BgΞ|w07g.[da[Y'2>8&jnR?ǝV;E7Rw[,A{̅yۙ]ۊyIGOC=-f)c&vfQ7>:[YH甦H7 c*3%f+Wp99kڗSġ^> endobj 13 0 obj <> endobj 20 0 obj <>stream +H|SiTY"\:NˣUNU@]ԣ ,-Ć$lA0B BHB"*ŭn=hx]9Sѿww{߽{ ܈ձG*UJVAzF+RMss\OӼcb5C357  &mOZͦkL;(HDIDI %L&DIcV%;LVH0a*9Ng D/fUj*3)a +B +f*>SyJ87#0(hOŰ*0 +efB$J!ZT'$*0s74(UĩN)5L* A"gTjBژXmY$* Af|1GuB0(A`:Be+.."oѠfSruC9Xݔ?O-lZ۴˧_d'Z|I a΋68|p~oWCp +8$㝌s2II9wA0~o(1=(v Qw"`Gp|xwTF6h >8_H5d_~ 0 2(r#kX1"6rALx*fnCe}Gcr#btp_eׄGWT:*\`poLG)# ,zw~ -R9ӗn#AѱX&7Xm0 ȭiρ>϶s/AH(+SrGҧe\jTCP"l"}cBЅѧG9G$ӊnp;YL0kl{F:a=.}}Г Lv9u5-1>1TwtoX>iW;{."oWR_]j,Fh>\y8.pv(D:$w-si9mOK}Oz_w_j}S\],<^ +.е& +&SŮk^Ja(>0 k4<#R=ꂅoDeчS?X=(m~@} ?lEzRwq*kFdi:EOv#x??ׇj$+l7K /HM2XJrSJ-Z g q}QmXcA + Օe'E8J"ۊҶlK9rNP"|9|4HS|FG2vaPuξAr+*=#EJ>GeWm2a@ڻ*Bu%E2 !t%6u-ǘcc^Ra~;ӈan熎Ogѧ!p9oڅQ@ߥ>LdzukNk򅴁[yHץωk;[xs+Pl* ѩ +jrcqPN(Sz#'X@ɬRZa%!*PSe4Uy(n<1Vo"%5`=qܷzI_1̪HJż]r*"do{}>V\_=RRocG'I0{ <$oR/FQ%8 5{ɕґ3X67a?\@ bKKvojaͶF3G!\_(sZkG=zc+ke3&n?ⅭbCaA XؗAgh]"=- +MKnAK*anĬ5 P+0ї9 +PCmKC#ʜ6*۞p`SX`8"PA~q4S=+p5Z0sg?A!& oಞ;S\d3Uj0*͉|d8= wA酈~|b [5,|(;Vo7p%`CoMajklm',fŚjhKQ졜*gawX:sVw9켤}BL_X(65gf{߄̢| {.nQ}{T 3E70|9/יݼz/Ê&t1/+%rY.j?:eYGrT[\H>v5; <]cSq;)S< 9UP&0饝L39icVVړ5~gnxd2S&Hؿw:yI(~ b"8'P V`MgA/|ol.| zM>__Z bTXED+1A聗@N(A!S{e :j$EG>j(xo:M$tߥuYWWGdjE >U$O0 Nk j6 #NP ڻ;N[)f bLɥtfɠ\WYj (fئع"~jYDj " +-),b~VN"Pj1z0;}}n|L^6ը/@.@Bt8̯0똰\2ebS\΃5 ƬH41׮PW}kGLبQprZ:Nz[xLN[ *~y. +RiAb( >& e >c78u6t{DCㆯA aél\0\ y/?."풥"'QYٮ2niә1{fPiS0ul g>VOHQ58ZW-܆y +: KKy-ư*vYuv,j {)WՉm&-)|?n 4㈏,:9&YEҵ +IlFE0N^Q\ѡlR$-UhT +*(Fb1V;ZQ 0!-..ڄmv xlQiYp'jsa0d[|!:P9Fm;Bcw#nU;F3pXcTQʏv.DwZ#O)pI_IϬ3 rpftarz=̻MbCPh^ýpyX@ԟ'vZ(:YRGh,Fkt$#/*ߞmXjT9bbD* Ymڸ|p \7BǸq?-fEy̙`sPmaUci3"\6?r"lv5#. up].BsӍ4 N5fdg L/7?UOיjmx{zzoku}8ǛCy Z + + c Uu endstream endobj 7 0 obj <> endobj 21 0 obj <> endobj xref 0 22 0000000000 65535 f +0000000016 00000 n +0000000076 00000 n +0000064575 00000 n +0000000000 00000 f +0000064626 00000 n +0000065049 00000 n +0000093610 00000 n +0000083576 00000 n +0000083982 00000 n +0000084205 00000 n +0000084363 00000 n +0000088738 00000 n +0000088822 00000 n +0000086856 00000 n +0000086108 00000 n +0000084600 00000 n +0000084880 00000 n +0000086383 00000 n +0000087143 00000 n +0000089198 00000 n +0000093722 00000 n +trailer <<5BF692A705824E93A2FEE6E4C14334E0>]>> startxref 93899 %%EOF \ No newline at end of file diff --git a/doc/images/bsdf_roughdiffuse_0.jpg b/doc/images/bsdf_roughdiffuse_0.jpg new file mode 100644 index 00000000..46d303ee Binary files /dev/null and b/doc/images/bsdf_roughdiffuse_0.jpg differ diff --git a/doc/images/bsdf_roughdiffuse_0_7.jpg b/doc/images/bsdf_roughdiffuse_0_7.jpg new file mode 100644 index 00000000..2c2ab3b0 Binary files /dev/null and b/doc/images/bsdf_roughdiffuse_0_7.jpg differ diff --git a/doc/macros.sty b/doc/macros.sty index f8408cc0..e4d215cc 100644 --- a/doc/macros.sty +++ b/doc/macros.sty @@ -37,9 +37,7 @@ %\toprule \\[-2.2ex] \textbf{Parameter}&\textbf{Type}&\textbf{Description}\\ - \otoprule #1 - %\bottomrule \end{tabular}} \end{figure} \setlength\fboxrule\fboxrulebackup @@ -60,12 +58,8 @@ \newcommand{\smallrendering}[2]{ \subfigure[#1]{\fbox{\includegraphics[width=0.2\textwidth]{images/#2}}}\hfill} \newcommand{\parameter}[3]{ - \small\texttt{#1} & \small #2 & \small #3\\ \otoprule + \small\texttt{#1} & \small #2 & \small #3 \\ } -\newcommand{\lastparameter}[3]{ - \small\texttt{#1} & \small #2 & \small #3\\ - } - \newcommand{\default}[1]{ (Default: #1)} diff --git a/doc/section_bsdf.tex b/doc/section_bsdf.tex index dccf24b2..7120baa9 100644 --- a/doc/section_bsdf.tex +++ b/doc/section_bsdf.tex @@ -1,4 +1,16 @@ \subsection{Surface scattering models} +\begin{figure}[h!] +\centering +\includegraphics[width=15.5cm]{images/bsdf_overview.pdf} +\caption{ + Schematic overview of the surface scattering models that ship with + Mitsuba. The arrows indicate possible outcomes of an + interaction with a surface that has the respective model applied to it. + \vspace{4mm} +} +\end{figure} + + \label{sec:bsdfs} Surface scattering models describe the manner in which light interacts with surfaces in the scene. They conveniently summarize the mesoscopic @@ -11,53 +23,11 @@ please refer to Sections~\ref{sec:media} and \ref{sec:subsurface}. This section presents an overview of all surface scattering models that are supported, along with their parameters. -\subsubsection*{Correctness considerations} -\begin{figure}[b!] -\centering -\vspace{-5mm} -\includegraphics[width=15cm]{images/glass_explanation.pdf} -\vspace{-5mm} -\caption{ - \label{fig:glass-explanation} - Some of the scattering models in Mitsuba need to know - the indices of refraction on the exterior and interior-facing - side of a surface. - It is therefore important to decompose the mesh into meaningful - separate surfaces corresponding to each index of refraction change. - The example here shows such a decomposition for a water-filled Glass. -} -\end{figure} - - -A vital consideration when modeling a scene in a physically-based rendering -system is that the used materials do not violate physical properties, and -that their arrangement is meaningful. For instance, imagine having designed -an architectural interior scene that looks good except for a white desk that -seems a bit too dark. A closer inspection reveals that it uses a Lambertian -material with a diffuse reflectance of $0.9$. - -In many rendering systems, it would be feasible to increase the -reflectance value above $1.0$ in such a situation. But in Mitsuba, even a -small surface that reflects a little more light than it receives will -likely break the available rendering algorithms, or cause them to produce otherwise -unpredictable results. In fact, we should rather change the lighting setup and -then \emph{reduce} the material's reflectance, since it is quite unlikely that -we could find a real-world desk with a reflectance as high as $0.9$. - -As an example of the necessity for a meaningful material arrangement, consider -the glass model illustrated in \figref{glass-explanation}. Here, careful thinking -is needed to decompose the object into boundaries that mark index of -refraction-changes. If this is done incorrectly and a beam of light can -potentially pass through a sequence of incompatible index of refraction changes (e.g. $1.00\to 1.33$ -followed by $1.50\to1.33$), the output is undefined and will quite likely -even contain inaccuracies in parts of the scene that are some distance -away from the glass. - \subsubsection*{BSDFs} To achieve realistic results, Mitsuba comes with a library of both general-purpose surface scattering models (smooth or rough glass, metal, plastic, etc.) and specializations to particular materials (woven cloth, -masks, etc.). Some model plugins fit neither category and could be described +masks, etc.). Some model plugins fit neither category and can best be described as \emph{modifiers} that are applied on top of one or more scattering models. Throughout the documentation and within the scene description @@ -95,4 +65,45 @@ The following fragment shows an example of both kinds of usages: It is generally more economical to use named BSDFs when they are used in several places, since this reduces Mitsuba's internal memory usage. +\subsubsection*{Correctness considerations} +\begin{figure}[b!] +\centering +\vspace{-5mm} +\includegraphics[width=15cm]{images/glass_explanation.pdf} +\vspace{-5mm} +\caption{ + \label{fig:glass-explanation} + Some of the scattering models in Mitsuba need to know + the indices of refraction on the exterior and interior-facing + side of a surface. + It is therefore important to decompose the mesh into meaningful + separate surfaces corresponding to each index of refraction change. + The example here shows such a decomposition for a water-filled Glass. +} +\end{figure} + +A vital consideration when modeling a scene in a physically-based rendering +system is that the used materials do not violate physical properties, and +that their arrangement is meaningful. For instance, imagine having designed +an architectural interior scene that looks good except for a white desk that +seems a bit too dark. A closer inspection reveals that it uses a Lambertian +material with a diffuse reflectance of $0.9$. + +In many rendering systems, it would be feasible to increase the +reflectance value above $1.0$ in such a situation. But in Mitsuba, even a +small surface that reflects a little more light than it receives will +likely break the available rendering algorithms, or cause them to produce otherwise +unpredictable results. In fact, we should rather change the lighting setup and +then \emph{reduce} the material's reflectance, since it is quite unlikely that +we could find a real-world desk with a reflectance as high as $0.9$. + +As an example of the necessity for a meaningful material arrangement, consider +the glass model illustrated in \figref{glass-explanation}. Here, careful thinking +is needed to decompose the object into boundaries that mark index of +refraction-changes. If this is done incorrectly and a beam of light can +potentially pass through a sequence of incompatible index of refraction changes (e.g. $1.00\to 1.33$ +followed by $1.50\to1.33$), the output is undefined and will quite likely +even contain inaccuracies in parts of the scene that are some distance +away from the glass. + diff --git a/include/mitsuba/core/frame.h b/include/mitsuba/core/frame.h index 24f099f8..ebd2b89b 100644 --- a/include/mitsuba/core/frame.h +++ b/include/mitsuba/core/frame.h @@ -112,6 +112,38 @@ struct Frame { return 1.0f - v.z * v.z; } + /** \brief Assuming that the given direction is in the local coordinate + * system, return the sine of the phi parameter in spherical coordinates */ + inline static Float sinPhi(const Vector &v) { + Float sinTheta = Frame::sinTheta(v); + if (sinTheta == 0.0f) + return 1.0f; + return clamp(v.y / sinTheta, -1.0f, 1.0f); + } + + /** \brief Assuming that the given direction is in the local coordinate + * system, return the cosine of the phi parameter in spherical coordinates */ + inline static Float cosPhi(const Vector &v) { + Float sinTheta = Frame::sinTheta(v); + if (sinTheta == 0.0f) + return 1.0f; + return clamp(v.x / sinTheta, -1.0f, 1.0f); + } + + /** \brief Assuming that the given direction is in the local coordinate + * system, return the squared sine of the phi parameter in spherical + * coordinates */ + inline static Float sinPhiSquared(const Vector &v) { + return clamp(v.y * v.y / sinTheta2(v), 0.0f, 1.0f); + } + + /** \brief Assuming that the given direction is in the local coordinate + * system, return the squared cosine of the phi parameter in spherical + * coordinates */ + inline static Float cosPhiSquared(const Vector &v) { + return clamp(v.x * v.x / sinTheta2(v), 0.0f, 1.0f); + } + /// Return a string representation of this frame inline std::string toString() const { std::ostringstream oss; diff --git a/src/bsdfs/conductor.cpp b/src/bsdfs/conductor.cpp index b5632f58..baa33e10 100644 --- a/src/bsdfs/conductor.cpp +++ b/src/bsdfs/conductor.cpp @@ -32,7 +32,7 @@ MTS_NAMESPACE_BEGIN * \parameter{k}{\Spectrum}{Imaginary part of the material's index of * refraction, also known as absorption coefficient. * \default{based on the value of \texttt{material}}} - * \lastparameter{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{ + * \parameter{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{ * Optional factor used to modulate the reflectance component * \default{1.0}} * } diff --git a/src/bsdfs/dielectric.cpp b/src/bsdfs/dielectric.cpp index 40d86b3b..78eb3a04 100644 --- a/src/bsdfs/dielectric.cpp +++ b/src/bsdfs/dielectric.cpp @@ -31,7 +31,7 @@ MTS_NAMESPACE_BEGIN * numerically or using a known material name. \default{\texttt{air} / 1.000277}} * \parameter{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional * factor used to modulate the reflectance component\default{1.0}} - * \lastparameter{specular\showbreak Transmittance}{\Spectrum\Or\Texture}{Optional + * \parameter{specular\showbreak Transmittance}{\Spectrum\Or\Texture}{Optional * factor used to modulate the transmittance component\default{1.0}} * } * diff --git a/src/bsdfs/difftrans.cpp b/src/bsdfs/difftrans.cpp index 6267f15a..a8761a12 100644 --- a/src/bsdfs/difftrans.cpp +++ b/src/bsdfs/difftrans.cpp @@ -25,7 +25,7 @@ MTS_NAMESPACE_BEGIN /*! \plugin{difftrans}{Diffuse transmitter} * * \parameters{ - * \lastparameter{transmittance}{\Spectrum\Or\Texture}{ + * \parameter{transmittance}{\Spectrum\Or\Texture}{ * Specifies the diffuse transmittance of the material * \default{0.5} * } diff --git a/src/bsdfs/diffuse.cpp b/src/bsdfs/diffuse.cpp index f7c169d1..ce4fb005 100644 --- a/src/bsdfs/diffuse.cpp +++ b/src/bsdfs/diffuse.cpp @@ -25,17 +25,20 @@ MTS_NAMESPACE_BEGIN /*!\plugin{diffuse}{Smooth diffuse material} * \order{1} * \parameters{ - * \lastparameter{reflectance}{\Spectrum\Or\Texture}{ - * Specifies the diffuse reflectance / albedo of the material \linebreak(Default: 0.5) + * \parameter{reflectance}{\Spectrum\Or\Texture}{ + * Specifies the diffuse albedo of the + * material \default{0.5} * } * } * * \renderings{ - * \rendering{Homogeneous reflectance, see \lstref{diffuse-uniform}}{bsdf_diffuse_plain} - * \rendering{Textured reflectance, see \lstref{diffuse-textured}}{bsdf_diffuse_textured} + * \rendering{Homogeneous reflectance, see \lstref{diffuse-uniform}} + * {bsdf_diffuse_plain} + * \rendering{Textured reflectance, see \lstref{diffuse-textured}} + * {bsdf_diffuse_textured} * } * - * The smooth diffuse material (sometimes referred to as ``Lambertian'') + * The smooth diffuse material (also referred to as ``Lambertian'') * represents an ideally diffuse material with a user-specified amount of * reflectance. Any received illumination is scattered so that the surface * looks the same independently of the direction of observation. @@ -51,13 +54,15 @@ MTS_NAMESPACE_BEGIN * consider using the \pluginref{twosided} BRDF adapter plugin. * \vspace{4mm} * - * \begin{xml}[caption={A diffuse material, whose reflectance is specified as an sRGB color}, label=lst:diffuse-uniform] + * \begin{xml}[caption={A diffuse material, whose reflectance is specified + * as an sRGB color}, label=lst:diffuse-uniform] * * * * \end{xml} * - * \begin{xml}[caption=A diffuse material with a texture map, label=lst:diffuse-textured] + * \begin{xml}[caption=A diffuse material with a texture map, + * label=lst:diffuse-textured] * * * diff --git a/src/bsdfs/plastic.cpp b/src/bsdfs/plastic.cpp index 9b162bdf..862566ed 100644 --- a/src/bsdfs/plastic.cpp +++ b/src/bsdfs/plastic.cpp @@ -31,7 +31,7 @@ MTS_NAMESPACE_BEGIN * numerically or using a known material name. \default{\texttt{air} / 1.000277}} * \parameter{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional * factor used to modulate the specular component\default{1.0}} - * \lastparameter{diffuse\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional + * \parameter{diffuse\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional * factor used to modulate the diffuse component\default{0.5}} * } * diff --git a/src/bsdfs/roughconductor.cpp b/src/bsdfs/roughconductor.cpp index e22cf0b2..1ef66372 100644 --- a/src/bsdfs/roughconductor.cpp +++ b/src/bsdfs/roughconductor.cpp @@ -62,7 +62,7 @@ MTS_NAMESPACE_BEGIN * \default{0.1}. * } * \parameter{alphaU, alphaV}{\Float\Or\Texture}{ - * Specifies the anisotropic rougness values along the tangent and + * Specifies the anisotropic roughness values along the tangent and * bitangent directions. These parameter are only valid when * \texttt{distribution=as}. \default{0.1}. * } @@ -73,7 +73,7 @@ MTS_NAMESPACE_BEGIN * \parameter{k}{\Spectrum}{Imaginary part of the material's index of * refraction, also known as absorption coefficient. * \default{based on the value of \texttt{material}}} - * \lastparameter{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional + * \parameter{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional * factor used to modulate the reflectance component\default{1.0}} * } * This plugin implements a realistic microfacet scattering model for rendering @@ -117,7 +117,7 @@ MTS_NAMESPACE_BEGIN * a value of $\alpha=0.001-0.01$ corresponds to a material * with slight imperfections on an * otherwise smooth surface finish, $\alpha=0.1$ is relatively rough, - * and $\alpha=0.3-0.5$ is \emph{extremely} rough (e.g. an etched or ground + * and $\alpha=0.3-0.7$ is \emph{extremely} rough (e.g. an etched or ground * finish). * \vspace{-2mm} * \subsubsection*{Techical details}\vspace{-2mm} diff --git a/src/bsdfs/roughdielectric.cpp b/src/bsdfs/roughdielectric.cpp index ff4e5510..8a0082ac 100644 --- a/src/bsdfs/roughdielectric.cpp +++ b/src/bsdfs/roughdielectric.cpp @@ -59,7 +59,7 @@ MTS_NAMESPACE_BEGIN * \default{0.1}. * } * \parameter{alphaU, alphaV}{\Float\Or\Texture}{ - * Specifies the anisotropic rougness values along the tangent and + * Specifies the anisotropic roughness values along the tangent and * bitangent directions. These parameter are only valid when * \texttt{distribution=as}. \default{0.1}. * } @@ -69,7 +69,7 @@ MTS_NAMESPACE_BEGIN * numerically or using a known material name. \default{\texttt{air} / 1.000277}} * \parameter{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional * factor used to modulate the reflectance component\default{1.0}} - * \lastparameter{specular\showbreak Transmittance}{\Spectrum\Or\Texture}{Optional + * \parameter{specular\showbreak Transmittance}{\Spectrum\Or\Texture}{Optional * factor used to modulate the transmittance component\default{1.0}} * }\vspace{4mm} * @@ -111,7 +111,7 @@ MTS_NAMESPACE_BEGIN * a value of $\alpha=0.001-0.01$ corresponds to a material * with slight imperfections on an * otherwise smooth surface finish, $\alpha=0.1$ is relatively rough, - * and $\alpha=0.3-0.5$ is \emph{extremely} rough (e.g. an etched or ground + * and $\alpha=0.3-0.7$ is \emph{extremely} rough (e.g. an etched or ground * finish). * * Please note that when using this plugin, it is crucial that the scene contains @@ -142,7 +142,7 @@ MTS_NAMESPACE_BEGIN * \renderings{ * \rendering{Ground glass (GGX, $\alpha$=0.304, * \lstref{roughdielectric-roughglass})}{bsdf_roughdielectric_ggx_0_304.jpg} - * \rendering{Textured rougness (\lstref{roughdielectric-textured})} + * \rendering{Textured roughness (\lstref{roughdielectric-textured})} * {bsdf_roughdielectric_textured.jpg} * } * @@ -467,7 +467,7 @@ public: 1. Take the Fresnel term with respect to the surface normal to be a good approximation to the microsurface Fresnel term -- this will be less true for higher - rougness values. To be safe, clamp it to some + roughness values. To be safe, clamp it to some reasonable range. 2. Use this approximate term and a random number to choose between reflection and refraction component. @@ -595,7 +595,7 @@ public: 1. Take the Fresnel term with respect to the surface normal to be a good approximation to the microsurface Fresnel term -- this will be less true for higher - rougness values. To be safe, clamp it to some + roughness values. To be safe, clamp it to some reasonable range. 2. Use this approximate term and a random number to choose between reflection and refraction component. diff --git a/src/bsdfs/roughdiffuse.cpp b/src/bsdfs/roughdiffuse.cpp index 1a30e27e..5465c298 100644 --- a/src/bsdfs/roughdiffuse.cpp +++ b/src/bsdfs/roughdiffuse.cpp @@ -21,60 +21,65 @@ MTS_NAMESPACE_BEGIN -/*!\plugin{diffuse}{Rough diffuse material} +/*!\plugin{roughdiffuse}{Rough diffuse material} * \order{2} * \parameters{ * \parameter{reflectance}{\Spectrum\Or\Texture}{ - * Specifies the reflectance / albedo of the material \linebreak(Default: 0.5) + * Specifies the diffuse albedo of the + * material. \default{0.5} * } - * \lastparameter{alpha}{\Spectrum\Or\Texture}{ - * Specifies the roughness of the unresolved surface microgeometry. - * This parameter is approximately equal to the \emph{root mean square} - * (RMS) slope of the microfacets.\default{0.1} + * \parameter{alpha}{\Spectrum\Or\Texture}{ + * Specifies the roughness of the unresolved surface microgeometry + * using the \emph{root mean square} (RMS) slope of the + * microfacets. \default{0.2} + * } + * \parameter{useFastApprox}{\Boolean}{ + * This parameter selects between the full version of the model + * or a fast approximation that still retains most qualitative features. + * \default{\texttt{false}, i.e. use the high-quality version} * } * } * * \renderings{ - * \rendering{Homogeneous reflectance, see \lstref{diffuse-uniform}}{bsdf_diffuse_plain} - * \rendering{Textured reflectance, see \lstref{diffuse-textured}}{bsdf_diffuse_textured} + * \rendering{Smooth diffuse surface ($\alpha=0$)} + * {bsdf_roughdiffuse_0} + * \rendering{Very rough diffuse surface ($\alpha=0.7$)} + * {bsdf_roughdiffuse_0_7} + * \vspace{-3mm} + * \caption{The effect of switching from smooth to rough diffuse scattering + * is fairly subtle on this model---generally, there will be higher + * reflectance at grazing angles, as well as an overall reduced contrast.}\vspace{3mm} * } * - * This reflectance model describes scattering from a rough diffuse material, - * such as plaster, sand, clay, or concrete. + * This reflectance model describes the interaction of light with a rough + * diffuse material, such as plaster, sand, clay, or concrete. * The underlying theory was developed by Oren and Nayar * \cite{Oren1994Generalization}, who model the microscopic surface structure as * unresolved planar facets arranged in V-shaped grooves, where each facet * is an ideal diffuse reflector. The model takes into account shadowing, * masking, as well as interreflections between the facets. * - * Since the original publication in 1994, this approach has been shown to + * Since the original publication, this approach has been shown to * be a good match for many real-world materials, particularly compared * to Lambertian scattering, which does not take surface roughness into account. * * The implementation in Mitsuba uses a surface roughness parameter $\alpha$ that - * is slighly different from the slope-area variance in the original paper. + * is slighly different from the slope-area variance in the original 1994 paper. * The reason for this change is to make the parameter $\alpha$ portable - * across different models (i.e. \pluginref{roughglass}, + * across different models (i.e. \pluginref{roughglass}, \pluginref{roughplastic}, * \pluginref{roughconductor}). * * To get an intuition about the effect of the * parameter $\alpha$, consider the following approximate differentiation: * a value of $\alpha=0.001-0.01$ corresponds to a material * with slight imperfections on an otherwise smooth surface (for such small - * values, the model will behave almost identically to \pluginref{diffuse}), $\alpha=0.1$ - * is relatively rough, and $\alpha=0.3-0.5$ is \emph{extremely} rough + * values, the model will behave identically to \pluginref{diffuse}), $\alpha=0.1$ + * is relatively rough, and $\alpha=0.3-0.7$ is \emph{extremely} rough * (e.g. an etched or ground surface). * * Note that this material is one-sided---that is, observed from the * back side, it will be completely black. If this is undesirable, * consider using the \pluginref{twosided} BRDF adapter plugin. - * \vspace{4mm} - * - * \begin{xml}[caption={A diffuse material, whose reflectance is specified as an sRGB color}, label=lst:diffuse-uniform] - * - * - * - * \end{xml} */ class RoughDiffuse : public BSDF { public: @@ -84,9 +89,11 @@ public: 'reflectance' and 'diffuseReflectance' as parameter names */ m_reflectance = new ConstantSpectrumTexture(props.getSpectrum( props.hasProperty("reflectance") ? "reflectance" - : "diffuseReflectance", Spectrum(.5f))); + : "diffuseReflectance", Spectrum(0.5f))); - m_alpha = new ConstantFloatTexture(props.getFloat("alpha", 0.1f)); + m_useFastApprox = props.getBoolean("useFastApprox", false); + + m_alpha = new ConstantFloatTexture(props.getFloat("alpha", 0.2f)); m_components.push_back(EGlossyReflection | EFrontSide); m_usesRayDifferentials = false; } @@ -95,6 +102,7 @@ public: : BSDF(stream, manager) { m_reflectance = static_cast(manager->getInstance(stream)); m_alpha = static_cast(manager->getInstance(stream)); + m_useFastApprox = stream->readBool(); m_components.push_back(EGlossyReflection | EFrontSide); m_usesRayDifferentials = m_reflectance->usesRayDifferentials(); } @@ -110,7 +118,7 @@ public: Spectrum eval(const BSDFQueryRecord &bRec, EMeasure measure) const { if (!(bRec.typeMask & EGlossyReflection) || measure != ESolidAngle - || Frame::cosTheta(bRec.wi) <= 0 + || Frame::cosTheta(bRec.wi) <= 0 || Frame::cosTheta(bRec.wo) <= 0) return Spectrum(0.0f); @@ -124,12 +132,80 @@ public: Float sigma = m_alpha->getValue(bRec.its).average() * conversionFactor; - Float sigma2 = sigma*sigma; - Float A = 10.f - (sigma2 / (2.0f * (sigma2 + 0.33f))); - Float B = 0.45f * sigma2 / (sigma2 + 0.09f); + const Float sigma2 = sigma*sigma; - return m_reflectance->getValue(bRec.its) - * (INV_PI * Frame::cosTheta(bRec.wo)); + Float sinThetaI = Frame::sinTheta(bRec.wi), + sinThetaO = Frame::sinTheta(bRec.wo); + + Float cosPhiDiff = 0; + if (sinThetaI > Epsilon && sinThetaO > Epsilon) { + /* Compute cos(phiO-phiI) using the half-angle formulae */ + Float sinPhiI = Frame::sinPhi(bRec.wi), + cosPhiI = Frame::cosPhi(bRec.wi), + sinPhiO = Frame::sinPhi(bRec.wo), + cosPhiO = Frame::cosPhi(bRec.wo); + cosPhiDiff = cosPhiI * cosPhiO + sinPhiI * sinPhiO; + } + + if (m_useFastApprox) { + Float A = 1.0f - 0.5f * sigma2 / (sigma2 + 0.33f), + B = 0.45f * sigma2 / (sigma2 + 0.09f), + sinAlpha, tanBeta; + + if (Frame::cosTheta(bRec.wi) > Frame::cosTheta(bRec.wo)) { + sinAlpha = sinThetaO; + tanBeta = sinThetaI / Frame::cosTheta(bRec.wi); + } else { + sinAlpha = sinThetaI; + tanBeta = sinThetaO / Frame::cosTheta(bRec.wo); + } + + return m_reflectance->getValue(bRec.its) + * (INV_PI * Frame::cosTheta(bRec.wo) * (A + B + * std::max(cosPhiDiff, (Float) 0.0f) * sinAlpha * tanBeta)); + } else { + Float sinThetaI = Frame::sinTheta(bRec.wi), + sinThetaO = Frame::sinTheta(bRec.wo), + thetaI = std::acos(Frame::cosTheta(bRec.wi)), + thetaO = std::acos(Frame::cosTheta(bRec.wo)), + alpha = std::max(thetaI, thetaO), + beta = std::min(thetaI, thetaO); + + Float sinAlpha, sinBeta, tanBeta; + if (Frame::cosTheta(bRec.wi) > Frame::cosTheta(bRec.wo)) { + sinAlpha = sinThetaO; sinBeta = sinThetaI; + tanBeta = sinThetaI / Frame::cosTheta(bRec.wi); + } else { + sinAlpha = sinThetaI; sinBeta = sinThetaO; + tanBeta = sinThetaO / Frame::cosTheta(bRec.wo); + } + + Float tmp = sigma2 / (sigma2 + 0.09f), + tmp2 = (4*INV_PI*INV_PI) * alpha * beta, + tmp3 = 2*beta*INV_PI; + + Float C1 = 1.0f - 0.5f * sigma2 / (sigma2 + 0.33f), + C2 = 0.45f * tmp, + C3 = 0.125f * tmp * tmp2 * tmp2, + C4 = 0.17f * sigma2 / (sigma2 + 0.13f); + + if (cosPhiDiff > 0) + C2 *= sinAlpha; + else + C2 *= sinAlpha - tmp3*tmp3*tmp3; + + /* Compute tan(0.5 * (alpha+beta)) using the half-angle formulae */ + Float tanHalf = (sinAlpha + sinBeta) / ( + std::sqrt(std::max((Float) 0.0f, 1.0f - sinAlpha * sinAlpha)) + + std::sqrt(std::max((Float) 0.0f, 1.0f - sinBeta * sinBeta))); + + Spectrum rho = m_reflectance->getValue(bRec.its), + snglScat = rho * (C1 + cosPhiDiff * C2 * tanBeta + + (1.0f - std::abs(cosPhiDiff)) * C3 * tanHalf), + dblScat = rho * rho * (C4 * (1.0f - cosPhiDiff*tmp3*tmp3)); + + return (snglScat + dblScat) * (INV_PI * Frame::cosTheta(bRec.wo)); + } } Float pdf(const BSDFQueryRecord &bRec, EMeasure measure) const { @@ -148,7 +224,8 @@ public: bRec.wo = squareToHemispherePSA(sample); bRec.sampledComponent = 0; bRec.sampledType = EGlossyReflection; - return m_reflectance->getValue(bRec.its); + return eval(bRec, ESolidAngle) / + (Frame::cosTheta(bRec.wo) * INV_PI); } Spectrum sample(BSDFQueryRecord &bRec, Float &pdf, const Point2 &sample) const { @@ -159,8 +236,7 @@ public: bRec.sampledComponent = 0; bRec.sampledType = EGlossyReflection; pdf = Frame::cosTheta(bRec.wo) * INV_PI; - return m_reflectance->getValue(bRec.its) - * (INV_PI * Frame::cosTheta(bRec.wo)); + return eval(bRec, ESolidAngle); } void addChild(const std::string &name, ConfigurableObject *child) { @@ -182,6 +258,7 @@ public: manager->serialize(stream, m_reflectance.get()); manager->serialize(stream, m_alpha.get()); + stream->writeBool(m_useFastApprox); } std::string toString() const { @@ -189,7 +266,8 @@ public: oss << "RoughDiffuse[" << endl << " name = \"" << getName() << "\"," << endl << " reflectance = " << indent(m_reflectance->toString()) << "," << endl - << " alpha = " << indent(m_alpha->toString()) << endl + << " alpha = " << indent(m_alpha->toString()) << "," << endl + << " useFastApprox = " << m_useFastApprox << endl << "]"; return oss.str(); } @@ -200,6 +278,7 @@ public: private: ref m_reflectance; ref m_alpha; + bool m_useFastApprox; }; // ================ Hardware shader implementation ================ @@ -233,11 +312,27 @@ public: oss << "vec3 " << evalName << "(vec2 uv, vec3 wi, vec3 wo) {" << endl << " if (cosTheta(wi) < 0.0 || cosTheta(wo) < 0.0)" << endl << " return vec3(0.0);" << endl - << " return " << depNames[0] << "(uv) * 0.31831 * cosTheta(wo);" << endl + << " float sigma = " << depNames[1] << "(uv)[0] * 0.70711;" << endl + << " float sigma2 = sigma * sigma;" << endl + << " float A = 1.0 - 0.5 * sigma2 / (sigma2 + 0.33);" << endl + << " float B = 0.45 * sigma2 / (sigma2 + 0.09);" << endl + << " float maxCos = max(0.0, cosPhi(wi)*cosPhi(wo)+sinPhi(wi)*sinPhi(wo));" << endl + << " float sinAlpha, tanBeta;" << endl + << " if (cosTheta(wi) > cosTheta(wo)) {" << endl + << " sinAlpha = sinTheta(wo);" << endl + << " tanBeta = sinTheta(wi) / cosTheta(wi);" << endl + << " } else {" << endl + << " sinAlpha = sinTheta(wi);" << endl + << " tanBeta = sinTheta(wo) / cosTheta(wo);" << endl + << " }" << endl + << " float value = A + B * maxCos * sinAlpha * tanBeta;" << endl + << " return " << depNames[0] << "(uv) * 0.31831 * cosTheta(wo) * value;" << endl << "}" << endl << endl << "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl - << " return " << evalName << "(uv, wi, wo);" << endl + << " if (cosTheta(wi) < 0.0 || cosTheta(wo) < 0.0)" << endl + << " return vec3(0.0);" << endl + << " return " << depNames[0] << "(uv) * 0.31831 * cosTheta(wo);" << endl << "}" << endl; } diff --git a/src/converter/collada.cpp b/src/converter/collada.cpp index 95b8b87b..1f761be2 100644 --- a/src/converter/collada.cpp +++ b/src/converter/collada.cpp @@ -1103,7 +1103,7 @@ void loadCamera(ColladaContext &ctx, Transform transform, domCamera &camera) { int xres=768; // Cameras in Mitsuba point along the positive Z axis (COLLADA: neg. Z) - transform = transform * Transform::scale(Vector(1,1,-1)); + transform = transform * Transform::scale(Vector(1, 1, -1)); std::ostringstream matrix; for (int i=0; i<4; ++i) @@ -1130,14 +1130,8 @@ void loadCamera(ColladaContext &ctx, Transform transform, domCamera &camera) { xres = ctx.cvt->m_xres; aspect = (Float) ctx.cvt->m_xres / (Float) ctx.cvt->m_yres; } else { - if (persp->getAspect_ratio().cast() != 0) { + if (persp->getAspect_ratio().cast() != 0) aspect = (Float) persp->getAspect_ratio()->getValue(); - if (std::abs(aspect-0.1) < Epsilon) { - SLog(EWarn, "Found the suspicious aspect ratio \"0.1\", which is likely due to a bug in Blender 2.5" - " - setting to 1.0. Please use the \"-r\" parameter to override the resolution."); - aspect = 1.0f; - } - } } ctx.os << "\t" << endl; if (persp->getXfov().cast()) { diff --git a/src/libcore/transform.cpp b/src/libcore/transform.cpp index b6b5b79f..23501184 100644 --- a/src/libcore/transform.cpp +++ b/src/libcore/transform.cpp @@ -174,16 +174,16 @@ Transform Transform::glOrthographic(Float clipNear, Float clipFar) { Transform Transform::lookAt(const Point &p, const Point &t, const Vector &up) { Matrix4x4 result; - Vector dirct = normalize(t-p); - Vector right = normalize(cross(dirct, up)); + Vector dir = normalize(t-p); + Vector left = normalize(cross(normalize(up), dir)); /* Generate a new, orthogonalized up vector */ - Vector newUp = cross(right, dirct); + Vector newUp = cross(dir, left); /* Store as columns */ - result.m[0][0] = right.x; result.m[1][0] = right.y; result.m[2][0] = right.z; result.m[3][0] = 0; + result.m[0][0] = left.x; result.m[1][0] = left.y; result.m[2][0] = left.z; result.m[3][0] = 0; result.m[0][1] = newUp.x; result.m[1][1] = newUp.y; result.m[2][1] = newUp.z; result.m[3][1] = 0; - result.m[0][2] = dirct.x; result.m[1][2] = dirct.y; result.m[2][2] = dirct.z; result.m[3][2] = 0; + result.m[0][2] = dir.x; result.m[1][2] = dir.y; result.m[2][2] = dir.z; result.m[3][2] = 0; result.m[0][3] = p.x; result.m[1][3] = p.y; result.m[2][3] = p.z; result.m[3][3] = 1; return Transform(result); diff --git a/src/libhw/vpl.cpp b/src/libhw/vpl.cpp index d3832f01..f6729376 100644 --- a/src/libhw/vpl.cpp +++ b/src/libhw/vpl.cpp @@ -308,6 +308,7 @@ void VPLShaderManager::setVPL(const VPL &vpl) { case 4: lightViewTrafo = Transform::lookAt(p, p + Vector(0, 0, 1), Vector(0, 1, 0)).inverse(); break; case 5: lightViewTrafo = Transform::lookAt(p, p + Vector(0, 0, -1), Vector(0, 1, 0)).inverse(); break; } + lightViewTrafo = Transform::scale(Vector(-1, 1, 1)) * lightViewTrafo; const Matrix4x4 &viewMatrix = lightViewTrafo.getMatrix(); m_shadowProgram->setParameter(m_shadowProgramParam_cubeMapTransform[i], lightProjTrafo * lightViewTrafo); m_shadowProgram->setParameter(m_shadowProgramParam_depthVec[i], Vector4( @@ -331,6 +332,7 @@ void VPLShaderManager::setVPL(const VPL &vpl) { case 4: lightViewTrafo = Transform::lookAt(p, p + Vector(0, 0, 1), Vector(0, 1, 0)).inverse(); break; case 5: lightViewTrafo = Transform::lookAt(p, p + Vector(0, 0, -1), Vector(0, 1, 0)).inverse(); break; } + lightViewTrafo = Transform::scale(Vector(-1, 1, 1)) * lightViewTrafo; const Matrix4x4 &viewMatrix = lightViewTrafo.getMatrix(); m_altShadowProgram->setParameter(m_altShadowProgramParam_cubeMapTransform, lightProjTrafo * lightViewTrafo); @@ -368,13 +370,6 @@ void VPLShaderManager::configure(const VPL &vpl, const BSDF *bsdf, return; } -#if 0 - if (bsdfShader->getFlags() & Shader::ETransparent) { - m_renderer->setColor(Spectrum(1.0f), 0.3f); - return; - } -#endif - bool anisotropic = bsdf->getType() & BSDF::EAnisotropic; m_targetConfig = VPLProgramConfiguration(vplShader, bsdfShader, @@ -495,6 +490,8 @@ void VPLShaderManager::configure(const VPL &vpl, const BSDF *bsdf, << "float sinTheta2(vec3 v) { return 1.0-v.z*v.z; }" << endl << "float sinTheta(vec3 v) { float st2 = sinTheta2(v); if (st2 <= 0) return 0.0; else return sqrt(sinTheta2(v)); }" << endl << "float tanTheta(vec3 v) { return sinTheta(v)/cosTheta(v); }" << endl + << "float sinPhi(vec3 v) { return v.y/sinTheta(v); }" << endl + << "float cosPhi(vec3 v) { return v.x/sinTheta(v); }" << endl << endl; std::string vplEvalName, bsdfEvalName, lumEvalName; diff --git a/src/librender/scene.cpp b/src/librender/scene.cpp index a52e8e3d..f45146e0 100644 --- a/src/librender/scene.cpp +++ b/src/librender/scene.cpp @@ -237,14 +237,17 @@ void Scene::configure() { Float maxExtents = std::max(extents.x, extents.y); Float distance = maxExtents/(2.0f * std::tan(45 * .5f * M_PI/180)); - props.setTransform("toWorld", Transform::translate(Vector(center.x, center.y, aabb.min.z - distance))); + props.setTransform("toWorld", Transform::translate(Vector(center.x, + center.y, aabb.min.z - distance))); props.setFloat("fov", 45.0f); - m_camera = static_cast (PluginManager::getInstance()->createObject(MTS_CLASS(Camera), props)); + m_camera = static_cast (PluginManager::getInstance()-> + createObject(MTS_CLASS(Camera), props)); m_camera->configure(); m_sampler = m_camera->getSampler(); } else { - Log(EWarn, "Unable to set up a default camera -- does the scene contain anything at all?"); + Log(EWarn, "Unable to set up a default camera -- does the scene " + "contain anything at all?"); } } diff --git a/src/qtgui/glwidget.cpp b/src/qtgui/glwidget.cpp index 940919c3..e6e7a7b8 100644 --- a/src/qtgui/glwidget.cpp +++ b/src/qtgui/glwidget.cpp @@ -675,7 +675,7 @@ void GLWidget::mouseMoveEvent(QMouseEvent *event) { if (coords.x < 0 || coords.x > M_PI) m_context->up *= -1; - if (camera->getViewTransform().det3x3() < 0) + if (camera->getViewTransform().det3x3() > 0) camera->setInverseViewTransform(Transform::lookAt(p, target, m_context->up)); else camera->setInverseViewTransform( @@ -693,7 +693,7 @@ void GLWidget::mouseMoveEvent(QMouseEvent *event) { * camera->getViewTransform(); d = trafo.inverse()(Vector(0,0,1)); - if (camera->getViewTransform().det3x3() < 0) + if (camera->getViewTransform().det3x3() > 0) camera->setInverseViewTransform(Transform::lookAt(p, p+d, up)); else camera->setInverseViewTransform( @@ -713,7 +713,7 @@ void GLWidget::mouseMoveEvent(QMouseEvent *event) { Float roll = rel.x() * m_mouseSensitivity * .02f; Float fovChange = rel.y() * m_mouseSensitivity * .03f; - if (camera->getViewTransform().det3x3() < 0) { + if (camera->getViewTransform().det3x3() > 0) { m_context->up = Transform::rotate(d, roll)(up); camera->setInverseViewTransform(Transform::lookAt(p, p+d, m_context->up)); } else { @@ -743,7 +743,7 @@ void GLWidget::mouseMoveEvent(QMouseEvent *event) { Vector d = Vector(camera->getImagePlaneNormal()); p = p + (oldFocusDepth - focusDepth) * d; - if (camera->getViewTransform().det3x3() < 0) + if (camera->getViewTransform().det3x3() > 0) camera->setInverseViewTransform(Transform::lookAt(p, p+d, up)); else camera->setInverseViewTransform( @@ -809,8 +809,13 @@ void GLWidget::wheelEvent(QWheelEvent *event) { Vector d = Vector(camera->getImagePlaneNormal()); Point o = camera->getPosition() + (oldFocusDepth - focusDepth) * d; - camera->setInverseViewTransform( - Transform::lookAt(o, o+d, up)); + if (camera->getViewTransform().det3x3() > 0) + camera->setInverseViewTransform(Transform::lookAt(o, o+d, up)); + else + camera->setInverseViewTransform( + Transform::lookAt(o, o+d, up) * + Transform::scale(Vector(-1,1,1)) + ); m_wheelTimer->reset(); if (!m_movementTimer->isActive()) diff --git a/src/qtgui/save.cpp b/src/qtgui/save.cpp index c8f190fe..86c8b8d4 100644 --- a/src/qtgui/save.cpp +++ b/src/qtgui/save.cpp @@ -165,7 +165,7 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) { u = ctx->up; Point t, p = sceneCamera->getInverseViewTransform()(Point(0,0,0)); - if (sceneCamera->getViewTransform().det3x3() > 0) { + if (sceneCamera->getViewTransform().det3x3() < 0) { QDomElement scale = doc.createElement("scale"); scale.setAttribute("x", "-1"); cameraTransform.insertBefore(scale, lookAt);