<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Opengl on CGLab</title><link>https://blog.cglab.top/tags/opengl/</link><description>Recent content in Opengl on CGLab</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Sat, 21 Mar 2026 14:13:19 +0800</lastBuildDate><atom:link href="https://blog.cglab.top/tags/opengl/index.xml" rel="self" type="application/rss+xml"/><item><title>【OpenGL】计算着色器入门</title><link>https://blog.cglab.top/posts/opengl-compute-shader-1/</link><pubDate>Sat, 21 Mar 2026 14:13:19 +0800</pubDate><guid>https://blog.cglab.top/posts/opengl-compute-shader-1/</guid><description>&lt;h2 id="摘要"&gt;摘要&lt;/h2&gt;
&lt;p&gt;OpenGL4.3 引入了 &lt;strong&gt;计算着色器（Compute Shader）&lt;/strong&gt; 这使得我们可以在 GPU 上直接利用 GPU 的并行计算能力来处理非图形任务，标志着 OpenGL正式具备了 GPGPU（通用图形处理器计算）的能力
这篇文章将会以例子的角度来介绍如何使用 Compute Shader&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="摘要">摘要</h2>
<p>OpenGL4.3 引入了 <strong>计算着色器（Compute Shader）</strong> 这使得我们可以在 GPU 上直接利用 GPU 的并行计算能力来处理非图形任务，标志着 OpenGL正式具备了 GPGPU（通用图形处理器计算）的能力
这篇文章将会以例子的角度来介绍如何使用 Compute Shader</p>
<h2 id="第一个compute-shader">第一个Compute Shader</h2>
<p>在这个例子中，我们将使用 Compute Shader 来将大小为1024的数组中所有的元素乘以2</p>
<h3 id="1-编写-compute-shader">1. 编写 compute shader</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-glsl" data-lang="glsl"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="cp">#version 460 core</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1">// 定义本地工作组大小 (Local Work Group Size)</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1">// 这里我们让每个小组处理 64 个元素</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="n">layout</span> <span class="p">(</span><span class="n">local_size_x</span> <span class="o">=</span> <span class="mi">64</span><span class="p">,</span> <span class="n">local_size_y</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> <span class="n">local_size_z</span> <span class="o">=</span> <span class="mi">1</span><span class="p">)</span> <span class="k">in</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1">// 定义SSBO</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="n">layout</span> <span class="p">(</span><span class="n">std430</span><span class="p">,</span> <span class="n">binding</span> <span class="o">=</span> <span class="mo">0</span><span class="p">)</span> <span class="n">buffer</span> <span class="n">DataBuffer</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="k">float</span> <span class="n">data</span><span class="p">[];</span> 
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">void</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="c1">// 获取当前线程在全局任务中的索引</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="n">uint</span> <span class="n">index</span> <span class="o">=</span> <span class="n">gl_GlobalInvocationID</span><span class="p">.</span><span class="n">x</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl">    <span class="c1">// 简单地将数组中的每个值乘以 2</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="n">data</span><span class="p">[</span><span class="n">index</span><span class="p">]</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="n">index</span><span class="p">]</span> <span class="o">*</span> <span class="mf">2.0</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>这涉及了非常多的概念，我们先从第二行代码开始：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-glsl" data-lang="glsl"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">layout</span> <span class="p">(</span><span class="n">local_size_x</span> <span class="o">=</span> <span class="mi">64</span><span class="p">,</span> <span class="n">local_size_y</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> <span class="n">local_size_z</span> <span class="o">=</span> <span class="mi">1</span><span class="p">)</span> <span class="k">in</span><span class="p">;</span>
</span></span></code></pre></div><p>先介绍两个概念：</p>
<ul>
<li><strong>全局工作组(Global Work Groups)</strong>：它直接对应了 NVIDA GPU 中的 SM （Streaming Multiprocessor）,一个 SM 中可以有多个 Warp。而 Warp 是什么请看本地工作组，而 SM 是什么呢？我们后续再介绍。</li>
<li><strong>本地工作组(Local Work Groups)</strong>：它直接对应了 GPU 中的线程，在 NVIDIA 架构中通常将 32 个线程组成一个线程块称为 Warp ,在同一个 Warp 中，所有的线程会执行相同的指令，这种运行模式叫做 SIMT(Single Instruction, Multiple Threads),例如当执行 加法指令的时候，一个 warp 中的指令解码器会调度该 warp 中的所有线程执行加法指令。</li>
</ul>
<p>这行代码定义了 <strong>本地工作组</strong> 的大小，它指定了在一个 SM 中使用多少线程来处理数据，这里将使用 64 个线程也就是 2 个 Warp 来处理一批数据，我们计划使用16个全局工作组，每个全局工作组中使用64个本地工作组进行计算，也就是使用 $16\times64=1024$ 个线程来处理数据, 这样我们每个数据都会被映射到一个线程上去。
而 <code>local_size_x = 64, local_size_y = 1, local_size_z = 1</code>指定了如何映射数据，这是纯软件上的概念，GPU 本身都是一堆并行执行的线程，这样做的好处是方便你处理不同维度的数据，让你省去复杂的下标转换计算。</p>
<ul>
<li>1D (x=64, y=1, z=1): 适合处理线性数据。比如数组运算、顶点缓冲区修改、简单的粒子列表。</li>
<li>2D (x=16, y=16, z=1): 适合处理平面数据。比如图像处理、高斯模糊、地形高度图。</li>
<li>3D (x=8, y=8, z=4): 适合处理空间数据。比如 3D 纹理、体素（Voxel）计算、流体模拟。
内线程的总数是 $x \times y \times z$ ，这里是
$$64 \times 1 \times 1 = 64$$个线程</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-glsl" data-lang="glsl"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">layout</span> <span class="p">(</span><span class="n">std430</span><span class="p">,</span> <span class="n">binding</span> <span class="o">=</span> <span class="mo">0</span><span class="p">)</span> <span class="n">buffer</span> <span class="n">DataBuffer</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">    <span class="k">float</span> <span class="n">data</span><span class="p">[];</span> 
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>这里我们定义了一个 SSBO 用于与 CPU 进行数据交换，这不是 compute shader 中的概念这里不做过多介绍。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-glsl" data-lang="glsl"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">uint</span> <span class="n">index</span> <span class="o">=</span> <span class="n">gl_GlobalInvocationID</span><span class="p">.</span><span class="n">x</span><span class="p">;</span>
</span></span></code></pre></div><p>这里我们获取线程在整个任务中的唯一绝对的索引，<code>gl_GlobalInvocationID</code>是 opengl4.3 中新增的一个内建变量, 还有其他的内建变量</p>
<ul>
<li><code>gl_LocalInvocationID </code>: 线程在当前“本地组”内的坐标。(sm中的哪个线程)</li>
<li><code>gl_WorkGroupID</code>:当前“本地组”在“全局范围”内的索引。(哪个sm)</li>
<li><code>gl_WorkGroupSize</code>: 当前“本地组”的大小。(sm中的线程数)</li>
<li><code>gl_GlobalInvocationID</code>:当前线程在“全局范围”内的索引。(全局中的哪个线程)
他们之间有如下关系

$$GlobalID = WorkGroupID \times WorkGroupSize + LocalInvocationID$$</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-glsl" data-lang="glsl"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">data</span><span class="p">[</span><span class="n">index</span><span class="p">]</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="n">index</span><span class="p">]</span> <span class="o">*</span> <span class="mf">2.0</span><span class="p">;</span>
</span></span></code></pre></div><p>这里我们将当前线程中的数据乘以2，因为我们的数据大小为1024，线程数量也是1024，每个线程都会被分配到一个数据，所以一个全局线程id就对应了一个数据，将这个数据乘以2</p>
<h3 id="2-创建-compute-shader">2. 创建 compute shader</h3>
<p>创建一个 compute shader 的方式和创建普通 shader 的方式非常类似，但是不需要将其 link 到渲染管线上的 shader program 中去，将着色器类型改为<code>GL_COMPUTE_SHADER</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">GLuint</span> <span class="n">computeShader</span> <span class="o">=</span> <span class="n">glCreateShader</span><span class="p">(</span><span class="n">GL_COMPUTE_SHADER</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="n">glShaderSource</span><span class="p">(</span><span class="n">computeShader</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">shaderCode</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="n">glCompileShader</span><span class="p">(</span><span class="n">computeShader</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="n">GLuint</span> <span class="n">computeProgram</span> <span class="o">=</span> <span class="n">glCreateProgram</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="n">glAttachShader</span><span class="p">(</span><span class="n">computeProgram</span><span class="p">,</span> <span class="n">computeShader</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="n">glLinkProgram</span><span class="p">(</span><span class="n">computeProgram</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="3-创建数据">3. 创建数据</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="ln">1</span><span class="cl"><span class="kt">float</span> <span class="n">myData</span><span class="p">[</span><span class="mi">1024</span><span class="p">];</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">1024</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="n">myData</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="kt">float</span><span class="p">)</span><span class="n">i</span><span class="p">;</span>
</span></span></code></pre></div><h3 id="4-创建-ssbo">4. 创建 SSBO</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">GLuint</span> <span class="n">ssbo</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="n">glGenBuffers</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ssbo</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="n">glBindBuffer</span><span class="p">(</span><span class="n">GL_SHADER_STORAGE_BUFFER</span><span class="p">,</span> <span class="n">ssbo</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1">// 将数据传给 GPU
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="n">glBufferData</span><span class="p">(</span><span class="n">GL_SHADER_STORAGE_BUFFER</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">myData</span><span class="p">),</span> <span class="n">myData</span><span class="p">,</span> <span class="n">GL_DYNAMIC_DRAW</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="c1">// 绑定到 binding = 0，与着色器中的声明对应
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="n">glBindBufferBase</span><span class="p">(</span><span class="n">GL_SHADER_STORAGE_BUFFER</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">ssbo</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="5-启动计算">5. 启动计算</h3>
<p>这是最关键的一步，你需要决定全局工作组的数量。我们的本地工作组是一组64个线程，所以我们为了让每个线程只处理一次数据，全局工作组数量应该为 $\frac{1024}{64}=16$</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">glUseProgram</span><span class="p">(</span><span class="n">computeProgram</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1">// 启动 16 个小组，每组 64 个线程，总计 1024 个线程
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="n">glDispatchCompute</span><span class="p">(</span><span class="mi">16</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span> 
</span></span><span class="line"><span class="ln">4</span><span class="cl">
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1">// 强制同步：确保 GPU 计算完成，且结果已写回显存，才允许后续读取
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="n">glMemoryBarrier</span><span class="p">(</span><span class="n">GL_SHADER_STORAGE_BARRIER_BIT</span><span class="p">);</span>
</span></span></code></pre></div><p>其中<code>glDispatchCompute</code> 是启动计算，参数为全局工作组(sm)的数量，它也有三个维度，他们的关系也是乘法关系总的线程数等于全局工作组不同分量的数量乘以本地工作组不同分量的数量，也是便于程序员使用。
<code>glMemoryBarrier</code> 是一个同步函数，用于等待 GPU 计算完成，并确保结果已写回显存，才允许后续读取。因为 GPU 和 CPU 是异步的，如果你不等待 GPU 计算完把数据写回，CPU就会读取到错误的数据。
<code>glMemoryBarrier</code> 函数的参数是一个位掩码，用于指定等待的资源类型。在这里，我们传入<code>GL_SHADER_STORAGE_BARRIER_BIT</code>，表示等待 SSBO 的数据写入完成。</p>
<h3 id="6-读取数据">6. 读取数据</h3>
<p>最后我们等待 GPU 计算完成，读取数据就行了</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="ln">1</span><span class="cl"><span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">N_</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">        <span class="n">ImGui</span><span class="o">::</span><span class="n">Text</span><span class="p">(</span><span class="s">&#34;%f&#34;</span><span class="p">,</span> <span class="n">myData</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="p">}</span>
</span></span></code></pre></div>]]></content:encoded></item><item><title>【Rendering】延迟渲染基于OpenGL</title><link>https://blog.cglab.top/posts/rendering-deffered-rendering/</link><pubDate>Thu, 19 Mar 2026 21:15:53 +0800</pubDate><guid>https://blog.cglab.top/posts/rendering-deffered-rendering/</guid><description>&lt;p&gt;&lt;strong&gt;延迟渲染（Deferred Rendering）&lt;/strong&gt; 是一种专为高效处理海量光源而设计的渲染技术，旨在解决传统&lt;strong&gt;前向渲染（Forward Rendering）&lt;/strong&gt; 在多光源场景下的性能瓶颈。在前向渲染中，每个物体在绘制时都必须遍历场景内的所有光源并逐一计算光照。这意味着，若场景中有 $n$ 个几何片段和 $m$ 个光源，计算复杂度将达到 $O(n \times m)$。当光源数量增加时，渲染开销会呈线性爆发式增长。&lt;/p&gt;</description><content:encoded><![CDATA[<p><strong>延迟渲染（Deferred Rendering）</strong> 是一种专为高效处理海量光源而设计的渲染技术，旨在解决传统<strong>前向渲染（Forward Rendering）</strong> 在多光源场景下的性能瓶颈。在前向渲染中，每个物体在绘制时都必须遍历场景内的所有光源并逐一计算光照。这意味着，若场景中有 $n$ 个几何片段和 $m$ 个光源，计算复杂度将达到 $O(n \times m)$。当光源数量增加时，渲染开销会呈线性爆发式增长。</p>
<p>为了打破这种耦合，延迟渲染将渲染流程解构为两个核心阶段：</p>
<h4 id="1-几何处理阶段geometry-pass">1. 几何处理阶段（Geometry Pass）</h4>
<p>在该阶段，场景仅进行一次常规渲染，但不立即执行复杂的光照计算。取而代之的是，我们将物体的各项几何与材质属性——如 <strong>反射率（Albedo</strong>、<strong>法线（Normal）</strong>、<strong>金属度（Metalic）</strong>、<strong>粗糙度（Roughness）</strong>、<strong>自发光（Emissive）</strong>、 <strong>环境光遮蔽（AO）</strong> 等——提取出来。</p>
<p>这些数据被渲染并存储在一系列高度集成的纹理缓冲区中，即 <strong>G-Buffer</strong>。由于 G-Buffer 只记录最终通过深度测试、对摄像机可见的片段信息，这为后续阶段剔除无效计算奠定了基础。下图展示了部分G-Buffer
<img loading="lazy" src="/images/opengl/defferred_rendering.png"></p>
<h4 id="2-光照处理阶段lighting-pass">2. 光照处理阶段（Lighting Pass）</h4>
<p>在这一阶段，渲染器不再关注场景中有多少个复杂的模型几何体，而是直接遍历 G-Buffer 中的像素。通过读取每个像素点的几何与材质“档案”，结合场景中的光源信息进行统一的着色计算。</p>
<p>此时，计算复杂度由原来的“物体数量 $\times$ 光源数量”优化为<strong>屏幕像素数量</strong>。这种将几何处理与光照计算分离的设计，使得在场景中实时渲染成百上千个光源成为可能。</p>
<p>下面这幅图片很好地展示了延迟着色法的整个过程：</p>
<p><img loading="lazy" src="/images/opengl/defferred_rendering1.png"></p>
<h2 id="opengl-实现">OpenGL 实现</h2>
<p>由于G-Buffer是由多个纹理组成的，所以我们需要使用多渲染目标(Multiple Render Targets)来在一个阶段（Pass）之内渲染多个颜色缓冲</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kt">void</span> <span class="n">AdvanceLightingState</span><span class="o">::</span><span class="n">CreateGBuffer</span><span class="p">(</span><span class="kt">int</span> <span class="n">width</span><span class="p">,</span> <span class="kt">int</span> <span class="n">height</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">    <span class="n">resources_</span><span class="p">.</span><span class="n">g_buffer_width</span> <span class="o">=</span> <span class="n">width</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="n">resources_</span><span class="p">.</span><span class="n">g_buffer_height</span> <span class="o">=</span> <span class="n">height</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="n">glCreateFramebuffers</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">resources_</span><span class="p">.</span><span class="n">g_buffer_fbo</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="n">glCreateTextures</span><span class="p">(</span><span class="n">GL_TEXTURE_2D</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">resources_</span><span class="p">.</span><span class="n">g_position_texture</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="n">glTextureStorage2D</span><span class="p">(</span><span class="n">resources_</span><span class="p">.</span><span class="n">g_position_texture</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">GL_RGBA16F</span><span class="p">,</span> <span class="n">width</span><span class="p">,</span> <span class="n">height</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="n">glTextureParameteri</span><span class="p">(</span><span class="n">resources_</span><span class="p">.</span><span class="n">g_position_texture</span><span class="p">,</span> <span class="n">GL_TEXTURE_MIN_FILTER</span><span class="p">,</span> <span class="n">GL_NEAREST</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="n">glTextureParameteri</span><span class="p">(</span><span class="n">resources_</span><span class="p">.</span><span class="n">g_position_texture</span><span class="p">,</span> <span class="n">GL_TEXTURE_MAG_FILTER</span><span class="p">,</span> <span class="n">GL_NEAREST</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="n">glTextureParameteri</span><span class="p">(</span><span class="n">resources_</span><span class="p">.</span><span class="n">g_position_texture</span><span class="p">,</span> <span class="n">GL_TEXTURE_WRAP_S</span><span class="p">,</span> <span class="n">GL_CLAMP_TO_EDGE</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="n">glTextureParameteri</span><span class="p">(</span><span class="n">resources_</span><span class="p">.</span><span class="n">g_position_texture</span><span class="p">,</span> <span class="n">GL_TEXTURE_WRAP_T</span><span class="p">,</span> <span class="n">GL_CLAMP_TO_EDGE</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="n">glNamedFramebufferTexture</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">        <span class="n">resources_</span><span class="p">.</span><span class="n">g_buffer_fbo</span><span class="p">,</span> <span class="n">GL_COLOR_ATTACHMENT0</span><span class="p">,</span> <span class="n">resources_</span><span class="p">.</span><span class="n">g_position_texture</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span><span class="c1">// 将g_position_texture绑定到g_buffer_fbo的GL_COLOR_ATTACHMENT0纹理槽
</span></span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="n">glCreateTextures</span><span class="p">(</span><span class="n">GL_TEXTURE_2D</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">resources_</span><span class="p">.</span><span class="n">g_normal_texture</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="n">glTextureStorage2D</span><span class="p">(</span><span class="n">resources_</span><span class="p">.</span><span class="n">g_normal_texture</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">GL_RGBA16F</span><span class="p">,</span> <span class="n">width</span><span class="p">,</span> <span class="n">height</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="n">glTextureParameteri</span><span class="p">(</span><span class="n">resources_</span><span class="p">.</span><span class="n">g_normal_texture</span><span class="p">,</span> <span class="n">GL_TEXTURE_MIN_FILTER</span><span class="p">,</span> <span class="n">GL_NEAREST</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="n">glTextureParameteri</span><span class="p">(</span><span class="n">resources_</span><span class="p">.</span><span class="n">g_normal_texture</span><span class="p">,</span> <span class="n">GL_TEXTURE_MAG_FILTER</span><span class="p">,</span> <span class="n">GL_NEAREST</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">    <span class="n">glTextureParameteri</span><span class="p">(</span><span class="n">resources_</span><span class="p">.</span><span class="n">g_normal_texture</span><span class="p">,</span> <span class="n">GL_TEXTURE_WRAP_S</span><span class="p">,</span> <span class="n">GL_CLAMP_TO_EDGE</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">    <span class="n">glTextureParameteri</span><span class="p">(</span><span class="n">resources_</span><span class="p">.</span><span class="n">g_normal_texture</span><span class="p">,</span> <span class="n">GL_TEXTURE_WRAP_T</span><span class="p">,</span> <span class="n">GL_CLAMP_TO_EDGE</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">    <span class="n">glNamedFramebufferTexture</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">        <span class="n">resources_</span><span class="p">.</span><span class="n">g_buffer_fbo</span><span class="p">,</span> <span class="n">GL_COLOR_ATTACHMENT1</span><span class="p">,</span> <span class="n">resources_</span><span class="p">.</span><span class="n">g_normal_texture</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span> <span class="c1">// 将法向量纹理绑定到g_buffer_fbo的COLOR_ATTACHMENT1纹理槽
</span></span></span><span class="line"><span class="ln">25</span><span class="cl">
</span></span><span class="line"><span class="ln">26</span><span class="cl">    <span class="n">glCreateTextures</span><span class="p">(</span><span class="n">GL_TEXTURE_2D</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">resources_</span><span class="p">.</span><span class="n">g_albedo_spec_texture</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">    <span class="n">glTextureStorage2D</span><span class="p">(</span><span class="n">resources_</span><span class="p">.</span><span class="n">g_albedo_spec_texture</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">GL_RGBA8</span><span class="p">,</span> <span class="n">width</span><span class="p">,</span> <span class="n">height</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">    <span class="n">glTextureParameteri</span><span class="p">(</span><span class="n">resources_</span><span class="p">.</span><span class="n">g_albedo_spec_texture</span><span class="p">,</span> <span class="n">GL_TEXTURE_MIN_FILTER</span><span class="p">,</span> <span class="n">GL_NEAREST</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">    <span class="n">glTextureParameteri</span><span class="p">(</span><span class="n">resources_</span><span class="p">.</span><span class="n">g_albedo_spec_texture</span><span class="p">,</span> <span class="n">GL_TEXTURE_MAG_FILTER</span><span class="p">,</span> <span class="n">GL_NEAREST</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">    <span class="n">glTextureParameteri</span><span class="p">(</span><span class="n">resources_</span><span class="p">.</span><span class="n">g_albedo_spec_texture</span><span class="p">,</span> <span class="n">GL_TEXTURE_WRAP_S</span><span class="p">,</span> <span class="n">GL_CLAMP_TO_EDGE</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">    <span class="n">glTextureParameteri</span><span class="p">(</span><span class="n">resources_</span><span class="p">.</span><span class="n">g_albedo_spec_texture</span><span class="p">,</span> <span class="n">GL_TEXTURE_WRAP_T</span><span class="p">,</span> <span class="n">GL_CLAMP_TO_EDGE</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">    <span class="n">glNamedFramebufferTexture</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">        <span class="n">resources_</span><span class="p">.</span><span class="n">g_buffer_fbo</span><span class="p">,</span> <span class="n">GL_COLOR_ATTACHMENT2</span><span class="p">,</span> <span class="n">resources_</span><span class="p">.</span><span class="n">g_albedo_spec_texture</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span> <span class="c1">// 将反射率和高光纹理绑定到g_buffer_fbo的GL_COLOR_ATTACHMENT2纹理槽
</span></span></span><span class="line"><span class="ln">34</span><span class="cl">
</span></span><span class="line"><span class="ln">35</span><span class="cl">    <span class="n">glCreateRenderbuffers</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">resources_</span><span class="p">.</span><span class="n">g_depth_rbo</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl">    <span class="n">glNamedRenderbufferStorage</span><span class="p">(</span><span class="n">resources_</span><span class="p">.</span><span class="n">g_depth_rbo</span><span class="p">,</span> <span class="n">GL_DEPTH_COMPONENT24</span><span class="p">,</span> <span class="n">width</span><span class="p">,</span> <span class="n">height</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">    <span class="n">glNamedFramebufferRenderbuffer</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl">        <span class="n">resources_</span><span class="p">.</span><span class="n">g_buffer_fbo</span><span class="p">,</span> <span class="n">GL_DEPTH_ATTACHMENT</span><span class="p">,</span> <span class="n">GL_RENDERBUFFER</span><span class="p">,</span> <span class="n">resources_</span><span class="p">.</span><span class="n">g_depth_rbo</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl">
</span></span><span class="line"><span class="ln">40</span><span class="cl">    <span class="n">GLenum</span> <span class="n">attachments</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="n">GL_COLOR_ATTACHMENT0</span><span class="p">,</span> <span class="n">GL_COLOR_ATTACHMENT1</span><span class="p">,</span> <span class="n">GL_COLOR_ATTACHMENT2</span><span class="p">};</span>
</span></span><span class="line"><span class="ln">41</span><span class="cl">    <span class="n">glNamedFramebufferDrawBuffers</span><span class="p">(</span><span class="n">resources_</span><span class="p">.</span><span class="n">g_buffer_fbo</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="n">attachments</span><span class="p">);</span> <span class="c1">//out 0 = GL_COLOR_ATTACHMENT0 , out  1 = GL_COLOR_ATTACHMENT1, out  2 = GL_COLOR_ATTACHMENT2
</span></span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>在 Fragment Shader 中，我们需要将不同的数据输出到对应的 layout(location = n) 中。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-glsl" data-lang="glsl"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="cp">#version 460 core</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="n">layout</span><span class="p">(</span><span class="n">location</span> <span class="o">=</span> <span class="mo">0</span><span class="p">)</span> <span class="k">in</span> <span class="k">vec3</span> <span class="n">a_Position</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="n">layout</span><span class="p">(</span><span class="n">location</span> <span class="o">=</span> <span class="mi">1</span><span class="p">)</span> <span class="k">in</span> <span class="k">vec3</span> <span class="n">a_Normal</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="n">layout</span><span class="p">(</span><span class="n">location</span> <span class="o">=</span> <span class="mi">2</span><span class="p">)</span> <span class="k">in</span> <span class="k">vec2</span> <span class="n">a_TexCoord</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">uniform</span> <span class="n">mat4</span> <span class="n">u_model</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">uniform</span> <span class="n">mat4</span> <span class="n">u_view</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="k">uniform</span> <span class="n">mat4</span> <span class="n">u_projection</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">out</span> <span class="k">vec3</span> <span class="n">v_FragPos</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">out</span> <span class="k">vec3</span> <span class="n">v_Normal</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">out</span> <span class="k">vec2</span> <span class="n">v_TexCoord</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">void</span> <span class="n">main</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="k">vec4</span> <span class="n">world_pos</span> <span class="o">=</span> <span class="n">u_model</span> <span class="o">*</span> <span class="k">vec4</span><span class="p">(</span><span class="n">a_Position</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="n">v_FragPos</span> <span class="o">=</span> <span class="n">world_pos</span><span class="p">.</span><span class="n">xyz</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="n">v_Normal</span> <span class="o">=</span> <span class="n">mat3</span><span class="p">(</span><span class="n">transpose</span><span class="p">(</span><span class="n">inverse</span><span class="p">(</span><span class="n">u_model</span><span class="p">)))</span> <span class="o">*</span> <span class="n">a_Normal</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="n">v_TexCoord</span> <span class="o">=</span> <span class="n">a_TexCoord</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl">    <span class="n">gl_Position</span> <span class="o">=</span> <span class="n">u_projection</span> <span class="o">*</span> <span class="n">u_view</span> <span class="o">*</span> <span class="n">world_pos</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-glsl" data-lang="glsl"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="cp">#version 460 core</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="k">in</span> <span class="k">vec3</span> <span class="n">v_FragPos</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="k">in</span> <span class="k">vec3</span> <span class="n">v_Normal</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">in</span> <span class="k">vec2</span> <span class="n">v_TexCoord</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="n">layout</span><span class="p">(</span><span class="n">location</span> <span class="o">=</span> <span class="mo">0</span><span class="p">)</span> <span class="k">out</span> <span class="k">vec4</span> <span class="n">g_Position</span><span class="p">;</span> <span class="c1">// 输出到0号纹理槽</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="n">layout</span><span class="p">(</span><span class="n">location</span> <span class="o">=</span> <span class="mi">1</span><span class="p">)</span> <span class="k">out</span> <span class="k">vec4</span> <span class="n">g_Normal</span><span class="p">;</span> <span class="c1">//  输出到1号纹理槽</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="n">layout</span><span class="p">(</span><span class="n">location</span> <span class="o">=</span> <span class="mi">2</span><span class="p">)</span> <span class="k">out</span> <span class="k">vec4</span> <span class="n">g_AlbedoSpec</span><span class="p">;</span> <span class="c1">//  输出到2号纹理槽</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">uniform</span> <span class="k">sampler2D</span> <span class="n">u_AlbedoTexture</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">void</span> <span class="n">main</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="n">g_Position</span> <span class="o">=</span> <span class="k">vec4</span><span class="p">(</span><span class="n">v_FragPos</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">    <span class="n">g_Normal</span> <span class="o">=</span> <span class="k">vec4</span><span class="p">(</span><span class="n">normalize</span><span class="p">(</span><span class="n">v_Normal</span><span class="p">)</span> <span class="o">*</span> <span class="mf">0.5</span> <span class="o">+</span> <span class="mf">0.5</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="n">g_AlbedoSpec</span><span class="p">.</span><span class="n">rgb</span> <span class="o">=</span> <span class="n">texture</span><span class="p">(</span><span class="n">u_AlbedoTexture</span><span class="p">,</span> <span class="n">v_TexCoord</span><span class="p">).</span><span class="n">rgb</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="n">g_AlbedoSpec</span><span class="p">.</span><span class="n">a</span> <span class="o">=</span> <span class="mf">1.0</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>G-Buffer Pass 就结束了接下来是光照处理 Pass
在光照阶段，我们渲染一个全屏四边形（Screen-filling Quad），并从 G-Buffer 纹理中重构光照所需的所有变量。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">glBindTextureUnit</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">resources_</span><span class="p">.</span><span class="n">g_position_texture</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="n">glUniform1i</span><span class="p">(</span><span class="n">resources_</span><span class="p">.</span><span class="n">lighting_shader</span><span class="o">-&gt;</span><span class="n">Uniform</span><span class="p">(</span><span class="s">&#34;u_GPosition&#34;</span><span class="p">),</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="n">glBindTextureUnit</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">resources_</span><span class="p">.</span><span class="n">g_normal_texture</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="n">glUniform1i</span><span class="p">(</span><span class="n">resources_</span><span class="p">.</span><span class="n">lighting_shader</span><span class="o">-&gt;</span><span class="n">Uniform</span><span class="p">(</span><span class="s">&#34;u_GNormal&#34;</span><span class="p">),</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="n">glBindTextureUnit</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">resources_</span><span class="p">.</span><span class="n">g_albedo_spec_texture</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="n">glUniform1i</span><span class="p">(</span><span class="n">resources_</span><span class="p">.</span><span class="n">lighting_shader</span><span class="o">-&gt;</span><span class="n">Uniform</span><span class="p">(</span><span class="s">&#34;u_GAlbedoSpec&#34;</span><span class="p">),</span> <span class="mi">2</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="n">glBindTextureUnit</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="n">resources_</span><span class="p">.</span><span class="n">depth_map_texture</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="n">glUniform1i</span><span class="p">(</span><span class="n">resources_</span><span class="p">.</span><span class="n">lighting_shader</span><span class="o">-&gt;</span><span class="n">Uniform</span><span class="p">(</span><span class="s">&#34;u_ShadowMap&#34;</span><span class="p">),</span> <span class="mi">3</span><span class="p">);</span>
</span></span></code></pre></div><p>使用G-Buffer中的数据来获取光照所需的信息</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-glsl" data-lang="glsl"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">uniform</span> <span class="k">sampler2D</span> <span class="n">u_GPosition</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="k">uniform</span> <span class="k">sampler2D</span> <span class="n">u_GNormal</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="k">uniform</span> <span class="k">sampler2D</span> <span class="n">u_GAlbedoSpec</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="k">uniform</span> <span class="k">sampler2D</span> <span class="n">u_ShadowMap</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="p">...</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">vec3</span> <span class="n">frag_pos</span> <span class="o">=</span> <span class="n">texture</span><span class="p">(</span><span class="n">u_GPosition</span><span class="p">,</span> <span class="n">v_TexCoord</span><span class="p">).</span><span class="n">xyz</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">vec4</span> <span class="n">albedo_spec</span> <span class="o">=</span> <span class="n">texture</span><span class="p">(</span><span class="n">u_GAlbedoSpec</span><span class="p">,</span> <span class="n">v_TexCoord</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">albedo_spec</span><span class="p">.</span><span class="n">a</span> <span class="o">&lt;=</span> <span class="mf">0.001</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="n">FragColor</span> <span class="o">=</span> <span class="k">vec4</span><span class="p">(</span><span class="mf">0.0</span><span class="p">,</span> <span class="mf">0.0</span><span class="p">,</span> <span class="mf">0.0</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="n">BrightColor</span> <span class="o">=</span> <span class="k">vec4</span><span class="p">(</span><span class="mf">0.0</span><span class="p">,</span> <span class="mf">0.0</span><span class="p">,</span> <span class="mf">0.0</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">vec3</span> <span class="n">normal</span> <span class="o">=</span> <span class="n">normalize</span><span class="p">(</span><span class="n">texture</span><span class="p">(</span><span class="n">u_GNormal</span><span class="p">,</span> <span class="n">v_TexCoord</span><span class="p">).</span><span class="n">xyz</span> <span class="o">*</span> <span class="mf">2.0</span> <span class="o">-</span> <span class="mf">1.0</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">vec3</span> <span class="n">albedo</span> <span class="o">=</span> <span class="n">albedo_spec</span><span class="p">.</span><span class="n">rgb</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">float</span> <span class="n">specular_mask</span> <span class="o">=</span> <span class="n">albedo_spec</span><span class="p">.</span><span class="n">a</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">vec3</span> <span class="n">view_dir</span> <span class="o">=</span> <span class="n">normalize</span><span class="p">(</span><span class="n">u_ViewPos</span> <span class="o">-</span> <span class="n">frag_pos</span><span class="p">);</span>
</span></span></code></pre></div><h2 id="总结">总结</h2>
<p>延迟渲染是现代游戏引擎的基石。但它对处理半透明物体不太友好需要结合前向渲染实现混合渲染，但其在处理复杂动态光照场景时的性能优势是无可替代的。</p>
]]></content:encoded></item><item><title>【CG】HDR &amp; Tone Mapping</title><link>https://blog.cglab.top/posts/cg-hdr-tone-mapping/</link><pubDate>Tue, 17 Mar 2026 00:00:00 +0000</pubDate><guid>https://blog.cglab.top/posts/cg-hdr-tone-mapping/</guid><description>&lt;h1 id="hdr"&gt;HDR&lt;/h1&gt;
&lt;p&gt;传统的显示设备使用的是 LDR 模式，即线性 Device-Independent-RGB 模式，它通常使用 8 位来代表一个每个颜色通道的值。
但是在 LDR 模式下所有的颜色都被限制到了 0 到 1 之间，这导致如果场景中存在一个颜色值超过 1 的物体，那么这个物体的颜色就会变成纯白。
HDR 模式则允许颜色的值超过 1，因此 HDR 模式下可以处理更丰富的场景。
如下图所示，LDR 模式下物体的纹理细节根本看不见&lt;/p&gt;</description><content:encoded><![CDATA[<h1 id="hdr">HDR</h1>
<p>传统的显示设备使用的是 LDR 模式，即线性 Device-Independent-RGB 模式，它通常使用 8 位来代表一个每个颜色通道的值。
但是在 LDR 模式下所有的颜色都被限制到了 0 到 1 之间，这导致如果场景中存在一个颜色值超过 1 的物体，那么这个物体的颜色就会变成纯白。
HDR 模式则允许颜色的值超过 1，因此 HDR 模式下可以处理更丰富的场景。
如下图所示，LDR 模式下物体的纹理细节根本看不见</p>
<p><img alt="图1" loading="lazy" src="/images/opengl/hdr0.png"></p>
<p>在 OpenGL 中默认的帧缓冲是使用的是 <code>GL_RGBA8</code> 即8 位的 RGBA 模式，因此在片段着色器运行之后无论你的颜色值是否超过了1，颜色都被限制在了 0 到 1 之间。
因此，HDR 模式下需要使用 <code>GL_RGBA16F</code> 即 16 位的 RGBA 模式，这样颜色值就可以超过 1 了。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">glTexImage2D</span><span class="p">(</span><span class="n">GL_TEXTURE_2D</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">GL_RGB16F</span><span class="p">,</span> <span class="n">width</span><span class="p">,</span> <span class="n">height</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">GL_RGB</span><span class="p">,</span> <span class="n">GL_FLOAT</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>  
</span></span></code></pre></div><p>虽然这么设置之后确实能够输出颜色值在 1 以上的颜色，但是在操作系统和显示设备之间进行颜色转换的时候，这些颜色值最终还是会被限制在了 0 到 1 之间，因此我们需要通过 Tone Mapping 来解决这个问题。</p>
<h1 id="tone-mapping">Tone Mapping</h1>
<p>Tone Mapping 是一种将 HDR 颜色值映射到 LDR 颜色值的方法。</p>
<h2 id="reinhard-tone-mapping">Reinhard tone mapping</h2>
<p>最基础的 Reinhard 公式非常简洁：</p>
$$L_{out} = \frac{L_{in}}{1 + L_{in}}$$<ul>
<li>$ L_{in} $ 表示输入的 HDR 颜色值</li>
<li>$ L_{out} $ 输出的 LDR 颜色值</li>
<li>当 $L_{in}$ 很小的时候，$L_{out} \approx L_{in}$ 保持暗部细节</li>
<li>当 $L_{in} \to \infty$ 时，$L_{out} \to 1$ 高光永远不会超过最大亮度</li>
</ul>
<p>这个算法是倾向明亮的区域的，暗的区域会不那么精细也不那么有区分度</p>
<p><img alt="图2" loading="lazy" src="/images/opengl/hdr1.png"></p>
<hr>
<h2 id="exposure-tone-mapping">Exposure tone mapping</h2>
<p>Exposure tone mapping 模仿的是真实摄像机的工作原理</p>
$$L_{out} = 1 - \exp(-exposure * L_{in})$$<ul>
<li>$ L_{in} $ 表示输入的 HDR 颜色值</li>
<li>$ L_{out} $ 输出的 LDR 颜色值</li>
<li>$exposure$ 表示曝光度</li>
</ul>
<p><img alt="图3" loading="lazy" src="/images/opengl/hdr2.png"></p>
<p>他们的glsl实现如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-glsl" data-lang="glsl"><span class="line"><span class="ln"> 1</span><span class="cl">
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"> <span class="k">vec4</span> <span class="n">final_color</span> <span class="o">=</span> <span class="k">vec4</span><span class="p">(</span><span class="n">ambient</span> <span class="o">+</span> <span class="p">(</span><span class="mf">1.0</span> <span class="o">-</span> <span class="n">shadow</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">diffuse</span> <span class="o">+</span> <span class="n">specular</span><span class="p">),</span> <span class="mf">1.0</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="c1">// HDR Tone Mapping</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">u_EnableHDR</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">        <span class="k">vec3</span> <span class="n">hdr_color</span> <span class="o">=</span> <span class="n">final_color</span><span class="p">.</span><span class="n">rgb</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">u_ToneMappingMode</span> <span class="o">==</span> <span class="mo">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">            <span class="c1">// Reinhard tone mapping</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">            <span class="n">final_color</span><span class="p">.</span><span class="n">rgb</span> <span class="o">=</span> <span class="n">hdr_color</span> <span class="o">/</span> <span class="p">(</span><span class="n">hdr_color</span> <span class="o">+</span> <span class="k">vec3</span><span class="p">(</span><span class="mf">1.0</span><span class="p">));</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">            <span class="c1">// Exposure tone mapping</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">            <span class="n">final_color</span><span class="p">.</span><span class="n">rgb</span> <span class="o">=</span> <span class="k">vec3</span><span class="p">(</span><span class="mf">1.0</span><span class="p">)</span> <span class="o">-</span> <span class="n">exp</span><span class="p">(</span><span class="o">-</span><span class="n">hdr_color</span> <span class="o">*</span> <span class="n">u_Exposure</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="p">}</span>
</span></span></code></pre></div>]]></content:encoded></item><item><title>【CG】Shadow Mapping 使用OpenGL实现</title><link>https://blog.cglab.top/posts/opengl-shadow-mapping-1/</link><pubDate>Mon, 16 Mar 2026 00:00:00 +0000</pubDate><guid>https://blog.cglab.top/posts/opengl-shadow-mapping-1/</guid><description>&lt;p&gt;如果你玩过现代 3D 游戏那么其中大多数的的动态物体阴影都是 **阴影映射（Shadow Mapping）**这个算法的变种。&lt;/p&gt;
&lt;p&gt;所以为了研究其他的现代的实时动态阴影我们必须先学习一下 Shadow Mapping 的原理，以及如何使用OpenGL去实现。&lt;/p&gt;</description><content:encoded><![CDATA[<p>如果你玩过现代 3D 游戏那么其中大多数的的动态物体阴影都是 **阴影映射（Shadow Mapping）**这个算法的变种。</p>
<p>所以为了研究其他的现代的实时动态阴影我们必须先学习一下 Shadow Mapping 的原理，以及如何使用OpenGL去实现。</p>
<h2 id="原理">原理</h2>
<p>他的思想原理非常简单，如果一个点能被光源看到（直射）并且也能被摄像机看到，那么这个点就在光照下（不在阴影中），反之如果一个点只能被摄像机看到，而不能被光源看到，那么他就在阴影中。</p>
<p>详细的实现是：</p>
<ul>
<li>对于 <strong>平行光（Directional Light）</strong>：由于平行光的位置我们假设是无限远的，但是我们需要从光源位置看向场景 $(0,0,0)$ 点，所以我们为了简便直接假设光源位置在 从 $(0,0,0)$ 点沿着光照方向的反方向位移一段距离得到一个新的点  $\mathbf P$ ，以这个 $\mathbf P$ 点作为我们的光源位置（事实上这个方案非常不严谨，因为如果位移的距离不够，但是摄像机的视锥体包含了更多的场景，就会导致某些场景区域计算不到阴影位置。一般可以根据视锥体动态计算，并且光源的<code> LookAt</code> 目标通常应该是 <strong>“视锥体中心”</strong> ）。
<ul>
<li>Pass one：有了 $\mathbf P$点，我们从该点使用正交投影并定义 <code>lookAt</code>，走一遍渲染管线，不需要计算着色，将深度信息保存到<code>TexImage2D</code>上</li>
<li>Pass two：再从摄像机走一遍正常的管线，在 Fragment Shader中结合保存的深度信息实现该点是否在阴影中的逻辑。</li>
</ul>
</li>
<li>对于 <strong>聚光灯（Spot Light）</strong>:因为聚光灯都有一个明确的方向、张角和位置，所以我们不需要做那么多假设在平行光中的问题在这里都没有。
<ul>
<li>Pass one：我们只需要在聚光灯的位置看向它的<strong>照射方向</strong>，并且使用透视投影<code>FOV</code>应该与聚光灯的参数一致，并定义<code>lookAt</code>，走一遍渲染管线，不需要计算着色，将深度信息保存到<code>TexImage2D</code>上</li>
<li>Pass two：再从摄像机走一遍正常的管线，在 Fragment Shader中结合保存的深度信息实现该点是否在阴影中的逻辑就行了。</li>
</ul>
</li>
<li>对于 <strong>点光源（Point Light）</strong>：点光源，有具体的位置，但是点光源四面八方都有光照，所以我们需要特殊处理。
<ul>
<li>Pass one：我们通过在点光源的位置向上下左右前后六个方向都计算一次深度信息，存入一张 <strong>Cube Map</strong></li>
<li>Pass two：再从摄像机走一遍正常的管线，在 Fragment Shader中结合保存的Cube Map实现该点是否在阴影中的逻辑就行了。</li>
</ul>
</li>
</ul>
<h2 id="实现">实现</h2>
<p>这里只实现平行光的Shadow Mapping。</p>
<p>首先我们定义 Pass one 中要使用的 FBO 和 TexImage2D，和Shader</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">std</span><span class="o">::</span><span class="n">unique_ptr</span><span class="o">&lt;</span><span class="n">gl</span><span class="o">::</span><span class="n">Shader</span><span class="o">&gt;</span> <span class="n">depth_shader_</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="n">GLuint</span> <span class="n">depth_map_fbo_</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="n">GLuint</span> <span class="n">depth_map_texture_</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1">// TexImage2D的大小
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="k">const</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">shadow_width_</span> <span class="o">=</span> <span class="mi">1&#39;024</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="k">const</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">shadow_height_</span> <span class="o">=</span> <span class="mi">1&#39;024</span><span class="p">;</span>
</span></span></code></pre></div><p>并且初始化</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="n">depth_shader_</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">make_unique</span><span class="o">&lt;</span><span class="n">gl</span><span class="o">::</span><span class="n">Shader</span><span class="o">&gt;</span><span class="p">();</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="n">depth_shader_</span><span class="o">-&gt;</span><span class="n">CompileFromFile</span><span class="p">(</span><span class="s">&#34;shaders/AdvanceLighting/shadow_mapping.vert&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">                                   <span class="s">&#34;shaders/AdvanceLighting/shadow_mapping.frag&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1">// Depth map texture for shadow mapping
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="n">glCreateTextures</span><span class="p">(</span><span class="n">GL_TEXTURE_2D</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">depth_map_texture_</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="n">glTextureStorage2D</span><span class="p">(</span><span class="n">depth_map_texture_</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">GL_DEPTH_COMPONENT24</span><span class="p">,</span> <span class="n">shadow_width_</span><span class="p">,</span> <span class="n">shadow_height_</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="n">glTextureParameteri</span><span class="p">(</span><span class="n">depth_map_texture_</span><span class="p">,</span> <span class="n">GL_TEXTURE_MIN_FILTER</span><span class="p">,</span> <span class="n">GL_NEAREST</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="n">glTextureParameteri</span><span class="p">(</span><span class="n">depth_map_texture_</span><span class="p">,</span> <span class="n">GL_TEXTURE_MAG_FILTER</span><span class="p">,</span> <span class="n">GL_NEAREST</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="n">glTextureParameteri</span><span class="p">(</span><span class="n">depth_map_texture_</span><span class="p">,</span> <span class="n">GL_TEXTURE_WRAP_S</span><span class="p">,</span> <span class="n">GL_CLAMP_TO_BORDER</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="n">glTextureParameteri</span><span class="p">(</span><span class="n">depth_map_texture_</span><span class="p">,</span> <span class="n">GL_TEXTURE_WRAP_T</span><span class="p">,</span> <span class="n">GL_CLAMP_TO_BORDER</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="kt">float</span> <span class="n">border_color</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mf">1.0f</span><span class="p">,</span> <span class="mf">1.0f</span><span class="p">,</span> <span class="mf">1.0f</span><span class="p">,</span> <span class="mf">1.0f</span><span class="p">};</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="n">glTextureParameterfv</span><span class="p">(</span><span class="n">depth_map_texture_</span><span class="p">,</span> <span class="n">GL_TEXTURE_BORDER_COLOR</span><span class="p">,</span> <span class="n">border_color</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="c1">// Depth map framebuffer
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="n">glCreateFramebuffers</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">depth_map_fbo_</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="n">glNamedFramebufferTexture</span><span class="p">(</span><span class="n">depth_map_fbo_</span><span class="p">,</span> <span class="n">GL_DEPTH_ATTACHMENT</span><span class="p">,</span> <span class="n">depth_map_texture_</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="n">glNamedFramebufferDrawBuffer</span><span class="p">(</span><span class="n">depth_map_fbo_</span><span class="p">,</span> <span class="n">GL_NONE</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="n">glNamedFramebufferReadBuffer</span><span class="p">(</span><span class="n">depth_map_fbo_</span><span class="p">,</span> <span class="n">GL_NONE</span><span class="p">);</span>
</span></span></code></pre></div><p>再写一个Pass one使用的Shader</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-glsl" data-lang="glsl"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">// vertex shader</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="cp">#version 460 core</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="n">layout</span><span class="p">(</span><span class="n">location</span> <span class="o">=</span> <span class="mo">0</span><span class="p">)</span> <span class="k">in</span> <span class="k">vec3</span> <span class="n">a_Position</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">uniform</span> <span class="n">mat4</span> <span class="n">u_model</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">uniform</span> <span class="n">mat4</span> <span class="n">u_LightSpaceMatrix</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">void</span> <span class="n">main</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="n">gl_Position</span> <span class="o">=</span> <span class="n">u_LightSpaceMatrix</span> <span class="o">*</span> <span class="n">u_model</span> <span class="o">*</span> <span class="k">vec4</span><span class="p">(</span><span class="n">a_Position</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1">// fragment shader</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="cp">#version 460 core</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">void</span> <span class="n">main</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">    
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>再执行pass one</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">// ====== Pass 1: Render depth map from light&#39;s perspective ======
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="n">glEnable</span><span class="p">(</span><span class="n">GL_DEPTH_TEST</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="n">glViewport</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">shadow_width_</span><span class="p">,</span> <span class="n">shadow_height_</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="n">glBindFramebuffer</span><span class="p">(</span><span class="n">GL_FRAMEBUFFER</span><span class="p">,</span> <span class="n">depth_map_fbo_</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="n">glClear</span><span class="p">(</span><span class="n">GL_DEPTH_BUFFER_BIT</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="n">depth_shader_</span><span class="o">-&gt;</span><span class="n">Bind</span><span class="p">();</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="n">glUniformMatrix4fv</span><span class="p">(</span><span class="n">depth_shader_</span><span class="o">-&gt;</span><span class="n">Uniform</span><span class="p">(</span><span class="s">&#34;u_LightSpaceMatrix&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">                       <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">                       <span class="n">GL_FALSE</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">                       <span class="n">glm</span><span class="o">::</span><span class="n">value_ptr</span><span class="p">(</span><span class="n">light_space_matrix</span><span class="p">));</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="n">RenderScene</span><span class="p">(</span><span class="o">*</span><span class="n">depth_shader_</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="n">depth_shader_</span><span class="o">-&gt;</span><span class="n">Unbind</span><span class="p">();</span>
</span></span></code></pre></div><p>最后将<code>depth_map_texture_</code>和<code>u_LightSpaceMatrix</code>传入shader以实现“该点是否在阴影中的逻辑”</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">// ====== Pass 2: Render scene with shadow mapping ======
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1">// Restore app&#39;s FBO and viewport
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="n">glBindFramebuffer</span><span class="p">(</span><span class="n">GL_FRAMEBUFFER</span><span class="p">,</span> <span class="n">prev_fbo</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="n">glViewport</span><span class="p">(</span><span class="n">prev_viewport</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">prev_viewport</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">prev_viewport</span><span class="p">[</span><span class="mi">2</span><span class="p">],</span> <span class="n">prev_viewport</span><span class="p">[</span><span class="mi">3</span><span class="p">]);</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="n">glClear</span><span class="p">(</span><span class="n">GL_COLOR_BUFFER_BIT</span> <span class="o">|</span> <span class="n">GL_DEPTH_BUFFER_BIT</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">selected_gamma_correction_</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="n">glEnable</span><span class="p">(</span><span class="n">GL_FRAMEBUFFER_SRGB</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="n">glDisable</span><span class="p">(</span><span class="n">GL_FRAMEBUFFER_SRGB</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="n">glm</span><span class="o">::</span><span class="n">mat4</span> <span class="n">view</span> <span class="o">=</span> <span class="n">camera_3d_</span><span class="o">-&gt;</span><span class="n">GetViewMatrix</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="n">glm</span><span class="o">::</span><span class="n">mat4</span> <span class="n">projection</span> <span class="o">=</span> <span class="n">camera_3d_</span><span class="o">-&gt;</span><span class="n">GetProjectionMatrix</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="n">shader_</span><span class="o">-&gt;</span><span class="n">Bind</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="n">glUniformMatrix4fv</span><span class="p">(</span><span class="n">shader_</span><span class="o">-&gt;</span><span class="n">Uniform</span><span class="p">(</span><span class="s">&#34;u_view&#34;</span><span class="p">),</span> <span class="mi">1</span><span class="p">,</span> <span class="n">GL_FALSE</span><span class="p">,</span> <span class="n">glm</span><span class="o">::</span><span class="n">value_ptr</span><span class="p">(</span><span class="n">view</span><span class="p">));</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="n">glUniformMatrix4fv</span><span class="p">(</span><span class="n">shader_</span><span class="o">-&gt;</span><span class="n">Uniform</span><span class="p">(</span><span class="s">&#34;u_projection&#34;</span><span class="p">),</span> <span class="mi">1</span><span class="p">,</span> <span class="n">GL_FALSE</span><span class="p">,</span> <span class="n">glm</span><span class="o">::</span><span class="n">value_ptr</span><span class="p">(</span><span class="n">projection</span><span class="p">));</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="n">glUniformMatrix4fv</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="n">shader_</span><span class="o">-&gt;</span><span class="n">Uniform</span><span class="p">(</span><span class="s">&#34;u_LightSpaceMatrix&#34;</span><span class="p">),</span> <span class="mi">1</span><span class="p">,</span> <span class="n">GL_FALSE</span><span class="p">,</span> <span class="n">glm</span><span class="o">::</span><span class="n">value_ptr</span><span class="p">(</span><span class="n">light_space_matrix</span><span class="p">));</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="n">glUniform3fv</span><span class="p">(</span><span class="n">shader_</span><span class="o">-&gt;</span><span class="n">Uniform</span><span class="p">(</span><span class="s">&#34;u_LightPos&#34;</span><span class="p">),</span> <span class="mi">1</span><span class="p">,</span> <span class="n">glm</span><span class="o">::</span><span class="n">value_ptr</span><span class="p">(</span><span class="n">light_pos_</span><span class="p">));</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="n">glUniform3fv</span><span class="p">(</span><span class="n">shader_</span><span class="o">-&gt;</span><span class="n">Uniform</span><span class="p">(</span><span class="s">&#34;u_ViewPos&#34;</span><span class="p">),</span> <span class="mi">1</span><span class="p">,</span> <span class="n">glm</span><span class="o">::</span><span class="n">value_ptr</span><span class="p">(</span><span class="n">camera_pos_</span><span class="p">));</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="n">glUniform3fv</span><span class="p">(</span><span class="n">shader_</span><span class="o">-&gt;</span><span class="n">Uniform</span><span class="p">(</span><span class="s">&#34;u_LightColor&#34;</span><span class="p">),</span> <span class="mi">1</span><span class="p">,</span> <span class="n">glm</span><span class="o">::</span><span class="n">value_ptr</span><span class="p">(</span><span class="n">light_color_</span><span class="p">));</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="n">glUniform1f</span><span class="p">(</span><span class="n">shader_</span><span class="o">-&gt;</span><span class="n">Uniform</span><span class="p">(</span><span class="s">&#34;u_Shininess&#34;</span><span class="p">),</span> <span class="n">shininess_</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="n">glUniform1f</span><span class="p">(</span><span class="n">shader_</span><span class="o">-&gt;</span><span class="n">Uniform</span><span class="p">(</span><span class="s">&#34;u_AmbientStrength&#34;</span><span class="p">),</span> <span class="n">ambient_strength_</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="n">glUniform1f</span><span class="p">(</span><span class="n">shader_</span><span class="o">-&gt;</span><span class="n">Uniform</span><span class="p">(</span><span class="s">&#34;u_SpecularStrength&#34;</span><span class="p">),</span> <span class="n">specular_strength_</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="n">glUniform1i</span><span class="p">(</span><span class="n">shader_</span><span class="o">-&gt;</span><span class="n">Uniform</span><span class="p">(</span><span class="s">&#34;u_BlinnPhong&#34;</span><span class="p">),</span> <span class="n">use_blinn_phong_</span> <span class="o">?</span> <span class="mi">1</span> <span class="o">:</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="n">glUniform1i</span><span class="p">(</span><span class="n">shader_</span><span class="o">-&gt;</span><span class="n">Uniform</span><span class="p">(</span><span class="s">&#34;u_UseGammaCorrection&#34;</span><span class="p">),</span> <span class="n">selected_gamma_correction_</span> <span class="o">==</span> <span class="mi">2</span> <span class="o">?</span> <span class="mi">1</span> <span class="o">:</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="n">glUniform1i</span><span class="p">(</span><span class="n">shader_</span><span class="o">-&gt;</span><span class="n">Uniform</span><span class="p">(</span><span class="s">&#34;u_EnableShadows&#34;</span><span class="p">),</span> <span class="n">enable_shadows_</span> <span class="o">?</span> <span class="mi">1</span> <span class="o">:</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="n">glBindTextureUnit</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">floor_texture_</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="n">glUniform1i</span><span class="p">(</span><span class="n">shader_</span><span class="o">-&gt;</span><span class="n">Uniform</span><span class="p">(</span><span class="s">&#34;u_FloorTexture&#34;</span><span class="p">),</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">
</span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="n">glBindTextureUnit</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">depth_map_texture_</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="n">glUniform1i</span><span class="p">(</span><span class="n">shader_</span><span class="o">-&gt;</span><span class="n">Uniform</span><span class="p">(</span><span class="s">&#34;u_ShadowMap&#34;</span><span class="p">),</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">
</span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="n">RenderScene</span><span class="p">(</span><span class="o">*</span><span class="n">shader_</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="n">shader_</span><span class="o">-&gt;</span><span class="n">Unbind</span><span class="p">();</span>
</span></span></code></pre></div><p>更新Pass two 的Shader</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-glsl" data-lang="glsl"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="cp">#version 460 core</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="n">layout</span><span class="p">(</span><span class="n">location</span> <span class="o">=</span> <span class="mo">0</span><span class="p">)</span> <span class="k">in</span> <span class="k">vec3</span> <span class="n">a_Position</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="n">layout</span><span class="p">(</span><span class="n">location</span> <span class="o">=</span> <span class="mi">1</span><span class="p">)</span> <span class="k">in</span> <span class="k">vec3</span> <span class="n">a_Normal</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="n">layout</span><span class="p">(</span><span class="n">location</span> <span class="o">=</span> <span class="mi">2</span><span class="p">)</span> <span class="k">in</span> <span class="k">vec2</span> <span class="n">a_TexCoord</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">uniform</span> <span class="n">mat4</span> <span class="n">u_model</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">uniform</span> <span class="n">mat4</span> <span class="n">u_view</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="k">uniform</span> <span class="n">mat4</span> <span class="n">u_projection</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">uniform</span> <span class="n">mat4</span> <span class="n">u_LightSpaceMatrix</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">out</span> <span class="k">vec3</span> <span class="n">v_FragPos</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">out</span> <span class="k">vec3</span> <span class="n">v_Normal</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">out</span> <span class="k">vec2</span> <span class="n">v_TexCoord</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">out</span> <span class="k">vec4</span> <span class="n">v_FragPosLightSpace</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">void</span> <span class="n">main</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="k">vec4</span> <span class="n">world_pos</span> <span class="o">=</span> <span class="n">u_model</span> <span class="o">*</span> <span class="k">vec4</span><span class="p">(</span><span class="n">a_Position</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="n">v_FragPos</span> <span class="o">=</span> <span class="n">world_pos</span><span class="p">.</span><span class="n">xyz</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">    <span class="n">v_Normal</span> <span class="o">=</span> <span class="n">mat3</span><span class="p">(</span><span class="n">transpose</span><span class="p">(</span><span class="n">inverse</span><span class="p">(</span><span class="n">u_model</span><span class="p">)))</span> <span class="o">*</span> <span class="n">a_Normal</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">    <span class="n">v_TexCoord</span> <span class="o">=</span> <span class="n">a_TexCoord</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">
</span></span><span class="line"><span class="ln">24</span><span class="cl">    <span class="c1">// 将世界坐标系下的位置转换到光源空间</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">    <span class="n">v_FragPosLightSpace</span> <span class="o">=</span> <span class="n">u_LightSpaceMatrix</span> <span class="o">*</span> <span class="n">world_pos</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">
</span></span><span class="line"><span class="ln">27</span><span class="cl">    <span class="n">gl_Position</span> <span class="o">=</span> <span class="n">u_projection</span> <span class="o">*</span> <span class="n">u_view</span> <span class="o">*</span> <span class="n">world_pos</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-glsl" data-lang="glsl"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="cp">#version 460 core</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="k">in</span> <span class="k">vec3</span> <span class="n">v_FragPos</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="k">in</span> <span class="k">vec3</span> <span class="n">v_Normal</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">in</span> <span class="k">vec2</span> <span class="n">v_TexCoord</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">in</span> <span class="k">vec4</span> <span class="n">v_FragPosLightSpace</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">out</span> <span class="k">vec4</span> <span class="n">FragColor</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">uniform</span> <span class="k">sampler2D</span> <span class="n">u_FloorTexture</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">uniform</span> <span class="k">sampler2D</span> <span class="n">u_ShadowMap</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">uniform</span> <span class="k">vec3</span> <span class="n">u_LightPos</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">uniform</span> <span class="k">vec3</span> <span class="n">u_ViewPos</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">uniform</span> <span class="k">vec3</span> <span class="n">u_LightColor</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">uniform</span> <span class="k">float</span> <span class="n">u_Shininess</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">uniform</span> <span class="k">float</span> <span class="n">u_AmbientStrength</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">uniform</span> <span class="k">float</span> <span class="n">u_SpecularStrength</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">uniform</span> <span class="k">int</span> <span class="n">u_BlinnPhong</span><span class="p">;</span> <span class="c1">// 0 = Phong, 1 = Blinn-Phong</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="k">uniform</span> <span class="k">int</span> <span class="n">u_UseGammaCorrection</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="k">uniform</span> <span class="k">int</span> <span class="n">u_EnableShadows</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="k">float</span> <span class="n">ShadowCalculation</span><span class="p">(</span><span class="k">vec4</span> <span class="n">fragPosLightSpace</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">    <span class="c1">// 将片段位置从齐次裁剪空间转换到纹理坐标空间</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">    <span class="k">vec3</span> <span class="n">projCoords</span> <span class="o">=</span> <span class="n">fragPosLightSpace</span><span class="p">.</span><span class="n">xyz</span> <span class="o">/</span> <span class="n">fragPosLightSpace</span><span class="p">.</span><span class="n">w</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">
</span></span><span class="line"><span class="ln">27</span><span class="cl">    <span class="c1">// 将范围从 [-1, 1] 转换到 [0, 1]</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">    <span class="n">projCoords</span> <span class="o">=</span> <span class="n">projCoords</span> <span class="o">*</span> <span class="mf">0.5</span> <span class="o">+</span> <span class="mf">0.5</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">
</span></span><span class="line"><span class="ln">30</span><span class="cl">    <span class="c1">// 处理光锥体外的区域</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">projCoords</span><span class="p">.</span><span class="n">z</span> <span class="o">&gt;</span> <span class="mf">1.0</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">        <span class="k">return</span> <span class="mf">0.0</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">
</span></span><span class="line"><span class="ln">34</span><span class="cl">    <span class="c1">// 获取阴影贴图中存储的最近深度</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl">    <span class="k">float</span> <span class="n">closestDepth</span> <span class="o">=</span> <span class="n">texture</span><span class="p">(</span><span class="n">u_ShadowMap</span><span class="p">,</span> <span class="n">projCoords</span><span class="p">.</span><span class="n">xy</span><span class="p">).</span><span class="n">r</span><span class="p">;</span> 
</span></span><span class="line"><span class="ln">36</span><span class="cl">
</span></span><span class="line"><span class="ln">37</span><span class="cl">    <span class="c1">// 获取当前像素的深度</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl">    <span class="k">float</span> <span class="n">currentDepth</span> <span class="o">=</span> <span class="n">projCoords</span><span class="p">.</span><span class="n">z</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl">
</span></span><span class="line"><span class="ln">40</span><span class="cl">    <span class="c1">// 如果当前深度大于贴图中的最近深度，则该点在阴影中 (1.0)，否则在光照下 (0.0)</span>
</span></span><span class="line"><span class="ln">41</span><span class="cl">    <span class="k">float</span> <span class="n">shadow</span> <span class="o">=</span> <span class="n">currentDepth</span> <span class="o">&gt;</span> <span class="n">closestDepth</span> <span class="o">?</span> <span class="mf">1.0</span> <span class="o">:</span> <span class="mf">0.0</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">42</span><span class="cl">
</span></span><span class="line"><span class="ln">43</span><span class="cl">    <span class="k">return</span> <span class="n">shadow</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">44</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">45</span><span class="cl">
</span></span><span class="line"><span class="ln">46</span><span class="cl"><span class="k">void</span> <span class="n">main</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">47</span><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="ln">48</span><span class="cl">    <span class="k">vec3</span> <span class="n">color</span> <span class="o">=</span> <span class="n">texture</span><span class="p">(</span><span class="n">u_FloorTexture</span><span class="p">,</span> <span class="n">v_TexCoord</span><span class="p">).</span><span class="n">rgb</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">49</span><span class="cl">    <span class="k">vec3</span> <span class="n">normal</span> <span class="o">=</span> <span class="n">normalize</span><span class="p">(</span><span class="n">v_Normal</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">50</span><span class="cl">    <span class="k">vec3</span> <span class="n">light_dir</span> <span class="o">=</span> <span class="n">normalize</span><span class="p">(</span><span class="n">u_LightPos</span> <span class="o">-</span> <span class="n">v_FragPos</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">51</span><span class="cl">    <span class="k">vec3</span> <span class="n">view_dir</span> <span class="o">=</span> <span class="n">normalize</span><span class="p">(</span><span class="n">u_ViewPos</span> <span class="o">-</span> <span class="n">v_FragPos</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">52</span><span class="cl">
</span></span><span class="line"><span class="ln">53</span><span class="cl">    <span class="c1">// ambient</span>
</span></span><span class="line"><span class="ln">54</span><span class="cl">    <span class="k">vec3</span> <span class="n">ambient</span> <span class="o">=</span> <span class="n">u_AmbientStrength</span> <span class="o">*</span> <span class="n">color</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">55</span><span class="cl">
</span></span><span class="line"><span class="ln">56</span><span class="cl">    <span class="c1">// diffuse</span>
</span></span><span class="line"><span class="ln">57</span><span class="cl">    <span class="k">float</span> <span class="n">diff</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="n">dot</span><span class="p">(</span><span class="n">normal</span><span class="p">,</span> <span class="n">light_dir</span><span class="p">),</span> <span class="mf">0.0</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">58</span><span class="cl">    <span class="k">vec3</span> <span class="n">diffuse</span> <span class="o">=</span> <span class="n">diff</span> <span class="o">*</span> <span class="n">u_LightColor</span> <span class="o">*</span> <span class="n">color</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">59</span><span class="cl">
</span></span><span class="line"><span class="ln">60</span><span class="cl">    <span class="c1">// specular</span>
</span></span><span class="line"><span class="ln">61</span><span class="cl">    <span class="k">float</span> <span class="n">spec</span> <span class="o">=</span> <span class="mf">0.0</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">62</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">u_BlinnPhong</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">63</span><span class="cl">        <span class="c1">// Blinn-Phong: use halfway vector</span>
</span></span><span class="line"><span class="ln">64</span><span class="cl">        <span class="k">vec3</span> <span class="n">halfway_dir</span> <span class="o">=</span> <span class="n">normalize</span><span class="p">(</span><span class="n">light_dir</span> <span class="o">+</span> <span class="n">view_dir</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">65</span><span class="cl">        <span class="n">spec</span> <span class="o">=</span> <span class="n">pow</span><span class="p">(</span><span class="n">max</span><span class="p">(</span><span class="n">dot</span><span class="p">(</span><span class="n">normal</span><span class="p">,</span> <span class="n">halfway_dir</span><span class="p">),</span> <span class="mf">0.0</span><span class="p">),</span> <span class="n">u_Shininess</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">66</span><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">67</span><span class="cl">        <span class="c1">// Phong: use reflect direction</span>
</span></span><span class="line"><span class="ln">68</span><span class="cl">        <span class="k">vec3</span> <span class="n">reflect_dir</span> <span class="o">=</span> <span class="n">reflect</span><span class="p">(</span><span class="o">-</span><span class="n">light_dir</span><span class="p">,</span> <span class="n">normal</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">69</span><span class="cl">        <span class="n">spec</span> <span class="o">=</span> <span class="n">pow</span><span class="p">(</span><span class="n">max</span><span class="p">(</span><span class="n">dot</span><span class="p">(</span><span class="n">view_dir</span><span class="p">,</span> <span class="n">reflect_dir</span><span class="p">),</span> <span class="mf">0.0</span><span class="p">),</span> <span class="n">u_Shininess</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">70</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">71</span><span class="cl">    <span class="k">vec3</span> <span class="n">specular</span> <span class="o">=</span> <span class="n">u_SpecularStrength</span> <span class="o">*</span> <span class="n">spec</span> <span class="o">*</span> <span class="n">u_LightColor</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">72</span><span class="cl">
</span></span><span class="line"><span class="ln">73</span><span class="cl">    <span class="c1">// shadow</span>
</span></span><span class="line"><span class="ln">74</span><span class="cl">    <span class="k">float</span> <span class="n">shadow</span> <span class="o">=</span> <span class="mf">0.0</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">75</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">u_EnableShadows</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">76</span><span class="cl">        <span class="n">shadow</span> <span class="o">=</span> <span class="n">ShadowCalculation</span><span class="p">(</span><span class="n">v_FragPosLightSpace</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">77</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">78</span><span class="cl">
</span></span><span class="line"><span class="ln">79</span><span class="cl">    <span class="k">vec4</span> <span class="n">final_color</span> <span class="o">=</span> <span class="k">vec4</span><span class="p">(</span><span class="n">ambient</span> <span class="o">+</span> <span class="p">(</span><span class="mf">1.0</span> <span class="o">-</span> <span class="n">shadow</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">diffuse</span> <span class="o">+</span> <span class="n">specular</span><span class="p">),</span> <span class="mf">1.0</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">80</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">u_UseGammaCorrection</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">81</span><span class="cl">        <span class="n">final_color</span><span class="p">.</span><span class="n">rgb</span> <span class="o">=</span> <span class="n">pow</span><span class="p">(</span><span class="n">final_color</span><span class="p">.</span><span class="n">rgb</span><span class="p">,</span> <span class="k">vec3</span><span class="p">(</span><span class="mf">1.0</span> <span class="o">/</span> <span class="mf">2.2</span><span class="p">));</span>
</span></span><span class="line"><span class="ln">82</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">83</span><span class="cl">    <span class="n">FragColor</span> <span class="o">=</span> <span class="n">final_color</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">84</span><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="阴影失真">阴影失真</h2>
<p>运行后你会发现阴影有是有了但是出现了好多类似摩尔纹的条纹, 如图1</p>
<p><img alt="图1" loading="lazy" src="/images/opengl/shadowmapping1.png"></p>
<p>这是一种叫做阴影失真的现象，这是由于 Shadow Mapping 的分辨率不足导致的，导致过远的片段使用同一个 Shadow Mapping 的深度值进行计算，如图2所示，片段2的深度大于Shadow mapping的深度所以是黑色，片段1和片段2采样同一个Shadow Mapping</p>
<p><img alt="图2" loading="lazy" src="/images/opengl/shadowmapping3.png"></p>
<h2 id="阴影偏移">阴影偏移</h2>
<p>为了解决这个问题，我们使用阴影偏移来解决这个问题。我们简单的对表面深度贴图应用一个偏移量，这样片段就不会被错误地认为在表面之下了。</p>
<p><img alt="图3" loading="lazy" src="https://learnopengl-cn.github.io/img/05/03/01/shadow_mapping_acne_bias.png"></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-glsl" data-lang="glsl"><span class="line"><span class="ln">1</span><span class="cl">    <span class="k">float</span> <span class="n">bias</span> <span class="o">=</span>  <span class="n">max</span><span class="p">(</span><span class="mf">0.05</span> <span class="o">*</span> <span class="p">(</span><span class="mf">1.0</span> <span class="o">-</span> <span class="n">dot</span><span class="p">(</span><span class="n">normalize</span><span class="p">(</span><span class="n">v_Normal</span><span class="p">),</span> <span class="n">normalize</span><span class="p">(</span><span class="n">u_LightPos</span> <span class="o">-</span> <span class="n">v_FragPos</span><span class="p">))),</span> <span class="mf">0.005</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">    <span class="k">float</span> <span class="n">shadow</span> <span class="o">=</span> <span class="n">currentDepth</span> <span class="o">-</span> <span class="n">bias</span> <span class="o">&gt;</span> <span class="n">closestDepth</span> <span class="o">?</span> <span class="mf">1.0</span> <span class="o">:</span> <span class="mf">0.0</span><span class="p">;</span>
</span></span></code></pre></div><p><img alt="图4" loading="lazy" src="/images/opengl/shadowmapping.png"></p>
<p>此时阴影失真现象消失了</p>
<h2 id="pcf">PCF</h2>
<p>我们离近阴影看可以看到很多锯齿，这是由于我们的Shadow Mapping 的分辨率不够，多个片段被同一个Shadow Mapping 的深度值进行计算，导致片段之间的深度值差异较大，导致锯齿。所以我们可以使用一种名为 PCF 的滤波算法来解决这个问题。
<img alt="图5" loading="lazy" src="/images/opengl/shadowmapping4.png"></p>
<p>PCF 的原理是，将阴影贴图进行区域采样，对每个采样点进行 shadow test, 并将 shadow test 的结果进行加权平均，最后的到shadow值。<br>
这里我们使用一个 3x3 的区域采样，对每个采样点进行 shadow test, 并加权平均，最终得到 shadow 值。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-glsl" data-lang="glsl"><span class="line"><span class="ln"> 1</span><span class="cl">    <span class="k">float</span> <span class="n">shadow</span> <span class="o">=</span> <span class="mf">0.0</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">    <span class="k">vec2</span> <span class="n">texelSize</span> <span class="o">=</span> <span class="mf">1.0</span> <span class="o">/</span> <span class="n">textureSize</span><span class="p">(</span><span class="n">u_ShadowMap</span><span class="p">,</span> <span class="mo">0</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">    <span class="k">for</span><span class="p">(</span><span class="k">int</span> <span class="n">x</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span> <span class="n">x</span> <span class="o">&lt;=</span> <span class="mi">1</span><span class="p">;</span> <span class="o">++</span><span class="n">x</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">        <span class="k">for</span><span class="p">(</span><span class="k">int</span> <span class="n">y</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span> <span class="n">y</span> <span class="o">&lt;=</span> <span class="mi">1</span><span class="p">;</span> <span class="o">++</span><span class="n">y</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">            <span class="k">float</span> <span class="n">pcfDepth</span> <span class="o">=</span> <span class="n">texture</span><span class="p">(</span><span class="n">u_ShadowMap</span><span class="p">,</span> <span class="n">projCoords</span><span class="p">.</span><span class="n">xy</span> <span class="o">+</span> <span class="k">vec2</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span> <span class="o">*</span> <span class="n">texelSize</span><span class="p">).</span><span class="n">r</span><span class="p">;</span> 
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">            <span class="n">shadow</span> <span class="o">+=</span> <span class="n">currentDepth</span> <span class="o">-</span> <span class="n">bias</span> <span class="o">&gt;</span> <span class="n">pcfDepth</span> <span class="o">?</span> <span class="mf">1.0</span> <span class="o">:</span> <span class="mf">0.0</span><span class="p">;</span>        
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">        <span class="p">}</span>    
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="n">shadow</span> <span class="o">/=</span> <span class="mf">9.0</span><span class="p">;</span>
</span></span></code></pre></div><p>这里 <code>textureSize</code> 返回的是 shadow map 的宽度和高度，我们用 1 去除以它就可以得到每个 texel 的偏移值了。
最后的 shader 代码如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-glsl" data-lang="glsl"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">float</span> <span class="n">ShadowCalculation</span><span class="p">(</span><span class="k">vec4</span> <span class="n">fragPosLightSpace</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">    <span class="c1">// 将片段位置从齐次裁剪空间转换到纹理坐标空间</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="k">vec3</span> <span class="n">projCoords</span> <span class="o">=</span> <span class="n">fragPosLightSpace</span><span class="p">.</span><span class="n">xyz</span> <span class="o">/</span> <span class="n">fragPosLightSpace</span><span class="p">.</span><span class="n">w</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="c1">// 将范围从 [-1, 1] 转换到 [0, 1]</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="n">projCoords</span> <span class="o">=</span> <span class="n">projCoords</span> <span class="o">*</span> <span class="mf">0.5</span> <span class="o">+</span> <span class="mf">0.5</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="c1">// 处理光锥体外的区域</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">projCoords</span><span class="p">.</span><span class="n">z</span> <span class="o">&gt;</span> <span class="mf">1.0</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">        <span class="k">return</span> <span class="mf">0.0</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="c1">// 获取阴影贴图中存储的最近深度</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="k">float</span> <span class="n">closestDepth</span> <span class="o">=</span> <span class="n">texture</span><span class="p">(</span><span class="n">u_ShadowMap</span><span class="p">,</span> <span class="n">projCoords</span><span class="p">.</span><span class="n">xy</span><span class="p">).</span><span class="n">r</span><span class="p">;</span> 
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl">    <span class="c1">// 获取当前像素的深度</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="k">float</span> <span class="n">currentDepth</span> <span class="o">=</span> <span class="n">projCoords</span><span class="p">.</span><span class="n">z</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="c1">// 计算阴影偏移，防止阴影 acne</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="k">float</span> <span class="n">bias</span> <span class="o">=</span>  <span class="n">max</span><span class="p">(</span><span class="mf">0.05</span> <span class="o">*</span> <span class="p">(</span><span class="mf">1.0</span> <span class="o">-</span> <span class="n">dot</span><span class="p">(</span><span class="n">normalize</span><span class="p">(</span><span class="n">v_Normal</span><span class="p">),</span> <span class="n">normalize</span><span class="p">(</span><span class="n">u_LightPos</span> <span class="o">-</span> <span class="n">v_FragPos</span><span class="p">))),</span> <span class="mf">0.005</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl">    <span class="k">float</span> <span class="n">shadow</span> <span class="o">=</span> <span class="mf">0.0</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">    <span class="k">vec2</span> <span class="n">texelSize</span> <span class="o">=</span> <span class="mf">1.0</span> <span class="o">/</span> <span class="n">textureSize</span><span class="p">(</span><span class="n">u_ShadowMap</span><span class="p">,</span> <span class="mo">0</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">    <span class="k">for</span><span class="p">(</span><span class="k">int</span> <span class="n">x</span> <span class="o">=</span> <span class="o">-</span><span class="n">u_PCFregionSize</span><span class="o">/</span><span class="mi">2</span><span class="p">;</span> <span class="n">x</span> <span class="o">&lt;=</span> <span class="n">u_PCFregionSize</span><span class="o">/</span><span class="mi">2</span><span class="p">;</span> <span class="o">++</span><span class="n">x</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">        <span class="k">for</span><span class="p">(</span><span class="k">int</span> <span class="n">y</span> <span class="o">=</span> <span class="o">-</span><span class="n">u_PCFregionSize</span><span class="o">/</span><span class="mi">2</span><span class="p">;</span> <span class="n">y</span> <span class="o">&lt;=</span> <span class="n">u_PCFregionSize</span><span class="o">/</span><span class="mi">2</span><span class="p">;</span> <span class="o">++</span><span class="n">y</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">            <span class="k">float</span> <span class="n">pcfDepth</span> <span class="o">=</span> <span class="n">texture</span><span class="p">(</span><span class="n">u_ShadowMap</span><span class="p">,</span> <span class="n">projCoords</span><span class="p">.</span><span class="n">xy</span> <span class="o">+</span> <span class="k">vec2</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span> <span class="o">*</span> <span class="n">texelSize</span><span class="p">).</span><span class="n">r</span><span class="p">;</span> 
</span></span><span class="line"><span class="ln">29</span><span class="cl">            <span class="c1">// shadow test</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">            <span class="n">shadow</span> <span class="o">+=</span> <span class="n">currentDepth</span> <span class="o">-</span> <span class="n">bias</span> <span class="o">&gt;</span> <span class="n">pcfDepth</span> <span class="o">?</span> <span class="mf">1.0</span> <span class="o">:</span> <span class="mf">0.0</span><span class="p">;</span>        
</span></span><span class="line"><span class="ln">31</span><span class="cl">        <span class="p">}</span>    
</span></span><span class="line"><span class="ln">32</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">    <span class="n">shadow</span> <span class="o">/=</span> <span class="n">u_PCFregionSize</span> <span class="o">*</span> <span class="n">u_PCFregionSize</span><span class="p">;</span> <span class="c1">// 平均阴影值</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">
</span></span><span class="line"><span class="ln">35</span><span class="cl">    <span class="k">return</span> <span class="n">shadow</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>运行后，阴影不再有锯齿，并且阴影更平滑。
<img loading="lazy" src="/images/opengl/shadowmapping5.png"></p>
]]></content:encoded></item><item><title>【OpenGL】使用曲面细分着色器绘制Bezier曲线</title><link>https://blog.cglab.top/posts/opengl-tessellation-beziercurve/</link><pubDate>Sun, 15 Mar 2026 22:44:41 +0800</pubDate><guid>https://blog.cglab.top/posts/opengl-tessellation-beziercurve/</guid><description>&lt;h1 id="opengl使用曲面细分着色器绘制bezier曲线"&gt;【OpenGL】使用曲面细分着色器绘制Bezier曲线&lt;/h1&gt;
&lt;p&gt;这篇文章将直接介绍&lt;strong&gt;使用曲面细分着色器绘制Bezier曲线，而不会介绍Bezier曲线&lt;/strong&gt;，但是会介绍 OpenGL 的曲面细分着色器的简单用法以及相关内容。&lt;/p&gt;
&lt;h2 id="介绍"&gt;介绍&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;曲面细分着色器（Tessellation Shaders）&lt;/strong&gt; 是在 OpenGL4.0 版本引入的。在这之前，有一些私有扩展可以使用，但是直到 4.0 曲面细分才正式成为 OpenGL 的一部分。&lt;/p&gt;</description><content:encoded><![CDATA[<h1 id="opengl使用曲面细分着色器绘制bezier曲线">【OpenGL】使用曲面细分着色器绘制Bezier曲线</h1>
<p>这篇文章将直接介绍<strong>使用曲面细分着色器绘制Bezier曲线，而不会介绍Bezier曲线</strong>，但是会介绍 OpenGL 的曲面细分着色器的简单用法以及相关内容。</p>
<h2 id="介绍">介绍</h2>
<p><strong>曲面细分着色器（Tessellation Shaders）</strong> 是在 OpenGL4.0 版本引入的。在这之前，有一些私有扩展可以使用，但是直到 4.0 曲面细分才正式成为 OpenGL 的一部分。</p>
<hr>
<p>在没有曲面细分之前，想在 OpenGL 里画 Bezier 曲线和曲面需要在 CPU 将所有的点计算出来再通过总线（带宽相对于现代GPU极小的 PCIE 接口）上传至 GPU ，而从 CPU 到 GPU 的数据交换及其耗时，所以曲面细分着色器的作用就是将细分任务直接交给 GPU 不仅减少了大量数据的传递也大大加快了计算速度。</p>
<p>除了绘制 Bezier 曲线和曲面，曲面细分着色器还能实现 <strong>位移贴图（Displacement Mapping）</strong> 和 <strong>动态细节级别（Dynamic LOD）</strong> 。</p>
<ul>
<li>
<p><strong>位移贴图</strong></p>
<p>传统的发现贴图是只是通过关照来欺骗眼睛（实际上还是一个平面），而曲面细分可以让一个三角形面内部的顶点沿着你想要的法线方向位移到你想要的位置从而实现真的轮廓。</p>
</li>
<li>
<p><strong>动态LOD</strong></p>
<p>可以根据相机到物理的距离动态调整物体的三角形面，从而大幅提升性能。</p>
</li>
</ul>
<hr>
<p>这时有人可能就要问了：“主播主播，曲面细分着色器这么厉害，但是我记得 <strong>几何着色器（Geometry Shader）</strong> 和 <strong>计算着色器（Compute Shader</strong> 也能干相关的任务，为什么不直接使用几何着色器和计算着色器，非要整出个曲面细分着色器呢“？？</p>
<ul>
<li>不使用 Geometry Shader 是因为它在执行细分任务的时候太慢了，GS 必须等到顶点着色器（Vertex Shader）把整个 <strong>图元（Primitive）</strong> 处理完成后了才能开始工作，而 TS 可以直接并行处理多个顶点，并且 TS 还有专门的硬件处理。</li>
<li>不使用 Compute Shader 是因它也太慢了，CS 是脱渲染离管线的，而 TS 是渲染管线的一部分，你需要将数据传入 CS 计算再将 CS 写入缓冲区，并且还需要使用 CPU 进行同步，并且 TS 还有专门的硬件处理。</li>
</ul>
<hr>
<p>上面提到了 TS 是渲染管线中的一部分，显然它是可编程的，并且位于 VS 的下一步，GS 的上一步。</p>
<p><strong>具体来分是：</strong></p>
<p><strong>Vertex Shader -&gt; Tessellation Control Shader -&gt; Tessellation Engine -&gt; Tessellation Evaluation Shader -&gt; Geometry Shader -&gt;  &hellip;</strong></p>
<p>可以看到 TS 不止一个，这就是为什么 曲面细分着色器 的英文是复数形式。</p>
<p>其中：</p>
<ul>
<li>
<p><strong>Tessellation Control Shader 决定了”细分多少份“</strong></p>
<p>输出 <strong>Tessellation Levels</strong>（细分因子）。它告诉下一步：边缘要切几段，内部要切几份。</p>
</li>
<li>
<p><strong>Tessellation Engine 根据TCS的定义执行切割操作生成新的顶点（固定的不可编程）</strong></p>
</li>
<li>
<p><strong>Tessellation Evaluation Shader 根据 TE 生成出来的点计算出该点的位置</strong></p>
<p>输入 TE 生成的参数化坐标，结合 TCS 传来的原始顶点，通过插值算法确定新顶点的<strong>真实世界位置</strong>。</p>
</li>
</ul>
<h2 id="开始绘制bezier曲线"><strong>开始绘制Bezier曲线</strong></h2>
<p>理解了曲面细分在渲染管线中的位置之后，我们就可以开始真正绘制一条 Bezier 曲线了。</p>
<p>整体流程其实很简单：</p>
<ol>
<li>在 CPU 端提供 <strong>Bezier 曲线的控制点</strong></li>
<li>让 GPU 通过 <strong>Tessellation Shader 对参数空间进行细分</strong></li>
<li>在 <strong>TES 中根据 Bezier 公式计算曲线上的真实点</strong></li>
</ol>
<p>换句话说：</p>
<blockquote>
<p>CPU 只负责提供控制点，GPU 负责计算整条曲线。</p>
</blockquote>
<p>这也是 Tessellation Shader 最大的价值：
<strong>避免 CPU 预计算大量顶点并传输到 GPU。</strong></p>
<h2 id="创建-shader-程序">创建 Shader 程序</h2>
<p>和普通的 OpenGL 程序一样，我们需要创建并编译 Shader，只不过这里多了两个阶段：</p>
<ul>
<li>Tessellation Control Shader (TCS)</li>
<li>Tessellation Evaluation Shader (TES)</li>
</ul>
<p>因此完整的程序包含四个阶段：</p>
<ul>
<li>Vertex Shader</li>
<li>Tessellation Control Shader</li>
<li>Tessellation Evaluation Shader</li>
<li>Fragment Shader</li>
</ul>
<p>代码如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kt">bool</span> <span class="n">Shader</span><span class="o">::</span><span class="n">Compile</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">vert_src</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">tcs_src</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">tes_src</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">frag_src</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">    <span class="n">GLuint</span> <span class="n">vs</span> <span class="o">=</span> <span class="n">CompileStage</span><span class="p">(</span><span class="n">GL_VERTEX_SHADER</span><span class="p">,</span> <span class="n">vert_src</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="n">GLuint</span> <span class="n">tc</span> <span class="o">=</span> <span class="n">CompileStage</span><span class="p">(</span><span class="n">GL_TESS_CONTROL_SHADER</span><span class="p">,</span> <span class="n">tcs_src</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="n">GLuint</span> <span class="n">te</span> <span class="o">=</span> <span class="n">CompileStage</span><span class="p">(</span><span class="n">GL_TESS_EVALUATION_SHADER</span><span class="p">,</span> <span class="n">tes_src</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="n">GLuint</span> <span class="n">fs</span> <span class="o">=</span> <span class="n">CompileStage</span><span class="p">(</span><span class="n">GL_FRAGMENT_SHADER</span><span class="p">,</span> <span class="n">frag_src</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">vs</span> <span class="o">||</span> <span class="o">!</span><span class="n">tc</span> <span class="o">||</span> <span class="o">!</span><span class="n">te</span> <span class="o">||</span> <span class="o">!</span><span class="n">fs</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">        <span class="n">glDeleteShader</span><span class="p">(</span><span class="n">vs</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">        <span class="n">glDeleteShader</span><span class="p">(</span><span class="n">tc</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">        <span class="n">glDeleteShader</span><span class="p">(</span><span class="n">te</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">        <span class="n">glDeleteShader</span><span class="p">(</span><span class="n">fs</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">        <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="n">id_</span> <span class="o">=</span> <span class="n">glCreateProgram</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">    <span class="n">glAttachShader</span><span class="p">(</span><span class="n">id_</span><span class="p">,</span> <span class="n">vs</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="n">glAttachShader</span><span class="p">(</span><span class="n">id_</span><span class="p">,</span> <span class="n">tc</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="n">glAttachShader</span><span class="p">(</span><span class="n">id_</span><span class="p">,</span> <span class="n">te</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="n">glAttachShader</span><span class="p">(</span><span class="n">id_</span><span class="p">,</span> <span class="n">fs</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="n">glLinkProgram</span><span class="p">(</span><span class="n">id_</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">    <span class="n">glDeleteShader</span><span class="p">(</span><span class="n">vs</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">    <span class="n">glDeleteShader</span><span class="p">(</span><span class="n">tc</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">    <span class="n">glDeleteShader</span><span class="p">(</span><span class="n">te</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">    <span class="n">glDeleteShader</span><span class="p">(</span><span class="n">fs</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">
</span></span><span class="line"><span class="ln">26</span><span class="cl">    <span class="n">GLint</span> <span class="n">ok</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">    <span class="n">glGetProgramiv</span><span class="p">(</span><span class="n">id_</span><span class="p">,</span> <span class="n">GL_LINK_STATUS</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ok</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">ok</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">        <span class="kt">char</span> <span class="n">log</span><span class="p">[</span><span class="mi">512</span><span class="p">];</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">        <span class="n">glGetProgramInfoLog</span><span class="p">(</span><span class="n">id_</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">log</span><span class="p">),</span> <span class="k">nullptr</span><span class="p">,</span> <span class="n">log</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">        <span class="n">glDeleteProgram</span><span class="p">(</span><span class="n">id_</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">        <span class="n">id_</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">        <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl">    <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">
</span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="n">GLuint</span> <span class="n">Shader</span><span class="o">::</span><span class="n">CompileStage</span><span class="p">(</span><span class="n">GLenum</span> <span class="n">type</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">src</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="ln">40</span><span class="cl">    <span class="n">GLuint</span> <span class="n">s</span> <span class="o">=</span> <span class="n">glCreateShader</span><span class="p">(</span><span class="n">type</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">41</span><span class="cl">    <span class="n">glShaderSource</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">src</span><span class="p">,</span> <span class="k">nullptr</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">42</span><span class="cl">    <span class="n">glCompileShader</span><span class="p">(</span><span class="n">s</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">43</span><span class="cl">    <span class="n">GLint</span> <span class="n">ok</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">44</span><span class="cl">    <span class="n">glGetShaderiv</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">GL_COMPILE_STATUS</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ok</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">45</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">ok</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">46</span><span class="cl">        <span class="kt">char</span> <span class="n">log</span><span class="p">[</span><span class="mi">512</span><span class="p">];</span>
</span></span><span class="line"><span class="ln">47</span><span class="cl">        <span class="n">glGetShaderInfoLog</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">log</span><span class="p">),</span> <span class="k">nullptr</span><span class="p">,</span> <span class="n">log</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">48</span><span class="cl">        <span class="n">glDeleteShader</span><span class="p">(</span><span class="n">s</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">49</span><span class="cl">        <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">50</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">51</span><span class="cl">    <span class="k">return</span> <span class="n">s</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">52</span><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>接下来和原来的一样，定义4个控制点，创建vbo，vao。唯一不同的点就是在渲染循环中我们需要使用</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">glPatchParameteri</span><span class="p">(</span><span class="n">GL_PATCH_VERTICES</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span>
</span></span></code></pre></div><p>来设置图形中最小单元的顶点数。也就是 Primitive</p>
<p>常规的Primitive有：</p>
<ul>
<li>点 1个顶点</li>
<li>线 2个顶点</li>
<li>三角形 3个顶点</li>
<li>矩形4个顶点</li>
</ul>
<p>而这里使用的是对Primitive的更抽象的形式是 <code>GL_PATCHES</code>，GL_PATCHES 不再预设这个单元长什么样，而是由你自己定义。<strong><code>glPatchParameteri(GL_PATCH_VERTICES, n)</code> 就是在填补这个定义的空白。</strong> 它告诉OpenGL：“从现在起，每 n 个点打包成一个单元扔给我。”</p>
<p>我们这里是以4个控制点为一个Bezier曲线 Patch</p>
<p>然后使用</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">glDrawArrays</span><span class="p">(</span><span class="n">GL_PATCHES</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span>
</span></span></code></pre></div><p>进行渲染</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">glBindVertexArray</span><span class="p">(</span><span class="n">vao_</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="n">glPatchParameteri</span><span class="p">(</span><span class="n">GL_PATCH_VERTICES</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="n">glLineWidth</span><span class="p">(</span><span class="n">line_width_</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="n">bezier_shader_</span><span class="p">.</span><span class="n">Bind</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="n">glUniform1f</span><span class="p">(</span><span class="n">bezier_shader_</span><span class="p">.</span><span class="n">Uniform</span><span class="p">(</span><span class="s">&#34;u_TessLevel&#34;</span><span class="p">),</span> <span class="n">tess_level_</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl">
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="n">glDrawArrays</span><span class="p">(</span><span class="n">GL_PATCHES</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="n">bezier_shader_</span><span class="p">.</span><span class="n">Unbind</span><span class="p">();</span>
</span></span></code></pre></div><p>接下来编写Shader</p>
<p>先介绍 TES</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-glsl" data-lang="glsl"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="cp">#version 460 core</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="n">layout</span><span class="p">(</span><span class="n">isolines</span><span class="p">,</span> <span class="n">equal_spacing</span><span class="p">)</span> <span class="k">in</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">uniform</span> <span class="n">mat4</span> <span class="n">u_Projection</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">void</span> <span class="n">main</span><span class="p">()</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="k">float</span> <span class="n">t</span> <span class="o">=</span> <span class="n">gl_TessCoord</span><span class="p">.</span><span class="n">x</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="k">float</span> <span class="n">t1</span> <span class="o">=</span> <span class="mf">1.0</span> <span class="o">-</span> <span class="n">t</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="c1">// Cubic Bezier: B(t) = (1-t)^3 P0 + 3(1-t)^2 t P1 + 3(1-t) t^2 P2 + t^3 P3</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="k">vec4</span> <span class="n">p</span> <span class="o">=</span> <span class="n">t1</span> <span class="o">*</span> <span class="n">t1</span> <span class="o">*</span> <span class="n">t1</span> <span class="o">*</span> <span class="n">gl_in</span><span class="p">[</span><span class="mo">0</span><span class="p">].</span><span class="n">gl_Position</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">           <span class="o">+</span> <span class="mf">3.0</span> <span class="o">*</span> <span class="n">t1</span> <span class="o">*</span> <span class="n">t1</span> <span class="o">*</span> <span class="n">t</span> <span class="o">*</span> <span class="n">gl_in</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">gl_Position</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">           <span class="o">+</span> <span class="mf">3.0</span> <span class="o">*</span> <span class="n">t1</span> <span class="o">*</span> <span class="n">t</span> <span class="o">*</span> <span class="n">t</span> <span class="o">*</span> <span class="n">gl_in</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="n">gl_Position</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">           <span class="o">+</span> <span class="n">t</span> <span class="o">*</span> <span class="n">t</span> <span class="o">*</span> <span class="n">t</span> <span class="o">*</span> <span class="n">gl_in</span><span class="p">[</span><span class="mi">3</span><span class="p">].</span><span class="n">gl_Position</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="n">gl_Position</span> <span class="o">=</span> <span class="n">u_Projection</span> <span class="o">*</span> <span class="n">p</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-glsl" data-lang="glsl"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">layout</span><span class="p">(</span><span class="n">isolines</span><span class="p">,</span> <span class="n">equal_spacing</span><span class="p">)</span> <span class="k">in</span><span class="p">;</span>
</span></span></code></pre></div><ul>
<li>
<p><code>isolines</code>：告诉 GPU 我们正在绘制<strong>线段</strong>。</p>
</li>
<li>
<p><code>equal_spacing</code>：要求硬件在 $[0, 1]$ 的参数空间内进行<strong>等间距</strong>采样。</p>
</li>
<li>
<p><code>in</code>：表示这些设置是针对输入到此着色器的数据。</p>
</li>
</ul>
<p>除了isolines布局还有</p>
<ul>
<li>triangles</li>
<li>quads</li>
</ul>
<p>不同的布局决定了使用什么样的坐标系去生成新的顶点。</p>
<h3 id="isolines布局">isolines布局</h3>
<p>isolines布局 TE 会将顶点细分到一个[0,0]*[1,1]上的坐标系中</p>
<p>细分因子</p>
<ul>
<li><code>gl_TessLevelOuter[0]</code>：决定“有多少条线”</li>
<li><code>gl_TessLevelOuter[1]</code>：决定“每条线切多少段”</li>
<li>没有<code>gl_TessLevelInner</code>，因为一条线不存在内部</li>
</ul>
<p>当你设置了这两个 Outer 因子后，硬件会生成 <code>gl_TessCoord</code></p>
<ul>
<li>
<p><code>gl_TessCoord.x</code>：代表当前点在<strong>该条线段</strong>上的位置（0.0 到 1.0）。这就是你之前代码里用来算贝塞尔曲线的 $t$。</p>
</li>
<li>
<p><code>gl_TessCoord.y</code>：代表当前点属于<strong>哪一条线</strong>（0.0 到 1.0）。</p>
</li>
<li>
<p>如果 <code>gl_TessLevelOuter[0] = 1</code>，那么所有的 <code>gl_TessCoord.y</code> 永远是 0。</p>
</li>
<li>
<p>如果 <code>gl_TessLevelOuter[0] = 2</code>，你会得到 $y=0$ 的一组点和 $y=1.0$ 的一组点。</p>
</li>
</ul>
<h3 id="triangles-布局">triangles 布局</h3>
<p><code>triangles</code> 布局将 Patch 视为一个平面三角形，并向其内部进行填充。</p>
<p><strong>坐标系：重心坐标 (Barycentric Coordinates)</strong></p>
<p>硬件生成的 <code>gl_TessCoord</code> 是一个 <code>vec3(u, v, w)</code>。</p>
<ul>
<li>这三个分量代表当前点距离三角形三条边的权重。</li>
<li>它们始终满足：$u + v + w = 1.0$。</li>
<li>三个顶点分别对应 $(1,0,0)$、$(0,1,0)$ 和 $(0,0,1)$。</li>
</ul>
<p><strong>细分因子</strong></p>
<ul>
<li><code>gl_TessLevelOuter[0, 1, 2]</code>：分别控制三角形的三条<strong>外边</strong>被切成多少段。</li>
<li><code>gl_TessLevelInner[0]</code>：控制三角形<strong>内部</strong>向中心点嵌套缩小的频率（内部只有 1 个因子）。</li>
</ul>
<h3 id="quads-布局">quads 布局</h3>
<p><code>quads</code> 布局将 Patch 视为一个单位正方形，最适合做参数化曲面（如贝塞尔曲面）或地形。</p>
<p><strong>坐标系：二维笛卡尔坐标 $[0,1] \times [0,1]$</strong></p>
<p>硬件生成的 <code>gl_TessCoord</code> 是一个 <code>vec2(u, v)</code>（虽然也是 <code>vec3</code> 类型，但 $z$ 轴始终为 0）。</p>
<ul>
<li><code>u</code> 是水平方向位置，<code>v</code> 是垂直方向位置。</li>
<li>这非常适合直接映射纹理坐标</li>
</ul>
<p>细分因子</p>
<ul>
<li><code>gl_TessLevelOuter[0, 1, 2, 3]</code>：
<ul>
<li><code>Outer[0]</code> 和 <code>Outer[2]</code>：控制左右两条边（垂直边）的段数。</li>
<li><code>Outer[1]</code> 和 <code>Outer[3]</code>：控制上下两条边（水平边）的段数。</li>
</ul>
</li>
<li><code>gl_TessLevelInner[0, 1]</code>：
<ul>
<li><code>Inner[0]</code>：控制内部水平方向（横向）切几刀。</li>
<li><code>Inner[1]</code>：控制内部垂直方向（纵向）切几刀。</li>
</ul>
</li>
</ul>
<p>接下来是 TCS</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-glsl" data-lang="glsl"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="cp">#version 460 core</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="n">layout</span><span class="p">(</span><span class="n">vertices</span> <span class="o">=</span> <span class="mi">4</span><span class="p">)</span> <span class="k">out</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">uniform</span> <span class="k">float</span> <span class="n">u_TessLevel</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">void</span> <span class="n">main</span><span class="p">()</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="n">gl_out</span><span class="p">[</span><span class="n">gl_InvocationID</span><span class="p">].</span><span class="n">gl_Position</span> <span class="o">=</span> <span class="n">gl_in</span><span class="p">[</span><span class="n">gl_InvocationID</span><span class="p">].</span><span class="n">gl_Position</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">gl_InvocationID</span> <span class="o">==</span> <span class="mo">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">        <span class="n">gl_TessLevelOuter</span><span class="p">[</span><span class="mo">0</span><span class="p">]</span> <span class="o">=</span> <span class="mf">1.0</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">        <span class="n">gl_TessLevelOuter</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">u_TessLevel</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>这其中有很多要讲的</p>
<p>TCS 中一般分为两种任务</p>
<ol>
<li>逐顶点任务</li>
<li>逐面片设置</li>
</ol>
<p>其中<code>layout(vertices = 4) out;</code>是定义了输出 <code>Patch</code> 的顶点数。这里必须要与你在C++中设置的<code>glPatchParameteri</code>对应，当你设置了这个的时候GPU就会启动4个thread去一一处理4个顶点</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-glsl" data-lang="glsl"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">gl_out</span><span class="p">[</span><span class="n">gl_InvocationID</span><span class="p">].</span><span class="n">gl_Position</span> <span class="o">=</span> <span class="n">gl_in</span><span class="p">[</span><span class="n">gl_InvocationID</span><span class="p">].</span><span class="n">gl_Position</span><span class="p">;</span>
</span></span></code></pre></div><p>这里是逐顶点任务，把从顶点着色器传来的坐标原封不动地交给输出，以便后续的 TES 使用。而 gl_InvocationID 就对应上述提到的 thread id</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-glsl" data-lang="glsl"><span class="line"><span class="ln">1</span><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">gl_InvocationID</span> <span class="o">==</span> <span class="mo">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">        <span class="n">gl_TessLevelOuter</span><span class="p">[</span><span class="mo">0</span><span class="p">]</span> <span class="o">=</span> <span class="mf">1.0</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">        <span class="n">gl_TessLevelOuter</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">u_TessLevel</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>这里是最重要，这里就是逐面片设置，这里的<code>gl_InvocationID == 0</code>是判断thread id 是不是 0 号thread 如果是则设置如何细分。为什么需要 <code>if (gl_InvocationID == 0) </code> 因为如果不这么设置，那么所有的thread (这里是4个) 就会都去设置，导致性能浪费和数据竞争。并且这是一个全局设置，不需要所有的thread都去设置。由于上述以及介绍过了<code>gl_TessLevelOuter</code>所以这里你应该明白了这是什么意思。</p>
<p>具体的代码可以去https://github.com/Yang-Junjie/GraphicsLab中的<code>BezierCurveScene.cpp</code>查看以及<code>shaders\BezierSurfaceScene</code></p>
<p>其中示例不仅有Bezier曲线，还有Bezier曲面</p>
<p><img alt="beziercurve" loading="lazy" src="/images/opengl/beziercurve.png">
<img alt="beziersurface" loading="lazy" src="/images/opengl/beziersurface.png"></p>
]]></content:encoded></item></channel></rss>