<labelid="sidebar-toggle"class="icon-button"for="sidebar-toggle-anchor"title="Toggle Table of Contents"aria-label="Toggle Table of Contents"aria-controls="sidebar">
<iclass="fa fa-bars"></i>
</label>
<!-- Filament: disable themes because the markdeep part does not look good for dark themes -->
<inputtype="search"id="searchbar"name="searchbar"placeholder="Search this book ..."aria-controls="searchresults-outer"aria-describedby="searchresults-header">
<spanclass="line"><spanclass="hljs-built_in">float</span> a = NoH * roughness;</span>
<spanclass="line"><spanclass="hljs-built_in">float</span> k = roughness / (<spanclass="hljs-number">1.0</span> - NoH * NoH + a * a);</span>
<spanclass="line"><spanclass="hljs-keyword">return</span> k * k * (<spanclass="hljs-number">1.0</span> / PI);</span>
<spanclass="line">}</span></code></pre><center><divclass="listingcaption tilde"><aclass="target"name="listing_speculard"> </a><bstyle="font-style:normal;">Listing 1:</b> Implementation of the specular D term in GLSL</div></center>
The GLSL implementation of the visibility term, shown in <ahref="#listing_specularv">listing 3</a>, is a bit more expensive than we would like since it requires two <code>sqrt</code> operations.
<spanclass="line">}</span></code></pre><center><divclass="listingcaption tilde"><aclass="target"name="listing_specularv"> </a><bstyle="font-style:normal;">Listing 3:</b> Implementation of the specular V term in GLSL</div></center>
This approximation is mathematically wrong but saves two square root operations and is good enough for real-time mobile applications, as shown in <ahref="#listing_approximatedspecularv">listing 4</a>.
<spanclass="line">}</span></code></pre><center><divclass="listingcaption tilde"><aclass="target"name="listing_approximatedspecularv"> </a><bstyle="font-style:normal;">Listing 4:</b> Implementation of the approximated specular V term in GLSL</div></center>
<p>
@@ -659,7 +659,7 @@ $$\begin{equation}
\end{equation}$$
</p><p>
In practice, the diffuse reflectance \(\sigma\) is multiplied later, as shown in <ahref="#listing_diffusebrdf">listing 8</a>.
<spanclass="line">}</span></code></pre><center><divclass="listingcaption tilde"><aclass="target"name="listing_diffusebrdf"> </a><bstyle="font-style:normal;">Listing 8:</b> Implementation of the diffuse Disney BRDF in GLSL</div></center>
<p>
@@ -704,47 +704,47 @@ We could allow artists/developers to choose the Disney diffuse BRDF depending on
<strongclass="asterisk">Diffuse term</strong>: a Lambertian diffuse model.
</p><p>
The full GLSL implementation of the standard model is shown in <ahref="#listing_glslbrdf">listing 9</a>.
<spanclass="line">}</span></code></pre><center><divclass="listingcaption tilde"><aclass="target"name="listing_glslbrdf"> </a><bstyle="font-style:normal;">Listing 9:</b> Evaluation of the BRDF in GLSL</div></center>
@@ -965,7 +965,7 @@ $$\begin{equation}
\end{equation}$$
</p><p>
<ahref="#listing_fnormal">Listing 12</a> shows how \(\fNormal\) is computed for both dielectric and metallic materials. It shows that the color of the specular reflectance is derived from the base color in the metallic case.
</p><preclass="listing tilde"><code><spanclass="line"><spanclass="hljs-symbol">vec3</span><spanclass="hljs-built_in">f0</span> = <spanclass="hljs-number">0</span>.<spanclass="hljs-number">16</span> * reflectance * reflectance * (<spanclass="hljs-number">1</span>.<spanclass="hljs-number">0</span> - metallic) + baseColor * metallic<spanclass="hljs-comment">;</span></span></code></pre><center><divclass="listingcaption tilde"><aclass="target"name="listing_fnormal"> </a><bstyle="font-style:normal;">Listing 12:</b> Computing \(\fNormal\) for dielectric and metallic materials in GLSL</div></center>
</p><preclass="listing tilde"><code><spanclass="line">vec3 f0 = 0.16 <spanclass="hljs-emphasis">* reflectance *</span> reflectance <spanclass="hljs-emphasis">* (1.0 - metallic) + baseColor *</span> metallic;</span></code></pre><center><divclass="listingcaption tilde"><aclass="target"name="listing_fnormal"> </a><bstyle="font-style:normal;">Listing 12:</b> Computing \(\fNormal\) for dielectric and metallic materials in GLSL</div></center>
<aclass="target"name="roughnessremappingandclamping"> </a><aclass="target"name="materialsystem/parameterization/remapping/roughnessremappingandclamping"> </a><aclass="target"name="toc4.8.3.3"> </a><h4id="roughness-remapping-and-clamping"><aclass="header"href="#roughness-remapping-and-clamping">Roughness remapping and clamping</a></h4>
<p>
<p>The roughness set by the user, called <code>perceptualRoughness</code> here, is remapped to a perceptually linear range using the following formulation:</p>
This masking-shadowing function is not physically based, as shown in [<ahref="#citation-heitz14">Heitz14</a>], but its simplicity makes it desirable for real-time rendering.
</p><p>
In summary, our clear coat BRDF is a Cook-Torrance specular microfacet model, with a GGX normal distribution function, a Kelemen visibility function, and a Schlick Fresnel function. <ahref="#listing_kelemen">Listing 13</a> shows how trivial the GLSL implementation is.
<spanclass="line">}</span></code></pre><center><divclass="listingcaption tilde"><aclass="target"name="listing_kelemen"> </a><bstyle="font-style:normal;">Listing 13:</b> Implementation of the Kelemen visibility term in GLSL</div></center>
<p>
@@ -1097,18 +1097,18 @@ The clear coat roughness parameter is remapped and clamped in a similar way to t
<center><divclass="image"style=""><ahref="../images/material_clear_coat2.png"target="_blank"><imgclass="markdeep"src="../images/material_clear_coat2.png"/></a><center><spanclass="imagecaption"><aclass="target"name="figure_clearcoatroughness"> </a><bstyle="font-style:normal;">Figure 26:</b> Clear coat roughness varying from 0.0 (left) to 1.0 (right) with metallic set to 1.0, roughness to 0.8 and clear coat to 1.0</span></center></div></center>
</p><p>
<ahref="#listing_clearcoatbrdf">Listing 14</a> shows the GLSL implementation of the clear coat material model after remapping, parameterization and integration in the standard surface response.
The implementation of the velvet NDF is presented in <ahref="#listing_clothbrdf">listing 17</a>, optimized to properly fit in half float formats and to avoid computing a costly cotangent, relying instead on trigonometric identities. Note that we removed the Fresnel component from this BRDF.
<spanclass="line">}</span></code></pre><center><divclass="listingcaption tilde"><aclass="target"name="listing_clothbrdf"> </a><bstyle="font-style:normal;">Listing 17:</b> Implementation of Ashikhmin's velvet NDF in GLSL</div></center>
<p>
<p>In [<ahref="#citation-estevez17">Estevez17</a>] Estevez and Kulla propose a different NDF (called the “Charlie” sheen) that is based on an exponentiated sinusoidal instead of an inverted Gaussian. This NDF is appealing for several reasons: its parameterization feels more natural and intuitive, it provides a softer appearance and, as shown in equation (\ref{charlieNDF}), its implementation is simpler:</p>
[<ahref="#citation-estevez17">Estevez17</a>] also presents a new shadowing term that we omit here because of its cost. We instead rely on the visibility term from [<ahref="#citation-neubelt13">Neubelt13</a>] (shown in equation \(\ref{clothSpecularBRDF}\) above).
The implementation of this NDF is presented in <ahref="#listing_clothcharliebrdf">listing 18</a>, optimized to properly fit in half float formats.
<spanclass="line">}</span></code></pre><center><divclass="listingcaption tilde"><aclass="target"name="listing_clothcharliebrdf"> </a><bstyle="font-style:normal;">Listing 18:</b> Implementation of the “Charlie” NDF in GLSL</div></center>
@@ -1745,21 +1745,21 @@ The photometric attenuation function can be easily implemented in GLSL by adding
<spanclass="line">}</span></code></pre><center><divclass="listingcaption tilde"><aclass="target"name="listing_glslphotometricpunctuallight"> </a><bstyle="font-style:normal;">Listing 22:</b> Implementation of attenuation from photometric profiles in GLSL</div></center>
<p>
<p>The light intensity is computed CPU-side (<ahref="#listing_photometriclightintensity">listing23</a>) and depends on whether the photometric profile is used as a mask.</p>
<spanclass="line"><spanclass="hljs-comment">// The max intensity in cd comes from the IES profile</span></span>
<spanclass="line"><spanclass="hljs-type">float</span> lightIntensity = photometricLight.<spanclass="hljs-built_in">getMaxIntensity</span>() * multiplier;</span></code></pre><center><divclass="listingcaption tilde"><aclass="target"name="listing_photometriclightintensity"> </a><bstyle="font-style:normal;">Listing 23:</b> Computing the intensity of a photometric light on the CPU</div></center>
<spanclass="line"><spanclass="hljs-keyword">float</span> lightIntensity = photometricLight.getMaxIntensity() * multiplier;</span></code></pre><center><divclass="listingcaption tilde"><aclass="target"name="listing_photometriclightintensity"> </a><bstyle="font-style:normal;">Listing 23:</b> Computing the intensity of a photometric light on the CPU</div></center>
<p>
<divclass="endnote"><aclass="target"name="endnote-xarrowintensity"> </a><sup>4</sup> The XArrow profile declares a luminous intensity of 1,750 lm but a Monte-Carlo integration shows an intensity of only 350 lm.
</div>
@@ -2058,19 +2058,19 @@ In practice only 4 or 9 coefficients (i.e.: 2 or 3 bands) are enough for \(\cosT
In practice we pre-convolve \(\Lt\) with \(\cosTheta\) and pre-scale these coefficients by the basis scaling factors \(K_l^m\) so that the reconstruction code is as simple as possible in the shader:
<spanclass="line">// We can <spanclass="hljs-selector-tag">use</span> only the first <spanclass="hljs-number">2</span> bands for better performance</span>
<spanclass="line">}</span></code></pre><center><divclass="listingcaption tilde"><aclass="target"name="listing_irradiancesh"> </a><bstyle="font-style:normal;">Listing 26:</b> GLSL code to reconstruct the irradiance from the pre-scaled SH</div></center>
<p>
<p>Note that with 2 bands, the computation above becomes a single (4 \times 4) matrix-by-vector multiply.</p>
@@ -2443,7 +2443,7 @@ LD(n, \alpha) &= \frac{\sum_i^N V(l_i, n,
$$
</p><p>
These two new \(DFG\) terms simply need to replace the ones used in the implementation shown in section <ahref="#toc9.5">9.5</a>:
</p><preclass="listing tilde"><code><spanclass="line"><spanclass="hljs-type">float</span> Fc = <spanclass="hljs-built_in">pow</span>(<spanclass="hljs-number">1</span> - VoH, <spanclass="hljs-number">5.0f</span>);</span>
</p><preclass="listing tilde"><code><spanclass="line"><spanclass="hljs-keyword">float</span> Fc = <spanclass="hljs-built_in">pow</span>(<spanclass="hljs-number">1</span> - VoH, <spanclass="hljs-number">5.0f</span>);</span>
<spanclass="line">r.x += Gv * Fc;</span>
<spanclass="line">r.y += Gv;</span></code></pre><center><divclass="listingcaption tilde"><aclass="target"name="listing_multiscatteriblpreintegration"> </a><bstyle="font-style:normal;">Listing 29:</b> C++ implementation of the \(L_{DFG}\) term for multiscattering</div></center>
<p>
@@ -2507,11 +2507,11 @@ using an environment made of colored vertical stripes (skybox hidden).</span></c
<p>
<p>When sampling the IBL, the clear coat layer is calculated as a second specular lobe. This specular lobe is oriented along the view direction since we cannot reasonably integrate over the hemisphere. <ahref="#listing_clearcoatibl">Listing31</a> demonstrates this approximation in practice. It also shows the energy conservation step. It is important to note that this second specular lobe is computed exactly the same way as the main specular lobe, using the same DFG approximation.</p>
</p><preclass="listing tilde"><code><spanclass="line"><spanclass="hljs-comment">// clearCoat_NoV == shading_NoV if the clear coat layer doesn't have its own normal map</span></span>
<spanclass="line"><spanclass="hljs-type">float</span> Fc = <spanclass="hljs-built_in">F_Schlick</span>(<spanclass="hljs-number">0.04</span>, <spanclass="hljs-number">1.0</span>, clearCoat_NoV) * clearCoat;</span>
<spanclass="line"><spanclass="hljs-built_in">float</span> Fc = F_Schlick(<spanclass="hljs-number">0.04</span>, <spanclass="hljs-number">1.0</span>, clearCoat_NoV) * clearCoat;</span>
<spanclass="line"><spanclass="hljs-comment">// base layer attenuation for energy compensation</span></span>
<p>[<ahref="#citation-mcauley15">McAuley15</a>] describes a technique called “bent reflection vector”, based [<ahref="#citation-revie12">Revie12</a>]. The bent reflection vector is a rough approximation of anisotropic lighting but the alternative is to use importance sampling. This approximation is sufficiently cheap to compute and provides good results, as shown in <ahref="#figure_anisotropicibl1">figure59</a> and <ahref="#figure_anisotropicibl2">figure60</a>.</p>
@@ -2550,17 +2550,17 @@ The DG term is generated using uniform sampling as recommended in [<a href="#cit
<center><divclass="image"style=""><ahref="../images/ibl/dfg_cloth.png"target="_blank"><imgclass="markdeep"src="../images/ibl/dfg_cloth.png"/></a><center><spanclass="imagecaption"><aclass="target"name="figure_dfgclothlut"> </a><bstyle="font-style:normal;">Figure 62:</b> DFG LUT with a 3rd channel encoding the DG term of the cloth BRDF</span></center></div></center>
</p><p>
The remainder of the image-based lighting implementation follows the same steps as the implementation of regular lights, including the optional subsurface scattering term and its wrap diffuse component. Just as with the clear coat IBL implementation, we cannot integrate over the hemisphere and use the view direction as the dominant light direction to compute the wrap diffuse component.
<spanclass="line">vec4 color = evaluateLighting();</span>
<spanclass="line">color.rgb *= exposure;</span></code></pre><center><divclass="listingcaption tilde"><aclass="target"name="listing_fragmentexposure"> </a><bstyle="font-style:normal;">Listing 42:</b> Implementation of exposure in GLSL</div></center>
<p>
<p>In practice the exposure factor can be pre-computed on the CPU to save shader instructions.</p>
@@ -3466,9 +3466,9 @@ Our implementation is presented in <a href="#listing_specularcolorimpl">listing&
<spanclass="line"><spanclass="hljs-comment">// Data source:</span></span>
<spanclass="line"><spanclass="hljs-type">float</span> y = <spanclass="hljs-number">0.0f</span>;</span>
<spanclass="line"><spanclass="hljs-keyword">float</span> y = <spanclass="hljs-number">0.0f</span>;</span>
<spanclass="line"></span>
<spanclass="line"><spanclass="hljs-keyword">for</span> (<spanclass="hljs-type">size_t</span> i = <spanclass="hljs-number">0</span>; i < CIE_XYZ_COUNT; i++) {</span>
<spanclass="line"><spanclass="hljs-keyword">for</span> (<spanclass="hljs-keyword">size_t</span> i = <spanclass="hljs-number">0</span>; i < CIE_XYZ_COUNT; i++) {</span>
<spanclass="line"><spanclass="hljs-comment">// Current wavelength</span></span>
<spanclass="line"><spanclass="hljs-type">float</span> w = CIE_XYZ_START + i;</span>
<spanclass="line"><spanclass="hljs-keyword">float</span> w = CIE_XYZ_START + i;</span>
<spanclass="line"></span>
<spanclass="line"><spanclass="hljs-comment">// Find most appropriate CIE XYZ sample for the wavelength</span></span>
<spanclass="line">}</span></code></pre><center><divclass="listingcaption tilde"><aclass="target"name="listing_specularcolorimpl"> </a><bstyle="font-style:normal;">Listing 46:</b> C++ implementation to compute the base color of a metallic surface from spectral data</div></center>
<spanclass="line">}</span></code></pre><center><divclass="listingcaption tilde">C++ implementation of a Hammersley sequence generator</div></center>
<aclass="target"name="precomputinglforimage-basedlighting"> </a><aclass="target"name="annex/precomputinglforimage-basedlighting"> </a><aclass="target"name="toc9.5"> </a><h2id="precomputing-l-for-image-based-lighting"><aclass="header"href="#precomputing-l-for-image-based-lighting">Precomputing L for image-based lighting</a></h2>
<p>
<p>The term ( L_{DFG} ) is only dependent on ( \NoV ). Below, the normal is arbitrarily set to ( n=\left[0, 0, 1\right] ) and (v) is chosen to satisfy ( \NoV ). The vector ( h_i ) is the ( D_{GGX}(\alpha) ) important direction sample (i).</p>
<spanclass="line"><spanclass="hljs-type">float3</span><spanclass="hljs-variable">H</span><spanclass="hljs-operator">=</span> importanceSampleGGX(Xi, a, N);</span>
<spanclass="line"><spanclass="hljs-type">float3</span><spanclass="hljs-variable">L</span><spanclass="hljs-operator">=</span><spanclass="hljs-number">2.0f</span> * dot(V, H) * H - V;</span>
<spanclass="line">float2 r =<spanclass="hljs-number">0.0</span>f;</span>
<spanclass="line"><spanclass="hljs-keyword">for</span> (<spanclass="hljs-type">uint</span>i =<spanclass="hljs-number">0</span>; i < sampleCount; i++) {</span>
<spanclass="line">float2 Xi = hammersley(i, sampleCount);</span>
<spanclass="line">float3 H = importanceSampleGGX(Xi, a, N);</span>
<spanclass="line">float3 L =<spanclass="hljs-number">2.0</span>f *<spanclass="hljs-built_in">dot</span>(V, H) * H - V;</span>
<ahref="#listing_nonnormalizedshbasis">Listing 47</a> shows the C++ code to compute the non-normalized SH basis \(\frac{y^m_l(s)}{\sqrt{2} K^m_l}\):
</p><preclass="listing tilde"><code><spanclass="line"><spanclass="hljs-function"><spanclass="hljs-type">static</span><spanclass="hljs-keyword">inline</span><spanclass="hljs-type">size_t</span><spanclass="hljs-title">SHindex</span><spanclass="hljs-params">(<spanclass="hljs-type">ssize_t</span> m, <spanclass="hljs-type">size_t</span> l)</span></span>{</span>
</p><preclass="listing tilde"><code><spanclass="line"><spanclass="hljs-function"><spanclass="hljs-keyword">static</span><spanclass="hljs-keyword">inline</span><spanclass="hljs-keyword">size_t</span><spanclass="hljs-title">SHindex</span><spanclass="hljs-params">(<spanclass="hljs-keyword">ssize_t</span> m, <spanclass="hljs-keyword">size_t</span> l)</span></span>{</span>
<spanclass="line"><spanclass="hljs-keyword">return</span> l * (l + <spanclass="hljs-number">1</span>) + m;</span>
<spanclass="line"><spanclass="hljs-keyword">for</span> (<spanclass="hljs-type">ssize_t</span> l = <spanclass="hljs-number">1</span>; l < numBands; l++) {</span>
<spanclass="line"><spanclass="hljs-keyword">for</span> (<spanclass="hljs-keyword">ssize_t</span> l = <spanclass="hljs-number">1</span>; l < numBands; l++) {</span>
<spanclass="line"><spanclass="hljs-comment">// l == m+1</span></span>
<spanclass="line"> SHb[<spanclass="hljs-built_in">SHindex</span>(-m, m + <spanclass="hljs-number">1</span>)] = Pml_1;</span>
<spanclass="line"> SHb[<spanclass="hljs-built_in">SHindex</span>( m, m + <spanclass="hljs-number">1</span>)] = Pml_1;</span>
<spanclass="line"><spanclass="hljs-keyword">for</span> (<spanclass="hljs-type">ssize_t</span> l = m + <spanclass="hljs-number">2</span>; l < numBands; l++) {</span>
<spanclass="line"><spanclass="hljs-type">double</span> Pml = ((<spanclass="hljs-number">2</span> * l - <spanclass="hljs-number">1</span>) * Pml_1 * s.z - (l + m - <spanclass="hljs-number">1</span>) * Pml_2)</span>
<spanclass="line"> SHb[SHindex(-m, m + <spanclass="hljs-number">1</span>)] = Pml_1;</span>
<spanclass="line"> SHb[SHindex( m, m + <spanclass="hljs-number">1</span>)] = Pml_1;</span>
<spanclass="line"><spanclass="hljs-keyword">for</span> (<spanclass="hljs-keyword">ssize_t</span> l = m + <spanclass="hljs-number">2</span>; l < numBands; l++) {</span>
<spanclass="line"><spanclass="hljs-keyword">double</span> Pml = ((<spanclass="hljs-number">2</span> * l - <spanclass="hljs-number">1</span>) * Pml_1 * s.z - (l + m - <spanclass="hljs-number">1</span>) * Pml_2)</span>
<spanclass="line"> SHb[SHindex( m, l)] *= Cm;</span>
<spanclass="line"> }</span>
<spanclass="line"><spanclass="hljs-type">double</span> Cm1 = Cm * s.x - Sm * s.y;</span>
<spanclass="line"><spanclass="hljs-type">double</span> Sm1 = Sm * s.x + Cm * s.y;</span>
<spanclass="line"><spanclass="hljs-keyword">double</span> Cm1 = Cm * s.x - Sm * s.y;</span>
<spanclass="line"><spanclass="hljs-keyword">double</span> Sm1 = Sm * s.x + Cm * s.y;</span>
<spanclass="line"> Cm = Cm1;</span>
<spanclass="line"> Sm = Sm1;</span>
<spanclass="line"> }</span>
@@ -3979,10 +3979,10 @@ $$\begin{equation}
\end{equation}$$
</p><p>
Here is the C++ code to compute \(\hat{C}_l\):
</p><preclass="listing tilde"><code><spanclass="line"><spanclass="hljs-function"><spanclass="hljs-type">static</span><spanclass="hljs-type">double</span><spanclass="hljs-title">factorial</span><spanclass="hljs-params">(<spanclass="hljs-type">size_t</span> n, <spanclass="hljs-type">size_t</span> d = <spanclass="hljs-number">1</span>)</span></span>;</span>
</p><preclass="listing tilde"><code><spanclass="line"><spanclass="hljs-function"><spanclass="hljs-keyword">static</span><spanclass="hljs-keyword">double</span><spanclass="hljs-title">factorial</span><spanclass="hljs-params">(<spanclass="hljs-keyword">size_t</span> n, <spanclass="hljs-keyword">size_t</span> d = <spanclass="hljs-number">1</span>)</span></span>;</span>
<spanclass="line"></span>
<spanclass="line"><spanclass="hljs-comment">// < cos(theta) > SH coefficients pre-multiplied by 1 / K(0,l)</span></span>
<spanclass="line"><spanclass="hljs-function"><spanclass="hljs-type">double</span><spanclass="hljs-title">factorial</span><spanclass="hljs-params">(<spanclass="hljs-type">size_t</span> n, <spanclass="hljs-type">size_t</span> d )</span></span>{</span>
<spanclass="line"> d = std::<spanclass="hljs-built_in">max</span>(<spanclass="hljs-built_in">size_t</span>(<spanclass="hljs-number">1</span>), d);</span>
<spanclass="line"> n = std::<spanclass="hljs-built_in">max</span>(<spanclass="hljs-built_in">size_t</span>(<spanclass="hljs-number">1</span>), n);</span>
<spanclass="line"><spanclass="hljs-type">double</span> r = <spanclass="hljs-number">1.0</span>;</span>
<spanclass="line"><spanclass="hljs-function"><spanclass="hljs-keyword">double</span><spanclass="hljs-title">factorial</span><spanclass="hljs-params">(<spanclass="hljs-keyword">size_t</span> n, <spanclass="hljs-keyword">size_t</span> d )</span></span>{</span>
<spanclass="line"> d = <spanclass="hljs-built_in">std</span>::max(<spanclass="hljs-keyword">size_t</span>(<spanclass="hljs-number">1</span>), d);</span>
<spanclass="line"> n = <spanclass="hljs-built_in">std</span>::max(<spanclass="hljs-keyword">size_t</span>(<spanclass="hljs-number">1</span>), n);</span>
<spanclass="line"><spanclass="hljs-keyword">double</span> r = <spanclass="hljs-number">1.0</span>;</span>
<labelid="sidebar-toggle"class="icon-button"for="sidebar-toggle-anchor"title="Toggle Table of Contents"aria-label="Toggle Table of Contents"aria-controls="sidebar">
<iclass="fa fa-bars"></i>
</label>
<!-- Filament: disable themes because the markdeep part does not look good for dark themes -->
<inputtype="search"id="searchbar"name="searchbar"placeholder="Search this book ..."aria-controls="searchresults-outer"aria-describedby="searchresults-header">
<labelid="sidebar-toggle"class="icon-button"for="sidebar-toggle-anchor"title="Toggle Table of Contents"aria-label="Toggle Table of Contents"aria-controls="sidebar">
<iclass="fa fa-bars"></i>
</label>
<!-- Filament: disable themes because the markdeep part does not look good for dark themes -->
<inputtype="search"id="searchbar"name="searchbar"placeholder="Search this book ..."aria-controls="searchresults-outer"aria-describedby="searchresults-header">
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.