3b(JidznOCxDo{XxvpAu_c4iz5nNH! z00UH`*H{+2?IJf*n*e_a;#b`ROupgTgZ?E?wUsAN7aw%Cl|&GyK(@J=DhoPWdHg#w zeUvQ&q>i*8@r=c-6_dk&13~Jywg`We{{O!?{--g4Ix0XTL|4;;x$ zo2QtIlM&h5+*v3;B%Esq^$YSN67dHLbScK9IU8NP%P``bl?vxCmse%rG$8bipiQ?S zMSrIglcuBxRyoJ)Nwi6YRJ1!iWmp_9o8jaCrps)zo{6%AUB__xj&PUM;8MwtHOPD< zG!R9fm$O%oh;WCLH5zv92)mXW(k>S;M0hqOe(9dbC=X^At(?V|som)M6v{3F(k`_w z5sVhVxu 0VU _q|uH zAPdZ@iJ#}GSfVuS3sFc$0Lir^Efogszr_jGyc7kY0K>U#WcGaE3_fR0zElJ2QeCZ$ z@R`xoM|N6SFs_;iBQv>{bHvKu`ngHVm5*Yy<4>Lv5p&!a!V%YZJYRm^9?wIZCPVL_ zJ-SNWC7nm?$5^xVnQ-r7EdK5@umS50pI35)!HdpmBn0>G7%I8=2w5hEkb8`Mt3Y)~ zK)>8A5+ SgkhH@b9iIZ+nwtN3} zip5*);1k(5a~0{oDYKkKKcji_&c)WV{Mbg !8 )VFlW`r zP_q?>&p~C5%Gc|-`buxEfno&%Ui#mYgn+csEx%(7X5-VL+WDjsRq}2kf953YMiOYV ziP0f^oQSvqm@$O)6OzCS1Mtn}#$ LWdDB`+1DbZBYqh7i8$YsuoA*gvl5KC82P%NO+wF*LnYjQ;&=P(S0f#a(lMA}w)+ zto~=* CttS;rP!xmG3y*a(au-q_8=d(LUwjT^XQP?3Q8=_^e98lSo(@hO0L-)h=w7qu;3*?eAzb z+*3}BDa)RYd-Y-%;iRkgBVDR?@{7rosRt*FBiW$Y?pT^AkUQyTOJ=# 0dRS2KCyY!*kh6AKTxG2evzXVW8BImCw)UDDaFPcmC6O{K4BZOHV zFcTcqW>N*>oHaP saPkXBt-ao2lD;Qp*bO37*SJzLak>|zHq?Y$EC?ixSiV^_jmbA zw|${+xD6M|_ `A~lP)U@^5zQE$gDSpz*@Wz-W_{!%PdF$ptcGo`vjvS-8Q-Qw7= z08XXr!!3z0Q)=vu-Xj*32MiO9axi|L+dHHx-{Qf}OjIN7$v@AyT7$n&kn^=E+)9R1 zDqF}u*%nzJx>W+%XA0CJP}ys31*d))8$3LYP}5PDT_j)0);T26??@^0E~JHK#KC2f z601#Yca=8g49MRE4DkcRV@O$^?&-^d6?UXSebrxWROw|6j_MB?E71CVCcz7GX08yE zfK^SqFNl6I1je4umg9!Q1Gtqu^_}%EJg-6I`BbZHPIxV8LEfz@yf9t+)@*`2%D G3~&KtJKrCA-R#Z$3EeY4Y8WEOz*ZnPKRTgF|Gdk_6NhyHauv6tcYAI z&LX$m917X@j!zP?W*c4rsZFM5n!XQMO-@Jk3%?|7(dB*Parcb 2Jz z_G|g1b8 ide_mq+Yrfj!+n&)||^n6?;>JviXibGpgdEY8r4k-ath$LU@$?SR) zfNgx0UGKZW&nGuW)vjzc^7wm-{Q}~hT4ZCUKX^+mG2cGglefOppebeQR?ay50PY_C z;Dk)M%eNwlYACalg8_N~SYh|rqX2YSf9~0D1jE>Hb-(LZtf$Srt3e*$}hg@m3cH{>^^*+{<3qLCS4)zjjPZL z6tQ<+2?B#_Y{HD}(2iPu&LHCSK6zjGn)!99%da-QNf7{5Av4>J;lNW@gG1n-SY+{i zfmVyLYn{Wz1oe UD-zjhS zxs(MOKbdOw;ms3Sa2(koBg@-58~o!%rOMmX8gNjG7fMt^DlK%{jBJO{1#)f;GDh2* zJreMQPgaG6*h?M=ww1cnD+FSDLIY)xF-G!vR25qX?v0_rFC9+fM6)MiKlVZU~A@jP6#*_5aM^B!E#R`U7g_bgN_BI?m z`FUae%i9&QDwTixWp_;mf6srC#kzcOzn># Y1IS zQj=*FjC%FfJK@J^x-{uKb?eE3np2~oS6k;Fw86e8X(-R;;*EvSA*;nP>8h!@8*P3U zzYB&v(1WToba#yhpTnvcVnxvgP`0+IuKp`x3(s4=t zz;bbsfK6_T9`|GS;Hg(ah0LLQ(|#9@JdW88(Fw}3-tVw-w}Nl|#40V|vAipFg=t`? zHP}~3un#M0KP+wr6m%jx9d2LWs s>vx`-*V_e#{ zb=3-}1_{64x59&p!Jy+Ysb}WCZoMq5#9{Qp$O#t-^cz&^#l)6!6y#(G6pR;RnU}XO zWjI;76?)y`G3c(2@6)ca;Y%>bc eJv)kSVx*l7dAh>ySX+?Ia60WR7<(&w2Ywq5e0cnrm3(N>E`HZ z50)v{6gIrpVrkp%Ez{Cb525^A^j>94n^`9n7S6jR9I3BWszqzdQnZubDB72P)bjZe zrCn5!i6myD%9uJ{u2I2i`GaBRr6Fr+ipl?jLy6@{b=(PxV=}z&nTniLy$p8$t1pN$ z9rY6NyfI}XhUaqpm^H^#B1V=;#zV3VZaF0|3YksQil2yRbw?k6$!b+X$1tHo60Gso z1oZ%YDA=AqBW;;Y66cNIV`165vj&v(T|g{2;{^pH4Jx1YNzr8jd%B$>TVQqpQ8u9x zaYG_D{M=WmhCZ$(Ory3L%?a>seLmUxf%MSYWwlzhh{c65I?22J5&q9$VhNGuR?^R1 zs|saNHSru_$W~vE|0Znc1MOq@LtCid9*>6pSP^ju6R{zsZVK6Oo0}qB xk%>d-dRSY Jo)eZoQ@_ z8@~WdNzb?|XFHayY%(_5uBXl%X*fH)r!Igki=713pJZ1_?bcW2pNGNH5!B`@qo1ix zKAPPv-{PXMZ*Jh{eeJ*G3XzoB-#3l`5bs?RniTT(&t*Z@*ccyE={5TceEZL{4A36t zvz)Z#a{-5tlR4}leW0q@G55*GGfh>x$R7j|wr|I6Hz&$>uPV*()g}5gG?SO %+ z=tQN43+ENnxdi{wxT^yodZt|yDK}R>cow=s)~lHBIgQQ$-OqU<9p?yZ!uRNYQ@n^x ziX{6^#4uo7E1k6*EI!j|2pzGz>g`hlH`1Lu1}pA}_kVBYe~$m@Gh@>96#ie8ztL-| zhlyv)yz-+8{^U%aaxkRoKva!IgFs{8S158iE0 Z-_ z>r=5FfBl;Oxv;H>7n`+H(hv*iv4!m|v>?bzt`deSfvd;Dd$cZ}KJX-2<@z8UNaSdn z*{A0K+SHZTIc-#n04UA4w%6~QaBr@cPRb-Z H7HSF#fg^VW8O T-zCdgu&t;HwyTOvt%hz4!kJ^B#N zL4lrtgn;?32xup)}CNk%t7PcdH;ucz?%djB+ ;VDSF&t48Bi64^ zL)3is{F(sPlfAyB#&@~KSi`|xmX}-ZTB3IXUIxF_+g?6;9%?3Bj>nf90g~NbYe$T1 zk4!W6!6G>)JtrANhrTshtY5+nI5OU`xwdtBhhYYTSHBe;?G##WGaQAX!^1Ri*>HCb zEu^7Zrr P{cz z2^>EmFxGzQAp`HbZXu9>uv+a}@h_iLDto~MLO`I)1UGZwWCT$9rLx;PV zdw}ZHAoTtNIYz7x5yZnq#JTiOEY`ceJGp0;`X-;H$UISPHo|q97Sf{z?&0LIL6!k; zlIN*`cP^hc(?aTOk7Ba!!)}Pl*2O%Hrb5ETaz3M*9*~%Hl|OsTmFIk0)?>cHxmDOs z_ZnfGr#@Lf^fD_yU7z`m|4ixi=#fD ZNm}f$IoI!{6w}yg*g=_}HWM)Kis!WcGF1IXS5s-ei8>RasB^w_@54D6dpt2K z>KRvLf<2WtX6+A6v`|XJH+V5lFRkl5JHw1k79; OZ9 z#l4H2El^15H(eI&)$$9j_KZ{WvG>QO#y- dIU+ePq`BHgCQtZ&X z=Yt=@RbwzAtNz;MGWAEh*TG-AG~e*tf7@&7c!3XtQ&Hzk928aDX(k=V6s!1CtgxN$ z)igEXt6YTyn(yU0$eD{lZ0XZE;=RbbymxTzo%Samk}jKKpfJF`QgcS!WZLH!&(u;l z{S_UCJEQjSy;}pTD+fFuih68y$oAs{?0@VZ`Vy1e&@t( hW2##828Ar@_gzbI z(^Z@(%V#rJpI@VU djdG5ezE9Tfci-X=QrO)Rg(KWrIID7gETdpV zg5YT$^SmW6aUKXoqLt>cMcxC (ZtJt7nOwAB8Q)64=i z-i|@mee_kSUOodtf<-SdlP_!q|52A0tw#eu+Nn{(2qc&$M@nyEN!DnZY+C-Z$9j=r zqYq*2gBo755 &t;CN60!amfr>t2 z!WN^4@DFRrBneNp$ Rf31KW~wnJc`0BOODR*cs#1_Y(sDn&<_Oc}rH_ zn(@=$CgM@BBM>r6_|0iA4ht0flJSh9Y0<7M`ac7c(+d(?)jC~}W;uob!D0;kK#tXh zaxTFn71OS}6rP^hRR|F;3VbAK>_f$K2Z0w|ORl{1Fg5-OC>U5)Ry4Kz)Akm}CD= z$J&Qtao!~un&lBcC8;%k1la$i%$P-Q^vb?3M`{QqfZ_iXO19lxvuPU9+-be5A_>3v z2Xw={MCMSxvu7@}3tL0ch*1mm*gxq(Ti$13f#s3S4z1Mu#*k;Fkc$NkY#P0->&_Q# zkN6M2$3EU8LSamsy-EHzur ^x zv=})@5%D5e^)3F}&F4qWBn~I6w}!CIc^ImsuU!!|?cGr$dGs!iqxZaj^<(}KS6-|L zUahlpQM!lzt^t^h-qp;nSB?{VA9F7XtC7? Ss@%{E`SV@(!YEthC_1w&^Q($kBd*r+w z#=S^Y15&c0J%8**zF)B$uigZhoRe04fBRp?q`6RG>TOTMvv5dMPRzj}wx8GKrImCb z-S|a#P%G-C)bSpzHr28>o$r6(nyT_{#bbT+9&wNT)W&{qLRPrLm@-UHW}I1=5EF XqE-ZP7G51r4<4_@ojD}I226-q5j6ZBS~phMS;F;L9UtAGrss6{ zHF p@Diw&gqq~D45 zk1Ikaow5!Zyt2!lbINpSlo1*}8qqXx#pU}zFYnXcTLV^Pu`#8|D1nGbDu-8?zr$r$ zn*Js86bY42lRD3Dht?p*$^9?9X8b>1o(du2ljJ vS|FPxs40|tf%F{ z+o-`j*mdaP`sTlP(3=%YD WTu^Vb6-m3T&{l^wX{}kp|R!IMw zS4401mQt39it5Oi#$FoA!-fG?X26N4s{W#DeDe-ly4a{h#Zo{mu`nJNrWy_UK+(y& zDiz?Va~(&5mOd5%Y?I`XN!a&9%nHbHrKT`&i^OR+IcNi1R-j}wa1YBTvau2 6kzps{f?|mul zd9;AwFiD4j9?una1V+qP$_^Pl9z9gHFv2WVD0)wn@hcSU^b@Jtd=Z@*Vtb}AJ?5zy zZj>}sHYPlfKRF%4uCx)PUBDIP#t_LePM+C>9XSq8g=goib)lctnrH* 8>kKkM}~jC%3EInlL9rn?ZHh?L}{uVlb)#W zTdL|nBoRH^FcvUx8Zd)~I?Vg`J|Vb42V;>xJ er9lTGiresRy>iG zbrq(&-o+FPQUHa~$;3Sj3(Iktdwvm9J0RJH0WN-&-HnwCby-$8gp!S2wDR2K-^>>Y zbuj`P?-50@tW#`;(H$dC_Px0Ypgi^TDcgsgP5|XO=I^0CZrsa|Z~9}FlW&;}p8W`r zElLC^C(cl3a(=7ivl;ri&3)Uy1+>J3AVd)jHduac)D`t#iRlTJFF}KyAMt%w*cD)9 ziWs8=o)j}eweaSNI+yABrME?MGe>mJ{W9Qd#j6Y(gXyC8^_t>u3HKiIX;- FXwf#epL3DC5!|_Id13zyi|cFe~)N zj8jav=W4ih;Ft24=!UGgblr11BU7MYAqz)40tVjB2Qd{vYw^#g7G@l?gz13I9$wz* zo@ZL585dnH*EG?^({SlMu{&n;9Hw?|-t3^9K^$7pXIdzKIiNAgIE|Vxq2~nlwmY1& zoEV15GDadLK8N7ovaK4#;irUJzsv23Trt?J217SS`> 7a=)dJs$0DSt>g68 ECCNiP+;_cy!7SKBs?aMfFLZt?CpWBkaUUQD _rE__HC`4x?()1w^fbfySQpY*G!$hjc-}*I78AlQkOyuFqZt+ z&WYT} !)2K~EP<@bZs^Z3KOLTmw-`$nW26ZMm$Mgo `0ENcD(&;di>qgXOpw(k0(hGx86A{>oHHjSaLY}a)wKKF{qu9UplvnqRx6y4 z01BY3oh8b{6Z S@+!9JPyl7d186hjWx( zfftZZ6`N)$l=(xAOMRmqa~(pGIHZ; J <}qq8-(sDMiDsu6|sE} z&_Wt~oJDNc4}eOpdmL@gYkM9D)nJHVKTs}?iTi0oaD@|2K{~*-pmwT)$?2(J@jwk0 zNWMi%V(ONHOubAGWvjp!6#~jp?a|@STu!pURC*&9BRH^Nq)4W&T+c9{s}R!=AE0JR zpw=KuX(k^-1k!~aNuaGzhbhhah~IjW0m=85YB|{@%ryKeNpvz^xRp`~qjHk)1uuaq zXx(jqJ1@WD83J4~ccmN}qnN4c0c*Ovps{Dq(UDhl!1of81V>0=3WQoLsEwerqa)HC z7^${W5sgvw)bxNgyQVbYDQ!`R6N^D0bso DG z=^5rP$xV#HL`c;KZgE^g+?DZP21yQ*<%63ew(sLD6OsUid8TXK&FXju5hAnx4HqH9 zRU2aM 5Ktbd8)yP-Q92cwbZ-^y7wCVA6Y6qqFX24Vy7=EwIVLTjoAm)<_0?vI zNhfD-H%MaUy$S1D#$Qqlj=omr$Y&;|n;A1auoH}y3C<7=GX?K^)}fXmdL7KDb0tHc zqFT?t>T-bP;1%%NnIp^Zp9G>$(AQc_BH;{C)kmK<6bfIz?Y&-$!L4pbuKn{iH%2Wo z9%oz?;TDlW9iIJV?) xmVYw1d;E_ zA14+tavDVXx&TK35GtTx2+o&(|TWn?8(z}=;cZXNGAjZ%zDv~c(=1G%iK^D zyjU*LbCZtINw#74-91(f3+B4Dg~sgCAJhvuWX5?teAJ!krc}(Ol<&xJfmG#)tTXD| z^RD)1*g^h@g1<37EaAa$oBXTqQ^Fp~1YVZ45G96g?h*%z;IhouFLri6hA<%-p<>NF zvJ~2DxF+02!m|W_-Ss!>zYIu5#zpXKe(V7@>fR$}nYtlacS!(0Ep^NJQm!IgrpVo( z`323iklt$*4Gj;XU^4#?slanFqgWxae)=Vw>y9{URMw9in|6w_SFQ#7h+AA4 ^A9r6gE`uu74Ivntg@C8hv7E4y1810~H1HK> ~^pV za8v@{WdQUOJehA{?^~b)zu>kQI~iQAj^zF$jYGmn0Ff*pemd_BJCTLlX|LIdEn;W* z1EH!$Z~RLFahRXnNsb}8#1HK|+Ipu0Ejdr>F=}f7H$0Fj@6&0sPbV0)g`XC$i327g z-3i+DQDVXyCH{s3Q`$XiUV VIS{yV?z& zHj_o;+G54r^Qs^IM`_5i+=_hC14|*9TbwA5XA`u<_IqN=;F#rO2mdM_D<`tdZ&I)x zU1kE?=3Dl&doCpt*oECu?5fwI4%*{kxE|u~8u*VAHC2>r3=@VG)xgFUi0jNH{v720 zL7 BzF6m2+wVfL&-d^F3?AQTa$zObV>!#5sf+=*R~ML{mzK};kpLiN zvHwxByH%I&rq7KTYw!bcnF;|HrrmVy#8fwZ63r6*k%W1#@|8uCe0Qtk48u|x{st3A zZdp2jfOc_1K&Gd8puMF|O*ExNo}DrLZyLsYVCYVGdE<9REDe+Bz@NQ$zooME}&( zbG=XOW;KGIi^BK$ySgWi*}g#k;od8P`_P>M(vAcln!h>Xr;|FGHO)ZjnsA4NgMp!| zOX*3EDeZ?_)w+H=fxU_`R^drZfCXKQeq`neRV)g2GGV`)#8m}ro=~;2=mwP`P8ROXnPer|eyEw}hI-hv zNB520UFruuNZqJSp`H|c!%=fp)PIKv^A=L@fd)Q)0=Jmo51_W4hX>O=pX~Y)1utda zozJc)^xB(z@{0NITy+DYIU0e?+I`QZaa8E<;ZA|>Bf*!lWH-A`$wZY*xO(R^id)3( zcO%x7<#gv|pD6 XkqqI2&h&%Vz4 z-voYN@zSV&0wE$~mP(~CFzDeZx-fGXrJmTXn@%SG`xeoFj7|Y}vbq^jEJ3jX>2Czd zF&uV&kEB0C#d~*sVCzSp8sfxeFrNvp|DHLF(H0wRC_-I7QHGD>-BWsancm)T7$PNV zv_kmFeipE4%bF4%O=<1VKt%scFB37bRG^%pE(91ZCeuXBK@_jL$z`m hs0 z2^9xq`62d-lCQTo`MJV?pZ}7ynHB@8CMeaM74sc(Vw>g8yaS=W-n&W{3wLLM0nt}x zWi=mJ&>$LpjHHQz=3GSn+yY{8Xw#)J c?o=g zkZ4hm<#f1i&5$h5?NKD-G(XLXRlFB%?NW)rcENi9-gFI7gT*DZ||hI+1+&B1`=b8e8p*E@^x{A<~5Xv8xk zk$v`UmS3FvN3yFR0cxXIBKz@Ni5N^5Tg9OISdqh?+nUK=tAbRuYA`9@|CX|=@0M82 z|K!)J3T;ATBY?n5*w6O(M3w7ZeE EV$b0T<9Uk@a`LwR zPPOTP Pz_D*g|G(NGg#3MuMtWr49mT)n6M8Ga|9WjPqM*aMW zb+zNMAaqq9eQh~EEnvAjlgH|nwKTE1Q~-%$HgqCRqpIXdwpSa{FC+F>%ldif4W@$P zl|nJ^t)71P#aRWScn*@Ee?OA>y(E&zqVSPXEsDsQR04cispk~+n}x|h+|5^BDR28r zmH~H1!u~32Pc4%{FLl+ElKR)|US_XMd>8t(Lo77Gy9nik(&Q{Fls*>b7&cDs#6n{f zF(7$K%8?fXF!A+)Td0LGVs53m4D9I?Ss;Pp!f&oeLsb$5ocsC&y4#!%hr$Xl3+$l> z^;ZLtNo9@acUy6K$<(EVt>kUh6elDLX486%=h-%V{dmgzHRH`7WD}5BwZp~o{naN4 z7W!Z{XZnf!Yrm~3d(?$KETVx1K-?Cf&PW1Gfn79%z2Nu2NF@0Q(0EQI#cKFkd`% z-HjoNIsR49_fgHgN0eenp(a$?)A`k)GmU|TXlAUuIju7~(Rryx_am{h5$xsn_+oip zu|n!~cmC$4#=yiw_uPK^fvDf2&o9T!?YfQU9Zt?#E>!-v>h3bCs;*xYI3XyV(p`#@ zQqoEzsem*{2%84!knR?wRbZol(y~DikOq;E4TuOxcT2}c&Rp<3j`um^e0j&c?;YoU z-R!+)tpA*I#s4>XKC6K6g4v2`*GqOS?l@B(WWGsC5QEjbq9RlG 1FaSqO3Tvu7-MKoXAOVc(C=cS>7A<4o4YZS zdZqIvtldp;hTca?v%HWfoBe^O%hTE0 4-8yf}O=8=z)KT2Vp z$XY7@n(!i)gI!IBttdLEV}h3Z0v?Fp^@m0-g=hpY3WaokdV<`V3epP^?4ky)+;& zx@Hcqa?iZSM$>)Xrs;xgSL=QS;;d`RivlXnF-kBnm&dW)(G{HZ`|) a?&xw& z=*Jp&H%bxycvt9LA#T)?2!4#ox)kq=WZ^Nt@x(vk710upV=xT*E8A&q`v{$ZfR3lj z8zh|l&`q$rO-A|tVT;nw_7s_z;VFX5c#b~tUB9U@dEHf>QfmYZ%TFwm I6SBi^>?lT DxG`gk> &d2ntuT zSUoZ{5RgR)ojj!!4o09;Sr5vBxGGqfZo9k0Y(EKeFNVvnVr0hWZx_2=nb6SkFdlcX zvFSW~$ -SvE!GO &5vis<1YOZeDOL6nDG;FvJRmZLb|Y )JU8n4Z3@T-<) zteGYWt|q{ee?}k+3AMOcl{5F`5z3pbu;&rlSzNuxg999OM0xkKQEax&*-{QkCw!R} zfoz|W#|rQ&=Jw45X>LkI3D-WQitC6rg{#>zD&$qnWWkbuL?B_sZKUh5)B+pn2iPKv zbO(}WY!Bla)$|lMsD?KQB`L%5L%|LnaVT@#kI9mkq^4IR8JH+O6=GGbqQkz~-b+n~ zH*9}qbb5tDmLYjbnD!>gK?^5ehM|KQ*UVxbCX2ySldTk{HzL)rACnk0>8JD 9;u1AS9?*3Fh|!?K#%T5Eghzq7(Z;clUzdrspU&DPc}Dyg@>Z|#g4ESYo7 z%nH>MY9-wIbDY^|itKP&E|>Tz`dbQ1zcwDDXYi ;z{CuR46 ( RL419P >57H^IKgjLwH%tu zabUX@H~bXwo&CI?PgO>u20r+iw2yDo=T9$vvDY~Xh!NscuoOD{(-4bD3L^ec>KTz= zoW%}?@mpbNIX&LYnhvRUpm4o^afRAG3Dn3Nt|pqR513UG2YlBeFJK`XfQ9il3PPuz z^hAQkv2;>ztIvkIGH6k6GZSpgqyGsDH rjS;v5#z*ne4Lk zwxjPwq$!ufJt)rqiXNx3+j$J0&&u-Xg}t|Beso_P5{Go)K`lFKW}!tmk$o_f^SKV* z-I!73zhGfhqkVljzfwJwHk1#~V|(p?goTt 81wxt|OyyGcbJJSH xTIEOwc4@TPu%jpN&PhJ<6cvVE7+CaO76+3pQ+{)RgOKHIoZkG zID8ro6G;*EMMnDzzMGS$s@|Rt(&(?gkdNW8xD2eVm#zcTL%-LgH9xL&+W#7V@SyG| zAp{|3b8xzz^W)oQXGk+lohcy-gfpdZ0$-03h?Tw4`qY!aHkz+bc5@2Jk;0&tfmedh z>jb530j}3)gO%`XPvvc<2o3z@k1IJDF3o}^3@?1fyp0f>A5=p+D$}3IAYZY|*&zto z3{cU<)q*UquS(;Ywq!mkR#Y2PBNwJzpTG9IEmgef!)>HHr7py&@R2kEcoKN-1-u{8 zSC~IxP*&LCq<0pvVQy9!NR(p3K19cw)o-2(5^$QLS-&T2$`ceHP`Q&f-3`0bdL5=< z9M0*-k$_Vke7YA6QXPSQ%w50sT(<3Gia-|eG-n#+JK9QR6(6zh>KMF@IG7B?qm&+t z+o;UfL@ZuGPPC`hz(gCSblU=6DJ?i~y@^mSj1um%(Ib0*d1YTH${~omdz5tiZc-R;GIP%o%A)|Q=+bDta#Kp^QUrAq1 z?KuJkx^wop#ITit#bABJc8lh{v?r!}d2;xHUY^9KV67JTDXq#ZhF6fj5rPt}+xT}# zLa_p!O=PtjS+!LkN>RE1E8!v*pS{1FLR{D6KYU~kcep(Cdd4pyi>JxKB1)mqd>%pAe z=FP)7amAx@xRV6<96}NE_7^b>BZN*?CVXAVnA!u!ib-y)5ZyFM1|;t@Z^jLMF~w2A zQ;PCTgc(x9pVH@7n1Ba^0UV<#{jcavT`Bo$Uuz;Luad1l8r~IPcSb(UO<^0Y; h0vXbPYdetR`(Cu`iHXRW zxJj=hf}E&|-1kpc7rMec8pI?$bRVh k$;n hM zBk#cX>m%-Y#N3V_BWtynZ6>-s6V4}$aM&Wc#Ahi6R-`${_#tY>!&zKFylLxu{#{nE zb>UXRH3og5x2pH#V>*I%1kPd_e`P-jI9n>O$nV$d{jDB!@R{b4X~Zf6N^_UE+L1p1 zEj|niD?Mw9Cf|1$+7_4lekXs;Y3F!jBq25pCSU`}>HvF7_bY218tq>ZEGhMQk%#lu zjfVxF+0BuiNfl5&=UhWjO!d+nu3*op4bNypg9@hL^w&XhV{GSb*&{XsBUM=@+IWo; z*iZgw14EA=4^w%&n0Ff5pR!m}91g?Y`Y+J%;Hog0L5bjZ;d`{upl*nJ!5}nxtQq{F zUq5}Pv!Gb6XKHK5YF1|3dbTFHMbtI|BjaqLF?X~|tJE&3f5-jSWY!4JGbj<-`#Rew zX~&T-J??9apJE?fi%P#|@KPt;S?~=>)4sMQp@6`VG~Q$cY{2Wt{rVM?oW8% =nafrRyi^tgW@;gtNkX3if-$P4QA2NHVS7bh?L0|{eBbMO2y zi~lc*8a+ZCt$&y{V|qqcE%n-@g{Zg)Bz;2lB?h_EO^oG@Zv;(ao6}Ntmr*PF BN1-2Ufx*=UQ6V~HE6@3x1Sv;H5ib5B&*OTe#}tcRlN@d4&bKIEuH<-W6V}})4&R~l z{{mey)DCu5RGp7VJ(q-5cVRNNl3!NTf!?|VLun>DL2qq5F0+JuJ$Leyl^Jr4jK?$_ z${UAm`X_ct6FrPrtRYz_eC#)cn>pxUsO#0OCnv%8{A>C=!y-62keb9TXnt1%Z&<3& zpO)LV_fys~`>4Rt0QQVEX?r4=J+>;~^z_BLY32r#lcZE&UQ06EdX^>1N#GaAJMJ`{ z-}$w~QYm>ORy1v~uefg>Mki9RUfXLEeu)yA&_}a`9-)j$j)*l7L}}E$f)?ds7EHbA zGw4v^|DUNs=saBquA_R?o^*XW`B=SM`GiKK`N-jN6Ia_CKJRqzf#U-9XRRwZ7xZme zXCF1p7nT3Yyr4jNi)6pJI_m3Wa9+{QIfQhM6ZZ2T)fbu$FXc;%s{STj8ZsS8yAqpm z$GC)H@$Tl`^ua3Wp_xsqqP8zGFEq4BYVTawvME!+m&Z3oWZ~;N^T*B31T(Dtb#_>! z4hQO(IUOPj2WSCUQg#{W 45eK7$<|9EPYtJ#VhI%j zyuFESHV(>jRaK$WG!WhZ`s}k788eY}KY6y|l+8Hy{K Fwzoc zze!l19_7I_gNTl#RL{3LI}4G#S22(wc=c*8ai0O<#20F#xhK&uFgeNj9+U _PfhedZ@%kM~b2U@o;Z4tcO}<@DhaIr= z*#u^{j)?D+M)bGAC=E!xW!21T&O;N`!&gBV|GFXWT2G#`$0j>j=Z l1;X4o1I!}kS z=V6mA)*$`vHPY1V$js*h`kqahSX`LF2Mc1kvAUx(B%7KMf;DH8;7uru@m-8vj`8;i z%rb&rjerXFXL(1l(I$z?Wq=W^$7Xu*RuxTF76pm&)!<7vsNXAS#MZc|9=(J=j1S*h z$h>1vQ(NijxaU;=LH~P|GW1rdRt8!9yIHZ_zWcN-yr1lh)dC239GGKoUsUjb`VhO` zbG23pPCt?KFh##cfNgYN==xA?5OGPmQEHS2!JDcFN{A{K%7*mz*CFiBM0WIB8X*0Y zZKC!GwOzY{saN*sIA}Axx>@q-D>)7Vluzepgz?&~X }3$9$s zbiYSGJU|0)$L%0r7zr`4>py#95p@46%cwffHJ%h88xWpO9y|^cXLHwTVLMBf$3n$N zVG|FWL1>JyE0i!r?)%LqM|sd^%ZRWUcyRl>3}!o@$>jMh#&;Ve5`V0GIK4{WdeiGF zSX9^jU>v9TK17{^-_ZLeqx!>4oGNKlD)+P-{WUUQB#k)URsyhsHUULF=AtvoHC#Y{ z{L`>NRX|?6-T|FKzfDes(u8+ljA~darpBVv=BLML;E7nV0@ny~p3C#-qOO~GFe`)M zZWg3Jwg8d8cp x^YNXGBHHU3AbsuQ{3Ckxp+bXM@6Wl;vutJoJI)>EXq#Y0ZGgjXklYju6S{?7(A+ zGcggwJKpKqYlZ)me(k4UgZNC$J)v7gBS`zTlrfSHb%|m&dp!OGj~&{T1m-l*GL37o zym#{}GmlwGi~J;$V^?VMrp@k^nR;8DoMnQVVDFz@vPMljgG5t! &$Fk#4w9cvc K(eKqUyz}V&o3}|{LJ#qTNx&t-{plP*uh_Q(trD9Hv zYvNCj8%$X!aQ _>a2kk27(dL z6#$a(fkoRV|IQuW6KM5y5m2xv*ADTR>8Qa66mKt)k#)a)48@#(cs^7NJz_^XX>r5m z2`|<-pv34ulD=!vF>mHRhx+s8v3i?jTlBxfRh6M@DP~CpdtdQt4Tb2ik&7cZ#*PfS zqum&&TQ#1g15X$JFQCGU*;tb_s1rcMUg{R-C@Au;PaImb5w9-do>L_+Ha>+|7h !)ck{ zJmfd$-z$%12ERF@uG!1WPSAx+M2v7BRYmKMm!a$t2YU1pmYtC$ZevGD=S2cwIJUAK zpJ;cn$`-a!Rq)crlBW7X=6KdBRfcR^HV+Z$&bo80JZm01!r|D$@E1$|oIoLxx8h>Tg_7_GjM?Q-zz($j%!0k#G1gq;I1ayfBl5-&zaS1K7S9tgn^wXXt* zShVG22I)E4gtqW8Kaw&5v%m4 $1RcsJ z&3l5cYklh I4|f{3Q9e@$;F*5w3= K8?+|yd$i= zgvI+-eZ=Q8V5i4;X|7Tn|5yn?v^!^+|4X!6cr8r}w>v}Ua|&xE_F{LL|GhVbf9;v` z@7;eoVYBgx-G7A<>6Kkes|}v7pAT=vE0Od;U#~wL;6h(5eq5>vZOjuhdKUmebbdw| zGNIJ#qPsU7ogirl`hyAByuca}dSRT(OFu3x`-UykK%3lvduhlT0&-rYna-|oo?PSm z{EKZm_{q=@9fd2gEvhinmS&3T2-T=EZBRxK^wI(##0nwI_xBL|BVb$JVG;Hkd59qz z4XML*6tA=ms|LP!!|wLX5Zaeli$@#chA`+Y2t6}#*zCZOpf0=I@$Da}=XOzVs1N$l ziv}pYCe@E4&%z&rQrPyfkX$)Dp80Q52;RMr15zmWA0&lTk!M6RE+>hzTa0+%pCX|t M%d5+k$~+4GFT~q&G5`Po literal 0 HcmV?d00001 From 15b4699572706a4a48120ad378d01d1cd36caf98 Mon Sep 17 00:00:00 2001 From: John Marcao <jmarcao@users.noreply.github.com> Date: Sat, 19 Oct 2019 10:18:07 -0400 Subject: [PATCH 02/27] TODOs in Dynamic Buffers Added creation of AABB primitve attribute buffers and calculated the transform matricies for Local to BLAS and BLAS to local space. --- .../DXR-DynamicBuffers.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp index e3ff63c..21249a0 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp @@ -111,7 +111,10 @@ void DXProceduralProject::CreateConstantBuffers() // structured buffers are for structs that have dynamic data (e.g lights in a scene, or AABBs in this case) void DXProceduralProject::CreateAABBPrimitiveAttributesBuffers() { + auto device = m_deviceResources->GetD3DDevice(); + auto elements = IntersectionShaderType::TotalPrimitiveCount; + m_aabbPrimitiveAttributeBuffer.Create(device, elements, 1, L"AABB Primitive Attr. Buffer"); } // LOOKAT-2.1: Update camera matrices stored in m_sceneCB. @@ -164,6 +167,11 @@ void DXProceduralProject::UpdateAABBPrimitiveAttributes(float animationTime) // You can infer what the bottom level AS space to local space transform should be. // The intersection shader tests in this project work with local space, but the geometries are provided in bottom level // AS space. So this data will be used to convert back and forth from these spaces. + + // Transformation matrix is Scaling * Rotation * Translation + auto mTransform = XMMatrixMultiply(XMMatrixMultiply(mScale, mRotation), mTranslation); + m_aabbPrimitiveAttributeBuffer[primitiveIndex].localSpaceToBottomLevelAS = mTransform; + m_aabbPrimitiveAttributeBuffer[primitiveIndex].bottomLevelASToLocalSpace = XMMatrixInverse(nullptr, mTransform); }; UINT offset = 0; From 61f2a324d17951f5c1b7f195a0597fa8352e7187 Mon Sep 17 00:00:00 2001 From: John Marcao <jmarcao@users.noreply.github.com> Date: Tue, 22 Oct 2019 20:27:01 -0400 Subject: [PATCH 03/27] A bunch of stuff for section 2, mostly filling in fields --- .../DXR-AccelerationStructure.cpp | 67 ++++++++++++++++++- .../DXR-Geometry.cpp | 24 ++++++- .../DXR-HitGroup.cpp | 32 ++++++++- .../DXR-RootSignature.cpp | 51 ++++++++++---- 4 files changed, 153 insertions(+), 21 deletions(-) diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp index 084077a..b31130b 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp @@ -31,6 +31,25 @@ void DXProceduralProject::BuildGeometryDescsForBottomLevelAS(array GetDesc().Width) auto& geometryDesc = geometryDescs[BottomLevelASType::Triangle][0]; geometryDesc = {}; + + // D3D12_RAYTRACING_GEOMETRY_DESC has a bunch of fields so take it one at a time + geometryDesc.Type = D3D12_RAYTRACING_GEOMETRY_TYPE_TRIANGLES; // Straighforward, this is a triangle + geometryDesc.Flags = geometryFlags; // Same flags as above? AABB below does the same so sure. + + // Assign Index Values + geometryDesc.Triangles.IndexBuffer = m_indexBuffer.resource->GetGPUVirtualAddress(); // Get GPU addr of this resource + geometryDesc.Triangles.IndexCount = m_indexBuffer.resource->GetDesc().Width / sizeof(Index); + geometryDesc.Triangles.IndexFormat = DXGI_FORMAT_R16_UINT; // Index is UINT16 + + // Now assign Vertex values. Just like above. + // Only diff is the buffer gets its data in start and stride (vertex length) + // Since I guess the vertex can be a complex type + geometryDesc.Triangles.VertexBuffer.StartAddress = m_vertexBuffer.resource->GetGPUVirtualAddress(); + geometryDesc.Triangles.VertexBuffer.StrideInBytes = sizeof(Vertex); + geometryDesc.Triangles.VertexCount = m_vertexBuffer.resource->GetDesc().Width / sizeof(Vertex); + geometryDesc.Triangles.VertexFormat = DXGI_FORMAT_R32G32B32_FLOAT; // Each vertex is a xyz of 32bit ints + // But each vertex includes vertex + normals + // But stride accounts for that. } @@ -49,7 +68,16 @@ void DXProceduralProject::BuildGeometryDescsForBottomLevelAS(array GetGPUVirtualAddress() + offset; + + // Everything else was filled and copied above, so I think thats the only thing unique to each + // AABB. Simpler than triangle because we smush it all into that buffer. + } } } @@ -68,7 +96,12 @@ AccelerationStructureBuffers DXProceduralProject::BuildBottomLevelAS(const vecto // Again, these tell the AS where the actual geometry data is and how it is laid out. // TODO-2.6: fill the bottom-level inputs. Consider using D3D12_ELEMENTS_LAYOUT_ARRAY as the DescsLayout. D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS &bottomLevelInputs = bottomLevelBuildDesc.Inputs; - + bottomLevelInputs.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY; + bottomLevelInputs.Flags = buildFlags; // Passed in as arg + bottomLevelInputs.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL; // This is a BLAS + bottomLevelInputs.NumDescs = static_cast (geometryDescs.size()); // Passed in as arg + // AS takes a union of different types, here we have geometryDescs to work with + bottomLevelInputs.pGeometryDescs = geometryDescs.data(); // Query the driver for resource requirements to build an acceleration structure. We've done this for you. D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO bottomLevelPrebuildInfo = {}; @@ -108,6 +141,8 @@ AccelerationStructureBuffers DXProceduralProject::BuildBottomLevelAS(const vecto // TODO-2.6: Now that you have the scratch and actual bottom-level AS desc, pass their GPU addresses to the bottomLevelBuildDesc. // Consider reading about D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC. // This should be as easy as passing the GPU addresses to the struct using GetGPUVirtualAddress() calls. + bottomLevelBuildDesc.DestAccelerationStructureData = bottomLevelAS->GetGPUVirtualAddress(); + bottomLevelBuildDesc.ScratchAccelerationStructureData = scratch->GetGPUVirtualAddress(); // Fill up the command list with a command that tells the GPU how to build the bottom-level AS. @@ -127,7 +162,12 @@ AccelerationStructureBuffers DXProceduralProject::BuildBottomLevelAS(const vecto // the AccelerationStructureBuffers struct so the top-level AS can use it! // Don't forget that this is the return value. // Consider looking into the AccelerationStructureBuffers struct in DXR-Structs.h - return AccelerationStructureBuffers{}; + AccelerationStructureBuffers asb = {}; + asb.accelerationStructure = bottomLevelAS; + asb.scratch = scratch; + asb.ResultDataMaxSizeInBytes = bottomLevelPrebuildInfo.ResultDataMaxSizeInBytes; + // Theres also a instanceDesc member, but that is ONLY for TLAS. + return asb; } // TODO-2.6: Build the instance descriptor for each bottom-level AS you built before. @@ -179,7 +219,28 @@ void DXProceduralProject::BuildBottomLevelASInstanceDescs(BLASPtrType *bottomLev // Where do you think procedural shader records would start then? Hint: right after. // * Make each instance hover above the ground by ~ half its width { + D3D12_RAYTRACING_INSTANCE_DESC a; // Just for reference... + // Start off just like triangles above + auto& instanceDesc = instanceDescs[BottomLevelASType::AABB]; + instanceDesc = {}; + instanceDesc.InstanceMask = 1; + + // 0 is triangle radiance, 1 is triangle shadow, 2 is aabb start + // Could probably not hardcode this + instanceDesc.InstanceContributionToHitGroupIndex = 2; + + // Like triangle! + instanceDesc.AccelerationStructure = bottomLevelASaddresses[BottomLevelASType::AABB]; + + // Now the transform. Hover above the ground by half its width... + // Don't scale here or rotate, so transform matrix == translation + const XMVECTOR vBasePosition = vWidth * XMLoadFloat3(&XMFLOAT3(0.0f, 0.5f, 0.0f)); + XMMATRIX mTranslation = XMMatrixTranslationFromVector(vBasePosition); + XMMATRIX mTransform = mTranslation; + + // Store the transform in the instanceDesc. + XMStoreFloat3x4(reinterpret_cast (instanceDesc.Transform), mTransform); } // Upload all these instances to the GPU, and make sure the resouce is set to instanceDescsResource. diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp index 9d93504..39c768f 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp @@ -33,12 +33,12 @@ void DXProceduralProject::BuildPlaneGeometry() { 3,1,0, 2,1,3, - }; // Cube vertices positions and corresponding triangle normals. Vertex vertices[] = { + // Position...................Normal...................... { XMFLOAT3(0.0f, 0.0f, 0.0f), XMFLOAT3(0.0f, 1.0f, 0.0f) }, // vertex 0: position 0, normal 0 { XMFLOAT3(1.0f, 0.0f, 0.0f), XMFLOAT3(0.0f, 1.0f, 0.0f) }, // vertex 1: position 1, normal 1 { XMFLOAT3(1.0f, 0.0f, 1.0f), XMFLOAT3(0.0f, 1.0f, 0.0f) }, // vertex 2.. @@ -87,6 +87,19 @@ void DXProceduralProject::BuildProceduralGeometryAABBs() auto InitializeAABB = [&](auto& offsetIndex, auto& size) { D3D12_RAYTRACING_AABB aabb{}; + // Minimum positions will be the base plus the offset of the + // object plus the size of the stride. This is like + // hopping to the right position in an array using size of + // the elements. + aabb.MinX = basePosition.x + offsetIndex.x * stride.x; + aabb.MinY = basePosition.y + offsetIndex.y * stride.y; + aabb.MinZ = basePosition.z + offsetIndex.z * stride.z; + + // Max is the same as above, but also add in the size. + aabb.MaxX = basePosition.x + offsetIndex.x * stride.x + size.x; + aabb.MaxY = basePosition.y + offsetIndex.y * stride.y + size.y; + aabb.MaxZ = basePosition.z + offsetIndex.z * stride.z + size.z; + return aabb; }; m_aabbs.resize(IntersectionShaderType::TotalPrimitiveCount); @@ -110,12 +123,17 @@ void DXProceduralProject::BuildProceduralGeometryAABBs() // TODO-2.5: Allocate an upload buffer for this AABB data. // The base data lives in m_aabbs.data() (the stuff you filled in!), but the allocationg should be pointed // towards m_aabbBuffer.resource (the actual D3D12 resource that will hold all of our AABB data as a contiguous buffer). - + AllocateUploadBuffer(device, + m_aabbs.data(), m_aabbs.size() * sizeof(D3D12_RAYTRACING_AABB), // Get the data from the CPU side buffer... + &m_aabbBuffer.resource, // And put in the GPU resource. + L"m_aabbBufferResouce"); // And give it a fun name } } // TODO-2.5: Build geometry used in the project. As easy as calling both functions above :) void DXProceduralProject::BuildGeometry() { - + // The hardest part of this project :( + BuildPlaneGeometry(); + BuildProceduralGeometryAABBs(); } \ No newline at end of file diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-HitGroup.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-HitGroup.cpp index 33899bd..2dbb413 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-HitGroup.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-HitGroup.cpp @@ -29,6 +29,24 @@ void DXProceduralProject::CreateHitGroupSubobjects(CD3D12_STATE_OBJECT_DESC* ray // TODO-2.3: AABB geometry hit groups. Very similar to triangles, except now you have to *also* loop over the primitive types. { + for (UINT primType = 0; primType < IntersectionShaderType::Count; primType++) { + for (UINT rayType = 0; rayType < RayType::Count; rayType++) { + auto hitGroup = raytracingPipeline->CreateSubobject (); + + // Like above, if this is a radiance ray, get the shader + if (rayType == RayType::Radiance) { + hitGroup->SetClosestHitShaderImport(c_closestHitShaderNames[GeometryType::AABB]); + } + + hitGroup->SetHitGroupExport(c_hitGroupNames_AABBGeometry[primType][rayType]); + hitGroup->SetHitGroupType(D3D12_HIT_GROUP_TYPE_PROCEDURAL_PRIMITIVE); + + // Also include the intersection shader based on the primitive + hitGroup->SetIntersectionShaderImport(c_intersectionShaderNames[primType]); + + // Anyhit shaders? Need to implement them. + } + } } } @@ -54,6 +72,18 @@ void DXProceduralProject::CreateLocalRootSignatureSubobjects(CD3D12_STATE_OBJECT // TODO-2.3: AABB geometry hitgroup/local root signature association. // Very similar to triangles, except now one for each primitive type. { - + auto localRootSignature = raytracingPipeline->CreateSubobject (); + + // Get the AABB local root signature + localRootSignature->SetRootSignature(m_raytracingLocalRootSignature[LocalRootSignature::Type::AABB].Get()); + + // Associate the shaders + auto rootSignatureAssociation = raytracingPipeline->CreateSubobject (); + rootSignatureAssociation->SetSubobjectToAssociate(*localRootSignature); + + // Associate for each primitive type + for (UINT primType = 0; primType < IntersectionShaderType::Count; primType++) { + rootSignatureAssociation->AddExports(c_hitGroupNames_AABBGeometry[primType]); + } } } \ No newline at end of file diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp index 2dff8b5..51a008b 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp @@ -20,26 +20,38 @@ void DXProceduralProject::CreateRootSignatures() ranges[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 1, 0); // 1 output texture // TODO-2.2: In range index 1 (the second range), initialize 2 SRV resources at register 1: indices and vertices of triangle data. - // This will effectively put the indices at register 1, and the vertices at register 2. - + // This will effectively put the indices at register 1, and the vertices at register 2. + ranges[1].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 2, 1); // TODO-2.2: Initialize all the parameters of the GlobalRootSignature in their appropriate slots. // * See GlobalRootSignature in RaytracingSceneDefines.h to understand what they are. // - The OutputView should correspond to the UAV range descriptor above (descriptor table), bound to register 0 of the UAV registers. - // - The Index/Vertex Buffer should correspond to the SRV range (descriptor table) above, bound to registers 1 and 2 of the SRV registers. - // Note that since we initialize these as a range of size 2, then you should bind the entire range to register 1. - // This will automatically fill in registers 1 and 2. + // - The Index/Vertex Buffer should correspond to the SRV range (descriptor table) above, bound to registers 1 and 2 of the SRV registers. + // Note that since we initialize these as a range of size 2, then you should bind the entire range to register 1. + // This will automatically fill in registers 1 and 2. // - The AccelerationStructure should be init as SRV bound to register 0 of the SRV registers. - // - The SceneConstant should be init as a ConstantBufferView (CBV) bound to register 0 of the CBV registers. - // - The AABBAttributeBuffer should be init as SRV bound to register 3 of the SRV registers. + // - The SceneConstant should be init as a ConstantBufferView (CBV) bound to register 0 of the CBV registers. + // - The AABBAttributeBuffer should be init as SRV bound to register 3 of the SRV registers. // - Look up InitAsDescriptorTable(), InitAsShaderResourceView(), and InitAsConstantBuffer() in the DirectX documentation // to understand what to do. - // - If you're ever unsure if the register mapping is correct, look at the top of Raytracing.hlsl. - // u registers --> UAV - // t registers --> SRV - // b registers --> CBV + // - If you're ever unsure if the register mapping is correct, look at the top of Raytracing.hlsl. + // u registers --> UAV + // t registers --> SRV + // b registers --> CBV CD3DX12_ROOT_PARAMETER rootParameters[GlobalRootSignature::Slot::Count]; + // JOHN: https://docs.microsoft.com/en-us/windows/win32/direct3d12/creating-a-root-signature + // Init the output view using the UAV Descriptor above + rootParameters[GlobalRootSignature::Slot::OutputView].InitAsDescriptorTable(1, &ranges[0]); + // Init to SRV register 0 + rootParameters[GlobalRootSignature::Slot::AccelerationStructure].InitAsShaderResourceView(0); + // Init to CBV register 0 + rootParameters[GlobalRootSignature::Slot::SceneConstant].InitAsConstantBufferView(0); + // Init to SRV register 3 + rootParameters[GlobalRootSignature::Slot::AABBattributeBuffer].InitAsShaderResourceView(3); + // Init to range above for SRV. We use 2 registers above... + rootParameters[GlobalRootSignature::Slot::VertexBuffers].InitAsDescriptorTable(1, &ranges[1]); + // Finally, we bundle up all the descriptors you filled up and tell the device to create this global root signature! CD3DX12_ROOT_SIGNATURE_DESC globalRootSignatureDesc(ARRAYSIZE(rootParameters), rootParameters); SerializeAndCreateRaytracingRootSignature(globalRootSignatureDesc, &m_raytracingGlobalRootSignature); @@ -63,11 +75,22 @@ void DXProceduralProject::CreateRootSignatures() // TODO-2.2: AABB geometry. Inspire yourself from the triangle local signature above to create an AABB local signature // - Remember that the AABB holds 1 slot for Material Constants, and another 1 for the geometry instance. // - See the AABB Definition in RaytracingSceneDefines.h to understand what this means. - // - Use registers 1 and 2 of the CBVs for the AABB. Yes, althought the triangle MaterialConstant *also* maps - // to register 1, this overlap is allowed since we are talking about *local* root signatures + // - Use registers 1 and 2 of the CBVs for the AABB. Yes, although the triangle MaterialConstant *also* maps + // to register 1, this overlap is allowed since we are talking about *local* root signatures // --> the values they hold will depend on the shader function the local signature is bound to! { - + namespace RootSignatureSlots = LocalRootSignature::AABB::Slot; + CD3DX12_ROOT_PARAMETER rootParameters[RootSignatureSlots::Count]; + + // In RayTractingDefines.h, LocalRootSignature::AABB, it defines each of the registers we need to pass in + // So this is starting to make sense, these will be passed to each shader uniquely, while the above are global + // to all shaders.... ahh? + rootParameters[RootSignatureSlots::MaterialConstant].InitAsConstants(SizeOfInUint32(PrimitiveConstantBuffer), 1); + rootParameters[RootSignatureSlots::GeometryIndex].InitAsConstants(SizeOfInUint32(PrimitiveInstanceConstantBuffer), 1); + + CD3DX12_ROOT_SIGNATURE_DESC localRootSignatureDesc(ARRAYSIZE(rootParameters), rootParameters); + localRootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_LOCAL_ROOT_SIGNATURE; + SerializeAndCreateRaytracingRootSignature(localRootSignatureDesc, &m_raytracingLocalRootSignature[LocalRootSignature::Type::AABB]); } } } From 6bea13a9cae3a7b6b7b9f0f4ddf7bfb5e6fa9c1f Mon Sep 17 00:00:00 2001 From: John Marcao <jmarcao@users.noreply.github.com> Date: Tue, 22 Oct 2019 20:56:45 -0400 Subject: [PATCH 04/27] Up to 2.6 done --- .../DXR-AccelerationStructure.cpp | 47 ++++++++++++++----- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp index b31130b..3f7ec18 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp @@ -235,7 +235,7 @@ void DXProceduralProject::BuildBottomLevelASInstanceDescs(BLASPtrType *bottomLev // Now the transform. Hover above the ground by half its width... // Don't scale here or rotate, so transform matrix == translation - const XMVECTOR vBasePosition = vWidth * XMLoadFloat3(&XMFLOAT3(0.0f, 0.5f, 0.0f)); + const XMVECTOR vBasePosition = c_aabbWidth * XMLoadFloat3(&XMFLOAT3(0.0f, 0.5f, 0.0f)); XMMATRIX mTranslation = XMMatrixTranslationFromVector(vBasePosition); XMMATRIX mTransform = mTranslation; @@ -263,6 +263,13 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt // TODO-2.6: fill in the topLevelInputs, read about D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS. // Consider using D3D12_ELEMENTS_LAYOUT_ARRAY as a DescsLayout since we are using an array of bottom-level AS. D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS &topLevelInputs = topLevelBuildDesc.Inputs; + topLevelInputs.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY; + topLevelInputs.Flags = buildFlags; // Passed in as arg + topLevelInputs.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL; // This is a TLAS + topLevelInputs.NumDescs = BottomLevelASType::Count; // Desc here are BLAS descs. + // How will it know size of each element in array??? + // Actually, we do this below after vuilding the BLAS + //topLevelInputs.InstanceDescs = bottomLevelAS[0].instanceDesc->GetGPUVirtualAddress(); D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO topLevelPrebuildInfo = {}; @@ -277,7 +284,7 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt ThrowIfFalse(topLevelPrebuildInfo.ResultDataMaxSizeInBytes > 0); // TODO-2.6: Allocate a UAV buffer for the scracth/temporary top-level AS data. - + AllocateUAVBuffer(device, topLevelPrebuildInfo.ScratchDataSizeInBytes, &scratch, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, L"ScratchResource"); // Allocate space for the top-level AS. { @@ -292,7 +299,7 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt } // TODO-2.6: Allocate a UAV buffer for the actual top-level AS. - + AllocateUAVBuffer(device, topLevelPrebuildInfo.ResultDataMaxSizeInBytes, &topLevelAS, initialResourceState, L"TopLevelAccelerationStructure"); } // Note on Emulated GPU pointers (AKA Wrapped pointers) requirement in Fallback Layer: @@ -320,7 +327,7 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt }; // TODO-2.6: Call the fallback-templated version of BuildBottomLevelASInstanceDescs() you completed above. - + BuildBottomLevelASInstanceDescs (bottomLevelASaddresses, &instanceDescsResource); } else // DirectX Raytracing { @@ -332,7 +339,7 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt }; // TODO-2.6: Call the DXR-templated version of BuildBottomLevelASInstanceDescs() you completed above. - + BuildBottomLevelASInstanceDescs (bottomLevelASaddresses, &instanceDescsResource); } // Create a wrapped pointer to the acceleration structure. @@ -344,7 +351,9 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt // TODO-2.6: fill in the topLevelBuildDesc. Read about D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC. // This should be as easy as passing the GPU addresses to the struct using GetGPUVirtualAddress() calls. - + // Similar to the triangles above + topLevelBuildDesc.DestAccelerationStructureData = topLevelAS->GetGPUVirtualAddress(); + topLevelBuildDesc.ScratchAccelerationStructureData = scratch->GetGPUVirtualAddress(); // Build acceleration structure. if (m_raytracingAPI == RaytracingAPI::FallbackLayer) @@ -363,7 +372,12 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt // Very similar to how you did this in BuildBottomLevelAS() except now you have to worry about topLevelASBuffers.instanceDesc. // Consider looking into the AccelerationStructureBuffers struct in DXR-Structs.h. // Make sure to return the topLevelASBuffers before you exit the function. - return AccelerationStructureBuffers{}; + AccelerationStructureBuffers asb; + asb.accelerationStructure = topLevelAS; + asb.instanceDesc = instanceDescsResource; + asb.ResultDataMaxSizeInBytes = topLevelPrebuildInfo.ResultDataMaxSizeInBytes; + asb.scratch = scratch; + return asb; } // TODO-2.6: This will wrap building the Acceleration Structure! This is what we will call when building our scene. @@ -379,12 +393,16 @@ void DXProceduralProject::BuildAccelerationStructures() // TODO-2.6: Build the geometry descriptors. Hint: you filled in a function that does this. array , BottomLevelASType::Count> geometryDescs; - + BuildGeometryDescsForBottomLevelAS(geometryDescs); // TODO-2.6: For each bottom-level object (triangle, procedural), build a bottom-level AS. // Hint: you filled in a function that does this. AccelerationStructureBuffers bottomLevelAS[BottomLevelASType::Count]; - + // Use a none build flag, maybe revisit + auto buildFlag = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_NONE; + for (int i = 0; i < BottomLevelASType::Count; i++) { + bottomLevelAS[i] = BuildBottomLevelAS(geometryDescs[i], buildFlag); + } // Batch all resource barriers for bottom-level AS builds. // This will Notifies the driver that it needs to synchronize multiple accesses to resources. @@ -397,7 +415,9 @@ void DXProceduralProject::BuildAccelerationStructures() // TODO-2.6: Build top-level AS. Hint, you already made a function that does this. AccelerationStructureBuffers topLevelAS; - + // Use a none build flag, maybe revisit + buildFlag = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_NONE; + topLevelAS = BuildTopLevelAS(bottomLevelAS, buildFlag); // Kick off acceleration structure construction. m_deviceResources->ExecuteCommandList(); @@ -406,7 +426,10 @@ void DXProceduralProject::BuildAccelerationStructures() m_deviceResources->WaitForGpu(); // TODO-2.6: Store the AS buffers. The rest of the buffers will be released once we exit the function. - // Do this for both the bottom-level and the top-level AS. Consider re-reading the DXProceduralProject class + // Do this for both the bottom-level and the top-level AS. Consider re-reading the class // to find what member variables should be set. - + for (int i = 0; i < BottomLevelASType::Count; i++) { + m_bottomLevelAS[i] = bottomLevelAS[i].accelerationStructure; + } + m_topLevelAS = topLevelAS.accelerationStructure; } \ No newline at end of file From 897e8cb568ebaa8847bc173e17f81aa7854fcadf Mon Sep 17 00:00:00 2001 From: John Marcao <jmarcao@users.noreply.github.com> Date: Tue, 22 Oct 2019 21:45:22 -0400 Subject: [PATCH 05/27] Started 2.7 --- .../DXR-ShaderTable.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp index 150e92d..33bc872 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp @@ -32,8 +32,10 @@ void DXProceduralProject::BuildShaderTables() // TODO-2.7: Miss shaders. // Similar to the raygen shader, but now we have 1 for each ray type (radiance, shadow) // Don't forget to update shaderIdToStringMap. - missShaderIDs[0] = nullptr; - missShaderIDs[1] = nullptr; + missShaderIDs[0] = stateObjectProperties->GetShaderIdentifier(c_missShaderNames[0]); + shaderIdToStringMap[missShaderIDs[0]] = c_missShaderNames[0]; + missShaderIDs[1] = stateObjectProperties->GetShaderIdentifier(c_missShaderNames[1]); + shaderIdToStringMap[missShaderIDs[1]] = c_missShaderNames[1]; // Hitgroup shaders for the Triangle. We have 2: one for radiance ray, and another for the shadow ray. for (UINT i = 0; i < RayType::Count; i++) @@ -43,7 +45,13 @@ void DXProceduralProject::BuildShaderTables() } // TODO-2.7: Hitgroup shaders for the AABBs. We have 2 for each AABB. - + for (UINT j = 0; j < IntersectionShaderType::Count; j++) { + for (UINT i = 0; i < RayType::Count; i++) + { + hitGroupShaderIDs_AABBGeometry[j][i] = stateObjectProperties->GetShaderIdentifier(c_hitGroupNames_AABBGeometry[j][i]); + shaderIdToStringMap[hitGroupShaderIDs_AABBGeometry[j][i]] = c_hitGroupNames_AABBGeometry[j][i]; + } + } }; // Get shader identifiers using the lambda function defined above. From bd8f40c9ddad6d2176b45f81cd9e44404073cf3e Mon Sep 17 00:00:00 2001 From: John Marcao <jmarcao@users.noreply.github.com> Date: Thu, 24 Oct 2019 19:08:57 -0400 Subject: [PATCH 06/27] Finish 2.7 --- .../DXR-ShaderTable.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp index 33bc872..e122733 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp @@ -103,7 +103,20 @@ void DXProceduralProject::BuildShaderTables() // TODO-2.7: Miss shader table. Very similar to the RayGen table except now we push_back() 2 shader records // 1 for the radiance ray, 1 for the shadow ray. Don't forget to call DebugPrint() on the table for your sanity! { - + UINT numShaderRecords = RayType::Count; // one for each ray + UINT shaderRecordSize = shaderIDSize; // No root arguments + + // The RayGen shader table contains a single ShaderRecord: the one single raygen shader! + ShaderTable missShaderTable(device, numShaderRecords, shaderRecordSize, L"MissShaderTable"); + + // Push back a shader record for each ray type + for (int i = 0; i < numShaderRecords; i++) { + missShaderTable.push_back(ShaderRecord(missShaderIDs[i], shaderRecordSize, nullptr, 0)); + } + + // Save the uploaded resource (remember that the uploaded resource is created when we call Allocate() on a GpuUploadBuffer + missShaderTable.DebugPrint(shaderIdToStringMap); + m_missShaderTable = missShaderTable.GetResource(); } // Hit group shader table. This one is slightly different given that a hit group requires its own custom root signature. @@ -142,6 +155,7 @@ void DXProceduralProject::BuildShaderTables() // the primitive type is used to tell the shader what type of procedural geometry this is. // Remember that hitGroupShaderIDs_AABBGeometry is a 2-array indexed like so [type of geometry][ray type] { + // .....I guess this is already done? LocalRootSignature::AABB::RootArguments rootArgs; UINT instanceIndex = 0; From 225ca9c6c7cdff22c30a69a893a7063dd6a80a07 Mon Sep 17 00:00:00 2001 From: John Marcao <jmarcao@users.noreply.github.com> Date: Thu, 24 Oct 2019 19:36:22 -0400 Subject: [PATCH 07/27] Part 2 done! --- .../DXR-DoRaytracing.cpp | 36 +++++++++++++++---- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp index 03a8c58..1cf6f1e 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp @@ -22,7 +22,11 @@ void DXProceduralProject::DoRaytracing() commandList->SetComputeRootConstantBufferView(GlobalRootSignature::Slot::SceneConstant, m_sceneCB.GpuVirtualAddress(frameIndex)); // TODO-2.8: do a very similar operation for the m_aabbPrimitiveAttributeBuffer - + m_aabbPrimitiveAttributeBuffer.CopyStagingToGpu(frameIndex); + commandList->SetComputeRootConstantBufferView( + GlobalRootSignature::Slot::AABBattributeBuffer, + m_aabbPrimitiveAttributeBuffer.GpuVirtualAddress(frameIndex) + ); // Bind the descriptor heaps. if (m_raytracingAPI == RaytracingAPI::FallbackLayer) @@ -49,26 +53,44 @@ void DXProceduralProject::DoRaytracing() // This should be done by telling the commandList to SetComputeRoot*(). You just have to figure out what * is. // Example: in the case of GlobalRootSignature::Slot::SceneConstant above, we used SetComputeRootConstantBufferView() // Hint: look at CreateRootSignatures() in DXR-Pipeline.cpp. - + + // We do indicies because verticies are just one register over? So we get register 1 + register 2 + // In CreateRootSignatures() we bound the data to m_raytracingGlobalRootSignature. That buffer + // has the rootSignature for each index/vertex + commandList->SetComputeRootSignature(m_raytracingGlobalRootSignature.Get()); // TODO-2.8: Bind the OutputView (basically m_raytracingOutputResourceUAVGpuDescriptor). Very similar to the Index/Vertex buffer. - + m_raytracingOutputResourceUAVGpuDescriptor; // Its a descriptor handle, so probably root descriptor table? + commandList->SetComputeRootDescriptorTable( + GlobalRootSignature::Slot::OutputView, + m_raytracingOutputResourceUAVGpuDescriptor); // This will define a `DispatchRays` function that takes in a command list, a pipeline state, and a descriptor // This will set the hooks using the shader tables built before and call DispatchRays on the command list auto DispatchRays = [&](auto* raytracingCommandList, auto* stateObject, auto* dispatchDesc) { // You will fill in a D3D12_DISPATCH_RAYS_DESC (which is dispatchDesc). - // TODO-2.8: fill in dispatchDesc->HitGroupTable. Look up the struct D3D12_GPU_VIRTUAL_ADDRESS_RANGE_AND_STRIDE - + // TODO-2.8: fill in dispatchDesc->HitGroupTable. Look up the struct D3D12_GPU_VIRTUAL_ADDRESS_RANGE_AND_STRIDE + D3D12_GPU_VIRTUAL_ADDRESS_RANGE_AND_STRIDE hgt; + dispatchDesc->HitGroupTable.StartAddress = m_hitGroupShaderTable->GetGPUVirtualAddress(); // Want to start here, makes sense. + dispatchDesc->HitGroupTable.SizeInBytes = m_hitGroupShaderTable->GetDesc().Width; // Similar to before + dispatchDesc->HitGroupTable.StrideInBytes = m_hitGroupShaderTableStrideInBytes; // Makes sense... // TODO-2.8: now fill in dispatchDesc->MissShaderTable - + // Similar to above? They're all very simialr structs + dispatchDesc->MissShaderTable.StartAddress = m_missShaderTable->GetGPUVirtualAddress(); + dispatchDesc->MissShaderTable.SizeInBytes = m_missShaderTable->GetDesc().Width; + dispatchDesc->MissShaderTable.StrideInBytes = m_missShaderTableStrideInBytes; // TODO-2.8: now fill in dispatchDesc->RayGenerationShaderRecord - + // This one is different, It doesn't have a stride + // Which makes sense. There is only one object represented here. + // No other object to stride to + dispatchDesc->RayGenerationShaderRecord.StartAddress = m_rayGenShaderTable->GetGPUVirtualAddress(); + dispatchDesc->RayGenerationShaderRecord.SizeInBytes = m_rayGenShaderTable->GetDesc().Width; // We do this for you. This will define how many threads will be dispatched. Basically like a blockDims in CUDA! + // 2d! dispatchDesc->Width = m_width; dispatchDesc->Height = m_height; dispatchDesc->Depth = 1; From 7068dd2f88de36cf62e5371df0af237f92cff33e Mon Sep 17 00:00:00 2001 From: John Marcao <jmarcao@users.noreply.github.com> Date: Thu, 24 Oct 2019 20:14:52 -0400 Subject: [PATCH 08/27] Hit bug --- .../Main.cpp | 2 +- .../Raytracing.hlsl | 16 +++++++- .../RaytracingShaderHelper.hlsli | 37 +++++++++++++++++-- 3 files changed, 50 insertions(+), 5 deletions(-) diff --git a/src/D3D12RaytracingProceduralGeometry/Main.cpp b/src/D3D12RaytracingProceduralGeometry/Main.cpp index 7f70bc6..6bbb443 100644 --- a/src/D3D12RaytracingProceduralGeometry/Main.cpp +++ b/src/D3D12RaytracingProceduralGeometry/Main.cpp @@ -16,7 +16,7 @@ #include "stdafx.h" #include "DXProceduralProject.h" -#define CPU_CODE_COMPLETE 0 +#define CPU_CODE_COMPLETE 1 _Use_decl_annotations_ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow) diff --git a/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl b/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl index d066933..8690b73 100644 --- a/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl +++ b/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl @@ -149,9 +149,23 @@ bool TraceShadowRayAndReportIfHit(in Ray ray, in UINT currentRayRecursionDepth) [shader("raygeneration")] void MyRaygenShader() { + // Snneaky shortcut from the end of this function. Probably noot at all sneaky + uint2 idx = DispatchRaysIndex().xy; + + // (1) Generate a ray using the function GenerateCameraRay() in RaytracingShaderHelper.hlsli + Ray r = GenerateCameraRay( + idx, // Index is uint2, though it can be up to uint3 + g_sceneCB.cameraPosition.xyz, // Use the buffers we set up before! + g_sceneCB.projectionToWorld // Same, use this! + ); + + // (2) Trace a radiance ray using the generated ray to obtain a color + // Set recursion to 0 becuase this is our inital ray. + // Other shaders will not have this set to 0 + float4 rayColor = TraceRadianceRay(r, 0); // See above function! // Write the color to the render target - g_renderTarget[DispatchRaysIndex().xy] = float4(0.0f, 0.0f, 0.0f, 0.0f); + g_renderTarget[DispatchRaysIndex().xy] = rayColor; } //*************************************************************************** diff --git a/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli b/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli index 94bf5cc..e9b5119 100644 --- a/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli +++ b/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli @@ -68,7 +68,20 @@ bool is_a_valid_hit(in Ray ray, in float thit, in float3 hitSurfaceNormal) // (3) Call the hlsl built-in function smoothstep() on this interpolant to smooth it out so it doesn't change abruptly. float CalculateAnimationInterpolant(in float elapsedTime, in float cycleDuration) { - return smoothstep(0, 1, 0); + float cyclePos = elapsedTime; + + // Subtract durations until we are under + while (cyclePos > cycleDuration) { cyclePos -= cycleDuration; } + + // Think of it as going from 0 to 2, but if greater than 1 subtract the excess. + // Like folding in on itself + float cyclePercent = cyclePos / cycleDuration; + float interpolant = cyclePercent * 2; + if (interpolant > 1) { + interpolant -= interpolant - 1; + } + + return smoothstep(0, 1, interpolant); } // Load three 2-byte indices from a ByteAddressBuffer. @@ -129,9 +142,27 @@ float3 HitAttribute(float3 vertexAttribute[3], float2 barycentrics) // as long as the direction of the ray is correct then the depth does not matter. inline Ray GenerateCameraRay(uint2 index, in float3 cameraPosition, in float4x4 projectionToWorld) { + // Index is our assigned pixel, which is also our ray index. + // We need to divide this by width/height to get a position beteen -1,-1 and 1,1 + // So easy: get it to 0,0 and 1,1. Then -0.5 and double it? + float2 screenDim; + screenDim.x = DispatchRaysDimensions().x; + screenDim.y = DispatchRaysDimensions().y; + + float2 pos = index / screenDim; + pos -= 0.5; + pos *= 2; + + // Now take that position and flip the y axis cause the DirectX screen geos from top to bottom + pos.y = -pos.y; + + // Transform to world position from camera position + float4 worldPos = mul(float4(pos, 1, 1), projectionToWorld); + Ray ray; - ray.origin = float3(0.0f, 0.0f, 0.0f); - ray.direction = normalize(float3(0.0f, 0.0f, 0.0f)); + ray.origin = cameraPosition; // All rays start at camera! + // Direction is the vector from origin to the worldpos + ray.direction = normalize(worldPos - ray.origin); return ray; } From 123ac4fbefaea9d86648fa96120e6869f8977103 Mon Sep 17 00:00:00 2001 From: John Marcao <jmarcao@users.noreply.github.com> Date: Thu, 24 Oct 2019 20:19:41 -0400 Subject: [PATCH 09/27] Switch to DXR --- src/D3D12RaytracingProceduralGeometry/DXR-Other.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-Other.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-Other.cpp index f889597..d551bcd 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-Other.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-Other.cpp @@ -23,7 +23,7 @@ DXProceduralProject::DXProceduralProject(UINT width, UINT height, std::wstring n m_forceComputeFallback(false) { m_forceComputeFallback = false; - SelectRaytracingAPI(RaytracingAPI::FallbackLayer); + SelectRaytracingAPI(RaytracingAPI::DirectXRaytracing); UpdateForSizeChange(width, height); } From caccf18d7e5f98dda1b6e862deac127f77a6ccc0 Mon Sep 17 00:00:00 2001 From: John Marcao <jmarcao@users.noreply.github.com> Date: Thu, 24 Oct 2019 20:20:47 -0400 Subject: [PATCH 10/27] Fix incorrectly initialized rootParameter InitAsConstant was incorrectly sized for the geometry type, so when Creating the root signature it was invalid --- src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp index 51a008b..9daa7fc 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp @@ -86,7 +86,7 @@ void DXProceduralProject::CreateRootSignatures() // So this is starting to make sense, these will be passed to each shader uniquely, while the above are global // to all shaders.... ahh? rootParameters[RootSignatureSlots::MaterialConstant].InitAsConstants(SizeOfInUint32(PrimitiveConstantBuffer), 1); - rootParameters[RootSignatureSlots::GeometryIndex].InitAsConstants(SizeOfInUint32(PrimitiveInstanceConstantBuffer), 1); + rootParameters[RootSignatureSlots::GeometryIndex].InitAsConstants(SizeOfInUint32(PrimitiveInstanceConstantBuffer), 2); CD3DX12_ROOT_SIGNATURE_DESC localRootSignatureDesc(ARRAYSIZE(rootParameters), rootParameters); localRootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_LOCAL_ROOT_SIGNATURE; From c1792e1878e7cf7c1024e64c40ba224ba77c46cd Mon Sep 17 00:00:00 2001 From: John Marcao <jmarcao@users.noreply.github.com> Date: Thu, 24 Oct 2019 20:33:49 -0400 Subject: [PATCH 11/27] Fixed missing field in TLAS TLAS was missing an instance descriptor. --- .../DXR-AccelerationStructure.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp index 3f7ec18..fbd8f53 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp @@ -354,6 +354,7 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt // Similar to the triangles above topLevelBuildDesc.DestAccelerationStructureData = topLevelAS->GetGPUVirtualAddress(); topLevelBuildDesc.ScratchAccelerationStructureData = scratch->GetGPUVirtualAddress(); + topLevelInputs.InstanceDescs = instanceDescsResource->GetGPUVirtualAddress(); // Build acceleration structure. if (m_raytracingAPI == RaytracingAPI::FallbackLayer) From 1bed95e256a8131362bbf8ccaa37c884689e44c7 Mon Sep 17 00:00:00 2001 From: John Marcao <jmarcao@users.noreply.github.com> Date: Thu, 24 Oct 2019 20:48:37 -0400 Subject: [PATCH 12/27] Add missing assignment of m_missShaderTableStrideInBytes --- src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp index e122733..5f98a0f 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp @@ -117,6 +117,7 @@ void DXProceduralProject::BuildShaderTables() // Save the uploaded resource (remember that the uploaded resource is created when we call Allocate() on a GpuUploadBuffer missShaderTable.DebugPrint(shaderIdToStringMap); m_missShaderTable = missShaderTable.GetResource(); + m_missShaderTableStrideInBytes = missShaderTable.GetShaderRecordSize(); } // Hit group shader table. This one is slightly different given that a hit group requires its own custom root signature. From bf1189385ff0baab865e721e046a2fbda1b1d73b Mon Sep 17 00:00:00 2001 From: John Marcao <jmarcao@users.noreply.github.com> Date: Thu, 24 Oct 2019 20:48:48 -0400 Subject: [PATCH 13/27] Add framecount to AABB primitves --- src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp index 21249a0..fa6a244 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp @@ -112,9 +112,10 @@ void DXProceduralProject::CreateConstantBuffers() void DXProceduralProject::CreateAABBPrimitiveAttributesBuffers() { auto device = m_deviceResources->GetD3DDevice(); + auto frameCount = m_deviceResources->GetBackBufferCount(); auto elements = IntersectionShaderType::TotalPrimitiveCount; - m_aabbPrimitiveAttributeBuffer.Create(device, elements, 1, L"AABB Primitive Attr. Buffer"); + m_aabbPrimitiveAttributeBuffer.Create(device, elements, frameCount, L"AABB Primitive Attr. Buffer"); } // LOOKAT-2.1: Update camera matrices stored in m_sceneCB. From 9ef2ada99a91ec36a1587aa9d45975e57a0181db Mon Sep 17 00:00:00 2001 From: John Marcao <jmarcao@users.noreply.github.com> Date: Thu, 24 Oct 2019 20:49:07 -0400 Subject: [PATCH 14/27] Set aabb primitves as correct view --- src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp index 1cf6f1e..f6299bc 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp @@ -23,7 +23,7 @@ void DXProceduralProject::DoRaytracing() // TODO-2.8: do a very similar operation for the m_aabbPrimitiveAttributeBuffer m_aabbPrimitiveAttributeBuffer.CopyStagingToGpu(frameIndex); - commandList->SetComputeRootConstantBufferView( + commandList->SetComputeRootShaderResourceView( GlobalRootSignature::Slot::AABBattributeBuffer, m_aabbPrimitiveAttributeBuffer.GpuVirtualAddress(frameIndex) ); From 714a4edb4f9e1a4d06a9154e9ccd6bb493931a47 Mon Sep 17 00:00:00 2001 From: John Marcao <jmarcao@users.noreply.github.com> Date: Thu, 24 Oct 2019 21:03:35 -0400 Subject: [PATCH 15/27] Fix ray generation perspective. Basic ray generation working! --- .../RaytracingShaderHelper.hlsli | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli b/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli index e9b5119..7c522ab 100644 --- a/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli +++ b/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli @@ -145,9 +145,7 @@ inline Ray GenerateCameraRay(uint2 index, in float3 cameraPosition, in float4x4 // Index is our assigned pixel, which is also our ray index. // We need to divide this by width/height to get a position beteen -1,-1 and 1,1 // So easy: get it to 0,0 and 1,1. Then -0.5 and double it? - float2 screenDim; - screenDim.x = DispatchRaysDimensions().x; - screenDim.y = DispatchRaysDimensions().y; + float2 screenDim = DispatchRaysDimensions().xy; float2 pos = index / screenDim; pos -= 0.5; @@ -156,13 +154,23 @@ inline Ray GenerateCameraRay(uint2 index, in float3 cameraPosition, in float4x4 // Now take that position and flip the y axis cause the DirectX screen geos from top to bottom pos.y = -pos.y; + // Create a position vector to multiply with the projection mat + float4 posvec = float4(pos, 0, 1); // Depth is 0, we dont care + // Transform to world position from camera position - float4 worldPos = mul(float4(pos, 1, 1), projectionToWorld); + float4 worldPos = mul(posvec, projectionToWorld); + + // "Each component of the vertex coordinate is divided by its + // w component giving smaller vertex coordinates the further + // away a vertex is from the viewer." - https://learnopengl.com/Getting-started/Coordinate-Systems + // I still don't get it, but it fixes my bugs. + worldPos.x /= worldPos.w; + worldPos.y /= worldPos.w; + worldPos.z /= worldPos.w; Ray ray; ray.origin = cameraPosition; // All rays start at camera! - // Direction is the vector from origin to the worldpos - ray.direction = normalize(worldPos - ray.origin); + ray.direction = normalize(worldPos.xyz - ray.origin); // Direction is the vector from origin to the worldpos return ray; } From d202eedfd935b6f4d48ecfa667f05bc87cbb4078 Mon Sep 17 00:00:00 2001 From: John Marcao <jmarcao@users.noreply.github.com> Date: Thu, 24 Oct 2019 21:19:10 -0400 Subject: [PATCH 16/27] 3.2 Done --- .../Raytracing.hlsl | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl b/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl index 8690b73..39c896f 100644 --- a/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl +++ b/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl @@ -135,7 +135,40 @@ float4 TraceRadianceRay(in Ray ray, in UINT currentRayRecursionDepth) // Hint 2: remember what the ShadowRay payload looks like. See RaytracingHlslCompat.h bool TraceShadowRayAndReportIfHit(in Ray ray, in UINT currentRayRecursionDepth) { - return false; + if (currentRayRecursionDepth >= MAX_RAY_RECURSION_DEPTH) + { + return false; + } + + // Set the ray's extents. + RayDesc rayDesc; + rayDesc.Origin = ray.origin; + rayDesc.Direction = ray.direction; + // Set TMin to a zero value to avoid aliasing artifacts along contact areas. + // Note: make sure to enable face culling so as to avoid surface face fighting. + rayDesc.TMin = 0; + rayDesc.TMax = 10000; + + // Updated to work with shadowrays + ShadowRayPayload rayPayload = { true }; + + // https://docs.microsoft.com/en-us/windows/win32/direct3d12/ray_flag + RAY_FLAG flags = + RAY_FLAG_CULL_BACK_FACING_TRIANGLES | + RAY_FLAG_FORCE_OPAQUE | + RAY_FLAG_SKIP_CLOSEST_HIT_SHADER | + RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH; + + TraceRay(g_scene, + flags, + TraceRayParameters::InstanceMask, + TraceRayParameters::HitGroup::Offset[RayType::Shadow], + TraceRayParameters::HitGroup::GeometryStride, + TraceRayParameters::MissShader::Offset[RayType::Shadow], + rayDesc, rayPayload); + + // Return if hit + return rayPayload.hit; } //*************************************************************************** From 09c44d7b4f21bcfa878e2dcaac150ffdb2654fbe Mon Sep 17 00:00:00 2001 From: John Marcao <jmarcao@users.noreply.github.com> Date: Fri, 25 Oct 2019 22:16:08 -0400 Subject: [PATCH 17/27] Miss shaders... not quite right??? --- src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl b/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl index 39c896f..2e8580e 100644 --- a/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl +++ b/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl @@ -287,14 +287,19 @@ void MyClosestHitShader_AABB(inout RayPayload rayPayload, in ProceduralPrimitive [shader("miss")] void MyMissShader(inout RayPayload rayPayload) { + // Hit nothing, so end the ray + rayPayload.recursionDepth = MAX_RAY_RECURSION_DEPTH; + // Set the color to the background. + rayPayload.color = BackgroundColor; } // TODO-3.3: Complete the Shadow ray miss shader. Is this ray a shadow ray if it hit nothing? [shader("miss")] void MyMissShader_ShadowRay(inout ShadowRayPayload rayPayload) { - + // We hit nothing. That means no hit. So... + rayPayload.hit = false; } //*************************************************************************** From 8da1fad0c6caa30b9d0c41b5780b5c274a1e89be Mon Sep 17 00:00:00 2001 From: John Marcao <jmarcao@users.noreply.github.com> Date: Sat, 26 Oct 2019 08:11:53 -0400 Subject: [PATCH 18/27] Multiple sphere intersection test --- .../AnalyticPrimitives.hlsli | 52 ++++++++++++++++--- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli b/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli index c6ccebb..720fb7e 100644 --- a/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli +++ b/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli @@ -163,21 +163,59 @@ bool RaySolidSphereIntersectionTest(in Ray ray, out float thit, out float tmax, // TODO-3.4.1: Change this code to support intersecting multiple spheres (~3 spheres). // You can hardcode the local centers/radii of the spheres, just try to maintain them between 1 and -1 (and > 0 for the radii). +// This is kinda lame cause it hardcodes the spheres in the intersection test. +// I guess the AABB is better for passing in stuff bool RayMultipleSpheresIntersectionTest(in Ray ray, out float thit, out ProceduralPrimitiveAttributes attr) { + bool hit = false; + + // Store values in here until ready to leave + float tmp_thit; + ProceduralPrimitiveAttributes tmp_attr; + // Define the spheres in local space (within the aabb) - float3 center = float3(-0.2, 0, -0.2); - float radius = 0.7f; + float3 center[3]; + float radius[3]; + + // Sphere 1 + center[0] = (float3(-0.2, 0, -0.2)); + radius[0] = (0.2f); + + // Sphere 2 + center[1] = (float3(-0.3, 0.5, -0.3)); + radius[1] = (0.2f); - thit = RayTCurrent(); + // Sphere 3 + center[2] = (float3(0.5, 0, 0.5)); + radius[2] = (0.2f); + + tmp_thit = RayTCurrent(); float tmax; - if (RaySphereIntersectionTest(ray, thit, tmax, attr, center, radius)) - { - return true; + float min_diff = 100; // Arbitratily large. Will never be greater than 2 due to bounding box + + // thit is modified by function + for (int i = 0; i < 3; i++) { + if (RaySphereIntersectionTest(ray, tmp_thit, tmax, tmp_attr, center[i], radius[i])) + { + // We intersected a sphere. Check now if this intersection is closest. + // A ray is just a line, so in this local space we need to see which is closest + // to the entry point on that line. + // RayTCurrent() - Ending point of ray + // RayTMin() - Starting point of ray + // So whichever is closest to RayTMin() + float diff = abs(tmp_thit - RayTMin()); // Could we be coming in from above? Below? + + // This is closer to the entry point, pick it + if (diff < min_diff) { + thit = tmp_thit; + attr = tmp_attr; + hit = true; + } + } } - return false; + return hit; } #endif // ANALYTICPRIMITIVES_H \ No newline at end of file From 1f5313a07b11774d1583f36ead004c3090d9fa1c Mon Sep 17 00:00:00 2001 From: John Marcao <jmarcao@users.noreply.github.com> Date: Sat, 26 Oct 2019 08:26:27 -0400 Subject: [PATCH 19/27] Resolve some compiler warnings --- .../DXR-AccelerationStructure.cpp | 6 ++---- src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp | 1 - src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp index fbd8f53..7b48ea5 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp @@ -38,7 +38,7 @@ void DXProceduralProject::BuildGeometryDescsForBottomLevelAS(array GetGPUVirtualAddress(); // Get GPU addr of this resource - geometryDesc.Triangles.IndexCount = m_indexBuffer.resource->GetDesc().Width / sizeof(Index); + geometryDesc.Triangles.IndexCount = (UINT)(m_indexBuffer.resource->GetDesc().Width) / sizeof(Index); geometryDesc.Triangles.IndexFormat = DXGI_FORMAT_R16_UINT; // Index is UINT16 // Now assign Vertex values. Just like above. @@ -46,7 +46,7 @@ void DXProceduralProject::BuildGeometryDescsForBottomLevelAS(array GetGPUVirtualAddress(); geometryDesc.Triangles.VertexBuffer.StrideInBytes = sizeof(Vertex); - geometryDesc.Triangles.VertexCount = m_vertexBuffer.resource->GetDesc().Width / sizeof(Vertex); + geometryDesc.Triangles.VertexCount = (UINT)(m_vertexBuffer.resource->GetDesc().Width) / sizeof(Vertex); geometryDesc.Triangles.VertexFormat = DXGI_FORMAT_R32G32B32_FLOAT; // Each vertex is a xyz of 32bit ints // But each vertex includes vertex + normals // But stride accounts for that. @@ -219,8 +219,6 @@ void DXProceduralProject::BuildBottomLevelASInstanceDescs(BLASPtrType *bottomLev // Where do you think procedural shader records would start then? Hint: right after. // * Make each instance hover above the ground by ~ half its width { - D3D12_RAYTRACING_INSTANCE_DESC a; // Just for reference... - // Start off just like triangles above auto& instanceDesc = instanceDescs[BottomLevelASType::AABB]; instanceDesc = {}; diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp index f6299bc..57bdbec 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp @@ -71,7 +71,6 @@ void DXProceduralProject::DoRaytracing() { // You will fill in a D3D12_DISPATCH_RAYS_DESC (which is dispatchDesc). // TODO-2.8: fill in dispatchDesc->HitGroupTable. Look up the struct D3D12_GPU_VIRTUAL_ADDRESS_RANGE_AND_STRIDE - D3D12_GPU_VIRTUAL_ADDRESS_RANGE_AND_STRIDE hgt; dispatchDesc->HitGroupTable.StartAddress = m_hitGroupShaderTable->GetGPUVirtualAddress(); // Want to start here, makes sense. dispatchDesc->HitGroupTable.SizeInBytes = m_hitGroupShaderTable->GetDesc().Width; // Similar to before dispatchDesc->HitGroupTable.StrideInBytes = m_hitGroupShaderTableStrideInBytes; // Makes sense... diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp index 5f98a0f..7db7b24 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp @@ -110,7 +110,7 @@ void DXProceduralProject::BuildShaderTables() ShaderTable missShaderTable(device, numShaderRecords, shaderRecordSize, L"MissShaderTable"); // Push back a shader record for each ray type - for (int i = 0; i < numShaderRecords; i++) { + for (UINT i = 0; i < numShaderRecords; i++) { missShaderTable.push_back(ShaderRecord(missShaderIDs[i], shaderRecordSize, nullptr, 0)); } From cb2c3d63ffe2a7ccd08a115d765c670c8b3b0956 Mon Sep 17 00:00:00 2001 From: John Marcao <jmarcao@users.noreply.github.com> Date: Sat, 26 Oct 2019 09:16:01 -0400 Subject: [PATCH 20/27] Metaballs. Not sure if working, hard to see --- .../Raytracing.hlsl | 18 +++++ .../VolumetricPrimitives.hlsli | 80 ++++++++++++++++++- 2 files changed, 95 insertions(+), 3 deletions(-) diff --git a/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl b/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl index 2e8580e..b337248 100644 --- a/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl +++ b/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl @@ -351,6 +351,24 @@ void MyIntersectionShader_AnalyticPrimitive() [shader("intersection")] void MyIntersectionShader_VolumetricPrimitive() { + Ray localRay = GetRayInAABBPrimitiveLocalSpace(); + VolumetricPrimitive::Enum primitiveType = (VolumetricPrimitive::Enum) l_aabbCB.primitiveType; + // The point of the intersection shader is to: + // (1) find out what is the t at which the ray hits the procedural + // (2) pass on some attributes used by the closest hit shader to do some shading (e.g: normal vector) + float thit; + ProceduralPrimitiveAttributes attr; + if (RayVolumetricGeometryIntersectionTest(localRay, primitiveType, thit, attr, g_sceneCB.elapsedTime)) + { + PrimitiveInstancePerFrameBuffer aabbAttribute = g_AABBPrimitiveAttributes[l_aabbCB.instanceIndex]; + + // Make sure the normals are stored in BLAS space and not the local space + attr.normal = mul(attr.normal, (float3x3) aabbAttribute.localSpaceToBottomLevelAS); + attr.normal = normalize(mul((float3x3) ObjectToWorld3x4(), attr.normal)); + + // thit is invariant to the space transformation + ReportHit(thit, /*hitKind*/ 0, attr); + } } #endif // RAYTRACING_HLSL \ No newline at end of file diff --git a/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli b/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli index 31a9444..305f6d4 100644 --- a/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli +++ b/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli @@ -19,10 +19,22 @@ struct Metaball // 2) If it is at the radius or beyond, the potential is 0. // 3) In between (i.e the distance from the center is between 0 and radius), consider using the a // quintic polynomial field function of the form 6x^5 - 15x^4 + 10x^3, such that x is the ratio -// of the distance from the center to the radius. +// of the distance from the center to the radius. float CalculateMetaballPotential(in float3 position, in Metaball blob) { - return 0.0f; + float pot = 0.0f; + + // How far from the center are we + float dist = distance(position, blob.center); + + // Our function will be 0 for input 0 and 1 for input 1 + // So clamp the distance ratio to avoid branches + dist = dist / blob.radius; + dist = clamp(dist, 0, 1); + + pot = (6 * pow(dist, 5)) - (15 * pow(dist, 4)) + (10 * pow(dist, 3)); + + return pot; } // LOOKAT-1.9.4: Calculates field potential from all active metaballs. This is just the sum of all potentials. @@ -79,10 +91,26 @@ void InitializeAnimatedMetaballs(out Metaball blobs[N_METABALLS], in float elaps // TODO-3.4.2: Find the entry and exit points for all metaball bounding spheres combined. // Remember that a metaball is just a solid sphere. Didn't we already do this somewhere else? +// Hmmmm... just a sphere test? void TestMetaballsIntersection(in Ray ray, out float tmin, out float tmax, inout Metaball blobs[N_METABALLS]) { tmin = INFINITY; tmax = -INFINITY; + + float tmp_thit; + float tmp_tmax; + + // Do something similar to the multiple spheres test + // Difference here is that we return the entry and exit through + // tmin and tmax + for (int i = 0; i < N_METABALLS; i++) { + Metaball m = blobs[i]; + if (RaySolidSphereIntersectionTest(ray, tmp_thit, tmp_tmax, m.center, m.radius)) { + // Intersected, set values + tmin = min(tmp_thit, tmin); + tmax = max(tmp_tmax, tmax); + } + } } // TODO-3.4.2: Test if a ray with RayFlags and segment intersects metaball field. @@ -100,9 +128,55 @@ void TestMetaballsIntersection(in Ray ray, out float tmin, out float tmax, inout // If this condition fails, keep raymarching! bool RayMetaballsIntersectionTest(in Ray ray, out float thit, out ProceduralPrimitiveAttributes attr, in float elapsedTime) { + bool hit = false; + float tmin; + float tmax; + Metaball blobs[N_METABALLS]; + thit = 0.0f; attr.normal = float3(0.0f, 0.0f, 0.0f); - return false; + + // 1) Initialize a metaball array. See InitializeAnimatedMetaballs() + InitializeAnimatedMetaballs(blobs, elapsedTime, 10); // Can't find a defined cycle duration + // TODO-JOHN: Find one. + + // 2) Test intersections on the metaballs to find the minimum t and the maximum t to raymarch between. + TestMetaballsIntersection(ray, tmin, tmax, blobs); + + // 3) Use some number of steps (~128 is a good number for raymarching) to do the following: + // a) Compute the total metaball potential over this point by summing ALL potentials of each metaball. + // See CalculateMetaballsPotential(). + // b) If the total potential crosses an isosurface threshold (defined on (0,1]), then we will potentially + // render this point: + // i) We compute the normal at this point (see CalculateMetaballsNormal()) + // ii) Only render this point if it is valid hit. See is_a_valid_hit(). + // If this condition fails, keep raymarching! + const float THRESHOLD = 0.5; + const UINT STEPS = 128; + float marchDist = tmax - tmin; + float pot = 0.0f; + float3 normal; + + // Step through... + for (UINT i = 0; i < STEPS; i++) { + float pos = tmin + ((i / STEPS)*marchDist); + + // And calculate the potential. + pot = CalculateMetaballsPotential(pos, blobs); + + // If the potential is greater than some threshold + is valid, render it + if (pot > THRESHOLD) { + normal = CalculateMetaballsNormal(pos, blobs); + if (is_a_valid_hit(ray, pos, normal)) { + thit = pos; + attr.normal = normal; + hit = true; + break; + } + } + } + + return hit; } #endif // VOLUMETRICPRIMITIVESLIBRARY_H \ No newline at end of file From bac93dc1be5372b0b07ac9bb7e37d0ebe9fce3fe Mon Sep 17 00:00:00 2001 From: John Marcao <jmarcao@users.noreply.github.com> Date: Sat, 26 Oct 2019 09:57:46 -0400 Subject: [PATCH 21/27] Phong... still not seeing things work right. Might be digging my own grave --- .../Raytracing.hlsl | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl b/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl index b337248..0aa4686 100644 --- a/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl +++ b/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl @@ -41,7 +41,16 @@ ConstantBuffer l_aabbCB: register(b2); // other // Remember to clamp the dot product term! float CalculateDiffuseCoefficient(in float3 incidentLightRay, in float3 normal) { - return 0.0f; + // From wiki + /* + The reflection is calculated by taking the dot product of the surface's normal + vector, {\displaystyle \mathbf {N} }\mathbf {N} , and a normalized light-direction + vector, {\displaystyle \mathbf {L} }\mathbf {L} , pointing from the surface to + the light source. + */ + + // So we want to return the clamped dot of reverse light and normal + return clamp(dot(normalize(-incidentLightRay), normal), 0, 1); } // TODO-3.6: Phong lighting specular component. @@ -51,7 +60,10 @@ float CalculateDiffuseCoefficient(in float3 incidentLightRay, in float3 normal) // Remember to normalize the reflected ray, and to clamp the dot product term float4 CalculateSpecularCoefficient(in float3 incidentLightRay, in float3 normal, in float specularPower) { - return float4(0.0f, 0.0f, 0.0f, 0.0f); + // Use DXR builtin + float3 reflected = normalize(reflect(incidentLightRay, normal)); + float4 coefficient = pow(clamp(dot(reflected, normalize(-incidentLightRay)), 0, 1), specularPower); + return coefficient; } // TODO-3.6: Phong lighting model = ambient + diffuse + specular components. @@ -68,6 +80,9 @@ float4 CalculateSpecularCoefficient(in float3 incidentLightRay, in float3 normal float4 CalculatePhongLighting(in float4 albedo, in float3 normal, in bool isInShadow, in float diffuseCoef = 1.0, in float specularCoef = 1.0, in float specularPower = 50) { + // Calculate the light ray by taking the ray from the light source to our hit position + float3 lightRay = normalize(HitWorldPosition() - g_sceneCB.lightPosition.xyz); + // Ambient component // Fake AO: Darken faces with normal facing downwards/away from the sky a little bit float4 ambientColor = g_sceneCB.lightAmbientColor; @@ -76,7 +91,15 @@ float4 CalculatePhongLighting(in float4 albedo, in float3 normal, in bool isInSh float a = 1 - saturate(dot(normal, float3(0, -1, 0))); ambientColor = albedo * lerp(ambientColorMin, ambientColorMax, a); - return ambientColor; + // Diffuse part + float diffuse = CalculateDiffuseCoefficient(lightRay, normal); + float4 diffuseColor = diffuseCoef * diffuse * g_sceneCB.lightDiffuseColor * albedo; + + // Specular part + float4 specular = CalculateSpecularCoefficient(lightRay, normal, specularPower); + float4 specularColor = specularCoef * specular * ~isInShadow; + + return ambientColor + diffuseColor + specularColor; } //*************************************************************************** From 08cdaeb63bfe380266923c4c6dc901323dae37e3 Mon Sep 17 00:00:00 2001 From: John Marcao <jmarcao@users.noreply.github.com> Date: Sat, 26 Oct 2019 11:05:51 -0400 Subject: [PATCH 22/27] ClosestHit + Fresenl. All TODOs done. Now to find out why it looks terrible --- .../Raytracing.hlsl | 50 +++++++++++++++++++ .../RaytracingShaderHelper.hlsli | 3 +- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl b/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl index 0aa4686..c4a8d3d 100644 --- a/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl +++ b/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl @@ -279,6 +279,13 @@ void MyClosestHitShader_Triangle(inout RayPayload rayPayload, in BuiltInTriangle // Hint 1: look at the intrinsic function RayTCurrent() that returns how "far away" your ray is. // Hint 2: use the built-in function lerp() to linearly interpolate between the computed color and the Background color. // When t is big, we want the background color to be more pronounced. + + // Interpolate between final color and background color + // lerp(x, y, s) --> x*(1-s) + y*s + // Want far to be closer to 1, near closer to 0 + // So a power of sorts? + float far = RayTCurrent(); + color = lerp(color, BackgroundColor, 1.0f - exp(-0.01 * far)); rayPayload.color = color; } @@ -297,7 +304,50 @@ void MyClosestHitShader_Triangle(inout RayPayload rayPayload, in BuiltInTriangle [shader("closesthit")] void MyClosestHitShader_AABB(inout RayPayload rayPayload, in ProceduralPrimitiveAttributes attr) { + float4 tmpColor; + + // Relevant positions + float3 pos = HitWorldPosition(); + float3 lightpos = g_sceneCB.lightPosition.xyz; + + // (1) Trace a shadow ray to determine if this ray is a shadow ray. + // TraceShadowRayAndReportIfHit() + Ray shadowray; + shadowray.origin = pos; + shadowray.direction = normalize(lightpos - pos); + bool shadowHit = TraceShadowRayAndReportIfHit(shadowray, rayPayload.recursionDepth); + + // (2) Trace a reflectance ray --> compute the reflected color. + Ray reflRay; + reflRay.origin = pos; + reflRay.direction = normalize(reflect(pos, attr.normal)); + float4 reflColor = TraceRadianceRay(reflRay, rayPayload.recursionDepth); + float3 fresnelR = FresnelReflectanceSchlick(WorldRayDirection(), attr.normal, l_materialCB.albedo.xyz); + reflColor = l_materialCB.reflectanceCoef * float4(fresnelR, 1) * reflColor; + + // (3) Use the fact that your ray is a shadow ray or not to compute the Phong lighting. + float4 phongLighting = CalculatePhongLighting( + l_materialCB.albedo, + attr.normal, + shadowHit, + l_materialCB.diffuseCoef, // Bunch of stuff from constant buffers + l_materialCB.specularCoef, + l_materialCB.specularPower + ); + + // (4) Combine the reflect color and the phong color into one color. + tmpColor = phongLighting + reflColor; + + // (5) Apply visibility falloff to select some interpolation between the computed color or the background color + // Interpolate between final color and background color + // lerp(x, y, s) --> x*(1-s) + y*s + // Want far to be closer to 1, near closer to 0 + // So a power of sorts? + float far = RayTCurrent(); + tmpColor = lerp(tmpColor, BackgroundColor, 1.0f - exp(-0.01 * far)); + // (6) Fill the payload color with whatever final color you computed + rayPayload.color = tmpColor; } //*************************************************************************** diff --git a/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli b/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli index 7c522ab..78dc835 100644 --- a/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli +++ b/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli @@ -180,7 +180,8 @@ inline Ray GenerateCameraRay(uint2 index, in float3 cameraPosition, in float4x4 // f0 is usually the albedo of the material assuming the outside environment is air. float3 FresnelReflectanceSchlick(in float3 I, in float3 N, in float3 f0) { - return f0; + // Did this in Project 3, so adapt it for DXR + return f0 + (1 - f0)*pow((1 - abs(dot(I, N))), 5.0f);; } #endif // RAYTRACINGSHADERHELPER_H \ No newline at end of file From 457d08190db8873eacd44b29e28a0ef7109cf80e Mon Sep 17 00:00:00 2001 From: John Marcao <jmarcao@users.noreply.github.com> Date: Sat, 26 Oct 2019 11:22:48 -0400 Subject: [PATCH 23/27] Fixed reflectance bug --- src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl b/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl index c4a8d3d..22445e7 100644 --- a/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl +++ b/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl @@ -308,6 +308,7 @@ void MyClosestHitShader_AABB(inout RayPayload rayPayload, in ProceduralPrimitive // Relevant positions float3 pos = HitWorldPosition(); + float3 direction = WorldRayDirection(); float3 lightpos = g_sceneCB.lightPosition.xyz; // (1) Trace a shadow ray to determine if this ray is a shadow ray. @@ -318,13 +319,19 @@ void MyClosestHitShader_AABB(inout RayPayload rayPayload, in ProceduralPrimitive bool shadowHit = TraceShadowRayAndReportIfHit(shadowray, rayPayload.recursionDepth); // (2) Trace a reflectance ray --> compute the reflected color. + float4 reflColor; Ray reflRay; reflRay.origin = pos; - reflRay.direction = normalize(reflect(pos, attr.normal)); - float4 reflColor = TraceRadianceRay(reflRay, rayPayload.recursionDepth); + reflRay.direction = normalize(reflect(direction, attr.normal)); + reflColor = TraceRadianceRay(reflRay, rayPayload.recursionDepth); + + // Apply fresnel float3 fresnelR = FresnelReflectanceSchlick(WorldRayDirection(), attr.normal, l_materialCB.albedo.xyz); + + // Update Color reflColor = l_materialCB.reflectanceCoef * float4(fresnelR, 1) * reflColor; + // (3) Use the fact that your ray is a shadow ray or not to compute the Phong lighting. float4 phongLighting = CalculatePhongLighting( l_materialCB.albedo, From 93148a1ff550894471b308e0f87bf84702e339ae Mon Sep 17 00:00:00 2001 From: John Marcao <jmarcao@users.noreply.github.com> Date: Sat, 26 Oct 2019 12:32:27 -0400 Subject: [PATCH 24/27] Fixed metaballs --- .../VolumetricPrimitives.hlsli | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli b/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli index 305f6d4..392d871 100644 --- a/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli +++ b/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli @@ -26,14 +26,16 @@ float CalculateMetaballPotential(in float3 position, in Metaball blob) // How far from the center are we float dist = distance(position, blob.center); - - // Our function will be 0 for input 0 and 1 for input 1 - // So clamp the distance ratio to avoid branches - dist = dist / blob.radius; - dist = clamp(dist, 0, 1); - - pot = (6 * pow(dist, 5)) - (15 * pow(dist, 4)) + (10 * pow(dist, 3)); - + + if (dist >= blob.radius) { + pot = 0.0f; + } + else { + // the ratio of the distance from the center to the radius. + float x = (blob.radius - dist) / blob.radius; + pot = (6 * pow(x, 5)) - (15 * pow(x, 4)) + (10 * pow(x, 3)); + } + return pot; } @@ -137,8 +139,7 @@ bool RayMetaballsIntersectionTest(in Ray ray, out float thit, out ProceduralPrim attr.normal = float3(0.0f, 0.0f, 0.0f); // 1) Initialize a metaball array. See InitializeAnimatedMetaballs() - InitializeAnimatedMetaballs(blobs, elapsedTime, 10); // Can't find a defined cycle duration - // TODO-JOHN: Find one. + InitializeAnimatedMetaballs(blobs, elapsedTime, 10); // duration can be whatever according to piaz // 2) Test intersections on the metaballs to find the minimum t and the maximum t to raymarch between. TestMetaballsIntersection(ray, tmin, tmax, blobs); @@ -154,12 +155,15 @@ bool RayMetaballsIntersectionTest(in Ray ray, out float thit, out ProceduralPrim const float THRESHOLD = 0.5; const UINT STEPS = 128; float marchDist = tmax - tmin; + float delta = marchDist / STEPS; float pot = 0.0f; float3 normal; + float3 posStart = ray.origin; + float posAlongRay = tmin; // Step through... - for (UINT i = 0; i < STEPS; i++) { - float pos = tmin + ((i / STEPS)*marchDist); + for (UINT i = 0; i < STEPS; i++, posAlongRay += delta) { + float3 pos = posStart + posAlongRay * ray.direction; // And calculate the potential. pot = CalculateMetaballsPotential(pos, blobs); @@ -167,8 +171,8 @@ bool RayMetaballsIntersectionTest(in Ray ray, out float thit, out ProceduralPrim // If the potential is greater than some threshold + is valid, render it if (pot > THRESHOLD) { normal = CalculateMetaballsNormal(pos, blobs); - if (is_a_valid_hit(ray, pos, normal)) { - thit = pos; + if (is_a_valid_hit(ray, posAlongRay, normal)) { + thit = posAlongRay; attr.normal = normal; hit = true; break; From 9165a344e4bc711cd4381404a9b326c5d1c5f646 Mon Sep 17 00:00:00 2001 From: John Marcao <jmarcao@users.noreply.github.com> Date: Sun, 27 Oct 2019 12:59:08 -0400 Subject: [PATCH 25/27] Fix bug where vertex buffers were not being copied down --- src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp index 57bdbec..74f01c7 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp @@ -65,6 +65,12 @@ void DXProceduralProject::DoRaytracing() GlobalRootSignature::Slot::OutputView, m_raytracingOutputResourceUAVGpuDescriptor); + // Copy down the vertex buffers. Duh. I forgot this line and + // spent hours trying to find out what was wrong + commandList->SetComputeRootDescriptorTable( + GlobalRootSignature::Slot::VertexBuffers, + m_indexBuffer.gpuDescriptorHandle); + // This will define a `DispatchRays` function that takes in a command list, a pipeline state, and a descriptor // This will set the hooks using the shader tables built before and call DispatchRays on the command list auto DispatchRays = [&](auto* raytracingCommandList, auto* stateObject, auto* dispatchDesc) From fd4ac779302a22a6552b2cdbe54aa19ebdc52fb6 Mon Sep 17 00:00:00 2001 From: John Marcao <jmarcao@users.noreply.github.com> Date: Sun, 27 Oct 2019 13:49:34 -0400 Subject: [PATCH 26/27] Fixed missing shadows --- src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl b/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl index 22445e7..a9c9a10 100644 --- a/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl +++ b/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl @@ -94,10 +94,16 @@ float4 CalculatePhongLighting(in float4 albedo, in float3 normal, in bool isInSh // Diffuse part float diffuse = CalculateDiffuseCoefficient(lightRay, normal); float4 diffuseColor = diffuseCoef * diffuse * g_sceneCB.lightDiffuseColor * albedo; + if (isInShadow) { + diffuseColor *= InShadowRadiance; + } // Specular part float4 specular = CalculateSpecularCoefficient(lightRay, normal, specularPower); - float4 specularColor = specularCoef * specular * ~isInShadow; + float4 specularColor = specularCoef * specular; + if (isInShadow) { + //specularColor *= 0; // Black out if in shadow + } return ambientColor + diffuseColor + specularColor; } From 5c31832f7e2fc16b1680b71bd479998373d091c9 Mon Sep 17 00:00:00 2001 From: John Marcao <jmarcao@users.noreply.github.com> Date: Sun, 27 Oct 2019 15:36:12 -0400 Subject: [PATCH 27/27] readme --- README.md | 49 ++++++++++++++++++++++++++++++++++++++++ data.xlsx | Bin 0 -> 23756 bytes images/mb_128.gif | Bin 0 -> 1970074 bytes images/mb_2.gif | Bin 0 -> 1242540 bytes images/mb_32.gif | Bin 0 -> 2087129 bytes images/mb_512.gif | Bin 0 -> 1627324 bytes images/mb_8.gif | Bin 0 -> 1903723 bytes images/metablls.png | Bin 0 -> 10535 bytes images/raydepth_fps.png | Bin 0 -> 6202 bytes images/scene.gif | Bin 0 -> 2875502 bytes 10 files changed, 49 insertions(+) create mode 100644 data.xlsx create mode 100644 images/mb_128.gif create mode 100644 images/mb_2.gif create mode 100644 images/mb_32.gif create mode 100644 images/mb_512.gif create mode 100644 images/mb_8.gif create mode 100644 images/metablls.png create mode 100644 images/raydepth_fps.png create mode 100644 images/scene.gif diff --git a/README.md b/README.md index ebc868a..e45e695 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,55 @@ DXR Raytracing * [Personal Website](https://jmarcao.github.io) * Tested on: Windows 10, i5-4690K @ 3.50GHz, 8GB DDR3, RTX 2080 TI 3071MB (Personal) +(Artifacts in the reflection are due to GIF encoding, will try to fix that) + + + +# Project +This project went through the steps of setting up DXR Raytracing pipeline with triangle support, as well as simple geometries (spheres), Axis-Aligned Bounding Box (AABB), as well as volumetric Metaballs that become distorted with time. + +The project was focused on simply understanding the API and applying the raytracing knowledge from Project 3 as well as additional geometries. I also perform some performance analysis on several parameters, including ray depth, metaball raymarching granularity, and number of elements in a metaball group. + +# DXR API + +The DXR API provides a lot of power, but also a lot of confusion. The API can handle triangles easily, but adding support for non-triangle geometries turned out to be complicated in my opinion. Below are some of the highlights from learning the API that can hopefully help somebody else out. DXR is all about structure. To automate a lot of steps, DXR just needs to be told about the layout of the data. From there, it can figure things out (such as triangle/ray intersections). + +### Global and Local Root Signatures +In CUDA, arguments are defined through normal C-style function arguments. With the DXR API, arguments can be passed in through root signatures. Each GPU thread/shader will receive the same copy of the GlobalRootSignature. This makes it great for globally true values, such as lighting locations, acceleration structure data, etc. LocalRootSignatures, on the other hand, are unique for each shader. Each is structured the same, but the contents of the data can change. This allows shaders to get data on the specific geometry they are intersecting with. + +# Performance + +I collected performance metrics on the DXR project by varying ray depth and metaball raymarching and complexity. + +First, I increased the maximum ray depth parameter. The collected data is below. + + + +| Ray Depth | FPS | +|-----------|--------| +| 3 | 550.92 | +| 4 | 510.75 | +| 5 | 482.51 | +| 6 | 471.74 | +| 7 | 486.36 | +| 8 | 467.83 | +| 9 | 477.81 | +| 10 | 471.27 | + +As ray depth increases, the FPS goes down. This is expected, since a deeper depth adds more iterations of each ray. However, it also becomes clear that after a ray depth of 5, the difference is minimal. This is because most of the rays will, by that point, be executing on no-hit shaders. Those shaders perform minimal work and set the depth of ray to the maximum value, essentially removing them from the equation. + +Additionally, I analyzed the performance of the Metaball Raymarching algorithm. The algorithm looks at each point from the entry of the ray in the bounding box and the exit of the ray. It then calculates a "potential" based on the distance between the point and each other sphere. By modifying the steps taken, we can see how drastic the performance hit is of rendering these metaballs. I also include what each step variant looks like. As can be seen, The performance loss is pretty huge, which is expected since each ray needs to test against each sphere in the bounding box. However, the metaballs do not render "correctly" if not enough steps are taken. + + + +| MB Steps | FPS | GIF | +|----------|--------| ----| +| 2 | 1726 |  | +| 8 | 1660 |  | +| 32 | 1267 |  | +| 128 | 550.92 |  | +| 512 | 135 |  | + # Conceptual Questions ## Question 1 diff --git a/data.xlsx b/data.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..b9a30066a5cad51e03dc8384768a1bcfcded4291 GIT binary patch literal 23756 zcmeIaWmsOjwl++W7A; 77Fz1uZjNBQyN0KpTG9-jSK#_o60)Ybo0pS3RJ=9> {;2hK5Ajh=AcI7*AS>HC}4h6KdG^ipKzJZy{?DzE($&IVc%wjpqh4wURB3KhV_ zoU#|Hb(^vFwgJ|%MyjGsfMQXD8RQ`&z^yme;>y}F; z?uhpMG4`66iKlvrTx#tpg{QD>?>m!<``5`1bYJS&hIb+(!Cxw#?a@(JC=qf~DFyL} zT}4Wl5T9`x5SRD2sJ*)7`GAT=uxok&nPzI|karS$6jyleE)B9nluc^{HqjWc # zj;!9)lwqe24PopOOxNHC;w#q9JtMZ*I01rf!rpIhwwmC+Sd%Z(cQ;CUzSBGz4Q6l$ z*!9B$2#~~oSp3=*f%Hp2&OE@Np#T=IZl!BtO-b?i|G$?0|FC)gt?7lM3r20&NUsiW zS{AtmUA1RJsre!oL%0q)yD^t##1RBf17_)6Z+bMaJd2?xh0iW8a=Xs-$}=4@;b7_^ zV|XFFlC(@! Cnh+%c8SXy&_8yxRADEN|WPINAD;lvTN=x6$ARMm8fKBFV`K#^WU3zpgyQ z>EoukqR40&mdxj@a2gyt%FO6C`kc#E(B~!9T*Kn8$I5l3MEbh=ZlL0}-hXbA*h#0V zW^FVOv)i$Q!SLO>XIrT@=3wK%9sxNWfmOokm0kKpDlXGa *hs&4oIa$FxQdMO~W1 C9&b<_t)@^ir=rShMVP<6E%+F?=yL|Lr9D;7}JARy#r3{`G zfwST^qDM8+DCKQ#d(NpMxE-bTAv6Az+^tv~I;JAYLulbGUd#hHUk_!b LicsIMm zn^3yQ4LTo-N;tb;IpjevsWYZ1LbL`L56Je>srSlxzrJw0%irMJ)g|EiK1uMP4;S=N z)3fQDyn!NSdE&j-R_{9*jC9?v_v79-I~=+b#SGjflu@^do3m{sCIe?E(0)lM-dglc zIu}Z1=-W #NVH_@3@~qqTpj>6c>^h^4lxm;vsh!(+r#{0x_HY025-N!tr+-Sbj)uR|jZ zZ_SA#?)saZ|68F}E~GFz% ;zjvuzDa*YB%r$8VFAJ77NnZ0=K%kebT@)g ztQ=n~wm?Tmha;U%t(4Prx5$nxC~RXi+afa24VtE0#$Zrl6AUBLVLGOQ*&ml`;j0B# zU tD21c4EAh8+usoOUDv{c6 zef%lIG!?0{OE@QM(sqQ^2n%0viN1cZl&gbip7QyvgbgitrZ5U9QQ(0V<$#_VCr*^w zxxnOC^`@hUgt=9-!BZeG6C- 4LuFlz zad!PFt^tcP&Hrkap6pSvCxDzfz-;yo2>b=WEdQF&{%)K fWEqKG6;te#V9pcUN`?7QCmDWR4Jr*6nl&?R}l9!lW=Ih`FER!9bVY1^w$4 z5X7?<(!3x9P$xu_0~Rpzfs+9caIL)}OfP;gM1 h4&o%xZze$)N# _f0QJ^V~66-|2zgT2I%+5}>F5Ig(e~7^cSqM)C+KARtu06VD^LwSlg# zjWy-dh5B(wPm-Jaw89AYIHcqBvIy?Z!&A-f)6Ke}8dgo|N1~Q82Ub*jniXv<)tG!7 zou|`;`<$i1)Fqp{d%WA{EY0Hl9)ERjo>y&|xsu$mV|HNvZf9s6-x}kKfO2?3jm^HB z#Fm@l?d{m^w~oYlseE)!gZ24^_Y M_c*a9c;8s;SC1GH@U N)3RtN>8rxL zv5(MNm8Nh-I#8jT)G8w0G|bai%D&+lP9TnJo-Y?m8BVZ*R{7XISDYBtumz;mVU)uT zowq}`erh{r#U)4Ie)kDiiunMz3Dxa;VCQs6+&ei)iYp~YkKH*Yb^p|xTJc`YE#`Yk zD&k|X1zPfDAYXSAA>#eAL!dYJ*b1ZGTQ-4PpJc5sI8=}v-|&k^qM#E*5`zZ`#-M!9 z3WzLp_*7wWC44-f+W-C~ xi4iI*>!b$858kTL)D>$5=l_{`x4EO3(oHs=&1+dF2d4d%@Da6#^nVQ z2e*f$x`{==E2y{=*)&D$kne@_B-73<3MGZrKtOL JVwzY!k?9s!4nq+EP$Lrx{%D9y3Cv6*kR@mAprx0mWkf zNFq%CYfNg_p(#ax4XeC~9)?<}f-(9lwNJ<2Bj*WyKtqW{pfzo{42q~B8|a!uBiOkk z-bK9V*%(yHgmymqJV_gGV~ i( zxWa^^bdMT}#a*urgHI9TeSQc`P%m4o+&o^rO}UNeO2|ZV*eCYJR1BwFE?VVkGpbxc z-&4Y1T{~q>OCN4Mpt#cqUA$9j%w%{6`oO*te7+A)Iki5soV*_PYMp=Bvu1GILl@DO zE)!|#3nl9hcE1it7e#d-Eghi =*=G91cn{0+)zozT!xl_u=1#=!ppg)!dD~J8AQ7?bUNbrS95Mbfol>2 zEXq2uAKzd+nX`?7uBk5N)Bh(+-c=k3Mq@x~M!DvIwzj{eUkxKzTplusT)@{!jzX`h z-j$T2rHN=jLj)#ZI+G&K%8unWXNwx;1c7Nh4THe1+`<|m8koi_TE|Z`5mK^)3Fg0V z%+0+>adSGcIT~$@bK;MKYK@3dZ<07>OA48e!yk)rH)bTJa}gJa3W3*!FuQ^xyA_^G zb+dvu?E&wa2D#!fNsqx2WSV>brUTqB{OdP8M$aX}kS>8%ywo>c=osFMac@YorHWAT zA}-auv?QEeX>jm%VWNfHoxe1Qf2R)Th6y2FlFu(ANH1G$E9VaI{UNoFfiruonL^Ei zx#B!}L5zUlW^hGtw~10i`DR3pkZ1j+;zzTz2bx+D4HtzG>*K8!qfu;dZzbn!b&x!j zuCtvxrRM6<+!qFPUeP(#)66^3W}h`-B(5jY>*Wt*``#=LtE*PjV&sSUPNm7Xm-=Br z*Ddoxm2i=;tF-wi>*eb2Or;^IE4NJv@2N@DoEl5;2wHvhqwuXVvag**)@l9m32_nH zo2xN`WJvNeOu?>{w1@&O!@5$)J`FpN2e1^r;7U&iwD>T1`PX1#rseiIf-!Ieb3X(^ zjju8!Fha7ICG4K}Ie0 QfnMbB< zay>rYibPp*J=+uy! 6O);V;APOt{nX5*PUtfN}|Pw^#uWnlB#A$F+t%**)+eTRn@DIyDzC$)j~ zfy3d(`xHWjKo^^|Y4R&lGohur8!$l#fjNvVqteY~A44z->OTgoPqERgh?EAN?!%RM z2aFMXw9+7uqNx0+70}nJi|sReJg{h5(w1jD!RDfarwPD8 ^Zo8e> FQ?@g#{=EI6s|#Zi=LslK_Tm%|(4K=`Gst9LvLLaL1GNit5!pI(bD z(*v(#tp$&fOo9ENil}cE+zAC&)Q@#~v+ts6LF#5+(WRlj$hOx6`qsthE%tfM`2d`x zu@|eRA2=j5%7Rw_^1TXAa)FY`epKRg;s;m|BtE%D#K^VKj-kMEqFei3O#2tsFTui; zk8Yxv6Nz; !|0pcLi-233)}8Hz!#~RBQBN5ce{HK8|cY3Xgo% zl8}RlW;SdXTIr+i!ndR#kuw 4 rrceMvkshN>x|hbC!@DyjaO8%a&yM{OovVZ=sZL}EKO zU5U?f9g@lv8QEd|e3`d#-Jffg%TEtIYEv6Iw=%vVq!@BAp3f @XYY znL0~HBU{7#M&2xPybzm;P^wWXFO=_OECVq&ik)@w{^K;_wOX}kR z-j~$jan4cg mM{wOXV6<4u|mTdL6|og;IkAIC`n&^3iJr}w%Q(`_Dn2A4qv~rU(x6_m}&?b zWs3TQ#~dtSml3Cv(n=p#+&>~tiiIjZlhBgBL%|xC4|3ipzQBMr(QEzEa6><* s@{d{fn%rf6zs z SH}13)9!HB`@MFnWDwPTqvH}kI@2(A3$*$I^VCA{^#?oeZ^Z1>(3z`8+yccm z$E=4ItffX1#ULxSJFSXn&{cg=sb#l=3Pk;y*^yhI@pkg^^M-?T9fru7dX}1ni{_xt zzv}kmcu0I`Q{GGf!>OL95!{P5i}Rwa`Vhf9od8o7s##fn{}~DYbsI@|6e%k zfyKsZ<*_!KI${Qne86i{IJ^)LtHHA@Cqh{e)FxexE{x*FMM=EbX3-$jo_XdiD~ekO z`G=_u)&afJHz0d?K;k}vDKSb0-sl3al6TmGgg)@hOSMe--IOK>QJj0ToPSc=` 5c=!brxU09hg)8>A>s;+^s0eG`0S>jvaQ!H(%cecn>Os~Kz z&1x-F?mVe9c MhV%LXOmG)HLtzQe^p iNiyFFFRx!eX0bdB>dFUukPFO3HG6q`4W4io zblOON5D1gjbzekDX?sOho{6nRB||)dDA|;Nf|GDz?xONjxg73kLHtp=*~A>hIUf*j z!;}7PO&Gn6@76hcl1tncG@!YfXyG?Xoej$^tfN^YiZFu|`{9>U(`*MrGr6xS |`<|J9&hOv9=K0Svkw@PY8zJ=U?(w0~?o<@3#7i)y(( zvm`2{1^I0b>;ni?6Sa2%8|&|-qV-1HJak1kfmvz9^fHJa)aJge(MLz*h1dPi+ps#A z_aCb5>R-7Xt{Nnd{fKvs{*4xku|Yq>RZ{i+CoknrKE2l0>T<>?^4ZNB4LhDCC0N)QRoww=!g6dAvr>9Z zziC9v_f00uElr6(5fjOdrV7P)vAQp1g8NgkP>@+=oB>q8n-nImGE=_~ZZfi--c*5~ za8}DCNzQp`>Oz@rL|FZZL&9(pHAy+nrB}+#J;X0y5YRJEZC2q@_Ms&|w@b6JO=$1b zey_@^uhZli{ltQyE48KpoY#DJj_*LP*-l|zk~J2^#QAN@R_^sAeRMB`?Yn_XE?2B^ z9VUkIo)$Ko7D#gltH5fNwUW>Evl-(A@48LlB5mRLU
le7tzFjxv*pP5F$m@PHs9 z?M6*xZF Uep1d`+MQ<3qA$J*iMnA(_v4EJ|KIJzqw!R5Fi-EuibK4fpC$LzR>vPv)B z@L(B*YCPB&vo3I#DCOR}h~JtHbPdsC-DM`IM}V&ocv3)opPjJBwu_Zee*uao!p)VHzJh{d3W>Xr5rQ+6ZVtmb(LI8%qRWP0xK_ zn#0fARyHm+X4jv_?-+uUoa7Y*T4%n&?sy((#vF%~&U|}tpw2&Y5r5xK!^6}D;vSg& zpm%v9eq)$47%s62oIMRfDAKw0EtOQZJ=9NRlaarC5UO9pte5u_l1)cpID`#MzxAxa zORikaxXVv3oPfR2;pK`@U=@dQr6X4GQev3&dWCSPcp>SqH?uw?*U;?-Y> Z?Lz1m{|b>yFhwXVcIOj4m~tU1S(Wi zT`yfzI$z0Xws^?4#shyzZaRKt$jZM`PG*E{_98I}PH248WDaW| V#g|+kvuF z|0WOh^4K~M^qj3-3iTxvv7{{m*qy` )He c(Vsievb*+L-;>P4I;U0PLDW+v0*Bh*1@vk5L; zSUUFwYQxLQ7MrTvvvDokE}kw0JNyUe)+~Sf7Idd+!w~i|RJV^D2MIYrJLBN)8Zttk zkF*;eya$@ru;RN&6U(-e+U`>A10N|UG~pF=aa&_%^dV}w@089xYP5c-0te$Fp33;( zyZJqk(VTCj26gxXnQ=*hF~EDv>6~bmEP@(`#3d5AM`_et`j*3jIa+N_8xotkXb+=E zV`b-aq;)C>dT%F-!!Blz7LU}XRZP+H t&Rh?U%RjupJ8TEqs0xa~KfJ^{ zYzwMs5efXEaWZEQ6@+sv9`rRwkw-ll?P$!~ySW&{8gD0#-I52p?J^o!7U=DT C>fUZeCRLhMrKU?Y@If8 z?NF{1M$X)Qq+sZ@>XC8 )L` z4Bdw#*&>)YKJTDS(25XNaSugGbhWn* Q%H?(u{s6CUiKPK~X~hih8@=tBlv>d60&eMWV*-#DTMmpkM4Dyo!F; z(*Y((DV~mABRCSJo1@@?ROc+!g3Fj77a!R&V%uVR-B6KnmNvgzlW4jmY-`Kpn*o{N z_@Y(dgLiMlVA}2&?}jvF!{I(~$wevf(7e@;glh<2yQ0;>t4$k((^LiX=E<4rLBDq| z_Q%#d3k+b~zGws`w!NnLL30aBmb9~z;?1=$XUYxxtwNOzX3_LumJo>o2j5PEv@#Y^ zBKUAD;=Pp>FHZFb&LDCnGIZcLdm;*@zKfyNj6meMtl%0iookBD3@Ld7>GRbt2I8yp zmsmhx>eOiztSMK>bFQcsHXoWiSL9;N9?+zmZNFfB%TBQ|zDdHFwdY`N&`L*9(3to2 zowR3qNwg5^+dqyYH-*Y_OIwCvPg(&?vvdRe^3}zLv%i@;?lDN_eoFuRQJX7rcn3~H ziApbs|HAT0uPm7bxm}j|?CBWlJenz2$#zc`!fBJfZ;we=WNRO(2j=m;enEa$leH%o zn5Iz(azn!a^k|Yc=z$>K;CHTGjUT`RR8d8=tyzvrKEaktV?#4%#Rn}nSy%GSJ_!)@ zDi=!ZNZkp+dxZ`yUA?XBg=VfQnFkZ@q^S2`|2EIE*lq6zf&$ivgn!PnPyXNYJiA~u z%M9m+9EOSgQVLv?TW?f{dF-uK!q%2Zi7&aglHzeV4}{}&MTC2)$;Y%8z?Jp}rucg` z1?KYgO%UVQaavy Q^cCxn}6Teh3x-_b0%`(Jq|u{T%7?DXxXAC XS>q*$N9@@|I{zn zeDrvB?U(WhEl@)r$GMx3d85h_KJ&ms*xaeM#J0v+;b_9MmKsGk6E>>H!Gyb+DN`qQ zgyD+lHkuXa%D02Sxd*xWotFjgr+cu$!<7O_e)C3!a_{I&JF)%<5qt5QU?=d5`iz zozxZRd(g*&*M0@r6IZm1W)<|NT3vJrD6UVYI0jCFmTWKeOSJ^9mUHo9?I-jQNTUq~ z=W7$!n1LA>Gy5R!?(6tXVI3?h?r3 KLmBOHvFo5$Iupm9p<|6%`CQYUl!JJ_Vw(YSaTSwq8kkbM+>XR z7paVk(UwL(OC#;*`2jk(?7?o52|V33K3KX|BCadFdxJMGY@!71f(*hmJ6OT>YkgdI z21t0KHAJKAFO)+lus$%0^H)BYboss{{5)an0A|*ge1<=m!U|-(`G%_69w9=k**RJt zKf>RT=Ie$9k+el(&S4FWw?&AWhs?M00D>&9dXRm#yL!K3(F?WJKIG4n2175(L6yOJ z;ng^8j>mB^p#;`J-h;f9$^HTLHp9$Lm-zl9SqMQ8JvD77#%c@1p%^Nv GLFoaPotHtuKwp^pCb|UQm;Km#~nr0| Dnu=ZqD_{@UtBD% zETk~P%P|!jh2aGQna_&nci4)8Qnn5$v-kW)-viwJZ8LW&w$ZXZ<|wE691u-i?ZSYb zq>A%KCQae9Do`T^aeG A2u>Z-dOD-~eVgja<*Tbp1;>6ku)kb`E% zv)r3Na^LJh2YQlSDavqpKKx)77RyenDzEvvWR8FC(4(#eiv{pCuDbZ;2bfOV!!|zE z>B{0Q)N4kr6RFeycEA0Li-3-ln|B9r?#%$X$bUKcKV1ZK6LTx;r?D$5lFO_Wu-&l) z;R?tS0lNsdHD@OXj0jcKM62f $_=qIW zWofH9sJ0&hJwj%jiZQ70vkBkft26?QjAHans(kXr@z^Vc&(n-w2DJJm9r-I!XAJOF zUIkR6j`WHJlP-8m!y(Ni8l%X#$BP>c!t|1*47NQa_ys5r-E@p)PD-UnTDp!&iib4w zVSY*#M{SN pjk&L;aK5XdYkn z?J6w*Tq-#C=J*>dmz1UjpXpDC0Qcz?`sfyVC3JdYytA|p!3d6UG5*I @%pa(SwtYudr_|KA73GaT>N;w}TRS zTky(UI$iEiI)4pA5!e qC4+*9~ z^n7RhFp2WDd5A6?T|n2%czL$3;A?C<6#h^_`HS6{(jF3BnUdY=w(pq3gKvP>;FBW6 zk)wHFxY2XvaRsp6dl=Nrm=fNrXjWLx$~$!g%t@dV=+z9I*^@(Qw@)K|x5i5h?bykd zKiTLo)r_ktd>8MRlK?-bkdT|@Kt2EQ?n}2k5zS}r#E;-Ifn751U45e1xe!h%UWX$8 zxHycXwqyc_&`yIws%#1IM)J8ue;JDq?JeP&{~2G?gV9g(b6kuq(b#a<0H$Or2^+O? zP4Z|+6w&DInvNaheBMwgU8qOum-V1oeR^=FxCCkz!J(G5uW{3Is7c-k-$*qW?V#+g zkVsnds0Y9Y5znGE=&+ !r(#HeL@$U#!kQ4lVs>@8#T zteS*6Y*-+lv_$eUQW0m*5%8@L)kv01%cYK%!$b_yu*U~<&I&nj1A_uA;McL~6ZAVW z_hTxk)XY5=cmroZ654h?{d%A~c-f(1-utr*FsAl{luG(Zi-_aQt=6k=Lc-0q7L>y0 z-=gv6v%Su*WTZ`o*W^JoS6^I@75U*R_}Th_RPt?f%N o_9`6u_9p-bdjuX_N;t|CcX=RPCYg}Hoq5^+9GhhgL z(3In iLM^H-k5e8StmR3aJ P|nT=kh zT&{=_U8jJqW>q*(B`Epr*;S4OJIlM @YxjUn_y!DxF%SI@~ge39qupAM^xGK0 DB?=?2?ys|VvO5daQeRlH?+L1HvB;r|QcH_=ELTC7aE;B}ghj{H6iDdCr&-7C- zhhZ@aCbb |*=J?y=NuH>4X0_Lei^wLrOMbU3Tf`wi0t2BoK3U)D zL?O^oz}CAAwtCFS`ilyhtNANg;W3+NyO!Cg3^QREhk?pcArh3_Fkgs1bbTJr7)ph9 zZ2B=dCTNcd#)@+o28HqUeeerz?_oR~3!)5Q1{a fCeJMIB2%7@}MB zqx1!QXLtB5*`-rf70c>xIVM -YF9fCF5I4riDrvx~d4 n~)Eh9;W2%p^39-$F%|@a5p@72l;bRe&{a zx8Az;u8mz`5FBsD0xfKsjp@o_60ifg$3|GO;k+|L-h5+70q&(7Tn1;cr%7!SxB)sk z$+X_9mbkymZDv?JX>ZpCw6C^TQoCM-xNkdV?E4uT?R{}Y4j5K}kiBconWcBr)WZtZ zE#Q6PKi@R|FCNg(Y4N`v(0@Ik|9U|G^??2_c|cEN*MB{rU&ka_Ka#z85BMH@>;(k$ z2H-L3m}^T|nOj%`HW}KQYME&ono!uA>h&itnQPlRb((gqTC!q>b!j*F74t_GTU2h# zkl&S=V#o@oM>|RL*DpZnubX^nD;L dwxtnP{`ej1KXg$4N`it ??5`{TZfeOFcETN-JxWQNHhQy1pQ+SPj4 zyD{ px0=KDlf%rhls$HIx3%p6*Ex2tS6HNxbwxusF&!bDMZ zk#*Y}^=dOOAyh->-G@o5PC{dKmUphK#?>V0s}cM p^=v6VQDdHJ@&JCUs3ijdBv|2IiytZELscW|__@6)#ls^PHE|?#{+h`1{mH z?WnD;r%#MG2j|T zq5tl>a&yq!>=Q@NEj#oQbbzSoV$oSRKq75j#39we!lKkC4)MjTBjO@a)6IUpaKI+7 zM+^g?(42X5qfZ>li`jgL1H>hllL3NuS@<+$K%vV{G;=bXH#{D8Er5DUn(wa>R=@yR zqJXUJf)WaxH@qHpBY-T*=G$Y06<+wXXh5m2%>WPz2rp&{9;>=s2^J2B)dF0H_Lz zvZ2&R;mwO#T|gH5#k@5ji`PSr8jwXh#9HB_0RLhZ`7!HkTv|Bbu~oOR^NAb188QyX zJw&l^ku$Q=H35!&BO+`oh~lheKHy+V13niu*JWLp%G`y4Kzqgjow%Hi@W;Y669QM( z0a}FRxE4;(UI#vGM*kfJG55|}nR)7VugYehcr(^E_`x{VU^{#=>Joej+CDI6ga%iS z$9tR2xPf8>$xt*f?2vtA2SGGbm$yIY fV`l}92qW$)F&MIxN^MS@AeQxpW1nV2&jTiNwht|K%$We&<>K(1O%+_ zt{GDV348^?3Mer749}2bIWIff6PE~-dNXYSmDIGVX6ROi1iT$d |8|5L3oBUZQ@Ji>Wp6e9?RssKISKemiUNv*xXKq6B$gJ87oxl$!Q z(f|cG7^6zzN**QoXD#&^@WKaYaE00h#t7^@YV=V@Z;SA?1nKfdeu{kT8uH9&Ub5hq z^s@v?xJ9JTJ)>J06oC8>kaEan?;k-L1Xlh=X@on;^Qas0@X6>)@F(5SD5 GXCD`&y{)M}_4QW^0R;NZ&iXSG(3 z2;s&&0e%+w*qI-c6x++60F#gUJ!^HAuOL9-pCD!73!b!EE$|y<5uujolUCmtetagp zY32lP;ZKlx{MffzD+31zY-j$a0B{!o8tDI`fq2$q+9M54|3-tNXS^xDX@F>`{F4T> zzi2=-TKx>z=@$)7d1mFFwL0{R1}Oq_&uabgn+AW0e53)}FB&-En*OAL{x2H*9pq0M z{0OZ4jq)cA*utxS(t!3C4FDj2(xAojD+>U}fqy;ElEGVe+9zc;2|T7fQuGHJIjNT1 zqcUG;@;~d+HFKS(KQKl@TQ!4n^b?@9caKJAUfB@f_|)U-Sr|VR7%B%>hW*&%KY}YW zpEcMt5`_Ea$$VYI?H)<=*y9XJg6$DcJ^q)-N8`QbBMnw~^GM56-dV|K1uhvN!;}64 zWYK#q*{2>yG5n2kf~PR>(T=Wh_kPmSys|F9_9w_Qa36J%hAUOAwENP6cK%DQufotF zG4RxQkAP7FDgkLvmhU-Ij;r?s7|!llm()~BV(m!=5cHD-FLC$Y14s`zze4bmuP-}OYGIESKhyH%Uqj+E$gt-D(Qg~p82}E; zufBcw7Y)({9@8Fafcow?4aA@Ee*8rPE#lFXXBuc3{G>s Ajmz`tmK8dRD4tkv{i zH24^P{!9aEx}P+77Wqg6zh5+f=AC_>bO0cK(%|nPf6{;&$?!MIpEU5p-Fu#H03e@f z@Cfo34YKY_O8_*G{3;ysn+AcEfV3wXg#V_&`A-_q{GtIL?%t!7KGJ~i7Y#yqW}nCZ z_U>Oa2p72dsnxf?Xn HLBOt!(v>FtJjV)3EUZ#k1GI|EP@_6k)aG`ceha;W3klKGo|ZJ z+9)|&zKJb0c2~Ypo9&^|q;R^cyNAtwjgw7K^8OTtv5Jtz{5z+D^L k)pefl21zxHklNxUEE+L5)Wta$W7P?3WrO)Ofu?2H>elJ9cj^n~;;~yO1Tz ztJTxFIc|_`S-Xzn)DjDA-%ykGGO_HCxs(6EJW94-R1{(*5)$L_;n+l2a@r}%ZcifK z+`FxoNO!hQ;^3cDRCZO1YDm_Mt=Qnc2q _KSfb~$M?zEMi-R?z!@X0Y0lCcC5p(NpfJq*&zoyBlZ3;OcCSgvod18^ zD48Z4swcN65T_INMtyvO{eR#_sNf^weL nZcF$%Z&Y^#?yr=-pDD;cQ%IjvPEE&UpYW>^$jHZ+>$dVELMi |W?2n7yCuY8R1!Vc9}W z-L*ta$lqVO+o>%ghQ-lpZ?95hKXP2rl&q~NW-ZuM*0;5oT+4Fis1e~llX<~6sWEWE z57DwGZu_tg<(_h#&R+ACIJJDPkiA}!cE}ZDReN=JO8 ;mNz$0F&he6|0mE=3jVgXxc|S~mJ-l%cvhi(~d4*~HDB;c_R~^#d1u zF|6R}r6V*_MGO!-vAV*cQi{Zq>38wRrn~9{yQfR17G(QGl~??XW^}9^gDYY+o4b1h z{R4KrLJ^VrJ-4XEzApOv5ixfY>SiWuyAHV(DO^iaPR(`t!vjtqL@vl`RECZ&--b`_ zIJ@;ZMkm=0bQ#8oCyE?TDY>wjsoizSCpHnB8Gl)0Y s5*waa z;u@t&63go_0(4h O9`l?^X**NMBx?ou^S6`Vu47$QDxE{u{kzg@eYM HRnEt2OIohuyOo z6*Cw0I 9l7YJ2s|*m26jotC=dw-&+&t@!yv)#LqNuudCgw%q~m4*&Tf z3ArSnwGog*3fO&t_t$=u$M6-;5efc^XYo5O!BaE~pRfT)!0r^E1Fr+PbF)z=xbPe` zp2PQNFJb^OLQE0Lk8;h;DZIRvP4xlWB64OqawX?Uj+c}Dq~2h6zcqg~W%!k@YvK(k zkCfiJE5{IL+%*%&wa6zT@cbSWzsJOkXfX5 zGN$t`dpxd#NyOwnvd$pz(E9J*rtZzzQP~`Bd >i*&(B>EPjyk|-_Lth| zPTLl8l`T~N*VcO;rE# a}Xg#z)p!E_~H4Aod@b#)Z z)3T>8xMS~HipWKgFV5faNWe8XLJD?Y+;kV$iD!P5Ty9Z>sqN?dOke!gB=@@ubKwVU z#tc&nn;5$Oc8Tb|j(q{VPMh|-WkviTT^QF8w*sWZ6hfUX)|pB})i|o04>rA-wHo~S z-Q`t~2hhYEaT#WGK6NCVNqT@NMir`aul>#kRfx)ugnek({Ng-NnS INIbi@LU;(axpcKzrlKxV`@3RX}3Ml;CA_NHH z;rH168)jp>Ts$8Zys>v>r(S_?Rl+3dw2HvpoadmF1@ni|wmrCVShKoj=dp(qNK|Xo zVIt{?Yj;=phV|{VArgO~2&0hVgCd1Y^1G>DMdOoK7KoAHp%;1sC0w+wXnX`kNQ8mh zzefX4BtO?(K{2Sl5OO_y5tiUit1(SI;aK@?^9X3WJVvicGj}=OCui1CC?SXBGSsL) zCI4>aYm)@NTZwTypEav?)pAX8Tro1}UCrFB{|z6haxhlGVx#5dzaHX%?{&f%``^^@ z^pWCO9c4!CRM37G+)FJ6=1Drl*-6$P(=CjhU4gz62Qtmk#4u5qtLu{tjFe^K-yhtJ ze0P9SQzZ#rHApj7l0YI4qZoYIG qY7UE1I0n73D&QMy$)+YfDH z_*Z&F`GKME0;r)6IG6 >mprRjquZFM!kmwQL^P9(34)U`N4!nnz3qAn%Xg6nSv zASWCX$XCchPi6=cBXJ3?RvMk}cHK~{QM;x6+!0XlhXkn;;oO0211X#M?f?gP|2aEp z3PQjI0=Nec*ewb7ckXFgSp1iI0M0%CB}H(V&pjRs1-Zt8UO;17kbw3q)Dn 7B5*we2@Dj?809?LVR}K; z4H+x~qUwm6li-ub3ZJ@*R3bW}j+F-(KWHJ3aFm=>hG{zOR}H4p(f0YbD<2MYP2TB$ z|1cZ-8n~yRydlN{8=QVTb_ L5KP|LSf+lCX~*>O-d?hdB% z8JDJTFO!5$30V*W%3jWmhg69RC~|b%hL8iLN0`L bYFfC2 HbS@7|C46up6Cg`Vm=+A q^qkLXu_O8GO)&TlEL za8FYH47&5DfIkBT{1(6kIA#pc#qY2Ie-{1oQ1 QOqYPKX;Koo;Q8kME<9QKevqimcR@+M)P~o_xlLVpA!Du zyZ&3k+55js(Eh!X{ZA2pZs-0jf`IHlL_9X*X_57(g#Wo2th9fZ@YIZdmGD0|;{vb( z`mKn+n(?n9{@j!CyBS1G|I-eQKO6F==k;5Lujmtu|IH8kQ{bP@%x{6-Vow5p5$%6) zZT=Mar(^J2V1W4F74@%f!k@zaTvq=U7Ax^T2>ZRr{!`e0u1SB30s_jF{G0xNu2BCh m{-4W=zZb`o`CIYlMTUeh7{F0@+ 2-M7jh7#1)VRmt4BLyFt2R=?>}cZs}Yv z|M$K3p7WX6IcMg~o~dVMzRz!@Wu^G|4SxZJfR_M(viheFpS0Dr^grnuX&IX8eX%gM z_-<-#YxdpF`~kxL>kr4THcl2c&K9;VmUgb+e!70QcR@J1{cu9sx_CIaySX4;+`Qae zJ&{fxUiQCy?U5)44?joG04J{?XP;mfpAa|SP 5Wd31I~Om0