<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>bomo的开发随笔</title>
  <icon>https://www.gravatar.com/avatar/095851cb603ce52753a91f0c55c16ad0</icon>
  <subtitle>记录工作学习点滴</subtitle>
  <link href="http://zhengbomo.github.io/atom.xml" rel="self"/>
  
  <link href="http://zhengbomo.github.io/"/>
  <updated>2021-11-15T06:48:37.000Z</updated>
  <id>http://zhengbomo.github.io/</id>
  
  <author>
    <name>bomo</name>
    <email>zhengbomo@hotmail.com</email>
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>Xcode调试链优化(速度提升50&amp;#37+)</title>
    <link href="http://zhengbomo.github.io/2021-11-15/xcode-build-io-optimize/"/>
    <id>http://zhengbomo.github.io/2021-11-15/xcode-build-io-optimize/</id>
    <published>2021-11-15T06:48:37.000Z</published>
    <updated>2021-11-15T06:48:37.000Z</updated>
    
    <content type="html"><![CDATA[<p>Xcode是增量编译的，所以日常开发很多时候，我们都是改少量的代码或不改代码而重复调试，实际使用发现，从工程要跑到手机上调试仍然非常耗时，由于缓存的存在，编译可能不是最耗时的环节了，这里探究和优化影响<code>编译完成</code>到<code>App启动调试</code>速度的因素。</p><span id="more"></span><h2 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h2><p>这里只考虑DEBUG包，我们看一张Xcode打包日志细节图</p><img src="/images/post/xcodebuildopt/xcode_build_review.png" class="" width="600" title="Xcode编译时间"><p>上图选中的为主要耗时的步骤，可以看出，编译成功后，还有下面步骤较为耗时</p><ul><li>Linking</li><li>Embed Pod Frameworks</li><li>Copy Pods Resources</li><li>Custom Script（这里为自定义脚本，主要用于资源处理）</li><li>Sign</li></ul><blockquote><p>还有一个较为耗时的时间是<code>Deploy</code>，但Xcode并不没有输出Deploy的时间，后面使用<code>ideviceinstaller</code>工具单独测试，时间总体上与Xcode基本一致</p></blockquote><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 安装</span></span><br><span class="line"><span class="comment"># brew install ideviceinstaller</span></span><br><span class="line"></span><br><span class="line">begin=`<span class="built_in">date</span> <span class="string">&#x27;+%s&#x27;</span>`</span><br><span class="line"><span class="comment"># 这里直接拿编译好的app文件安装即可</span></span><br><span class="line">ideviceinstaller -i <span class="string">&#x27;xxx.ipa&#x27;</span></span><br><span class="line">end=`<span class="built_in">date</span> <span class="string">&#x27;+%s&#x27;</span>`</span><br><span class="line"></span><br><span class="line"><span class="comment"># 计算时间差，单位s</span></span><br><span class="line"><span class="built_in">expr</span> <span class="variable">$begin</span> - <span class="variable">$end</span></span><br></pre></td></tr></table></figure><p>分析</p><ol><li>Linking: 合并静态资源与地址修正，考虑使用第三方更快的link工具<code>zld</code></li><li>Embed Pod Frameworks: 主要是处理动态库，拷贝到目标路径，主要影响是<code>动态库文件数量</code></li><li>Copy Pods Resources: 主要对文件资源拷贝，主要影响是<code>文件IO</code></li><li>Custom Script：这里是自定义脚本，主要是的图片文件的处理，主要是对图片文件进行展开，文件多则<code>文件IO</code>耗时长</li><li>Sign：对整个包签名，主要影响是文件数量和大小，文件多则<code>文件IO</code>耗时长</li><li>Deploy：主要是拷贝文件和签名校验，主要影响是文件数量，文件多则<code>文件IO</code>耗时长，文件大小影响首次安装，而二次安装会做差异拷贝，有缓存，影响会减小</li></ol><p>有上面可以看出，减少IO操作是一个可行的优化方向，项目中的图片资源是放到根目录的，由于项目中用到的图片非常多，直接使用目录管理，并且使用<code>Custom Script</code>脚本单独处理图片，把图片拷贝到包的根目录上，查看<code>xx直播</code>在编译完成后的包，总文件数为<code>9297</code>个, 其中<code>8821</code>个为图片，占<code>95%</code>，文件数量占比大，这意味着每次都要进行大量的IO操作，如果能减少文件的数量，就能减少整个流程的时间</p><h2 id="优化"><a href="#优化" class="headerlink" title="优化"></a>优化</h2><h3 id="关于IO问题"><a href="#关于IO问题" class="headerlink" title="关于IO问题"></a>关于IO问题</h3><p>对于图片，我们知道除了可以放到根目录，还有可以放到<code>Images.xcassets</code>，放到<code>Images.xcassets</code>的图片最终会被编译成<code>Assets.car</code>，这里考虑把png和jpg图片提前制作成<code>Assets.car</code>，在日常开发迭代过程中，项目中用到的图片不会频繁的变动，这里我们考虑提前把图片做成<code>Assets.car</code>，如果有变动，再重新编译一次，操作步骤如下</p><ol><li>如果项目已经存在<code>Images.xcassets</code>，则拷贝一份出来，在它的基础上添加图片</li><li>通过工具把所有图片构造成<code>xxx.imageset</code>，放到<code>Images.xcassets</code>里面，如</li></ol><p>把<code>bottom_logo@2x.png</code>和<code>bottom_logo@3x.png</code>构造成下面目录结构</p><pre><code><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">├── bottom_logo.imageset</span><br><span class="line">│   ├── Contents.json</span><br><span class="line">│   ├── bottom_logo@2x.png</span><br><span class="line">│   └── bottom_logo@3x.png</span><br></pre></td></tr></table></figure></code></pre><p>其中<code>Content.json</code>为</p><pre><code><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;images&quot;</span> <span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">        <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;idiom&quot;</span> <span class="punctuation">:</span> <span class="string">&quot;universal&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;filename&quot;</span> <span class="punctuation">:</span> <span class="string">&quot;bottom_logo@2x.png&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;scale&quot;</span> <span class="punctuation">:</span> <span class="string">&quot;2x&quot;</span></span><br><span class="line">        <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;idiom&quot;</span> <span class="punctuation">:</span> <span class="string">&quot;universal&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;filename&quot;</span> <span class="punctuation">:</span> <span class="string">&quot;bottom_logo@3x.png&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;scale&quot;</span> <span class="punctuation">:</span> <span class="string">&quot;3x&quot;</span></span><br><span class="line">        <span class="punctuation">&#125;</span></span><br><span class="line">    <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;info&quot;</span> <span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;version&quot;</span> <span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;author&quot;</span> <span class="punctuation">:</span> <span class="string">&quot;xcode&quot;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure></code></pre><ol start="2"><li>使用<code>actool</code>编译<code>Images.xcassets</code></li></ol><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 输入</span></span><br><span class="line">xcassetPath=<span class="string">&quot;path/to/Images.xcassets&quot;</span></span><br><span class="line"><span class="comment"># 输出文件夹</span></span><br><span class="line">outputPath=<span class="string">&quot;path/to/outputpath&quot;</span></span><br><span class="line"></span><br><span class="line">dependenciesPath=<span class="string">&quot;path/to/outputpath/dependenciesPath&quot;</span></span><br><span class="line">generatedInfoPath=<span class="string">&quot;path/to/outputpath/generatedInfoPath&quot;</span></span><br><span class="line"></span><br><span class="line">/usr/bin/actool                                             \</span><br><span class="line">      --output-format <span class="string">&quot;human-readable-text&quot;</span>                 \</span><br><span class="line">      --notices                                             \</span><br><span class="line">      --export-dependency-info <span class="string">&quot;<span class="variable">$dependenciesPath</span>&quot;</span>          \</span><br><span class="line">      --output-partial-info-plist <span class="string">&quot;<span class="variable">$generatedInfoPath</span>&quot;</span>      \</span><br><span class="line">      --app-icon <span class="string">&quot;AppIcon&quot;</span>                                  \</span><br><span class="line">      --compress-pngs                                       \</span><br><span class="line">      --enable-on-demand-resources <span class="string">&quot;YES&quot;</span>                    \</span><br><span class="line">      --development-region <span class="string">&quot;English&quot;</span>                        \</span><br><span class="line">      --target-device <span class="string">&quot;iphone&quot;</span>                              \</span><br><span class="line">      --minimum-deployment-target <span class="string">&quot;9.0&quot;</span>                     \</span><br><span class="line">      --platform <span class="string">&quot;iphoneos&quot;</span>                                 \</span><br><span class="line">      --compile <span class="string">&quot;<span class="variable">$outputPath</span>&quot;</span> <span class="string">&quot;<span class="variable">$xcassetPath</span>&quot;</span></span><br></pre></td></tr></table></figure><p>编译后得到<code>Assets.car</code></p><ol start="3"><li>由于<code>Images.xcassets</code>已经编译好了，去掉工程对<code>Images.xcassets</code>的引用，并且把编译好的<code>Assets.car</code>引入工程，上面我们只把<code>jpg/png</code>图片编译进了<code>Assets.car</code>，对于其他文件（不多），这里我通过工具单独引入工程</li></ol><img src="/images/post/xcodebuildopt/xcode_xcassets.png" class="" width="800"><ol start="4"><li>把<code>Run Script</code>中的图片拷贝去掉，我这里的是去掉<code>Pods-xxx-resources.sh</code>文件中对图片资源的引用</li></ol><p>经过上面一番处理后，重新测试，数据对比如下</p><table><thead><tr><th></th><th>优化前</th><th>优化后</th></tr></thead><tbody><tr><td>Copy Pod Resources</td><td>11s</td><td>0.1s（减少97%）</td></tr><tr><td>Run Custom Script</td><td>32.5</td><td>0s(减少100%)</td></tr><tr><td>Sign</td><td>4.5</td><td>2.5s（减少45%）</td></tr></tbody></table><p>从上面的测试结果来看，速度提升效果显著</p><blockquote><p>因为优化后的方案有前置处理时间，需要把编译图片为<code>Assets.car</code>，这个时间没算上，上面的对比仅供参考</p></blockquote><p>需要注意的是，<code>Assets.car</code>的图片只能通过<code>UIImage.named(&quot;xxx&quot;)</code>读取，如果项目中是通过路径</p><p>上面的操作过于繁琐，这里我写了一个工具进行处理【一键操作】</p><img src="/images/post/xcodebuildopt/xcode_build_opt_tool.png" class="" width="800"><h3 id="关于Linking"><a href="#关于Linking" class="headerlink" title="关于Linking"></a>关于Linking</h3><p>有一个第三方的linking工具<a href="https://github.com/michaeleisel/zld">zld</a>，优化的缓存策略，在<code>Other Linker Flag</code>添加<code>-fuse-ld=&#39;/path/to/zld&#39; -Wl,-zld_original_ld_path,$(DT_TOOLCHAIN_DIR)/usr/bin/ld</code>即可</p><p>经测试，Linking时间从<code>15s</code>减少到<code>10.1s</code>，减少了<code>32%</code></p><h3 id="关于CodeSign"><a href="#关于CodeSign" class="headerlink" title="关于CodeSign"></a>关于CodeSign</h3><ol><li><p>Xcode默认使用<code>SHA-256</code>签名，我们可以改成更快的<code>SHA-1</code>，由于只是本地开发调试使用，不会有安全性问题，在<code>Other Code Signing Flags</code>添加<code>--digest-algorithm=sha1</code></p></li><li><p>iOS校验签名只会二进制的签名，其他文件不签名也不会报错，可以通过过滤文件减少签名的文件，在<code>Other Code Signing Flags</code>添加<code>--resource-rules=/path/to/rule.plist</code>，<code>rule.plist</code>文件如下</p> <figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">plist</span> <span class="keyword">PUBLIC</span> <span class="string">&quot;-//Apple//DTD PLIST 1.0//EN&quot;</span> <span class="string">&quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">plist</span> <span class="attr">version</span>=<span class="string">&quot;1.0&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dict</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">key</span>&gt;</span>rules<span class="tag">&lt;/<span class="name">key</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dict</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">key</span>&gt;</span>.*<span class="tag">&lt;/<span class="name">key</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">false</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dict</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dict</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">plist</span>&gt;</span></span><br></pre></td></tr></table></figure></li></ol><p>上面处理并不会影响正常开发，通过上面处理编译后的包<code>_CodeSignature/CodeResources</code>文件从<code>2.88 MB</code>减少到<code>589 Byte</code>，减少了<code>99.98%</code><br>Sign时间从<code>2.2s</code>减少到<code>0.5s</code>，减少了<code>77%</code></p><h3 id="关于Deploy"><a href="#关于Deploy" class="headerlink" title="关于Deploy"></a>关于Deploy</h3><p>由于<code>APFS</code>的特性，相同的文件不会深拷贝，省去拷贝的时间，由于文件数量大大减少，IO次数也大幅提高，如果不改动文件的话，基本可以达到秒启动，由于Xcode没有输出安装所有的时间，这里使用<code>ideviceinstaller</code>进行测试，这里把优化前后打出来的<code>xxx.app</code>安装到手机上（卸载），测试时间</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 安装ideviceinstaller</span></span><br><span class="line"><span class="comment"># brew install -HEAD libimobiledevice</span></span><br><span class="line"></span><br><span class="line">ideviceinstaller -i xxx.ipa</span><br></pre></td></tr></table></figure><p>优化前：<code>40s</code>，优化后：<code>18s</code>，时间减少了<code>55%</code></p><h2 id="综合统计"><a href="#综合统计" class="headerlink" title="综合统计"></a>综合统计</h2><h3 id="微观统计"><a href="#微观统计" class="headerlink" title="微观统计"></a>微观统计</h3><table><thead><tr><th></th><th>优化前</th><th>优化后</th></tr></thead><tbody><tr><td>Linking</td><td>15s</td><td>10.1s（下降32%）</td></tr><tr><td>Copy Pod Resources</td><td>11s</td><td>0.1（减少97%）</td></tr><tr><td>Run Custom Script</td><td>32.5s</td><td>0（下降100%)）</td></tr><tr><td>Sign</td><td>2.2s</td><td>0.5s（下降77%）</td></tr><tr><td>Deploy</td><td>40s</td><td>18s（下降55%）</td></tr><tr><td>汇总</td><td>100.7s</td><td>28.7s（下降71%）</td></tr></tbody></table><blockquote><p>数据来源：Xcode编译日志</p></blockquote><img src="/images/post/xcodebuildopt/xcode_io_time.png" class=""><h3 id="宏观统计"><a href="#宏观统计" class="headerlink" title="宏观统计"></a>宏观统计</h3><p>为了减少编译时间的影响，工程中大多数组件都使用二进制库，数据来源为Xcode编译打包后显示的时间（不包含Deploy和Run），如下</p><img src="/images/post/xcodebuildopt/xcode_build_time.png" class="" width="400"><table><thead><tr><th></th><th>首次</th><th>二次（修改代码）</th><th>二次（不改代码）</th></tr></thead><tbody><tr><td>优化前</td><td>88.75s</td><td>27.4s</td><td>9.2s</td></tr><tr><td>优化后</td><td>37.35s</td><td>14.2s</td><td>2.2s</td></tr><tr><td>对比</td><td>减少58%</td><td>减少48%</td><td>减少76%</td></tr></tbody></table> <img src="/images/post/xcodebuildopt/xcode_macro_time.png" class=""><blockquote><p>测试设备：Mac Mini M1 16G</p></blockquote><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>经过一段时间的使用，上面所有操作都是基于对开发效率的提升（for DEBUG），通过一些细节优化，可以把项目入侵降到最小，可以做到提高效率的同时，基本不影响日常开发</p><p>市面上大多数App包里面都会带非常多图片资源，图片太多确实会影响调试性能，减少图片数量从而减少IO次数，提升开发体验明显</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;Xcode是增量编译的，所以日常开发很多时候，我们都是改少量的代码或不改代码而重复调试，实际使用发现，从工程要跑到手机上调试仍然非常耗时，由于缓存的存在，编译可能不是最耗时的环节了，这里探究和优化影响&lt;code&gt;编译完成&lt;/code&gt;到&lt;code&gt;App启动调试&lt;/code&gt;速度的因素。&lt;/p&gt;</summary>
    
    
    
    <category term="iOS" scheme="http://zhengbomo.github.io/categories/iOS/"/>
    
    
    <category term="iOS" scheme="http://zhengbomo.github.io/tags/iOS/"/>
    
  </entry>
  
  <entry>
    <title>xx直播编译优化(减少372.7s)</title>
    <link href="http://zhengbomo.github.io/2021-09-27/fanxing-build-optimize/"/>
    <id>http://zhengbomo.github.io/2021-09-27/fanxing-build-optimize/</id>
    <published>2021-09-27T09:49:32.000Z</published>
    <updated>2021-09-27T09:49:32.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="一、背景"><a href="#一、背景" class="headerlink" title="一、背景"></a>一、背景</h2><p><code>编译</code>对于开发者可以说是最为频繁的操作，编译速度的快慢会极大的影响我们的开发效率，对于xx直播App来说，由于代码量大，加上代码结构设计不合理等原因，导致编译速度非常慢，每次启动App（即使不改动代码）需要3分钟的时间才能跑在手机上，而一次完整的编译，则需要15分钟，严重影响日常的业务开发和调试，现阶段编译速度的优化显得尤为重要，主要的时间消耗有</p><span id="more"></span><ul><li>Compile</li><li>Linking</li><li>Run Script</li><li>Sign</li><li>Install（文件多可能是主因）</li></ul><p>其中<code>Compile</code>时间占用<code>90%</code>以上，这里只关注Compile</p><h2 id="二、调研"><a href="#二、调研" class="headerlink" title="二、调研"></a>二、调研</h2><p>Xcode是基于<code>llvm</code>编译的，llvm编译器的编译过程主要分为3个部分</p><ul><li>前端（Frontend）：负责解析源码，检查错误，生成抽象语法树（AST），并把 AST 转化成类汇编中间代码</li><li>优化器（Optimizer）：对中间代码进行架构无关的优化，提高运行效率，减少代码体积，例如无效代码，无用变量等</li><li>后端（Backend）：把中间代码转换成目标平台的机器码</li></ul><p>其中我们代码影响最大的<code>前端</code>，这里我们只关注前端，前端处理主要有</p><ol><li>预处理：这阶段的工作主要是</li><li>头文件展开处理</li><li>宏展开&#x2F;替换，预编译指令处理</li><li>注释去除处理</li><li>编译：这阶段做的事情比较多</li><li>词法分析（Lexical Analysis）：将代码转换成一系列 token</li><li>语法分析（Semantic Analysis）：将token树抽象语法树 AST</li><li>静态分析（Static Analysis）：检查代码错误，例如参数类型是否错误，调用对象方法是否有实现</li><li>中间代码生成（Code Generation）：将语法树自顶向下遍历逐步翻译成 LLVM IR</li></ol><p>llvm9.0之后添加了一个新的编译参数可以用于统计编译过程中各个阶段的耗时（<code>-ftime-trace</code>），利用该参数和编译器我们可以得到项目中所有文件编译的详细时间信息</p><h3 id="2-1-ftime-trace测试"><a href="#2-1-ftime-trace测试" class="headerlink" title="2.1 -ftime-trace测试"></a>2.1 -ftime-trace测试</h3><ol><li><p>下载llvm编译器，<code>9.0</code>以上的版本均可，解压后得到clang+llvm-9.0.0，我这里放到（~&#x2F;development&#x2F;clang+llvm-9.0.0）</p></li><li><p>在需要统计编译时间的Target中修改clang前端编译器（CC和CXX），和编译参数（OtherCFlag）</p> <figure class="highlight arcade"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">CC: <span class="regexp">/path/</span>to/clang</span><br><span class="line">CXX: <span class="regexp">/path/</span>to/clang++</span><br><span class="line">Other C Flag: 添加-ftime-trace</span><br></pre></td></tr></table></figure></li><li><p>关闭INDEX：<code>COMPILER_INDEX_STORE_ENABLE=NO</code></p></li><li><p>编译后，在目标文件（<code>xxx.o</code>）同个目录下，会生成<code>xxx.json</code>文件，如</p><ul><li>YYTimer.o</li><li>YYTimer.json</li></ul></li><li><p>整个json文件如下</p> <figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span><span class="attr">&quot;traceEvents&quot;</span><span class="punctuation">:</span><span class="punctuation">[</span><span class="punctuation">&#123;</span><span class="attr">&quot;pid&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;tid&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">,</span><span class="attr">&quot;ph&quot;</span><span class="punctuation">:</span><span class="string">&quot;X&quot;</span><span class="punctuation">,</span><span class="attr">&quot;ts&quot;</span><span class="punctuation">:</span><span class="number">21778</span><span class="punctuation">,</span><span class="attr">&quot;dur&quot;</span><span class="punctuation">:</span><span class="number">43204</span><span class="punctuation">,</span><span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="string">&quot;Module Load&quot;</span><span class="punctuation">,</span><span class="attr">&quot;args&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span><span class="attr">&quot;detail&quot;</span><span class="punctuation">:</span><span class="string">&quot;UIKit&quot;</span><span class="punctuation">&#125;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span><span class="punctuation">&#123;</span><span class="attr">&quot;pid&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;tid&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">,</span><span class="attr">&quot;ph&quot;</span><span class="punctuation">:</span><span class="string">&quot;X&quot;</span><span class="punctuation">,</span><span class="attr">&quot;ts&quot;</span><span class="punctuation">:</span><span class="number">21329</span><span class="punctuation">,</span><span class="attr">&quot;dur&quot;</span><span class="punctuation">:</span><span class="number">44253</span><span class="punctuation">,</span><span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="string">&quot;Source&quot;</span><span class="punctuation">,</span><span class="attr">&quot;args&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span><span class="attr">&quot;detail&quot;</span><span class="punctuation">:</span><span class="string">&quot;/Users/bomo/Documents/Code/iOS/Work/Analyze/4986-02-header2/FanXing/Pods/Target Support Files/KGThirdParty/KGThirdParty-prefix.pch&quot;</span><span class="punctuation">&#125;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span><span class="punctuation">&#123;</span><span class="attr">&quot;pid&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;tid&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">,</span><span class="attr">&quot;ph&quot;</span><span class="punctuation">:</span><span class="string">&quot;X&quot;</span><span class="punctuation">,</span><span class="attr">&quot;ts&quot;</span><span class="punctuation">:</span><span class="number">66818</span><span class="punctuation">,</span><span class="attr">&quot;dur&quot;</span><span class="punctuation">:</span><span class="number">1868</span><span class="punctuation">,</span><span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="string">&quot;Source&quot;</span><span class="punctuation">,</span><span class="attr">&quot;args&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span><span class="attr">&quot;detail&quot;</span><span class="punctuation">:</span><span class="string">&quot;/Users/bomo/Documents/Code/iOS/Work/Analyze/4986-02-header2/FanXing/Pods/KGThirdParty/KGThirdParty/YYKit/Utility/YYTimer.h&quot;</span><span class="punctuation">&#125;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span><span class="punctuation">&#123;</span><span class="attr">&quot;pid&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;tid&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">,</span><span class="attr">&quot;ph&quot;</span><span class="punctuation">:</span><span class="string">&quot;X&quot;</span><span class="punctuation">,</span><span class="attr">&quot;ts&quot;</span><span class="punctuation">:</span><span class="number">20679</span><span class="punctuation">,</span><span class="attr">&quot;dur&quot;</span><span class="punctuation">:</span><span class="number">70778</span><span class="punctuation">,</span><span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="string">&quot;Frontend&quot;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span><span class="punctuation">&#123;</span><span class="attr">&quot;pid&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;tid&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">,</span><span class="attr">&quot;ph&quot;</span><span class="punctuation">:</span><span class="string">&quot;X&quot;</span><span class="punctuation">,</span><span class="attr">&quot;ts&quot;</span><span class="punctuation">:</span><span class="number">91457</span><span class="punctuation">,</span><span class="attr">&quot;dur&quot;</span><span class="punctuation">:</span><span class="number">595</span><span class="punctuation">,</span><span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="string">&quot;Frontend&quot;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span><span class="punctuation">&#123;</span><span class="attr">&quot;pid&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;tid&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">,</span><span class="attr">&quot;ph&quot;</span><span class="punctuation">:</span><span class="string">&quot;X&quot;</span><span class="punctuation">,</span><span class="attr">&quot;ts&quot;</span><span class="punctuation">:</span><span class="number">94811</span><span class="punctuation">,</span><span class="attr">&quot;dur&quot;</span><span class="punctuation">:</span><span class="number">617</span><span class="punctuation">,</span><span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="string">&quot;RunPass&quot;</span><span class="punctuation">,</span><span class="attr">&quot;args&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span><span class="attr">&quot;detail&quot;</span><span class="punctuation">:</span><span class="string">&quot;AArch64 Assembly Printer&quot;</span><span class="punctuation">&#125;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span><span class="punctuation">&#123;</span><span class="attr">&quot;pid&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;tid&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">,</span><span class="attr">&quot;ph&quot;</span><span class="punctuation">:</span><span class="string">&quot;X&quot;</span><span class="punctuation">,</span><span class="attr">&quot;ts&quot;</span><span class="punctuation">:</span><span class="number">94084</span><span class="punctuation">,</span><span class="attr">&quot;dur&quot;</span><span class="punctuation">:</span><span class="number">1370</span><span class="punctuation">,</span><span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="string">&quot;OptFunction&quot;</span><span class="punctuation">,</span><span class="attr">&quot;args&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span><span class="attr">&quot;detail&quot;</span><span class="punctuation">:</span><span class="string">&quot;\u0001+[YYTimer timerWithTimeInterval:target:selector:repeats:]&quot;</span><span class="punctuation">&#125;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span><span class="punctuation">&#123;</span><span class="attr">&quot;pid&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;tid&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">,</span><span class="attr">&quot;ph&quot;</span><span class="punctuation">:</span><span class="string">&quot;X&quot;</span><span class="punctuation">,</span><span class="attr">&quot;ts&quot;</span><span class="punctuation">:</span><span class="number">95456</span><span class="punctuation">,</span><span class="attr">&quot;dur&quot;</span><span class="punctuation">:</span><span class="number">526</span><span class="punctuation">,</span><span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="string">&quot;OptFunction&quot;</span><span class="punctuation">,</span><span class="attr">&quot;args&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span><span class="attr">&quot;detail&quot;</span><span class="punctuation">:</span><span class="string">&quot;\u0001-[YYTimer init]&quot;</span><span class="punctuation">&#125;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span><span class="punctuation">&#123;</span><span class="attr">&quot;pid&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;tid&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">,</span><span class="attr">&quot;ph&quot;</span><span class="punctuation">:</span><span class="string">&quot;X&quot;</span><span class="punctuation">,</span><span class="attr">&quot;ts&quot;</span><span class="punctuation">:</span><span class="number">95983</span><span class="punctuation">,</span><span class="attr">&quot;dur&quot;</span><span class="punctuation">:</span><span class="number">1123</span><span class="punctuation">,</span><span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="string">&quot;OptFunction&quot;</span><span class="punctuation">,</span><span class="attr">&quot;args&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span><span class="attr">&quot;detail&quot;</span><span class="punctuation">:</span><span class="string">&quot;\u0001-[YYTimer initWithFireTime:interval:target:selector:repeats:]&quot;</span><span class="punctuation">&#125;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span><span class="punctuation">&#123;</span><span class="attr">&quot;pid&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;tid&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">,</span><span class="attr">&quot;ph&quot;</span><span class="punctuation">:</span><span class="string">&quot;X&quot;</span><span class="punctuation">,</span><span class="attr">&quot;ts&quot;</span><span class="punctuation">:</span><span class="number">93022</span><span class="punctuation">,</span><span class="attr">&quot;dur&quot;</span><span class="punctuation">:</span><span class="number">6873</span><span class="punctuation">,</span><span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="string">&quot;OptModule&quot;</span><span class="punctuation">,</span><span class="attr">&quot;args&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span><span class="attr">&quot;detail&quot;</span><span class="punctuation">:</span><span class="string">&quot;/Users/bomo/Documents/Code/iOS/Work/Analyze/4986-02-header2/FanXing/Pods/KGThirdParty/KGThirdParty/YYKit/Utility/YYTimer.m&quot;</span><span class="punctuation">&#125;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span><span class="punctuation">&#123;</span><span class="attr">&quot;pid&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;tid&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">,</span><span class="attr">&quot;ph&quot;</span><span class="punctuation">:</span><span class="string">&quot;X&quot;</span><span class="punctuation">,</span><span class="attr">&quot;ts&quot;</span><span class="punctuation">:</span><span class="number">93013</span><span class="punctuation">,</span><span class="attr">&quot;dur&quot;</span><span class="punctuation">:</span><span class="number">6977</span><span class="punctuation">,</span><span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="string">&quot;CodeGenPasses&quot;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span><span class="punctuation">&#123;</span><span class="attr">&quot;pid&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;tid&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">,</span><span class="attr">&quot;ph&quot;</span><span class="punctuation">:</span><span class="string">&quot;X&quot;</span><span class="punctuation">,</span><span class="attr">&quot;ts&quot;</span><span class="punctuation">:</span><span class="number">92061</span><span class="punctuation">,</span><span class="attr">&quot;dur&quot;</span><span class="punctuation">:</span><span class="number">8108</span><span class="punctuation">,</span><span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="string">&quot;Backend&quot;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span><span class="punctuation">&#123;</span><span class="attr">&quot;pid&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;tid&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">,</span><span class="attr">&quot;ph&quot;</span><span class="punctuation">:</span><span class="string">&quot;X&quot;</span><span class="punctuation">,</span><span class="attr">&quot;ts&quot;</span><span class="punctuation">:</span><span class="number">66</span><span class="punctuation">,</span><span class="attr">&quot;dur&quot;</span><span class="punctuation">:</span><span class="number">100526</span><span class="punctuation">,</span><span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="string">&quot;ExecuteCompiler&quot;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span><span class="punctuation">&#123;</span><span class="attr">&quot;pid&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;tid&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;ph&quot;</span><span class="punctuation">:</span><span class="string">&quot;X&quot;</span><span class="punctuation">,</span><span class="attr">&quot;ts&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">,</span><span class="attr">&quot;dur&quot;</span><span class="punctuation">:</span><span class="number">100526</span><span class="punctuation">,</span><span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="string">&quot;Total ExecuteCompiler&quot;</span><span class="punctuation">,</span><span class="attr">&quot;args&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span><span class="attr">&quot;count&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;avg ms&quot;</span><span class="punctuation">:</span><span class="number">100</span><span class="punctuation">&#125;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span><span class="punctuation">&#123;</span><span class="attr">&quot;pid&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;tid&quot;</span><span class="punctuation">:</span><span class="number">2</span><span class="punctuation">,</span><span class="attr">&quot;ph&quot;</span><span class="punctuation">:</span><span class="string">&quot;X&quot;</span><span class="punctuation">,</span><span class="attr">&quot;ts&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">,</span><span class="attr">&quot;dur&quot;</span><span class="punctuation">:</span><span class="number">71372</span><span class="punctuation">,</span><span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="string">&quot;Total Frontend&quot;</span><span class="punctuation">,</span><span class="attr">&quot;args&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span><span class="attr">&quot;count&quot;</span><span class="punctuation">:</span><span class="number">2</span><span class="punctuation">,</span><span class="attr">&quot;avg ms&quot;</span><span class="punctuation">:</span><span class="number">35</span><span class="punctuation">&#125;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span><span class="punctuation">&#123;</span><span class="attr">&quot;pid&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;tid&quot;</span><span class="punctuation">:</span><span class="number">3</span><span class="punctuation">,</span><span class="attr">&quot;ph&quot;</span><span class="punctuation">:</span><span class="string">&quot;X&quot;</span><span class="punctuation">,</span><span class="attr">&quot;ts&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">,</span><span class="attr">&quot;dur&quot;</span><span class="punctuation">:</span><span class="number">46121</span><span class="punctuation">,</span><span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="string">&quot;Total Source&quot;</span><span class="punctuation">,</span><span class="attr">&quot;args&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span><span class="attr">&quot;count&quot;</span><span class="punctuation">:</span><span class="number">2</span><span class="punctuation">,</span><span class="attr">&quot;avg ms&quot;</span><span class="punctuation">:</span><span class="number">23</span><span class="punctuation">&#125;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span><span class="punctuation">&#123;</span><span class="attr">&quot;pid&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;tid&quot;</span><span class="punctuation">:</span><span class="number">4</span><span class="punctuation">,</span><span class="attr">&quot;ph&quot;</span><span class="punctuation">:</span><span class="string">&quot;X&quot;</span><span class="punctuation">,</span><span class="attr">&quot;ts&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">,</span><span class="attr">&quot;dur&quot;</span><span class="punctuation">:</span><span class="number">43318</span><span class="punctuation">,</span><span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="string">&quot;Total Module Load&quot;</span><span class="punctuation">,</span><span class="attr">&quot;args&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span><span class="attr">&quot;count&quot;</span><span class="punctuation">:</span><span class="number">3</span><span class="punctuation">,</span><span class="attr">&quot;avg ms&quot;</span><span class="punctuation">:</span><span class="number">14</span><span class="punctuation">&#125;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span><span class="punctuation">&#123;</span><span class="attr">&quot;pid&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;tid&quot;</span><span class="punctuation">:</span><span class="number">5</span><span class="punctuation">,</span><span class="attr">&quot;ph&quot;</span><span class="punctuation">:</span><span class="string">&quot;X&quot;</span><span class="punctuation">,</span><span class="attr">&quot;ts&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">,</span><span class="attr">&quot;dur&quot;</span><span class="punctuation">:</span><span class="number">8108</span><span class="punctuation">,</span><span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="string">&quot;Total Backend&quot;</span><span class="punctuation">,</span><span class="attr">&quot;args&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span><span class="attr">&quot;count&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;avg ms&quot;</span><span class="punctuation">:</span><span class="number">8</span><span class="punctuation">&#125;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span><span class="punctuation">&#123;</span><span class="attr">&quot;pid&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;tid&quot;</span><span class="punctuation">:</span><span class="number">6</span><span class="punctuation">,</span><span class="attr">&quot;ph&quot;</span><span class="punctuation">:</span><span class="string">&quot;X&quot;</span><span class="punctuation">,</span><span class="attr">&quot;ts&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">,</span><span class="attr">&quot;dur&quot;</span><span class="punctuation">:</span><span class="number">7094</span><span class="punctuation">,</span><span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="string">&quot;Total OptModule&quot;</span><span class="punctuation">,</span><span class="attr">&quot;args&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span><span class="attr">&quot;count&quot;</span><span class="punctuation">:</span><span class="number">2</span><span class="punctuation">,</span><span class="attr">&quot;avg ms&quot;</span><span class="punctuation">:</span><span class="number">3</span><span class="punctuation">&#125;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span><span class="punctuation">&#123;</span><span class="attr">&quot;pid&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;tid&quot;</span><span class="punctuation">:</span><span class="number">7</span><span class="punctuation">,</span><span class="attr">&quot;ph&quot;</span><span class="punctuation">:</span><span class="string">&quot;X&quot;</span><span class="punctuation">,</span><span class="attr">&quot;ts&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">,</span><span class="attr">&quot;dur&quot;</span><span class="punctuation">:</span><span class="number">6977</span><span class="punctuation">,</span><span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="string">&quot;Total CodeGenPasses&quot;</span><span class="punctuation">,</span><span class="attr">&quot;args&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span><span class="attr">&quot;count&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;avg ms&quot;</span><span class="punctuation">:</span><span class="number">6</span><span class="punctuation">&#125;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span><span class="punctuation">&#123;</span><span class="attr">&quot;pid&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;tid&quot;</span><span class="punctuation">:</span><span class="number">8</span><span class="punctuation">,</span><span class="attr">&quot;ph&quot;</span><span class="punctuation">:</span><span class="string">&quot;X&quot;</span><span class="punctuation">,</span><span class="attr">&quot;ts&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">,</span><span class="attr">&quot;dur&quot;</span><span class="punctuation">:</span><span class="number">5819</span><span class="punctuation">,</span><span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="string">&quot;Total OptFunction&quot;</span><span class="punctuation">,</span><span class="attr">&quot;args&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span><span class="attr">&quot;count&quot;</span><span class="punctuation">:</span><span class="number">40</span><span class="punctuation">,</span><span class="attr">&quot;avg ms&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">&#125;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span><span class="punctuation">&#123;</span><span class="attr">&quot;pid&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;tid&quot;</span><span class="punctuation">:</span><span class="number">9</span><span class="punctuation">,</span><span class="attr">&quot;ph&quot;</span><span class="punctuation">:</span><span class="string">&quot;X&quot;</span><span class="punctuation">,</span><span class="attr">&quot;ts&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">,</span><span class="attr">&quot;dur&quot;</span><span class="punctuation">:</span><span class="number">5585</span><span class="punctuation">,</span><span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="string">&quot;Total RunPass&quot;</span><span class="punctuation">,</span><span class="attr">&quot;args&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span><span class="attr">&quot;count&quot;</span><span class="punctuation">:</span><span class="number">729</span><span class="punctuation">,</span><span class="attr">&quot;avg ms&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">&#125;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span><span class="punctuation">&#123;</span><span class="attr">&quot;pid&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;tid&quot;</span><span class="punctuation">:</span><span class="number">10</span><span class="punctuation">,</span><span class="attr">&quot;ph&quot;</span><span class="punctuation">:</span><span class="string">&quot;X&quot;</span><span class="punctuation">,</span><span class="attr">&quot;ts&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">,</span><span class="attr">&quot;dur&quot;</span><span class="punctuation">:</span><span class="number">479</span><span class="punctuation">,</span><span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="string">&quot;Total DebugType&quot;</span><span class="punctuation">,</span><span class="attr">&quot;args&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span><span class="attr">&quot;count&quot;</span><span class="punctuation">:</span><span class="number">88</span><span class="punctuation">,</span><span class="attr">&quot;avg ms&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">&#125;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span><span class="punctuation">&#123;</span><span class="attr">&quot;pid&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;tid&quot;</span><span class="punctuation">:</span><span class="number">11</span><span class="punctuation">,</span><span class="attr">&quot;ph&quot;</span><span class="punctuation">:</span><span class="string">&quot;X&quot;</span><span class="punctuation">,</span><span class="attr">&quot;ts&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">,</span><span class="attr">&quot;dur&quot;</span><span class="punctuation">:</span><span class="number">279</span><span class="punctuation">,</span><span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="string">&quot;Total CodeGen Function&quot;</span><span class="punctuation">,</span><span class="attr">&quot;args&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span><span class="attr">&quot;count&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;avg ms&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">&#125;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span><span class="punctuation">&#123;</span><span class="attr">&quot;pid&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;tid&quot;</span><span class="punctuation">:</span><span class="number">12</span><span class="punctuation">,</span><span class="attr">&quot;ph&quot;</span><span class="punctuation">:</span><span class="string">&quot;X&quot;</span><span class="punctuation">,</span><span class="attr">&quot;ts&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">,</span><span class="attr">&quot;dur&quot;</span><span class="punctuation">:</span><span class="number">223</span><span class="punctuation">,</span><span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="string">&quot;Total PerModulePasses&quot;</span><span class="punctuation">,</span><span class="attr">&quot;args&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span><span class="attr">&quot;count&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;avg ms&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">&#125;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span><span class="punctuation">&#123;</span><span class="attr">&quot;pid&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;tid&quot;</span><span class="punctuation">:</span><span class="number">13</span><span class="punctuation">,</span><span class="attr">&quot;ph&quot;</span><span class="punctuation">:</span><span class="string">&quot;X&quot;</span><span class="punctuation">,</span><span class="attr">&quot;ts&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">,</span><span class="attr">&quot;dur&quot;</span><span class="punctuation">:</span><span class="number">56</span><span class="punctuation">,</span><span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="string">&quot;Total Module LoadIndex&quot;</span><span class="punctuation">,</span><span class="attr">&quot;args&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span><span class="attr">&quot;count&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;avg ms&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">&#125;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span><span class="punctuation">&#123;</span><span class="attr">&quot;pid&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;tid&quot;</span><span class="punctuation">:</span><span class="number">14</span><span class="punctuation">,</span><span class="attr">&quot;ph&quot;</span><span class="punctuation">:</span><span class="string">&quot;X&quot;</span><span class="punctuation">,</span><span class="attr">&quot;ts&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">,</span><span class="attr">&quot;dur&quot;</span><span class="punctuation">:</span><span class="number">21</span><span class="punctuation">,</span><span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="string">&quot;Total PerFunctionPasses&quot;</span><span class="punctuation">,</span><span class="attr">&quot;args&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span><span class="attr">&quot;count&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;avg ms&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">&#125;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span><span class="punctuation">&#123;</span><span class="attr">&quot;pid&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;tid&quot;</span><span class="punctuation">:</span><span class="number">15</span><span class="punctuation">,</span><span class="attr">&quot;ph&quot;</span><span class="punctuation">:</span><span class="string">&quot;X&quot;</span><span class="punctuation">,</span><span class="attr">&quot;ts&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">,</span><span class="attr">&quot;dur&quot;</span><span class="punctuation">:</span><span class="number">3</span><span class="punctuation">,</span><span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="string">&quot;Total PerformPendingInstantiations&quot;</span><span class="punctuation">,</span><span class="attr">&quot;args&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span><span class="attr">&quot;count&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;avg ms&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">&#125;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span><span class="punctuation">&#123;</span><span class="attr">&quot;cat&quot;</span><span class="punctuation">:</span><span class="string">&quot;&quot;</span><span class="punctuation">,</span><span class="attr">&quot;pid&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;tid&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">,</span><span class="attr">&quot;ts&quot;</span><span class="punctuation">:</span><span class="number">0</span><span class="punctuation">,</span><span class="attr">&quot;ph&quot;</span><span class="punctuation">:</span><span class="string">&quot;M&quot;</span><span class="punctuation">,</span><span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="string">&quot;process_name&quot;</span><span class="punctuation">,</span><span class="attr">&quot;args&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span><span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="string">&quot;clang-10&quot;</span><span class="punctuation">&#125;</span><span class="punctuation">&#125;</span><span class="punctuation">]</span><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure></li><li><p>可以用Chrome打开<code>chrome://tracing</code>，然后拖入该json文件，可以直观的看到各个阶段编译消耗的时间</p> <img src="/images/post/fanxingbuild/build_tracing.png" class="" width="1000"></li><li><p>这里摘取<code>YYTimer.json</code>里面的一个片段</p> <figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;pid&quot;</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;tid&quot;</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;ph&quot;</span><span class="punctuation">:</span> <span class="string">&quot;X&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;ts&quot;</span><span class="punctuation">:</span> <span class="number">66818</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;dur&quot;</span><span class="punctuation">:</span> <span class="number">1868</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Source&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;args&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;detail&quot;</span><span class="punctuation">:</span> <span class="string">&quot;/Users/bomo/Documents/Code/iOS/Work/FanXing/Pods/KGThirdParty/KGThirdParty/YYKit/Utility/YYTimer.h&quot;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line">    <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;pid&quot;</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;tid&quot;</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;ph&quot;</span><span class="punctuation">:</span> <span class="string">&quot;X&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;ts&quot;</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;dur&quot;</span><span class="punctuation">:</span> <span class="number">100526</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Total ExecuteCompiler&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;args&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;count&quot;</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;avg ms&quot;</span><span class="punctuation">:</span> <span class="number">100</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p> 这里的<code>name=Source</code>为头文件YYTimer.h预编译处理的时间，dur为时间，单位为<code>微秒</code>，name&#x3D;Total ExecuteCompiler为该文件的编译时间</p></li></ol><h2 id="三、项目测试"><a href="#三、项目测试" class="headerlink" title="三、项目测试"></a>三、项目测试</h2><p>通过上面的方法，编译整个项目，并统计所有文件的预编译处理时间，我这里用<code>python</code>扫描所有编译文件，然后做汇总统计</p><h3 id="3-1-各编译阶段耗时"><a href="#3-1-各编译阶段耗时" class="headerlink" title="3.1 各编译阶段耗时"></a>3.1 各编译阶段耗时</h3><table><thead><tr><th>编译类型</th><th>耗时</th></tr></thead><tbody><tr><td>Total ExecuteCompiler:</td><td>5077.09 秒</td></tr><tr><td>Total Frontend:</td><td>3571.56 秒</td></tr><tr><td>Total Source:</td><td>2479.86 秒</td></tr><tr><td>Total Module Load:</td><td>1598.68 秒</td></tr><tr><td>Total Backend:</td><td>396.09 秒</td></tr><tr><td>Total CodeGenPasses:</td><td>373.86 秒</td></tr><tr><td>Total OptModule:</td><td>371.34 秒</td></tr><tr><td>Total OptFunction:</td><td>272.03 秒</td></tr><tr><td>Total RunPass:</td><td>263.66 秒</td></tr><tr><td>Total Module Compile:</td><td>142.07 秒</td></tr></tbody></table><p>从上面数据看出<code>Source</code>耗时最长，占用<code>2479.86s</code>，占比较大</p><h3 id="3-2-头文件引用次数（TOP10）"><a href="#3-2-头文件引用次数（TOP10）" class="headerlink" title="3.2 头文件引用次数（TOP10）"></a>3.2 头文件引用次数（TOP10）</h3><p> 通过上面生成的json文件可以得到 </p><table><thead><tr><th>头文件</th><th>引用次数</th><th>平均耗时</th></tr></thead><tbody><tr><td>******Common-prefix.pch</td><td>1619次</td><td>51.91毫秒</td></tr><tr><td>******Singleton.h</td><td>712次</td><td>6.01毫秒</td></tr><tr><td>******User-prefix.pch</td><td>701次</td><td>50.23毫秒</td></tr><tr><td>******GiftList.h</td><td>623次</td><td>10.22毫秒</td></tr><tr><td>******ModelObject.h</td><td>617次</td><td>3.42毫秒</td></tr><tr><td>******Constants.h</td><td>617次</td><td>1.86毫秒</td></tr><tr><td>******LiveInfo.h</td><td>612次</td><td>3.34毫秒</td></tr><tr><td>******ProgramInfo.h</td><td>612次</td><td>5.58毫秒</td></tr><tr><td>******AnimationView.h</td><td>612次</td><td>2.27毫秒</td></tr><tr><td>******ViewDefine.h</td><td>611次</td><td>2.75毫秒</td></tr></tbody></table><blockquote><p>注：这里的引用，包含间接引用 * A引用C * B引用A * D引用B</p></blockquote><p>则C被引用3次，会参与3次预编译处理，当D被引用100次时，A,B,C也会被引用处理100次，编译器在编译的时候会有其他优化策略，具体次数可能会细微差异 </p><h3 id="3-3-头文件单次预处理耗时（TOP10）"><a href="#3-3-头文件单次预处理耗时（TOP10）" class="headerlink" title="3.3 头文件单次预处理耗时（TOP10）"></a>3.3 头文件单次预处理耗时（TOP10）</h3><table><thead><tr><th>头文件</th><th>耗时（平均）</th><th>引用次数</th></tr></thead><tbody><tr><td>******ResultVC.h</td><td>31971.25毫秒</td><td>1</td></tr><tr><td>******ListView.h</td><td>17175.35毫秒</td><td>1</td></tr><tr><td>******ItemEntity.h</td><td>16750.17毫秒</td><td>1</td></tr><tr><td>******GuideView.h</td><td>16623.81毫秒</td><td>1</td></tr><tr><td>******RecommendView.h</td><td>15125.93毫秒</td><td>1</td></tr><tr><td>******DrawerCell.h</td><td>13072.67毫秒</td><td>2</td></tr><tr><td>******HelperMsgContainer.h</td><td>13055.01毫秒</td><td>2</td></tr><tr><td>******RecordCell.h</td><td>10176.65毫秒</td><td>2</td></tr><tr><td>******VerifyDao.h</td><td>7482.75毫秒</td><td>2</td></tr><tr><td>******AppealModel.h</td><td>7247.25毫秒</td><td>4</td></tr></tbody></table><blockquote><p>注：由于头文件会多级引用，所以处理时间会叠加，这里仅供参考</p></blockquote><h3 id="3-4-头文件预处理总耗时（TOP10）"><a href="#3-4-头文件预处理总耗时（TOP10）" class="headerlink" title="3.4 头文件预处理总耗时（TOP10）"></a>3.4 头文件预处理总耗时（TOP10）</h3><table><thead><tr><th>头文件</th><th>总耗时</th><th>引用次数</th></tr></thead><tbody><tr><td>****Common.h</td><td>939.64秒</td><td>610</td></tr><tr><td>****AlertView.h</td><td>267.89秒</td><td>608</td></tr><tr><td>****Common-prefix.pch</td><td>84.04秒</td><td>1619</td></tr><tr><td>****MesageParse.h</td><td>58.98秒</td><td>600</td></tr><tr><td>****RoomManager.h</td><td>43.77秒</td><td>598</td></tr><tr><td>****User-prefix.pch</td><td>35.21秒</td><td>701</td></tr><tr><td>****VideoInfoModel.h</td><td>34.40秒</td><td>10</td></tr><tr><td>****VideoModel.h</td><td>33.91秒</td><td>8</td></tr><tr><td>****ResultVC.h</td><td>31.97秒</td><td>1</td></tr><tr><td>****Data-umbrella.h</td><td>31.47秒</td><td>64</td></tr></tbody></table><blockquote><p>注：<code>xxxxCommon.h</code>文件引用次数不是最多，但总耗时最长</p></blockquote><h2 id="4-方案"><a href="#4-方案" class="headerlink" title="4. 方案"></a>4. 方案</h2><p>通过上面测试数据可以看出，<code>xxxxCommon.h</code>预处理耗时最长，通过查看该文件可以看出 </p><ul><li>该文件引用了非常多头文件（500+），完全展开的话会非常大，符合耗时的预期 </li><li>项目中很多地方引用<code>xxxxCommon.h</code>文件可能只是用到里面其中一个或几个类，而有些地方为了图方便，就引用了这个大而全的文件，而引用整个<code>xxxxCommon.h</code>导致而预处理却花去了大量非必要的时间</li></ul><p>这里决定给<code>xxxxCommon.h</code>文件进行瘦身，逐步把引用迁移到更轻量的头文件引用，并在具体的文件中补齐其他缺失的头文件，**<code>头文件按需引用，减少不必要的预编译处理</code>** </p><h2 id="5-成效"><a href="#5-成效" class="headerlink" title="5. 成效"></a>5. 成效</h2><ul><li>机器：iMac (Retina 4K, 21.5-inch, 2019)， </li><li>CPU: 3 GHz 六核Intel Core i5 * 内存：16 GB 2667 MHz DDR4 * 显卡：Radeon Pro 560X 4 GB </li><li>Configuration: <code>DEBUG</code> </li><li>编译架构：<code>arm64</code></li></ul><h3 id="5-1宏观统计（6核）（减少110-9s）"><a href="#5-1宏观统计（6核）（减少110-9s）" class="headerlink" title="5.1宏观统计（6核）（减少110.9s）"></a>5.1宏观统计（6核）（减少110.9s）</h3><p>测试方法：</p><ol><li>关闭Xcode，关闭Chrome等大进程 </li><li>删除所有Xcode缓存文件（<code>~/Library/Developer/Xcode/DerivedData/</code>） </li><li>打开Xcode编译 </li><li>编译完成后，查看Xcode显示的编译总时间 </li><li>测试结果去掉最高最低值</li></ol><table><thead><tr><th>宏观统计</th><th>测试1</th><th>测试2</th><th>测试3</th><th>测试4</th><th>平均</th></tr></thead><tbody><tr><td>基础工程</td><td>447.9</td><td>446.3</td><td>452.7</td><td>453.2</td><td>450.025</td></tr><tr><td>优化后工程</td><td>339.8</td><td>336.8</td><td>338.1</td><td>341.7</td><td>339.1</td></tr></tbody></table><blockquote><p>注：部分子库用了二进制，这里更多关注差值</p></blockquote><h3 id="5-2-微观统计（减少372-7s）"><a href="#5-2-微观统计（减少372-7s）" class="headerlink" title="5.2 微观统计（减少372.7s）"></a>5.2 微观统计（减少372.7s）</h3><p> 测试方法： </p><ol><li>使用上面提到的llvm自带统计工具，然后汇总结果统计所有文件的<code>Total ExecuteCompiler</code> </li><li>测试结果去掉最高最低值 </li><li>去掉所有的clang插件</li></ol><table><thead><tr><th>微观统计</th><th>测试1</th><th>测试2</th><th>测试3</th><th>平均</th></tr></thead><tbody><tr><td>基础工程</td><td>1449.3</td><td>1445.8</td><td>1456.3</td><td>1450.4</td></tr><tr><td>优化后工程</td><td>1076.8</td><td>1092.3</td><td>1064.1</td><td>1077.7</td></tr></tbody></table><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><ol><li>上面优化修改了<code>2115</code>个文件，工作量还是比较大的，主要还是编码习惯和长期未规范使用带来量变积累，导致质变，平时养成良好的编码习惯，减少量变积累</li><li>Xcode在编译的时候会把<code>#import &lt;AAA/BBB.h&gt;</code>自动转成<code>@import AAA.BBB</code>，为了统一风格，建议统一使用<code>#import &lt;AAA/BBB.h&gt;</code>方式引用，详情见<a href="https://developer.apple.com/videos/play/wwdc2013/404/">WWDC2013-Advances in Objective-C</a> </li><li>坚持一个原则：<strong>按需引用</strong>，<strong>按需引用</strong>（最小import原则），请引用<code>#import &lt;FAFuncUnit/XXX.h&gt;</code>，而不是<code>#import &lt;LibA/LibA-umbrella.h&gt;</code></li></ol>]]></content>
    
    
    <summary type="html">&lt;h2 id=&quot;一、背景&quot;&gt;&lt;a href=&quot;#一、背景&quot; class=&quot;headerlink&quot; title=&quot;一、背景&quot;&gt;&lt;/a&gt;一、背景&lt;/h2&gt;&lt;p&gt;&lt;code&gt;编译&lt;/code&gt;对于开发者可以说是最为频繁的操作，编译速度的快慢会极大的影响我们的开发效率，对于xx直播App来说，由于代码量大，加上代码结构设计不合理等原因，导致编译速度非常慢，每次启动App（即使不改动代码）需要3分钟的时间才能跑在手机上，而一次完整的编译，则需要15分钟，严重影响日常的业务开发和调试，现阶段编译速度的优化显得尤为重要，主要的时间消耗有&lt;/p&gt;</summary>
    
    
    
    <category term="iOS" scheme="http://zhengbomo.github.io/categories/iOS/"/>
    
    
    <category term="iOS" scheme="http://zhengbomo.github.io/tags/iOS/"/>
    
  </entry>
  
  <entry>
    <title>（六）树莓派4B安装gitea</title>
    <link href="http://zhengbomo.github.io/2021-09-12/raspberrypi-gitea/"/>
    <id>http://zhengbomo.github.io/2021-09-12/raspberrypi-gitea/</id>
    <published>2021-09-12T08:56:54.000Z</published>
    <updated>2021-09-12T08:56:54.000Z</updated>
    
    <content type="html"><![CDATA[<p>最近git项目多了一些比较大的文件（数据库），第三方服务器的lfs基本都要收费，所以考虑自己搭建一个git服务器存放代码，关于git服务器有<code>gitea</code>，<code>gitlab</code>，gitlab功能太多，个人使用很多用不到，而且内存占用高，而gitea相对简洁，功能够用，选择gitea</p><span id="more"></span><h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 下载gitea</span></span><br><span class="line">wget -O gitea https://dl.gitea.io/gitea/1.17.2/gitea-1.17.2-linux-arm64</span><br><span class="line"></span><br><span class="line"><span class="comment"># 添加权限</span></span><br><span class="line"><span class="built_in">chmod</span> +x gitea</span><br></pre></td></tr></table></figure><h2 id="启动"><a href="#启动" class="headerlink" title="启动"></a>启动</h2><p>指定端口为<code>8899</code></p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./gitea web -p 8899</span><br></pre></td></tr></table></figure><p>访问<code>http://192.168.2.11:8899</code>就能进入gitea</p><img src="/images/post/raspberrypi/gitea_init.png" class="" width="600"><p>由于是自己用，我这里选择SQLite，配置完成后，会跳转到<code>http://localhost:3000/user/login</code>，由于服务在树莓派上，我们需要把localhost改为树莓派的地址，配置完成后会<code>custom/conf/app.ini</code>生成配置文件，我们需要吧localhost换成对应的地址，如果绑定了域名，可以换成域名</p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">APP_NAME</span> = Gitea: Git with a cup of tea</span><br><span class="line"><span class="attr">RUN_USER</span> = ubuntu</span><br><span class="line"><span class="attr">RUN_MODE</span> = prod</span><br><span class="line"></span><br><span class="line"><span class="section">[server]</span></span><br><span class="line"><span class="attr">LOCAL_ROOT_URL</span>   = http://<span class="number">192.168</span>.<span class="number">2.11</span>:<span class="number">8899</span>/</span><br><span class="line"><span class="attr">SSH_DOMAIN</span>       = <span class="number">192.168</span>.<span class="number">2.11</span></span><br><span class="line"><span class="attr">DOMAIN</span>           = <span class="number">192.168</span>.<span class="number">2.11</span></span><br><span class="line"><span class="attr">HTTP_PORT</span>        = <span class="number">8899</span></span><br><span class="line"><span class="attr">ROOT_URL</span>         = http://<span class="number">192.168</span>.<span class="number">2.11</span>:<span class="number">8899</span>/</span><br><span class="line"><span class="attr">DISABLE_SSH</span>      = <span class="literal">false</span></span><br><span class="line"><span class="attr">SSH_PORT</span>         = <span class="number">22222</span></span><br><span class="line"><span class="attr">LFS_START_SERVER</span> = <span class="literal">true</span></span><br><span class="line"><span class="attr">LFS_JWT_SECRET</span>   = JU6s1q1kgPVDIIKHRzsUkjJ5HgUSBknJGN7FyKf-hgY</span><br><span class="line"><span class="attr">OFFLINE_MODE</span>     = <span class="literal">true</span></span><br><span class="line"></span><br><span class="line"><span class="section">[database]</span></span><br><span class="line"><span class="attr">DB_TYPE</span>  = sqlite3</span><br><span class="line"><span class="attr">HOST</span>     = <span class="number">127.0</span>.<span class="number">0.1</span>:<span class="number">3306</span></span><br><span class="line"><span class="attr">NAME</span>     = gitea</span><br><span class="line"><span class="attr">USER</span>     = gitea</span><br><span class="line"><span class="attr">PASSWD</span>   =</span><br><span class="line"><span class="attr">SCHEMA</span>   =</span><br><span class="line"><span class="attr">SSL_MODE</span> = disable</span><br><span class="line"><span class="attr">CHARSET</span>  = utf8</span><br><span class="line"><span class="attr">PATH</span>     = /home/ubuntu/server/gitea/data/gitea.db</span><br><span class="line"><span class="attr">LOG_SQL</span>  = <span class="literal">false</span></span><br><span class="line"></span><br><span class="line"><span class="section">[repository]</span></span><br><span class="line"><span class="attr">ROOT</span> = /home/ubuntu/server/gitea/data/gitea-repositories</span><br><span class="line"></span><br><span class="line"><span class="section">[lfs]</span></span><br><span class="line"><span class="attr">PATH</span> = /home/ubuntu/server/gitea/data</span><br><span class="line"></span><br><span class="line"><span class="section">[mailer]</span></span><br><span class="line"><span class="attr">ENABLED</span> = <span class="literal">false</span></span><br><span class="line"></span><br><span class="line"><span class="section">[service]</span></span><br><span class="line"><span class="attr">REGISTER_EMAIL_CONFIRM</span>            = <span class="literal">false</span></span><br><span class="line"><span class="attr">ENABLE_NOTIFY_MAIL</span>                = <span class="literal">false</span></span><br><span class="line"><span class="attr">DISABLE_REGISTRATION</span>              = <span class="literal">false</span></span><br><span class="line"><span class="attr">ALLOW_ONLY_EXTERNAL_REGISTRATION</span>  = <span class="literal">false</span></span><br><span class="line"><span class="attr">ENABLE_CAPTCHA</span>                    = <span class="literal">false</span></span><br><span class="line"><span class="attr">REQUIRE_SIGNIN_VIEW</span>               = <span class="literal">false</span></span><br><span class="line"><span class="attr">DEFAULT_KEEP_EMAIL_PRIVATE</span>        = <span class="literal">false</span></span><br><span class="line"><span class="attr">DEFAULT_ALLOW_CREATE_ORGANIZATION</span> = <span class="literal">true</span></span><br><span class="line"><span class="attr">DEFAULT_ENABLE_TIMETRACKING</span>       = <span class="literal">true</span></span><br><span class="line"><span class="attr">NO_REPLY_ADDRESS</span>                  = noreply.localhost</span><br><span class="line"></span><br><span class="line"><span class="section">[picture]</span></span><br><span class="line"><span class="attr">DISABLE_GRAVATAR</span>        = <span class="literal">true</span></span><br><span class="line"><span class="attr">ENABLE_FEDERATED_AVATAR</span> = <span class="literal">false</span></span><br><span class="line"></span><br><span class="line"><span class="section">[openid]</span></span><br><span class="line"><span class="attr">ENABLE_OPENID_SIGNIN</span> = <span class="literal">false</span></span><br><span class="line"><span class="attr">ENABLE_OPENID_SIGNUP</span> = <span class="literal">false</span></span><br><span class="line"></span><br><span class="line"><span class="section">[session]</span></span><br><span class="line"><span class="attr">PROVIDER</span> = file</span><br><span class="line"></span><br><span class="line"><span class="section">[log]</span></span><br><span class="line"><span class="attr">MODE</span>      = console</span><br><span class="line"><span class="attr">LEVEL</span>     = info</span><br><span class="line"><span class="attr">ROOT_PATH</span> = /home/ubuntu/server/gitea/log</span><br><span class="line"><span class="attr">ROUTER</span>    = console</span><br><span class="line"></span><br><span class="line"><span class="section">[repository.pull-request]</span></span><br><span class="line"><span class="attr">DEFAULT_MERGE_STYLE</span> = merge</span><br><span class="line"></span><br><span class="line"><span class="section">[repository.signing]</span></span><br><span class="line"><span class="attr">DEFAULT_TRUST_MODEL</span> = committer</span><br><span class="line"></span><br><span class="line"><span class="section">[security]</span></span><br><span class="line"><span class="attr">INSTALL_LOCK</span>       = <span class="literal">true</span></span><br><span class="line"><span class="attr">INTERNAL_TOKEN</span>     = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE2NjQ3NjIwMDR9.C0b_xPSiljfhuDChyKTnzKuVtvSMjZsFSr31RxRZYLA</span><br><span class="line"><span class="attr">PASSWORD_HASH_ALGO</span> = pbkdf2</span><br></pre></td></tr></table></figure><p>如果使用gitea内置的SSH服务器的话，需要添加<code>START_SSH_SERVER</code>开启服务</p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">[server]</span></span><br><span class="line">...</span><br><span class="line"><span class="attr">START_SSH_SERVER</span> = <span class="literal">true</span></span><br></pre></td></tr></table></figure><h2 id="开机启动"><a href="#开机启动" class="headerlink" title="开机启动"></a>开机启动</h2><p>添加一个系统用户<code>gitea</code>用于执行<code>gitea</code></p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 创建用户gitea，用户组为watchdog</span></span><br><span class="line">sudo adduser --system --ingroup watchdog --disabled-password --shell /bin/bash --no-create-home --gecos <span class="string">&#x27;Git Version Control&#x27;</span> gitea</span><br><span class="line"></span><br><span class="line"><span class="comment"># 给用户添加文件权限</span></span><br><span class="line">sudo <span class="built_in">chown</span> -R gitea:watchdog /home/ubuntu/server/gitea</span><br></pre></td></tr></table></figure><p>添加开机启动脚本<code>/etc/systemd/system/gitea.service</code></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">[Unit]</span><br><span class="line">Description=Gitea (Git with a cup of tea)</span><br><span class="line">After=syslog.target</span><br><span class="line">After=network.target</span><br><span class="line">#After=mysqld.service</span><br><span class="line"></span><br><span class="line">[Service]</span><br><span class="line">RestartSec=2s</span><br><span class="line">User=gitea</span><br><span class="line">Group=watchdog</span><br><span class="line">UMask=0002</span><br><span class="line">WorkingDirectory=/home/ubuntu/server/gitea</span><br><span class="line">ExecStart=/home/ubuntu/server/gitea/gitea -c /home/ubuntu/server/gitea/custom/conf/app.ini -p 8899</span><br><span class="line"></span><br><span class="line">[Install]</span><br><span class="line">WantedBy=multi-user.target</span><br></pre></td></tr></table></figure><p>开机启动</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 重新加载配置文件</span></span><br><span class="line">sudo systemctl daemon-reload</span><br><span class="line"></span><br><span class="line"><span class="comment"># 开机启动</span></span><br><span class="line">sudo systemctl <span class="built_in">enable</span> gitea</span><br><span class="line"><span class="comment"># 关闭开机启动</span></span><br><span class="line">sudo systemctl <span class="built_in">disable</span> gitea</span><br><span class="line"></span><br><span class="line"><span class="comment"># 启动</span></span><br><span class="line">sudo systemctl start gitea</span><br><span class="line"><span class="comment"># 停止</span></span><br><span class="line">sudo systemctl stop gitea</span><br><span class="line"><span class="comment"># 重启</span></span><br><span class="line">sudo systemctl restart gitea</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看状态</span></span><br><span class="line">sudo systemctl status gitea</span><br></pre></td></tr></table></figure><p>接下来就可以直接在web页面玩耍了</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;最近git项目多了一些比较大的文件（数据库），第三方服务器的lfs基本都要收费，所以考虑自己搭建一个git服务器存放代码，关于git服务器有&lt;code&gt;gitea&lt;/code&gt;，&lt;code&gt;gitlab&lt;/code&gt;，gitlab功能太多，个人使用很多用不到，而且内存占用高，而gitea相对简洁，功能够用，选择gitea&lt;/p&gt;</summary>
    
    
    
    <category term="树莓派" scheme="http://zhengbomo.github.io/categories/%E6%A0%91%E8%8E%93%E6%B4%BE/"/>
    
    
    <category term="树莓派" scheme="http://zhengbomo.github.io/tags/%E6%A0%91%E8%8E%93%E6%B4%BE/"/>
    
  </entry>
  
  <entry>
    <title>（五）树莓派安装可道云</title>
    <link href="http://zhengbomo.github.io/2021-09-08/raspberrypi-kodexplorer/"/>
    <id>http://zhengbomo.github.io/2021-09-08/raspberrypi-kodexplorer/</id>
    <published>2021-09-08T14:11:26.000Z</published>
    <updated>2021-09-08T14:11:26.000Z</updated>
    
    <content type="html"><![CDATA[<p>文件管理的工具有很多，如<code>filebrowser</code>，<code>可道云</code>，<code>nextcloud</code>，<code>seafile</code>，几种都试过，最终觉得可道云最符合自己的操作习惯，可道云有两个版本，<code>kodbox</code>和 <code>kodexplorer</code>，kodbox为kodexplorer的重构版本，新增了一些功能，个人用我认为kodexplorer就够了</p><span id="more"></span><h2 id="下载安装"><a href="#下载安装" class="headerlink" title="下载安装"></a>下载安装</h2><p>可道云是基于<code>php</code>开发的，这里需要先安装<code>apache2</code>和<code>php</code></p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">sudo apt update</span><br><span class="line"></span><br><span class="line"><span class="comment"># apache2</span></span><br><span class="line">sudo apt install apache2</span><br><span class="line"></span><br><span class="line"><span class="comment"># 安装php</span></span><br><span class="line">sudo apt install php</span><br><span class="line"></span><br><span class="line"><span class="comment"># 解压</span></span><br><span class="line">sudo apt install unzip</span><br></pre></td></tr></table></figure><p>安装php依赖</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo apt install php-curl php-mbstring </span><br></pre></td></tr></table></figure><p>下载可道云</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 下载</span></span><br><span class="line">wget https://static.kodcloud.com/update/download/kodexplorer4.46.zip</span><br><span class="line"></span><br><span class="line"><span class="comment"># 解压</span></span><br><span class="line">unzip kodexplorer4.46.zip</span><br><span class="line"></span><br><span class="line"><span class="comment"># 移动</span></span><br><span class="line">sudo <span class="built_in">mv</span> kodexplorer /var/www/kodexplorer</span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置权限</span></span><br><span class="line">sudo <span class="built_in">chmod</span> -R 777 /var/www/kodexplorer</span><br></pre></td></tr></table></figure><h2 id="配置apache2"><a href="#配置apache2" class="headerlink" title="配置apache2"></a>配置apache2</h2><p>修改<code>apache2</code>站点配置 <code>/etc/apache2/sites-available/000-default.conf</code>，添加端口<code>8081</code>用于可道云（也可以使用原来的80端口）</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"># 这是apache默认的站点</span><br><span class="line">&lt;VirtualHost *:80&gt;</span><br><span class="line">        ServerAdmin webmaster@localhost</span><br><span class="line">        DocumentRoot /var/www/html</span><br><span class="line"></span><br><span class="line">        ErrorLog $&#123;APACHE_LOG_DIR&#125;/error.log</span><br><span class="line">        CustomLog $&#123;APACHE_LOG_DIR&#125;/access.log combined</span><br><span class="line">&lt;/VirtualHost&gt;</span><br><span class="line"></span><br><span class="line"># 可道云</span><br><span class="line">&lt;VirtualHost *:8081&gt;</span><br><span class="line">        ServerAdmin webmaster@localhost</span><br><span class="line">        DocumentRoot /var/www/kodexplorer</span><br><span class="line">        </span><br><span class="line">        ErrorLog $&#123;APACHE_LOG_DIR&#125;/error.log</span><br><span class="line">        CustomLog $&#123;APACHE_LOG_DIR&#125;/access.log combined</span><br><span class="line">&lt;/VirtualHost&gt;</span><br></pre></td></tr></table></figure><p>添加端口监听 <code>/etc/apache2/ports.conf</code></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">Listen 80</span><br><span class="line"></span><br><span class="line"># 可道云端口</span><br><span class="line">NameVirtualHost *:8081</span><br><span class="line">Listen 8081</span><br><span class="line"></span><br><span class="line">&lt;IfModule ssl_module&gt;</span><br><span class="line">        Listen 443</span><br><span class="line">&lt;/IfModule&gt;</span><br><span class="line"></span><br><span class="line">&lt;IfModule mod_gnutls.c&gt;</span><br><span class="line">        Listen 443</span><br><span class="line">&lt;/IfModule&gt;</span><br></pre></td></tr></table></figure><p>配置完成后，重启<code>apache2</code></p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo systemctl restart apache2</span><br></pre></td></tr></table></figure><p>这时候就可以访问可道云了 <code>http://192.168.2.*:8081</code></p><h2 id="权限"><a href="#权限" class="headerlink" title="权限"></a>权限</h2><p><code>apache2</code>默认使用的用户为<code>www-data</code>，这里给<code>www-data</code>添加组权限，这样管理文件方便点（这里我用的组是<code>watchdog</code>，可以根据自己的习惯或需要设置）</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 添加www-data到组watchdog</span></span><br><span class="line">sudo gpasswd -a www-data watchdog</span><br><span class="line"></span><br><span class="line"><span class="comment"># 也可以www-data为watchdog组</span></span><br><span class="line">sudo usermod -g watchdog www-data</span><br></pre></td></tr></table></figure><h2 id="其他设置"><a href="#其他设置" class="headerlink" title="其他设置"></a>其他设置</h2><p>在<code>可道云</code>上默认的文件创建权限为<code>755</code>，可以到<code>config/config.php</code>修改，我自己是改为<code>774</code>，组内成员可以修改</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_ invoke__">define</span>(<span class="string">&#x27;DEFAULT_PERRMISSIONS&#x27;</span>,<span class="number">0755</span>);<span class="comment">//新建文件、解压文件默认权限，777 部分虚拟主机限制了777;</span></span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;文件管理的工具有很多，如&lt;code&gt;filebrowser&lt;/code&gt;，&lt;code&gt;可道云&lt;/code&gt;，&lt;code&gt;nextcloud&lt;/code&gt;，&lt;code&gt;seafile&lt;/code&gt;，几种都试过，最终觉得可道云最符合自己的操作习惯，可道云有两个版本，&lt;code&gt;kodbox&lt;/code&gt;和 &lt;code&gt;kodexplorer&lt;/code&gt;，kodbox为kodexplorer的重构版本，新增了一些功能，个人用我认为kodexplorer就够了&lt;/p&gt;</summary>
    
    
    
    <category term="树莓派" scheme="http://zhengbomo.github.io/categories/%E6%A0%91%E8%8E%93%E6%B4%BE/"/>
    
    
    <category term="树莓派" scheme="http://zhengbomo.github.io/tags/%E6%A0%91%E8%8E%93%E6%B4%BE/"/>
    
  </entry>
  
  <entry>
    <title>（四）树莓派安装Samba</title>
    <link href="http://zhengbomo.github.io/2021-09-05/raspberrypi-samba/"/>
    <id>http://zhengbomo.github.io/2021-09-05/raspberrypi-samba/</id>
    <published>2021-09-05T02:55:59.000Z</published>
    <updated>2021-09-05T02:55:59.000Z</updated>
    
    <content type="html"><![CDATA[<p>树莓派挂载了一个硬盘，用来存放文件，视频，照片，需要共享到其他设备查看，并且需要权限管理</p><p>常用的文件共享工具有</p><ul><li><code>DLNA</code>: 主要用于多媒体共享，没有权限管理，所有人都可以看到，由于视频是服务器解码，对于大码率（4K）视频支持比较好</li><li><code>Samba</code>: 主要用于局域网文件共享，支持权限控制，大码率视频支持较弱</li><li><code>FTP</code>: 速度比Samba快，支持权限控制</li></ul><span id="more"></span><p>我这里还是选择用<code>Samba</code>，因为电视和手机支持比较好，而对于大码率视频，则使用DLNA（minidlna），这里介绍安装samba的过程</p><h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">sudo apt update</span><br><span class="line">sudo apt install samba</span><br></pre></td></tr></table></figure><h2 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h2><p>配置文件在<code>/etc/samba/smb.conf</code>，下面配置放到文件最后面</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">[学习]</span><br><span class="line">  comment = 学习，可读写，只有bomo可以查看</span><br><span class="line">  path = /mnt/h1/learn</span><br><span class="line">  browseable = yes</span><br><span class="line">  writable = yes</span><br><span class="line">  available = yes</span><br><span class="line">  valid users = ubuntu,bomo</span><br><span class="line">  guest ok = no</span><br><span class="line">  </span><br><span class="line">[视频]</span><br><span class="line">  comment = 视频，所有人可以查看，只读</span><br><span class="line">  path = /home/ubuntu/git</span><br><span class="line">  browseable = yes</span><br><span class="line">  writable = no</span><br><span class="line">  available = yes</span><br><span class="line">  guest ok = yes  </span><br></pre></td></tr></table></figure><p>由于samba的用户必须是系统用户，这里我们创建用户<code>bomo</code>，并且不需要密码，不需要</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 添加系统用户bomo，属于组watchdog，无密码，不创建home目录</span></span><br><span class="line">sudo adduser --system --ingroup watchdog --disabled-password --shell /bin/bash --no-create-home bomo</span><br></pre></td></tr></table></figure><p>虽然samba的用户必须是系统用户，但是<code>密码</code>可以单独设置</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 设置用户samba密码</span></span><br><span class="line">smbpasswd -a bomo</span><br></pre></td></tr></table></figure><p>设置完成，重启samba</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 重启samba</span></span><br><span class="line">sudo systemctl restart smbd</span><br></pre></td></tr></table></figure><p>接下来就可以在其他支持samba的设备发现并登录了</p><h2 id="设置文件权限"><a href="#设置文件权限" class="headerlink" title="设置文件权限"></a>设置文件权限</h2><p>安装<code>samba</code>的时候，会自动添加开机启动服务<code>/lib/systemd/system/smbd.service</code>，这里我们添加<code>UMask</code>和<code>Group</code>，方便管理文件</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">[Unit]</span><br><span class="line">Description=Samba SMB Daemon</span><br><span class="line">Documentation=man:smbd(8) man:samba(7) man:smb.conf(5)</span><br><span class="line">Wants=network-online.target</span><br><span class="line">After=network.target network-online.target nmbd.service winbind.service</span><br><span class="line"></span><br><span class="line">[Service]</span><br><span class="line">Type=notify</span><br><span class="line">NotifyAccess=all</span><br><span class="line">PIDFile=/run/samba/smbd.pid</span><br><span class="line">User=root</span><br><span class="line">Group=watchdog</span><br><span class="line">UMask=0002</span><br><span class="line">LimitNOFILE=16384</span><br><span class="line">EnvironmentFile=-/etc/default/samba</span><br><span class="line">ExecStartPre=/usr/share/samba/update-apparmor-samba-profile</span><br><span class="line">ExecStart=/usr/sbin/smbd --foreground --no-process-group <span class="variable">$SMBDOPTIONS</span></span><br><span class="line">ExecReload=/bin/kill -HUP <span class="variable">$MAINPID</span></span><br><span class="line">LimitCORE=infinity</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">[Install]</span><br><span class="line">WantedBy=multi-user.target</span><br></pre></td></tr></table></figure><p>刷新服务重启</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">sudo systemctl daemon-reload</span><br><span class="line"><span class="comment"># 启动</span></span><br><span class="line">sudo systemctl restart smbd</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;树莓派挂载了一个硬盘，用来存放文件，视频，照片，需要共享到其他设备查看，并且需要权限管理&lt;/p&gt;
&lt;p&gt;常用的文件共享工具有&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;DLNA&lt;/code&gt;: 主要用于多媒体共享，没有权限管理，所有人都可以看到，由于视频是服务器解码，对于大码率（4K）视频支持比较好&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Samba&lt;/code&gt;: 主要用于局域网文件共享，支持权限控制，大码率视频支持较弱&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FTP&lt;/code&gt;: 速度比Samba快，支持权限控制&lt;/li&gt;
&lt;/ul&gt;</summary>
    
    
    
    <category term="树莓派" scheme="http://zhengbomo.github.io/categories/%E6%A0%91%E8%8E%93%E6%B4%BE/"/>
    
    
    <category term="树莓派" scheme="http://zhengbomo.github.io/tags/%E6%A0%91%E8%8E%93%E6%B4%BE/"/>
    
  </entry>
  
  <entry>
    <title>（三）树莓派开机自动挂载硬盘</title>
    <link href="http://zhengbomo.github.io/2021-09-02/raspberrypi-mount-disk/"/>
    <id>http://zhengbomo.github.io/2021-09-02/raspberrypi-mount-disk/</id>
    <published>2021-09-02T11:13:42.000Z</published>
    <updated>2021-09-02T11:13:42.000Z</updated>
    
    <content type="html"><![CDATA[<p>我的硬盘是长期接在树莓派上的，这里设置开机自动挂载，避免重启后，读不到硬盘数据</p><span id="more"></span><h2 id="挂载硬盘"><a href="#挂载硬盘" class="headerlink" title="挂载硬盘"></a>挂载硬盘</h2><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 查看硬盘，得到硬盘为/dev/sda1</span></span><br><span class="line">sudo fdisk -l</span><br><span class="line"></span><br><span class="line"><span class="comment"># 需要确保挂载的目录是存在的，如果不存在则创建</span></span><br><span class="line"><span class="built_in">mkdir</span> /mnt/h1</span><br><span class="line"></span><br><span class="line"><span class="comment"># 1. 挂载硬盘</span></span><br><span class="line">sudo mount /dev/sdb1 /mnt/h1</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 挂载硬盘可以设置目录的所属用户所属组和umask</span></span><br><span class="line">sudo mount -o <span class="built_in">umask</span>=0002,gid=watchdog,uid=ubuntu /dev/sdb1 /mnt/h1</span><br><span class="line"></span><br><span class="line"><span class="comment"># 取消挂载</span></span><br><span class="line">sudo umount /dev/sdb1</span><br></pre></td></tr></table></figure><h2 id="自动挂载硬盘"><a href="#自动挂载硬盘" class="headerlink" title="自动挂载硬盘"></a>自动挂载硬盘</h2><ol><li>查看硬盘UUID<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 1. 查看硬盘uuid</span></span><br><span class="line">sudo blkid</span><br><span class="line"></span><br><span class="line"><span class="comment"># 输出得到下面信息</span></span><br><span class="line">/dev/sda1: LABEL=<span class="string">&quot;pi&quot;</span> UUID=<span class="string">&quot;3E5F551D2B409931&quot;</span> TYPE=<span class="string">&quot;ntfs&quot;</span> PTTYPE=<span class="string">&quot;atari&quot;</span></span><br></pre></td></tr></table></figure></li></ol><p>得到UUID为：<code>3E5F551D2B409931</code></p><ol start="2"><li>开机挂载<code>/etc/fstab</code></li></ol><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">UUID=<span class="string">&quot;3E5F551D2B409931&quot;</span> /mnt/h1 ntfs defaults 0 1</span><br><span class="line"></span><br><span class="line"><span class="comment"># 添加所属用户，组，umask</span></span><br><span class="line">UUID=<span class="string">&quot;3E5F551D2B409931&quot;</span> /mnt/h1 ntfs user,rw,<span class="built_in">umask</span>=0002,uid=ubuntu,gid=watchdog 0 1</span><br></pre></td></tr></table></figure><p>使用 <code>df</code>命令查看硬盘挂载情况</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">ubuntu@ubuntu:~$ <span class="built_in">df</span></span><br><span class="line">Filesystem     1K-blocks    Used Available Use% Mounted on</span><br><span class="line">tmpfs             189000    4080    184920   3% /run</span><br><span class="line">tmpfs             944992       0    944992   0% /dev/shm</span><br><span class="line">tmpfs               5120       0      5120   0% /run/lock</span><br><span class="line">tmpfs               4096       0      4096   0% /sys/fs/cgroup</span><br><span class="line">/dev/sda1      976760828 72838540 903922288   8% /mnt/h1</span><br></pre></td></tr></table></figure><p>完成</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;我的硬盘是长期接在树莓派上的，这里设置开机自动挂载，避免重启后，读不到硬盘数据&lt;/p&gt;</summary>
    
    
    
    <category term="树莓派" scheme="http://zhengbomo.github.io/categories/%E6%A0%91%E8%8E%93%E6%B4%BE/"/>
    
    
    <category term="树莓派" scheme="http://zhengbomo.github.io/tags/%E6%A0%91%E8%8E%93%E6%B4%BE/"/>
    
  </entry>
  
  <entry>
    <title>（二）树莓派4B自动控制风扇开关</title>
    <link href="http://zhengbomo.github.io/2021-08-28/raspberrypi-autofan/"/>
    <id>http://zhengbomo.github.io/2021-08-28/raspberrypi-autofan/</id>
    <published>2021-08-28T03:27:39.000Z</published>
    <updated>2021-08-28T03:27:39.000Z</updated>
    
    <content type="html"><![CDATA[<p>树莓派4B性能强大，发热也大，我在上面跑可道云和aria2，在没有风扇的情况下基本都要到60-70℃了，通常都会买个外壳接一个小风扇用于散热，淘宝上有很多，挑一个自己喜欢的，我这台设备加了风扇后可以稳定在40-50之间</p><p>默认情况下，风扇是随着电源开关控制的，即使关机了，风扇也会转，这里介绍使用三极管控制风扇开关的方法</p><span id="more"></span><img src="/images/post/raspberrypi/raspberrypi_fan.jpg" class="" width="300"><h2 id="接线引脚"><a href="#接线引脚" class="headerlink" title="接线引脚"></a>接线引脚</h2><p>树莓派4B的引脚如下图</p><img src="/images/post/raspberrypi/raspberrypi_pin.png" class="" width="400"><p>买来的风扇的正负极接4, 6引脚</p><h2 id="通过三极管添加控制线"><a href="#通过三极管添加控制线" class="headerlink" title="通过三极管添加控制线"></a>通过三极管添加控制线</h2><p>风扇接上树莓派引脚后就会开启，随电源开关，无法进行控制，关机的时候也会转，通常有两种方式</p><ol><li>使用三极管接线从而达到控制风扇的目的</li><li>taobao买T9温控模块（<a href="https://item.taobao.com/item.htm?id=553295324487%EF%BC%89">https://item.taobao.com/item.htm?id=553295324487）</a></li></ol><p>这里第一种方式，添加三极管</p><ul><li><code>三极管</code>，我这里用的是S8050（NPN型）的三极管<br>  我是在这里买的，2.8块钱50个</li><li><code>杜邦线-公对母</code>: 2根</li><li><code>杜邦线-母对母</code>: 2根</li></ul><p>三极管三级</p><img src="/images/post/raspberrypi/triode.jpg" class="" width="250"><p>接线示意图（分别接到4，6，12号引脚上）</p><img src="/images/post/raspberrypi/fan_wiring.png" class="" width="500"><p>效果图</p><img src="/images/post/raspberrypi/raspberrypi_fan_final.jpg" class="" width="800"><blockquote><p>有朋友可能买到的是S8850（PNP型）的三极管，接线和上面不一样，需要注意，可以参考这个链接，不过我没试过，<a href="https://blog.csdn.net/Xxy605/article/details/115960846">https://blog.csdn.net/Xxy605/article/details/115960846</a></p></blockquote><p>接完之后开机，会发现风扇默认是不转的，我们需要手动控制风扇的开关</p><h2 id="通过python脚本控制开关"><a href="#通过python脚本控制开关" class="headerlink" title="通过python脚本控制开关"></a>通过python脚本控制开关</h2><p>安装依赖文件</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">sudo apt update</span><br><span class="line">sudo apt install python3-pip</span><br><span class="line"></span><br><span class="line">sudo apt -y install python3-rpi.gpio</span><br><span class="line">sudo pip install RPi.GPIO</span><br></pre></td></tr></table></figure><p>下面脚本控制风扇开关</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> RPi.GPIO <span class="keyword">as</span> GPIO</span><br><span class="line"></span><br><span class="line"><span class="comment"># 控制线接的是12号引脚</span></span><br><span class="line">FAN = <span class="number">12</span></span><br><span class="line"></span><br><span class="line">GPIO.setwarnings(<span class="literal">False</span>)</span><br><span class="line">GPIO.setmode(GPIO.BOARD)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 设为输出模式</span></span><br><span class="line">GPIO.setup(FAN, GPIO.OUT)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 开风扇</span></span><br><span class="line">GPIO.output(FAN, GPIO.HIGH)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 关风扇</span></span><br><span class="line">GPIO.output(FAN, GPIO.LOW)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 设为输入模式</span></span><br><span class="line">GPIO.setup(FAN, GPIO.IN, pull_up_down=GPIO.PUD_UP)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 读取当前风扇状态</span></span><br><span class="line">isOff = GPIO.<span class="built_in">input</span>(FAN) == GPIO.LOW;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 关闭</span></span><br><span class="line">GPIO.cleanup()</span><br></pre></td></tr></table></figure><p>可以<code>PWM</code>控制风扇的风速</p><figure class="highlight py"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">pwm = GPIO.PWM(FAN, <span class="number">50</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 满速（0-100）</span></span><br><span class="line">pwm.start(<span class="number">100</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 关闭</span></span><br><span class="line">pwm.stop()</span><br></pre></td></tr></table></figure><h2 id="查看CPU温度"><a href="#查看CPU温度" class="headerlink" title="查看CPU温度"></a>查看CPU温度</h2><p>通过读取文件<code>/sys/class/thermal/thermal_zone0/temp</code>获得CPU温度</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 查看当前CPU温度</span></span><br><span class="line"><span class="built_in">cat</span> /sys/class/thermal/thermal_zone0/temp</span><br><span class="line"></span><br><span class="line"><span class="comment"># 观察CPU温度，每秒更新一次</span></span><br><span class="line">watch -n 1 <span class="built_in">cat</span> /sys/class/thermal/thermal_zone0/temp</span><br></pre></td></tr></table></figure><h2 id="温控风扇脚本"><a href="#温控风扇脚本" class="headerlink" title="温控风扇脚本"></a>温控风扇脚本</h2><p>下面是通过温度控制风扇开关的脚本（由于我的风扇比较小，就没有考虑控制风速，只做开关）</p><figure class="highlight py"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># -*- coding: utf-8 -*-</span></span><br><span class="line"> </span><br><span class="line"><span class="keyword">import</span> RPi.GPIO <span class="keyword">as</span> GPIO</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"> </span><br><span class="line"><span class="comment"># 控制风扇的GPIO</span></span><br><span class="line">FAN_GPIO = <span class="number">12</span></span><br><span class="line"><span class="comment"># 低温阈值，低于它则关闭风扇</span></span><br><span class="line">MIN_TEMP = <span class="number">45</span></span><br><span class="line"><span class="comment"># 高温阈值，高于它则全速运转</span></span><br><span class="line">MAX_TEMP = <span class="number">50</span></span><br><span class="line"><span class="comment"># 多长时间读取一次CPU温度，单位秒</span></span><br><span class="line">SAMPLING = <span class="number">60</span></span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line"><span class="comment"># 单位为千分之一度</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_cpu_temp</span>():</span><br><span class="line">    <span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&#x27;/sys/class/thermal/thermal_zone0/temp&#x27;</span>) <span class="keyword">as</span> f:</span><br><span class="line">        cpu_temp = <span class="built_in">int</span>(f.read())</span><br><span class="line">    <span class="keyword">return</span> cpu_temp</span><br><span class="line"> </span><br><span class="line"><span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    GPIO.setwarnings(<span class="literal">False</span>)</span><br><span class="line">    GPIO.setmode(GPIO.BOARD)</span><br><span class="line">    GPIO.setup(FAN_GPIO, GPIO.OUT)</span><br><span class="line">    GPIO.output(FAN_GPIO, GPIO.LOW) </span><br><span class="line">    </span><br><span class="line">    time.sleep(<span class="number">5</span>)</span><br><span class="line"> </span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="keyword">while</span> <span class="number">1</span>:</span><br><span class="line">            temp = get_cpu_temp()</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&#x27;CPU temperature:&#x27;</span>, temp)</span><br><span class="line">            <span class="keyword">if</span> temp &lt; MIN_TEMP * <span class="number">1000</span>:</span><br><span class="line">                GPIO.output(FAN_GPIO, <span class="number">0</span>)</span><br><span class="line">            <span class="keyword">elif</span> temp &gt; MAX_TEMP * <span class="number">1000</span>:</span><br><span class="line">                GPIO.output(FAN_GPIO, <span class="number">1</span>)</span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                <span class="comment"># 中间地带，开</span></span><br><span class="line">                GPIO.output(FAN_GPIO, <span class="number">1</span>)</span><br><span class="line">            time.sleep(SAMPLING)</span><br><span class="line">    <span class="keyword">except</span> KeyboardInterrupt:</span><br><span class="line">        <span class="keyword">pass</span></span><br><span class="line"> </span><br><span class="line">    GPIO.cleanup()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&#x27;__main__&#x27;</span>:</span><br><span class="line">    main()</span><br></pre></td></tr></table></figure><p>脚本保存到<code>/home/ubuntu/server/fan/autofan.py</code></p><h2 id="开机启动"><a href="#开机启动" class="headerlink" title="开机启动"></a>开机启动</h2><p>创建service（<code>/etc/systemd/system/autofan.service</code>）</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">[Unit]</span><br><span class="line">Description=auto fan control</span><br><span class="line">After=syslog.target</span><br><span class="line">After=network.target</span><br><span class="line"></span><br><span class="line">[Service]</span><br><span class="line">RestartSec=2s</span><br><span class="line">User=root</span><br><span class="line">Group=root</span><br><span class="line">WorkingDirectory=/home/ubuntu/server/fan/</span><br><span class="line">ExecStart=/usr/bin/python3 /home/ubuntu/server/fan/autofan.py</span><br><span class="line"></span><br><span class="line">[Install]</span><br><span class="line">WantedBy=multi-user.target</span><br></pre></td></tr></table></figure><p>刷新并加载</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 重新加载配置  </span></span><br><span class="line">sudo systemctl daemon-reload</span><br><span class="line"><span class="comment"># 开机启动</span></span><br><span class="line">sudo systemctl <span class="built_in">enable</span> autofan</span><br><span class="line"><span class="comment"># 启动</span></span><br><span class="line">sudo systemctl start autofan</span><br><span class="line"><span class="comment"># 状态</span></span><br><span class="line">sudo systemctl status autofan</span><br></pre></td></tr></table></figure><p>之后每次重启都会自动根据温度开关风扇了</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;树莓派4B性能强大，发热也大，我在上面跑可道云和aria2，在没有风扇的情况下基本都要到60-70℃了，通常都会买个外壳接一个小风扇用于散热，淘宝上有很多，挑一个自己喜欢的，我这台设备加了风扇后可以稳定在40-50之间&lt;/p&gt;
&lt;p&gt;默认情况下，风扇是随着电源开关控制的，即使关机了，风扇也会转，这里介绍使用三极管控制风扇开关的方法&lt;/p&gt;</summary>
    
    
    
    <category term="树莓派" scheme="http://zhengbomo.github.io/categories/%E6%A0%91%E8%8E%93%E6%B4%BE/"/>
    
    
    <category term="树莓派" scheme="http://zhengbomo.github.io/tags/%E6%A0%91%E8%8E%93%E6%B4%BE/"/>
    
  </entry>
  
  <entry>
    <title>（一）树莓派4B开机</title>
    <link href="http://zhengbomo.github.io/2021-08-27/raspberrypi-start/"/>
    <id>http://zhengbomo.github.io/2021-08-27/raspberrypi-start/</id>
    <published>2021-08-27T02:42:56.000Z</published>
    <updated>2021-08-27T02:42:56.000Z</updated>
    
    <content type="html"><![CDATA[<p>最近上手了一个树莓派4B，这里记录安装系统和连接的过程的，由于没有mini hdmi线，需要提前做一些设置</p><span id="more"></span><h2 id="安装系统"><a href="#安装系统" class="headerlink" title="安装系统"></a>安装系统</h2><h3 id="1-下载镜像写入工具"><a href="#1-下载镜像写入工具" class="headerlink" title="1. 下载镜像写入工具"></a>1. 下载镜像写入工具</h3><p>用官方的<a href="https://www.raspberrypi.org/software">Raspberry Pi Imager</a>就行</p><h3 id="2-下载系统"><a href="#2-下载系统" class="headerlink" title="2. 下载系统"></a>2. 下载系统</h3><ul><li>官方系统: <a href="https://www.raspberrypi.org/software/operating-systems">https://www.raspberrypi.org/software/operating-systems</a></li><li>ubuntu: <a href="https://ubuntu.com/download/raspberry-pi">https://ubuntu.com/download/raspberry-pi</a></li><li>其他: <a href="https://www.raspberrypi.org/software/operating-systems">https://www.raspberrypi.org/software/operating-systems</a></li></ul><p>树莓派虽然很强大，但还是很难作为真正的生产力，大多数情况还是作为服务，我没有选择Desktop版本，而是选择<code>Ubuntu Server 20.04 TLS</code></p><h3 id="3-写入TF卡"><a href="#3-写入TF卡" class="headerlink" title="3. 写入TF卡"></a>3. 写入TF卡</h3><img src="/images/post/raspberrypi/raspberrypi_burn.png" class="" width="500"><h3 id="4-SSH连接"><a href="#4-SSH连接" class="headerlink" title="4. SSH连接"></a>4. SSH连接</h3><p>特别是没法外接显示器的，第一步只能通过SSH连接，烧录完成后，先不急着开机，会提示拔出卡，重新插入tf卡，可以看到<code>system-boot</code>分区，这里我们在里面新建一个空的文本文件，命名为<code>SSH</code>，这样开机后就可以通过SSH连接了</p><h3 id="5-wifi设置"><a href="#5-wifi设置" class="headerlink" title="5. wifi设置"></a>5. wifi设置</h3><p>如果你<code>没有网线</code>，也没有外接屏幕，可以设置wifi信息，树莓派在启动后会自动连接wifi，在<code>system-boot</code>分区根目录有个<code>network-config</code>文件，可以配置无线网络，找到<code>wifis</code>相关配置</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">#</span><span class="language-bash">wifis:</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash"> wlan0:</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">   dhcp4: <span class="literal">true</span></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">   optional: <span class="literal">true</span></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">   access-points:</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">     myhomewifi:</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">       password: <span class="string">&quot;S3kr1t&quot;</span></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">     myworkwifi:</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">       password: <span class="string">&quot;correct battery horse staple&quot;</span></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">     workssid:</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">       auth:</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">         key-management: eap</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">         method: peap</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">         identity: <span class="string">&quot;me@example.com&quot;</span></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">         password: <span class="string">&quot;passw0rd&quot;</span></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">         ca-certificate: /etc/my_ca.pem</span></span><br></pre></td></tr></table></figure><p>修改为（SSID: HomeWifi，密码：12345678）</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">wifis:</span></span><br><span class="line">  <span class="attr">wlan0:</span></span><br><span class="line">    <span class="attr">dhcp4:</span> <span class="literal">true</span></span><br><span class="line">    <span class="attr">optional:</span> <span class="literal">true</span></span><br><span class="line">    <span class="attr">access-points:</span></span><br><span class="line">      <span class="attr">&quot;HomeWifi&quot;:</span></span><br><span class="line">        <span class="attr">password:</span> <span class="string">&quot;12345678&quot;</span></span><br></pre></td></tr></table></figure><h3 id="6-关闭LED指示灯"><a href="#6-关闭LED指示灯" class="headerlink" title="6. 关闭LED指示灯"></a>6. 关闭LED指示灯</h3><p>可以通过<code>config.txt</code>文件的<code>[pi4]</code>下面添加下面命令关闭</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"># 关闭电源指示灯(红色)</span><br><span class="line">dtparam=pwr_led_trigger=none</span><br><span class="line">dtparam=pwr_led_activelow=off</span><br><span class="line"></span><br><span class="line"># 关闭活动指示灯(绿色)</span><br><span class="line">dtparam=act_led_trigger=none</span><br><span class="line">dtparam=act_led_activelow=off</span><br><span class="line"></span><br><span class="line"># 关闭网线口指示灯</span><br><span class="line">dtparam=eth_led0=4</span><br><span class="line">dtparam=eth_led1=4</span><br></pre></td></tr></table></figure><h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><p>TF卡插入树莓派通电即可，通过<code>路由器</code>可以看到树莓派连接的IP地址（如：192.168.2.*），通过ssh连接</p><ul><li><code>ubuntu</code>系统默认用户名和密码都是<code>ubuntu</code></li><li>官方系统的默认用户名和密码是<code>pi</code>和<code>raspberry</code></li></ul><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ssh ubuntu@192.168.2.*</span><br></pre></td></tr></table></figure><p>接下来就可以愉快的玩耍了</p><h2 id="通过VNC连接"><a href="#通过VNC连接" class="headerlink" title="通过VNC连接"></a>通过VNC连接</h2><p>如果你安装的是官方的系统，则可以通过VNC连接到树莓派，而不用外接屏幕</p><p>通过ssh连接到树莓派后，进入设置</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo raspi-config</span><br></pre></td></tr></table></figure><p>选择<code>Interfacing Options</code> -&gt; <code>VNC</code>-&gt; （询问是否enabled）<code>Yes</code></p><p>之后直接用<a href="https://www.realvnc.com/en/connect/download/viewer/">VNC Viewer</a>，就可以连接到树莓派了</p><h3 id="黑屏"><a href="#黑屏" class="headerlink" title="黑屏"></a>黑屏</h3><p>这里可能会遇到黑屏的情况，还是进入<code>raspi-config</code>，选择<code>Advanced Options</code> -&gt; <code>Resolution</code> -&gt; <code>1280x720</code>（随便选择一个分辨率，不要选Default）</p><h2 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h2><p>查看硬件版本，我的是<code>1.4</code>版本的，另外还有1.1和1.2版本</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cat</span> /sys/firmware/devicetree/base/model</span><br><span class="line">Raspberry Pi 4 Model B Rev 1.4</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;最近上手了一个树莓派4B，这里记录安装系统和连接的过程的，由于没有mini hdmi线，需要提前做一些设置&lt;/p&gt;</summary>
    
    
    
    <category term="树莓派" scheme="http://zhengbomo.github.io/categories/%E6%A0%91%E8%8E%93%E6%B4%BE/"/>
    
    
    <category term="树莓派" scheme="http://zhengbomo.github.io/tags/%E6%A0%91%E8%8E%93%E6%B4%BE/"/>
    
  </entry>
  
  <entry>
    <title>【OpenGL案例5】球的自转和公转</title>
    <link href="http://zhengbomo.github.io/2020-07-20/opengl-demo5/"/>
    <id>http://zhengbomo.github.io/2020-07-20/opengl-demo5/</id>
    <published>2020-07-20T08:47:49.000Z</published>
    <updated>2023-06-11T03:02:51.869Z</updated>
    
    <content type="html"><![CDATA[<p>通过<a href="/2020-07-20/opengl-demo4/">案例4</a>我们已经知道如何绘制<code>球</code>了，本案例绘制一个非常经典的案例，就是球的自转和公转（小球公转，大球自转）</p><span id="more"></span><img src="/images/post/opengl/sphere-world.gif" class="" width="600"><p>为了方便看出自转，这里只画三角形线，不进行填充</p><h2 id="绘制图形"><a href="#绘制图形" class="headerlink" title="绘制图形"></a>绘制图形</h2><p>基于前面的<a href="(/2020-07-20/opengl-demo4/)">案例4</a>的基础上来绘制</p><h3 id="绘制地板"><a href="#绘制地板" class="headerlink" title="绘制地板"></a>绘制地板</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 地板</span></span><br><span class="line">GLBatch                floorBatch;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 设置地板顶点数据</span></span><br><span class="line">floorBatch.<span class="built_in">Begin</span>(GL_LINES, <span class="number">324</span>);</span><br><span class="line"><span class="keyword">for</span>(GLfloat x = <span class="number">-20.0</span>; x &lt;= <span class="number">20.0f</span>; x+= <span class="number">0.5</span>) &#123;</span><br><span class="line">    floorBatch.<span class="built_in">Vertex3f</span>(x, <span class="number">-0.55f</span>, <span class="number">20.0f</span>);</span><br><span class="line">    floorBatch.<span class="built_in">Vertex3f</span>(x, <span class="number">-0.55f</span>, <span class="number">-20.0f</span>);</span><br><span class="line"></span><br><span class="line">    floorBatch.<span class="built_in">Vertex3f</span>(<span class="number">20.0f</span>, <span class="number">-0.55f</span>, x);</span><br><span class="line">    floorBatch.<span class="built_in">Vertex3f</span>(<span class="number">-20.0f</span>, <span class="number">-0.55f</span>, x);</span><br><span class="line">&#125;</span><br><span class="line">floorBatch.<span class="built_in">End</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 在renderSence绘制地板</span></span><br><span class="line">shaderManager.<span class="built_in">UseStockShader</span>(GLT_SHADER_FLAT, transformPipeline.<span class="built_in">GetModelViewProjectionMatrix</span>(), vGreen);</span><br><span class="line">floorBatch.<span class="built_in">Draw</span>();</span><br></pre></td></tr></table></figure><h3 id="大球和小球"><a href="#大球和小球" class="headerlink" title="大球和小球"></a>大球和小球</h3><p>自转公转小球</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 设置大球模型</span></span><br><span class="line"><span class="built_in">gltMakeSphere</span>(torusBatch, <span class="number">0.4f</span>, <span class="number">20</span>, <span class="number">40</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 设置小球模型</span></span><br><span class="line"><span class="built_in">gltMakeSphere</span>(sphereBatch, <span class="number">0.2f</span>, <span class="number">8</span>, <span class="number">16</span>);</span><br></pre></td></tr></table></figure><p>随机小球</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 随机球个数</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> NUM_SPHERES 50</span></span><br><span class="line"><span class="comment">// 记录随机球位置</span></span><br><span class="line">GLFrame spheres[NUM_SPHERES];</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 随机生成位置放置小球</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; NUM_SPHERES; i++) &#123;</span><br><span class="line">    <span class="comment">//y轴不变，X,Z产生随机值</span></span><br><span class="line">    GLfloat x = ((GLfloat)((<span class="built_in">rand</span>() % <span class="number">400</span>) - <span class="number">200</span> ) * <span class="number">0.1f</span>);</span><br><span class="line">    GLfloat z = ((GLfloat)((<span class="built_in">rand</span>() % <span class="number">400</span>) - <span class="number">200</span> ) * <span class="number">0.1f</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 在y方向，将球体设置为0.0的位置，这使得它们看起来是飘浮在眼睛的高度</span></span><br><span class="line">    <span class="comment">// 对spheres数组中的每一个顶点，设置顶点数据</span></span><br><span class="line">    spheres[i].<span class="built_in">SetOrigin</span>(x, <span class="number">0.0f</span>, z);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="renderSence"><a href="#renderSence" class="headerlink" title="renderSence"></a>renderSence</h3><p>添加<code>视图矩阵</code>到矩阵堆栈</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 模型视图矩阵，push单元矩阵</span></span><br><span class="line">modelViewMatrix.<span class="built_in">PushMatrix</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 模型变换</span></span><br><span class="line">M3DMatrix44f mCamera;</span><br><span class="line">cameraFrame.<span class="built_in">GetCameraMatrix</span>(mCamera);</span><br><span class="line"><span class="comment">// push视图变换</span></span><br><span class="line">modelViewMatrix.<span class="built_in">PushMatrix</span>(mCamera);</span><br></pre></td></tr></table></figure><p>由于地板不需要其他变换，这时候可以绘制地板</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">shaderManager.<span class="built_in">UseStockShader</span>(GLT_SHADER_FLAT, transformPipeline.<span class="built_in">GetModelViewProjectionMatrix</span>(), vGreen);</span><br><span class="line">floorBatch.<span class="built_in">Draw</span>();</span><br></pre></td></tr></table></figure><p>让大小球显示在观察者前面，这里添加一个<code>视图变换</code></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 平移（z轴）让小球显示到观察者前面，</span></span><br><span class="line">modelViewMatrix.<span class="built_in">Translate</span>(<span class="number">0.0f</span>, <span class="number">0.0f</span>, <span class="number">-3.0f</span>);</span><br></pre></td></tr></table></figure><p>然后绘制大球，这里使用点<code>点光源着⾊器</code>，可以看到球的光照效果</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 这里添加一个旋转角度(5°)，通过这个变量控制自转</span></span><br><span class="line"><span class="type">float</span> yRot = <span class="number">5</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义光源位置</span></span><br><span class="line">M3DVector4f vLightPos = &#123;<span class="number">0.0f</span>,<span class="number">10.0f</span>,<span class="number">5.0f</span>,<span class="number">1.0f</span>&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 绘制随机球，这里直接用小球的批次类来绘制</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; NUM_SPHERES; i++) &#123;</span><br><span class="line">    modelViewMatrix.<span class="built_in">PushMatrix</span>();</span><br><span class="line">    modelViewMatrix.<span class="built_in">MultMatrix</span>(spheres[i]);</span><br><span class="line">    shaderManager.<span class="built_in">UseStockShader</span>(</span><br><span class="line">                                 GLT_SHADER_POINT_LIGHT_DIFF,</span><br><span class="line">                                 transformPipeline.<span class="built_in">GetModelViewMatrix</span>(),</span><br><span class="line">                                 transformPipeline.<span class="built_in">GetProjectionMatrix</span>(),</span><br><span class="line">                                 vLightPos,</span><br><span class="line">                                 vBlue);</span><br><span class="line">    sphereBatch.<span class="built_in">Draw</span>();</span><br><span class="line">    modelViewMatrix.<span class="built_in">PopMatrix</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 旋转（用于大球自转）</span></span><br><span class="line">modelViewMatrix.<span class="built_in">PushMatrix</span>();</span><br><span class="line">modelViewMatrix.<span class="built_in">Rotate</span>(yRot, <span class="number">0.0f</span>, <span class="number">1.0f</span>, <span class="number">0.0f</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 球上的三角形画线</span></span><br><span class="line"><span class="built_in">glPolygonMode</span>(GL_FRONT_AND_BACK, GL_LINE);</span><br><span class="line"><span class="built_in">glLineWidth</span>(<span class="number">2f</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 指定合适的着色器(点光源着色器)</span></span><br><span class="line">shaderManager.<span class="built_in">UseStockShader</span>(</span><br><span class="line">                             GLT_SHADER_POINT_LIGHT_DIFF,</span><br><span class="line">                             transformPipeline.<span class="built_in">GetModelViewMatrix</span>(),</span><br><span class="line">                             transformPipeline.<span class="built_in">GetProjectionMatrix</span>(),</span><br><span class="line">                             vLightPos,</span><br><span class="line">                             vRed);</span><br><span class="line"><span class="comment">// 画球</span></span><br><span class="line">torusBatch.<span class="built_in">Draw</span>();</span><br><span class="line"><span class="comment">// 弹出旋转矩阵（因为旋转只用于大球）</span></span><br><span class="line">modelViewMatrix.<span class="built_in">PopMatrix</span>();</span><br></pre></td></tr></table></figure><p>接下来画<code>小球</code>，先旋转，再平移（先旋转到固定的角度，再移到外面）</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 旋转（公转）</span></span><br><span class="line">modelViewMatrix.<span class="built_in">Rotate</span>(yRot * <span class="number">-2.0f</span>, <span class="number">0.0f</span>, <span class="number">1.0f</span>, <span class="number">0.0f</span>);</span><br><span class="line"><span class="comment">// 平移</span></span><br><span class="line">modelViewMatrix.<span class="built_in">Translate</span>(<span class="number">0.8f</span>, <span class="number">0.0f</span>, <span class="number">0.0f</span>);</span><br><span class="line">shaderManager.<span class="built_in">UseStockShader</span>(</span><br><span class="line">                             GLT_SHADER_POINT_LIGHT_DIFF,</span><br><span class="line">                             transformPipeline.<span class="built_in">GetModelViewMatrix</span>(),</span><br><span class="line">                             transformPipeline.<span class="built_in">GetProjectionMatrix</span>(),</span><br><span class="line">                             vLightPos,</span><br><span class="line">                             vBlue);</span><br><span class="line"><span class="comment">// 画小球</span></span><br><span class="line">sphereBatch.<span class="built_in">Draw</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 前面一共push了三个矩阵，用完后pop</span></span><br><span class="line">modelViewMatrix.<span class="built_in">PopMatrix</span>();</span><br><span class="line">modelViewMatrix.<span class="built_in">PopMatrix</span>();</span><br><span class="line">modelViewMatrix.<span class="built_in">PopMatrix</span>();</span><br></pre></td></tr></table></figure><blockquote><p>注意，这里的小球没有自转，只是跟随者Y轴旋转</p></blockquote><h3 id="定时器"><a href="#定时器" class="headerlink" title="定时器"></a>定时器</h3><p>接下来是控制刷新，主要是修改<code>yRot</code>，就能控制自转和公转了，这里通过<code>CStopWatch</code>获取一个时间间隔，来生成一个动态的值</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> CStopWatch rotTimer;</span><br><span class="line"><span class="comment">// 可以通过流失的时间获取一个动态的值</span></span><br><span class="line"><span class="type">float</span> yRot = rotTimer.<span class="built_in">GetElapsedSeconds</span>() * <span class="number">60.0f</span>;</span><br></pre></td></tr></table></figure><p>完整代码见<a href="https://github.com/zhengbomo/OpenGLDemo/tree/master/005--%E5%B0%8F%E7%90%83%E8%87%AA%E8%BD%AC%E5%85%AC%E8%BD%AC">这里</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;通过&lt;a href=&quot;/2020-07-20/opengl-demo4/&quot;&gt;案例4&lt;/a&gt;我们已经知道如何绘制&lt;code&gt;球&lt;/code&gt;了，本案例绘制一个非常经典的案例，就是球的自转和公转（小球公转，大球自转）&lt;/p&gt;</summary>
    
    
    
    <category term="OpenGL" scheme="http://zhengbomo.github.io/categories/OpenGL/"/>
    
    
    <category term="OpenGL" scheme="http://zhengbomo.github.io/tags/OpenGL/"/>
    
  </entry>
  
  <entry>
    <title>【OpenGL案例4】球，环，圆柱，磁盘的绘制</title>
    <link href="http://zhengbomo.github.io/2020-07-20/opengl-demo4/"/>
    <id>http://zhengbomo.github.io/2020-07-20/opengl-demo4/</id>
    <published>2020-07-20T04:55:22.000Z</published>
    <updated>2023-06-11T03:02:51.869Z</updated>
    
    <content type="html"><![CDATA[<p>在之前的案例中，我们通过定义三角形顶点绘制了一些几何图形，本案例使用OpenGL内置的图形绘制，查看<code>GLTool.h</code>，可以看到内置了下面图形</p><span id="more"></span><img src="/images/post/opengl/object-render-demo.gif" class="" width="600"><h2 id="内置图形"><a href="#内置图形" class="headerlink" title="内置图形"></a>内置图形</h2><h3 id="球"><a href="#球" class="headerlink" title="球"></a>球</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//参数1：sphereBatch，三角形批次类对象</span></span><br><span class="line"><span class="comment">//参数2：fRadius，球体半径</span></span><br><span class="line"><span class="comment">//参数3：iSlices，从球体底部堆叠到顶部的三角形带的数量；其实球体是一圈一圈三角形带组成</span></span><br><span class="line"><span class="comment">//参数4：iStacks，围绕球体一圈排列的三角形对数</span></span><br><span class="line"><span class="built_in">gltMakeSphere</span>(GLTriangleBatch&amp; sphereBatch, GLfloat fRadius, GLint iSlices, GLint iStacks);</span><br></pre></td></tr></table></figure><h3 id="环"><a href="#环" class="headerlink" title="环"></a>环</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//参数1：torusBatch，三角形批次类对象</span></span><br><span class="line"><span class="comment">//参数2：majorRadius,甜甜圈中心到外边缘的半径</span></span><br><span class="line"><span class="comment">//参数3：minorRadius,甜甜圈中心到内边缘的半径</span></span><br><span class="line"><span class="comment">//参数4：numMajor,沿着主半径的三角形数量</span></span><br><span class="line"><span class="comment">//参数5：numMinor,沿着内部较小半径的三角形数量</span></span><br><span class="line"><span class="built_in">gltMakeTorus</span>(GLTriangleBatch&amp; torusBatch, GLfloat majorRadius, GLfloat minorRadius, GLint numMajor, GLint numMinor);</span><br></pre></td></tr></table></figure><h3 id="圆柱-x2F-圆锥"><a href="#圆柱-x2F-圆锥" class="headerlink" title="圆柱&#x2F;圆锥"></a>圆柱&#x2F;圆锥</h3><p>设置顶部和底部面的半径，相同为<code>圆住</code>，其中一个面的<code>半径为0</code>则为<code>圆锥</code>，半径不同类似一把中间镂空的伞</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//参数1：cylinderBatch，三角形批次类对象</span></span><br><span class="line"><span class="comment">//参数2：baseRadius,底部半径</span></span><br><span class="line"><span class="comment">//参数3：topRadius,头部半径</span></span><br><span class="line"><span class="comment">//参数4：fLength,圆形长度</span></span><br><span class="line"><span class="comment">//参数5：numSlices,围绕Z轴的三角形对的数量</span></span><br><span class="line"><span class="comment">//参数6：numStacks,圆柱底部堆叠到顶部圆环的三角形数量</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">gltMakeCylinder</span><span class="params">(GLTriangleBatch&amp; cylinderBatch, GLfloat baseRadius, GLfloat topRadius, GLfloat fLength, GLint numSlices, GLint numStacks)</span></span>;</span><br></pre></td></tr></table></figure><h3 id="磁盘"><a href="#磁盘" class="headerlink" title="磁盘"></a>磁盘</h3><p>2D平面图形，有两个圆组成一个平面环</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//参数1:diskBatch，三角形批次类对象</span></span><br><span class="line"><span class="comment">//参数2:innerRadius,内圆半径</span></span><br><span class="line"><span class="comment">//参数3:outerRadius,外圆半径</span></span><br><span class="line"><span class="comment">//参数4:nSlices,圆盘围绕Z轴的三角形对的数量</span></span><br><span class="line"><span class="comment">//参数5:nStacks,圆盘外网到内围的三角形数量</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">gltMakeDisk</span><span class="params">(GLTriangleBatch&amp; diskBatch, GLfloat innerRadius, GLfloat outerRadius, GLint nSlices, GLint nStacks)</span></span>;</span><br></pre></td></tr></table></figure><h3 id="立方体"><a href="#立方体" class="headerlink" title="立方体"></a>立方体</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//参数1:cubeBatch，立方体批次类对象</span></span><br><span class="line"><span class="comment">//参数2:fRadius，每个方向到原点距离都为20个单位长度的立方体</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">gltMakeCube</span><span class="params">(GLBatch&amp; cubeBatch, GLfloat fRadius)</span></span>;</span><br></pre></td></tr></table></figure><h2 id="案例"><a href="#案例" class="headerlink" title="案例"></a>案例</h2><p>本案例基于<a href="/2020-07-19/opengl-demo3/">案例3</a>，原来的部分不做介绍，定义图形批次类</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 球</span></span><br><span class="line">GLTriangleBatch     sphereBatch;</span><br><span class="line"><span class="comment">// 环</span></span><br><span class="line">GLTriangleBatch     torusBatch;</span><br><span class="line"><span class="comment">// 圆柱</span></span><br><span class="line">GLTriangleBatch     cylinderBatch;</span><br><span class="line"><span class="comment">// 锥</span></span><br><span class="line">GLTriangleBatch     coneBatch;</span><br><span class="line"><span class="comment">// 磁盘</span></span><br><span class="line">GLTriangleBatch     diskBatch;</span><br><span class="line"><span class="comment">// 立方体</span></span><br><span class="line">GLBatch             cubeBatch;</span><br></pre></td></tr></table></figure><p>生成批次类数据</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 球</span></span><br><span class="line"><span class="built_in">gltMakeSphere</span>(sphereBatch, <span class="number">3.0</span>, <span class="number">10</span>, <span class="number">20</span>);</span><br><span class="line"><span class="comment">// 环面</span></span><br><span class="line"><span class="built_in">gltMakeTorus</span>(torusBatch, <span class="number">3.0f</span>, <span class="number">0.75f</span>, <span class="number">15</span>, <span class="number">15</span>);</span><br><span class="line"><span class="comment">// 圆柱</span></span><br><span class="line"><span class="built_in">gltMakeCylinder</span>(cylinderBatch, <span class="number">2.0f</span>, <span class="number">2.0f</span>, <span class="number">3.0f</span>, <span class="number">15</span>, <span class="number">2</span>);</span><br><span class="line"><span class="comment">// 锥</span></span><br><span class="line"><span class="built_in">gltMakeCylinder</span>(coneBatch, <span class="number">2.0f</span>, <span class="number">0.0f</span>, <span class="number">3.0f</span>, <span class="number">13</span>, <span class="number">2</span>);</span><br><span class="line"><span class="comment">// 磁盘</span></span><br><span class="line"><span class="built_in">gltMakeDisk</span>(diskBatch, <span class="number">1.5f</span>, <span class="number">3.0f</span>, <span class="number">13</span>, <span class="number">3</span>);</span><br><span class="line"><span class="comment">// 正立方体</span></span><br><span class="line"><span class="built_in">gltMakeCube</span>(cubeBatch, <span class="number">1</span>);</span><br></pre></td></tr></table></figure><p>在renderSence判断显示哪一个图形</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">switch</span>(nStep) &#123;</span><br><span class="line">    <span class="keyword">case</span> <span class="number">0</span>:</span><br><span class="line">        <span class="built_in">drawWireFramedBatch</span>(&amp;sphereBatch);</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    <span class="keyword">case</span> <span class="number">1</span>:</span><br><span class="line">        <span class="built_in">drawWireFramedBatch</span>(&amp;torusBatch);</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    <span class="keyword">case</span> <span class="number">2</span>:</span><br><span class="line">        <span class="built_in">drawWireFramedBatch</span>(&amp;cylinderBatch);</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    <span class="keyword">case</span> <span class="number">3</span>:</span><br><span class="line">        <span class="built_in">drawWireFramedBatch</span>(&amp;coneBatch);</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    <span class="keyword">case</span> <span class="number">4</span>:</span><br><span class="line">        <span class="built_in">drawWireFramedBatch</span>(&amp;diskBatch);</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    <span class="keyword">case</span> <span class="number">5</span>:</span><br><span class="line">        <span class="built_in">drawWireFramedBatch</span>(&amp;cubeBatch);</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>代码见<a href="https://github.com/zhengbomo/OpenGLDemo/tree/master/004--OpenGL%E7%BB%98%E5%88%B6%E5%87%A0%E4%BD%95%E5%9B%BE%E5%BD%A2">这里</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;在之前的案例中，我们通过定义三角形顶点绘制了一些几何图形，本案例使用OpenGL内置的图形绘制，查看&lt;code&gt;GLTool.h&lt;/code&gt;，可以看到内置了下面图形&lt;/p&gt;</summary>
    
    
    
    <category term="OpenGL" scheme="http://zhengbomo.github.io/categories/OpenGL/"/>
    
    
    <category term="OpenGL" scheme="http://zhengbomo.github.io/tags/OpenGL/"/>
    
  </entry>
  
  <entry>
    <title>【OpenGL案例3】绘制金字塔、六边形、圆环</title>
    <link href="http://zhengbomo.github.io/2020-07-19/opengl-demo3/"/>
    <id>http://zhengbomo.github.io/2020-07-19/opengl-demo3/</id>
    <published>2020-07-19T14:08:46.000Z</published>
    <updated>2020-07-19T14:08:46.000Z</updated>
    
    <content type="html"><![CDATA[<p>上节说到了不同的图元的装配方式，本节主要通过绘制金字塔，圆环，并使用<code>透视投影</code>，和<code>矩阵变换</code>来控制图形的显示</p><span id="more"></span><img src="/images/post/opengl/primitive-demo.gif" class="" width="600"><h2 id="基础知识"><a href="#基础知识" class="headerlink" title="基础知识"></a>基础知识</h2><p>OpenGL涉及到的基础变换主要有下面4种</p><table><thead><tr><th>变换</th><th>说明</th></tr></thead><tbody><tr><td>视图变换</td><td>移动观察者的位置，观察者动，物体不动</td></tr><tr><td>模型变换</td><td>在场景中移动物体，观察者不动，物体动</td></tr><tr><td>投影变换</td><td>正投影，透视投影</td></tr><tr><td>视口变换</td><td>对窗口上最终输出进行缩放</td></tr></tbody></table><p>其中对于同一个物体，使用<code>视图变换</code>和<code>模型变换</code>都能做到同样的效果，具体采用什么变换取决于实际情况</p><h3 id="视图变换"><a href="#视图变换" class="headerlink" title="视图变换"></a>视图变换</h3><p>视图变换是应用到场景中的第一种变换，通过物体&#x2F;观察者在Z轴上的移动（默认是Z轴，也可以改为其他轴），确定场景中利于观察的位置。默认情况下，透视投影中的观察者位置处于原点（0，0，0），并沿着z轴负方向看向屏幕里面，一般通过<code>moveForward</code>方法来调整观察者位置，moveForward默认的朝向是-z轴，所以向屏幕里面移动传正数值，向屏幕外即+z轴，需要传负数值</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 定义一个参考证</span></span><br><span class="line">GLFrame                cameraFrame;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 向屏幕外（+z轴方向）移动15.0f</span></span><br><span class="line">cameraFrame.<span class="built_in">MoveForward</span>(<span class="number">-15.0f</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 生成矩阵到mCamera，用于矩阵变换操作</span></span><br><span class="line">M3DMatrix44f mCamera;</span><br><span class="line">cameraFrame.<span class="built_in">GetCameraMatrix</span>(mCamera);</span><br></pre></td></tr></table></figure><h3 id="模型变换"><a href="#模型变换" class="headerlink" title="模型变换"></a>模型变换</h3><p>模型变换，主要涉及三个函数，移动、旋转、缩放。有了这三个函数的组合，我们可以进行任意变换。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 平移，沿x/y/z三个方向平移，矩阵结果放在第一个参数里面</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">m3dTranslationMatrix44</span><span class="params">(M3DMatrix44f m, <span class="type">float</span> x, <span class="type">float</span> y, <span class="type">float</span> z)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 旋转</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">m3dRotationMatrix44</span><span class="params">(M3DMatrix44f m, <span class="type">float</span> angle, <span class="type">float</span> x, <span class="type">float</span> y, <span class="type">float</span> z)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 缩放</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">m3dScaleMatrix44</span><span class="params">(M3DMatrix44f m, <span class="type">float</span> xScale, <span class="type">float</span> yScale, <span class="type">float</span> zScale)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 矩阵叉乘，结果放在product</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">m3dMatrixMultiply44</span><span class="params">(M3DMatrix44f product, <span class="type">const</span> M3DMatrix44f a, <span class="type">const</span> M3DMatrix44f b)</span></span>;</span><br></pre></td></tr></table></figure><blockquote><p>翻转操作可以用旋转，也可以用缩放（缩放到-1）</p></blockquote><h3 id="投影变换"><a href="#投影变换" class="headerlink" title="投影变换"></a>投影变换</h3><p>通常也在窗口改变的时候设置</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">GLFrustum              viewFrustum;</span><br><span class="line"><span class="comment">// 设置透视投影</span></span><br><span class="line">viewFrustum.<span class="built_in">SetPerspective</span>(<span class="number">35.0f</span>, <span class="built_in">float</span>(w) / <span class="built_in">float</span>(h), <span class="number">1.0f</span>, <span class="number">500.0f</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 加载投影矩阵到矩阵堆栈projectionMatrix</span></span><br><span class="line">projectionMatrix.<span class="built_in">LoadMatrix</span>(viewFrustum.<span class="built_in">GetProjectionMatrix</span>());</span><br></pre></td></tr></table></figure><h3 id="视口变换"><a href="#视口变换" class="headerlink" title="视口变换"></a>视口变换</h3><p>视口变换相关函数为，通常在窗口大小改变的时候修改</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 修改glView视口</span></span><br><span class="line"><span class="built_in">glViewport</span> (GLint x, GLint y, GLsizei width, GLsizei height);</span><br></pre></td></tr></table></figure><p>这里需要注意的是，矩阵变换叉乘不满足交换律，变换顺序会导致结果不一致</p><img src="/images/post/opengl/translate-order.png" class="" width="800"><h2 id="绘制"><a href="#绘制" class="headerlink" title="绘制"></a>绘制</h2><p>定义7个批次类，用于保存顶点</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">GLBatch                pointBatch;</span><br><span class="line">GLBatch                lineBatch;</span><br><span class="line">GLBatch                lineStripBatch;</span><br><span class="line">GLBatch                lineLoopBatch;</span><br><span class="line">GLBatch                triangleBatch;</span><br><span class="line">GLBatch                triangleStripBatch;</span><br><span class="line">GLBatch                triangleFanBatch;</span><br></pre></td></tr></table></figure><p>在<code>setupRC</code>初始化顶点数据</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 定义三个点</span></span><br><span class="line">GLfloat vCoast[] = &#123;</span><br><span class="line">    <span class="number">3</span>,<span class="number">3</span>,<span class="number">0</span>,</span><br><span class="line">    <span class="number">0</span>,<span class="number">3</span>,<span class="number">0</span>,</span><br><span class="line">    <span class="number">3</span>,<span class="number">0</span>,<span class="number">0</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 画点</span></span><br><span class="line">pointBatch.<span class="built_in">Begin</span>(GL_POINTS, <span class="number">3</span>);</span><br><span class="line">pointBatch.<span class="built_in">CopyVertexData3f</span>(vCoast);</span><br><span class="line">pointBatch.<span class="built_in">End</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 画线</span></span><br><span class="line">lineBatch.<span class="built_in">Begin</span>(GL_LINES, <span class="number">3</span>);</span><br><span class="line">lineBatch.<span class="built_in">CopyVertexData3f</span>(vCoast);</span><br><span class="line">lineBatch.<span class="built_in">End</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 画连续线段</span></span><br><span class="line">lineStripBatch.<span class="built_in">Begin</span>(GL_LINE_STRIP, <span class="number">3</span>);</span><br><span class="line">lineStripBatch.<span class="built_in">CopyVertexData3f</span>(vCoast);</span><br><span class="line">lineStripBatch.<span class="built_in">End</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 画闭合线段</span></span><br><span class="line">lineLoopBatch.<span class="built_in">Begin</span>(GL_LINE_LOOP, <span class="number">3</span>);</span><br><span class="line">lineLoopBatch.<span class="built_in">CopyVertexData3f</span>(vCoast);</span><br><span class="line">lineLoopBatch.<span class="built_in">End</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 3个三角形，构成金字塔形状</span></span><br><span class="line">GLfloat vPyramid[<span class="number">12</span>][<span class="number">3</span>] = &#123;</span><br><span class="line">    <span class="number">-2.0f</span>, <span class="number">0.0f</span>, <span class="number">-2.0f</span>,</span><br><span class="line">    <span class="number">2.0f</span>, <span class="number">0.0f</span>, <span class="number">-2.0f</span>,</span><br><span class="line">    <span class="number">0.0f</span>, <span class="number">4.0f</span>, <span class="number">0.0f</span>,</span><br><span class="line"></span><br><span class="line">    <span class="number">2.0f</span>, <span class="number">0.0f</span>, <span class="number">-2.0f</span>,</span><br><span class="line">    <span class="number">2.0f</span>, <span class="number">0.0f</span>, <span class="number">2.0f</span>,</span><br><span class="line">    <span class="number">0.0f</span>, <span class="number">4.0f</span>, <span class="number">0.0f</span>,</span><br><span class="line"></span><br><span class="line">    <span class="number">2.0f</span>, <span class="number">0.0f</span>, <span class="number">2.0f</span>,</span><br><span class="line">    <span class="number">-2.0f</span>, <span class="number">0.0f</span>, <span class="number">2.0f</span>,</span><br><span class="line">    <span class="number">0.0f</span>, <span class="number">4.0f</span>, <span class="number">0.0f</span>,</span><br><span class="line"></span><br><span class="line">    <span class="number">-2.0f</span>, <span class="number">0.0f</span>, <span class="number">2.0f</span>,</span><br><span class="line">    <span class="number">-2.0f</span>, <span class="number">0.0f</span>, <span class="number">-2.0f</span>,</span><br><span class="line">    <span class="number">0.0f</span>, <span class="number">4.0f</span>, <span class="number">0.0f</span></span><br><span class="line">&#125;;</span><br><span class="line">triangleBatch.<span class="built_in">Begin</span>(GL_TRIANGLES, <span class="number">12</span>);</span><br><span class="line">triangleBatch.<span class="built_in">CopyVertexData3f</span>(vPyramid);</span><br><span class="line">triangleBatch.<span class="built_in">End</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 三角形扇形--六边形</span></span><br><span class="line">GLfloat vPoints[<span class="number">100</span>][<span class="number">3</span>];</span><br><span class="line"><span class="type">int</span> nVerts = <span class="number">0</span>;</span><br><span class="line"><span class="comment">// 半径</span></span><br><span class="line">GLfloat r = <span class="number">3.0f</span>;</span><br><span class="line"><span class="comment">// 原点(x,y,z) = (0,0,0);</span></span><br><span class="line">vPoints[nVerts][<span class="number">0</span>] = <span class="number">0.0f</span>;</span><br><span class="line">vPoints[nVerts][<span class="number">1</span>] = <span class="number">0.0f</span>;</span><br><span class="line">vPoints[nVerts][<span class="number">2</span>] = <span class="number">0.0f</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// M3D_2PI 就是2Pi 的意思，就一个圆的意思。 绘制圆形</span></span><br><span class="line"><span class="keyword">for</span> (GLfloat angle = <span class="number">0</span>; angle &lt; M3D_2PI; angle += M3D_2PI / <span class="number">6.0f</span>) &#123;</span><br><span class="line">    <span class="comment">// 数组下标自增（每自增1次就表示一个顶点）</span></span><br><span class="line">    nVerts++;</span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">        弧长=半径*角度,这里的角度是弧度制,不是平时的角度制</span></span><br><span class="line"><span class="comment">        既然知道了cos值,那么角度=arccos,求一个反三角函数就行了</span></span><br><span class="line"><span class="comment">        */</span></span><br><span class="line">    <span class="comment">// x点坐标 cos(angle) * 半径</span></span><br><span class="line">    vPoints[nVerts][<span class="number">0</span>] = <span class="built_in">float</span>(<span class="built_in">cos</span>(angle)) * r;</span><br><span class="line">    <span class="comment">// y点坐标 sin(angle) * 半径</span></span><br><span class="line">    vPoints[nVerts][<span class="number">1</span>] = <span class="built_in">float</span>(<span class="built_in">sin</span>(angle)) * r;</span><br><span class="line">    <span class="comment">// z点的坐标</span></span><br><span class="line">    vPoints[nVerts][<span class="number">2</span>] = <span class="number">-0.5f</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 结束扇形 前面一共绘制7个顶点（包括圆心）</span></span><br><span class="line"><span class="comment">// 添加闭合的终点</span></span><br><span class="line"><span class="comment">// 课程添加演示：屏蔽177-180行代码，并把绘制节点改为7.则三角形扇形是无法闭合的。</span></span><br><span class="line">nVerts++;</span><br><span class="line">vPoints[nVerts][<span class="number">0</span>] = r;</span><br><span class="line">vPoints[nVerts][<span class="number">1</span>] = <span class="number">0</span>;</span><br><span class="line">vPoints[nVerts][<span class="number">2</span>] = <span class="number">0.0f</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 加载！GL_TRIANGLE_FAN 以一个圆心为中心呈扇形排列，共用相邻顶点的一组三角形</span></span><br><span class="line">triangleFanBatch.<span class="built_in">Begin</span>(GL_TRIANGLE_FAN, <span class="number">8</span>);</span><br><span class="line">triangleFanBatch.<span class="built_in">CopyVertexData3f</span>(vPoints);</span><br><span class="line">triangleFanBatch.<span class="built_in">End</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 三角形条带，一个小环或圆柱段</span></span><br><span class="line"><span class="comment">// 顶点下标</span></span><br><span class="line"><span class="type">int</span> iCounter = <span class="number">0</span>;</span><br><span class="line"><span class="comment">// 半径</span></span><br><span class="line">GLfloat radius = <span class="number">3.0f</span>;</span><br><span class="line"><span class="comment">// 从0度~360度，以0.3弧度为步长</span></span><br><span class="line"><span class="keyword">for</span> (GLfloat angle = <span class="number">0.0f</span>; angle &lt;= (<span class="number">2.0f</span>*M3D_PI); angle += <span class="number">0.3f</span>) &#123;</span><br><span class="line">    <span class="comment">//或许圆形的顶点的X,Y</span></span><br><span class="line">    GLfloat x = radius * <span class="built_in">sin</span>(angle);</span><br><span class="line">    GLfloat y = radius * <span class="built_in">cos</span>(angle);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//绘制2个三角形（他们的x,y顶点一样，只是z点不一样）</span></span><br><span class="line">    vPoints[iCounter][<span class="number">0</span>] = x;</span><br><span class="line">    vPoints[iCounter][<span class="number">1</span>] = y;</span><br><span class="line">    vPoints[iCounter][<span class="number">2</span>] = <span class="number">-0.5</span>;</span><br><span class="line">    iCounter++;</span><br><span class="line"></span><br><span class="line">    vPoints[iCounter][<span class="number">0</span>] = x;</span><br><span class="line">    vPoints[iCounter][<span class="number">1</span>] = y;</span><br><span class="line">    vPoints[iCounter][<span class="number">2</span>] = <span class="number">0.5</span>;</span><br><span class="line">    iCounter++;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//结束循环，在循环位置生成2个三角形</span></span><br><span class="line">vPoints[iCounter][<span class="number">0</span>] = vPoints[<span class="number">0</span>][<span class="number">0</span>];</span><br><span class="line">vPoints[iCounter][<span class="number">1</span>] = vPoints[<span class="number">0</span>][<span class="number">1</span>];</span><br><span class="line">vPoints[iCounter][<span class="number">2</span>] = <span class="number">-0.5</span>;</span><br><span class="line">iCounter++;</span><br><span class="line"></span><br><span class="line">vPoints[iCounter][<span class="number">0</span>] = vPoints[<span class="number">1</span>][<span class="number">0</span>];</span><br><span class="line">vPoints[iCounter][<span class="number">1</span>] = vPoints[<span class="number">1</span>][<span class="number">1</span>];</span><br><span class="line">vPoints[iCounter][<span class="number">2</span>] = <span class="number">0.5</span>;</span><br><span class="line">iCounter++;</span><br><span class="line"></span><br><span class="line"><span class="comment">// GL_TRIANGLE_STRIP 共用一个条带（strip）上的顶点的一组三角形</span></span><br><span class="line">triangleStripBatch.<span class="built_in">Begin</span>(GL_TRIANGLE_STRIP, iCounter);</span><br><span class="line">triangleStripBatch.<span class="built_in">CopyVertexData3f</span>(vPoints);</span><br><span class="line">triangleStripBatch.<span class="built_in">End</span>();</span><br></pre></td></tr></table></figure><p>然后在renderSence里面，把对应的批次类draw出来就可以了</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 画点</span></span><br><span class="line"><span class="built_in">glPointSize</span>(<span class="number">4.0f</span>);</span><br><span class="line">pointBatch.<span class="built_in">Draw</span>();</span><br><span class="line"><span class="built_in">glPointSize</span>(<span class="number">1.0f</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 画三角形</span></span><br><span class="line"><span class="built_in">glLineWidth</span>(<span class="number">2.0f</span>);</span><br><span class="line">lineLoopBatch.<span class="built_in">Draw</span>();</span><br><span class="line"><span class="built_in">glLineWidth</span>(<span class="number">1.0f</span>);</span><br></pre></td></tr></table></figure><h2 id="矩阵堆栈"><a href="#矩阵堆栈" class="headerlink" title="矩阵堆栈"></a>矩阵堆栈</h2><p>本案例涉及到3个矩阵，<code>视图矩阵</code>（修改观察者位置），<code>模型矩阵</code>（旋转），<code>投影矩阵</code>（透视投影）</p><p>这里我们定义两个矩阵堆栈，来计算</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 矩阵堆栈，用于设置投影矩阵</span></span><br><span class="line">GLMatrixStack          projectionMatrix;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 矩阵堆栈，用于设置视图矩阵，模型矩阵，</span></span><br><span class="line">GLMatrixStack          modelViewMatrix;</span><br></pre></td></tr></table></figure><p>为了方便计算，我们定义一个<code>变换管道</code>transformPipeline，用于合并2个矩阵堆栈（投影矩阵堆栈projectionMatrix和模型视图矩阵堆栈modelViewMatrix）</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 几何变换的管道</span></span><br><span class="line">GLGeometryTransform    transformPipeline;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 设置变换管线以使用两个矩阵堆栈</span></span><br><span class="line">transformPipeline.<span class="built_in">SetMatrixStacks</span>(modelViewMatrix, projectionMatrix);</span><br></pre></td></tr></table></figure><p>对于投影矩阵，直接在<code>changeSize</code>的时候配置就可以</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 透视投影</span></span><br><span class="line">GLFrustum              viewFrustum;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 设置透视投影</span></span><br><span class="line">viewFrustum.<span class="built_in">SetPerspective</span>(<span class="number">35.0f</span>, <span class="built_in">float</span>(w) / <span class="built_in">float</span>(h), <span class="number">1.0f</span>, <span class="number">500.0f</span>);</span><br><span class="line"><span class="comment">// 重新加载投影矩阵到矩阵堆栈projectionMatrix</span></span><br><span class="line">projectionMatrix.<span class="built_in">LoadMatrix</span>(viewFrustum.<span class="built_in">GetProjectionMatrix</span>());</span><br></pre></td></tr></table></figure><p>接下来我们在<code>renderSence</code>配置模型视图矩阵</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 加载一个单元矩阵到栈顶</span></span><br><span class="line">modelViewMatrix.<span class="built_in">LoadIdentity</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 复制一份栈顶矩阵到栈顶</span></span><br><span class="line">modelViewMatrix.<span class="built_in">PushMatrix</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获得视图矩阵</span></span><br><span class="line">M3DMatrix44f mCamera;</span><br><span class="line">cameraFrame.<span class="built_in">GetCameraMatrix</span>(mCamera);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 与栈顶矩阵叉乘并覆盖栈顶矩阵</span></span><br><span class="line">modelViewMatrix.<span class="built_in">MultMatrix</span>(mCamera);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获得模型矩阵</span></span><br><span class="line">M3DMatrix44f mObjectFrame;</span><br><span class="line">objectFrame.<span class="built_in">GetMatrix</span>(mObjectFrame);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 与栈顶矩阵叉乘并覆盖栈顶矩阵</span></span><br><span class="line">modelViewMatrix.<span class="built_in">MultMatrix</span>(mObjectFrame);</span><br></pre></td></tr></table></figure><p>最终通过变换管道，生成矩阵传递给着色器</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 生成最终的矩阵</span></span><br><span class="line">M3DMatrix44f *fMatrix = transformPipeline.<span class="built_in">GetModelViewProjectionMatrix</span>()</span><br><span class="line"></span><br><span class="line"><span class="comment">// 传给平面着色器处理，vBlack为填充颜色</span></span><br><span class="line">shaderManager.<span class="built_in">UseStockShader</span>(GLT_SHADER_FLAT, fMatrix, vBlack);</span><br></pre></td></tr></table></figure><p>对于几何图形除了直接填充颜色，还需要绘制边框（<code>glPolygonMode</code>）</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 开启多边形偏移</span></span><br><span class="line"><span class="built_in">glPolygonOffset</span>(<span class="number">-1.0f</span>, <span class="number">-1.0f</span>);</span><br><span class="line"><span class="built_in">glEnable</span>(GL_POLYGON_OFFSET_LINE);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 开启反锯齿</span></span><br><span class="line"><span class="built_in">glEnable</span>(GL_LINE_SMOOTH);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 开启混合</span></span><br><span class="line"><span class="built_in">glEnable</span>(GL_BLEND);</span><br><span class="line"><span class="comment">// 混合方法</span></span><br><span class="line"><span class="built_in">glBlendFunc</span>(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 绘制线框几何黑色版 三种模式，实心，边框，点，可以作用在正面，背面，或者两面</span></span><br><span class="line"><span class="comment">//通过调用glPolygonMode将多边形正面或者背面设为线框模式，实现线框渲染</span></span><br><span class="line"><span class="built_in">glPolygonMode</span>(GL_FRONT_AND_BACK, GL_LINE);</span><br><span class="line"><span class="comment">//设置线条宽度</span></span><br><span class="line"><span class="built_in">glLineWidth</span>(<span class="number">2.5f</span>);</span><br><span class="line"></span><br><span class="line">shaderManager.<span class="built_in">UseStockShader</span>(GLT_SHADER_FLAT, transformPipeline.<span class="built_in">GetModelViewProjectionMatrix</span>(), vBlack);</span><br><span class="line">pBatch-&gt;<span class="built_in">Draw</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 复原原本的设置</span></span><br><span class="line"><span class="comment">//通过调用glPolygonMode将多边形正面或者背面设为全部填充模式</span></span><br><span class="line"><span class="built_in">glPolygonMode</span>(GL_FRONT_AND_BACK, GL_FILL);</span><br><span class="line"><span class="built_in">glDisable</span>(GL_POLYGON_OFFSET_LINE);</span><br><span class="line"><span class="built_in">glLineWidth</span>(<span class="number">1.0f</span>);</span><br><span class="line"><span class="built_in">glDisable</span>(GL_BLEND);</span><br><span class="line"><span class="built_in">glDisable</span>(GL_LINE_SMOOTH);</span><br></pre></td></tr></table></figure><p>关于矩阵堆栈的操作</p><table><thead><tr><th>矩阵堆栈API</th><th>说明</th></tr></thead><tbody><tr><td>GLMatrixStack::LoadIdentity(void)</td><td>在栈顶加载&#x2F;覆盖一个单元矩阵</td></tr><tr><td>GLMatrixStack::LoadMatrix(const M3DMatrix44f m)</td><td>在栈顶加载&#x2F;覆盖成矩阵m</td></tr><tr><td>GLMatrixStack::MultMatrix(const M3DMatrix44f m)</td><td>矩阵m与栈顶矩阵叉乘，结果覆盖栈顶矩阵</td></tr><tr><td>GLMatrixStack::GetMatrix(void)</td><td>获取栈顶矩阵</td></tr><tr><td>GLMatrixStack::PushMatrix(void)</td><td>拷贝栈顶矩阵入栈</td></tr><tr><td>GLMatrixStack::PushMatrix(const M3DMatrix44f m)</td><td>把矩阵m入栈</td></tr><tr><td>GLMatrixStack::PopMatrix(void)</td><td>出栈，移出栈顶矩阵</td></tr></tbody></table><p>完整代码在<a href="https://github.com/zhengbomo/OpenGLDemo/tree/master/003--OpenGL%E5%9B%BE%E5%85%83%E7%BB%98%E5%88%B6(%E7%BB%BC%E5%90%88)">这里</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;上节说到了不同的图元的装配方式，本节主要通过绘制金字塔，圆环，并使用&lt;code&gt;透视投影&lt;/code&gt;，和&lt;code&gt;矩阵变换&lt;/code&gt;来控制图形的显示&lt;/p&gt;</summary>
    
    
    
    <category term="OpenGL" scheme="http://zhengbomo.github.io/categories/OpenGL/"/>
    
    
    <category term="OpenGL" scheme="http://zhengbomo.github.io/tags/OpenGL/"/>
    
  </entry>
  
  <entry>
    <title>OpenGL渲染架构</title>
    <link href="http://zhengbomo.github.io/2020-07-19/opengl-render-architecture/"/>
    <id>http://zhengbomo.github.io/2020-07-19/opengl-render-architecture/</id>
    <published>2020-07-19T06:16:13.000Z</published>
    <updated>2020-07-19T06:16:13.000Z</updated>
    
    <content type="html"><![CDATA[<p>本节主要是了解OpenGL是如何把我们的数据（顶点数据和纹理）传递到GPU的，GPU又是如何处理这些数据的</p><span id="more"></span><p>先来看一张架构图</p><img src="/images/post/opengl/opgngl-architecture.png" class="" width="800" title="OpenGL渲染架构图"><h2 id="Client与Server"><a href="#Client与Server" class="headerlink" title="Client与Server"></a>Client与Server</h2><p>这里的端不同于我们平常开发的客户端服务端</p><p>客户端：通常指在CPU存储的代码，负责<code>发送OpenGL命令</code>，比如OpenGLAPI，底层的C&#x2F;C++代码，运行在CPU上<br>服务端：负责<code>接收OpenGL命令</code>并调用<code>GPU</code>芯片执行相应的操作，运行在GPU上</p><p>服务器和客户端在功能上也是异步的。客户端不断的把<code>数据块</code>和<code>命令块</code>组合在⼀起输送到缓冲区，然后缓冲区就会发送到服务器执行</p><h2 id="数据通道"><a href="#数据通道" class="headerlink" title="数据通道"></a>数据通道</h2><p>从上面架构图可以看出，从client到server传递的数据有三种</p><ul><li>Attributes：属性</li><li>Uniforms：渲染矩阵</li><li>TextureData：纹理数据</li></ul><h3 id="Attributes"><a href="#Attributes" class="headerlink" title="Attributes"></a>Attributes</h3><p>属性会从客户端内存中复制存储在图形硬件中的一个缓冲区（显存）上，然后会被传递到<code>顶点着色器</code>Vertex Shader，无法直接传递到<code>片元着色器</code>Fragment Shader（但可间接传递到片元着色器），数据有下面类型（这些属性通常是经常变化的）</p><ul><li><code>顶点数据</code>：四维向量（x, y, z, w）</li><li><code>纹理坐标</code> (图片映射坐标)，实际是通过Vertex Shader传递给Fragment Shader</li><li>颜色数据</li><li>光照法线</li></ul><h3 id="Uniform"><a href="#Uniform" class="headerlink" title="Uniform"></a>Uniform</h3><p>Uniform通过既可以传递到<code>顶点着色器</code>，也可以传递到<code>片元着色器</code>，Uniform中传递的通常是比较统一的批次数据，通常用来传递一些不经常发生变动的数据，例如变换矩阵</p><h3 id="Texture-Data"><a href="#Texture-Data" class="headerlink" title="Texture Data"></a>Texture Data</h3><p>Texture Data同Uniform一样，可以将数据传递到<code>顶点着色器</code>和<code>片元着色器</code>，正如名字一样，主要传递纹理数据，由于顶点着色器主要是处理顶点数据的，我们将纹理数据传过去并没有多大的意义。而纹理的处理的逻辑主要是在片元着色器中进行的。用于对光栅化后的图像填充颜色</p><h2 id="着色器"><a href="#着色器" class="headerlink" title="着色器"></a>着色器</h2><p>• 着色器是使用GLSL编写的程序，看起来与C语言非常类似。着色器必须从源代码中编译和链接在一起。最终准备就绪的着色器程序<br>• 顶点着色器<br>    处理从客户机输入的数据、应用变换、进行其他的类型的数学运算来计算关照效果、位移、颜色值等等。（例如对于三个顶点的三角形渲染，顶点着色器将执行3次，也就是为了每个顶点执行一次）在目前的硬件上有多个执行单元同时运行，处理是并发的<br>• 图元装配：在顶点着色器确定完所有顶点的位置后，根据传入的配置确定图元形状，然后会对三角形逐个片段进行了光栅化处理<br>• 片元着色器<br>    光栅化后的每个片段通过执行片元着色器进行颜色填充</p><p>最终会输出我们将屏幕上变成我们最终看到的颜色</p><img src="/images/post/opengl/shadow-flow.png" class="" width="800" title="着色器的渲染流程"><ul><li>设置顶点数据和其他参数。</li><li>在顶点着色器中进行运算得到裁剪坐标。</li><li>细分着色器、几何着色器，不可自定义，跳过。</li><li>图元设置，根据设置构成点、线、三角形。</li><li>裁剪，裁剪掉超出显示区域的部分。</li><li>光栅化, 将图源栅格化为一个个的像素点。</li><li>片元着色器，将对应的栅格(像素)填充为具体的颜色。</li><li>渲染图像</li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;本节主要是了解OpenGL是如何把我们的数据（顶点数据和纹理）传递到GPU的，GPU又是如何处理这些数据的&lt;/p&gt;</summary>
    
    
    
    <category term="OpenGL" scheme="http://zhengbomo.github.io/categories/OpenGL/"/>
    
    
    <category term="OpenGL" scheme="http://zhengbomo.github.io/tags/OpenGL/"/>
    
  </entry>
  
  <entry>
    <title>OpenGL投影、图元和存储着色器</title>
    <link href="http://zhengbomo.github.io/2020-07-18/primitive-shader/"/>
    <id>http://zhengbomo.github.io/2020-07-18/primitive-shader/</id>
    <published>2020-07-18T13:01:31.000Z</published>
    <updated>2020-07-18T13:01:31.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="投影方式"><a href="#投影方式" class="headerlink" title="投影方式"></a>投影方式</h2><p>OpenGL有两种投影方式</p><table><thead><tr><th>投影方式</th><th>说明</th><th>函数</th></tr></thead><tbody><tr><td>正投影</td><td>相同的物体远近看起来都一样大</td><td>GLFrustum::SetPerspective(float fFov, float fAspect, float fNear, float fFar)</td></tr><tr><td>透视投影</td><td>近大远小</td><td>GLFrustum::SetOrthographic(GLfloat xMin, GLfloat xMax, GLfloat yMin, GLfloat yMax, GLfloat zMin, GLfloat zMax)</td></tr></tbody></table><span id="more"></span><img src="/images/post/opengl/two-projection.png" class="" width="600"><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;GLFrustum.h&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 投影矩阵</span></span><br><span class="line">GLFrustum              viewFrustum;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 设置透视投影</span></span><br><span class="line">viewFrustum.<span class="built_in">SetPerspective</span>(<span class="number">35.0f</span>, <span class="built_in">float</span>(w) / <span class="built_in">float</span>(h), <span class="number">1.0f</span>, <span class="number">500.0f</span>);</span><br><span class="line"><span class="comment">// 获取投影矩阵</span></span><br><span class="line">M3DMatrix44f *projectionMatrix = viewFrustum.<span class="built_in">GetProjectionMatrix</span>();</span><br></pre></td></tr></table></figure><h2 id="图元装配方式"><a href="#图元装配方式" class="headerlink" title="图元装配方式"></a>图元装配方式</h2><p>在OpenGL中，相同的顶点可以有不同的装配方式，如下图</p><img src="/images/post/opengl/primitive-assembly.jpg" class="" width="800"><table><thead><tr><th>图元</th><th>描述</th></tr></thead><tbody><tr><td>GL_POINTS</td><td>每个顶点在屏幕上都是单独点</td></tr><tr><td>GL_LINES</td><td>每⼀对顶点定义⼀个线段</td></tr><tr><td>GL_LINE_STRIP</td><td>一个从第⼀个顶点依次经过每⼀个后续顶点而绘制的线条</td></tr><tr><td>GL_LINE_LOOP</td><td>和GL_LINE_STRIP相同，但是最后⼀个顶点和第⼀个顶点连接起来了</td></tr><tr><td>GL_TRIANGLES</td><td>每3个顶点定义⼀个新的三角形</td></tr><tr><td>GL_TRIANGLE_STRIP</td><td>共⽤一个条带(strip)上的顶点的一组三⻆形</td></tr><tr><td>GL_TRIANGLE_FAN</td><td>以⼀个圆点为中⼼呈扇形排列，共⽤相邻顶点的⼀组三⻆形</td></tr></tbody></table><p>其中<code>GL_TRIANGLE_STRIP</code>和<code>GL_TRIANGLE_FAN</code>共享三角形的一条边，在绘制大量三角形的时候，可以节省存储空间和提高性能</p><p>如</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">GLfloat vCoast[] = &#123;</span><br><span class="line">    <span class="number">3</span>,<span class="number">3</span>,<span class="number">0</span>,</span><br><span class="line">    <span class="number">0</span>,<span class="number">3</span>,<span class="number">0</span>,</span><br><span class="line">    <span class="number">3</span>,<span class="number">0</span>,<span class="number">0</span></span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">// 三个点</span></span><br><span class="line">pointBatch.<span class="built_in">Begin</span>(GL_POINTS, <span class="number">3</span>);</span><br><span class="line">pointBatch.<span class="built_in">CopyVertexData3f</span>(vCoast);</span><br><span class="line">pointBatch.<span class="built_in">End</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 一条线</span></span><br><span class="line">lineBatch.<span class="built_in">Begin</span>(GL_LINES, <span class="number">3</span>);</span><br><span class="line">lineBatch.<span class="built_in">CopyVertexData3f</span>(vCoast);</span><br><span class="line">lineBatch.<span class="built_in">End</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 闭合三角形线段</span></span><br><span class="line">lineLoopBatch.<span class="built_in">Begin</span>(GL_LINE_LOOP, <span class="number">3</span>);</span><br><span class="line">lineLoopBatch.<span class="built_in">CopyVertexData3f</span>(vCoast);</span><br><span class="line">lineLoopBatch.<span class="built_in">End</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 闭合三角形，可以填充颜色</span></span><br><span class="line">triangleBatch.<span class="built_in">Begin</span>(GL_TRIANGLES, <span class="number">3</span>);</span><br><span class="line">triangleBatch.<span class="built_in">CopyVertexData3f</span>(vCoast);</span><br><span class="line">triangleBatch.<span class="built_in">End</span>();</span><br></pre></td></tr></table></figure><h2 id="存储着色器-x2F-固定管线着色器"><a href="#存储着色器-x2F-固定管线着色器" class="headerlink" title="存储着色器&#x2F;固定管线着色器"></a>存储着色器&#x2F;固定管线着色器</h2><p>在使用存储着色器之前需要先进行初始化</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">GLShaderManager shaderManager;</span><br><span class="line">shaderManager.<span class="built_in">InitializeStockShaders</span>();</span><br></pre></td></tr></table></figure><p>OpenGL内置了很多存储着色器可以使用</p><h3 id="单元着色器"><a href="#单元着色器" class="headerlink" title="单元着色器"></a>单元着色器</h3><p>使⽤场景：绘制默认OpenGL 坐标系(-1,1)下图形。 图形所有片段都会以⼀种颜⾊填充。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 参数1: 存储着⾊器种类-单元着⾊器</span></span><br><span class="line"><span class="comment">// 参数2: 颜⾊值</span></span><br><span class="line">GLShaderManager::<span class="built_in">UserStockShader</span>(GLT_SHADER_IDENTITY,</span><br><span class="line">                                 GLfloat vColor[<span class="number">4</span>]);</span><br></pre></td></tr></table></figure><h3 id="平面着色器"><a href="#平面着色器" class="headerlink" title="平面着色器"></a>平面着色器</h3><p>使⽤场景：在绘制图形时, 可以应⽤矩阵变换(模型&#x2F;投影变化)。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 参数1: 存储着⾊器种类-平⾯着⾊器</span></span><br><span class="line"><span class="comment">// 参数2: 允许变化的4*4矩阵</span></span><br><span class="line"><span class="comment">// 参数3: 颜⾊色值</span></span><br><span class="line">GLShaderManager::<span class="built_in">UserStockShader</span>(GLT_SHADER_FLAT,</span><br><span class="line">                                 GLfloat mvp[<span class="number">16</span>],</span><br><span class="line">                                 GLfloat vColor[<span class="number">4</span>]);</span><br></pre></td></tr></table></figure><h3 id="上⾊着⾊器"><a href="#上⾊着⾊器" class="headerlink" title="上⾊着⾊器"></a>上⾊着⾊器</h3><p>使⽤场景：在绘制图形时, 可以应⽤变换(模型&#x2F;投影变化)。颜色将会平滑地插入到顶点之间，称为平滑着色。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 参数1: 存储着⾊器种类-上⾊着⾊器</span></span><br><span class="line"><span class="comment">// 参数2: 允许变化的4*4矩阵</span></span><br><span class="line">GLShaderManager::<span class="built_in">UserStockShader</span>(GLT_SHADER_SHADED,</span><br><span class="line">                                 GLfloat mvp[<span class="number">16</span>]);</span><br></pre></td></tr></table></figure><h3 id="默认光源着色器"><a href="#默认光源着色器" class="headerlink" title="默认光源着色器"></a>默认光源着色器</h3><p>使⽤场景：在绘制图形时, 可以应⽤变换(模型&#x2F;投影变化)。这种着⾊器会使绘制的图形产生阴影和光照的效果。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 参数1: 存储着⾊器种类-默认光源着⾊器</span></span><br><span class="line"><span class="comment">// 参数2: 模型4*4矩阵</span></span><br><span class="line"><span class="comment">// 参数3: 投影4*4矩阵</span></span><br><span class="line"><span class="comment">// 参数4: 颜⾊值</span></span><br><span class="line">GLShaderManager::<span class="built_in">UserStockShader</span>(GLT_SHADER_DEFAULT_LIGHT,</span><br><span class="line">                                 GLfloat mvMatrix[<span class="number">16</span>],</span><br><span class="line">                                 GLfloat pMatrix[<span class="number">16</span>],</span><br><span class="line">                                 GLfloat vColor[<span class="number">4</span>]);</span><br></pre></td></tr></table></figure><h3 id="点光源着⾊器"><a href="#点光源着⾊器" class="headerlink" title="点光源着⾊器"></a>点光源着⾊器</h3><p>使⽤场景：在绘制图形时, 可以应用变换(模型&#x2F;投影变化)。这种着⾊器会使绘制的图形产⽣阴影和光照的效果。它与默认光源着⾊器⾮常类似，区别只是光源位置可能是特定的。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 参数1: 存储着⾊器种类-点光源着⾊器</span></span><br><span class="line"><span class="comment">// 参数2: 模型4*4矩阵</span></span><br><span class="line"><span class="comment">// 参数3: 投影4*4矩阵</span></span><br><span class="line"><span class="comment">// 参数4: 点光源的位置</span></span><br><span class="line"><span class="comment">// 参数5: 漫反射颜⾊值</span></span><br><span class="line">GLShaderManager::<span class="built_in">UserStockShader</span>(GLT_SHADER_POINT_LIGHT_DIFF,</span><br><span class="line">                                 GLfloat mvMatrix[<span class="number">16</span>],</span><br><span class="line">                                 GLfloat pMatrix[<span class="number">16</span>],</span><br><span class="line">                                 GLfloat vLightPos[<span class="number">3</span>],</span><br><span class="line">                                 GLfloat vColor[<span class="number">4</span>]);</span><br></pre></td></tr></table></figure><h3 id="纹理替换矩阵着⾊器"><a href="#纹理替换矩阵着⾊器" class="headerlink" title="纹理替换矩阵着⾊器"></a>纹理替换矩阵着⾊器</h3><p>使⽤场景：在绘制图形时, 可以应⽤变换(模型&#x2F;投影变化)。这种着⾊器通过给定的模型视图投影矩阵，使⽤纹理单元来进⾏颜⾊填充。其中每个像素点的颜⾊是从纹理中获取。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 参数1: 存储着⾊器种类-纹理替换矩阵着⾊器</span></span><br><span class="line"><span class="comment">// 参数2: 模型4*4矩阵</span></span><br><span class="line"><span class="comment">// 参数3: 纹理单元</span></span><br><span class="line">GLShaderManager::<span class="built_in">UserStockShader</span>(GLT_SHADER_TEXTURE_REPLACE,</span><br><span class="line">                                 GLfloat mvMatrix[<span class="number">16</span>],</span><br><span class="line">                                 GLint nTextureUnit);</span><br></pre></td></tr></table></figure><h3 id="纹理调整着⾊器"><a href="#纹理调整着⾊器" class="headerlink" title="纹理调整着⾊器"></a>纹理调整着⾊器</h3><p>使⽤场景：在绘制图形时, 可以应⽤变换(模型&#x2F;投影变化)。这种着⾊器通过给定的模型视图投影矩阵。着⾊器将⼀个基本⾊乘以⼀个取⾃纹理单元nTextureUnit 的纹理，将颜⾊与纹理进⾏颜⾊混合后才填充到⽚段中。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 参数1: 存储着⾊器种类-纹理调整着⾊器</span></span><br><span class="line"><span class="comment">// 参数2: 模型4*4矩阵</span></span><br><span class="line"><span class="comment">// 参数3: 颜⾊值</span></span><br><span class="line"><span class="comment">// 参数4: 纹理单元</span></span><br><span class="line">GLShaderManager::<span class="built_in">UserStockShader</span>(GLT_SHADER_TEXTURE_MODULATE,</span><br><span class="line">                                 GLfloat mvMatrix[<span class="number">16</span>],</span><br><span class="line">                                 GLfloat vColor[<span class="number">4</span>],</span><br><span class="line">                                 GLint nTextureUnit);</span><br></pre></td></tr></table></figure><h3 id="纹理光源着⾊器"><a href="#纹理光源着⾊器" class="headerlink" title="纹理光源着⾊器"></a>纹理光源着⾊器</h3><p>使⽤用场景：在绘制图形时, 可以应⽤变换(模型&#x2F;投影变化)。这种着⾊器通过给定的模型视图投影矩阵，着⾊器将⼀个纹理通过漫反射照明计算进⾏调整(相乘)。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 参数1: 存储着⾊器种类-纹理光源着⾊器</span></span><br><span class="line"><span class="comment">// 参数2: 模型4*4矩阵</span></span><br><span class="line"><span class="comment">// 参数3: 投影4*4矩阵</span></span><br><span class="line"><span class="comment">// 参数4: 点光源位置</span></span><br><span class="line"><span class="comment">// 参数5: 颜⾊值</span></span><br><span class="line"><span class="comment">// 参数6: 纹理单元</span></span><br><span class="line">GLShaderManager::<span class="built_in">UserStockShader</span>(GLT_SHADER_TEXTURE_POINT_LIGHT_DIEF,</span><br><span class="line">                                 GLfloat mvMatrix[<span class="number">16</span>],</span><br><span class="line">                                 GLfloat pMatrix[<span class="number">16</span>],</span><br><span class="line">                                 GLfloat vLightPos[<span class="number">3</span>],</span><br><span class="line">                                 GLfloat vBaseColor[<span class="number">4</span>],</span><br><span class="line">                                 GLint nTextureUnit);</span><br></pre></td></tr></table></figure><h2 id="综合案例"><a href="#综合案例" class="headerlink" title="综合案例"></a>综合案例</h2><p>图元绘制完整代码见<a href="https://github.com/zhengbomo/OpenGLDemo/tree/master/003--OpenGL%E5%9B%BE%E5%85%83%E7%BB%98%E5%88%B6(%E7%BB%BC%E5%90%88)">这里</a></p>]]></content>
    
    
    <summary type="html">&lt;h2 id=&quot;投影方式&quot;&gt;&lt;a href=&quot;#投影方式&quot; class=&quot;headerlink&quot; title=&quot;投影方式&quot;&gt;&lt;/a&gt;投影方式&lt;/h2&gt;&lt;p&gt;OpenGL有两种投影方式&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;投影方式&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;th&gt;函数&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;正投影&lt;/td&gt;
&lt;td&gt;相同的物体远近看起来都一样大&lt;/td&gt;
&lt;td&gt;GLFrustum::SetPerspective(float fFov, float fAspect, float fNear, float fFar)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;透视投影&lt;/td&gt;
&lt;td&gt;近大远小&lt;/td&gt;
&lt;td&gt;GLFrustum::SetOrthographic(GLfloat xMin, GLfloat xMax, GLfloat yMin, GLfloat yMax, GLfloat zMin, GLfloat zMax)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;</summary>
    
    
    
    <category term="OpenGL" scheme="http://zhengbomo.github.io/categories/OpenGL/"/>
    
    
    <category term="OpenGL" scheme="http://zhengbomo.github.io/tags/OpenGL/"/>
    
  </entry>
  
  <entry>
    <title>OpenGL正背面剔除和深度测试</title>
    <link href="http://zhengbomo.github.io/2020-07-16/opengl-face-culling/"/>
    <id>http://zhengbomo.github.io/2020-07-16/opengl-face-culling/</id>
    <published>2020-07-16T02:29:52.000Z</published>
    <updated>2020-07-16T02:29:52.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p>当我们在绘制3D图形的时候，由于观察者的角度问题，3D图形拥有多个面，假设所有的面是不透明的，在前面的面是可见的，而背后的面是不可见的，对于不可见的部分，不应该渲染出来，并且出于性能的考虑，应该丢弃（如下面正方体有6个面，而我们能看到的只有三个面），对于下面正方体，面比较少，影响不大，但对于一些复杂的3D图形，影响就比较大了，这种问题称为<code>隐藏面消除/找出可见面</code>（Hidden surface elimination&#x2F;Visible surface detemination）</p><span id="more"></span><img src="/images/post/opengl/cube.png" class="" width="200" title="正方体"><h2 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h2><h3 id="油画算法"><a href="#油画算法" class="headerlink" title="油画算法"></a>油画算法</h3><p>对于2D的图形，通常采用的是油画算法，远的物体先绘制，近的物体覆盖远的物体</p><img src="/images/post/opengl/painter-draw.png" class="" width="600" title="油画算法"><p>对于3D图形，由于有深度的影响，无法简单分辨物体的远近，如下图，相互交叉的三角形，油画算法将无法处理</p><img src="/images/post/opengl/triangle-composition.png" class="" width="300" title="多个三角形交叉叠加"><h3 id="正背面剔除（Face-Culling）"><a href="#正背面剔除（Face-Culling）" class="headerlink" title="正背面剔除（Face Culling）"></a>正背面剔除（Face Culling）</h3><p>在OpenGL中所有的面都是三角形组成，而所有的平面都有两个面（<code>正面</code>和<code>背面</code>），我们在一个时刻只能看到一个正面。而看不到的背面，OpenGL会检查所有正面朝向观察者的面，并渲染它们，从而丢弃背面朝向的⾯面</p><p>在OpenGL规定</p><p><code>正面</code>: 按照逆时针顶点连接顺序的三角形面<br><code>背面</code>: 按照顺时针顶点连接顺序的三⻆形面</p><img src="/images/post/opengl/cube-face-culling.png" class="" width="400"><p>从图中可以看出</p><p>左边三角形面相对于观察者是顺时针，所以面向观察者的是背面，右边的三角形面是逆时针，所以面向观察者的是正面，所有右边的三角形面被渲染，而左边的三角形面不会被渲染，<br>如果观察者在左边，则情况会反过来，左边的三角形面被渲染，而右边的三角形面被丢弃。三角形的正面还是背面，是根据观察者的观察方向而变动的。</p><h4 id="案例"><a href="#案例" class="headerlink" title="案例"></a>案例</h4><p>我们通过画一个甜甜圈来看下开启和关闭正背面剔除的区别</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;GLTools.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;GLMatrixStack.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;GLFrame.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;GLFrustum.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;GLGeometryTransform.h&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;math.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> __APPLE__</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;glut/glut.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> FREEGLUT_STATIC</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;GL/glut.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">////设置角色帧，作为相机</span></span><br><span class="line">GLFrame             viewFrame;</span><br><span class="line"><span class="comment">//使用GLFrustum类来设置透视投影</span></span><br><span class="line">GLFrustum           viewFrustum;</span><br><span class="line">GLTriangleBatch     torusBatch;</span><br><span class="line">GLMatrixStack       modelViewMatix;</span><br><span class="line">GLMatrixStack       projectionMatrix;</span><br><span class="line">GLGeometryTransform transformPipeline;</span><br><span class="line">GLShaderManager     shaderManager;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 标记：是否背面剔除</span></span><br><span class="line"><span class="type">int</span> iCull = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 渲染场景</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">renderScene</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 1.清除窗口和深度缓冲区</span></span><br><span class="line">    <span class="built_in">glClear</span>(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 开启/关闭正背面剔除功能</span></span><br><span class="line">    <span class="keyword">if</span> (iCull) &#123;</span><br><span class="line">        <span class="built_in">glEnable</span>(GL_CULL_FACE);</span><br><span class="line">        <span class="built_in">glFrontFace</span>(GL_CCW);</span><br><span class="line">        <span class="built_in">glCullFace</span>(GL_BACK);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="built_in">glDisable</span>(GL_CULL_FACE);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 2.把摄像机矩阵压入模型矩阵中</span></span><br><span class="line">    modelViewMatix.<span class="built_in">PushMatrix</span>(viewFrame);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 3.设置绘图颜色</span></span><br><span class="line">    GLfloat vRed[] = &#123; <span class="number">1.0f</span>, <span class="number">0.0f</span>, <span class="number">0.0f</span>, <span class="number">1.0f</span> &#125;;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 使用默认光源着色器</span></span><br><span class="line">    <span class="comment">// 通过光源、阴影效果跟提现立体效果</span></span><br><span class="line">    <span class="comment">// 参数1：GLT_SHADER_DEFAULT_LIGHT 默认光源着色器</span></span><br><span class="line">    <span class="comment">// 参数2：模型视图矩阵</span></span><br><span class="line">    <span class="comment">// 参数3：投影矩阵</span></span><br><span class="line">    <span class="comment">// 参数4：基本颜色值</span></span><br><span class="line">    shaderManager.<span class="built_in">UseStockShader</span>(GLT_SHADER_DEFAULT_LIGHT, transformPipeline.<span class="built_in">GetModelViewMatrix</span>(), transformPipeline.<span class="built_in">GetProjectionMatrix</span>(), vRed);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 5.绘制</span></span><br><span class="line">    torusBatch.<span class="built_in">Draw</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 6.出栈 绘制完成恢复</span></span><br><span class="line">    modelViewMatix.<span class="built_in">PopMatrix</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 7.交换缓存区</span></span><br><span class="line">    <span class="built_in">glutSwapBuffers</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">setupRC</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">//1.设置背景颜色</span></span><br><span class="line">    <span class="built_in">glClearColor</span>(<span class="number">0.3f</span>, <span class="number">0.3f</span>, <span class="number">0.3f</span>, <span class="number">1.0f</span> );</span><br><span class="line">    <span class="comment">//2.初始化着色器管理器</span></span><br><span class="line">    shaderManager.<span class="built_in">InitializeStockShaders</span>();</span><br><span class="line">    <span class="comment">//3.将相机向后移动7个单元：肉眼到物体之间的距离</span></span><br><span class="line">    viewFrame.<span class="built_in">MoveForward</span>(<span class="number">5</span>);</span><br><span class="line">    <span class="comment">//4.创建一个甜甜圈</span></span><br><span class="line">    <span class="comment">//void gltMakeTorus(GLTriangleBatch&amp; torusBatch, GLfloat majorRadius, GLfloat minorRadius, GLint numMajor, GLint numMinor);</span></span><br><span class="line">    <span class="comment">//参数1：GLTriangleBatch 容器帮助类</span></span><br><span class="line">    <span class="comment">//参数2：外边缘半径</span></span><br><span class="line">    <span class="comment">//参数3：内边缘半径</span></span><br><span class="line">    <span class="comment">//参数4、5：主半径和从半径的细分单元数量</span></span><br><span class="line">    <span class="built_in">gltMakeTorus</span>(torusBatch, <span class="number">1.0f</span>, <span class="number">0.3f</span>, <span class="number">52</span>, <span class="number">26</span>);</span><br><span class="line">    <span class="comment">//5.点的大小(方便点填充时,肉眼观察)</span></span><br><span class="line">    <span class="built_in">glPointSize</span>(<span class="number">1.0f</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 通过键盘↑↓←→修改旋转方向，控制Camera的移动，从而改变视口</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">specialKeys</span><span class="params">(<span class="type">int</span> key, <span class="type">int</span> x, <span class="type">int</span> y)</span> </span>&#123;</span><br><span class="line">    <span class="comment">//1.判断方向</span></span><br><span class="line">    <span class="keyword">if</span>(key == GLUT_KEY_UP)</span><br><span class="line">        <span class="comment">//2.根据方向调整观察者位置</span></span><br><span class="line">        viewFrame.<span class="built_in">RotateWorld</span>(<span class="built_in">m3dDegToRad</span>(<span class="number">-5.0</span>), <span class="number">1.0f</span>, <span class="number">0.0f</span>, <span class="number">0.0f</span>);</span><br><span class="line">    <span class="keyword">if</span>(key == GLUT_KEY_DOWN)</span><br><span class="line">        viewFrame.<span class="built_in">RotateWorld</span>(<span class="built_in">m3dDegToRad</span>(<span class="number">5.0</span>), <span class="number">1.0f</span>, <span class="number">0.0f</span>, <span class="number">0.0f</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(key == GLUT_KEY_LEFT)</span><br><span class="line">        viewFrame.<span class="built_in">RotateWorld</span>(<span class="built_in">m3dDegToRad</span>(<span class="number">-5.0</span>), <span class="number">0.0f</span>, <span class="number">1.0f</span>, <span class="number">0.0f</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(key == GLUT_KEY_RIGHT)</span><br><span class="line">        viewFrame.<span class="built_in">RotateWorld</span>(<span class="built_in">m3dDegToRad</span>(<span class="number">5.0</span>), <span class="number">0.0f</span>, <span class="number">1.0f</span>, <span class="number">0.0f</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 重新刷新</span></span><br><span class="line">    <span class="built_in">glutPostRedisplay</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/// 窗口改变</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">changeSize</span><span class="params">(<span class="type">int</span> w, <span class="type">int</span> h)</span> </span>&#123;</span><br><span class="line">    <span class="comment">//1.防止h变为0</span></span><br><span class="line">    <span class="keyword">if</span>(h == <span class="number">0</span>) &#123;</span><br><span class="line">        h = <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//2.设置视口窗口尺寸</span></span><br><span class="line">    <span class="built_in">glViewport</span>(<span class="number">0</span>, <span class="number">0</span>, w, h);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//3.setPerspective函数的参数是一个从顶点方向看去的视场角度（用角度值表示）</span></span><br><span class="line">    <span class="comment">// 设置透视模式，初始化其透视矩阵</span></span><br><span class="line">    viewFrustum.<span class="built_in">SetPerspective</span>(<span class="number">35.0f</span>, <span class="built_in">float</span>(w)/<span class="built_in">float</span>(h), <span class="number">1.0f</span>, <span class="number">100.0f</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//4.把透视矩阵加载到透视矩阵对阵中</span></span><br><span class="line">    projectionMatrix.<span class="built_in">LoadMatrix</span>(viewFrustum.<span class="built_in">GetProjectionMatrix</span>());</span><br><span class="line"></span><br><span class="line">    <span class="comment">//5.初始化渲染管线</span></span><br><span class="line">    transformPipeline.<span class="built_in">SetMatrixStacks</span>(modelViewMatix, projectionMatrix);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span>* argv[])</span> </span>&#123;</span><br><span class="line">    <span class="built_in">gltSetWorkingDirectory</span>(argv[<span class="number">0</span>]);</span><br><span class="line"></span><br><span class="line">    <span class="built_in">glutInit</span>(&amp;argc, argv);</span><br><span class="line">    <span class="built_in">glutInitDisplayMode</span>(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);</span><br><span class="line">    <span class="built_in">glutInitWindowSize</span>(<span class="number">800</span>, <span class="number">600</span>);</span><br><span class="line">    <span class="built_in">glutCreateWindow</span>(<span class="string">&quot;Geometry Test Program&quot;</span>);</span><br><span class="line">    <span class="built_in">glutReshapeFunc</span>(changeSize);</span><br><span class="line">    <span class="built_in">glutSpecialFunc</span>(specialKeys);</span><br><span class="line">    <span class="built_in">glutDisplayFunc</span>(renderScene);</span><br><span class="line"></span><br><span class="line">    GLenum err = <span class="built_in">glewInit</span>();</span><br><span class="line">    <span class="keyword">if</span> (GLEW_OK != err) &#123;</span><br><span class="line">        <span class="built_in">fprintf</span>(stderr, <span class="string">&quot;GLEW Error: %s\n&quot;</span>, <span class="built_in">glewGetErrorString</span>(err));</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">setupRC</span>();</span><br><span class="line"></span><br><span class="line">    <span class="built_in">glutMainLoop</span>();</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如果不开启正背面剔除，背面的黑色面也会显示出来</p><img src="/images/post/opengl/face-culling.png" class="" width="600"><p>在旋转到中间的过程时遇到下面情况</p><img src="/images/post/opengl/face-culling-deep-problem.png" class="" width="300"><h3 id="深度测试"><a href="#深度测试" class="headerlink" title="深度测试"></a>深度测试</h3><p>出现上面缺口的原因是由于，在同一个位置，出现了两个正面，甜甜圈的内环和外环，都有一个正面，OpenGL不知道显示哪一个面，就出现了上面的问题，把内环的面显示出来了，覆盖了外环的面，解决问题之前，先来理解几个概念</p><h4 id="深度"><a href="#深度" class="headerlink" title="深度"></a>深度</h4><p>深度是指OpenGL坐标系中，像素点的Z坐标距观察者的距离</p><ul><li>如果观察者在Z轴的正方向，Z值越大则越靠近观察者</li><li>如果观察者在Z轴的负方向，Z值越小则越靠近观察者</li></ul><h4 id="深度缓冲区（Depth-Buffer）"><a href="#深度缓冲区（Depth-Buffer）" class="headerlink" title="深度缓冲区（Depth Buffer）"></a>深度缓冲区（Depth Buffer）</h4><p>深度缓存区是指一块专门内存区域，存储在显存中，用于存储屏幕上所绘制图形的每个像素点的深度值，深度越大，离观察者越远</p><h4 id="深度测试-1"><a href="#深度测试-1" class="headerlink" title="深度测试"></a>深度测试</h4><p>在绘制物体的时候，像素点的深度会和之前的深度值做比较，如果 新值 &gt; 旧值，则会被丢弃不绘制，反之，新值会更新到<code>深度缓冲区</code>，新的颜色值同样会更新到<code>颜色缓冲区</code>，这个过程称为深度测试，确保所有绘制的点都是距离观察者最近的</p><p>开启和关闭深度测试</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 在RenderScene清空完缓冲区后设置</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 开启深度测试</span></span><br><span class="line"><span class="built_in">glEnable</span>(GL_DEPTH_TEST);</span><br><span class="line"><span class="comment">// 关闭深度测试</span></span><br><span class="line"><span class="built_in">glDisable</span>(GL_DEPTH_TEST);</span><br></pre></td></tr></table></figure><img src="/images/post/opengl/face-culling-deep.png" class="" width="600"><p>相比于<code>正背面剔除</code>，<code>深度测试</code>不仅可以解决正背面显示问题，还能解决上面隐藏面消除的问题</p><h4 id="ZFighting闪烁"><a href="#ZFighting闪烁" class="headerlink" title="ZFighting闪烁"></a>ZFighting闪烁</h4><p>深度测试很好的解决了3D图形层次显示的问题，但是还存在一个误差的问题，开启深度测试后，由于深度缓冲区精度有限制，导致深度值相差很小时，OpenGL出现无法判断的情况，导致出现画面交错闪现的现象，这个问题成为<code>ZFighting闪烁</code>，如下图</p><img src="/images/post/opengl/z-fighting.png" class="" width="600"><p>其问题产生的主要原因是由于图形靠的太近，导致无法区分出图层先后次序，针对该问题，OpenGL提供了一种多边形偏移（Polygon Offset）方案</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 开启多边形偏移</span></span><br><span class="line"><span class="built_in">glEnable</span>(GL_POLYGON_OFFSET_FILL);</span><br></pre></td></tr></table></figure><table><thead><tr><th>多边形偏移枚举值</th><th>对应光栅化模式</th></tr></thead><tbody><tr><td>GL_POLYGON_OFFSET_POINT</td><td>GL_POINT</td></tr><tr><td>GL_POLYGON_OFFSET_LINE</td><td>GL_LINE</td></tr><tr><td>GL_POLYGON_OFFSET_FILL</td><td>GL_FILL</td></tr></tbody></table><p>使用步骤</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 1. 开启多边形偏移</span></span><br><span class="line"><span class="built_in">glEnable</span>(GL_POLYGON_OFFSET_FILL);</span><br><span class="line"><span class="comment">// 2. 指定偏移量glPolygonOffset (GLfloat factor, GLfloat units);，参数一般填 -1 和 -1</span></span><br><span class="line"><span class="built_in">glPolygonOffset</span> (GLfloat factor, GLfloat units);</span><br><span class="line"><span class="comment">// 3. 关闭多边形偏移</span></span><br><span class="line"><span class="built_in">glDisable</span>(GL_POLYGON_OFFSET_FILL)</span><br></pre></td></tr></table></figure><p>预防ZFighting闪烁</p><ul><li>避免两个物体靠的太近：在绘制时，插入一个小偏移</li><li>将近裁剪面（设置透视投影时设置）设置的离观察者远一些：提高裁剪范围内的精确度</li><li>使用更高位数的深度缓冲区：提高深度缓冲区的精确度</li></ul>]]></content>
    
    
    <summary type="html">&lt;h2 id=&quot;背景&quot;&gt;&lt;a href=&quot;#背景&quot; class=&quot;headerlink&quot; title=&quot;背景&quot;&gt;&lt;/a&gt;背景&lt;/h2&gt;&lt;p&gt;当我们在绘制3D图形的时候，由于观察者的角度问题，3D图形拥有多个面，假设所有的面是不透明的，在前面的面是可见的，而背后的面是不可见的，对于不可见的部分，不应该渲染出来，并且出于性能的考虑，应该丢弃（如下面正方体有6个面，而我们能看到的只有三个面），对于下面正方体，面比较少，影响不大，但对于一些复杂的3D图形，影响就比较大了，这种问题称为&lt;code&gt;隐藏面消除/找出可见面&lt;/code&gt;（Hidden surface elimination&amp;#x2F;Visible surface detemination）&lt;/p&gt;</summary>
    
    
    
    <category term="OpenGL" scheme="http://zhengbomo.github.io/categories/OpenGL/"/>
    
    
    <category term="OpenGL" scheme="http://zhengbomo.github.io/tags/OpenGL/"/>
    
  </entry>
  
  <entry>
    <title>iOS离屏渲染原理和优化</title>
    <link href="http://zhengbomo.github.io/2020-07-14/ios-offscreen-render/"/>
    <id>http://zhengbomo.github.io/2020-07-14/ios-offscreen-render/</id>
    <published>2020-07-14T02:07:11.000Z</published>
    <updated>2020-07-14T02:07:11.000Z</updated>
    
    <content type="html"><![CDATA[<p>几乎做iOS开发的人都知道，设置<code>圆角</code>会触发<code>离屏渲染</code>，那么什么情况下设置圆角不会触发离屏渲染呢，为什么会触发离屏渲染。</p><span id="more"></span><h2 id="油画算法"><a href="#油画算法" class="headerlink" title="油画算法"></a>油画算法</h2><p>计算机图层的叠加绘制大概遵循<code>油画算法</code>，在这种算法下会按层绘制，首先绘制距离较远的场景，然后用绘制距离较近的场景覆盖较远的部分，如下图。</p><img src="/images/post/opengl/painter-draw.png" class="" width="800" title="油画算法"><p>这样就不会导致远的物体挡住近的物体，但是有个局限，就是无法在后面一层渲染完成后，再回去修改前面图层，因为前面的图层已经被覆盖了</p><h2 id="离屏渲染"><a href="#离屏渲染" class="headerlink" title="离屏渲染"></a>离屏渲染</h2><p>对于有前后依赖的图层（如全局剪切，阴影等），油画算法无法满足我们的需求，对于有前后依赖的图层，我们可以再另开辟一个空间，用于临时渲染，渲染完成后再渲染到当前的缓冲区上，这个临时渲染，就是<code>离屏渲染</code>，由于需要开辟一个新的内存空间，并且共享同一个上下文，所以还需要做上下文切换（状态切换），并且渲染完成后还要进行拷贝操作</p><ol><li><code>开辟临时缓存空间</code></li><li><code>上下文切换</code>，上下文对象比较大，切换操作会带来一定的性能消耗</li><li><code>内存拷贝</code></li><li><code>额外的渲染</code>（没有进一步考证）</li></ol><p>上面4项带来的开销会很大，并且每一帧渲染都需要执行，如果屏幕上触发离屏渲染的操作过多，会导致GPU渲染时间过长造成卡顿，应该避免触发离屏渲染</p><img src="/images/post/opengl/offscreen-flow.png" class="" width="600"><h2 id="iOS圆角问题"><a href="#iOS圆角问题" class="headerlink" title="iOS圆角问题"></a>iOS圆角问题</h2><p>官方文档关于<code>layer.cornerRadius</code>的描述</p><img src="/images/post/opengl/layer-cornerradius.png" class="" width="600"><blockquote><p><code>layer.cornerRadius</code>只作用<code>backgroundColor</code>和<code>border</code>，不会作用于<code>content</code>，支持<code>动画</code></p></blockquote><p>离屏渲染是GPU无法按油画算法一次性渲染完我们的视图才会触发，我们先来看几个iOS的例子，模拟器打开<code>Color Off-screen Rendered</code></p><figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 1. UIImageView</span></span><br><span class="line"><span class="keyword">let</span> imageView <span class="operator">=</span> <span class="type">UIImageView</span>(frame: <span class="type">CGRect</span>(x: <span class="number">50</span>, y: <span class="number">100</span>, width: <span class="number">300</span>, height: <span class="number">200</span>))</span><br><span class="line"><span class="keyword">self</span>.view.addSubview(imageView)</span><br><span class="line">imageView.image <span class="operator">=</span> <span class="type">UIImage</span>.<span class="keyword">init</span>(named: <span class="string">&quot;test.jpg&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// image + cornerRadius + masksToBounds 不会触发离屏渲染</span></span><br><span class="line">imageView.layer.cornerRadius <span class="operator">=</span> <span class="number">10</span></span><br><span class="line">imageView.layer.masksToBounds <span class="operator">=</span> <span class="literal">true</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 触发离屏渲染</span></span><br><span class="line">imageView.backgroundColor <span class="operator">=</span> <span class="type">UIColor</span>.green</span><br><span class="line"><span class="comment">// 添加一个空的UIView不会触发离屏渲染</span></span><br><span class="line"><span class="comment">// imageView.addSubview(UIView(frame: CGRect(x: 0, y: 0, width: 10, height: 10)))</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. UIButton</span></span><br><span class="line"><span class="keyword">let</span> button <span class="operator">=</span> <span class="type">UIButton</span>(type: .custom)</span><br><span class="line">button.frame <span class="operator">=</span> <span class="type">CGRect</span>(x: <span class="number">50</span>, y: <span class="number">300</span> <span class="operator">+</span> <span class="number">50</span>, width: <span class="number">300</span>, height: <span class="number">50</span>)</span><br><span class="line"><span class="keyword">self</span>.view.addSubview(button)</span><br><span class="line">button.setTitle(<span class="string">&quot;Test&quot;</span>, for: .normal)</span><br><span class="line">button.setTitleColor(<span class="type">UIColor</span>.blue, for: .normal)</span><br><span class="line">button.layer.cornerRadius <span class="operator">=</span> <span class="number">10</span></span><br><span class="line">button.layer.masksToBounds <span class="operator">=</span> <span class="literal">true</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 触发离屏渲染</span></span><br><span class="line">button.backgroundColor <span class="operator">=</span> <span class="type">UIColor</span>.green</span><br><span class="line"><span class="comment">// 触发离屏渲染</span></span><br><span class="line">button.setBackgroundImage(<span class="type">UIImage</span>(named: <span class="string">&quot;test.jpg&quot;</span>), for: .normal)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 3. UIView</span></span><br><span class="line"><span class="keyword">let</span> view <span class="operator">=</span> <span class="type">UIView</span>(frame: <span class="type">CGRect</span>(x: <span class="number">50</span>, y: <span class="number">400</span> <span class="operator">+</span> <span class="number">50</span>, width: <span class="number">300</span>, height: <span class="number">50</span>))</span><br><span class="line"><span class="keyword">self</span>.view.addSubview(view)</span><br><span class="line">view.backgroundColor <span class="operator">=</span> <span class="type">UIColor</span>.red</span><br><span class="line">view.layer.cornerRadius <span class="operator">=</span> <span class="number">10</span></span><br><span class="line">view.layer.masksToBounds <span class="operator">=</span> <span class="literal">true</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// label如果被渲染，则会触发渲染，如果text为空不会被渲染</span></span><br><span class="line"><span class="keyword">let</span> label <span class="operator">=</span> <span class="type">UILabel</span>(frame: <span class="type">CGRect</span>(x: <span class="number">10</span>, y: <span class="number">10</span>, width: <span class="number">1</span>, height: <span class="number">1</span>))</span><br><span class="line">label.text <span class="operator">=</span> <span class="string">&quot;1&quot;</span></span><br><span class="line">view.addSubview(label)</span><br></pre></td></tr></table></figure><img src="/images/post/opengl/offscreen-demo.png" class="" width="300" title="出现离屏渲染的地方被标记为黄色"><p>如果设置了<code>cornerRadius+masksToBounds</code>（裁切），并且用于渲染的图层大于1，就会触发离屏渲染，其中如果设置<code>backgroundColor</code>，背景颜色相当于一个单独一个图层，<code>subviews</code>的图层也算，UILabel如果text为空（subviews为空，backgroundColor为空），则不会生成渲染图层</p><p>所以设置了<code>cornerRadius+masksToBounds</code>的</p><ul><li><code>UIImageView</code>设置图片不会触发离屏渲染</li><li><code>UIView</code>设置了背景颜色，但不添加subview，不会触发离屏渲染</li><li><code>UILabel</code>设置了文字，并且设置了backgroundColor，会触发离屏渲染</li><li><code>UIButton</code>只设置文字和背景，会触发离屏渲染</li></ul><h2 id="优化圆角问题"><a href="#优化圆角问题" class="headerlink" title="优化圆角问题"></a>优化圆角问题</h2><p>基于上面的问题，我们可以有几个优化方向</p><ol><li>避免使用<code>裁切</code>(<code>masksToBounds</code>)操作，如果我们能确保View里面的内容不会溢出，就可以不用<code>masksToBounds</code></li><li>即使要用到裁切的操作，尽量放到子view里面，不要在上层view使用masksToBounds，因为裁切需要对所有的layer和subview所有图层都进行裁切，这样离屏渲染会需要更大的空间，裁切更多的图层，应该只对必要的view&#x2F;layer进行裁切</li><li>提前切好需要的圆角，避免渲染的时候再切</li></ol><h2 id="其他触发离屏渲染的情况"><a href="#其他触发离屏渲染的情况" class="headerlink" title="其他触发离屏渲染的情况"></a>其他触发离屏渲染的情况</h2><ul><li>使用了遮罩的 layer (<code>layer.mask</code>)</li><li>需要进行裁剪的 layer (<code>layer.masksToBounds</code> &#x2F; <code>view.clipsToBounds</code>)</li><li>设置了组透明度为 YES，并且透明度不为 1 的layer (<code>layer.allowsGroupOpacity</code> &#x2F; <code>layer.opacity</code>)</li><li>添加了投影的 layer (<code>layer.shadow</code>)，但如果设置了shadowPath，则系统已经知道如何绘制阴影了，不会触发离屏渲染</li><li>采用了光栅化的 layer (<code>layer.shouldRasterize</code>)，光栅化也可以优化离屏渲染问题</li><li>绘制了文字的 layer (<code>UILabel</code>, <code>CATextLayer</code>, <code>CoreText</code>等)</li></ul><h2 id="毛玻璃"><a href="#毛玻璃" class="headerlink" title="毛玻璃"></a>毛玻璃</h2><p>在iOS系统中，毛玻璃效果应用的非常广泛，从上面分析也可以知道，这个肯定会触发离屏渲染的，图层之间存在依赖，下面是<code>UIBlurEffect</code>的处理过程</p><img src="/images/post/opengl/uiblureffect-render2.png" class="" width="600"><p>在GPU的渲染过程如下图</p><img src="/images/post/opengl/uiblureffect-render.png" class="" width="1000"><p>GPU在渲染完Content之后，会另外开辟一个<code>Off-screen buffer</code>，执行下面步骤，最后再做合并处理，最后再拷贝回<code>On-screen buffer</code>上</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;几乎做iOS开发的人都知道，设置&lt;code&gt;圆角&lt;/code&gt;会触发&lt;code&gt;离屏渲染&lt;/code&gt;，那么什么情况下设置圆角不会触发离屏渲染呢，为什么会触发离屏渲染。&lt;/p&gt;</summary>
    
    
    
    <category term="iOS" scheme="http://zhengbomo.github.io/categories/iOS/"/>
    
    
    <category term="iOS" scheme="http://zhengbomo.github.io/tags/iOS/"/>
    
  </entry>
  
  <entry>
    <title>【OpenGL案例2】正方形键位控制</title>
    <link href="http://zhengbomo.github.io/2020-07-12/opengl-triangle-demo2/"/>
    <id>http://zhengbomo.github.io/2020-07-12/opengl-triangle-demo2/</id>
    <published>2020-07-12T11:40:12.000Z</published>
    <updated>2020-07-12T11:40:12.000Z</updated>
    
    <content type="html"><![CDATA[<p>上一篇完成了<a href="/2020-07-11/opengl-triangle-demo/">三角形的绘制</a>，今天来添加一个变换控制，通过键盘的↑↓←→控制图形的移动</p><span id="more"></span><h2 id="画正方形"><a href="#画正方形" class="headerlink" title="画正方形"></a>画正方形</h2><p>基于原来的工程，把三角形改为正方形，<code>GL_TRIANGLES</code>修改为<code>GL_TRIANGLE_FAN</code></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 正方形边长</span></span><br><span class="line">GLfloat blockSize = <span class="number">0.2f</span>;</span><br><span class="line"><span class="comment">// 正方形的4个点坐标</span></span><br><span class="line">GLfloat vVerts[] = &#123;</span><br><span class="line">        -blockSize,-blockSize,<span class="number">0.0f</span>,</span><br><span class="line">        blockSize,-blockSize,<span class="number">0.0f</span>,</span><br><span class="line">        blockSize,blockSize,<span class="number">0.0f</span>,</span><br><span class="line">        -blockSize,blockSize,<span class="number">0.0f</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">...</span><br><span class="line"></span><br><span class="line"><span class="comment">// 三角形顶点改为正方形，图元装配改为GL_TRIANGLE_FAN</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">setupRC</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 修改为GL_TRIANGLE_FAN ，4个顶点构成四边形</span></span><br><span class="line">    triangleBatch.<span class="built_in">Begin</span>(GL_TRIANGLE_FAN, <span class="number">4</span>);</span><br><span class="line">    triangleBatch.<span class="built_in">CopyVertexData3f</span>(vVerts);</span><br><span class="line">    triangleBatch.<span class="built_in">End</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><img src="/images/post/opengl/square-demo.png" class="" width="600" title="绘制正方形"><h2 id="平移变换"><a href="#平移变换" class="headerlink" title="平移变换"></a>平移变换</h2><h3 id="修改坐标"><a href="#修改坐标" class="headerlink" title="修改坐标"></a>修改坐标</h3><p>平移变换可以直接修改正方形的4个顶点，然后重新刷新</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// x轴步长</span></span><br><span class="line">GLfloat xStepSize = <span class="number">0.025f</span>;</span><br><span class="line"><span class="comment">// y轴步长</span></span><br><span class="line">GLfloat yStepSize = <span class="number">0.025f</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 重新生成顶点</span></span><br><span class="line">GLfloat vVerts[] = &#123;</span><br><span class="line">    -blockSize + xStepSize, -blockSize + yStepSize, <span class="number">0.0f</span>,</span><br><span class="line">    blockSize + xStepSize, -blockSize + yStepSize, <span class="number">0.0f</span>,</span><br><span class="line">    blockSize + xStepSize, blockSize + yStepSize, <span class="number">0.0f</span>,</span><br><span class="line">    -blockSize + xStepSize, blockSize + yStepSize, <span class="number">0.0f</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 新的顶点重新设置到批次类中</span></span><br><span class="line">triangleBatch.<span class="built_in">CopyVertexData3f</span>(vVerts);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 触发重新绘制</span></span><br><span class="line"><span class="built_in">glutPostRedisplay</span>();</span><br></pre></td></tr></table></figure><h3 id="矩阵变换"><a href="#矩阵变换" class="headerlink" title="矩阵变换"></a>矩阵变换</h3><p>上面直接通过修改坐标的方式过于麻烦，对于复杂的图形做变换会非常麻烦，而推荐使用<code>矩阵变换</code>，基于<code>平面着色器</code>，可以作用于任何的图形，不需要手动计算坐标，不仅可以做平移，还能做旋转缩放等</p><p><code>renderScene</code>操作流程</p><ol><li>清理特定缓存区</li><li>根据平移距离生成<code>平移矩阵</code></li><li>如果有多个矩阵变换，通过<code>叉乘</code>得到最终矩阵</li><li>将<code>矩阵结果</code>交给存储着色器（<code>平面着色器</code>）中绘制</li></ol><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// x轴平移距离</span></span><br><span class="line">GLfloat xPos = <span class="number">0.1f</span>;</span><br><span class="line"><span class="comment">// y轴平移距离</span></span><br><span class="line">GLfloat yPos = <span class="number">0.1f</span>;</span><br><span class="line"><span class="comment">// 旋转(旋转5度)</span></span><br><span class="line">GLfloat rotate = <span class="number">5.0f</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">renderScene</span><span class="params">(<span class="type">void</span>)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 清空缓冲区</span></span><br><span class="line">    <span class="built_in">glClear</span>(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 定义2个矩阵（平移矩阵，旋转矩阵）</span></span><br><span class="line">    M3DMatrix44f mTransfromMatrix, mRotationMartix;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 平移(xPos, yPos)</span></span><br><span class="line">    <span class="built_in">m3dTranslationMatrix44</span>(mTransfromMatrix, xPos, yPos, <span class="number">0.0f</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 旋转(rotate)</span></span><br><span class="line">    <span class="built_in">m3dRotationMatrix44</span>(mRotationMartix, <span class="built_in">m3dDegToRad</span>(rotate), <span class="number">0.0f</span>, <span class="number">0.0f</span>, <span class="number">1.0f</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将旋转和移动的矩阵结果 合并到mFinalTransform （矩阵相乘）</span></span><br><span class="line">    M3DMatrix44f mFinalTransform;</span><br><span class="line">    <span class="built_in">m3dMatrixMultiply44</span>(mFinalTransform, mTransfromMatrix, mRotationMartix);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 正方形填充颜色</span></span><br><span class="line">    GLfloat vRed[] = &#123;<span class="number">1.0f</span>,<span class="number">0.0f</span>,<span class="number">0.0f</span>,<span class="number">0.0f</span>&#125;;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//将矩阵结果 提交给固定着色器（平面着色器）中绘制</span></span><br><span class="line">    shaderManager.<span class="built_in">UseStockShader</span>(GLT_SHADER_FLAT, mFinalTransform, vRed);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 提交顶点到着色器绘制</span></span><br><span class="line">    triangleBatch.<span class="built_in">Draw</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">//执行交换缓存区</span></span><br><span class="line">    <span class="built_in">glutSwapBuffers</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="键盘控制"><a href="#键盘控制" class="headerlink" title="键盘控制"></a>键盘控制</h2><p>GLUT提供了键盘监听事件</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 注册特殊函数</span></span><br><span class="line"><span class="built_in">glutSpecialFunc</span>(onSpecialKeys);</span><br></pre></td></tr></table></figure><p>实现上下左右键的监听</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">SpecialKeys</span><span class="params">(<span class="type">int</span> key, <span class="type">int</span> x, <span class="type">int</span> y)</span></span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (key == GLUT_KEY_UP) &#123;</span><br><span class="line">        yPos += stepSize;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (key == GLUT_KEY_DOWN) &#123;</span><br><span class="line">        yPos -= stepSize;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (key == GLUT_KEY_LEFT) &#123;</span><br><span class="line">        xPos -= stepSize;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (key == GLUT_KEY_RIGHT) &#123;</span><br><span class="line">        xPos += stepSize;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 边缘检测</span></span><br><span class="line">    <span class="keyword">if</span> (xPos &lt; (<span class="number">-1.0f</span> + blockSize)) &#123;</span><br><span class="line">        xPos = <span class="number">-1.0f</span> + blockSize;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (xPos &gt; (<span class="number">1.0f</span> - blockSize)) &#123;</span><br><span class="line">        xPos = <span class="number">1.0f</span> - blockSize;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (yPos &lt; (<span class="number">-1.0f</span> + blockSize)) &#123;</span><br><span class="line">        yPos = <span class="number">-1.0f</span> + blockSize;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (yPos &gt; (<span class="number">1.0f</span> - blockSize)) &#123;</span><br><span class="line">        yPos = <span class="number">1.0f</span> - blockSize;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 重新绘制</span></span><br><span class="line">    <span class="built_in">glutPostRedisplay</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><img src="/images/post/opengl/square-demo.gif" class="" width="600" title="键盘控制正方形移动"><p>完整代码见<a href="https://github.com/zhengbomo/OpenGLDemo/tree/master/002--%E9%94%AE%E7%9B%98%E6%8E%A7%E5%88%B6%E6%AD%A3%E6%96%B9%E5%BD%A2">这里</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;上一篇完成了&lt;a href=&quot;/2020-07-11/opengl-triangle-demo/&quot;&gt;三角形的绘制&lt;/a&gt;，今天来添加一个变换控制，通过键盘的↑↓←→控制图形的移动&lt;/p&gt;</summary>
    
    
    
    <category term="OpenGL" scheme="http://zhengbomo.github.io/categories/OpenGL/"/>
    
    
    <category term="OpenGL" scheme="http://zhengbomo.github.io/tags/OpenGL/"/>
    
  </entry>
  
  <entry>
    <title>【OpenGL案例1】绘制一个三角形</title>
    <link href="http://zhengbomo.github.io/2020-07-11/opengl-triangle-demo/"/>
    <id>http://zhengbomo.github.io/2020-07-11/opengl-triangle-demo/</id>
    <published>2020-07-11T13:42:13.000Z</published>
    <updated>2020-07-11T13:42:13.000Z</updated>
    
    <content type="html"><![CDATA[<p>通过使用OpenGL绘制一个三角形，了解OpenGL的一些常用API和绘制流程，这里使用了<a href="https://www.opengl.org/resources/libraries/glut/">GLUT</a>（OpenGL Utility Toolkit）的API，<code>GLUT</code>是OpenGL官方提供的OpenGL工具箱，封装了一些有用的工具，用于辅助操作OpenGL</p><span id="more"></span><h2 id="工具API"><a href="#工具API" class="headerlink" title="工具API"></a>工具API</h2><p>本案例也使用<code>GLUT</code>工具进行绘制，使用最基本的固定着色器来绘制，本案例会用到下面几个头文件</p><table><thead><tr><th>头文件</th><th>说明</th></tr></thead><tbody><tr><td>GLShaderManager.h</td><td>着色器管理类，用于创建和管理着色器，还提供一组内置的存储着色器（固定管线着色器）</td></tr><tr><td>GLTools.h</td><td>包含大部分类似C语⾔的独⽴函数</td></tr><tr><td>GLUT.h</td><td>OpenGL工具箱</td></tr></tbody></table><h2 id="相关函数"><a href="#相关函数" class="headerlink" title="相关函数"></a>相关函数</h2><p>这里会注册两个函数用于绘制</p><ul><li><code>renderSence</code>: 类似于iOS的类似于<code>drawRect</code>，每次View需要重新绘制的时候，会被调用</li><li><code>changeSize</code>: 窗口大小变化的时候被调用，通常用于调整glview的视口</li></ul><p>定义两个变量用于<code>管理着色器</code>和<code>顶点数据</code></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;GLShaderManager.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;GLTools.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;GLUT/GLUT.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义一个，着色管理器</span></span><br><span class="line">GLShaderManager shaderManager;</span><br><span class="line"><span class="comment">// 简单的批次容器，用于将顶点数据提交给着色器使用</span></span><br><span class="line">GLBatch triangleBatch;</span><br></pre></td></tr></table></figure><h3 id="main"><a href="#main" class="headerlink" title="main"></a>main</h3><p>程序启动的时候，我们需要做一些初始化操作</p><ol><li>初始化<code>glutInit</code></li><li>初始化双缓冲区，颜色模式，深度，模板</li><li>设置窗口信息（大小，标题）</li><li>注册生命周期函数：<code>renderSence</code>和<code>changeSize</code></li><li>测试驱动可用性: 通过<code>glewInit</code>结果判断</li><li>初始化渲染数据<code>setupRC</code></li><li>开启事件循环</li></ol><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">int</span> argc,<span class="type">char</span> *argv[])</span> </span>&#123;</span><br><span class="line">    <span class="comment">//初始化GLUT库,这个函数只是传说命令参数并且初始化glut库</span></span><br><span class="line">    <span class="built_in">glutInit</span>(&amp;argc, argv);</span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">     初始化双缓冲窗口，其中标志GLUT_DOUBLE、GLUT_RGBA、GLUT_DEPTH、GLUT_STENCIL分别指</span></span><br><span class="line"><span class="comment">     双缓冲窗口、RGBA颜色模式、深度测试、模板缓冲区</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">     --GLUT_DOUBLE`：双缓存窗口，是指绘图命令实际上是离屏缓存区执行的，然后迅速转换成窗口视图，这种方式，经常用来生成动画效果；</span></span><br><span class="line"><span class="comment">     --GLUT_DEPTH`：标志将一个深度缓存区分配为显示的一部分，因此我们能够执行深度测试；</span></span><br><span class="line"><span class="comment">     --GLUT_STENCIL`：确保我们也会有一个可用的模板缓存区。</span></span><br><span class="line"><span class="comment">     深度、模板测试后面会细致讲到</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="built_in">glutInitDisplayMode</span>(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//GLUT窗口大小、窗口标题</span></span><br><span class="line">    <span class="built_in">glutInitWindowSize</span>(<span class="number">800</span>, <span class="number">600</span>);</span><br><span class="line">    <span class="built_in">glutCreateWindow</span>(<span class="string">&quot;Triangle&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 注册窗口改变事件</span></span><br><span class="line">    <span class="built_in">glutReshapeFunc</span>(changeSize);</span><br><span class="line">    <span class="comment">// 注册显示函数，当需要重新绘制的时候，会调用</span></span><br><span class="line">    <span class="built_in">glutDisplayFunc</span>(renderScene);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 初始化一个GLEW库，测试是否报错，确保OpenGL API对程序完全可用。</span></span><br><span class="line">    GLenum status = <span class="built_in">glewInit</span>();</span><br><span class="line">    <span class="keyword">if</span> (GLEW_OK != status) &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;GLEW Error:%s\n&quot;</span>,<span class="built_in">glewGetErrorString</span>(status));</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 准备我们需要渲染的数据</span></span><br><span class="line">    <span class="built_in">setupRC</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 开启事件循环，相当于iOS的runloop</span></span><br><span class="line">    <span class="built_in">glutMainLoop</span>();</span><br><span class="line">    <span class="keyword">return</span>  <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="setupRC"><a href="#setupRC" class="headerlink" title="setupRC"></a>setupRC</h3><p>我们要画一个<code>三角形</code>，可以在这里做一些准备工作</p><ul><li>设置清屏颜色</li><li>初始化着色器</li><li>初始化顶点数据</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">setupRC</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 设置清屏颜色（背景颜色，白色）</span></span><br><span class="line">    <span class="built_in">glClearColor</span>(<span class="number">1.0f</span>, <span class="number">1.0f</span>, <span class="number">1.0f</span>, <span class="number">1.0f</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 没有着色器，在OpenGL 核心框架中是无法进行任何渲染的。这里初始化一个渲染管理器，在renderSence会用到。这里使用固定管线着色器</span></span><br><span class="line">    shaderManager.<span class="built_in">InitializeStockShaders</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 三角形顶点</span></span><br><span class="line">    GLfloat vVerts[] = &#123;</span><br><span class="line">        <span class="number">-0.5f</span>,<span class="number">0.0f</span>,<span class="number">0.0f</span>,</span><br><span class="line">        <span class="number">0.5f</span>,<span class="number">0.0f</span>,<span class="number">0.0f</span>,</span><br><span class="line">        <span class="number">0.0f</span>,<span class="number">0.5f</span>,<span class="number">0.0f</span></span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将顶点数据传递到三角形批次类中</span></span><br><span class="line">    triangleBatch.<span class="built_in">Begin</span>(GL_TRIANGLES, <span class="number">3</span>);</span><br><span class="line">    triangleBatch.<span class="built_in">CopyVertexData3f</span>(vVerts);</span><br><span class="line">    triangleBatch.<span class="built_in">End</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里<code>triangleBatch</code>在Begin的时候设置了一个图元装配方式为<code>GL_TRIANGLES</code>，在OpenGL中，相同的顶点可以有不同的装配方式，如下图</p><img src="/images/post/opengl/primitive-assembly.jpg" class="" width="800"><table><thead><tr><th>图元</th><th>描述</th></tr></thead><tbody><tr><td>GL_POINTS</td><td>每个顶点在屏幕上都是单独点</td></tr><tr><td>GL_LINES</td><td>每⼀对顶点定义⼀个线段</td></tr><tr><td>GL_LINE_STRIP</td><td>一个从第⼀个顶点依次经过每⼀个后续顶点而绘制的线条</td></tr><tr><td>GL_LINE_LOOP</td><td>和GL_LINE_STRIP相同，但是最后⼀个顶点和第⼀个顶点连接起来了</td></tr><tr><td>GL_TRIANGLES</td><td>每3个顶点定义⼀个新的三角形</td></tr><tr><td>GL_TRIANGLE_STRIP</td><td>共⽤一个条带(strip)上的顶点的一组三⻆形</td></tr><tr><td>GL_TRIANGLE_FAN</td><td>以⼀个圆点为中⼼呈扇形排列，共⽤相邻顶点的⼀组三⻆形</td></tr></tbody></table><h3 id="renderSence"><a href="#renderSence" class="headerlink" title="renderSence"></a>renderSence</h3><p>准备好数据之后，就可以开始绘制了，核心代码在之前注册的<code>renderSence</code>里面</p><ul><li>清空缓冲区（颜色缓冲区，深度缓冲区，模板缓冲区），避免脏数据</li><li>使用<code>着色器</code>填充颜色</li><li>批次类将<code>顶点数据</code>提交到<code>着色器</code>上绘制</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">RenderScene</span><span class="params">(<span class="type">void</span>)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 1.清除一个或者一组特定的缓存区，如果后面需要用到这些缓冲区，就需要清空，不然会出现之前使用的脏数据（如深度缓冲区，颜色缓冲区，模板缓冲区等）</span></span><br><span class="line">    <span class="built_in">glClear</span>(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 2.设置一个颜色用来填充三角形</span></span><br><span class="line">    GLfloat vRed[] = &#123;<span class="number">1.0</span>, <span class="number">0.0</span>, <span class="number">0.0</span>, <span class="number">1.0f</span>&#125;;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 3. 使用单元着色器，来对图形进行着色，即GLT_SHADER_IDENTITY着色器，这个着色器只是使用指定颜色以默认笛卡尔坐标第在屏幕上渲染几何图形</span></span><br><span class="line">    shaderManager.<span class="built_in">UseStockShader</span>(GLT_SHADER_IDENTITY, vRed);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 4. 提交顶点数据到着色器，进行绘制</span></span><br><span class="line">    triangleBatch.<span class="built_in">Draw</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 在开始的设置openGL 窗口的时候，我们指定要一个双缓冲区的渲染环境。这就意味着将在后台缓冲区进行渲染，渲染结束后交换给前台。</span></span><br><span class="line">    <span class="built_in">glutSwapBuffers</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="changeSize"><a href="#changeSize" class="headerlink" title="changeSize"></a>changeSize</h3><p>当窗口大小改变的时候，我们需要重新调整视口大小</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">changeSize</span><span class="params">(<span class="type">int</span> w,<span class="type">int</span> h)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// x,y 参数代表窗口中视图的左下角坐标，而宽度、高度是像素为表示，通常x,y 都是为0</span></span><br><span class="line">    <span class="built_in">glViewport</span>(<span class="number">0</span>, <span class="number">0</span>, w, h);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="运行"><a href="#运行" class="headerlink" title="运行"></a>运行</h2><img src="/images/post/opengl/triangle-demo.png" class="" width="600" title="三角形"><p>完整demo在<a href="https://github.com/zhengbomo/OpenGLDemo/tree/master/001--%E5%AE%8C%E6%95%B4%E6%B8%B2%E6%9F%93%E4%B8%89%E8%A7%92%E5%BD%A2">这里</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;通过使用OpenGL绘制一个三角形，了解OpenGL的一些常用API和绘制流程，这里使用了&lt;a href=&quot;https://www.opengl.org/resources/libraries/glut/&quot;&gt;GLUT&lt;/a&gt;（OpenGL Utility Toolkit）的API，&lt;code&gt;GLUT&lt;/code&gt;是OpenGL官方提供的OpenGL工具箱，封装了一些有用的工具，用于辅助操作OpenGL&lt;/p&gt;</summary>
    
    
    
    <category term="OpenGL" scheme="http://zhengbomo.github.io/categories/OpenGL/"/>
    
    
    <category term="OpenGL" scheme="http://zhengbomo.github.io/tags/OpenGL/"/>
    
  </entry>
  
  <entry>
    <title>iOS图像渲染原理解析</title>
    <link href="http://zhengbomo.github.io/2020-07-10/ios-render/"/>
    <id>http://zhengbomo.github.io/2020-07-10/ios-render/</id>
    <published>2020-07-10T13:33:03.000Z</published>
    <updated>2020-07-10T13:33:03.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="CPU和GPU"><a href="#CPU和GPU" class="headerlink" title="CPU和GPU"></a>CPU和GPU</h2><img src="/images/post/opengl/cpu-gpu.png" class="" width="800"><p>从图中结构可以看出</p><ul><li><code>CPU</code>的处理单元少，由控制器和缓存单元，擅长处理复杂的逻辑以及数据结构，CPU中的并行其实是通过时间切片完成的。任务之间依赖性高，<code>擅长逻辑控制</code>  </li><li><code>GPU</code>是由许多计算单元组成，每个计算单元可以独立工作，任务之间依赖性低，擅长浮点运算，<code>擅长并发计算</code></li></ul><span id="more"></span><h2 id="计算机渲染原理"><a href="#计算机渲染原理" class="headerlink" title="计算机渲染原理"></a>计算机渲染原理</h2><h3 id="⾼级光栅扫描显示系统结构"><a href="#⾼级光栅扫描显示系统结构" class="headerlink" title="⾼级光栅扫描显示系统结构"></a>⾼级光栅扫描显示系统结构</h3><img src="/images/post/opengl/print-structure.png" class="" width="700"><h3 id="屏幕扫描"><a href="#屏幕扫描" class="headerlink" title="屏幕扫描"></a>屏幕扫描</h3><p><code>视频控制器/显示控制器</code>从<code>帧缓冲区</code>中读取图像信息（位图），经过数模转换（数字信号处-&gt;模拟型号）后通过<code>逐行扫描</code>把图像显示到显示器上的</p><img src="/images/post/opengl/screen-scan.png" class="" width="500"><h4 id="撕裂问题"><a href="#撕裂问题" class="headerlink" title="撕裂问题"></a>撕裂问题</h4><p>显示一个完整画面是需要一定时间的，视频控制器在显示图像的过程中，如果这时候帧缓冲区的数据被刷新了，就会造成<code>撕裂</code>的问题，上面部分显示前面一帧的数据，下面部分为新的数据</p><img src="/images/post/opengl/image-tear.png" class="" width="500"><h4 id="双缓冲区和垂直同步"><a href="#双缓冲区和垂直同步" class="headerlink" title="双缓冲区和垂直同步"></a>双缓冲区和垂直同步</h4><p>为了解决撕裂，苹果引入了<code>垂直同步</code>（VSync） + <code>双缓存区</code>（DoubleBuffering）来解决撕裂的问题（苹果使用的就是这个策略）</p><ol><li>垂直同步Vsync：每隔1&#x2F;60s就会发出一个信号，让GPU开始渲染图像，而这个时间间隔足够视频控制器显示图像了，App启动后，会在Runloop注册对应的CFRunLoopSource，通过mach_port，接受来自系统的Vsync事件（实际上是由硬件发出的，每秒钟发60次），CADisplayLink也是同样的原理</li><li>双缓存区 DoubleBuffering，使用两个帧缓冲区，视频控制器使用的帧缓冲区和GPU使用的分开，避免视频控制器正在使用的缓冲区被修改，避免撕裂问题，在GPU把帧数据写到帧缓冲区后，会和视频控制器使用的帧缓冲区进行交换，然后等待下一帧的渲染</li></ol><img src="/images/post/opengl/double-buffer.png" class="" width="800"><h4 id="掉帧"><a href="#掉帧" class="headerlink" title="掉帧"></a>掉帧</h4><p>上面解决了撕裂的问题，但是还有一个掉帧的问题，如下图</p><img src="/images/post/opengl/jank.png" class="" width="800"><p>当CPU和GPU渲染图像的时间过长，在下一个垂直同步信号来的时候，GPU并没有处理完一帧的数据，帧缓冲区也就没有交换，视频控制器就会显示原来缓冲区的内容</p><h4 id="三缓冲区"><a href="#三缓冲区" class="headerlink" title="三缓冲区"></a>三缓冲区</h4><p>从上图可以看出，CPU和GPU是在垂直同步信号到来的时候才开始渲染的工作，为了<code>减少掉帧</code>的情况，引入了<code>三缓冲区</code></p><p>A：显示到屏幕<br>B：提前渲染号<br>C：正在渲染</p><p>其实相当于预加载，充分利用CPU和GPU的空闲时间，提前渲染好一帧B（同时也会带来画面延迟，当然1帧的延迟是可以接受的），多留出了一帧的时间，即使在渲染C的时候出现了一次掉帧，依然能刘畅渲染，这种情况大大减小了掉帧的可能</p><p>但如果渲染C的时间过长（掉多帧），依然会带来掉帧的问题，三缓冲区本质上并不解决掉帧的问题，只是缓解</p><blockquote><p>为了解决掉帧的问题，我们只能尽可能优化我们的代码，减少CPU和GPU的渲染时间</p></blockquote><h2 id="iOS的渲染框架"><a href="#iOS的渲染框架" class="headerlink" title="iOS的渲染框架"></a>iOS的渲染框架</h2><h3 id="渲染框架"><a href="#渲染框架" class="headerlink" title="渲染框架"></a>渲染框架</h3><img src="/images/post/opengl/ios-render-structure.png" class="" width="800"><p>可以看到在iOS中的<code>CoreGraphics</code>, <code>CoreAnimation</code>, <code>CoreImage</code>都是通过OpenGL&#x2F;Metal进行渲染的，我们的App也可以使用OpenGL&#x2F;Metal来操作GPU进行渲染</p><h3 id="CoreAnimation-渲染流⽔线"><a href="#CoreAnimation-渲染流⽔线" class="headerlink" title="CoreAnimation 渲染流⽔线"></a>CoreAnimation 渲染流⽔线</h3><img src="/images/post/opengl/coreanimation-pipe.png" class="" width="1000"><p><code>CoreAnimation</code>会在<code>Runloop</code>注册一个<code>Observer</code>监听触摸事件，当点击事件到来的时候，Runloop会被唤醒处理相关的业务逻辑（UIView的创建，修改，添加动画等）</p><p>最终会在CALayer通过<code>CATransaction</code>提交到<code>RenderServer</code>中，RenderServer会对图片进行解码，并等待下一个<code>VSync</code>的到来</p><p>VSync信号到来后，<code>RenderService</code>会通过OpenGL&#x2F;Metal做一些绘制操作，然后把处理完的数据（纹理，顶点，着色器等）提交给<code>GPU</code></p><p>GPU通过下面渲染流程程（顶点数据-&gt;顶点着⾊器-&gt;⽚元着⾊器），渲染到<code>帧缓冲区</code>，然后交换<code>帧缓冲区</code>（双缓冲区）</p><p>下一个VSync信号到来的时候，视频控制器读取帧缓冲区的数据显示到屏幕上</p><p>如果此处有动画，CoreAnimation会通过<code>DisplayLink</code>等机制多次触发相关流程</p><img src="/images/post/opengl/renderservice.png" class="" width="800"><h3 id="渲染流程"><a href="#渲染流程" class="headerlink" title="渲染流程"></a>渲染流程</h3><ol><li><p><code>CPU</code>阶段</p><ul><li>布局（Frame）: <code>layoutSubviews</code>, <code>addSubview</code></li><li>显示（Core Graphics）: <code>drawRect</code>, 绘制字符串</li><li>准备（QuartzCore&#x2F;Core Animation）：图片<code>decode</code></li><li>提交：通过<code>IPC</code>提交(打包好的layers以及动画属性)给OpenGL&#x2F;Metal，递归提交subview的layers</li></ul></li><li><p><code>OpenGL ES/Metal</code>阶段，主要是对图层进行取色，采样，生成纹理，绑定数据，生成前后帧缓存，为GPU渲染做准备</p><ul><li>生成(Generate)</li><li>绑定(Bind)</li><li>缓存数据(Buffer Data)</li><li>启用(Enable)</li><li>设置指针(Set Pointers)</li><li>绘图(Draw)</li><li>清除(Delete)</li></ul></li><li><p><code>GPU</code>阶段</p><ul><li>接收提交的纹理（Texture）和顶点描述（三角形）</li><li>应用变换（transform）</li><li>合并渲染（离屏渲染等）</li></ul></li></ol><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://www.jianshu.com/p/39b91ecaaac8">iOS界面渲染流程分析</a></li></ul>]]></content>
    
    
    <summary type="html">&lt;h2 id=&quot;CPU和GPU&quot;&gt;&lt;a href=&quot;#CPU和GPU&quot; class=&quot;headerlink&quot; title=&quot;CPU和GPU&quot;&gt;&lt;/a&gt;CPU和GPU&lt;/h2&gt;&lt;img src=&quot;/images/post/opengl/cpu-gpu.png&quot; class=&quot;&quot; width=&quot;800&quot;&gt;

&lt;p&gt;从图中结构可以看出&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;CPU&lt;/code&gt;的处理单元少，由控制器和缓存单元，擅长处理复杂的逻辑以及数据结构，CPU中的并行其实是通过时间切片完成的。任务之间依赖性高，&lt;code&gt;擅长逻辑控制&lt;/code&gt;  &lt;/li&gt;
&lt;li&gt;&lt;code&gt;GPU&lt;/code&gt;是由许多计算单元组成，每个计算单元可以独立工作，任务之间依赖性低，擅长浮点运算，&lt;code&gt;擅长并发计算&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;</summary>
    
    
    
    <category term="iOS" scheme="http://zhengbomo.github.io/categories/iOS/"/>
    
    
    <category term="iOS" scheme="http://zhengbomo.github.io/tags/iOS/"/>
    
    <category term="OpenGL" scheme="http://zhengbomo.github.io/tags/OpenGL/"/>
    
  </entry>
  
  <entry>
    <title>理解flutter中的Key</title>
    <link href="http://zhengbomo.github.io/2020-07-04/flutter-key/"/>
    <id>http://zhengbomo.github.io/2020-07-04/flutter-key/</id>
    <published>2020-07-04T01:22:15.000Z</published>
    <updated>2023-06-11T03:02:51.868Z</updated>
    
    <content type="html"><![CDATA[<p>我们知道，flutter有三颗树，widget树在每次setState的时候都会重建，而element树不会，而是会通过diff算法，计算出哪些element需要重建，哪些element可以重用，我们通过一个例子来开始</p><span id="more"></span><h2 id="例子"><a href="#例子" class="headerlink" title="例子"></a>例子</h2><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="string">&#x27;dart:math&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">&#x27;package:flutter/material.dart&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">HomePage</span> <span class="keyword">extends</span> <span class="title">StatefulWidget</span> </span>&#123;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  _HomePageState createState() =&gt; _HomePageState();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">final</span> _random = Random();</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">_HomePageState</span> <span class="keyword">extends</span> <span class="title">State</span>&lt;<span class="title">HomePage</span>&gt; </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> _items = [</span><br><span class="line">    ListItem(title: <span class="string">&quot;aaa&quot;</span>),</span><br><span class="line">    ListItem(title: <span class="string">&quot;bbb&quot;</span>),</span><br><span class="line">    ListItem(title: <span class="string">&quot;ccc&quot;</span>),</span><br><span class="line">  ];</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  Widget build(BuildContext context) &#123;</span><br><span class="line">    <span class="keyword">return</span> Scaffold(</span><br><span class="line">      appBar: AppBar(</span><br><span class="line">        title: Text(<span class="string">&quot;key demo&quot;</span>),</span><br><span class="line">      ),</span><br><span class="line">      body: Row(</span><br><span class="line">        mainAxisAlignment: MainAxisAlignment.center,</span><br><span class="line">        children: _items,</span><br><span class="line">      ),</span><br><span class="line">      floatingActionButton: FloatingActionButton(</span><br><span class="line">        child: Icon(Icons.add),</span><br><span class="line">        onPressed: () &#123;</span><br><span class="line">          <span class="comment">// 删除第一个元素</span></span><br><span class="line">          _items.removeAt(<span class="number">0</span>);</span><br><span class="line">          setState(() &#123;&#125;);</span><br><span class="line">        &#125;,</span><br><span class="line">      ),</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/// <span class="language-markdown">定义一个item</span></span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ListItem</span> <span class="keyword">extends</span> <span class="title">StatelessWidget</span> </span>&#123;</span><br><span class="line">  <span class="keyword">final</span> <span class="built_in">String</span> title;</span><br><span class="line">  <span class="comment">// color放在widget</span></span><br><span class="line">  <span class="keyword">final</span> Color color = Color.fromARGB(</span><br><span class="line">      <span class="number">255</span>, _random.nextInt(<span class="number">256</span>), _random.nextInt(<span class="number">256</span>), _random.nextInt(<span class="number">256</span>));</span><br><span class="line"></span><br><span class="line">  ListItem(&#123;<span class="keyword">this</span>.title&#125;);</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  Widget build(BuildContext context) &#123;</span><br><span class="line">    <span class="keyword">return</span> Container(</span><br><span class="line">      alignment: Alignment.center,</span><br><span class="line">      child: Text(</span><br><span class="line">        <span class="keyword">this</span>.title,</span><br><span class="line">        style: TextStyle(color: Colors.white, fontSize: <span class="number">20</span>),</span><br><span class="line">      ),</span><br><span class="line">      color: <span class="keyword">this</span>.color,</span><br><span class="line">      width: <span class="number">100</span>,</span><br><span class="line">      height: <span class="number">100</span>,</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><img src="/images/post/flutter/flutter-key-stateless.gif" class="" width="300"><p>运行正常，接下来我们把<code>ListItem</code>换成stateful</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ListItem</span> <span class="keyword">extends</span> <span class="title">StatefulWidget</span> </span>&#123;</span><br><span class="line">  <span class="keyword">final</span> <span class="built_in">String</span> title;</span><br><span class="line"></span><br><span class="line">  ListItem(&#123;<span class="keyword">this</span>.title&#125;);</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  _ListItemState createState() =&gt; _ListItemState();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">_ListItemState</span> <span class="keyword">extends</span> <span class="title">State</span>&lt;<span class="title">ListItem</span>&gt; </span>&#123;</span><br><span class="line">    <span class="comment">// color放在state</span></span><br><span class="line">  <span class="keyword">final</span> Color color = Color.fromARGB(</span><br><span class="line">      <span class="number">255</span>, _random.nextInt(<span class="number">256</span>), _random.nextInt(<span class="number">256</span>), _random.nextInt(<span class="number">256</span>));</span><br><span class="line"></span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  Widget build(BuildContext context) &#123;</span><br><span class="line">    <span class="keyword">return</span> Container(</span><br><span class="line">      alignment: Alignment.center,</span><br><span class="line">      child: Text(</span><br><span class="line">        widget.title,</span><br><span class="line">        style: TextStyle(color: Colors.white, fontSize: <span class="number">20</span>),</span><br><span class="line">      ),</span><br><span class="line">      color: <span class="keyword">this</span>.color,</span><br><span class="line">      width: <span class="number">100</span>,</span><br><span class="line">      height: <span class="number">100</span>,</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><img src="/images/post/flutter/flutter-key-stateful.gif" class="" width="300"><p>从上图可以看出，颜色和文字混了，这是由于element树判断增量更新重用element导致的</p><img src="/images/post/flutter/flutter-widget-element-tree.png" class="" width="800" title="删除前"><img src="/images/post/flutter/flutter-widget-element-tree2.png" class="" width="800" title="删除后"><p>当widget重建的时候，element通过对比新旧两个widget是否需要更新，从而判断是否重用，默认的逻辑是对比<code>runtimeType</code>和<code>key</code>，我们上面的例子中显然会返回true（我们没有定义key），则表示可以element可以直接使用新的widget</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="built_in">bool</span> canUpdate(Widget oldWidget, Widget newWidget) &#123;</span><br><span class="line">    <span class="keyword">return</span> oldWidget.runtimeType == newWidget.runtimeType</span><br><span class="line">        &amp;&amp; oldWidget.key == newWidget.key;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>由于颜色是state持有的，没有变化，所以蓝色的element，直接使用了灰色的widget，而最后一个绿色的element，没有用到，会被释放</p><p>通过<code>canUpdate</code>方法可以看到，我们可以设置key来标识element是否可以直接更新widget，flutter中的key有两种</p><ul><li>LocalKey</li><li>GlobalKey</li></ul><h2 id="LocalKey"><a href="#LocalKey" class="headerlink" title="LocalKey"></a>LocalKey</h2><p>LocalKey有下面三种，其成员key用于比较，使用起来类似</p><ul><li>ValueKey: 使用一个泛型数据作为key</li><li>ObjectKey: 使用一个对象作为key</li><li>UniqueKey: 自动生成key，并且保证唯一，比较少用</li></ul><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 在StatefulWidget构造方法添加参数key，并传给super</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ListItem</span> <span class="keyword">extends</span> <span class="title">StatefulWidget</span> </span>&#123;</span><br><span class="line">  <span class="keyword">final</span> <span class="built_in">String</span> title;</span><br><span class="line"></span><br><span class="line">  ListItem(&#123;<span class="keyword">this</span>.title, Key key&#125;) : <span class="keyword">super</span>(key: key);</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  _ListItemState createState() =&gt; _ListItemState();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用ListItem的时候，传入key</span></span><br><span class="line"><span class="keyword">var</span> _items = [</span><br><span class="line">    ListItem(title: <span class="string">&quot;aaa&quot;</span>, key: ValueKey&lt;<span class="built_in">String</span>&gt;(<span class="string">&quot;aaa&quot;</span>)),</span><br><span class="line">    ListItem(title: <span class="string">&quot;bbb&quot;</span>, key: ValueKey&lt;<span class="built_in">String</span>&gt;(<span class="string">&quot;bbb&quot;</span>)),</span><br><span class="line">    ListItem(title: <span class="string">&quot;ccc&quot;</span>, key: ValueKey&lt;<span class="built_in">String</span>&gt;(<span class="string">&quot;ccc&quot;</span>)),</span><br><span class="line">];</span><br></pre></td></tr></table></figure><p>这样在判断的时候不同的ListItem会在canUpdate方法就会返回false，就不会重用了</p><blockquote><p>通常我们在自定义<code>StatefulWidget</code>的时候，需要在构造函数添加可选参数key</p></blockquote><h2 id="GlobalKey"><a href="#GlobalKey" class="headerlink" title="GlobalKey"></a>GlobalKey</h2><p>GlobalKey可以获取到context（element），widget，和state，通常用于在父widget操作子widget</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="string">&#x27;package:flutter/material.dart&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">HomePage</span> <span class="keyword">extends</span> <span class="title">StatelessWidget</span> </span>&#123;</span><br><span class="line">  <span class="keyword">final</span> GlobalKey&lt;_TestWidgetState&gt; _globalKey = GlobalKey();</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  Widget build(BuildContext context) &#123;</span><br><span class="line">    <span class="keyword">return</span> Scaffold(</span><br><span class="line">      appBar: AppBar(</span><br><span class="line">        title: Text(<span class="string">&quot;global key&quot;</span>),</span><br><span class="line">      ),</span><br><span class="line">      body: Center(</span><br><span class="line">          child: TestWidget(</span><br><span class="line">        key: _globalKey,</span><br><span class="line">      )),</span><br><span class="line">      floatingActionButton: FloatingActionButton(</span><br><span class="line">        child: Icon(Icons.add),</span><br><span class="line">        onPressed: () &#123;</span><br><span class="line">          <span class="comment">// 直接操作子widget的state</span></span><br><span class="line">          _globalKey.currentState.increseCount();</span><br><span class="line">        &#125;,</span><br><span class="line">      ),</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">TestWidget</span> <span class="keyword">extends</span> <span class="title">StatefulWidget</span> </span>&#123;</span><br><span class="line">  TestWidget(&#123;Key key&#125;) : <span class="keyword">super</span>(key: key);</span><br><span class="line"></span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  _TestWidgetState createState() =&gt; _TestWidgetState();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">_TestWidgetState</span> <span class="keyword">extends</span> <span class="title">State</span>&lt;<span class="title">TestWidget</span>&gt; </span>&#123;</span><br><span class="line">  <span class="built_in">int</span> _count = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">  increseCount() &#123;</span><br><span class="line">    setState(() &#123;</span><br><span class="line">      _count = _count + <span class="number">1</span>;</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  Widget build(BuildContext context) &#123;</span><br><span class="line">    <span class="keyword">return</span> Text(<span class="string">&quot;<span class="subst">$_count</span>&quot;</span>);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;我们知道，flutter有三颗树，widget树在每次setState的时候都会重建，而element树不会，而是会通过diff算法，计算出哪些element需要重建，哪些element可以重用，我们通过一个例子来开始&lt;/p&gt;</summary>
    
    
    
    <category term="flutter" scheme="http://zhengbomo.github.io/categories/flutter/"/>
    
    
    <category term="flutter" scheme="http://zhengbomo.github.io/tags/flutter/"/>
    
    <category term="key" scheme="http://zhengbomo.github.io/tags/key/"/>
    
  </entry>
  
  <entry>
    <title>OpenGL名词解释</title>
    <link href="http://zhengbomo.github.io/2020-07-01/opengl-words/"/>
    <id>http://zhengbomo.github.io/2020-07-01/opengl-words/</id>
    <published>2020-07-01T12:27:18.000Z</published>
    <updated>2023-06-11T03:02:51.868Z</updated>
    
    <content type="html"><![CDATA[<p>为了更好的学习 OpenGL，我们需要对部分专业属于有一些基本认识，本文介绍在计算机图形学常用的一些专业名词</p><span id="more"></span><h2 id="图形API"><a href="#图形API" class="headerlink" title="图形API"></a>图形API</h2><p><code>OpenGL</code>（Open Graphics Library）是⼀个跨编程语⾔、跨平台的编程图形程序接⼝，它将计算机的资源抽象称为⼀个OpenGL的对象，对这些资源的操作抽象为⼀个个的OpenGL指令</p><p><code>OpenGL ES</code>（OpenGL for Embedded Systems）是OpenGL的API子集，针对⼿机、PDA和游戏主机等嵌⼊式设备⽽设计，去除了许多不必要和性能较低的API接⼝</p><p><code>DirectX</code>是Windows平台的多媒体处理工具，包含图形API，功能比较多，按性质分类，可以分为四⼤部分，显示部分、声⾳部分、输⼊部分和⽹络部分</p><p><code>Metal</code>是Apple为游戏开发者推出了新的平台技术，苹果宣称该技术能够为 3D 图像提⾼ 10 倍的渲染性能，是Apple为了解决3D渲染⽽推出的框架</p><h3 id="图形API的作用"><a href="#图形API的作用" class="headerlink" title="图形API的作用"></a>图形API的作用</h3><p>图形API用于实现图形的底层渲染，最终输出到显示屏上，例如，图形从点线面，再到3D图形，最终渲染称屏幕上看到的2D图像这个过程</p><ul><li>⽐如在游戏开发中,对于游戏场景&#x2F;游戏⼈物的渲染</li><li>⽐如在⾳视频开发中,对于视频解码后的数据渲染</li><li>⽐如在地图引擎,对于地图上的数据渲染</li><li>⽐如在动画中,实现动画的绘制</li><li>⽐如在视频处理中,对于视频加上滤镜效果</li></ul><p>OpenGL &#x2F;OpenGL ES&#x2F; Metal 在任何项⽬中解决问题的本质就是利⽤<code>GPU芯⽚</code>来⾼效渲染图形图像，开发者可以通过这些图形API间接利用GPU</p><blockquote><p>OpenGL只是协议层，而具体的实现是由硬件厂商完成的，不同厂商实现上有差异，通常都是高度优化过的</p></blockquote><h2 id="OpenGL上下⽂（Context）"><a href="#OpenGL上下⽂（Context）" class="headerlink" title="OpenGL上下⽂（Context）"></a>OpenGL上下⽂（Context）</h2><p>在使用OpenGL操作之前，需要先创建一个OpenGL上下文，这是一个<code>状态机</code>，保存了各种用于描述OpenGL如何运行的状态，例如拍照的时候开启滤镜等，上下文是OpenGL所有操作的基础</p><ul><li>OpenGL API是面向过程的，本质上是对OpenGL上下文状态机的某个状态或对象进行操作</li><li>应用程序中可以创建多个不同的上下文，他们分别在各自的线程中使用。上下文之间共享纹理，缓冲区等资源，采用这中方案更为高效，因为它避免了反复切换上下文，或者大量修改渲染状态所造成的较大的开销。</li></ul><p>iOS中的<code>CoreGraphics</code>里面的API就与OpenGL的API有些类似，面向过程</p><p>假设当我们想告诉OpenGL去画线段而不是三角形的时候，我们通过改变一些上下文变量来改变OpenGL状态，从而告诉OpenGL如何去绘图。一旦我们改变了OpenGL的状态为绘制线段，下一个绘制命令就会画出线段而不是三角形。</p><ul><li>上下文OpenGL指令执⾏的基础，所有绘制操作都需要通过上下文进行</li><li>OpenGL上下文对象切换操作开销大</li><li>OpenGL的函数虽然是面向过程的，但可以把相关的调用封装为面向过程的图形API。</li></ul><h2 id="OpenGL状态机"><a href="#OpenGL状态机" class="headerlink" title="OpenGL状态机"></a>OpenGL状态机</h2><p>状态机描述了⼀个对象在其⽣命周期内的各种状态变化，和响应事件，体现在代码中就是<code>上下文</code></p><ul><li>有<code>状态</code>，能保存当前的状态</li><li>有<code>输⼊</code>和<code>输出</code>，根据输⼊的内容和⾃⼰的原先状态，修改⾃⼰当前状态，并且可以有对应输出</li><li>能<code>关闭</code>，当进⼊特殊状态（停机状态）的时候，就不再接收输⼊，停⽌⼯作</li></ul><h2 id="渲染（Rendering）"><a href="#渲染（Rendering）" class="headerlink" title="渲染（Rendering）"></a>渲染（Rendering）</h2><p>将图形&#x2F;图像数据转换成3D空间图像操作，从图元到图像的过程</p><h2 id="图元"><a href="#图元" class="headerlink" title="图元"></a>图元</h2><p>图元可以理解为组成图形的基本单元，比如点、线、三角形。 我们可以通过一系列函数或顶点数据帮助我们实现多种多样的图形</p><blockquote><p>在<code>OpenGL ES</code>有三种图元：<code>点</code>，<code>线</code>，<code>三角形</code>，其他所有图形都是由这三种图形组合而成的</p></blockquote><h2 id="顶点数组和顶点缓冲区（VertexArray-amp-VertexBuffer）"><a href="#顶点数组和顶点缓冲区（VertexArray-amp-VertexBuffer）" class="headerlink" title="顶点数组和顶点缓冲区（VertexArray &amp; VertexBuffer）"></a>顶点数组和顶点缓冲区（VertexArray &amp; VertexBuffer）</h2><p><code>顶点数组</code>用于描述图形的轮廓（如三个顶点可以描述一个三角形），图元是由顶点组成的，顶点数组通常存放在显存上（性能更高），而这部分内存称为<code>顶点缓冲区</code></p><h2 id="管线"><a href="#管线" class="headerlink" title="管线"></a>管线</h2><p>在OpenGL下渲染图形,就会有经历⼀个⼀个节点步骤，⽽这样的操作可以理解管线。管线是⼀个抽象的概念，之所以称之为管线是因为显卡在处理数据的时候是按照⼀个固定的顺序来的，⽽且严格按照这个顺序。就像⽔从⼀根管⼦的⼀端流到另⼀端，这个顺序<code>固定的</code>，<code>不能修改</code></p><h2 id="固定管线"><a href="#固定管线" class="headerlink" title="固定管线"></a>固定管线</h2><p>在早期的OpenGL版本中，封装了很多API，用于完成特定的功能，例如光照，坐标变换，裁切等，这些API被称为固定管线，而对于一些特殊需求的场景，固定管线无法实现，这部分可以通过<code>可编程管线</code>来实现</p><blockquote><p>固定管线在实际开发中用的少，大多已经废弃，基本都是通过编程来实现需求</p></blockquote><h2 id="着⾊器程序（Shader）"><a href="#着⾊器程序（Shader）" class="headerlink" title="着⾊器程序（Shader）"></a>着⾊器程序（Shader）</h2><p>将<code>固定渲染管线架构</code>变为<code>可编程渲染管线</code></p><p>OpenGL在实际调⽤绘制函数之前，还需要指定⼀个由shader编译成的着⾊器程序。常⻅的着⾊器主要有</p><ul><li><code>顶点着⾊器</code>（VertexShader）</li><li><code>⽚段着⾊器</code>&#x2F;<code>像素着⾊器</code>（FragmentShader&#x2F;PixelShader）</li><li><code>⼏何着⾊器</code>（GeometryShader）</li><li><code>曲⾯细分着⾊器</code>（TessellationShader）。</li></ul><p><code>OpenGL ES</code>只⽀持了<code>顶点着⾊器</code>和<code>⽚段着⾊器</code>两个着⾊器</p><p>着色器程序处理流程</p><ol><li>OpenGL在处理shader时，和其他编译器⼀样。通过编译、链接等步骤，⽣成了<code>着⾊器程序</code>（glProgram），着⾊器程序同时包含了顶点着⾊器和⽚段着⾊器的运算逻辑</li><li>在OpenGL进⾏绘制的时候，⾸先由顶点着⾊器对传⼊的顶点数据进⾏运算。再通过图元装配，将顶点转换为图元</li><li>然后进⾏<code>光栅化</code>，将图元这种⽮量图形，转换为栅格化数据</li><li>将栅格化数据传⼊<code>⽚段着⾊器</code>中进⾏运算。⽚段着⾊器会对栅格化数据中的每⼀个像素进⾏运算，并决定像素的颜⾊</li></ol><h3 id="顶点着⾊器（VertexShader）"><a href="#顶点着⾊器（VertexShader）" class="headerlink" title="顶点着⾊器（VertexShader）"></a>顶点着⾊器（VertexShader）</h3><ul><li>⼀般⽤来处理图形每个顶点变换(<code>旋转</code>&#x2F;<code>平移</code>&#x2F;<code>投影</code>等)，顶点坐标由⾃身坐标系转换到归⼀化坐标系的运算</li><li>顶点着⾊器是OpenGL中⽤于计算顶点属性的程序。顶点着⾊器是逐顶点运算的程序，这个过程是<code>并⾏的</code>，并且顶点着⾊器运算过程中⽆法访问其他顶点的数据</li></ul><p>过程</p><ol><li>确定位置</li><li>缩放&#x2F;平移&#x2F;旋转位置换算</li><li>3D图形数据-&gt;2D(投影换算)</li></ol><h3 id="⽚段着⾊器-x2F-片元着色器（FragmentShader）"><a href="#⽚段着⾊器-x2F-片元着色器（FragmentShader）" class="headerlink" title="⽚段着⾊器&#x2F;片元着色器（FragmentShader）"></a>⽚段着⾊器&#x2F;片元着色器（FragmentShader）</h3><ul><li>⼀般⽤来处理图形中每个像素点<code>颜⾊计算和填充</code></li><li>⽚段着⾊器是OpenGL中⽤于计算⽚段（像素）颜⾊的程序。⽚段着⾊器是逐像素运算的程序，也就是说每个像素都会执⾏⼀次⽚段着⾊器，也是<code>并⾏的</code></li></ul><h3 id="GLSL（OpenGL-Shading-Language）"><a href="#GLSL（OpenGL-Shading-Language）" class="headerlink" title="GLSL（OpenGL Shading Language）"></a>GLSL（OpenGL Shading Language）</h3><p><code>GLSL</code>是OpenGL着⾊语⾔，是⽤来在OpenGL中着⾊编程的语⾔，运行在GPU上，代替了固定的渲染管线的⼀部分，⽐如：视图转换、投影转换等。GLSL（GL Shading Language）的着⾊器代码分成2个部分：</p><ul><li>Vertex Shader（顶点着⾊器）</li><li>Fragment Shader（⽚断着⾊器&#x2F;片元着色器）</li></ul><h3 id="光栅化（Rasterization）"><a href="#光栅化（Rasterization）" class="headerlink" title="光栅化（Rasterization）"></a>光栅化（Rasterization）</h3><p>栅格化或者像素化，就是把矢量图形转化成像素点的过程，该过程包含了两部分的工作</p><ol><li>决定窗口坐标中的哪些整型栅格区域被基本图元占用</li><li>分配一个颜色值和一个深度值到各个区域。光栅化过程产生的是片元。</li></ol><h3 id="纹理（Texture）"><a href="#纹理（Texture）" class="headerlink" title="纹理（Texture）"></a>纹理（Texture）</h3><p>纹理可以理解为图⽚，在OpenGL习惯叫纹理</p><h3 id="混合（Blending）"><a href="#混合（Blending）" class="headerlink" title="混合（Blending）"></a>混合（Blending）</h3><p>不同图层进行<code>颜色混合</code>，例如<code>组透明度</code>（例如将两个图层（透明度0.2的红色图层和透明度0.5的绿色图层）混合叠加在一起，这个过程就叫做混合），OpenGL提供的默认的混合算法混合算法，如果需要更加复杂的混合算法，⼀般可以通过像素着⾊器进⾏实现，当然性能会⽐原⽣的混合算法差⼀些</p><h3 id="变换矩阵（Transformation）"><a href="#变换矩阵（Transformation）" class="headerlink" title="变换矩阵（Transformation）"></a>变换矩阵（Transformation）</h3><p>用于对图形进行平移，缩放，旋转变换操作时使用</p><h3 id="投影矩阵（Projection）"><a href="#投影矩阵（Projection）" class="headerlink" title="投影矩阵（Projection）"></a>投影矩阵（Projection）</h3><p>⽤于将3D坐标转换为⼆维屏幕坐标时使用</p><h3 id="渲染上屏-x2F-交换缓冲区-SwapBuffer"><a href="#渲染上屏-x2F-交换缓冲区-SwapBuffer" class="headerlink" title="渲染上屏&#x2F;交换缓冲区(SwapBuffer)"></a>渲染上屏&#x2F;交换缓冲区(SwapBuffer)</h3><ol><li>渲染缓冲区⼀般映射的是系统的资源⽐如窗⼝。如果将图像直接渲染到窗⼝对应的渲染缓冲区，则可以将图像显示到屏幕上。</li><li>但是，如果每个窗⼝只有⼀个缓冲区，那么在绘制过程中屏幕进⾏了刷新，窗⼝可能显示出不完整的图像，前后两次绘制重叠</li><li>为了解决这个问题，常规的OpenGL程序⾄少都会有两个缓冲区。显示在屏幕上的称为屏幕缓冲区，没有显示的称为离屏缓冲区。在⼀个缓冲区渲染完成之后，通过将屏幕缓冲区和离屏缓冲区交换，实现图像在屏幕上的显示，就是<code>双缓冲</code></li><li>由于显示器的刷新⼀般是逐⾏进⾏的，因此为了防⽌交换缓冲区的时候屏幕上下区域的图像分属于两个不同的帧，因此交换⼀般会等待显示器刷新完成的信号，在显示器两次刷新的间隔中进⾏交换，这个信号就被称为垂直同步信号（VSync），这个技术被称为<code>垂直同步</code></li><li>使⽤了双缓冲区和垂直同步技术之后，由于总是要等待缓冲区交换之后再进⾏下⼀帧的渲染，使得帧率⽆法完全达到硬件允许的最⾼⽔平。为了解决这个问题，引⼊了<code>三缓冲区技术</code>，在等待垂直同步时，来回交替渲染两个离屏的缓冲区，⽽垂直同步发⽣时，屏幕缓冲区和最近渲染完成的离屏缓冲区交换，实现充分利⽤硬件性能的⽬的</li></ol>]]></content>
    
    
    <summary type="html">&lt;p&gt;为了更好的学习 OpenGL，我们需要对部分专业属于有一些基本认识，本文介绍在计算机图形学常用的一些专业名词&lt;/p&gt;</summary>
    
    
    
    <category term="OpenGL" scheme="http://zhengbomo.github.io/categories/OpenGL/"/>
    
    
    <category term="OpenGL" scheme="http://zhengbomo.github.io/tags/OpenGL/"/>
    
  </entry>
  
</feed>
