From 3bef01a3a943e72c87d138cc921bf9f9acbc1844 Mon Sep 17 00:00:00 2001 From: 1i9h7_b1u3 <1012796366@qq.com> Date: Sun, 26 Jan 2025 14:34:56 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E2=80=9C=E6=8A=97=E9=94=AF?= =?UTF-8?q?=E9=BD=BF=E2=80=9D=E8=AF=91=E6=96=87+=E7=81=B0=E5=BA=A6?= =?UTF-8?q?=E5=8C=96=E5=9B=BE=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/04 Advanced OpenGL/11 Anti Aliasing.md | 22 +++++++++--------- ...nti_aliasing_post_processing_grayscale.png | Bin 0 -> 10911 bytes 2 files changed, 11 insertions(+), 11 deletions(-) create mode 100644 docs/img/04/11/anti_aliasing_post_processing_grayscale.png diff --git a/docs/04 Advanced OpenGL/11 Anti Aliasing.md b/docs/04 Advanced OpenGL/11 Anti Aliasing.md index beb6e64..6b97457 100644 --- a/docs/04 Advanced OpenGL/11 Anti Aliasing.md +++ b/docs/04 Advanced OpenGL/11 Anti Aliasing.md @@ -36,7 +36,7 @@ 由于屏幕像素总量的限制,有些边缘的像素能够被渲染出来,而有些则不会。结果就是我们使用了不光滑的边缘来渲染图元,导致之前讨论到的锯齿边缘。 -多重采样所做的正是将单一的采样点变为多个采样点(这也是它名称的由来)。我们不再使用像素中心的单一采样点,取而代之的是以特定图案排列的4个子采样点(Subsample)。我们将用这些子采样点来决定像素的遮盖度。当然,这也意味着颜色缓冲的大小会随着子采样点的增加而增加。 +多重采样所做的正是将单一的采样点变为多个采样点(这也是它名称的由来)。我们不再使用像素中心的单一采样点,取而代之的是以特定图案排列的4个子采样点(Subsample)。我们将用这些子采样点来决定像素的遮盖度。 ![](../img/04/11/anti_aliasing_sample_points.png) @@ -48,7 +48,7 @@ 从这里开始多重采样就变得有趣起来了。我们知道三角形只遮盖了2个子采样点,所以下一步是决定这个像素的颜色。你的猜想可能是,我们对每个被遮盖住的子采样点运行一次片段着色器,最后将每个像素所有子采样点的颜色平均一下。在这个例子中,我们需要在两个子采样点上对被插值的顶点数据运行两次片段着色器,并将结果的颜色储存在这些采样点中。(幸运的是)这并**不是**它工作的方式,因为这本质上说还是需要运行更多次的片段着色器,会显著地降低性能。 -MSAA真正的工作方式是,无论三角形遮盖了多少个子采样点,(每个图元中)每个像素只运行**一次**片段着色器。片段着色器所使用的顶点数据会插值到每个像素的**中心**,所得到的结果颜色会被储存在每个被遮盖住的子采样点中。当颜色缓冲的子样本被图元的所有颜色填满时,所有的这些颜色将会在每个像素内部平均化。因为上图的4个采样点中只有2个被遮盖住了,这个像素的颜色将会是三角形颜色与其他两个采样点的颜色(在这里是无色)的平均值,最终形成一种淡蓝色。 +MSAA真正的工作方式是,无论三角形遮盖了多少个子采样点,(每个图元中)每个像素只运行**一次**片段着色器。片段着色器使用插值到像素**中心**的顶点数据,然后,MSAA使用更大的深度/模板缓冲区来确定子采样点的覆盖率。被覆盖的子采样点数量将决定了像素颜色对帧缓冲的影响程度。因为上图的4个采样点中只有2个被遮盖住了,所以三角形的颜色会有一半与帧缓冲区的颜色(在这里是无色)进行混合,最终形成一种淡蓝色。 这样子做之后,颜色缓冲中所有的图元边缘将会产生一种更平滑的图形。让我们来看看前面三角形的多重采样会是什么样子: @@ -60,9 +60,9 @@ MSAA真正的工作方式是,无论三角形遮盖了多少个子采样点, ![](../img/04/11/anti_aliasing_rasterization_samples_filled.png) -对于每个像素来说,越少的子采样点被三角形所覆盖,那么它受到三角形的影响就越小。三角形的不平滑边缘被稍浅的颜色所包围后,从远处观察时就会显得更加平滑了。 +三角形的不平滑边缘被稍浅的颜色所包围后,从远处观察时就会显得更加平滑了。 -不仅仅是颜色值会受到多重采样的影响,深度和模板测试也能够使用多个采样点。对深度测试来说,每个顶点的深度值会在运行深度测试之前被插值到各个子样本中。对模板测试来说,我们对每个子样本,而不是每个像素,存储一个模板值。当然,这也意味着深度和模板缓冲的大小会乘以子采样点的个数。 +深度值和模板值会按各个子采样点存储,并且当多个三角形重叠单个像素时,即使我们只运行一次片段着色器,颜色值也依然会按子采样点存储。对深度测试来说,在运行深度测试之前,每个顶点的深度值会被插值到各个子样本中。而对模板测试来说,我们会为每个子样本存储模板值,这意味着缓冲区的大小会根据每个像素的子采样点数量而相应增加。 我们到目前为止讨论的都是多重采样抗锯齿的背后原理,光栅器背后的实际逻辑比目前讨论的要复杂,但你现在应该已经可以理解多重采样抗锯齿的大体概念和逻辑了。 @@ -87,15 +87,15 @@ glfwWindowHint(GLFW_SAMPLES, 4); glEnable(GL_MULTISAMPLE); ``` -只要默认的帧缓冲有了多重采样缓冲的附件,我们所要做的只是调用glEnable来启用多重采样。因为多重采样的算法都在OpenGL驱动的光栅器中实现了,我们不需要再多做什么。如果现在再来渲染本节一开始的那个绿色的立方体,我们应该能看到更平滑的边缘: +因为多重采样的算法都在OpenGL驱动的光栅器中实现了,我们不需要再多做什么。如果现在再来渲染本节一开始的那个绿色的立方体,我们应该能看到更平滑的边缘: ![](../img/04/11/anti_aliasing_multisampled.png) -这个箱子看起来的确要平滑多了,如果在场景中有其它的物体,它们也会看起来平滑很多。你可以在[这里](https://learnopengl.com/code_viewer.php?code=advanced/anti_aliasing_multisampling)找到这个简单例子的源代码。 +这个箱子看起来的确要平滑多了,如果在场景中有其它的物体,它们也会看起来平滑很多。你可以在[这里](https://learnopengl.com/code_viewer_gh.php?code=src/4.advanced_opengl/11.1.anti_aliasing_msaa/anti_aliasing_msaa.cpp)找到这个简单例子的源代码。 ## 离屏MSAA -由于GLFW负责了创建多重采样缓冲,启用MSAA非常简单。然而,如果我们想要使用我们自己的帧缓冲来进行离屏渲染,那么我们就必须要自己动手生成多重采样缓冲了。 +由于GLFW负责了创建多重采样缓冲,启用MSAA非常简单。然而,如果我们想要使用我们自己的帧缓冲来进行离屏渲染,那么我们就必须要自己动手生成多重采样缓冲了。现在,我们**确实**需要自己创建多重采样缓冲区。 有两种方式可以创建多重采样缓冲,将其作为帧缓冲的附件:纹理附件和渲染缓冲附件,这和在[帧缓冲](05 Framebuffers.md)教程中所讨论的普通附件很相似。 @@ -131,7 +131,7 @@ glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, width, ### 渲染到多重采样帧缓冲 -渲染到多重采样帧缓冲对象的过程都是自动的。只要我们在帧缓冲绑定时绘制任何东西,光栅器就会负责所有的多重采样运算。我们最终会得到一个多重采样颜色缓冲以及/或深度和模板缓冲。因为多重采样缓冲有一点特别,我们不能直接将它们的缓冲图像用于其他运算,比如在着色器中对它们进行采样。 +渲染到多重采样帧缓冲对象的过程非常简单。只要我们在帧缓冲绑定时绘制任何东西,光栅器就会负责所有的多重采样运算。我们最终会得到一个多重采样颜色缓冲以及/或深度和模板缓冲。因为多重采样缓冲有一点特别,我们不能直接将它们的缓冲图像用于其他运算,比如在着色器中对它们进行采样。 一个多重采样的图像包含比普通图像更多的信息,我们所要做的是缩小或者还原(Resolve)图像。多重采样帧缓冲的还原通常是通过glBlitFramebuffer来完成,它能够将一个帧缓冲中的某个区域复制到另一个帧缓冲中,并且将多重采样缓冲还原。 @@ -147,7 +147,7 @@ glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, ![](../img/04/11/anti_aliasing_multisampled.png) -你可以在[这里](https://learnopengl.com/code_viewer_gh.php?code=src/4.advanced_opengl/11.anti_aliasing_offscreen/anti_aliasing_offscreen.cpp)找到源代码。 +你可以在[这里](https://learnopengl.com/code_viewer_gh.php?code=src/4.advanced_opengl/11.2.anti_aliasing_offscreen/anti_aliasing_offscreen.cpp)找到源代码。 但如果我们想要使用多重采样帧缓冲的纹理输出来做像是后期处理这样的事情呢?我们不能直接在片段着色器中使用多重采样的纹理。但我们能做的是将多重采样缓冲位块传送到一个没有使用多重采样纹理附件的FBO中。然后用这个普通的颜色附件来做后期处理,从而达到我们的目的。然而,这也意味着我们需要生成一个新的FBO,作为中介帧缓冲对象,将多重采样缓冲还原为一个能在着色器中使用的普通2D纹理。这个过程的伪代码是这样的: @@ -178,9 +178,9 @@ while(!glfwWindowShouldClose(window)) } ``` -如果现在再实现[帧缓冲](05 Framebuffers.md)教程中的后期处理效果,我们就能够在一个几乎没有锯齿的场景纹理上进行后期处理了。如果施加模糊的核滤镜,看起来将会是这样: +如果现在再实现[帧缓冲](05 Framebuffers.md)教程中的后期处理效果,我们就能够在一个几乎没有锯齿的场景纹理上进行后期处理了。如果让图像灰度化,看起来将会是这样: -![](../img/04/11/anti_aliasing_post_processing.png) +![](../img/04/11/anti_aliasing_post_processing_grayscale.png) !!! Important diff --git a/docs/img/04/11/anti_aliasing_post_processing_grayscale.png b/docs/img/04/11/anti_aliasing_post_processing_grayscale.png new file mode 100644 index 0000000000000000000000000000000000000000..75959a396630fefb5f629b0978782c44a544ca59 GIT binary patch literal 10911 zcmaia1yoeg`|YKX7AYw~5Rh)9Q@XoBq;sT(4rvvTj*(V61SE$N1f->;25MmTZ#~v{X)mR2!@K^GFMPo>zD#CcKOs<7(7g z6rwLMU*yX1_6FuW6pR!m{dGJF0EH=(;M*Qw2aJ={j*p8bHIvxz>~!PaKDM)4yV;ni zg2({?(l>1KhMTXEHbN3N!sDj+2@2cUny}Ch{noL8RIUjq++H*}hTPfadC~i}g@ujv z^?CJg3KlPhO+s&+dTn}4uHDbWq^{7Xzq)?2MhaR*s$l>7)jN8k`H6la^5 zRSV(m6~px7Z)$dJrkoUOdgQN?lycu43x0Z>ppg2s|H;&?{qI%oODvgH5#Y0{3`TGY zPr21gtTPREGE?84-)I0hf_r_P;lRg=aE?MwKyMFZuT=`401=LAsjmRQT8Wj{aJW%+ z5FY@P3L`l`%hR2FrQzb9_)2(XD;ll9`mI-iOo6~Yg5KNqY4vLbj@Yr!44k%n zGja^VJ$lZuX%G_LUZYkLX$a}X3;g`A937GOu;stwKX}2s7>{B1#*`%qpE@VzAM;mL zZ1T6POhfS$dXF_yL{tUz-xx9(sWKc%dt-{nSt_@uNPY(dUth&`s0gG-Ht0#u5H%^b zm5b3vD;E>~u=f?nO{OY@{qX)mEB#KiX!A#vGwEWojN%}F&lU}y&^LE}-hn=a`e)qt z(q4|#KdvUK8le4bT#W@!IDI)$55*>reBN_U6dS6*^ny`d)l}0|dtSAiiJn#X{^C8c zdsxwWz3h+E$~C^QwckJgMq?)^5NE2&&GM5x`2inUaJ=F-F;?;fMM3tq_n2z+9}TC- zrP!jWOId@qGtU%y}0rR!hX5pV^LIU8lP&4Gp1>s zsh!a-bz8pe_m?vmwX=ShOpR=-Y-Md>oj{3*V=1EsasOUA5Oo0 zIB3W(Ca{~mnM0c6Wn{|P%o#$NGE9)6p23zu$H`|{QCV8KQb}lZY&2q+Tw!exTB~d% zWUy9oZzi+Ks4`D?*MQEzsY0?muF|R8%TV@XnAvp#u34XEpN`^3y~28%wT=NM!j}&l z1Gt&ph^u1#B1OKj)sHQN_;bRuuNmNPr7J6n#1)0qg-jatkJBAx@z%0m{rljneQ%Sa zEbBPz%?icA)89|&N`*Ds1RN~gDl@Y(O{-L^^r}>LJ4I{v%koM)pTo^ABT3{3)!8lC zRh*K?aq>0|77Uil2+F9mq$N&kc`MyM(G|g!pxvHjsOxg_!ud*`qo1g_9k%Q%iqsA1bXgEwnu}@md;>IBtQYiou*A%;$?Y zg@m!hO78dvlx%4KN#-`6lj#K6#Jtd2^MjG6-(2zxVACp2)J|xqm98k-g$#@s6$*f6y5>rN1!As69ktwGcul+3juRIxNIn}!-M-QM+ zk_ZBO8G4-_^G%9Q#?LESax!Wksd8!srS7G4hy=3s3w`3=Xp3gAc;BH(tho%6$fwHn zXYvq~mgp3SN=V+q6JnN|5*WBa3x!%OeQJ{%O>?vwYDBU76{#cG)FFqr=7tacS| zka`ukM6gDQ!xd|cTP@Z4rkT@YR;Oy;B&#;8lPyTgEzbUJgQl-t)yG3wZoxoXnagk*6^d@iQ$Ez zC~9iC>BZbX&q)+raL1YO-t3Y^2l`rOsB6gx!$@n^an|8d?XnRqTboe1{V(&UHx z41BH%xktImFoWDn3p~1Ap8S7x3wUd_Ri-#$Kf#tyeN%ihQcF$67xr&< zcW!(#d$K6&C@ZboBjnTdPS5Vcx}|~M0miPO03iYE6XVPMuTTV(;hPXHhy3INV;!S8JV@Z$%79a{j9 z$OZsvuT-0UWdL|ItfnMy7_hjv9O#>l?p!@weW$E|`6Yri8e7$tDv}>2FWSsc;~o|H z?aXCyC9Pj=u@sGH-DLCFyLzRwZvfx?*~UIs=HeCXy(pXnD;1YM^H`(Xn}22M>d%5L z=$tpHTH+vl*(Kd5d;L;>RD`C{*)f0P`l1i-^QXZ4gM(etg60nAngQ@ca^T?p-$* zd|l9Sw&=#!@wZnxY-uw;7-iCZG267-_3!uFy6&3`hB&x9jU&TX8~R7(<-;bUBiCth zEyhL(mBW=R)cYRCX}dDlq$S~}e`S|$mRbrdz2o9sWH0j`-Gr>w#*x=fF0$KPte{Gl zE-L?hOU}R5n?2ia)BXLGx{K~#3!h3s__YPY2|0i>WG`4@xN{+UewZ8{a z*L{eXxY-RC$kF7x`5PCEMs>ot&>h@_%;d^X-{TLPsf~Nx?`4WM{XtRpeYgN^mCAe> zKil*Nmd}_pu6+) zb@C%LT`qovr_}kk2Rs^?SMa$oLlcY3J)sGyn}2I9$3|k|g%Xr48X1vS9iKUr;AC{h zGqWM6<+H=?8TMK->7lMJxE7u2Q`=MU9vjtuI!n6b7e`g9b@uqKgo94zh zkFlnrF2x9#qsuLttB$SeCa$hKZO&PwIW= zu5-Jm5{|F-b8EQHoqFX#o!8RYJdEWYE?tlGqJI>NN)pGJ^FjCR&DXDyt5?gC($dl` z=TAyuH`^UC`bJun4H8eHIV@}IY!KqVXgkl*f3+Flg2I=ws~_eX{H~#Q8 z*o(x0Q)Q4X(Tjxe)!!7~{ayy0z1@2beEBpJ!g!%9dWR=n)ZsVOv1$5U`2*MxuZzpe zS?}^x*3P4ys@3yxDD4j2&8Y@o=fyA=AvJFY45CwJ0AKt`leqBajio58LSQt_K|F70 z{kWiwCrw8!K}liM&9c2z^v3CSC&N!;bA&xyw;Np$CXHT#Hi}&eM{Q5sY}DcvT<%(h zqxocRVBKG?&)pb~6+FEPRv(@JdxzAJ6-ey7ueE=1W5Z}|F79>Kx!Z;MTTmTnduii- zxxQh@<|IhKy#6I!l~rk@BX^hp%Y#6UnLMGbyXmi&b>Qi)&E-xek3}@u*qB!MP3J-P z`CGBBzg3*=a5(8BnUH-nQk9h|*>{F_r`*F_Zz*-eX?olJ87(1p6k}c8%l@OCot<84 z(b{TQy`>0jEO8&6e;zc^??bIjkJQqU&Z-aDt2tP??g?cWS<2+InlcrsbJ!T(a9iA= zg&BY0HS4h3otE@w*FNQ$_x-owG)IV(IDb2N_2$h_l{2aOVvox#l6B>AD-jr1?oW>( z+fWsgV5{rj%}+FptCtTay1)FIPgt>Ei}jLfDD8463jSNX;9l!&4gdI4Nftx*b(7m1 z2{Ca_T_c#9fByUlzG?=&GGG5v1vxi1HpZ~OUsF@la+A7xje^D=grVVcbv$Vkn|;W? zr^kpo6B85Y8uw7})xs8D@=dbI&4JENyfcu^G9i0=xgEX-^Er967@^WmrN&_L!P z62PZ&uim^VVB0Ph1NcMbHUi325`YPT0XV$a+S~g`02B(8x{F%8GeZLr-Z`@2GA#~( zToj0vcWa_0fd~j5K!yT<{|=!4%sk`6wbnVZu{T}NM9@tQoN&t!FR zb>*Z@EY*KniA{#m?bFFpe(C3T+U~d8-rmj_esySZJNV$i1BLjPgGn3S;#IS`BOCe^ z>D(hZnz8B%CPY;xb=s`t6qv&&-&vCPRyyHDMMZ6G=i1$(8S0;=w{7`3M!Z?cUrFRq zz3tyT9vdCiHFaLPIzPS#x}@gTxn?>C)LM6S~BK$Nd+H_GXz5;sq1bVK3@UV`F34*x7lg z2w5%V=Z28p|7+*EOWug;G)4i67|>90a$_@duC=s!gs5pEWemm-BvKi^Wr9p zdt_~GZOHxz3>?pvr48?U%v0N6$R1~n=!uQ34KuXt7R51|cLtpfB+|ihb6e5H67{B< z_OkWl)I;yC>zB#W&E6ZsNl7FG#cGX>jY2{~+GW$8 zErV-^hMbi6*r`MIqOecTc8H<1-4LJs{e76I2Y4{u=g*%zI6T3b{Om9S;&pTN-(5@I ztclIW1$UW(R9v1a>{fkX*6}Yw3_}H(%7EWv) z2X#iuF=eQ0X=;A66Tno;F+oL3e(MN4c6D<@AFSTST+(BRaqfkQ@bmvV3>X*~@NWCS zs@A|w&A9R!qVn)dOUuDB915D3hlfXsrN5I)vyU{c?>s~%MO9NrCpkG8Zdtp%?ea#k zwWPJOs%mL*QPdOB>air6pHgi9LEGsWeSUWK-=NOJ0I3|!_GM4C)Zd5C`k4{0bih)= z7JTweDrTsj`Ynjd9h2d#PZwq^F66#nR37CAh6|owb%oAtXih zBmX7%aEfNZg6wRL4j+wIC=Nv1Sf!S7Nqt2{MM=q8Pb5~AaZNFk?&L(4pPwIi@Pc!n zsUnZEl0PB5rpM@!1>~ar+68WuWb56QTJ7vShE7j$SYDptaXk zrrcBsO4Y`0!nDM3eMP$8H;NYLB%ruztQ)h9r$iYnUJMKjmuLHK&d$#6?jMDr|HO?ucwKKh#YTDl+nt$P%zF@iS`@?NLkmR!Q;hz z^23KBI>*PyfByW5LZL2E@Vd&%H?IMLl!;BO$cX9fcS?_AwCai{%cr+bF0TpNq9)uQ z_E%KjU5FMh8?e4QIkDqlP?Y~=R5LJ`PP}yu36V-0qWG!SytJS-pg zRnTfETKe$Y1JHYKsd$Q+A{Hr8Gb$yMN{{tgbA*J2DH4^X8FlKjM%SA)&qc(AuWCH#&%>uftj*U57X!30o zD(vr9ye!ZW)7R~QEs2)4HaY6~%+!>=jm_=+%Vdw`_KYSBApPCaJ{#h#-CQk*K2{dZ z?swiUFE3;Q)CW9U@S@~Avh_*&J93)SXP9K4jfqnnYFurG zkF+*0@Buw^c6OFXCnlH)K*qMVoa<$aa> zLL8SIsNIA-Q~&#Ppg0UYG^BPnfkwtXs+2+Kvc7Q_Z4h@RHoXO3#dxlYH3lhu>^$Hn83>gax%iV$x&t4S^KQcUl zh{4AC!|oFThVq#Xu(8q<2(`iq8ZA@~=nnI9KX1))9FPGxEcMW{T#Bve(^Ch*hXa%m z;~Czs)t>fBr>L@~sr_gC=k(K541D!~Z^t+iC=6L}2T{V<0LdHO8g$C3NGLaX#vNDSLz{YYL}S!oFItmw<*f&%T{$X*<)X!xwF z0|O?As_4rRzvph;*DBiD+JfuANzcB2)^5oN848Rwhwm6$@(;ZX)0q){NPde-%pXyQ zhp!l7g1M={Qu|H<7;7}Iksv_dB{0?x#Rj~nuMHtzxJL#WLjE4E4L%G27Da`HSw|YW zdh<+j0O$OEyQgz1V%4T*`H+^SeNk9qJzk+vf>QCj=`LbQ$^q|CeuJP77a2(Pbnx+n-c|mb41|)N4}8L_?85sae@2pm9jP2(o|V} z8BlGOyi{)|34kxGDXQt$4YRf!&{k4X(rIw!n`=|%^{_N-jCLd#+aI213=}?bY;4=p zd4MJ3meY6ryZ`MMOn~Ph9Ua%D08NSV%LUTnJ%eiu+uGe% z$9FY`2|(kWA8&4x0g2&~a-ebz@(dq6 zkukDSJjr2&DQ5-JYs|7}4i1DtMf#MdIY!UEy$KT;6T&Ozx&fnn{N&^d<{XcSaci&? zBP~uuJBLqcK|^mS1oB=PL$1}02h4S9Uduugpgd;vz~oQlQ(lCbJW<3OjwKwKwV4v~p=J9=0%M&tuFta6aBaSwX16!7~T331REJFas z8eS#z*IKe{sSCidI$5GZ8SJi31dZ#vPlUTo2N0C4^Q^xcmg2TH!YPD!wpa|gF>nea zRFsvS831)qf2MbYe^BVl1K)rHF*8Gf3>&1l1ZV*QTwL7rkN^T=D2}HaCJ=o|uX{w# z0!WBSN^*?|BE!UFuKp_0;ztPPNkX-eD&+{Umo!a)Xd?iQ&O46g>Wn;FLAI)v>dXSD zr;Tjb)zcuuvh*t`d>jD|UA8ok&PHD@zcgV32<+=k)2}=$33@|ALOzIM77BOYULRzH zzkfplt(&WwZ%W13%H*^z663u32j;^TD=_?`$y_Lh2&oO`CIz_U&6;6Q>*aNr(bbcC za_ln=)kRZ%%XdZt(iP9^_$pWsPt@$ji9jOb20|J?Z0zI#@MLRucVzQ8`V#T)R}`st z8@&}y;hy1%<0m~z6>({4EwHrL*$avb2*|fiJjsm1*7ZLOuzY?jIddPyoAq`8Q}>MN z&EQ(}r6riHMfE^>*0QI`DJ(O=06Dy99l_g8Wwx4%8%LEVHO)AY}vuQpOy&rGV$BL+l0R=wFGP(kO#6 z?a4sk4LkTALA#Dkv6sJoF^%CB2F!Hf)|mG#d$wq#D&1l*Aw(!qV4%^HOosz+mFMvE3E?AcHpJ0o1&xuQ^cox)$$rNzv^TCY+CQ!VPXubJ z4e!Yq;E!c^c=w{t*YceblS?fDfPjJs7woJct+hdQFZ?*7O+-MTD6&x(+8W$x4erR_ zG6N_7=-(?%o=^@odjt%BO!RY_Wk7wkeNXufY^0eJf?$OAC`67AGn>o0Zf@<647Gt? z2|k!i0{*Dm*c6Cre=Cpl$2hW~Mr6Eg3dca;cLj z1B)K0O#0Wr!kau@0ifc?99a+6{d}I$BO%$IxRAuytn*nt&`BS(*_Vk5nP6i(>!*JEaqvGjMgZgz zta*&6p{>XN$#iD`CsMBG0Q~@E{>!_~8629_S&xA*3a1(!9sRt^2rx%;k6bL+E|kZm z*{3`N6yia`uvZ?TVzk9Y6$cE=b=BmL~VHTEz@A~)qoy%#HCU=f*r~g5xmU_|_RG8Fj zWny57LGkY0yR8?J?!62VT^-kFOA;OIC3SZu<-R4{dNa2RHsmg(pe42AnIH?Z68GQB z_MEV?L56`%5KCT_K`e>Mr6rBl-PhbBo-ME>yc64?APMpWGE~r4cc>ic)b zh~4(EA?dwN=B6TCCUwqWRRcwCF-Z^+6gI{-BKVTjS)ZR~eKJ?OPnv?DewVS(NF|BB z2w#F2G~htkLIhi4a!yVnYzd0=obZHSctaPIm07b3-2MiPDbC{HFdmjsyM zg^hKAJ(-zE$nDJ576$`RZpBKT^Wnosa7`Il=X;U-T0r^Y_wT9_d8K!AN9R&LWq{FJ zoxmtD?7e0KNsBW$278-NF_B>pz~U;5QK%m&7jYW{ z>SG^?yb8lNGJv5<=7!D9QP$p=Tx&S**$D28v!mmHyQl$P@3oJ>5`(TwtCQeE@D0Ox z-}d)rRz~d5O9!PtCPTHPYM1%6(fa|0>w620V8H;7-<_d)*PSlMDx_-QwrPb#N|2#! zs#{vJ9s{nR8gq1Vs=&IVD!JY#Ta#csGT@{6{v4l!H5S|^5-02)TJqOv&{NF95blQx zt*bd)CbhcQa_1n7solp%OZI%>l%uS*?o_`U{6b%X3n7C^3~MzMz1K;7d1IEqlebEb z@4QaZGW6MVS>lF`+ZpVgxVRLNLU;d-8LxQbSn4{06^`zwo