<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>dtysky|一个行者的轨迹</title>
<link>http://dtysky.moe</link><description>不灭的理想，永远的真诚，坚定的信念，执着的希望</description><atom:link href="http://dtysky.moe/feeds/Skill.rss.xml" rel="self"></atom:link>
<lastBuildDate>Monday, 23 Feb 2026 01:39:47 -00:00</lastBuildDate>

<item>
<title>【WebGPU实时光追美少女】踩坑与调试心得</title>
<link>http://dtysky.moe/article/Skill-2021_10_05_a</link>
<description>&lt;p&gt;本系列文章设计的所有代码均已开源，Github仓库在这里：&lt;a href=&amp;quot;https://github.com/dtysky/webgpu-renderer&amp;quot;&gt;dtysky/webgpu-renderer&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;在前面的七篇文章中，我从概论开始，论述了简单WebGPU渲染引擎的实现，并实现了一个支持BVH加速结构和BRDF光照模型的实时路径追踪渲染器。但由于WebGPU的API的实验性，目前相关标准仍然可能不断变更，而由于配套于WebGPU的调试工具还不存在，所以在不重编Chromium的情况下只能想一些朴素的方法来调试，这里就记录了一些调试心得。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;教程基础部分到此完结，BSDF和BVH优化部分等有生之年吧。又在焦虑中做到了一次无偿的知识分享，算是以此再次纪念&lt;a href=&amp;quot;https://movie.douban.com/subject/25785114/&amp;quot;&gt;“互联网之子”亚伦·斯沃茨&lt;/a&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;关于API变更&lt;/h2&gt;
&lt;p&gt;目前WebGPU的API标准仍然可能变更，所以发现之前跑得好好的代码忽然就挂了也是正常的，当遇到这种问题后，我们一般需要去以下几个地方寻找解决方案：&lt;/p&gt;
&lt;p&gt;Chromium WebGPU部分的Issue： &lt;a href=&amp;quot;https://bugs.chromium.org/p/dawn/issues/list&amp;quot;&gt;https://bugs.chromium.org/p/dawn/issues/list&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;WebGPU最新标准：&lt;a href=&amp;quot;https://www.w3.org/TR/webgpu&amp;quot;&gt;https://www.w3.org/TR/webgpu&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;着色语言WGSL最新标准： &lt;a href=&amp;quot;https://www.w3.org/TR/WGSL&amp;quot;&gt;https://www.w3.org/TR/WGSL&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;同时有时候报错会不清晰，这时候可以直接看Chromium的WebGPU模块源码来分析：&lt;/p&gt;
&lt;p&gt;Dawn： &lt;a href=&amp;quot;https://dawn.googlesource.com/dawn/+/HEAD/src/dawn_native&amp;quot;&gt;https://dawn.googlesource.com/dawn/+/HEAD/src/dawn_native&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;如何调试CS&lt;/h2&gt;
&lt;p&gt;对于有经验的开发者来说，相对而言渲染的部分比较好调试，实在不行可以使用朴素的&lt;strong&gt;Color Picker大法&lt;/strong&gt;，毕竟有了数据什么都好说，并且对于路径追踪来讲渲染部分并不复杂，不需要怎么调试。&lt;/p&gt;
&lt;p&gt;但&lt;strong&gt;Compute Shader&lt;/strong&gt;部分就不同了，在路径追踪实现中我大量使用了CS，每个部分都并不简单，而且环环相扣，出了问题十分难以排查，下面每一部我都遇到过问题：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;射线生成。&lt;/li&gt;
&lt;li&gt;BVH求交。&lt;/li&gt;
&lt;li&gt;三角形求交。&lt;/li&gt;
&lt;li&gt;重心坐标插值。&lt;/li&gt;
&lt;li&gt;重要性采样。&lt;/li&gt;
&lt;li&gt;BRDF着色计算。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;比如在一开始，我遇到过这种情况：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/webgpu-rt/8/1.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;当然对路径追踪十分熟悉的朋友，应该一眼就能看出这可能是射线生成的问题，但当时刚开始学习并没想到这个，况且就算想到了也需要有方法来确认和调试，为了调试，我使用&lt;code&gt;SSBO&lt;/code&gt;构建了一条用来调试CS的流程。&lt;/p&gt;
&lt;h3&gt;SSBO&lt;/h3&gt;
&lt;p&gt;SSBO即&lt;code&gt;Shader Storage Buffer Object&lt;/code&gt;，在前面的主流程中也使用过，用于存储合并过的场景数据。这种数据可以被CPU和GPU共享，不但可以用于从CPU向GPU传输数据，也可以将数据从GPU到CPU读回来。虽然这两个过程都比较慢，但对于调试而言已经足够。&lt;/p&gt;
&lt;p&gt;在构建流程之前，需要先明确调试的步骤：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;路径追踪过程始于屏幕空间的像素，所以以像素为入口调试。&lt;/li&gt;
&lt;li&gt;构建一个屏幕大小的SSBO来存储CS计算过程中的信息，SSBO中的数据结构可以按情况定制。&lt;/li&gt;
&lt;li&gt;可以用一个简单的策略，读回需要的GPU数据，在CPU端复刻算法进行的调试。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;梳理清楚流程后，我编写了&lt;code&gt;DebugInfo&lt;/code&gt;类以及各个Debug方法，比如&lt;code&gt;debugRay&lt;/code&gt;、&lt;code&gt;debugRayShadow&lt;/code&gt;，它们都在&lt;code&gt;demo/debugCS.ts&lt;/code&gt;中。&lt;/p&gt;
&lt;h3&gt;DebugInfo&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;DebugInfo&lt;/code&gt;提供了给&lt;code&gt;RayTracingManager&lt;/code&gt;中用于路径追踪的计算单元&lt;code&gt;rtUnit&lt;/code&gt;注入调试信息的能力，这个调试信息通过一个&lt;code&gt;SSBO&lt;/code&gt;作为Uniform传入，骑在Shader中的定义为：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;struct&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;DebugRay&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;preOrigin&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;preDir&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;origin&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dir&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;nextOrigin&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;nextDir&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;block&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;struct&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;DebugInfo&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rays&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;array&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;DebugRay&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;`&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;其对应的Interface和具体构造为：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;interface&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;IDebugPixel&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;preOrigin&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;preDir&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;origin&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;dir&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;nextOrigin&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;nextDir&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;normal&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;class&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;DebugInfo&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;protected&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_cpu&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;protected&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_gpu&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;GPUBuffer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;protected&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_view&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;GPUBuffer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;protected&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_size&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;protected&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_rtManager&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;H.RayTracingManager&lt;/span&gt;

  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 构造SSBO并作为Uniform注入给计算单元&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;setup&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rtManager&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;H.RayTracingManager&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;renderEnv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;H&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;size&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_size&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;7&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_cpu&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;size&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;renderEnv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;renderEnv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_gpu&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;H&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;createGPUBuffer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_cpu&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;GPUBufferUsage&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;STORAGE&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;GPUBufferUsage&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;COPY_SRC&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_view&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;H&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;createGPUBufferBySize&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;size&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;renderEnv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;renderEnv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;GPUBufferUsage&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;MAP_READ&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;GPUBufferUsage&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;COPY_DST&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_rtManager&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;rtManager&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_rtManager&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rtUnit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setUniform&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;u_debugInfo&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_cpu&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_gpu&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 拷贝SSBO，具体原因详见下面&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;run&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;scene&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;H.Scene&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;scene&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;copyBuffer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_gpu&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_view&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_cpu&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;byteLength&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 从GPU读回数据，并指定需要解析的区域&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;async&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;showDebugInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;point1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;len&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;step&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Promise&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;rays&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IDebugPixel&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[],&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;mesh&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;H.Mesh&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;await&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_view&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;mapAsync&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;GPUMapMode&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;READ&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;data&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_view&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getMappedRange&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;());&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;rays&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_decodeDebugInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;data&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;point1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;len&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;step&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;c1&amp;quot;&gt;// 将读回的数据进行解析，变成可理解的结构&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;protected&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_decodeDebugInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;view&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;point1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;len&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;step&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IDebugPixel&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[];&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;logs&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[];&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;point1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;point1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;len&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;step&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;step&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;])&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;point1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;  &lt;span class=&amp;quot;nx&amp;quot;&gt;point1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;len&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;step&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;step&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;])&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;H&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;renderEnv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;offset&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_size&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;push&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;
          &lt;span class=&amp;quot;nx&amp;quot;&gt;preOrigin&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;view.slice&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;offset&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;offset&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;
          &lt;span class=&amp;quot;nx&amp;quot;&gt;preDir&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;view.slice&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;offset&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;offset&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;8&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;
          &lt;span class=&amp;quot;nx&amp;quot;&gt;origin&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;view.slice&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;offset&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;8&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;offset&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;12&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;
          &lt;span class=&amp;quot;nx&amp;quot;&gt;dir&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;view.slice&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;offset&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;12&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;offset&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;16&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;
          &lt;span class=&amp;quot;nx&amp;quot;&gt;nextOrigin&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;view.slice&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;offset&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;16&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;offset&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;20&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;
          &lt;span class=&amp;quot;nx&amp;quot;&gt;nextDir&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;view.slice&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;offset&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;20&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;offset&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;24&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;
          &lt;span class=&amp;quot;nx&amp;quot;&gt;normal&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;view.slice&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;offset&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;24&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;offset&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;28&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;IDebugPixel&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
      &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;在这段代码中，需要注意的是我创建了两个&lt;code&gt;GPUBuffer&lt;/code&gt;，一个设置给了uniform，另一个则用于读取，而期间做了一次拷贝&lt;code&gt;scene.copyBuffer&lt;/code&gt;，这个拷贝的实现为：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_command&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;copyBufferToBuffer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;src&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;dst&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;size&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这是由于在WebGPU标准中，对&lt;code&gt;GPUBuffer&lt;/code&gt;的使用有所限制，&lt;code&gt;STORAGE&lt;/code&gt;不能和&lt;code&gt;MAP_READ&lt;/code&gt;共存，也就是说一个Buffer不能又可以被CPU读又可以被GPU读，所以必须要先将其拷贝到另一个&lt;code&gt;GPUBuffer&lt;/code&gt;中，再进行读取。&lt;/p&gt;
&lt;h3&gt;在GPU中填充数据&lt;/h3&gt;
&lt;p&gt;在流程组织完毕后，便可以在GPU中对SSBO进行填充，这个可以根据不同场景有不同的设置，比如这里是：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hited&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hited&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;startRay&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;gBInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;light&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;calcLight&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kc&amp;quot;&gt;false&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kc&amp;quot;&gt;false&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;debugIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;u_debugInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rays&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;debugIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;origin&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;origin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sign&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;u_debugInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rays&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;debugIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dir&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;matType&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;light&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;next&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hitTest&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;light&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;calcLight&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kc&amp;quot;&gt;false&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kc&amp;quot;&gt;false&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;debugIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;u_debugInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rays&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;debugIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;nextOrigin&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;origin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sign&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;u_debugInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rays&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;debugIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;nextDir&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;matType&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;u_debugInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rays&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;debugIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;glass&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;我将初始射线的参数和在CS中求得的下一条射线参数记录了下来，以待调试。&lt;/p&gt;
&lt;h3&gt;在CPU调试指定数据&lt;/h3&gt;
&lt;p&gt;有了GPU的数据，就可以通过上面的&lt;code&gt;showDebugInfo&lt;/code&gt;函数，来通过指定的像素位置和步长，来decode一定窗口大小的数据，只要有一个时机来触发即可：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;H&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;renderEnv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;canvas&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;mouseup&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;async&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;e&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;clientX&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;clientY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;e&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rays&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;mesh&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;await&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_rtDebugInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;showDebugInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;([&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;clientX&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;clientY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;10&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;10&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]);&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;console&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;log&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rays&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;rays&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;forEach&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ray&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;debugRay&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_rtManager&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;bvh&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_rtManager&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;gBufferMesh&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;geometry&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getValues&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;position&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cpu&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这段代码将调试过程和鼠标点击事件关联了起来，注意到最后一句我对每个像素的数据都执行了&lt;code&gt;debugRay&lt;/code&gt;操作，这其实就是在CPU内实现了和GPU一样的算法，比如光源采样、BVH求交和三角形求交等等。由于是JS代码，所以很容易进行调试，并且也可以和GPU中存下的结果直接进行对比，虽然也很麻烦，但至少有一个调试的方法了，比如&lt;code&gt;debugRay&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;function&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;debugRay&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rayInfo&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;origin&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;dir&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;bvh&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;H.BVH&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;positions&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;ray&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Ray&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;origin&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;rayInfo.origin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;dir&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;rayInfo.dir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;invDir&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;H&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;math&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;div&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;invDir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;([&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]),&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;dir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

  &lt;span class=&amp;quot;nx&amp;quot;&gt;console&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;log&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;ray info&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;rayInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;console&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;log&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;ray&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;fragInfo&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;hitTest&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;bvh&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;positions&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;console&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;log&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fragInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这里可以看到此调试方法将上面decode好的信息中的&lt;code&gt;origin&lt;/code&gt;和&lt;code&gt;dir&lt;/code&gt;作为参数进行了CPU端的&lt;code&gt;hitTest&lt;/code&gt;，&lt;code&gt;hitTest&lt;/code&gt;的实现对于此章节无关紧要，不再赘述。&lt;/p&gt;
&lt;h3&gt;合理借助外部工具&lt;/h3&gt;
&lt;p&gt;在实际的调试过程中，往往即便我们有了数据和调试函数，但像是BVH求交、三角形求交这种过程的计算比较复杂，数值上又很难分析出结果，这时候就需要外部工具的帮助了。设想如果有个工具能够将三角形、射线这些都直观得在三维空间内简洁地绘制出来，调试难度会大幅降低。&lt;/p&gt;
&lt;p&gt;而这个工具确实存在，而且是免费在线的——&lt;a href=&amp;quot;https://wolframcloud.com/&amp;quot;&gt;Wolfram Cloud&lt;/a&gt;，可以认为是&lt;strong&gt;Mathematica&lt;/strong&gt;的在线版，功能其实已经够用了。&lt;/p&gt;
&lt;p&gt;对于本项目，用它调试最核心的就是生成各种绘制指令，我在CPU端的调试代码中插入了各种生成绘制指令的代码，比如：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 绘制点&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;plotS&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;push&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;`&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Graphics3D&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Red&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;PointSize&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Point&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rsiPoints&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;join&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;,&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)}}]}]&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;`&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

&lt;span class=&amp;quot;c1&amp;quot;&gt;// 绘制射线&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;plotS&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;push&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;`&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ParametricPlot3D&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;dir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]}&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;t&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;origin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]},&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;dir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]}&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;t&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;origin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]},&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;dir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]}&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;t&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;origin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]}},&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;maxT&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}}]&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;`&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

&lt;span class=&amp;quot;c1&amp;quot;&gt;// 绘制三角形&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;plotS&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;push&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;`&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Graphics3D&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Triangle&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[{{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;p0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;join&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;,&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)}},&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;p1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;join&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;,&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)}},&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;p2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;join&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;,&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)}}}]]&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;`&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

&lt;span class=&amp;quot;c1&amp;quot;&gt;// 绘制长方体&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;plotS&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;push&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;`&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Graphics3D&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Cuboid&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;join&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;,&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)}},&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;min&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;join&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;,&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)}}]]&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;`&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;有了这些指令，最后我们就可以画出类似下面的图形：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/webgpu-rt/8/2.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;h2&gt;结语&lt;/h2&gt;
&lt;p&gt;当然除了调试BVH求交这种问题，像是光照计算等等也都可以通过这种方式输出数据来观察和调试，在开发过程中我也是这么做的。&lt;/p&gt;
&lt;p&gt;一开始计划的时候本篇应该还是有挺多内容，但现在回想起来很多踩的坑已经在前面的文章都论述过了，比如&lt;strong&gt;16字节对齐&lt;/strong&gt;、&lt;strong&gt;GPUBuffer的Usage&lt;/strong&gt;、&lt;strong&gt;射线原点偏移&lt;/strong&gt;等等。&lt;/p&gt;
&lt;p&gt;那么就到此为止吧，祝大家国庆快乐。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Tuesday, 05 Oct 2021 19:30:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2021.10.05 19:30:article/Skill-2021_10_05_a</guid>
<category>WebGPU</category>
<category>路径追踪</category>
<category>光线追踪</category>
<category>图形学</category>
</item>

<item>
<title>【WebGPU实时光追美少女】降噪与色调映射</title>
<link>http://dtysky.moe/article/Skill-2021_10_04_a</link>
<description>&lt;p&gt;本系列文章设计的所有代码均已开源，Github仓库在这里：&lt;a href=&amp;quot;https://github.com/dtysky/webgpu-renderer&amp;quot;&gt;dtysky/webgpu-renderer&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;在上一篇文章&lt;strong&gt;BRDF与蒙特卡洛积分&lt;/strong&gt;的最后，我们最终得到了一个充满了噪点的输出，接下来要考虑的就是如何&lt;strong&gt;降噪（Denoise）&lt;/strong&gt;，这个分为时间和空间两部分。并且由于整个光照计算都是在高动态范围（HDR）计算的，最后还要做一次&lt;strong&gt;色调映射（Tone Mapping）&lt;/strong&gt;来将其转换到有限的低动态范围（LDR），便于显示器显示。&lt;/p&gt;
&lt;h2&gt;降噪&lt;/h2&gt;
&lt;p&gt;实时路径追踪的噪声来自于样本数量不足下的信息量不足，所以有两个方面可以来优化：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;提高样本量。&lt;/li&gt;
&lt;li&gt;尽可能使用有效的样本。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;时间滤波（Temporal Filter）&lt;/strong&gt;就是用来解决这两个问题的。而由于样本的增加总是需要累计时间，所以会有一个启动时间，所以为了尽可能加快速度达到一个相对可看的程度，我们在空间上也会做一些滤波，或者说是图像处理，这就是&lt;strong&gt;空间滤波（Spatial Filter）&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;目前工程上的滤波方案往往是混合滤波，即结合时间空间二者，目前最常见的就是&lt;strong&gt;SVGF&lt;/strong&gt;以及其变种，但由于本项目只涉及到静态场景，并未实现&lt;strong&gt;Motion Vector&lt;/strong&gt;这种动态场景的技术，所以只是借鉴其中的一部分。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/webgpu-rt/7/1.png&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;这里就直接用SVGF的一个流程图来论述整个流程：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;首先求得当前帧的1SPP原始图像。&lt;/li&gt;
&lt;li&gt;和上一次的结果作时间滤波。&lt;/li&gt;
&lt;li&gt;将时间滤波的结果用于空间滤波。&lt;/li&gt;
&lt;li&gt;空间滤波的输出反馈到下一帧，同时作为色调映射的来源，做最终输出。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;时间滤波&lt;/h3&gt;
&lt;p&gt;时间滤波顾名思义，是通过不同帧之间的信息来做滤波。路径追踪的时间滤波比较简单：&lt;/p&gt;
&lt;p&gt;$$C_{out} = \alpha C_{i-1} + (1 - \alpha)C_{i}$$&lt;/p&gt;
&lt;p&gt;这个公式本质上是个简单的混合，将上一帧颜色和这一帧颜色通过权重$\alpha$混合起来。这个权重的值是动态变更的，我这里取：&lt;/p&gt;
&lt;p&gt;$$\alpha = \frac{frameCount - 1}{frameCount}$$&lt;/p&gt;
&lt;p&gt;这么做也是有原理可循的，回顾一下上一篇文章所说蒙特卡洛积分的&lt;strong&gt;采样相加求平均&lt;/strong&gt;，我们需要相对保证每帧的结果被均匀平均：&lt;/p&gt;
&lt;p&gt;$$\frac {C_{n}} n + \frac {n-1}n(\frac {C_{n-1}} {n-1} + \frac {n-2} {n-1}(\frac {C_{n-2}} {n-2} + ...) = \frac 1 n(C_{n} + C_{n - 1} + C_{n - 2} + ...)$$&lt;/p&gt;
&lt;p&gt;所以只要在实现的Shader内接受完结传来的这个权重&lt;code&gt;u_preWeight&lt;/code&gt;，然后在JS里不断更新权重即可：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;stage&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;compute&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;workgroup_size&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;16&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;16&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;main&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;builtin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;workgroup_id&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;workGroupID&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;u32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;builtin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;local_invocation_id&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;localInvocationID&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;u32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;size&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureDimensions&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_current&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;groupOffset&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;workGroupID&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xy&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;16&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseIndex&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;groupOffset&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;localInvocationID&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xy&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pre&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureLoad&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_pre&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;current&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureLoad&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_current&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;mixed&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;mix&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;current&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rgb&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pre&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rgb&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_preWeight&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)),&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;current&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureStore&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_output&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;mixed&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;空间滤波&lt;/h3&gt;
&lt;p&gt;空间滤波本质上就是图像处理，最简单的空间滤波就是高斯模糊。&lt;/p&gt;
&lt;h4&gt;高斯模糊&lt;/h4&gt;
&lt;p&gt;$$exp(-\frac {(x-x_c)^2 + (y-y_c)^2} {2\sigma_d^2})$$&lt;/p&gt;
&lt;p&gt;其以某个像素为中心开一个固定大小“窗口”，根据周边像素到中心像素的半径计算权重，然后将窗口中的所有像素颜色加权平均，最终得到一个模糊的效果：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/webgpu-rt/7/2.png&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;单纯的高斯模糊其实是一个低通滤波器，其本质是在频域将高频噪声过滤，留下低频信号，这种频域的处理就会得到一个如图的看起来很糊的效果，这个糊来源于边界的消失，而边界的消失则是基于以下两个事实：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;高频信息并非都是噪声。&lt;/li&gt;
&lt;li&gt;低频信息也并非不都是噪声。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;所以我们需要又一个更好的策略，来在降噪的同时还能保留有效的高频信息，这就要提到&lt;strong&gt;联合双边滤波（Joint Bilateral Filter）&lt;/strong&gt;。&lt;/p&gt;
&lt;h4&gt;联合双边滤波&lt;/h4&gt;
&lt;p&gt;$$exp({-\frac {(x-x_c)^2 + (y-y_c)^2} {2\sigma_d^2}} - {\frac {||color_{x,y} - color_{x_c,y_c}||^2} {2\sigma_c^2}})$$&lt;/p&gt;
&lt;p&gt;变换后为：&lt;/p&gt;
&lt;p&gt;$$exp({-\frac {(x-x_c)^2 + (y-y_c)^2} {2\sigma_d^2}}) * exp({-\frac {||color_{x,y} - color_{x_c,y_c}||^2} {2\sigma_c^2}})$$&lt;/p&gt;
&lt;p&gt;如公式所示，在方才高斯模糊的基础上，我们加了一个和颜色相关的项，来共同决定窗口中某个像素的权重，同时还可以控制二者的方差来决定像素位置和颜色分别决定权重的比例。那么接下来就可以很自然想到——既然如此，是否可以再加上别的项，通过别的信息来共同决定这个权重呢？当然是可以的，这就又要用到&lt;strong&gt;Gbuffer&lt;/strong&gt;了。&lt;/p&gt;
&lt;p&gt;已知Gbuffer中提供了世界坐标、法线等等信息，我们可以考虑对于当前图像“边界”到底意味着什么？很自然可以想到就是一些合理可控的突变的点，比如在一个立方体的转折处（法线发生了较大变化），再比如一前一后的两个物体（深度发生了较大变化）。利用这些特性，就可以构造一个比较合理的滤波器：&lt;/p&gt;
&lt;p&gt;$$exp({-\frac {(x-x_c)^2 + (y-y_c)^2} {2\sigma_d^2}}) * exp({-\frac {||color_{x,y} - color_{x_c,y_c}||^2} {2\sigma_c^2}}) * exp({-\frac {||normal_{x,y} - normal_{x_c,y_c}||^2} {2\sigma_n^2}}) * exp({-\frac {||z_{x,y} - z_{x_c,y_c}||^2} {2\sigma_n^2}})$$&lt;/p&gt;
&lt;p&gt;然后接下来就是调整系数（方差）的工作了，我在CPU中通过方差事先算好了系数，来供Shader中使用：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;function&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;genFilterParams&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;sigmas&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Float32Array&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;sigmas&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;length&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;sigmas&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;length&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;s&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;sigmas&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.5&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;s&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;s&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;到此就可以实现最终的滤波器了，联合双边滤波的结果如下，可见解决了边界被模糊的问题：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/webgpu-rt/7/3.png&amp;quot; /&gt;&lt;/p&gt;
&lt;h4&gt;Outlier Removal&lt;/h4&gt;
&lt;p&gt;滤波滤波到此结束，但还有另一个问题，如下图：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/webgpu-rt/7/4.png&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;图上有一些亮点，这些亮点的亮度明显高于周边。这是由于采样率不足，导致没有足够的样本做平均来达到真正的效果，换言之如果样本足够这些亮点是可以被平均掉的。但对于实时应用，我们等不起这个过程，所以必须用方法将它们“抹除”掉。这确实会造成&lt;strong&gt;能量的不守恒&lt;/strong&gt;，但却是一个必要的权衡。&lt;/p&gt;
&lt;p&gt;这个过程称为&lt;strong&gt;Outlier Removal&lt;/strong&gt;，采用的是统计的方法：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在滤波操作之前进行。&lt;/li&gt;
&lt;li&gt;计算所有点亮度的均值和标准差。&lt;/li&gt;
&lt;li&gt;根据均值和方差算出上界，如果某像素亮度大于上界，则判定为Outlier。&lt;/li&gt;
&lt;li&gt;针对Outlier，将其&lt;strong&gt;拉到&lt;/strong&gt;上界范围内。&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;代码实现&lt;/h4&gt;
&lt;p&gt;至此，所有的要素都已集齐，就可以进行代码实现了（当然写的有点糙，凑合看吧）：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;calcWeightNumber&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;factor&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;exp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;factor&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;calcWeightVec2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;factor&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;diff&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;exp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;factor&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;diff&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;diff&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;calcWeightVec3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;factor&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;diff&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;exp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;factor&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;diff&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;diff&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;calcLum&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.2125&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.7154&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.0721&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;blur&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;center&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;size&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;radius&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;WINDOW_SIZE&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sigmaD&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_filterFactors&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sigmaC&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_filterFactors&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sigmaZ&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_filterFactors&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sigmaN&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_filterFactors&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;w&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;centerColor&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureLoad&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_preFilter&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;center&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;alpha&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;centerColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;centerPosition&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureLoad&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_gbPositionMetalOrSpec&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;center&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xyz&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;centerNormal&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureLoad&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_gbNormalGlass&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;center&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xyz&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;colors&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;array&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;array&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;cp&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;WINDOW_SIZE&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;cp&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;WINDOW_SIZE&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lums&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;array&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;array&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;cp&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;WINDOW_SIZE&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;cp&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;WINDOW_SIZE&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;minUV&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;center&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;radius&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;radius&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;maxUV&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;min&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;center&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;radius&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;radius&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;size&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;localUV&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sumLum&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;count&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 计算每个像素的颜色亮度存起来&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;minUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;maxUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;localUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;c&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;minUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;c&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;maxUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;c&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;c&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;iuv&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;c&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureLoad&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_preFilter&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;iuv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rgb&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lum&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;calcLum&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;colors&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;localUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;][&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;localUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lums&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;localUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;][&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;localUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lum&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sumLum&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sumLum&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lum&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;count&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;count&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;localUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;localUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;localUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;localUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 计算亮度均值&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;meanLum&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sumLum&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;count&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 计算亮度方差&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;std&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;localUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;c&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;c&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;localUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;c&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;c&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lum&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lums&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;][&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;c&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;std&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;std&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lum&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;meanLum&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lum&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;meanLum&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;std&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sqrt&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;std&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;count&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 确定亮度最大边界&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;largestLum&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;meanLum&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;std&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;2.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lum&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;calcLum&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;centerColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rgb&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lum&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;largestLum&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;centerColor&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;centerColor&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;largestLum&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lum&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;localUV&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;weightsSum&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;minUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;maxUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;localUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;c&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;minUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;c&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;maxUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;c&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;c&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;colors&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;localUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;][&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;localUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lum&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lums&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;localUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;][&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;localUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lum&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;largestLum&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;        &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 当前像素亮度大于最大边界，将其拉回来&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;        &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;largestLum&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lum&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;iuv&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;c&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureLoad&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_gbPositionMetalOrSpec&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;iuv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureLoad&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_gbNormalGlass&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;iuv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 联合双边滤波&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;weight&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;calcWeightVec2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sigmaD&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;iuv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;center&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;        &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;calcWeightVec3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sigmaC&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rgb&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;centerColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rgb&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;        &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;calcWeightVec3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sigmaN&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xyz&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;centerNormal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xyz&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;        &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;calcWeightNumber&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sigmaZ&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;centerPosition&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;weightsSum&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;weightsSum&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;weight&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;weight&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;localUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;localUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;localUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;localUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;weightsSum&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;alpha&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;色调映射&lt;/h2&gt;
&lt;p&gt;在完成了滤波后，如果不加任何处理，我们会得到这样的一个结果：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/webgpu-rt/7/5.png&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;可见其有明显的过曝，所以就需要色调映射。评估一个色调映射质量最重要的维度是&lt;strong&gt;对比度的保留&lt;/strong&gt;，历史上出现过很多种策略，像是&lt;strong&gt;Reinhard曲线&lt;/strong&gt;、&lt;strong&gt;指数曲线&lt;/strong&gt;等，但都不足够好，会有灰蒙蒙的感觉。直到后期，人们最终找到了高次曲线拟合的方法，最终实现了目前最通用的拟合策略，其计算高效，实现简单：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;A&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;2.51&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;B&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.03&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;C&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;2.43&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;D&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.59&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;E&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.14&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;acesToneMapping&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;A&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;B&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;C&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;D&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;E&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;色调映射后的最终的结果如下：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/webgpu-rt/1/0.png&amp;quot; /&gt;&lt;/p&gt;
&lt;h2&gt;结语&lt;/h2&gt;
&lt;p&gt;本篇文章所言都是针对静态场景的，在这种实现下，物体和相机都不能移动，这在实际的应用中显然是不够的。一般在真正可用的实现中，我们必须引入&lt;strong&gt;Motion Vector&lt;/strong&gt;，来计算当前一个像素上一帧所对应的像素，然后分别存储权重，最终达到动态场景的滤波。但这也会引入更多其他的问题，由于篇幅和时间确实没空完成了。&lt;/p&gt;
&lt;p&gt;当然如果只是玩票，理论上加上&lt;strong&gt;Motion Vector&lt;/strong&gt;，对&lt;code&gt;SVGF&lt;/code&gt;之类的进行实现也并非难事，这篇文章的很多知识是可以用到的，有兴趣的读者可以自行实现，或者等有生之年我进行优化（逃&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Monday, 04 Oct 2021 18:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2021.10.04 18:00:article/Skill-2021_10_04_a</guid>
<category>WebGPU</category>
<category>路径追踪</category>
<category>光线追踪</category>
<category>图形学</category>
<category>降噪</category>
<category>色调映射</category>
</item>

<item>
<title>【WebGPU实时光追美少女】BRDF与蒙特卡洛积分</title>
<link>http://dtysky.moe/article/Skill-2021_10_01_a</link>
<description>&lt;p&gt;本系列文章设计的所有代码均已开源，Github仓库在这里：&lt;a href=&amp;quot;https://github.com/dtysky/webgpu-renderer&amp;quot;&gt;dtysky/webgpu-renderer&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;在上一篇文章&lt;strong&gt;BVH与射线场景求交插值&lt;/strong&gt;中，我论述了如何对场景进行划分、如何求得射线和场景中三角形的交点、如何判断射线和某点之间是否有遮挡、射线是否与光源相交，以及如何得到插值后的顶点和材质属性。有了这些数据，便可以进行最终的光照计算，这里会涉及到BRDF模型、直接光照、间接光照、蒙特卡洛积分、重要性采样、随机数生成等等的概念和实现。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;长假第一天从学习开始，大家卷起来！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;BRDF&lt;/h2&gt;
&lt;p&gt;BRDF，即双向反射分布函数，是一种针对物体表面的光照反射模型，其综合了漫反射和镜面反射两部分，给出了一种比较物理真实的算法。&lt;/p&gt;
&lt;h3&gt;从渲染方程说起&lt;/h3&gt;
&lt;p&gt;在一般的渲染中，光照本质上是一个针对“入射光线”（一般由方向、亮度、颜色等描述）击中“表面”（一般由法线、各种材质参数描述）后，求得表面最终颜色的计算。为了统一描述这个过程，在1986年，渲染方程被提出：&lt;/p&gt;
&lt;p&gt;$$L_0(p,w_0) = L_e(p,w_0) + \int_{\xi^{2}}f_{r}(p,w_{i},w_{o})L_{i}cos{\theta}dw_{i}$$&lt;/p&gt;
&lt;p&gt;渲染方程描述了一个基于辐射度量学和能量守恒定律的光照模型。其中&lt;code&gt;p&lt;/code&gt;是计算光照的点，&lt;code&gt;wo&lt;/code&gt;是出射方向，&lt;code&gt;Le&lt;/code&gt;部分可认为是自发光，后面部分是一个半球积分，其描述了此点入射半球内的光照累加和，其中&lt;code&gt;wi&lt;/code&gt;是入射方向，&lt;code&gt;fr&lt;/code&gt;是散射函数，&lt;code&gt;Li&lt;/code&gt;是入射辐亮度（单位立体角单位面积的功率），&lt;code&gt;cos&lt;/code&gt;是法线和入射光线的夹角。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;至于为何要用辐亮度，可以参考闫老师的Games101。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;可见，除了自发光这个比较简单的参数，其他计算都和“入射方向”、“光照点的材质属性”和“出射方向”有关。而其中最重要的就是&lt;code&gt;fr&lt;/code&gt;，即散射函数。那么散射函数到底是什么呢？它描述的实际上是出射辐亮度和入射辐亮度微分的比例，也可以认为是一条光线从特定方向入射、出射时发生的能量吸收后剩余的能量。&lt;/p&gt;
&lt;p&gt;不同的光照模型实际上就是在定义&lt;code&gt;fr&lt;/code&gt;这个散射函数，尤其是对于&lt;code&gt;PBR&lt;/code&gt;（基于物理的渲染），就是在寻找模型来尽量逼近物理真实。&lt;/p&gt;
&lt;h3&gt;BRDF模型&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;BRDF&lt;/code&gt;就是一种散射函数，其不考虑透射现象，只考虑非透明物体的表面反射和回弹，所以是双向&lt;strong&gt;反射&lt;/strong&gt;分布函数。我们这里使用的BRDF模型定义为：&lt;/p&gt;
&lt;p&gt;$$f_{r}(p,w_{i},w_{o}) = k_{d}\frac{c}{\pi} + k_{s}\frac{DFG}{4cos\theta_{i}cos\theta_{o}}$$&lt;/p&gt;
&lt;p&gt;可见BRDF模型分为两部分，第一部分为&lt;strong&gt;漫反射&lt;/strong&gt;，第二部分为&lt;strong&gt;高光反射&lt;/strong&gt;，这两部分各自有一个系数，这个系数由菲涅尔系数决定。这两部分基于&lt;strong&gt;微表面模型&lt;/strong&gt;，它将每一个着色点理解为很多朝向不同的微小表面的集合：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/webgpu-rt/6/1.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;在这个模型下，每条光线入射某个着色点后，都将以一定的概率反射到不同的方向，并有不同程度的能量损耗。&lt;/p&gt;
&lt;h3&gt;菲涅尔方程&lt;/h3&gt;
&lt;p&gt;菲涅尔方程描述了这样的而一种现象——当光从一个介质入射到另一个不同折射率的介质的表面时，一部分会被反射、另一部分会被折射:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/webgpu-rt/6/3.png&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;被菲涅尔反射的这部分就是&lt;strong&gt;高光反射&lt;/strong&gt;，而另外么有被折射也没有被吸收的部分就是&lt;strong&gt;漫反射&lt;/strong&gt;。菲涅尔系数的计算一般和材质属性相关，这个后面会说。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注意对于金属是没有漫反射的，因为这部分能量会被完全吸收掉，这在后面实际的计算中也会体现。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;漫反射&lt;/h3&gt;
&lt;p&gt;漫反射部分从表现上来看，就是一条光线入射到着色点后，没有被镜面反射的部分在物体内部或微表面多次弹射，没有被吸收的部分会随机得反射到整个法线半球中，其出射只和入射方向与法线相关，和观察方向无关：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/webgpu-rt/6/2.png&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;在我们使用的这个模型中，对其抽象比较简单，认为这个随机是&lt;strong&gt;均匀&lt;/strong&gt;的，所以可以看到是一个定值&lt;code&gt;c/pi&lt;/code&gt;，&lt;code&gt;c&lt;/code&gt;是反照率，主要由&lt;code&gt;baseColor&lt;/code&gt;和不同工作流的系数控制，除以&lt;code&gt;pi&lt;/code&gt;是因为&lt;code&gt;fr&lt;/code&gt;是将辐照度转换到辐亮度的比例，需要换算。&lt;/p&gt;
&lt;h3&gt;高光反射&lt;/h3&gt;
&lt;p&gt;和漫反射不同，BRDF的高光反射部分相对复杂。高光部分本身其实挺简单的，就是根据入射光线、法线、视线计算一个反射比例，从而确定出射光线的方向和强度。但由于微表面模型的存在，着色点并非是一个简单的镜面，我们需要能够模拟着色点表面粗糙的状况，这也就是这部分在BRDF的公式中比较复杂的原因。&lt;/p&gt;
&lt;p&gt;如公式所示，高光反射主要分为三个部分：菲涅尔项&lt;code&gt;F&lt;/code&gt;、法线分布&lt;code&gt;D&lt;/code&gt;和几何遮蔽&lt;code&gt;G&lt;/code&gt;。其中菲涅尔项在上面已经论述过；法线分布本质上描述了模型中各个微表面的分布状况，有不同实现；几何遮蔽则是为了描述&lt;strong&gt;微表面的反射光线可能被其他微表面遮挡&lt;/strong&gt;的现象，也有不同的实现。但由于这里只是先说原理，详细的实现会放在后面讲：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/webgpu-rt/6/4.png&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;现在我们有了一个理论上的光照模型，可以在光线的入射和出射方向都确定的情况下计算出入射和出射之间的比例，但对于路径追踪而言，还有以下问题要解决：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;对于这样一个很难给出解析解的积分，如何求解。&lt;/li&gt;
&lt;li&gt;对于实时渲染，几乎不可能一帧完成积分，如何处理。&lt;/li&gt;
&lt;li&gt;如何通过已知的出射光线和材质属性，求取入射光线。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这些问题，都可以通过蒙特积分解决。&lt;/p&gt;
&lt;h2&gt;蒙特卡洛积分&lt;/h2&gt;
&lt;p&gt;蒙特卡洛积分是一种统计方法，用于求解一个难以求出解析解的积分的数值解。其基本思路是&lt;strong&gt;采样&lt;/strong&gt;和&lt;strong&gt;平均&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/webgpu-rt/6/5.png&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;如图，&lt;code&gt;f(x)&lt;/code&gt;为要求解的积分，我们可以在其定义域上采样，如此便可以产生许多矩形样条，用这些样条去采样然后相加求平均，便可以证明其数学期望与希望得到的积分一致，这也证明通过蒙特卡洛积分得到的结果是无偏的。当然，这个无偏不代表有效性，所以我们必须要保证大量的样本才能使得结果最终收敛于真值。&lt;/p&gt;
&lt;h3&gt;采样与概率密度&lt;/h3&gt;
&lt;p&gt;在实际的实现中，我们往往希望尽量减少采样的次数，以最快的速度达到收敛。好在蒙特卡洛积分允许我们进行非均匀采样，即提供一个分布来取代均匀采样，如此一来只要保证无偏，就可以就可能提高有效性，来达到最快的收敛速度：&lt;/p&gt;
&lt;p&gt;$$F_{n} = \frac{1}{N}\sum_{i=1}^{N}\frac{f(X_{i})}{p(X_{i})}$$&lt;/p&gt;
&lt;p&gt;如公式，其中&lt;code&gt;p(x)&lt;/code&gt;是需要提供分布的概率密度函数（PDF）。所以对于我们而言最重要的如何选择这个用于采样的概率分布，这就要提到重要性采样：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/webgpu-rt/6/6.png&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;如图，重要性采样指的是对于被积函数，我们选取的采样分布能够满足随着被积函数而变化——被积函数值越大的地方，采样点选取概率越高。同时有了PDF之后，我们还需要一个方法来采样，然而不幸的是，目前计算机只能实现对均匀分布的采样，所以就需要有一个策略来通过均匀分布的输入来通过指定的PDF，来求取最终的采样值。&lt;/p&gt;
&lt;p&gt;对于路径追踪的应用，已经有比较成熟的重要性采样的分布了，同时通过均匀分布求取采样点的方法也很成熟，下个章节就会说到。&lt;/p&gt;
&lt;h3&gt;随机数&lt;/h3&gt;
&lt;p&gt;在实际讨论重要性采样之前，我想先说说随机数，或者说是如何生成均匀分布的随机数，毕竟这是接下来所有采样的数据源头。对于路径追踪的应用而言，我们希望这个随机数&lt;strong&gt;尽可能均匀和无偏&lt;/strong&gt;，所以这里选择的是一个四维&lt;code&gt;Blue Noise&lt;/code&gt;（天蓝噪声）：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/webgpu-rt/6/7.png&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;如图，蓝噪声分布均匀且频率较高。理论上来讲，要对蓝噪声进行采样，最好使用分层等策略，但这里我图简单，就直接每帧用&lt;code&gt;Math.random()&lt;/code&gt;传进来四个随机数，然后加一下算了...&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fn&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;getRandom&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;uv&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;noise&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;textureSampleLevel&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;u_noise&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;u_sampler&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.);&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;fract&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;u_randoms&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;noise&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;重要性采样&lt;/h2&gt;
&lt;p&gt;上一章节提到了重要性采样可以提高蒙特卡洛几分的采样有效性，来加快收敛速度，而重要性采样的核心有二：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;找到合适的分布，求取PDF。&lt;/li&gt;
&lt;li&gt;寻找通过均匀分布转换为该分布的方法。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;在路径追踪中，我们一般需要对以下几种状况分别进行采样。接下来就从以上两点，论述这几种状况。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;采样部分的代码都在&lt;code&gt;buildin/shaders/sample.chuck.wgsl&lt;/code&gt;内。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;漫反射半球采样&lt;/h3&gt;
&lt;p&gt;漫反射需要的是在半球空间内均匀采样，这里采用的是&lt;a href=&amp;quot;https://www.pbr-book.org/3ed-2018/Monte_Carlo_Integration/2D_Sampling_with_Multidimensional_Transformations#Cosine-WeightedHemisphereSampling&amp;quot;&gt;Cosine-Weighted Hemisphere Sampling&lt;/a&gt;，即先进行均匀的圆盘采样，然后转换到半球空间：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 圆盘采样&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sampleCircle&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pi&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;2.0&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pi&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;greater&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;bool&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;abs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;abs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;theta&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;greater&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;theta&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.25&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;PI&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;theta&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;PI&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.5&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.25&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;cos&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;theta&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;theta&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;c1&amp;quot;&gt;// 半球采样&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;cosineSampleHemisphere&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;h&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sampleCircle&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sqrt&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;h&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;h&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;h&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;h&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;h&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;c1&amp;quot;&gt;// 最终生成漫反射部分的入射光向量&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;calcDiffuseLightDir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;basis&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;mat3x3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sign&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;random&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;basis&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sign&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;cosineSampleHemisphere&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;random&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;输入的&lt;code&gt;random&lt;/code&gt;是一个均匀分布的随机向量，由前面章节论述的随机数提供，&lt;code&gt;sign&lt;/code&gt;是后续要提到的一个法线的符号，而&lt;code&gt;basis&lt;/code&gt;，顾名思义是通过法线生成的一个变换矩阵，用于&lt;code&gt;cosineSampleHemisphere&lt;/code&gt;生成的本地单位空间的向量，转换到世界空间的法线半球内，这里使用的是&lt;a href=&amp;quot;https://graphics.pixar.com/library/OrthonormalB/paper.pdf&amp;quot;&gt;pixar采用的一种算法&lt;/a&gt;：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;orthonormalBasis&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;mat3x3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;zsign&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;zsign&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;zsign&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;s&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;zsign&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;zsign&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;zsign&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;zsign&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;mat3x3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;s&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这种分布的PDF为：&lt;/p&gt;
&lt;p&gt;$$\frac{cos\theta}{\pi}$$&lt;/p&gt;
&lt;p&gt;其中角度为法线和入射光线的夹角。&lt;/p&gt;
&lt;h3&gt;高光GGX采样&lt;/h3&gt;
&lt;p&gt;高光反射比较复杂，业界提出过的模型也比较多，这里最终采用的是目前工业界比较通用的GGX分布：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;calcSpecularLightDir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;basis&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;mat3x3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;HitPoint&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;random&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;phi&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;PI&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;2.&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;random&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;alpha&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pbrData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;alphaRoughness&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;cosTheta&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sqrt&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;((&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;random&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;alpha&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;alpha&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;random&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sinTheta&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sqrt&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;cosTheta&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;cosTheta&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;halfVector&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;basis&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sign&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sinTheta&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;cos&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;phi&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sinTheta&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;phi&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;cosTheta&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;reflect&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;halfVector&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;由于是高光反射，所以分布和法线相关很合理。而这种分布的PDF为：&lt;/p&gt;
&lt;p&gt;$$\frac{D \vec N \cdot \vec H}{4\vec L \cdot \vec H}$$&lt;/p&gt;
&lt;p&gt;其中&lt;code&gt;D&lt;/code&gt;是法线分布函数，&lt;code&gt;N&lt;/code&gt;是法线，&lt;code&gt;L&lt;/code&gt;是入射光线，&lt;code&gt;H&lt;/code&gt;是半程向量。&lt;/p&gt;
&lt;h3&gt;直接光和间接光&lt;/h3&gt;
&lt;p&gt;在工程实现中，我们并非在路径追踪的每次迭代中直接求出入射光线，然后反向追踪，看其和物体还是光源相交。虽然在样本足够多的情况下得到的结果仍然是无偏的，但出于尽可能加快收敛速度的考量，我们需要思考如下事实：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;对于某一着色点，光源往往是光的主要来源，提供了最多的能量，而从其他物体反射而来的能量则相对较少。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这里有两种解法，第一种是找到一个合适的分布，来使得采样采到光源的概率增加，但显然由于场景的动态性，这种分布十分难以找到。所以我们往往使用第二种方法——对渲染方程中的&lt;code&gt;Li&lt;/code&gt;进行拆分，对光源单独采样：&lt;/p&gt;
&lt;p&gt;$$\int_{\xi^{2}}f_{r}(p,w_{i},w_{o})L_{e}cos{\theta}dw_{i} + \int_{\xi^{2}}f_{r}(p,w_{i},w_{o})L_{s}cos{\theta}dw_{i}$$&lt;/p&gt;
&lt;p&gt;如公式，我们将渲染方程的积分部分拆成了两个部分，第一个部分为&lt;strong&gt;直接光照&lt;/strong&gt;，表示对光源的直接采样；第二个部分为&lt;strong&gt;间接光照&lt;/strong&gt;，表示对其他部分的采样。间接光照的采样其实就是上面说的两种，而直接光照的采样，就是对面光源的采样。&lt;/p&gt;
&lt;h3&gt;面光源采样&lt;/h3&gt;
&lt;p&gt;本项目中我一共使用了&lt;code&gt;disc&lt;/code&gt;和&lt;code&gt;rect&lt;/code&gt;两种面光源，它们的采样和PDF本身都相对简单：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;samplePoint2D&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;c1&amp;quot;&gt;// 求出光源平面世界空间的法线&lt;/span&gt;
&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normalize&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;((&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;areaLight&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;worldTransform&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)).&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xyz&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;area&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;areaLight&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;areaMode&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;==&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;LIGHT_AREA_DISC&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// `disc`光源，进行圆盘采样并求得面积&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;samplePoint2D&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sampleCircle&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;random&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;areaLight&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;areaSize&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;area&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;2.&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;PI&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;areaLight&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;areaSize&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// `rect`光源，可以非常简单得通过均匀分布变换采样而来，并求得面积&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;samplePoint2D&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;random&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;areaLight&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;areaSize&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;area&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;samplePoint2D&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;samplePoint2D&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;samplePoint2D&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;samplePoint2D&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;2.&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;areaLight&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;areaSize&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;c1&amp;quot;&gt;// 将采样点变换到世界空间，并求出光线向量&lt;/span&gt;
&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;samplePoint&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;areaLight&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;worldTransform&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;samplePoint2D&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;samplePoint2D&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sampleDir&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;samplePoint&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xyz&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;c1&amp;quot;&gt;// 算出光源平面和光线方向的夹角余弦&lt;/span&gt;
&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;cosine&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sampleDir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;cosine&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 证明光线是从光源背面而来，没有光照&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;c1&amp;quot;&gt;// 通过投影面积和长度，求得最后的PDF&lt;/span&gt;
&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;maxT&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;length&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sampleDir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pdf&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;maxT&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;maxT&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;area&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;cosine&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;directLight&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;areaLight&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rgb&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pdf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;可见在整个运算过程中，采样是比较简单的，最后PDF的求取中我们依靠了距离和投影面积，因为计算中有一个从本地空间投影到法线半球空间的过程。&lt;/p&gt;
&lt;h3&gt;环境光采样&lt;/h3&gt;
&lt;p&gt;理论上我们也需要支持环境光，环境光一半来自于一个环境贴图，或者说一般是是天空盒。对于环境贴图的采样，同样存在&lt;strong&gt;尽可能采样光源部分&lt;/strong&gt;的问题，但这里我没做，有兴趣的同学可以自己查阅资料。对于环境贴图，我的做法就是很无脑得直接采样纹理颜色。&lt;/p&gt;
&lt;h2&gt;工程实现&lt;/h2&gt;
&lt;p&gt;原理到这就论述得差不多了，接下来是实际的工程实现。首先我们要给整个着色过程定义个框架。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;光照部分的代码都在&lt;code&gt;buildin/shaders/lighting.chuck.wgsl&lt;/code&gt;内。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;框架&lt;/h3&gt;
&lt;p&gt;着色的过程从前面文章提到的GBuffer开始，GBuffer中存储了可以认为是第一次射线检测到的着色点的世界坐标，通过它可以很简单得重建出第一条射线：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;gBInfo&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;HitPoint&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;getGBInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;gBInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;isLight&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureStore&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_output&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;gBInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;gBInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 处理没有被光栅化的像素，即等价为第一条射线没有相交的三角形&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 直接采样环境贴图返回&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;global&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_skyVP&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;2.&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;2.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;cubeUV&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normalize&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xyz&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;w&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;bgColor&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureSampleLevel&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_envTexture&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_sampler&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;cubeUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// rgbd&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureStore&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_output&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;bgColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rgb&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;bgColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;global&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_envColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rgb&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;worldRay&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Ray&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;genWorldRayByGBuffer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;gBInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这是第一条出射光线，通过这个光线便可以进入实际的着色流程，然后不断反射、直接光照和间接光照，最后终止。这个过程中有两点需要注意：&lt;/p&gt;
&lt;h4&gt;终止条件&lt;/h4&gt;
&lt;p&gt;路颈追踪是理论上可以是一个无穷的过程，&lt;strong&gt;直到射线和场景不相交为止&lt;/strong&gt;，但我们显然不能让其一直进行。在实际的测试中，一般每条光线进行七八次&lt;code&gt;bounce&lt;/code&gt;（弹射）后就可以认为已经到极限了，每次&lt;code&gt;bounce&lt;/code&gt;都是一次间接光照。对于实时而言，在不考虑透射的情况下，我们往往只会进行一次&lt;code&gt;bounce&lt;/code&gt;，这意味着在最终效果中我们只会体现出一次的间接光照，从能量守恒的角度这自然是有瑕疵的，但效果也比传统得好得多。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;由此来看，场景越亮、层次越丰富就代表越真实、效果越好。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;当然如果不考虑这么苛刻的实时性，一般还有像是&lt;strong&gt;俄罗斯轮盘赌&lt;/strong&gt;这样的策略来达成终止条件，也就是通过随机性来判定是否终止。&lt;/p&gt;
&lt;h4&gt;射线原点&lt;/h4&gt;
&lt;p&gt;在每次&lt;code&gt;bounce&lt;/code&gt;过程中，我们都会生成一个由特定原点发出、到特定方向的射线。但由于浮点数运算精度等问题，如果只是刚好以着色点重心插值后的世界坐标为起点，可能会产生锯齿、乱纹等现象，这一般是由于&lt;strong&gt;不该自相交的时候自相交&lt;/strong&gt;（比如普通反射）或&lt;strong&gt;该自相交的时候不自相交&lt;/strong&gt;（比如阴影射线）。所以需要人为给射线原点加上一个偏移：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;light&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;next&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dir&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;RAY_DIR_OFFSET&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;RAY_NORMAL_OFFSET&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;可见其实就是在法线方向、以及射线方向加了个微小的偏移。&lt;/p&gt;
&lt;h4&gt;光照累计&lt;/h4&gt;
&lt;p&gt;路径追踪是迭代累加的，在每次&lt;code&gt;bounce&lt;/code&gt;的过程中，我们总是计算当前着色点的直接光照，然后计算间接光照的系数&lt;code&gt;fr&lt;/code&gt;，而间接光照的能量部分则来自于下一次&lt;code&gt;bounce&lt;/code&gt;，这就可以很清晰得得出一个计算步骤：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;traceLight&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;startRay&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;gBInfo&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;HitPoint&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseUV&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;debugIndex&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;light&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Light&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;calcLight&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;startRay&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;gBInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kc&amp;quot;&gt;false&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kc&amp;quot;&gt;false&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;debugIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lightColor&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;light&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;throughEng&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;light&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;throughEng&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;HitPoint&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Ray&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;light&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;next&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lightHited&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;bounce&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;loop&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;preIsGlass&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;isGlass&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lightHited&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hitTestLights&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hitTest&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;isHitLight&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;bool&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lightHited&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hited&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;isOut&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;bool&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;||&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;isHitLight&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;isHitLight&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;light&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lightHited&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rgb&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;light&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;calcLight&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;bounce&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;isLast&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;isOut&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;light&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;next&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lightColor&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lightColor&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;light&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;throughEng&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;throughEng&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;throughEng&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;light&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;throughEng&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;bounce&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;bounce&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;throughEng&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;throughEng&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;throughEng&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.01&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;||&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;isOut&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;break&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lightColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;其中最值得关注的是&lt;code&gt;lightColor&lt;/code&gt;和&lt;code&gt;throughEng&lt;/code&gt;，第一个是当前直接光照的结果，第二个是出射（未被吸收）的能量比例。可见这里认为如果检测到了和光源相交，则直接累计，否则而进入光照计算阶段，计算完成后将当前的直接光照结果和累计至今的能量损耗计算，得到应当计入的能量。而光照的计算部分框架实现为：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;calcLight&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;HitPoint&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseUV&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;bounce&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;isLast&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;bool&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;isOut&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;bool&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Light&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;light&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Light&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 计算随机数&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;random&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;getRandom&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseUV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;bounce&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;isOut&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 如果射线和场景无相交，则进行环境光照&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;light&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;calcOutColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;light&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 直接光照&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;light&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;calcDirectColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;random&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;zw&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;isLast&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 最后一次测试，不再进行间接光照计算&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;light&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 间接光照过程&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;probDiffuse&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;getDiffuseProb&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;isDiffuse&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;bool&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;random&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;probDiffuse&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;nextDir&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;calcBrdfDir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;isDiffuse&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;random&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xy&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;isDiffuse&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;light&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;throughEng&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;calcDiffuseFactor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;nextDir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xyz&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;probDiffuse&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;light&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;throughEng&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;calcSpecularFactor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;nextDir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xyz&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;probDiffuse&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;light&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;next&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;genRay&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;light&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;next&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dir&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;RAY_DIR_OFFSET&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;RAY_NORMAL_OFFSET&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;nextDir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xyz&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;light&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;可见光照计算切实被分为了&lt;strong&gt;直接光照&lt;/strong&gt;、&lt;strong&gt;间接光照&lt;/strong&gt;、&lt;strong&gt;环境光照&lt;/strong&gt;（没有做特别处理，直接采样贴图）三部分。有了基本的框架，就可以进行真正的光照计算了。&lt;/p&gt;
&lt;h3&gt;直接光照&lt;/h3&gt;
&lt;p&gt;当射线和表面上的着色点相交后，首先要进行的是直接光照计算。这里分两步，第一步需要对光源进行采样，第二部则是用这个采样的结果进行计算。采样的部分在上面已经论述过（得到了入射光的方向和辐射度），接下来就是利用采样的结果进行&lt;code&gt;fr&lt;/code&gt;的计算：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;calcDirectColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;HitPoint&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;random&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 同上面光源采样部分的代码，求得了入射光方向`sampleLight`、距离`maxT`和辐射度`directLight`等&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;shadowInfo&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;FragmentInfo&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hitTestShadow&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sampleLight&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;maxT&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;shadowInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;brdf&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;calcBrdfDirectOrSpecular&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pbrData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sampleDir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;directLight&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;brdf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;可见这里主要有两步计算，第一步是进行&lt;strong&gt;阴影射线相交测试&lt;/strong&gt;，这个在前面的文章《BVH与射线场景求交插值》中论述过，是为了判断光源和着色点之间是否有物体遮挡，如果有遮挡则证明在阴影范围内，直接返回。否则就进入下一步——计算&lt;code&gt;fr&lt;/code&gt;，即代码中的&lt;code&gt;brdf&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;直接光照的&lt;code&gt;fr&lt;/code&gt;使用BRDF的高光反射模型。&lt;/p&gt;
&lt;h3&gt;间接光照&lt;/h3&gt;
&lt;p&gt;由于高光反射也被间接光照使用，所以在合理先论述间接光照。间接光照由漫反射和高光反射两部分构成，有了着色点和出射光线的信息后，可以按照上面章节论述的采样方式分别求得漫反射和高光反射的入射光线，但这里要注意一点，那就是比例。在&lt;strong&gt;框架&lt;/strong&gt;一节的代码中，有这么一句话：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;probDiffuse&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;getDiffuseProb&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这求取的是漫反射的比例，还记得一开始的BRDF方程中的&lt;code&gt;kd&lt;/code&gt;和&lt;code&gt;ks&lt;/code&gt;吗?其实就是它。在工程实现中，为了效率，我们往往不会在同一次&lt;code&gt;bounce&lt;/code&gt;计算漫反射和高光反射将它们加权相加，而是将这个比例作为&lt;strong&gt;概率&lt;/strong&gt;，来作为当次要进行漫反射还是高光反射的判据，而它的实现为：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;getDiffuseProb&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;HitPoint&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lumDiffuse&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(.&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;01&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pbrData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;diffuseColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.2125&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.7154&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.0721&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)));&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lumSpecular&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(.&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;01&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pbrData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;specularColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.2125&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.7154&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.0721&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)));&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lumDiffuse&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lumDiffuse&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lumSpecular&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;计算中分别求得了着色点的名为&lt;code&gt;diffuseColor&lt;/code&gt;和&lt;code&gt;specularColor&lt;/code&gt;的变量的亮度，然后算出了漫反射的权重。那么这两个变量又是如何求得的呢？这就涉及到PBR材质的处理了。&lt;/p&gt;
&lt;h3&gt;材质预处理&lt;/h3&gt;
&lt;p&gt;在前面的文章，我们了解了使用的材质模型，其中有金属和高光两种工作流，有不同的系数和贴图，现在我们需要将其处理为BRDF需要的参数：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pbrPrepareData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;isSpecGloss&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;bool&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseColor&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;metal&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rough&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;spec&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;gloss&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;PBRData&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pbr&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;PBRData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;specularColor&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;roughness&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;isSpecGloss&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 金属工作流&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;roughness&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;clamp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rough&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.04&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;metallic&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;clamp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;metal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;f0&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.04&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.04&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.04&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;specularColor&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;mix&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;f0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;metallic&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pbr&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;diffuseColor&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;metallic&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseColor&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;f0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 高光工作流&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;roughness&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;gloss&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;specularColor&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;spec&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pbr&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;diffuseColor&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseColor&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;specularColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;specularColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;g&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;specularColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pbr&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseColor&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pbr&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;specularColor&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;specularColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pbr&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;roughness&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;roughness&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;reflectance&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;specularColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;specularColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;g&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;specularColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pbr&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;reflectance90&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;clamp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;reflectance&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;25.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pbr&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;reflectance0&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;specularColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rgb&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pbr&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;alphaRoughness&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;roughness&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;roughness&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pbr&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这个方法完成了材质数据的预处理，其主要通过两种工作流，统一计算出了粗糙度、漫反射颜色、高光反射颜色、菲涅尔系数等等。这里可以看到比如金属工作流和高光工作流的差异，主要在于高光工作流可以自己控制&lt;code&gt;f0&lt;/code&gt;，而金属工作流可以由金属度来控制漫反射比例（完全金属没有漫反射）。&lt;/p&gt;
&lt;h3&gt;高光反射&lt;/h3&gt;
&lt;p&gt;有了材质数据，可以先来求高光反射。我实现了一个方法，用于求解高光反射，针对&lt;strong&gt;直接光照&lt;/strong&gt;和&lt;strong&gt;间接光照的高光部分&lt;/strong&gt;做了区分，但大部分计算都是一致的，区分主要在PDF部分。&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;calcBrdfDirectOrSpecular&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pbr&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;PBRData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;viewDir&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lightDir&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;isDirect&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;bool&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;probDiffuse&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;H&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normalize&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lightDir&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;viewDir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;NdotV&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;clamp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;abs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;viewDir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)),&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.001&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;NdotL&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;clamp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;abs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lightDir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)),&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.001&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;NdotH&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;clamp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;abs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;H&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)),&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;LdotH&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;clamp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;abs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lightDir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;H&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)),&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;VdotH&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;clamp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;viewDir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;H&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// Calculate the shading terms for the microfacet specular shading model&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;F&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pbrSpecularReflection&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pbr&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;reflectance0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pbr&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;reflectance90&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;VdotH&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;G&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pbrGeometricOcclusion&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;NdotL&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;NdotV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pbr&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;alphaRoughness&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;D&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pbrMicrofacetDistribution&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pbr&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;alphaRoughness&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;NdotH&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;specular&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;F&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;G&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;D&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;4.0&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;NdotL&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;NdotV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;isDirect&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;diffuse&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;NdotL&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;INV_PI&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pbr&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;diffuseColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;specular&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;diffuse&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;specularPdf&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;D&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;NdotH&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;4.0&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;LdotH&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;NdotL&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;specular&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;mix&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;specularPdf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;probDiffuse&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;在求解的实现中，先不看PDF部分，主要计算的是&lt;code&gt;specular&lt;/code&gt;，而其最主要的是菲涅尔项&lt;code&gt;F&lt;/code&gt;、法线分布项&lt;code&gt;D&lt;/code&gt;和几何遮蔽项&lt;code&gt;G&lt;/code&gt;的实现。&lt;/p&gt;
&lt;h4&gt;F&lt;/h4&gt;
&lt;p&gt;菲涅尔项的求解实现为：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pbrSpecularReflection&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;reflectance0&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;reflectance90&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;VdotH&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;reflectance0&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;reflectance90&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;reflectance0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pow&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;clamp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;VdotH&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;5.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这里采用的是&lt;code&gt;Schilick&lt;/code&gt;近似方法。&lt;/p&gt;
&lt;h4&gt;D&lt;/h4&gt;
&lt;p&gt;法线分布我们采用的常见的&lt;code&gt;GGX&lt;/code&gt;分布：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pbrMicrofacetDistribution&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;alphaRoughness&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;NdotH&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;roughnessSq&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;alphaRoughness&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;alphaRoughness&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;f&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;NdotH&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;NdotH&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;roughnessSq&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;NdotH&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;roughnessSq&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;INV_PI&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;f&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;f&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h4&gt;G&lt;/h4&gt;
&lt;p&gt;几何遮蔽则使用&lt;code&gt;Smith GGX&lt;/code&gt;近似：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pbrGeometricOcclusion&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;NdotL&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;NdotV&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;alphaRoughness&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;alphaRoughness&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;alphaRoughness&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;attenuationL&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;2.0&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;NdotL&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;NdotL&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sqrt&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;NdotL&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;NdotL&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)));&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;attenuationV&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;2.0&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;NdotV&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;NdotV&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sqrt&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;NdotV&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;NdotV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)));&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;attenuationL&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;attenuationV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h4&gt;PDF&lt;/h4&gt;
&lt;p&gt;如果是直接光照，那么直接返回这个高光&lt;code&gt;specular&lt;/code&gt;的值即可（因为直接光照的PDF已经在前面的计算除过了），但是对于间接光照，还是需要考虑PDF，以及漫反射的概率。前面提到过我们对高光使用的是GGX分布，其PDF公式翻译为代码为：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;specularPdf&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;D&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;NdotH&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;4.0&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;LdotH&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;而在考虑漫反射概率后，其最终值为：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;specular&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;NdotL&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;mix&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;specularPdf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;probDiffuse&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;漫反射&lt;/h3&gt;
&lt;p&gt;相对于高光，漫反射比较简单，其值根据原理所得即为&lt;code&gt;hit.pbrData.diffuseColor * INV_PI&lt;/code&gt;，而PDF则为：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;diffusePdf&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;NdotL&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;INV_PI&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;考虑到概率，最后的值为：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pbrData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;diffuseColor&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;INV_PI&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;NdotL&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;diffusePdf&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;probDiffuse&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;化简后为：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pbrData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;diffuseColor&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;probDiffuse&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;结果&lt;/h2&gt;
&lt;p&gt;至此，整个着色过程完成，由于是1SPP，并且只有一次&lt;code&gt;bounce&lt;/code&gt;，在单帧着色后会得到一张充满噪点的结果：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/webgpu-rt/6/0.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;那么接下来就自然要想到如何&lt;strong&gt;降噪&lt;/strong&gt;了，下一文章将会提到。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Friday, 01 Oct 2021 21:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2021.10.01 21:00:article/Skill-2021_10_01_a</guid>
<category>WebGPU</category>
<category>路径追踪</category>
<category>光线追踪</category>
<category>图形学</category>
<category>BRDF</category>
<category>蒙特卡洛</category>
</item>

<item>
<title>【WebGPU实时光追美少女】BVH与射线场景求交插值</title>
<link>http://dtysky.moe/article/Skill-2021_09_26_a</link>
<description>&lt;p&gt;本系列文章设计的所有代码均已开源，Github仓库在这里：&lt;a href=&amp;quot;https://github.com/dtysky/webgpu-renderer&amp;quot;&gt;dtysky/webgpu-renderer&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;在上一篇文章&lt;strong&gt;管线组织与GBuffer&lt;/strong&gt;中，我提到了路径追踪的第一步是射线和场景的求交，进一步引出了BVH的概念。在这一章，我将会论述如何利用BVH对场景中的所有三角形进行高效得分割和求交，并且给出得出交点后属性插值的方法。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;不知道为何最近有点抑郁，所以更新效率低了一些...&lt;/p&gt;
&lt;p&gt;求交部分的代码都在&lt;code&gt;buildin/shaders/ray-tracing/hitTest.chunk.wgsl&lt;/code&gt;内。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;射线和场景求交&lt;/h2&gt;
&lt;p&gt;首先要讨论的是射线和场景的求交，或者用在物理引擎中的叫法&lt;strong&gt;碰撞测试&lt;code&gt;hitTest&lt;/code&gt;&lt;/strong&gt;。我们并不需要先论述BVH这样的加速结构，因为无论哪种加速结构，到最后总是要进行射线和三角形的求交和插值。&lt;/p&gt;
&lt;h3&gt;射线和三角形描述&lt;/h3&gt;
&lt;p&gt;为了求交计算，需要先对射线和三角形进行描述。首先是射线，描述一条射线有很多种方式，这里使用最便于GPU计算的结构：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;struct&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Ray&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;origin&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dir&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;invDir&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;通过一个原点&lt;code&gt;origin&lt;/code&gt;和方向&lt;code&gt;dir&lt;/code&gt;来使用方程&lt;code&gt;p = origin + t * dir&lt;/code&gt;描述射线，&lt;code&gt;invDir&lt;/code&gt;是方向向量的倒数，便于后续使用。有了射线的结构后，考虑如何生成射线，但这一部分我将放在下一章来讲。&lt;/p&gt;
&lt;p&gt;接下来是三角形，在前面的文章我论述过如何将整个场景的数据进行合并，其中就包括了顶点数据&lt;code&gt;indexData&lt;/code&gt;的合并。一个三角形由三个顶点描述，所以只需要知道三角形三个顶点的索引即可。这里先不讨论BVH，所以就认为我们是在顺序、每三个顶点遍历整个&lt;code&gt;indexData&lt;/code&gt;即可。得到顶点索引后，便可以直接从&lt;code&gt;storage buffer&lt;/code&gt;中获得对应的顶点属性，比如&lt;code&gt;u_positions.value[index0]&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;求交&lt;/h3&gt;
&lt;p&gt;有了射线和三角形的信息，便可以进行求交计算。射线和三角形的求交思路不止一种，一个最简单和直观的思路为：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;先求射线和三角形所在平面的交点。&lt;/li&gt;
&lt;li&gt;再判断交点是否在三角形内部。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;但由于需要首先确定三角形所在平面，这么做实际运算效率其实不高，实际上我们可以直接通过方程来描述三角形，和射线方程联立整理，来简化计算：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/webgpu-rt/5/1.png&amp;quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;空间射线方程：&lt;code&gt;P = O + t * D&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;空间三角形方程：&lt;code&gt;P = u * E1 + v * E2&lt;/code&gt;，其中&lt;code&gt;E1 = V1 - V0&lt;/code&gt;和&lt;code&gt;E2 = V2 - V0&lt;/code&gt;是两条边的向量，&lt;code&gt;u&lt;/code&gt;、&lt;code&gt;v&lt;/code&gt;是权重，&lt;code&gt;V1&lt;/code&gt;、&lt;code&gt;V2&lt;/code&gt;和&lt;code&gt;V3&lt;/code&gt;是三个顶点。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;联立后得到方程：&lt;code&gt;O + t * D = u * E1 + v * E2&lt;/code&gt;，这是一个三元方程组，自然可以化简写成矩阵形式：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;[-D, E1, E2] * [t, u, v] = T&lt;/code&gt;，其中&lt;code&gt;T = O - V0&lt;/code&gt;，&lt;code&gt;[-D, E1, E2]&lt;/code&gt;是3x3列主序矩阵，&lt;code&gt;[t, u, v]&lt;/code&gt;是转置（赖得打公式了...&lt;/p&gt;
&lt;p&gt;而经过克莱姆法则和混合积公式推导，记&lt;code&gt;det = D x E2 · E1&lt;/code&gt;最后可写为：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;t = (T x E1 * E2) / det&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;u = (D x E2 * T) / det&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;v = (T x E1 * E2) / det&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;如此一来，便可以求出这三个系数，并在求取的过程中顺便将不满足条件的情况判断出来：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 判断是否相交，如果相交，返回所有的顶点属性&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;triangleHitTest&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;leaf&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;BVHLeaf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;FragmentInfo&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;FragmentInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;indexes&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;u32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;leaf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;indexes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 索引出三个顶点&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p0&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_positions&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;value&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;indexes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]];&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p1&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_positions&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;value&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;indexes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]];&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p2&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_positions&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;value&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;indexes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]];&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;e1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p1&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;e2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p2&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;cross&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;e2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;det&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;e1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;origin&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 保证`det`为正&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;det&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;det&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;det&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// `det`为0，三向量共面，算作不相交&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;det&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.0001&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// `u / det`必须在(0,1)&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;||&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;det&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;q&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;cross&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;e1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;v&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;q&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// `v / det`和`(1 - v - u) / det`必须在(0,1)&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;v&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;||&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;v&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;det&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lt&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;e2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;q&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// lt即为射线方程的`t`，由于是射线，其必须大于0&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lt&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;invDet&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;det&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;weights&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;v&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;invDet&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;weights&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;weights&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;weights&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lt&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;invDet&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hitPoint&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;origin&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dir&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;uv0&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_uvs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;value&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;indexes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;uv1&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_uvs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;value&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;indexes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;uv2&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_uvs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;value&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;indexes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;n0&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_normals&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;value&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;indexes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;n1&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_normals&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;value&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;indexes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;n2&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_normals&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;value&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;indexes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;meshIndex&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_meshMatIndexes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;value&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;indexes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;matIndex&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_meshMatIndexes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;value&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;indexes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;metallicRoughnessFactorNormalScaleMaterialType&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_metallicRoughnessFactorNormalScaleMaterialTypes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;matIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;matType&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;u32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;metallicRoughnessFactorNormalScaleMaterialType&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;如果这些测试都通过，则射线和三角形相交，而我们也顺便得到了&lt;code&gt;u&lt;/code&gt;、&lt;code&gt;v&lt;/code&gt;、&lt;code&gt;det&lt;/code&gt;，通过它们，也就顺便获得了重心坐标。于此同时，我还将属于这个三角形的三个顶点属性都存了下来，以供下一个阶段做插值。&lt;/p&gt;
&lt;h3&gt;重心坐标插值&lt;/h3&gt;
&lt;p&gt;重心坐标插值这个概念在传统光栅化渲染器的栅格化一步也会提到，即当某个像素被一个平面三角形覆盖时，需要通过三角形的三个顶点属性算出当前像素实际的顶点属性。而对于路径追踪渲染器，由于同样是用三角形描述场景，所以也需要这么一个插值的过程，来计算出交点实际的顶点属性值。&lt;/p&gt;
&lt;p&gt;所谓重心坐标，就是上面的&lt;code&gt;info.weights&lt;/code&gt;。对于一个三角形内的点，它们都在(0,1)之间，记录了三角形的每个顶点到交点的距离的比例，从这个角度而言，它们实际上也是这个交点相对于三角形三个顶点的&lt;strong&gt;权重&lt;/strong&gt;。&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 通过相交点的参数，求取最终插值后的结果并返回&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;fillHitPoint&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;frag&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;FragmentInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;HitPoint&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;HitPoint&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hited&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;frag&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;meshIndex&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;frag&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;meshIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;matIndex&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;frag&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;matIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;metallicRoughnessFactorNormalScaleMaterialType&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_metallicRoughnessFactorNormalScaleMaterialTypes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;frag&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;matIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;frag&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p0&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;frag&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;weights&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;frag&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p1&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;frag&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;weights&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;frag&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p2&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;frag&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;weights&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;frag&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;uv0&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;frag&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;weights&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;frag&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;uv1&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;frag&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;weights&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;frag&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;uv2&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;frag&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;weights&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureIds&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_matId2TexturesId&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;frag&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;matIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;faceNormal&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;getFaceNormal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;frag&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sign&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sign&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;faceNormal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sign&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;getNormal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;frag&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureIds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;metallicRoughnessFactorNormalScaleMaterialType&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseColor&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;getBaseColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_baseColorFactors&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;frag&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;matIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureIds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseColor&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rgb&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;glass&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;matType&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;frag&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;matType&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;isSpecGloss&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;isMatSpecGloss&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;frag&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;matType&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;isGlass&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;isMatGlass&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;frag&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;matType&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;isSpecGloss&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;specularGlossinessFactors&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_specularGlossinessFactors&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;frag&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;matIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;spec&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;getSpecular&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;specularGlossinessFactors&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xyz&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureIds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rgb&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;gloss&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;getGlossiness&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;specularGlossinessFactors&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureIds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;metal&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;getMetallic&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;metallicRoughnessFactorNormalScaleMaterialType&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureIds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rough&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;getRoughness&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;metallicRoughnessFactorNormalScaleMaterialType&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureIds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pbrData&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pbrPrepareData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;isSpecGloss&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;metal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rough&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;spec&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;gloss&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;以上是插值、求得最后结果的逻辑，可以见到像是&lt;code&gt;position&lt;/code&gt;、&lt;code&gt;uv&lt;/code&gt;之类的顶点属性都使用了重心坐标进行插值，而后也是用插值后的结果来采样贴图、计算材质属性的。&lt;/p&gt;
&lt;h2&gt;加速结构&lt;/h2&gt;
&lt;p&gt;如果不考虑性能，理论上上面的算法已经足够满足需求，我们只需要在每次发射射线的时候用该射线和场景内所有三角形求交，得到所有相交的点，然后找到其中离射线原点最近的即可。然而这是不现实的，在当前的这个时代，一个场景有上万上十万个三角形都挺正常，在实时的情况下即便是1SPP开销都是巨大的，所以这就需要我们有办法来加速整个求交的过程，也就引出了接下来要论述的——加速结构。&lt;/p&gt;
&lt;h2&gt;常见加速结构&lt;/h2&gt;
&lt;p&gt;加速结构的本质在于场景划分，通过合适的算法划分场景，来达到索引优化，最终减少真正的计算。场景划分不仅仅用于路径追踪，实际上在传统的实时渲染中也有很大的作用，比如ForwardAdd中的多光源实现等等。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/webgpu-rt/5/2.png&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;如图，常见的加速结构有八叉树、KD树、BSP树等等。但他们都属于空间划分，而空间划分对于我们的场景有很大的劣势：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;物体可能存在与多个划分区域，划分不彻底。&lt;/li&gt;
&lt;li&gt;为了建立正确的划分，需要求取区域和三角形的相交。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;所以在实时光追的引擎中，大家一般都使用BVH，即层次包围盒：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/webgpu-rt/5/5.png&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;如图，这种结构不针对空间，而是根据物体的层次来对场景做划分。这样可以保证每个三角形都存在于唯一的划分区域内，同时整棵树比较平衡、最大深度低，而且构建十分简单。但当然世上没有免费的优化，其代价为：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;由于不是空间划分，所以包围盒之间可能重叠，必须完全遍历整棵树才能得到最终结果，某些情况下相对求交效率可能低一些。&lt;/li&gt;
&lt;li&gt;虽然构建效率高，但结构变了需要完全重建。不过这一部分已经被现代算法优化得很好了，有GPU算法（但本文不会提及）。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;不过相对来讲，BVH仍然是当下最适合实时路径追踪的加速结构。&lt;/p&gt;
&lt;h3&gt;构造BVH&lt;/h3&gt;
&lt;p&gt;构造BVH可以在CPU进行，也可以在GPU进行（使用莫顿码），但由于个人精力有限，所以这里只实现了传统CPU的版本。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;CPU部分构造BVH的代码全部在&lt;code&gt;extension/BVH.ts&lt;/code&gt;内。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这里我不会讲所有的构建的代码都事无巨细列出来，只会说一些思路，具体的可以参考实际代码。&lt;/p&gt;
&lt;p&gt;简单来讲，为了构建一个BVH，我们至少需要三步：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;通过当前合并好的三角形，求取每个三角形的包围盒（世界空间）。&lt;/li&gt;
&lt;li&gt;通过这些包围盒，构建二叉树，树的叶子节点为实际的三角形信息，其他为包围盒信息。&lt;/li&gt;
&lt;li&gt;将树状结构打平为紧密的数组来存储。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;所以我也讲这三步实现为了三个方法。&lt;/p&gt;
&lt;h4&gt;求取包围盒&lt;/h4&gt;
&lt;p&gt;首先需要一个数据结构来描述包围盒，其声明为：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;class&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Bounds&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 最大边界&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;max&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 最小边界&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;min&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 中点&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;get&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;center&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 尺寸&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;get&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;size&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 长度最大的轴&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;get&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;maxExtends&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;EAxis&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 包围盒的表面积&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;get&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;surfaceArea&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 生成一个空的包围盒&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;initEmpty&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Bounds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 从顶点生成一个包围盒&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;fromVertexes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;v1&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;v2&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;v3&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Bounds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 扩张一个包围盒&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;update&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;v&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Bounds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 合并另一个包围盒，求并&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;mergeBounds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;bounds&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Bounds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Bounds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 一个点是否在包围盒内&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;pointIn&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;p&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;boolean&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 获取一个点与包围盒某个轴最小值的偏移比例，一般0~1&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;getOffset&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;axis&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;EAxis&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;v&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这个类也是接下来所有操作的基础。有了它，便可以很简单地求得所有三角形的包围盒了：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;protected&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_setupBoundsInfo&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;worldPositions&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;indexes&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Uint32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_boundsInfos&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[];&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;indexes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;length&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;idxes&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;indexes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;slice&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

    &lt;span class=&amp;quot;c1&amp;quot;&gt;// 从`worldPositions`中解出每个顶点需要的数据&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;copyTypedArray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;tmpV1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;worldPositions&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;idxes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;copyTypedArray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;tmpV2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;worldPositions&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;idxes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;copyTypedArray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;tmpV3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;worldPositions&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;idxes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

    &lt;span class=&amp;quot;c1&amp;quot;&gt;// 构建包围盒&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;bounds&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Bounds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;().&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fromVertexes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;tmpV1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;tmpV2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;tmpV3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;c1&amp;quot;&gt;// 用于构建树的信息，包括包围盒本身，以及对应的三角形顶点索引&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_boundsInfos&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;push&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;indexes&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;idxes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;bounds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h4&gt;构建二叉树&lt;/h4&gt;
&lt;p&gt;有了包围盒后，便可以利用它们构建二叉树，构建BVH树的基本原理非常简单，首先我们要定义两个数据结构：结点&lt;code&gt;BVHNode&lt;/code&gt;和叶子&lt;code&gt;BVHLeaf&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;interface&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;IBVHNode&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 沿哪个轴划分&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;axis&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;EAxis&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 所有子节点合并的包围盒&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;bounds&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Bounds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 左右子节点&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;child0&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IBVHNode&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;IBVHLeaf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;child1&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IBVHNode&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;IBVHLeaf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 结点在树中的深度（层数）&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;depth&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;c1&amp;quot;&gt;// 用于存储数个按策略不可划分的三角形&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;interface&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;IBVHLeaf&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 包含三角形的起始索引&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;infoStart&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 包含三角形的结束索引&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;infoEnd&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 所有子节点的包围盒&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;bounds&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Bounds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;最基础的BVH的构建本质上是一个递归，自顶而下不断二分，直到无法继续分割。同时在这个过程中将所有子节点的包围盒合并到父节点，最终无法分割的节点记为叶子节点&lt;code&gt;BVHLeaf&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;protected&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_buildRecursive&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;start&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;end&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;depth&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;IBVHNode&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;IBVHLeaf&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_boundsInfos&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;bounds&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Bounds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;().&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;initEmpty&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 合并所有子节点的包围盒&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;start&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;end&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;bounds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;mergeBounds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_boundsInfos&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;bounds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;nPrimitives&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;end&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;start&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;nPrimitives&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_maxPrimitivesPerLeaf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;c1&amp;quot;&gt;// 如果当前三角形数量不可再分割，直接返回叶子节点&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;infoStart&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;start&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;infoEnd&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;end&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;bounds&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;c1&amp;quot;&gt;// 否则，通过一定规则算出新的分割点`mid`，进行下一轮递归&lt;/span&gt;
    &lt;span class=&amp;quot;c1&amp;quot;&gt;// 这里还可能执行一些图元顺序的调整，来达到最有分割，分割有不同的策略&lt;/span&gt;

    &lt;span class=&amp;quot;c1&amp;quot;&gt;// 递归计算左右子节点&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;child0&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_buildRecursive&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;start&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;mid&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;depth&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;child1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_buildRecursive&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;mid&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;end&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;depth&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

    &lt;span class=&amp;quot;c1&amp;quot;&gt;// 返回新的`BVHNode`&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;bounds&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Bounds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;().&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;initEmpty&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;().&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;mergeBounds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;child0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;bounds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;mergeBounds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;child1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;bounds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;axis&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;dim&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;child0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;child1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;depth&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;可见原理确实很简单，实际上对于这种传统的BVH构建算法，最精髓的在于注释中写道的&lt;strong&gt;分割策略&lt;/strong&gt;。考察一个BVH的构建是否优秀，一般需要评估两点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在相同的图元数量下，BVH的层数是否最低。&lt;/li&gt;
&lt;li&gt;每一层两个子节点中的包围盒重叠程度是否足够低。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;围绕这两点有各种不同的算法来权衡，在本项目的实现中，使用的是&lt;strong&gt;SAH - Surface Area Heuristic&lt;/strong&gt;，即表面积启发式算法。这是一种从概率角度的优化，从理论上来讲，一条射线和场景求交的代价，主要由和图元即三角形的代价来决定，一次求交中计算的图元数量越多，也就意味着效率越低。而和图元求交的概率，又可以用包含图元的包围盒的重叠程度来表征，进一步可以用包围盒的&lt;strong&gt;表面积&lt;/strong&gt;来表征，最大程度减少包围盒的表面积，就是SAH的目的，所以它实际上是解决了上面的第二个问题。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/webgpu-rt/5/3.png&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;如图，实现上来讲，我们可以先求得所有包围盒中心所构成的最大包围盒，以此包围盒最长的轴作为用于切分的轴。如果包围盒已然不可划分（min=max），则直接返回，接着如果目前图元数量小于一定级别，则使用简单的包围盒中心位置做快速选择&lt;code&gt;nth_element&lt;/code&gt;划分；再否则就进行正常的SAH，这里使用了一种基于均分的buckets（桶）的算法）——将图元按照中心位置划分到不同的桶内，再从低到高不断合并这些桶来求得划分代价，接着得出最低的划分代价，最终按照这个最低代价的桶为中点进行划分。实现详见代码。&lt;/p&gt;
&lt;h4&gt;打平为数组&lt;/h4&gt;
&lt;p&gt;有了BVH树还不够，因为最后一步要送到CS内进行最后的求交，所以必须要将其组装成一个&lt;code&gt;storage buffer&lt;/code&gt;，这就要求我们设计一种紧凑的数据结构，来将整棵树存储到一个&lt;code&gt;ArrayBuffer&lt;/code&gt;中。而我设计的结构如下:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;对于Node，使用&lt;code&gt;uint32 x 1&lt;/code&gt;来存储子节点的类型（最低位，0为Node，1为Leaf）和偏移，&lt;code&gt;float x 3&lt;/code&gt;存储子节点的包围盒，共两个子节点，消耗8个float的长度。&lt;/li&gt;
&lt;li&gt;对于Leaf，使用&lt;code&gt;uint32 x 1&lt;/code&gt;来存储接下来有几个三角形属于同一个节点，&lt;code&gt;uint32 x 3&lt;/code&gt;存储当前三角形的三个顶点索引。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;有了结构，整个Buffer的构建也就比较简单，同样是递归构建：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;protected&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_traverseNode&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;node&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IBVHNode&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;IBVHLeaf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;maxDepth&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;nodes&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[],&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;leaves&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[]},&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;depth&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;parentOffset&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;childIndex&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;maxDepth&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;Math&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;depth&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;maxDepth&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;nodes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;leaves&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_boundsInfos&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_bvhNodes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_bvhLeaves&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;isBVHLeaf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;_bvhLeaves&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;push&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;parentOffset&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;nodes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;parentOffset&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;8&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;childIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;31&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;leaves&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;length&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;count&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;infoEnd&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;infoStart&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;infoStart&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;infoEnd&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;idxes&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_boundsInfos&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;indexes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

      &lt;span class=&amp;quot;nx&amp;quot;&gt;leaves&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;push&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;count&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;idxes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;idxes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;idxes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]);&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;_bvhNodes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;push&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;bounds&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;bounds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;nodeOffset&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;nodes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;length&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;8&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;parentOffset&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;nodes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;parentOffset&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;8&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;childIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;nodeOffset&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;nx&amp;quot;&gt;nodes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;push&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
      &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;bounds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;min&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;bounds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;min&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;bounds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;min&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt;
      &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;bounds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;bounds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;bounds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_traverseNode&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;child0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;depth&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;nodeOffset&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_traverseNode&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;child1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;depth&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;nodeOffset&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;注意这里我们构建好的Buffer正好是16字节对齐的。&lt;/p&gt;
&lt;h3&gt;BVH求交&lt;/h3&gt;
&lt;p&gt;构建了BVH后，使用&lt;code&gt;u_bvh&lt;/code&gt;作为&lt;code&gt;storage buffer&lt;/code&gt;传给CS来进行求交。BVH的求交远离比较简单巧妙，由于BVH本质上是一种AABB，同时和射线也并不需要得到真正的交点，只需要得到是否相交的结果。所以可以将其视为&lt;strong&gt;三对互相平行的平板&lt;/strong&gt;，如图：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/webgpu-rt/5/4.png&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;可以将射线投影到三个平面，最终需要满足最大的&lt;code&gt;xmin - origin&lt;/code&gt;小于最小的&lt;code&gt;xmax - origin&lt;/code&gt;，这个原理也好理解，如果将前者理解为光线&lt;strong&gt;进入盒子&lt;/strong&gt;的时间，后者理解为光线&lt;strong&gt;离开盒子&lt;/strong&gt;的时间，那么我们最终要保证&lt;strong&gt;进入时间小于离开时间&lt;/strong&gt;：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;boxHitTest&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;min&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;min&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;origin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;invDir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;origin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;invDir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tvmin&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;min&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tvmax&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tmin&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tvmin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tvmin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tvmin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tmax&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;min&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tvmax&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;min&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tvmax&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tvmax&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tmax&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tmin&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tmin&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tmin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tmax&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;以上方法返回的结果如果大于0，则表示射线和BVH相交，可见比起三角形求交，是十分高效的。而其中的&lt;code&gt;max&lt;/code&gt;和&lt;code&gt;min&lt;/code&gt;则是通过对数据的&lt;code&gt;decode&lt;/code&gt;得来的：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;struct&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Child&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;isLeaf&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;bool&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;offset&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;u32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;decodeChild&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;index&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;u32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Child&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Child&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;((&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;index&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;31&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;index&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;getBVHNodeInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;offset&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;u32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;BVHNode&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;BVHNode&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;data0&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_bvh&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;value&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;offset&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;data1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_bvh&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;value&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;offset&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;child0Index&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;bitcast&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;u32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;data0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;child1Index&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;bitcast&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;u32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;data1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;min&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;data0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;yzw&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;data1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;yzw&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;getBVHLeafInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;offset&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;u32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;BVHLeaf&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;leaf&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;BVHLeaf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;data1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_bvh&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;value&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;offset&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;leaf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;primitives&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;bitcast&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;u32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;data1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;leaf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;indexes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;bitcast&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;u32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;data1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;leaf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;indexes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;bitcast&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;u32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;data1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;leaf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;indexes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;bitcast&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;u32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;data1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;w&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;leaf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;综合实现&lt;/h2&gt;
&lt;p&gt;有了BVH、BVH求交算法、三角形求交算法、插值算法，便可以组装出最终的求交方案。但在这之前，我们需要先明确求交的分类：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;普通射线求交：最普通的求交，用于间接光照，需要找到离射线原点最近的三角形。&lt;/li&gt;
&lt;li&gt;阴影射线求交：阴影射线，用于直接光照，详细在后面文章会讲到，只需要判断射线和一定范围内的三角形有相交。&lt;/li&gt;
&lt;li&gt;光源求交：和光源求交，是和三角形不同的另一种算法，在这里顺便也讲了。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;一般来讲，普通求交要和光源求交联合起来，因为如果射线最先相交的是光源，还进行正常的表面计算会出现问题，阴影射线求交理论同理（若只有一个光源可以忽略）。&lt;/p&gt;
&lt;h3&gt;普通射线&lt;/h3&gt;
&lt;p&gt;由于要寻找的是离射线原点最近的交点，所以一定是&lt;strong&gt;遍历完整棵树&lt;/strong&gt;才能停下，这可以用一次&lt;code&gt;dfs&lt;/code&gt;实现，途中还可以直接剪枝。&lt;code&gt;dfs&lt;/code&gt;最简单的是使用递归，但显然我们不能再wgsl里使用递归，所以只能自己维护一个递归栈，用迭代来实现了：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hitTest&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;HitPoint&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;HitPoint&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;fragInfo&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;FragmentInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 初始化为最大长度&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;fragInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;MAX_RAY_LENGTH&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;BVHNode&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 构建BVH深度大小的结点栈，栈中数据为结点索引&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;nodeStack&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;array&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;u32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;cp&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;BVH_DEPTH&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;nodeStack&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;stackDepth&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;loop&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 当前深度小于10，栈中已无结点，结束&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;     &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;stackDepth&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;       &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;break&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;     &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;child&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;decodeChild&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;nodeStack&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;stackDepth&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;stackDepth&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;stackDepth&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;child&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;isLeaf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 进行叶子的求交，内部本质上是进行了三角形求交，最终返回求交的信息&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;FragmentInfo&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;leafHitTest&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;child&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;offset&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kc&amp;quot;&gt;false&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;fragInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;        &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 如果交点比当前交点近，则使用替换&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;        &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;fragInfo&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;continue&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;getBVHNodeInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;child&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;offset&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 进行结点的求交&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hited&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;boxHitTest&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;min&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hited&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;continue&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 相交，两个子节点入栈，进入下一次迭代&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;stackDepth&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;stackDepth&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;nodeStack&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;stackDepth&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;child0Index&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;stackDepth&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;stackDepth&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;nodeStack&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;stackDepth&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;child1Index&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;fragInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 如果有交点，进行重心坐标插值，同时生成材质属性&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;fillHitPoint&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;fragInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;阴影射线&lt;/h3&gt;
&lt;p&gt;阴影射线求交和普通求交大差不差，唯一的区别在于需要给定一个初始的最大值&lt;code&gt;maxT&lt;/code&gt;，即射线原点到光源之上采样点之间的距离，并且判断有相交后即可结束求交，立即返回：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hitTestShadow&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;maxT&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;FragmentInfo&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;fragInfo&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;FragmentInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;BVHNode&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;nodeStack&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;array&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;u32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;cp&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;BVH_DEPTH&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;nodeStack&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;stackDepth&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;loop&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;     &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;stackDepth&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;       &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;break&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;     &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;child&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;decodeChild&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;nodeStack&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;stackDepth&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;stackDepth&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;stackDepth&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;child&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;isLeaf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;FragmentInfo&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;leafHitTest&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;child&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;offset&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;maxT&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;EPS&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;        &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 判断在射线原点和光源采样点之间有遮挡，返回&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;        &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;info&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;continue&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;getBVHNodeInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;child&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;offset&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hited&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;boxHitTest&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;min&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hited&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;      &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;continue&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;stackDepth&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;stackDepth&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;nodeStack&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;stackDepth&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;child0Index&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;stackDepth&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;stackDepth&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;nodeStack&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;stackDepth&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;child1Index&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;fragInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;光源&lt;/h3&gt;
&lt;p&gt;最后就是和光源的求交了，早在前面的文章&lt;strong&gt;WebGPU基础与简单渲染器&lt;/strong&gt;中，我们就提到了本引擎是如何描述光源，尤其是面光源的：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;struct&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;LightInfo&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lightType&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;u32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;areaMode&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;u32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;areaSize&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;worldTransform&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;mat4x4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;worldTransformInverse&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;mat4x4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;而对于面光源（无论是什么形状），一个通用的做法是先求取射线和平面的交点，然后判断交点是否在光源内，这个也就是本文一开始提到的、低效的&lt;strong&gt;三维平面求交&lt;/strong&gt;。但这里好在引擎已经提供了光源的&lt;code&gt;worldTransformInverse&lt;/code&gt;，所以理论上我们可以认为面光源总是在自己的本地空间（XZ平面），此时只要反向将射线变换到光源的本地空间，问题就变得简单了很多：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;求取射线与平面的交点被简化了，最终得到的是一个XZ平面的点。&lt;/li&gt;
&lt;li&gt;判定交点和光源的关系被简化到了二维空间。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;目前引擎支持&lt;code&gt;disc&lt;/code&gt;和&lt;code&gt;rect&lt;/code&gt;两种面光源，在平面上可以非常简单得判定包含关系：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hitTestXZPlane&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;inverseMat&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;mat4x4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 射线方向变换到光源本地空间&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;invDir&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normalize&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;((&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;inverseMat&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)).&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xyz&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 光源本地空间法线的反向基向量&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dot&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;invDir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;abs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;EPS&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 射线平行或背向光源&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;MAX_RAY_LENGTH&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;MAX_RAY_LENGTH&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;MAX_RAY_LENGTH&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 射线原点变换到光源本地空间&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;invOrigin&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;inverseMat&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;origin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)).&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xyz&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 求取相交段的射线步长&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;invOrigin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;EPS&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;MAX_RAY_LENGTH&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;MAX_RAY_LENGTH&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;MAX_RAY_LENGTH&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;invOrigin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xy&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;invDir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xy&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hitTestLights&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;areaLight&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;LightInfo&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;global&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_lightInfos&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// xyz存光的颜色，w存射线相交段的`t`&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;areaLight&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rgb&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;MAX_RAY_LENGTH&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 目前只支持一个光源&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;areaLight&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lightType&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;LIGHT_TYPE_AREA&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 求得XZ平面上的交点，以及相交段射线的`t`&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xyt&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hitTestXZPlane&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;areaLight&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;worldTransformInverse&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;bool&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kc&amp;quot;&gt;false&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;areaLight&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;areaMode&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;==&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;LIGHT_AREA_DISC&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 判定是否在圆盘内&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;length&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xyt&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xy&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;areaLight&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;areaSize&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 判定是否在矩形内&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;all&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;abs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xyt&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xy&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;areaLight&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;areaSize&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;2.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xyt&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;展示&lt;/h2&gt;
&lt;p&gt;以上就是本文的所有内容，但在最后，为了调试，我还做了一个功能来将将整个BVH的包围盒画出来，其实原理也很简单：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;通过包围盒信息，生成&lt;code&gt;line&lt;/code&gt;类型的图元数据（主要是索引数据是一个图元两个点，而非三角形的三个点）。&lt;/li&gt;
&lt;li&gt;组装&lt;code&gt;Mesh&lt;/code&gt;绘制。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;由于太过简单这里就不详细讲了，直接看看最后的结果吧：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/webgpu-rt/5/0.png&amp;quot; /&gt;&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Sunday, 26 Sep 2021 00:10:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2021.09.26 00:10:article/Skill-2021_09_26_a</guid>
<category>WebGPU</category>
<category>路径追踪</category>
<category>光线追踪</category>
<category>图形学M</category>
<category>BVH</category>
</item>

<item>
<title>【WebGPU实时光追美少女】管线组织与GBuffer</title>
<link>http://dtysky.moe/article/Skill-2021_09_17_a</link>
<description>&lt;p&gt;本系列文章设计的所有代码均已开源，Github仓库在这里：&lt;a href=&amp;quot;https://github.com/dtysky/webgpu-renderer&amp;quot;&gt;dtysky/webgpu-renderer&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;上一篇文章&lt;strong&gt;场景数据组织与合并&lt;/strong&gt;，我们讨论了要使用的材质、以及如何组织好场景数据，并对场景数据进行了合并。现在我们可以认为有了一个很大的&lt;code&gt;Geometry&lt;/code&gt;，其中存储着整个场景所有的图元信息，还有合并好的&lt;code&gt;Uniform&lt;/code&gt;、&lt;code&gt;Texture&lt;/code&gt;，以便在渲染过程中查找到物体上某一点对应的材质数据。那么下一步，就是如何利用这些数据进行渲染了。&lt;/p&gt;
&lt;h2&gt;路径追踪管线&lt;/h2&gt;
&lt;p&gt;路径追踪管线和传统的光栅化管线是完全不同的，目前一般有两种方案来实现。在设计整个管线之前，我们要先回顾并深入一下路径追踪的原理。&lt;/p&gt;
&lt;h3&gt;追踪过程&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/webgpu-rt/1/1.png&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;还是这张图，但这次我们要更深入一些。首先从左侧的相机看起，路径追踪起始于相机发出的若干世界空间的射线，这些射线进入场景后：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;首先进行&lt;strong&gt;相交测试&lt;/strong&gt;，从结果上来看，我们需要的是&lt;strong&gt;第一个和射线相交的三角面&lt;/strong&gt;，换言之是&lt;strong&gt;离相机最近的相交三角面&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;获取相交点的材质数据，进行着色。&lt;/li&gt;
&lt;li&gt;生成下一条/多条射线。&lt;/li&gt;
&lt;li&gt;对每一条射线进行相交测试、着色。&lt;/li&gt;
&lt;li&gt;循环过程直到满足结束条件，累计结果。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;通过对这几步的抽象，我们已经可以给出一个不同于传统光栅化流程的全新管线实现，事实上这就是新一代图形API提供的光线追踪管线（XXX Ray tracing pipeline）的实现，比如M$的&lt;a href=&amp;quot;https://developer.nvidia.com/blog/introduction-nvidia-rtx-directx-ray-tracing/&amp;quot;&gt;DXR&lt;/a&gt;。不同于光栅化的&lt;strong&gt;VS -&amp;gt; Rasterization -&amp;gt; FS&lt;/strong&gt;管线，这个管线被抽象为下图的形式：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/webgpu-rt/4/1.png&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;在RTX硬件的支持下，这个管线的性能是非常不错的，也可以和光栅化管线同时存在、相互协作。但显然&lt;strong&gt;现在WebGPU目前是不支持的&lt;/strong&gt;，所以我们只能寻求另一种方案——CS + GBuffer的混合方案。&lt;/p&gt;
&lt;h3&gt;CS和GBuffer混合方案&lt;/h3&gt;
&lt;p&gt;说是CS（计算着色器）和GBuffer（几何缓冲对象）的混合方案，实际上只有CS也可以。让我们考虑一下，CS使用通用逻辑单元进行计算，理论上可以完成任何渲染需要的计算，甚至是光栅化也可以，只不过在大部分情况下比较慢（但事实上已经有这样的应用了）。我们现在已经有了整个场景需要用于渲染的的所有数据，那么将它们直接传给CS，理论上当然可以完成路径追踪的计算，只不过比起专用硬件慢了一些而已。&lt;/p&gt;
&lt;p&gt;既然如此，我们又为何需要GBuffer呢？这不是画蛇添足？其实很简单——为了性能，虽然CS比起硬件专用加速方案慢，但能快一点总归是好的，而且这在原理上也是行得通的。&lt;/p&gt;
&lt;p&gt;让我们再回忆下光栅化的过程，光栅化最重要的分为两步——投影和栅格化，投影是将3D空间的三角面变换到近裁剪平面的三角形，栅格化则是将近裁剪平面的矢量三角形变换为一个个像素（片元）；再对比下路径追踪流程——根据相机投影模式，从近裁剪平面的像素发射一条条射线，找到射线相交的最近三角面上的点。&lt;/p&gt;
&lt;p&gt;相信读者已经可以觉察到了——从结果上来讲，光栅化，实际上就是路径追踪的第一步，也就是求取某个像素发出的射线和场景中三角面的第一个交点。而光栅化由于硬件的高度优化，速度是非常快的。而除了速度优势，GBuffer在后面要提到的降噪方面也有用处。&lt;/p&gt;
&lt;p&gt;所以总结来看，到此我们的渲染管线至少有两步——&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;通过光栅化流程渲染GBuffer。&lt;/li&gt;
&lt;li&gt;通过CS进行后续的路径追踪流程。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;BVH&lt;/h3&gt;
&lt;p&gt;在上面的绘制流程我们提到了第一步是&lt;strong&gt;求得射线和场景中最近的三角面的交点&lt;/strong&gt;，但并没有提到怎么做。最简单的方法当然是遍历整个场景所有三角面，但考虑到射线数量以及三角面的数量，这显然是不现实的，所以需要一个结构来进行加速，加速结构的分类会在后面讲到，这里只给结论——我最后使用了BVH，这也是业界一般的做法。&lt;/p&gt;
&lt;p&gt;由于在相交测试就会使用到BVH，所以它的构建应当先于整个CS流程。但概论中已经说了，本项目目前只针对静态场景，不涉及到Mesh的增减，所以BVH只需要在资源加载、场景组织完成后做一次即可。&lt;/p&gt;
&lt;h3&gt;降噪和色调映射&lt;/h3&gt;
&lt;p&gt;最后还需要的流程就是降噪和色调映射了。由于我们要做的是&lt;strong&gt;实时&lt;/strong&gt;路径追踪，并且由于原理（涉及到积分），每次弹射出复数射线进行积分显然是不现实的，所以只能时间换空间——利用随机采样的原理，每帧渲染出一个带有大量噪点的结果，最后进行时域滤波，再辅以空间滤波，得到相对可以接受的最终结果。这整个滤波过程就是&lt;strong&gt;降噪&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;同时路径追踪渲染出的结果最终基本都是高动态范围（HDR）的，为了让显示器能够正确显示，我们最后还要进行一个通用操作色调映射（Tone mapping），将图像亮度压缩到LDR范围，并尽可能保证细节。&lt;/p&gt;
&lt;p&gt;下图表现出了降噪前和降噪+色调映射后的结果对比：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/webgpu-rt/4/2.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;h3&gt;总结&lt;/h3&gt;
&lt;p&gt;至此，可以总结出整个路径追踪管线的组成：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;构建BVH。&lt;/li&gt;
&lt;li&gt;通过光栅化流程渲染GBuffer。&lt;/li&gt;
&lt;li&gt;通过CS进行后续的路径追踪流程。&lt;/li&gt;
&lt;li&gt;降噪。&lt;/li&gt;
&lt;li&gt;色调映射。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;GBuffer渲染&lt;/h2&gt;
&lt;p&gt;讲完了管线的组成，就让我们以GBuffer为开端，顺便实战一下之前文章论述的这个WebGPU渲染器吧。&lt;/p&gt;
&lt;h3&gt;构建Mesh&lt;/h3&gt;
&lt;p&gt;GBuffer的渲染走的是正常的光栅化流程，所以需要构建一个Mesh，这个Mesh需要的Geometry和Material可以直接使用上一篇文章合并过的场景数据：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;protected&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_buildGBufferMesh&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_attributesInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_indexInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_commonUniforms&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;geometry&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Geometry&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
    &lt;span class=&amp;quot;nb&amp;quot;&gt;Object&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;keys&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_attributesInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;map&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;((&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;value&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;length&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;format&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_attributesInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;any&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;IBVHAttributeValue&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

      &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;layout&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
          &lt;span class=&amp;quot;nx&amp;quot;&gt;arrayStride&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;length&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
          &lt;span class=&amp;quot;nx&amp;quot;&gt;attributes&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[{&lt;/span&gt;
            &lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;offset&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;format&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;shaderLocation&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;index&lt;/span&gt;
          &lt;span class=&amp;quot;p&amp;quot;&gt;}]&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;data&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;value&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;usage&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;GPUBufferUsage.STORAGE&lt;/span&gt;
      &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}),&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;_indexInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;value&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;_indexInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;value&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;length&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;material&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;buildinEffects&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rRTGBuffer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;u_matId2TexturesId&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;_commonUniforms.matId2TexturesId&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;u_baseColorFactors&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;_commonUniforms.baseColorFactors&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;u_metallicRoughnessFactorNormalScaleMaterialTypes&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;_commonUniforms.metallicRoughnessFactorNormalScaleMaterialTypes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;u_specularGlossinessFactors&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;_commonUniforms.specularGlossinessFactors&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;u_baseColorTextures&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;_commonUniforms.baseColorTextures&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;u_normalTextures&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;_commonUniforms.normalTextures&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;u_metalRoughOrSpecGlossTextures&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;_commonUniforms.metalRoughOrSpecGlossTextures&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_gBufferMesh&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Mesh&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;geometry&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;可见这里构建的Geometry没有使用交错形式，而是使用了多个Buffer，每个Buffer对应一个Attribute。而Material的UniformBlock直接使用了场景合并过的各个Uniform和Texture，同时使用了&lt;code&gt;buildinEffects.rRTGBuffer&lt;/code&gt;这个Effect。&lt;/p&gt;
&lt;h3&gt;Effect&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;buildinEffects&lt;/code&gt;是引擎内置的一些Effect，&lt;code&gt;rRTGBuffer&lt;/code&gt;如其名是用于渲染GBuffer的，前缀的&lt;code&gt;r&lt;/code&gt;表明是用于&lt;code&gt;Render&lt;/code&gt;的。对于一个Effect，最重要的就是它的Shader。&lt;/p&gt;
&lt;p&gt;首先是顶点着色器：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;struct&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;VertexOutput&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;builtin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;location&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;wPosition&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;location&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;texcoord_0&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;location&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;location&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;meshMatIndex&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;u32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;stage&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vertex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;main&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;attrs&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Attrs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;VertexOutput&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;output&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;VertexOutput&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;wPosition&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;attrs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;output&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;global&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_vp&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;wPosition&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;output&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;wPosition&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;wPosition&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;output&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;texcoord_0&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;attrs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;texcoord_0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;output&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;attrs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;output&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;meshMatIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;attrs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;meshMatIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;output&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;meshMatIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;attrs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;meshMatIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;output&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;可见比较简单，由于直接使用了合并过的顶点数据，所以不需要Model -&amp;gt; World变换，输出给光栅器重点处理的&lt;code&gt;position&lt;/code&gt;只需要乘以VP矩阵即可，剩下要用于插值的数据有世界坐标、UV和法线这三个f32向量，以及&lt;code&gt;meshMatIndex&lt;/code&gt;这个存储顶点材质索引的u32向量。&lt;/p&gt;
&lt;p&gt;之后是片段着色器：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;struct&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;VertexOutput&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;builtin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;location&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;wPosition&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;location&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;texcoord_0&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;location&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;location&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;meshMatIndex&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;u32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;struct&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;FragmentOutput&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;location&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;positionMetalOrSpec&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;location&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseColorRoughOrGloss&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;location&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normalGlass&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;location&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;meshIndexMatIndexMatType&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;u32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;getRoughness&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;factor&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureId&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureId&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;==&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;factor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;factor&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureSampleLevel&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_metalRoughOrSpecGlossTextures&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_sampler&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureId&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;g&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;getMetallic&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;factor&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureId&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureId&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;==&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;factor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;factor&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureSampleLevel&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_metalRoughOrSpecGlossTextures&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_sampler&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureId&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;getSpecular&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;factor&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureId&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureId&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;==&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;factor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;factor&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureSampleLevel&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_metalRoughOrSpecGlossTextures&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_sampler&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureId&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rgb&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;getGlossiness&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;factor&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureId&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureId&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;==&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;factor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;factor&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureSampleLevel&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_metalRoughOrSpecGlossTextures&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_sampler&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureId&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;getBaseColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;factor&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureId&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureId&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;==&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;factor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;factor&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureSampleLevel&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_baseColorTextures&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_sampler&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureId&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;getFaceNormal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normalize&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;cross&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dpdy&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dpdx&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)));&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;getNormal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vNormal&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;faceNormal&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureId&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normalScale&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normalize&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vNormal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sign&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;faceNormal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureId&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;==&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// http://www.thetenthplanet.de/archives/1180&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dp1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dpdx&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dp2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dpdy&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;duv1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dpdx&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;duv2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dpdy&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dp2perp&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;cross&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dp2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dp1perp&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;cross&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dp1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dpdu&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dp2perp&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;duv1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dp1perp&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;duv2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dpdv&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dp2perp&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;duv1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dp1perp&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;duv2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;invmax&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;inverseSqrt&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dpdu&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dpdu&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dpdv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dpdv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)));&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dpdu&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dpdu&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;invmax&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dpdv&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dpdv&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;invmax&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tbn&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;mat3x3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;mat3x3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dpdu&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dpdv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tNormal&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;2.&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureSample&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_normalTextures&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_sampler&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureId&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xyz&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tNormal&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tNormal&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normalScale&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normalScale&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normalize&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tbn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tNormal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;


&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;stage&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;fragment&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;main&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vo&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;VertexOutput&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;FragmentOutput&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;fo&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;FragmentOutput&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;meshId&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;u32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;meshMatIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;matId&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;u32&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;meshMatIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;metallicRoughnessFactorNormalScaleMaterialType&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_metallicRoughnessFactorNormalScaleMaterialTypes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;matId&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;specularGlossinessFactor&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_specularGlossinessFactors&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;matId&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureIds&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_matId2TexturesId&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;matId&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;matType&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;u32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;metallicRoughnessFactorNormalScaleMaterialType&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;isSpecGloss&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;bool&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;isMatSpecGloss&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;matType&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;fo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;positionMetalOrSpec&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;wPosition&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xyz&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseColor&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;getBaseColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_baseColorFactors&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;matId&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureIds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;texcoord_0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;fo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseColorRoughOrGloss&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rgb&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;isSpecGloss&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;fo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;positionMetalOrSpec&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;w&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;getSpecular&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;specularGlossinessFactor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xyz&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureIds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;texcoord_0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;fo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseColorRoughOrGloss&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;w&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;getGlossiness&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;specularGlossinessFactor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureIds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;texcoord_0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;fo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;positionMetalOrSpec&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;w&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;getMetallic&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;metallicRoughnessFactorNormalScaleMaterialType&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureIds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;texcoord_0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;fo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseColorRoughOrGloss&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;w&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;getRoughness&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;metallicRoughnessFactorNormalScaleMaterialType&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureIds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;texcoord_0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;faceNormal&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;getFaceNormal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;wPosition&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xyz&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;fo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normalGlass&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;getNormal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;wPosition&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xyz&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;faceNormal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;textureIds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;texcoord_0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;metallicRoughnessFactorNormalScaleMaterialType&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]),&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;baseColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;fo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;meshIndexMatIndexMatType&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;u32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;meshId&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;matId&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;matType&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;fo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;可见这个比较顶点处理就复杂多了，但其实也不难理解。从宏观角度来看，片元处理的目的就是将需要的数据写入到输出的目标纹理中，这里我们可以回忆一下在只文章&lt;strong&gt;WebGPU基础与简单渲染器&lt;/strong&gt;中讲过的&lt;code&gt;RenderTexture&lt;/code&gt;，那时候提到了它支持&lt;strong&gt;多渲染目标MRT&lt;/strong&gt;，其实就是主要用在了GBuffer中。要使用MRT，我们首先需要在CPU创建一个RenderTexture（下一节会讲），然后在FS中&lt;strong&gt;按照顺序&lt;/strong&gt;定义好&lt;code&gt;FragmentOutput&lt;/code&gt;作为它的输出，即可完成CPU和GPU两端的映射。&lt;/p&gt;
&lt;p&gt;在实际的Shader编程中，我们可以认为FS可以输出到不同的纹理，本FS就输出到了以下几个纹理中：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;positionMetalOrSpec：存储世界空间的坐标，以及金属工作流下的&lt;code&gt;metallic&lt;/code&gt;或者高光工作流下的&lt;code&gt;specular&lt;/code&gt;，注意&lt;code&gt;specular&lt;/code&gt;本来有三个通道，但这里只保留一个，这是出于节省空间，而且实际场合中其实一个通道也不是不行（逃。&lt;/li&gt;
&lt;li&gt;baseColorRoughOrGloss：存储&lt;code&gt;baseColor&lt;/code&gt;，以及金属工作流下的&lt;code&gt;roughness&lt;/code&gt;或者高光工作流下的&lt;code&gt;glossiness&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;normalGlass：存储世界空间的顶点法线&lt;code&gt;normal&lt;/code&gt;，以及透射材质的折射率的倒数&lt;code&gt;glass&lt;/code&gt;，之所以存倒数，是因为实际应用中折射率基本都大于1，而调整的时候0~1比较好调。&lt;/li&gt;
&lt;li&gt;meshIndexMatIndexMatType：存储顶点索引、材质索引、材质类型，注意这是一个u8向量，意味着整个场景支持的Mesh和材质数量最多256。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;而其中的大部分计算其实都是索引，比如通过&lt;code&gt;vo.meshMatIndex[1]&lt;/code&gt;拿到这个片元对应的材质id，然后通过&lt;code&gt;material.u_matId2TexturesId[matId]&lt;/code&gt;获取到几种纹理的id数组，再通过&lt;code&gt;textureIds[0]&lt;/code&gt;获取到对应&lt;code&gt;baseColor&lt;/code&gt;纹理的id，最后用这个id和uv去用&lt;code&gt;textureSampleLevel(u_baseColorTextures, u_sampler, uv, textureId, 0.)&lt;/code&gt;采样数组纹理，获得最终的baseColor值。&lt;/p&gt;
&lt;p&gt;在这些最终输出到纹理的数据的计算中，有几个需要特别说明：&lt;/p&gt;
&lt;h4&gt;法线计算&lt;/h4&gt;
&lt;p&gt;和其他数据不同，这里法线的计算比较复杂，因为法线本身就有其复杂性，每个着色的片元都有自己的顶点法线、面法线和可能的法线贴图采样值：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;顶点法线：顶点数据中的法线插值而来，如果没有法线贴图，就是最终的法线矢量的&lt;strong&gt;值&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;面法线：顶点所在三角面的法线，一般用于和顶点法线计算求取法线矢量最终的&lt;strong&gt;方向&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;法线贴图：由于物体的表面会有复杂的凹凸不平，全部用几何信息描述开销过高，而使用贴图来存储法线信息能有效降低开销，一般会构建一个&lt;strong&gt;切线空间&lt;/strong&gt;，将实际的顶点法线换算到其中，在渲染时还原。为何要使用切线空间读者可以自行查找，不再赘述。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;面法线的一般计算方法是利用三角面两个边向量的叉乘，在片元着色器中无法直接获取边向量的信息，但有&lt;a href=&amp;quot;https://www.w3.org/TR/WGSL/#derivatives&amp;quot;&gt;Derivative functions&lt;/a&gt;，可以用于近似计算，具体实现为上面的&lt;code&gt;getFaceNormal&lt;/code&gt;函数，这里我们计算了世界空间坐标对屏幕空间坐标的导数，最终近似求得&lt;strong&gt;世界空间的面法线&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;而对于最终法线的求取，如果没有使用法线贴图，那么直接用已有的世界空间顶点法线和面法线点乘求得方向，返回即可。但如果有法线贴图，就需要涉及到切线空间的反变换了，如果使用传统的算法，还需要提供预计算的顶点切线数据&lt;code&gt;Tangent&lt;/code&gt;，最终利用顶点法线、切线构造TBN矩阵，但由于懒得生成，这里就使用了&lt;a href=&amp;quot;http://www.thetenthplanet.de/archives/1180&amp;quot;&gt;这篇文章&lt;/a&gt;介绍的方法，其本质上是还原了切线的预计算过程。当然这也是近似，毕竟无法拿到真正的三角面的三个顶点数据。&lt;/p&gt;
&lt;h4&gt;meshIndexMatIndexMatType的alpha通道&lt;/h4&gt;
&lt;p&gt;读者可能刚才已经疑惑了为什么这个纹理的alpha通道要给个2，这其实是处于后续运算的考虑。在实际的渲染中，&lt;strong&gt;并非每个像素都会被三角面覆盖&lt;/strong&gt;，所以一些像素是没有值的，对应于路径追踪流程，就是&lt;strong&gt;这个像素射出的射线和场景物体没有任何交点&lt;/strong&gt;。至于为何是2而不是1，因为alpha会被相机clear为1，设计而已。&lt;/p&gt;
&lt;h3&gt;渲染和展示&lt;/h3&gt;
&lt;p&gt;现在我们有了GBuffer需要的Mesh，接下来只需要创建用于MRT的RenderTexture，并将它们关联起来：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_gBufferRT&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;H&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;RenderTexture&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;renderEnv.width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;renderEnv.height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;colors&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;positionMetalOrSpec&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;format&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;rgba16float&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;baseColorRoughOrGloss&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;format&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;rgba16float&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;normalGlass&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;format&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;rgba16float&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;meshIndexMatIndexMatType&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;format&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;rgba8uint&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;depthStencil&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;needStencil&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;false&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;

&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_gBufferDebugMesh&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;H&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ImageMesh&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;H&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;H&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;buildinEffects&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;iRTGShow&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setUniform&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;u_gbPositionMetalOrSpec&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_gBufferRT&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;positionMetalOrSpec&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setUniform&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;u_gbBaseColorRoughOrGloss&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_gBufferRT&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;baseColorRoughOrGloss&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setUniform&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;u_gbNormalGlass&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_gBufferRT&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;normalGlass&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setUniform&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;u_gbMeshIndexMatIndexMatType&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_gBufferRT&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;meshIndexMatIndexMatType&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;然后渲染就OK：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_scene&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setRenderTarget&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_gBufferRT&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_scene&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;renderCamera&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_camera&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_rtManager&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;gBufferMesh&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;最后我用一个&lt;code&gt;ImageMesh&lt;/code&gt;将最终的结果显示了出来，如图：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/webgpu-rt/4/3.png&amp;quot; /&gt;&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Friday, 17 Sep 2021 02:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2021.09.17 02:00:article/Skill-2021_09_17_a</guid>
<category>WebGPU</category>
<category>图形学</category>
<category>路径追踪</category>
<category>光线追踪</category>
</item>

<item>
<title>【WebGPU实时光追美少女】场景数据组织与合并</title>
<link>http://dtysky.moe/article/Skill-2021_09_15_a</link>
<description>&lt;p&gt;本系列文章设计的所有代码均已开源，Github仓库在这里：&lt;a href=&amp;quot;https://github.com/dtysky/webgpu-renderer&amp;quot;&gt;dtysky/webgpu-renderer&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;本文将在上一篇文章&lt;strong&gt;WebGPU基础与简单渲染器&lt;/strong&gt;的基础上，论述针对路径追踪，应当如何组织场景数据，这涉及到PBR材质定义、glTF资源的导出和加载、场景的构建、以及最重要的场景合并等内容。  &lt;/p&gt;
&lt;h2&gt;PBR材质&lt;/h2&gt;
&lt;p&gt;这篇文章以PBR材质开始，为什么要以材质开始？因为如上一篇文章所言，材质（以及效果）决定了物体的渲染方式，也可以从某种程度上认为是决定了使用光照模型（或者说是光照模型决定了材质和效果），这个对整个场景数据和渲染流程的组织有决定性的影响。为了搞清楚为何要如此组织数据和渲染流程，我们必须要先了解使用到的材质。&lt;/p&gt;
&lt;p&gt;PBR即“基于物理的渲染”是工业界的说法，其可以认为是基于相对物理正确的BRDF、BSDF等模型，根据不同的场合，抽象了一些参数供美术人员调节，并加上一些技术（比如用于实时渲染的预计算IBL、SH技术等等）来达到相对物理真实的效果。不过这里我们&lt;strong&gt;不讨论背后实现的理论细节，这些细节将在后续的章节详细论述&lt;/strong&gt;，本文只需要了解这些参数和工作流即可。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;事实上，在光栅化渲染流程如果单纯使用PBR材质进行直接光照，而不使用IBL/SH进行间接光照，相对于传统的光照模型，并不能带来多大的提升，所以Baking是十分重要的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;根源上来讲，PBR材质只是一个统称，其有很多变种，这些变种一般被称为工作流（Workflow）。同时这些变种也有不同的定义和实现，比如著名的迪士尼的&lt;strong&gt;Disney Principled BRDF&lt;/strong&gt;。而本引擎的定义则基于后续要提到的glTF资源中的标准定义，支持了两种最常见的工作流——金属（MetallicRoughness）和高光（SpecularGlossiness）工作流。&lt;/p&gt;
&lt;h3&gt;Metallic Roughness&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/webgpu-rt/3/0.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;金属工作流，是指主要通过金属度和粗糙度来控制渲染效果，这实际上模拟的是自然界的&lt;strong&gt;导体&lt;/strong&gt;，也有一些其他参数来共同决定最终效果：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Metallic：金属度，由单通道贴图&lt;code&gt;metallicMap&lt;/code&gt;和系数&lt;code&gt;metallicFactor&lt;/code&gt;相乘得到。&lt;/li&gt;
&lt;li&gt;Roughness：粗糙度，由单通道贴图&lt;code&gt;roughnessMap&lt;/code&gt;和系数&lt;code&gt;roughnessFactor&lt;/code&gt;相乘得到。&lt;/li&gt;
&lt;li&gt;BaseColor：反照率，有的引擎也称为&lt;code&gt;Albedo&lt;/code&gt;，由RGBA贴图&lt;code&gt;baseColorMap&lt;/code&gt;和颜色系数&lt;code&gt;baseColorFactor&lt;/code&gt;相乘得到。&lt;/li&gt;
&lt;li&gt;Occlusion：环境光遮蔽，由单通道贴图&lt;code&gt;occlusionMap&lt;/code&gt;和系数&lt;code&gt;occlusionFactor&lt;/code&gt;相乘得到。&lt;/li&gt;
&lt;li&gt;Normal：法线贴图，一般是切线空间的法线贴图&lt;code&gt;normalMap&lt;/code&gt;和系数&lt;code&gt;normalScale&lt;/code&gt;计算得到。&lt;/li&gt;
&lt;li&gt;Emission：自发光，由RGBA贴图&lt;code&gt;emissiveMap&lt;/code&gt;和颜色系数&lt;code&gt;emissiveFactor&lt;/code&gt;相乘得到。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;在实际使用中，&lt;code&gt;metallicMap&lt;/code&gt;、&lt;code&gt;roughnessMap&lt;/code&gt;和&lt;code&gt;occlusionMap&lt;/code&gt;一般会合并成一张RGB贴图。&lt;/p&gt;
&lt;h3&gt;Specular Glossiness&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/webgpu-rt/3/1.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;除了金属度和粗糙度，高光工作流的大部分参数和金属工作流都是通用的，但高光和光泽度是独有的，也是控制渲染的最主要因素：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Specular：高光，由RGB贴图&lt;code&gt;specularMap&lt;/code&gt;和颜色系数&lt;code&gt;specularFactor&lt;/code&gt;相乘得到。&lt;/li&gt;
&lt;li&gt;Glossiness：光泽度，由单通道贴图&lt;code&gt;glossinessMap&lt;/code&gt;和系数&lt;code&gt;glossinessFactor&lt;/code&gt;相乘得到。&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;这里要注意除了BaseColor和Emissive一般是SRGB空间的，其他都是线性空间的，并且在实际计算时都是转到线性空间计算的，这也是物理正确的要求。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;glTF资源&lt;/h2&gt;
&lt;p&gt;有了材质的定义，接下来的问题就是如何将它们应用到物体上了，这又引出了更多的问题——我们该如何组织整个场景？又如何去存储它们、如何去生成它们？答案很简单——使用glTF格式。&lt;/p&gt;
&lt;p&gt;glTF是KhronosGroup近年来提出的一种标准模型格式，从某种意义上你可以认为它是3D场景序列化的一种标准解决方案。其方案非常简单明确，基本由一个存储节点、曲面、材质、纹理、动画等等索引信息的json格式的.gltf文件，同时还提供了一个二进制.bin文件用于存储实际的顶点、动画信息等，而这个二进制文件中的数据和GL所需格式基本一致，这大幅降低加载延迟、提升了首屏速度。更多详细的信息可以看这里——&lt;a href=&amp;quot;https://seinjs.com/cn/guide/scene-editor/gltf&amp;quot;&gt;Sein.js glTF和实例化&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;读者可能会疑惑为何这里会提及另一个引擎，因为这个引擎是我还在支付宝供职时研发的（当然已开源），同时还为其开发了配套的Unity扩展——&lt;a href=&amp;quot;https://github.com/hiloteam/SeinJSUnityToolkit&amp;quot;&gt;SeinjsUnityToolkit&lt;/a&gt;，而本引擎也可以直接使用这个工具导出的资源，也算是物尽其用吧，但相比于工具支持的所有特性，本引擎只用到了需要的部分：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;贴图。&lt;/li&gt;
&lt;li&gt;2D和Cube纹理。&lt;/li&gt;
&lt;li&gt;PBR材质，主要是glTF官方内置的金属工作流，以及通过&lt;code&gt;KHR_materials_pbrSpecularGlossiness&lt;/code&gt;扩展实现的高光工作流。&lt;/li&gt;
&lt;li&gt;图元和Mesh，直接对应引擎中的&lt;code&gt;Geometry&lt;/code&gt;和&lt;code&gt;Mesh&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;相机，支持正交和透视。&lt;/li&gt;
&lt;li&gt;灯光，在路径追踪中主要用到了&lt;code&gt;rect&lt;/code&gt;和&lt;code&gt;disc&lt;/code&gt;两种面光源。&lt;/li&gt;
&lt;li&gt;结点，用于构建节点树。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;整个加载器的代码的实现在&lt;code&gt;resource/GlTFLoader内&lt;/code&gt;，实际使用中，只需要调用&lt;code&gt;load&lt;/code&gt;方法，之后将返回的结果中的根节点&lt;code&gt;rootNode&lt;/code&gt;添加给场景的根节点即可：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;model&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_model&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;await&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;H&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;resource&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;load&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;gltf&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;scene.gltf&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;src&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;MODEL_SRC&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;model&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cameras&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;length&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_camera&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;model&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cameras&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;_scene&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rootNode&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;addChild&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;model&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rootNode&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;场景合并&lt;/h2&gt;
&lt;p&gt;加载完资源并将其添加到场景后，我们有了一整颗节点树，节点树上也有了不同的&lt;code&gt;Mesh&lt;/code&gt;、&lt;code&gt;Light&lt;/code&gt;、&lt;code&gt;Camera&lt;/code&gt;，而&lt;code&gt;Mesh&lt;/code&gt;则拥有&lt;code&gt;Geometry&lt;/code&gt;和&lt;code&gt;Material&lt;/code&gt;，&lt;code&gt;Material&lt;/code&gt;全部是PBR材质，并使用UniformBlock将需要的参数管理了起来。如果是传统的光栅化流程，我们可以直接开始渲染——Camera剔除出需要的Mesh，将lightInfo、VP矩阵等写入全局UB，之后分别绘制每个Mesh.....但对于路径追踪流程，却不能这么做，原因很简单——信息不足。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/webgpu-rt/3/2.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;上面这张图生动地描述了两个流程的差异（当然第二个实际上是光线追踪流程，不过用于描述差距还是足够了，把射线反过来就OK）。&lt;/p&gt;
&lt;p&gt;光栅化流程实际上是将场景中的每个三角面投影到近裁剪面上，然后通过算法将矢量的三角形栅格化为像素（片元），之后经过重心坐标插值、像素着色、各种测试、混合等等决定屏幕空间某些像素最后的颜色值，这实际上是反直觉的，并且只能计算直接光照。&lt;/p&gt;
&lt;p&gt;而路径追踪则不同，其出发点并非三角形，而是屏幕空间的像素，我们通过相机坐标-&amp;gt;像素对应世界空间坐标生成一条世界空间的射线，然后让其和场景中所有三角面求交，找到最近的相交点后再通过反射/折射计算光路，最终可以通过N次弹射，最终收集完整个场景对此像素的贡献。除了部分场景（比如焦散），单向的路径追踪可以比较准确地计算出全局光照的结果。&lt;/p&gt;
&lt;p&gt;那么这些和场景合并又有什么关系呢？当然有——路径追踪需要在每个射线求交的过程中&lt;strong&gt;拥有整个场景所有三角面的信息&lt;/strong&gt;，除此之外，由于交点可能存在于任意三角面上，所以也需要能够&lt;strong&gt;即时获取到三角面对应的材质信息&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;有经验的读者可能已经联想到了，这个特征其实和工业界讨论许久的&lt;strong&gt;GPU Driven&lt;/strong&gt;有些相似。&lt;/p&gt;
&lt;h3&gt;GPU Driven Rendering Pipeline&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;GPU Driven Rendering&lt;/strong&gt;指的是一类渲染技术，其根本目的是为了充分利用GPU的计算能力，使用Bindless Resource、Virtual Texture、Indirect Draw等等技术，尽可能减少渲染过程中CPU和GPU之间数据传输、调用的cost，还可以充分控制利用cache，来提升性能。这里最主要的就是上一篇文章前面提到过剔除、资源绑定、绘制等等。此技术的细节比较复杂，需要很多工程上的工作，有兴趣的读者可以自行搜索。对于本文我们只需要关心它和路径追踪相关的内容，也就是——资源绑定部分。&lt;/p&gt;
&lt;p&gt;资源绑定在WebGPU中，实质上就是上篇文章提到&lt;code&gt;pass.setIndexBuffer&lt;/code&gt;、&lt;code&gt;pass.setVertexBuffer&lt;/code&gt;、&lt;code&gt;pass.setBindingGroup&lt;/code&gt;等等方法，CPU通过这些方法告诉GPU我们接下来的绘制指令将会使用哪些顶点数据、Uniform数据......之后进行渲染。但因为硬件结构和软件设计，资源绑定操作是有开销的，一部分是指令本身、另一部分就是GPU中的计算单元获取这些资源对应Buffer的机制，那么如何尽量减少这些操作便是一个可以考虑的问题。&lt;/p&gt;
&lt;p&gt;这里我们可以回忆一下Instanced Drawing，即GPU实例化的机制，这个机制它提供一个一次性绘制一批相同图元、相同渲染状态，但可以有不同位置、颜色等等的Mesh的方法。无论是使用&lt;code&gt;InstanceBuffer&lt;/code&gt;，还是使用&lt;code&gt;instanceId&lt;/code&gt; + &lt;code&gt;const buffer&lt;/code&gt;/&lt;code&gt;texture array&lt;/code&gt;的机制，本质上都是将这些Mesh中不同的Uniform（比如worldMatrix、color）转换成&lt;strong&gt;可以按照实例索引&lt;/strong&gt;的数据，然后按照实例的维度去索引。这实际上是以在CPU对这些数据打包为代价，降低了实际渲染时绑定和绘制指令的大幅降低（当然，绑定这个操作很久之前就已经可以通过&lt;strong&gt;VAO&lt;/strong&gt;来降低，在新的技术里也有&lt;strong&gt;Bindless Resource&lt;/strong&gt;来降低）。&lt;/p&gt;
&lt;p&gt;那么在这个基础上进一步考虑，我们是否可以将图元、某些渲染状态、大量Uniform都不一致的Mesh一批渲染完成呢？当然可以——这就是合批（Batch）技术。在目前常见的光栅化渲染管线（比如ForwardBase）中，合批一般指将相同渲染状态、只有少数Uniform不同的Mesh进行合并的技术，一般在简单的图元、UI上比较常用，其原理是Mesh的某些顶点数据和对应的uniform预先在CPU处理好（比如position和worldMatrix相乘得到worldPos），并将这些Mesh图元数据合并，被合批的uniform统一，便可以实现一次DrawCall绘制大量Mesh。&lt;/p&gt;
&lt;p&gt;GPU实例化和合批在目前的游戏中都有大量应用，而GPU Driven的资源部分可以认为是基于它们和一些新技术，做得更加彻底、做了更多优化、也更为复杂。本项目用于路径追踪的部分实际上也可以认为结合了这两者，但毕竟和光栅化流程面对的问题不完全相同，所以做法也不完全一致。&lt;/p&gt;
&lt;h3&gt;顶点合并&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;整个路径追踪场景数据管理相关的代码都在&lt;code&gt;extension/RayTracingManager&lt;/code&gt;中。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;首先要做的就是顶点数据&lt;code&gt;vertexData&lt;/code&gt;的合并，这个流程和传统的合批流程基本一致，将位置&lt;code&gt;position&lt;/code&gt;和法线&lt;code&gt;normal&lt;/code&gt;转换到世界空间，其他的直接拼接即可。如果只是为了合并，其实也可以不转换，然后在下面的流程将&lt;code&gt;worldMatrix&lt;/code&gt;合并，通过索引后续计算也行，但由于构建BVH必须要使用世界空间的坐标，这里也就必须要合并，至于法线也只是顺带算了。在合并顶点数据的过程中，还需要同时构建索引数据&lt;code&gt;indexData&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;protected&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_buildAttributeBuffers&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;meshes&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Mesh&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[])&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_materials&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

  &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;indexCount&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;vertexCount&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;meshes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;forEach&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;mesh&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;vertexCount&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;mesh&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;geometry&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;vertexCount&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;indexCount&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;mesh&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;geometry&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;count&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;

  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;value&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;indexes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_indexInfo&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;value&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Uint32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;indexCount&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;

  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;texcoord_0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;meshMatIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_attributesInfo&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;c1&amp;quot;&gt;// alignment of vec3 is 16bytes!&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;value&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;vertexCount&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;length&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;format&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;float32x3&amp;#39;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;texcoord_0&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;value&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;vertexCount&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;length&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;format&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;float32x2&amp;#39;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;c1&amp;quot;&gt;// alignment of vec3 is 16bytes!&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;value&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;vertexCount&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;length&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;format&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;float32x3&amp;#39;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;meshMatIndex&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;value&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Uint32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;vertexCount&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;length&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;format&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;uint32x2&amp;#39;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;

  &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;attrOffset&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;indexOffset&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;meshIndex&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;meshIndex&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;meshes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;length&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;meshIndex&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;mesh&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;meshes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;meshIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;worldMat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;mesh&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;quat&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;mat4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getRotation&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;worldMat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;geometry&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;mesh&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;indexData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;vertexInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;vertexCount&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;count&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;geometry&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;effect&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!==&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;rPBR&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;k&amp;quot;&gt;throw&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;Error&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;Only support Effect rPBR!&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;materialIndex&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_materials&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;indexOf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;materialIndex&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;_materials&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;push&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;materialIndex&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_materials&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;length&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;nx&amp;quot;&gt;indexData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;forEach&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;((&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;value&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;indexes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;indexOffset&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;value&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;attrOffset&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;vertexInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;geometry&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;calculateNormals&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;vertexCount&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_copyAttribute&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;vertexInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;attrOffset&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;worldMat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
      &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_copyAttribute&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;vertexInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;texcoord_0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;texcoord_0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;attrOffset&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
      &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_copyAttribute&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;vertexInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;attrOffset&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;quat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

      &lt;span class=&amp;quot;nx&amp;quot;&gt;meshMatIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;value&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;set&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;([&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;meshIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;materialIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;attrOffset&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;meshMatIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;length&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;nx&amp;quot;&gt;indexOffset&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;count&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;attrOffset&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;vertexCount&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;有读者应该注意到了打包的除了传统的那些顶点数据，还有&lt;code&gt;meshMatIndex&lt;/code&gt;，这其实是记录了当前顶点属于哪个Mesh（meshIndex），拥有哪个材质（matIndex）。这是因为光有点点数据并不足以支撑整个渲染流程，所以需要通过某个顶点获取到对应的材质。于是我借鉴上面说到的GPU实例化思想——通过&lt;strong&gt;材质ID&lt;/strong&gt;来索引，这也就引出了“材质合并”。&lt;/p&gt;
&lt;h3&gt;材质合并&lt;/h3&gt;
&lt;p&gt;所谓材质合并，其实是将材质中的UniformBlock进行合并（Mesh级别的UB目前只有&lt;code&gt;worldMatrix&lt;/code&gt;，已经在上一步合过了），来准备给每个顶点的&lt;code&gt;matIndex&lt;/code&gt;索引出渲染需要的参数。前面提到过我们使用的都是PBR材质，所以合并的也就是这些参数：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;protected&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_buildCommonUniforms&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;materials&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[])&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;matId2TexturesId&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Int32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;materials&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;length&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fill&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;baseColorFactors&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;materials&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;length&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fill&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;metallicRoughnessFactorNormalScaleMaterialTypes&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;materials&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;length&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fill&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;specularGlossinessFactors&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;materials&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;length&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fill&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;baseColorTextures&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Texture&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[];&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;normalTextures&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Texture&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[];&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;metalRoughOrSpecGlossTextures&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Texture&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[];&lt;/span&gt;

  &lt;span class=&amp;quot;nx&amp;quot;&gt;materials&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;forEach&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;((&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;mat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;useGlass&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;mat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;marcos&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;USE_GLASS&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;useSpecGloss&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;mat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;marcos&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;USE_SPEC_GLOSS&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;baseColorFactor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;mat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getUniform&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;u_baseColorFactor&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;metallicFactor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;mat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getUniform&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;u_metallicFactor&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;roughnessFactor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;mat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getUniform&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;u_roughnessFactor&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;specularFactor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;mat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getUniform&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;u_specularFactor&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;glossinessFactor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;mat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getUniform&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;u_glossinessFactor&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;normalScale&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;mat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getUniform&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;u_normalTextureScale&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;baseColorTexture&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;mat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getUniform&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;u_baseColorTexture&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Texture&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;normalTexture&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;mat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getUniform&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;u_normalTexture&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Texture&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;metallicRoughnessTexture&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;mat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getUniform&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;u_metallicRoughnessTexture&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Texture&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;specularGlossinessTexture&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;mat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getUniform&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;u_specularGlossinessTexture&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Texture&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;mid&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;baseColorTexture&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!==&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;buildinTextures&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;empty&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_setTextures&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;mid&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;baseColorTextures&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;baseColorTexture&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;matId2TexturesId&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;normalTexture&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!==&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;buildinTextures&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;empty&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_setTextures&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;mid&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;normalTextures&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;normalTexture&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;matId2TexturesId&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;baseColorFactor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;baseColorFactors&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;set&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;baseColorFactor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;normalScale&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!==&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;undefined&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;metallicRoughnessFactorNormalScaleMaterialTypes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;set&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;normalScale&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;slice&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;useSpecGloss&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;specularFactor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!==&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;undefined&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;specularGlossinessFactors&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;set&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;specularFactor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;slice&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;glossinessFactor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!==&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;undefined&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;specularGlossinessFactors&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;set&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;glossinessFactor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;slice&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;specularGlossinessTexture&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!==&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;buildinTextures&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;empty&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_setTextures&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;mid&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;metalRoughOrSpecGlossTextures&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;specularGlossinessTexture&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;matId2TexturesId&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;metallicRoughnessFactorNormalScaleMaterialTypes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;set&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;([&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;useGlass&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;EPBRMaterialType.GLASS_SPEC_GLOSS&lt;/span&gt; : &lt;span class=&amp;quot;kt&amp;quot;&gt;EPBRMaterialType.SPEC_GLOSS&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;metallicFactor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!==&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;undefined&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;metallicRoughnessFactorNormalScaleMaterialTypes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;set&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;metallicFactor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;slice&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;roughnessFactor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!==&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;undefined&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;metallicRoughnessFactorNormalScaleMaterialTypes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;set&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;roughnessFactor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;slice&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;metallicRoughnessTexture&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!==&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;buildinTextures&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;empty&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_setTextures&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;mid&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;metalRoughOrSpecGlossTextures&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;metallicRoughnessTexture&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;matId2TexturesId&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;metallicRoughnessFactorNormalScaleMaterialTypes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;set&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;([&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;useGlass&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;EPBRMaterialType.GLASS_METAL_ROUGH&lt;/span&gt; : &lt;span class=&amp;quot;kt&amp;quot;&gt;EPBRMaterialType.METAL_ROUGH&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_commonUniforms&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;matId2TexturesId&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;baseColorFactors&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;metallicRoughnessFactorNormalScaleMaterialTypes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;specularGlossinessFactors&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;baseColorTextures&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;this._generateTextureArray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;baseColorTextures&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;normalTextures&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;this._generateTextureArray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;normalTextures&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;metalRoughOrSpecGlossTextures&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;this._generateTextureArray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;metalRoughOrSpecGlossTextures&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;可以看到合并的逻辑也比较简单，唯一注意的是合并的规则——都是尽量凑齐16字节对齐的。同时除了材质本身固有的基本参数，还有&lt;code&gt;materialType&lt;/code&gt;和&lt;code&gt;matId2TexturesId&lt;/code&gt;。&lt;code&gt;materialType&lt;/code&gt;用于表示材质的类型，分为不透明金属、透明金属、不透明高光、透明高光，不同材质对应的光照计算流程不同。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;matId2TexturesId&lt;/code&gt;记录的是该材质到纹理id的索引。通过顶点找到材质需要索引，那么通过材质找到需要用到的纹理也需要索引，这是由于不同材质可能复用同一张纹理资源，所以需要同样对纹理进行合并。&lt;/p&gt;
&lt;h3&gt;纹理合并&lt;/h3&gt;
&lt;p&gt;纹理合并即将不同的纹理合并成一个可以索引的集合，其并没有看起来那么简单。目前工业界比较成熟和推荐的方案是使用&lt;strong&gt;Bindless Texture&lt;/strong&gt;，但WebGPU并不支持，另外妥协的方案有两个：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;大纹理：创建一张很大的纹理，通过UV映射将需要的纹理分配在大纹理的一个个子区域，存下offset和size来进行采样。&lt;/li&gt;
&lt;li&gt;ArrayTexture：创建数组纹理，直接在shader中通过索引采样。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;二者各有各的缺点，大纹理的缺点主要是容量有限，对于很多最大支持&lt;code&gt;2048 x 2048&lt;/code&gt;的平台很容易超，并且即便是支持不同大小的纹理，也容易产生空间的浪费，当然最主要的还是容量问题。ArrayTexture主要就是要求纹理尺寸必须一致了，但胜在使用方便不太需要担心容量超过。本项目使用的是ArrayTexture：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;protected&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_generateTextureArray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;textures&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Texture&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[])&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Texture&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

  &lt;span class=&amp;quot;nx&amp;quot;&gt;textures&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;forEach&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;tex&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;Math&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;tex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;Math&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;tex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;

  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;images&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;textures&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;map&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;tex&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;tex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;tex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;tex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;source&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;TTextureSource&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;tex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;source&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;instanceof&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;ImageBitmap&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;k&amp;quot;&gt;throw&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;Error&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;Can only resize image bitmap!&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;RayTracingManager&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;RESIZE_CANVAS&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;RayTracingManager&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;RESIZE_CANVAS&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;document&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;createElement&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;canvas&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;RayTracingManager&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;RESIZE_CANVAS&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2048&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;RayTracingManager&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;RESIZE_CANVAS&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2048&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;RayTracingManager&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;RESIZE_CTX&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;RayTracingManager&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;RESIZE_CANVAS&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getContext&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;2d&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;ctx&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;RayTracingManager&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;RESIZE_CTX&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;ctx&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;drawImage&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;tex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;source&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;ImageBitmap&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;ctx&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getImageData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;data&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;buffer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;})&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Texture&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;images&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;textures&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;format&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;至此，整个场景合并部分完成。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Wednesday, 15 Sep 2021 00:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2021.09.15 00:00:article/Skill-2021_09_15_a</guid>
<category>WebGPU</category>
<category>图形学</category>
<category>路径追踪</category>
<category>光线追踪</category>
</item>

<item>
<title>【WebGPU实时光追美少女】WebGPU基础与简单渲染器</title>
<link>http://dtysky.moe/article/Skill-2021_09_13_a</link>
<description>&lt;p&gt;本系列文章设计的所有代码均已开源，Github仓库在这里：&lt;a href=&amp;quot;https://github.com/dtysky/webgpu-renderer&amp;quot;&gt;dtysky/webgpu-renderer&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;WebGPU作为Web平台的下一代图形API标准（当然不仅仅是图形），提供了类似于DX12、Metal、Vulkan对GPU深入控制的能力，在经过了数个版本的迭代后，其在当下时点（2021年9月）终于基本稳定，可见&lt;a href=&amp;quot;https://www.w3.org/TR/webgpu&amp;quot;&gt;WebGPU标准&lt;/a&gt;，其使用的着色器语言&lt;strong&gt;wgsl&lt;/strong&gt;也基本稳定，详见&lt;a href=&amp;quot;https://www.w3.org/TR/WGSL&amp;quot;&gt;WebGPU Shading Language&lt;/a&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;WTF，就在我写这篇文章这一周（2021年9月第二周）标准又有更新，写完发现原来的代码跑不起来了......待我修一修。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在本文中，笔者将论述如何用WebGPU来实现一个简单的渲染器，并且目前只支持静态物体渲染，没有动画物理等等组件，这个渲染器的架构脱胎于笔者过去研究和实现过的几个渲染引擎，以求&lt;strong&gt;尽量简单&lt;/strong&gt;，而非&lt;strong&gt;大而全&lt;/strong&gt;，所以实现中可能有粗糙之处，但作为如何使用WebGPU的例子而言是绝对足够的。&lt;/p&gt;
&lt;h2&gt;概述&lt;/h2&gt;
&lt;p&gt;在设计一个渲染引擎，或者说渲染器的时候，我们最关心的问题毫无疑问是“渲染”，无论如何设计，首要考虑的都是“渲染是如何被驱动的”。一般来讲，目前通用的渲染驱动方案大致都是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;一帧开始 -&amp;gt; 组织提取渲染数据 -&amp;gt; 提交数据更改到GPU -&amp;gt; 设置渲染目标并清屏 -&amp;gt; 提交绘制指令 -&amp;gt; 一帧结束&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;对应于比较详细的工程概念，帧的调度可以视作是Web中的&lt;code&gt;requestAnimationFrame&lt;/code&gt;，以每秒30、60或者120帧驱动；组织提取渲染数据可以分为两部分，一部分是渲染所需资源的管理，另一部分是“剔除”，即通过某种手段在整个场景中筛选出真正需要渲染的物体；而提交必要数据到GPU，则是指渲染过程中可能有一些数据会被更新，比如模型数据、Uniform等等，这时候就需要把这些数据更新上传到GPU；然后是设置渲染目标为画布或者主屏，并进行颜色、深度和模板缓冲区的清除；最后就是提交绘制指令，即进行实际的绘制。&lt;/p&gt;
&lt;p&gt;为了实现以上整个驱动的过程，我们首先需要有一个用于渲染的环境，这个环境用于管理Canvas、Context以及一些全局的变量；其次是需要一些资源，这些资源管理着所有渲染所需要的数据；之后还需要一个场景结构和容器，来将这些渲染数据组装管理起来，方便剔除和绘制，与此同时还需要相机来提供观察场景的视角，需要灯光来让场景更加真实；最终还需要一种数据格式用于存储模型数据，还需要对应的加载器来它转换为我们需要的资源数据。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;以下内容需要参考文首提到的项目源代码同步观看。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;HObject&lt;/h3&gt;
&lt;p&gt;在真正开始讲述渲染部分的原理之前，要大概说一下整个工程的基本架构。这里我采用的是一个比较简单的继承式OO，对于工程中的每个类，都拥有统一的基类，并遵循统一的模式。这个基类叫做&lt;code&gt;HObject&lt;/code&gt;（至于为何是&lt;code&gt;H&lt;/code&gt;Object，那当然是因为青年H的原因咯）：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;default&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;class&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;HObject&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;static&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;CLASS_NAME&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;HObject&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;isHObject&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;boolean&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;get&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;id&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;get&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;hash&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;每个继承自&lt;code&gt;HObject&lt;/code&gt;的类都需要提供&lt;code&gt;static CLASS_NAME&lt;/code&gt;、&lt;code&gt;isXXXX&lt;/code&gt;，并会通过&lt;code&gt;CLASS_NAME&lt;/code&gt;自动生成&lt;code&gt;id&lt;/code&gt;和&lt;code&gt;hash&lt;/code&gt;，还有可选的&lt;code&gt;name&lt;/code&gt;方便Debug。&lt;/p&gt;
&lt;p&gt;而有了&lt;code&gt;isXXXX&lt;/code&gt;这种标示，还可以避免使用&lt;code&gt;instanceof&lt;/code&gt;而是使用谓词判断类型，比如：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;default&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;class&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;RenderTexture&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;extends&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;HObject&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;static&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;IS&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;value&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;any&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;value&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;is&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;RenderTexture&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!!&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;value&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;isRenderTexture&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

  &lt;span class=&amp;quot;kr&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;static&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;CLASS_NAME&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;RenderTexture&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;isRenderTexture&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;boolean&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;便可以通过&lt;code&gt;RenderTexture.IS(obj)&lt;/code&gt;来判断是否为&lt;code&gt;obj&lt;/code&gt;是否为&lt;code&gt;RenderTexture&lt;/code&gt;，无论是运行时还是编译期。&lt;/p&gt;
&lt;h2&gt;渲染环境&lt;/h2&gt;
&lt;p&gt;渲染环境在引擎中的实现是&lt;code&gt;core/renderEnv&lt;/code&gt;，其实现比较简单，主要是通过Canvas初始化整个WebGPU的Context，同时管理全局Uniform和全局宏，Uniform和宏后面资源的死后会说到，这里先主要关注最重要的&lt;code&gt;init&lt;/code&gt;方法实现：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;async&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;init&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;canvas&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;HTMLCanvasElement&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;navigator&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;gpu&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;throw&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;Error&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;WebGPU is not supported!&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;adapter&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;await&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;navigator&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;gpu&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;requestAdapter&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;adapter&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;throw&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;Error&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;Require adapter failed!&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_device&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;await&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;adapter&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;requestDevice&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_device&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;throw&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;Error&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;Require device failed!&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_canvas&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;canvas&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_ctx&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;canvas&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getContext&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;webgpu&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;any&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;||&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;canvas&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getContext&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;gpupresent&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;GPUCanvasContext&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_ctx&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;configure&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;device&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;this._device&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;format&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;this._swapChainFormat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这里可以看出WebGPU的Context的初始化方式，我们首先要检查&lt;code&gt;navigator&lt;/code&gt;上是否有&lt;code&gt;gpu&lt;/code&gt;实例，以及&lt;code&gt;gpu.requestAdapter&lt;/code&gt;是否能够返回值，然后再看是否能够用&lt;code&gt;adapter.requestDevice&lt;/code&gt;分配到设备，这个设备可以认为是图形硬件的一个抽象，其可以把实现和底层硬件隔离，有了这个中间层，便可以做各种兼容和适配。&lt;/p&gt;
&lt;p&gt;有了&lt;code&gt;device&lt;/code&gt;，还需要一个Context，这可以通过传入的&lt;code&gt;canvas&lt;/code&gt;使用&lt;code&gt;getContext(&amp;apos;webgpu&amp;apos;)&lt;/code&gt;获取，获取了Context后，便可以使用&lt;code&gt;ctx.configure&lt;/code&gt;来配置SwapChain，注意这里使用到的&lt;code&gt;format&lt;/code&gt;一般默认为&lt;code&gt;&amp;apos;bgra8unorm&amp;apos;&lt;/code&gt;。&lt;/p&gt;
&lt;h2&gt;场景和容器&lt;/h2&gt;
&lt;p&gt;根据一开始的论述，读者可能认为接下来就要讲资源的实现，接着才是场景和容器，但这种论述其实是反直觉的。对于了解一个渲染引擎的设计而言，自顶向下才是最优解，下面我会先从场景的组织讲起，然后是渲染的驱动，再到渲染和计算单元，最后才是各个资源的细节。&lt;/p&gt;
&lt;h3&gt;Node&lt;/h3&gt;
&lt;p&gt;虽然是自顶向下，但Node，即节点还是要放在最开始说的。由于本文不是3D渲染的科普贴，就不再去说MVP矩阵这种入门知识了。简单来讲，Node就是描述3D场景中某个物体位置信息的基础，其拥有平移&lt;code&gt;pos&lt;/code&gt;、旋转&lt;code&gt;quat&lt;/code&gt;、缩放&lt;code&gt;scale&lt;/code&gt;三个属性，并拥有父级&lt;code&gt;parent&lt;/code&gt;和子级&lt;code&gt;children&lt;/code&gt;来进行级联，级联后即为树状结构的&lt;strong&gt;节点树&lt;/strong&gt;，这也就是&lt;strong&gt;场景&lt;/strong&gt;的基础：&lt;/p&gt;
&lt;p&gt;节点树将会在每一帧驱动的时候通过深度优先搜索&lt;code&gt;dfs&lt;/code&gt;来从根节点遍历每个节点，调用&lt;code&gt;updateMatrix&lt;/code&gt;进行自顶向下的世界矩阵&lt;code&gt;worldMat&lt;/code&gt;更新。成熟的渲染引擎都会在节点树刷新这一步做很多优化，比如脏检查，但本项目没有考虑这些，每帧全量刷新。&lt;/p&gt;
&lt;p&gt;节点是非常重要的单元，其也是相机、灯光、渲染单元的基类。&lt;/p&gt;
&lt;h3&gt;Scene&lt;/h3&gt;
&lt;p&gt;有了Node，场景Scene就自然而然构成了。所有的渲染引擎基本都有场景的概念，只不过功能可能不同。而本引擎为了方便，直接把渲染驱动的能力给了它。所以本引擎的Scene主要有两个功能——管理场景，以及管理绘制流程。&lt;/p&gt;
&lt;p&gt;对于场景的管理主要在于节点树，节点树管理的实现很简单，场景内部持有了一个&lt;code&gt;rootNode&lt;/code&gt;，直接将自己新建的节点作为&lt;code&gt;rootNode&lt;/code&gt;的子级即可将其加入场景。但场景管理不限于此，在每帧从&lt;code&gt;rootNode&lt;/code&gt;自顶向下刷新整棵节点树的时候，我们还可以收集一些渲染必要的信息，比如&lt;strong&gt;当帧可供剔除的渲染单元列表&lt;code&gt;meshes&lt;/code&gt;&lt;/strong&gt;和&lt;strong&gt;当帧需要的灯光列表&lt;code&gt;lights&lt;/code&gt;&lt;/strong&gt;：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_rootNode&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;updateSubTree&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;node&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Mesh&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;IS&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_meshes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;push&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Light&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;IS&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_lights&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;push&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;关于这二者的时候马上就会讲到。&lt;/p&gt;
&lt;p&gt;那么现在有了场景，我们如何驱动渲染呢，渲染当然需要一些资源，但也需要流程将它们组织起来。在文章的一开始已经提到过一个渲染流程应该是如何的，那么其实直接将它们翻译成接口给Scene即可：&lt;/p&gt;
&lt;p&gt;1.&lt;code&gt;startFrame(deltaTime: number)&lt;/code&gt;：&lt;/p&gt;
&lt;p&gt;一帧开始，执行节点树刷新、收集信息，并设置全局的Uniform，比如&lt;code&gt;u_lightInfos&lt;/code&gt;、&lt;code&gt;u_gameTime&lt;/code&gt;等。然后是最重要的：创建&lt;code&gt;GPUCommandEncoder&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_command&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;renderEnv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;device&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;createCommandEncoder&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;code&gt;GPUCommandEncoder&lt;/code&gt;顾名思义，是WebGPU中用于指令编码的管理器。一般每一帧都会重新创建一个，并在一帧结束时完成它的生命周期。这也是这一代图形API的标准设计，但其实类似思想早就存在于一些成熟的游戏引擎中，比如UE。&lt;/p&gt;
&lt;p&gt;2.&lt;code&gt;setRenderTarget(target: RenderTexture | null)&lt;/code&gt;：&lt;/p&gt;
&lt;p&gt;这个接口用于设置渲染目标，如果是&lt;code&gt;RenderTexture&lt;/code&gt;资源，那么接下来执行的所有绘制指令都会会知道这个RT上，如果是null则会会知道主屏的缓冲区。&lt;/p&gt;
&lt;p&gt;3.&lt;code&gt;cullCamera(camera: Camera): Mesh[]&lt;/code&gt;：&lt;/p&gt;
&lt;p&gt;使用某个相机的&lt;code&gt;camera.cull&lt;/code&gt;进行剔除，返回一个Mesh列表，并对列表按照z进行排序。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注意，在标准流程中，透明物体和非透明物体需要分类反着排序，透明物体由远及近画，非透明物体由近及远。并且往往还会加上&lt;code&gt;renderQueue&lt;/code&gt;来加上一个可控的排序维度。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;4.&lt;code&gt;renderCamera(camera: Camera, meshes: Mesh[], clear: boolean = true)&lt;/code&gt;：&lt;/p&gt;
&lt;p&gt;使用某个相机进行渲染一批Mesh，直接代理到&lt;code&gt;camera.render&lt;/code&gt;方法。&lt;/p&gt;
&lt;p&gt;5.&lt;code&gt;renderImages(meshes: ImageMesh[], clear: boolean = true)&lt;/code&gt;：&lt;/p&gt;
&lt;p&gt;渲染一批特殊的渲染单元&lt;code&gt;ImageMesh&lt;/code&gt;，专用于处理图像效果。&lt;/p&gt;
&lt;p&gt;6.&lt;code&gt;computeUnits(units: ComputeUnit[])&lt;/code&gt;：&lt;/p&gt;
&lt;p&gt;对一批计算单元进行计算，专用于处理计算着色器。&lt;/p&gt;
&lt;p&gt;7.&lt;code&gt;endFrame()&lt;/code&gt;：&lt;/p&gt;
&lt;p&gt;结束一帧的绘制，主要是将当帧的&lt;code&gt;commandEncoder&lt;/code&gt;送入&lt;code&gt;GPUQueue&lt;/code&gt;并提交：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;renderEnv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;device&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;queue&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;submit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;([&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_command&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;finish&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()]);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;如此，&lt;code&gt;this._command&lt;/code&gt;便结束了其生命周期，不能再被使用。&lt;/p&gt;
&lt;h3&gt;Camera&lt;/h3&gt;
&lt;p&gt;相机是观察整个场景的眼睛，其继承自&lt;code&gt;Node&lt;/code&gt;，对渲染的主要贡献有以下几点：&lt;/p&gt;
&lt;p&gt;1.剔除：&lt;/p&gt;
&lt;p&gt;剔除分为很多种，相机的剔除是物体级别的可见性剔除，其利用相机位置和投影参数构造的视锥体和物体自身求交（出于性能考虑，往往使用包围盒或者包围球）来判定物体是否在可见范围内，并算出相机到物体的距离，来作为后续排序的依据。剔除的实现为&lt;code&gt;camera.cull&lt;/code&gt;，但由于其不是教程重点，并且对于目前场景也没什么意义，所以&lt;strong&gt;没有实现&lt;/strong&gt;。不过实现也很简单，读者可以自行查阅相关资料。&lt;/p&gt;
&lt;p&gt;2.提供VP矩阵：&lt;/p&gt;
&lt;p&gt;相机还需要为渲染过程提供VP矩阵，即视图矩阵&lt;code&gt;ViewMatrix&lt;/code&gt;和投影矩阵&lt;code&gt;ProjectionMatrix&lt;/code&gt;。前者和相机结点的&lt;code&gt;WorldMatrix&lt;/code&gt;相关，后者则和相机的投影模式（透视或者正交）以及参数有关。这些参数将在&lt;code&gt;updateMatrix&lt;/code&gt;方法更新了&lt;code&gt;WorldMatrix&lt;/code&gt;后计算，计算后会立即设置到全局Uniform中。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;当然理论上这种做法在场景中存在多个相机的时候会出问题，实际上应该有个专门的&lt;code&gt;preRender&lt;/code&gt;之类的流程来处理，但为了方便这里就这么做吧。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;3.天空盒：&lt;/p&gt;
&lt;p&gt;除了计算VP矩阵外，在&lt;code&gt;updateMatrix&lt;/code&gt;中还会计算一个&lt;code&gt;skyVP&lt;/code&gt;，这个是用于天空盒渲染的。天空盒是一种特殊的Mesh，其不会随着相机的移动变换相对位置（只会相对旋转）。本引擎对天空盒的实现很简单，固定使用内置的几何体&lt;code&gt;Geometry&lt;/code&gt;，为相机增加了&lt;code&gt;skyboxMat&lt;/code&gt;这个材质属性来定制绘制过程，并提供了内置的天空盒材质。&lt;/p&gt;
&lt;p&gt;4.渲染：&lt;/p&gt;
&lt;p&gt;相机最重要的功能就是进行渲染实际的渲染驱动了，其&lt;code&gt;render(cmd: GPUCommandEncoder, rt: RenderTexture, meshes: Mesh[], clear: boolean)&lt;/code&gt;方法就用于此。这个方法的实现并不复杂，直接贴代码：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;g&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;clearColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;w&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;h&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;viewport&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;colorViews&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;depthStencilView&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;rt&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;renderPassDescriptor&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;GPURenderPassDescriptor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;colorAttachments&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;colorViews.map&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;view&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;view&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;loadValue&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;clear&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;g&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;a&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;load&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;GPULoadOp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;storeOp&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;this.colorOp&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;})),&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;depthStencilAttachment&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;depthStencilView&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;view&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;depthStencilView&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;depthLoadValue&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;this.clearDepth&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;stencilLoadValue&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;this.clearStencil&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;depthStoreOp&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;this.depthOp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;stencilStoreOp&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;this.stencilOp&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;

&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;pass&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;cmd&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;beginRenderPass&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;renderPassDescriptor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;pass&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setViewport&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;w&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;h&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;pass&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setBindGroup&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;renderEnv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;bindingGroup&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;mesh&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;of&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;meshes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;mesh&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;render&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;pass&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;rt&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;drawSkybox&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_skyboxMesh&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_skyboxMesh&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;render&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;pass&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;rt&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;nx&amp;quot;&gt;pass&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;endPass&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;其中比较重要的是&lt;code&gt;RenderPass&lt;/code&gt;的概念，在WebGPU中这可以认为是&lt;strong&gt;一次渲染的流程&lt;/strong&gt;，创建其使用的&lt;code&gt;GPURenderPassDescriptor&lt;/code&gt;对象是这个流程要&lt;strong&gt;绘制的目标和操作&lt;/strong&gt;的描述符。可以看到其中主要是设置了颜色和深度/模板缓冲的&lt;code&gt;view&lt;/code&gt;，关于这个将会在后面的&lt;code&gt;RenderTexture&lt;/code&gt;中详细说到，这里就认为其就是画布即可；而&lt;code&gt;loadValue&lt;/code&gt;和&lt;code&gt;storeOp&lt;/code&gt;是决定如何清屏的，在这里这些参数都可以交由开发者决定，一般来讲是&lt;strong&gt;颜色值&lt;/strong&gt;和&lt;code&gt;store&lt;/code&gt;操作。&lt;/p&gt;
&lt;p&gt;在创建了一个RenderPass后，就可以设置&lt;code&gt;viewport&lt;/code&gt;和&lt;code&gt;bindGroup&lt;/code&gt;，前者和OpenGL中的视口概念等同，后者可以认为就是UniformBlock，这个在后面会详细讲到，这里设置的是第0个UniformBlock，即&lt;strong&gt;全局UB&lt;/strong&gt;。设置好后便是调用每个Mesh的&lt;code&gt;render&lt;/code&gt;方法逐个渲染，最终渲染天空盒。渲染完毕后调用&lt;code&gt;pass.endPass&lt;/code&gt;来结束这个RenderPass。&lt;/p&gt;
&lt;p&gt;事实上关于&lt;code&gt;ImageMesh&lt;/code&gt;和&lt;code&gt;ComputeUnit&lt;/code&gt;的绘制处理也类似，后面会详细讲到。&lt;/p&gt;
&lt;h3&gt;Mesh&lt;/h3&gt;
&lt;p&gt;网格&lt;code&gt;Mesh&lt;/code&gt;是用于渲染的基本单元，其构造为&lt;code&gt;new Mesh(geometry, material)&lt;/code&gt;。其将存储着图元数据的几何体&lt;code&gt;Geometry&lt;/code&gt;和决定了渲染方式的材质&lt;code&gt;Material&lt;/code&gt;对应组装起来，加之每个对象各有的UniformBlock，在相机绘制的过程中，被调用进行渲染：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;render&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;pass&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;GPURenderPassEncoder&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;rt&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;RenderTexture&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_geometry&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;version&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!==&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_matVersion&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;||&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_pipelines&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rt&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;pipelineHash&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;])&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_createPipeline&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rt&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_matVersion&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;version&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setUniform&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;u_world&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_worldMat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_bindingGroup&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_ubTemplate&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getBindingGroup&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_uniformBlock&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_bindingGroup&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

  &lt;span class=&amp;quot;nx&amp;quot;&gt;_geometry&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;vertexes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;forEach&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;((&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;vertex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;pass&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setVertexBuffer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;vertex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;pass&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setIndexBuffer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_geometry&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;indexes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_geometry&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;indexFormat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;pass&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setBindGroup&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;bindingGroup&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;pass&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setBindGroup&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_bindingGroup&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;pass&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setPipeline&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_pipelines&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rt&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;pipelineHash&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]);&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;pass&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;drawIndexed&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_geometry&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;count&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;渲染的流程并不复杂，先不管下面章节会说到的资源的具体实现，从宏观的角度来看，这里主要的工作是设置顶点缓冲&lt;code&gt;vertexBuffer&lt;/code&gt;，设置索引缓冲&lt;code&gt;indexBuffer&lt;/code&gt;，分别设置物体（index=1）和材质（index=2）级别的Uniform，最后设置一个叫做&lt;code&gt;pipeline&lt;/code&gt;的参数，全部设置完后调用&lt;code&gt;drawIndexed&lt;/code&gt;来绘制这一批图元。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;当然，这也支持GPU实例化，但不在本引擎的讨论范围内。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;ImageMesh&lt;/h3&gt;
&lt;p&gt;图像渲染单元&lt;code&gt;ImageMesh&lt;/code&gt;一般用于图像处理，其构造为&lt;code&gt;new ImageMesh(material)&lt;/code&gt;。其内置了绘制一张图像的图元数据，直接用传入的Material进行绘制。所以其绘制流程也相对简单，不需要MVP矩阵，所以并不需要相机，也不需要结点，不需要深度缓冲，其他和&lt;code&gt;camera.render&lt;/code&gt;几乎一致。&lt;/p&gt;
&lt;p&gt;还要注意的是ImageMesh最终的绘制并不是用&lt;code&gt;drawIndexed&lt;/code&gt;方法而是用&lt;code&gt;pass.draw(6, 1, 0, 0)&lt;/code&gt;方法，因为其并不需要顶点数据，这个在着色器章节会详细论述。&lt;/p&gt;
&lt;h3&gt;ComputeUnit&lt;/h3&gt;
&lt;p&gt;WebGPU相对于WebGL最重大的进化之一就是支持&lt;strong&gt;计算着色器&lt;/strong&gt;，计算单元&lt;code&gt;ComputeUnit&lt;/code&gt;就用于支持它。和渲染单元不同，其专门用于使用&lt;strong&gt;Compute Shader&lt;/strong&gt;进行计算，相比于渲染单元，其不需要顶点、渲染状态等等数据，所有数据都将视为用于计算的缓冲，所以其构造为&lt;code&gt;new ComputeUnit(effect, groups, values, marcos)&lt;/code&gt;，效果&lt;code&gt;effect&lt;/code&gt;以及&lt;code&gt;values&lt;/code&gt;、&lt;code&gt;marcos&lt;/code&gt;参数也是构造Material的参数，所以ComputeUnit合并了一部分Material的功能。与此同时还加上了&lt;code&gt;groups&lt;/code&gt;（类型为&lt;code&gt;{x: number, y?: number, z?: number}&lt;/code&gt;），这里先不谈，让我们看看整个一个单元列表的计算是如何实现的：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 在Scene.ts中&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;computeUnits&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;units&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;ComputeUnit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[])&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;pass&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_command&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;beginComputePass&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;pass&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setBindGroup&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;renderEnv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;bindingGroup&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;unit&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;of&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;units&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;unit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;compute&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;pass&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

  &lt;span class=&amp;quot;nx&amp;quot;&gt;pass&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;endPass&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;c1&amp;quot;&gt;// 在ComputeUnit.ts中&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;compute&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;pass&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;GPUComputePassEncoder&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_groups&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;version&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!==&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_matVersion&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_createPipeline&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_matVersion&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;version&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

  &lt;span class=&amp;quot;nx&amp;quot;&gt;pass&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setPipeline&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_pipeline&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;pass&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setBindGroup&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;bindingGroup&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;pass&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;dispatch&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_groups&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_groups&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_groups&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;z&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;可以看到，和渲染流程的&lt;code&gt;RenderPass&lt;/code&gt;不同，这里创建了一个&lt;code&gt;ComputePass&lt;/code&gt;用于这批计算，并且同样需要设置&lt;code&gt;BindingGroup0&lt;/code&gt;（可以认为是全局UniformBlock）。之后针对每个计算单元，都设置了&lt;code&gt;pipeline&lt;/code&gt;、单元级别的UniformBlock，最后执行&lt;code&gt;dispatch&lt;/code&gt;，后面的参数涉及到线程组的概念，和计算着色器也有关，这个后面会讲到。在计算完所有单元后，执行&lt;code&gt;endPass&lt;/code&gt;来结束这个流程。&lt;/p&gt;
&lt;h3&gt;Light&lt;/h3&gt;
&lt;p&gt;除了相机和渲染单元之外，对于一个最简单的渲染器，灯光也是不可或缺的。本引擎的灯光设计比较简单，全部集中在&lt;code&gt;Light&lt;/code&gt;这一个类的实现中。灯光主要属性有类型&lt;code&gt;type&lt;/code&gt;、颜色&lt;code&gt;color&lt;/code&gt;以及分类型的各种参数，类型一般有：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;enum&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;ELightType&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;INVALID&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;Area&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;Directional&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;Point&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;Spot&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;每种类型的光源都有不同参数，对于本引擎针对的路径追踪而言，比较重要的是面光源的参数：模式（矩形&lt;code&gt;Rect&lt;/code&gt;或圆盘&lt;code&gt;Disc&lt;/code&gt;）和尺寸&lt;code&gt;宽高或半径&lt;/code&gt;。和相机一样，灯光本身也继承自结点，所以在更新矩阵的时候也要计算自己的灯光矩阵&lt;code&gt;LightMatrix&lt;/code&gt;，并更新需要送往全局UniformBlock的信息：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;updateMatrix() {&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;super&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;updateMatrix&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_ubInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;set&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_color&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_ubInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;set&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_worldMat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;8&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_ubInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;set&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;mat4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;invert&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Float32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;16&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_worldMat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;24&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;在实际渲染中，&lt;code&gt;renderEnv.startFrame&lt;/code&gt;中全局UB的&lt;code&gt;u_lightInfos&lt;/code&gt;就是从这个&lt;code&gt;ubInfo&lt;/code&gt;中获取信息更新的，目前一个物体一次绘制最多支持四个灯光。&lt;/p&gt;
&lt;h2&gt;资源&lt;/h2&gt;
&lt;p&gt;有了宏观的渲染管线和容器，就只剩具体的资源来填充它们了。在接下来的章节，我将论述我们熟悉的一些渲染资源抽象如何在WebGPU中实现。&lt;/p&gt;
&lt;h3&gt;Shader&lt;/h3&gt;
&lt;p&gt;首先必须要说的是着色器&lt;code&gt;Shader&lt;/code&gt;，因为后续的所有资源最终都会在Shader中被应用，并且它们的部分设计和Shader也有着十分紧密的联系。着色器是什么我不再赘述，相信写过OpenGL或WebGL的读者都写过&lt;strong&gt;顶点着色器&lt;/strong&gt;和&lt;strong&gt;片段着色器&lt;/strong&gt;，在WebGPU中它们同样存在，而比起WebGL，WebGPU还实现了&lt;strong&gt;计算着色器&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;WebGPU使用的着色器语言在一次又一次的变更后，终于定论为&lt;a href=&amp;quot;https://www.w3.org/TR/WGSL&amp;quot;&gt;WGSL&lt;/a&gt;。其语法和&lt;code&gt;glsl&lt;/code&gt;与&lt;code&gt;hlsl&lt;/code&gt;都有较大差异，以本人的观点看比较像融合了metal和rust的很多部分，这一点也体现在编译阶段——WGSL的类型系统十分严格。&lt;/p&gt;
&lt;p&gt;本文并非是一个WGPU或者WGSL的详细教程（否则至少得写一本书），下面就分别以三个/三种着色器的简单示例，来论述一下本文需要用到的一些WGSL的基本知识。&lt;/p&gt;
&lt;h4&gt;顶点着色器&lt;/h4&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;//model.vert.wgsl&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;struct&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;VertexOutput&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;builtin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Position&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;location&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;texcoord_0&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;location&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;location&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tangent&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;location&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;color_0&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;location&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;texcoord_1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;stage&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vertex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;main&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;attrs&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Attrs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;VertexOutput&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;output&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;VertexOutput&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;output&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Position&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;global&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_vp&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;mesh&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u_world&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;attrs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;#&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;defined&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;USE_TEXCOORD_0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;output&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;texcoord_0&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;attrs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;texcoord_0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;#&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;endif&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;#&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;defined&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;USE_NORMAL&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;output&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;attrs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;#&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;endif&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;#&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;defined&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;USE_TANGENT&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;output&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tangent&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;attrs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tangent&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;#&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;endif&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;#&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;defined&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;USE_COLOR_0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;output&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;color_0&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;attrs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;color_0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;#&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;endif&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;#&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;defined&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;USE_TEXCOORD_1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;output&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;texcoord_1&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;attrs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;texcoord_1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;#&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;endif&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;output&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这是一个典型的顶点着色器，首先从其入口&lt;code&gt;main&lt;/code&gt;看起，&lt;code&gt;[[stage(vertex)]]&lt;/code&gt;表明这是一个顶点着色器的入口，&lt;code&gt;attrs: Attrs&lt;/code&gt;表明其输入是一个类型为&lt;code&gt;Attrs&lt;/code&gt;的struct，&lt;code&gt;-&amp;gt; VertexOutput&lt;/code&gt;则说明顶点着色器要传输给下一步插值的数据类型为&lt;code&gt;VertexOutput&lt;/code&gt;。&lt;code&gt;Attrs&lt;/code&gt;在这里没有写，这是因为其相对动态，我将其生成集成到了整个渲染过程的设计中，这个在下面的Geometry章节会讲到。而&lt;code&gt;VertexOutput&lt;/code&gt;的定义就在代码顶部，其是一个struct，里面的内容都是形如&lt;code&gt;[[位置]] 名字: 类型&lt;/code&gt;的形式，唯一不同的是&lt;code&gt;[[builtin(position)]] Position&lt;/code&gt;，因为位置信息是重心坐标插值的重要依据，所以需要特殊指明。&lt;/p&gt;
&lt;p&gt;接下来&lt;code&gt;main&lt;/code&gt;的函数体中，主要实现了顶点数据的计算和输出，其中包括大家熟悉的MVP变换&lt;code&gt;output.Position = global.u_vp * mesh.u_world * vec4&amp;lt;f32&amp;gt;(attrs.position, 1.);&lt;/code&gt;，这里要注意&lt;code&gt;var output: VertexOutput&lt;/code&gt;的定义中使用的是&lt;code&gt;var&lt;/code&gt;，在WGSL中使用&lt;code&gt;var&lt;/code&gt;和&lt;code&gt;let&lt;/code&gt;区分变量是动态还是静态，静态变量不可变并且必须初始化，动态的可变。在结尾&lt;code&gt;return output&lt;/code&gt;表明返回计算结果到下一个阶段。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注意这里出现了&lt;code&gt;#if defined(USE_TEXCOORD_0)&lt;/code&gt;这样的写法，而我们也在前面提到过&lt;strong&gt;宏&lt;/strong&gt;，但实际上WGSL目前并没有实现宏、或者在这里的功能层面为&lt;strong&gt;预处理器&lt;/strong&gt;，详见Issue[&lt;a href=&amp;quot;https://github.com/gpuweb/gpuweb/issues/568&amp;quot;&gt;wgsl Consider a preprocessor&lt;/a&gt;。这里其实是我自己通过正则实现的一个简易预处理器，除此之外我还是用webpack的loader机制实现了一个简单的&lt;code&gt;require&lt;/code&gt;方法，来完成shader文件的分隔复用，这里不再赘述。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这里还有必要单独提一下&lt;code&gt;ImageMesh&lt;/code&gt;使用的顶点着色器，在上面的章节中提到了其绘制并不依赖于顶点数据，其实是利用WGSL的模块作用域变量（MODULE SCOPE VARIABLE）实现的：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;struct&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;VertexOutput&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;builtin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;location&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;private&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pos&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;array&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;6&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;array&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;6&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;private&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;array&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;6&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;array&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;6&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;stage&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vertex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;fn&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;main&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;([[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;builtin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vertex_index&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;VertexIndex&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;u32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;VertexOutput&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;output&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;VertexOutput&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;output&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pos&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;VertexIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;output&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;VertexIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;#&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;defined&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;FLIP&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;    &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;output&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;output&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;#&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;endif&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;

&lt;span class=&amp;quot;w&amp;quot;&gt;  &lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt; &lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;output&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;w&amp;quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这里通过&lt;code&gt;var&amp;lt;private&amp;gt;&lt;/code&gt;定义了两个模块作用域的变量数组&lt;code&gt;pos&lt;/code&gt;和&lt;code&gt;uv&lt;/code&gt;，可以被着色器函数索引内容，而此处函数入参&lt;code&gt;[[builtin(vertex_index)]] VertexIndex : u32&lt;/code&gt;是当前正在绘制的顶点索引，用这个索引和两个数组即可完成输出。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;模块作用域变量不止有&lt;code&gt;private&lt;/code&gt;一个域，还有&lt;code&gt;workgroup&lt;/code&gt;等，这里不再赘述。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;片段着色器&lt;/h4&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;struct&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;VertexOutput&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;builtin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Position&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;location&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;texcoord_0&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;location&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;normal&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;location&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;tangent&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;location&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;color_0&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;location&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;texcoord_1&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;

&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;stage&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fragment&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;fn&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;main&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;vo&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;VertexOutput&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;location&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;u_baseColorFactor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;textureSample&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;u_baseColorTexture&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;u_sampler&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;vo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;texcoord_0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;code&gt;main&lt;/code&gt;函数上方的&lt;code&gt;[[stage(fragment)]]&lt;/code&gt;表明这是一个片段着色器。这个着色器很简答也很典型，其将顶点着色器的输出&lt;code&gt;VertexOutput&lt;/code&gt;重心插值后的结果作为输入，最终输出一个
&lt;code&gt;[[location(0)]] vec4&amp;lt;f32&amp;gt;&lt;/code&gt;的结果，这个结果就是最终这个片元颜色。&lt;/p&gt;
&lt;p&gt;有了结构，再看看函数体中做了什么。这里从一个叫&lt;code&gt;material&lt;/code&gt;的变量中取出了&lt;code&gt;u_baseColorFactor&lt;/code&gt;，并用&lt;code&gt;textureSample&lt;/code&gt;方法和UV&lt;code&gt;vo.texcoord_0&lt;/code&gt;，对名为&lt;code&gt;u_baseColorTexture&lt;/code&gt;的贴图进行了采样，并且还用上了一个叫做&lt;code&gt;u_sampler&lt;/code&gt;的变量。纹理采样我们熟悉，但这个&lt;code&gt;materia&lt;/code&gt;、&lt;code&gt;u_baseColorTexture&lt;/code&gt;、&lt;code&gt;u_sampler&lt;/code&gt;又是什么呢？这就要涉及到WGSL的另一点：&lt;strong&gt;BindingGroup了&lt;/strong&gt;。&lt;/p&gt;
&lt;h4&gt;BindingGroup&lt;/h4&gt;
&lt;p&gt;这不是本文第一次出现&lt;code&gt;BindingGroup&lt;/code&gt;这个词，在前面我们已经提到过无数次，并将其和&lt;code&gt;UniformBlock&lt;/code&gt;相提并论，并在绘制流程中调用了&lt;code&gt;pass.setBindGroup&lt;/code&gt;方法。那么这东西到底是什么呢？回想一下我们在OpenGL或者WebGL中如何在渲染过程中更新uniform的信息，一般是调用类似&lt;code&gt;gl.uniform4vf&lt;/code&gt;这种借口，来将从shader反射信息拿到的location作为key，把值更新上去，如果是纹理则还需要线绑定纹理等等操作。就算是加上了缓存，每帧用于更新uniform的时间也是可观的，所以新一代图形API为了解决这个问题，就给出了BindingGroup或者类似的方案。&lt;/p&gt;
&lt;p&gt;BindingGroup本质上可以看做是一个uniform的集合，和其他资源一样，在WebGPU最后创建一个BindingGroup也需要一个描述符：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;device&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;createBindGroup&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;descriptor&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;layout&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;GPUBindGroupLayout&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;entries&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Iterable&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;GPUBindGroupEntry&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;})&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;GPUBindGroup&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;描述符需要一个&lt;code&gt;layout&lt;/code&gt;和一个&lt;code&gt;entries&lt;/code&gt;，前者给出了&lt;strong&gt;结构&lt;/strong&gt;，后者给出了实际的&lt;strong&gt;值&lt;/strong&gt;。以一个简单的构造为例：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;visibility&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;GPUShaderStage&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;VERTEX&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;GPUShaderStage&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;FRAGMENT&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;bindingGroup&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;renderEnv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;device&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;createBindGroup&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;layout&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;device.createBindGroupLayout&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;entries&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;binding&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;visibility&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;buffer&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;uniform&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;GPUBufferBindingType&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;binding&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;visibility&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;texture&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;sampleType&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;rgba8unorm&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;viewDimension&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;2d&amp;#39;&lt;/span&gt;
      &lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;binding&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;visibility&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;sampler&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;filtering&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;binding&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;visibility&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;GPUShaderStage.COMPUTE&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;buffer&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;storage&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;]}),&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;entries&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;binding&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;resource&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;buffer&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;gpuBuffer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;binding&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;resource&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;textureView&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;binding&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;resource&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;sampler&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;binding&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;resource&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;buffer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这里构建的BindingGroup涵盖了几种值，分别是Uniform、Texture、Sampler和Storage，可见每种方式的描述定义都不一样，但它们都有共同之处——需要制定&lt;code&gt;binding&lt;/code&gt;（可以认为是这个Group下的子地址）和&lt;code&gt;visibility&lt;/code&gt;（在哪种着色器可见）。而这里构建的BindingGroup最终会设置到Pass中指定地址给Shader使用，在Shader中我们同样需要定义它们的结构：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;block&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]]&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;struct&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;UniformsMaterial&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
 &lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;align&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;16&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;u_baseColorFactor&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;group&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;binding&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;uniform&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;material&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;UniformsMaterial&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;group&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;binding&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;var&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;u_baseColorTextures&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;texture_2d&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;group&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;binding&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;var&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;u_sampler&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;sampler&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;nx&amp;quot;&gt;struct&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Debug&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;origin&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;dir&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;block&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]]&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;struct&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;DebugInfo&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;rays&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;array&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;DebugRay&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;};[[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;group&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;binding&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;var&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;storage&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;read_write&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;u_debugInfo&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;DebugInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这里将一个BindingGroup分为了几个部分，所有的向量、矩阵等uniform类型的数据被打包在&lt;code&gt;UniformsMaterial&lt;/code&gt;这个struct中，texture、sampler和storage类型的数据则需要分离出来，并且都有各自的定义形式，这个就是为何我们上面要通过&lt;code&gt;materia.u_baseColorFactor&lt;/code&gt;去取uniform数据，而直接用&lt;code&gt;u_texture&lt;/code&gt;来取纹理数据。&lt;/p&gt;
&lt;p&gt;还需要注意的是各种前缀，&lt;code&gt;group(2)&lt;/code&gt;表明这是绑定的第2个BindingGroup，这和前面说过的全局、单元、材质的UniformBlock是绑定在不同级别的一致。事实上前面也给出过其他两个级别的例子，在顶点着色器中使用到的&lt;code&gt;global.u_vp * mesh.u_world&lt;/code&gt;就是在全局0和单元1级别的。而后面的&lt;code&gt;binding(x)&lt;/code&gt;则需要和ts中的声明一致。同时还要注意的是uniform类型的struct的&lt;code&gt;align(16)&lt;/code&gt;，这是为了强制每一项16字节对齐，同时可能使用的还是&lt;code&gt;stride&lt;/code&gt;这样的修饰。&lt;/p&gt;
&lt;p&gt;由于BindingGroup的创建和Shader描述耦合十分高，其中还有字节对齐之类的问题，为了方便使用、减少冗余代码，在引擎中我将BindingGroup的构造、Shader对应的struct的定义生成、Uniform管理都深度融合了，将在下面的UBTemplate论述。&lt;/p&gt;
&lt;h4&gt;计算着色器&lt;/h4&gt;
&lt;p&gt;计算着色器和前两种都不同，其不属于渲染管线的一部分，利用的是GPU的通用计算能力，所以也没有顶点输入、像素输出之类的要求，下面是一个比较糙的对图像卷积滤波的例子：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;c_radius&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;RADIUS&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;c_windowSize&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;WINDOW_SIZE&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;

&lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;stage&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;compute&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;workgroup_size&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;c_windowSize&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;c_windowSize&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;fn&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;main&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;builtin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;workgroup_id&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;workGroupID&lt;/span&gt; : &lt;span class=&amp;quot;kt&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;u32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;builtin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;local_invocation_id&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]]&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;localInvocationID&lt;/span&gt; : &lt;span class=&amp;quot;kt&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;u32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;size&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;textureDimensions&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;u_input&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;windowSize&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;c_windowSize&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;c_windowSize&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;groupOffset&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;workGroupID&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;xy&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;windowSize&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;baseIndex&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;groupOffset&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;localInvocationID&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;xy&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;baseUV&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;baseIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;size&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

  &lt;span class=&amp;quot;kd&amp;quot;&gt;var&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;weightsSum&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.;&lt;/span&gt;
  &lt;span class=&amp;quot;kd&amp;quot;&gt;var&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.);&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;var&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;r&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;c_radius&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;r&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;c_radius&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;r&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;r&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;var&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;c&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;c_radius&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;c&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;c_radius&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;c&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;c&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;iuv&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;baseIndex&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;c&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

      &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;any&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;iuv&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;i32&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;||&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;any&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;iuv&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;size&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;continue&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
      &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

      &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;weightIndex&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;i32&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;r&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;c_radius&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;c_windowSize&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;c&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;c_radius&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
      &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;weight&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;f32&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;u_kernel&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;weightIndex&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;][&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;weightIndex&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;%&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;weightsSum&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;weightsSum&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;weight&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;weight&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;textureLoad&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;u_input&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;iuv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;f32&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;weightsSum&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

  &lt;span class=&amp;quot;nx&amp;quot;&gt;textureStore&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;u_output&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;baseIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;其&lt;code&gt;main&lt;/code&gt;函数的修饰&lt;code&gt;stage(compute)&lt;/code&gt;表明这是一个计算着色器，除此之外后面还有&lt;code&gt;workgroup_size(c_windowSize, c_windowSize, 1)&lt;/code&gt;，读者应该注意到了这里有三个维度的参数，还记得我们前面在论述&lt;code&gt;ComputeUint&lt;/code&gt;时提到的&lt;code&gt;groupSize&lt;/code&gt;以及&lt;code&gt;pass.dispatch(x, y, z)&lt;/code&gt;的三个参数吗？它们毫无疑问是有关联的，这就是&lt;strong&gt;线程组&lt;/strong&gt;的概念。&lt;/p&gt;
&lt;p&gt;这里不再赘述卷积滤波的原理，只需要知道它每次要对一个N X N窗口内的像素做处理，所以这里定义了一个窗口大小&lt;code&gt;windowSize&lt;/code&gt;、也就定义了一个二维的（第三个维度为1）、大小为&lt;code&gt;size = windowSize X windowSize&lt;/code&gt;的线程组，这表明一个线程组有这么大数量的线程，如果我们要处理纹理数据、同时每个线程处理一个像素，那这么一个线程组可以处理&lt;code&gt;size&lt;/code&gt;个数量的像素，那么如果我们要处理一张&lt;code&gt;width x height&lt;/code&gt;大小的图片，就应当将&lt;code&gt;groupSize&lt;/code&gt;设置&lt;code&gt;(width / windowSize, height / windowSize, 1)&lt;/code&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;不同平台上允许的最大线程数量有限制，但一般都应当是&lt;code&gt;16 x 16&lt;/code&gt;以上。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;除此之外，可以看到计算着色器确实没有顶点像素之类的概念，但它有输入&lt;code&gt;workgroup_id&lt;/code&gt;、&lt;code&gt;local_invocation_id&lt;/code&gt;，这其实就是&lt;strong&gt;线程组的偏移&lt;/strong&gt;和&lt;strong&gt;线程组内线程的偏移&lt;/strong&gt;，通过它们我们就可以得到真正的线程偏移，也可以作为纹理采样的依据。能够采样纹理，就可以进行计算，最后将计算的结果通过&lt;code&gt;textureStore&lt;/code&gt;写回输出纹理即可，输出纹理是一个&lt;code&gt;write&lt;/code&gt;类型的&lt;code&gt;storageTexture&lt;/code&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;着色器中的&lt;code&gt;${WINDOW_SIZE}&lt;/code&gt;这种也属于我自己实现的简陋的宏的一部分。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Geometry&lt;/h3&gt;
&lt;p&gt;几何体&lt;code&gt;Geometry&lt;/code&gt;本质上就是顶点数据和索引数据的集合。在OpenGL中，我们往往会自己组织一个结构来描述顶点数据的存储构造，在WebGPU中，API标准即将这个结构定死了，其为&lt;code&gt;GPUVertexBufferLayout[]&lt;/code&gt;。这个结构的描述加上&lt;code&gt;vertexBuffer&lt;/code&gt;、&lt;code&gt;indexBuffer&lt;/code&gt;即构成了整个渲染单元的几何信息：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;constructor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;protected&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_vertexes&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;layout&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;attributes&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;GPUVertexAttribute&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;})[],&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;arrayStride&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;data&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;TTypedArray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;usage?&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}[],&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;protected&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_indexData&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Uint16Array&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Uint32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;count&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;protected&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_boundingBox?&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IBoundingBox&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_iBuffer&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;createGPUBuffer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_indexData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;GPUBufferUsage&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;INDEX&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_vBuffers&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_vertexes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;length&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_vLayouts&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_vertexes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;length&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_indexFormat&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_indexData&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;instanceof&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Uint16Array&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;uint16&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;uint32&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_vInfo&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{};&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_marcos&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{};&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_attributesDef&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;struct Attrs {\n&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

    &lt;span class=&amp;quot;nx&amp;quot;&gt;_vertexes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;forEach&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(({&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;layout&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;data&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;usage&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;vBuffer&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;createGPUBuffer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;data&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;GPUBufferUsage&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;VERTEX&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;usage&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;

      &lt;span class=&amp;quot;nx&amp;quot;&gt;layout&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;attributes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;forEach&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;((&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;attr&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_marcos&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;`&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;USE_$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;attr&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;toUpperCase&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()}&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;`&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_attributesDef&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;err&amp;quot;&gt;`&lt;/span&gt;  &lt;span class=&amp;quot;p&amp;quot;&gt;[[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;location&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;attr&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;shaderLocation&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;})]]&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;attr&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_convertFormat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;attr&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;format&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)};&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;\&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;n&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;`&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_vInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;attr&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;toLowerCase&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
          &lt;span class=&amp;quot;nx&amp;quot;&gt;data&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
          &lt;span class=&amp;quot;nx&amp;quot;&gt;offset&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;attr.offset&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;stride&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;layout.arrayStride&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;length&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;this._getLength&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;attr&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;format&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
      &lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;

      &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_vBuffers&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;vBuffer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
      &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_vLayouts&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;layout&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

      &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_vertexCount&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;data&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;byteLength&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;layout&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;arrayStride&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_attributesDef&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;};\n\n&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这是Geometry的构造方法，可以看到其主要是将多个&lt;code&gt;vertexBuffer&lt;/code&gt;以及对应的&lt;code&gt;layout&lt;/code&gt;、一个&lt;code&gt;indexBuffer&lt;/code&gt;、顶点个数&lt;code&gt;count&lt;/code&gt;处理，最终生成WebGPU需要的&lt;code&gt;VertexLayout&lt;/code&gt;和&lt;code&gt;GPUBuffer&lt;/code&gt;，同时还生成了其他必要的数据。VertexLayout这是描述无须赘述，这里主要需要注意&lt;code&gt;GPUBuffer&lt;/code&gt;和所谓&lt;strong&gt;其他数据&lt;/strong&gt;的生成。&lt;/p&gt;
&lt;h4&gt;GPUBuffer&lt;/h4&gt;
&lt;p&gt;首先是GPUBuffer，其在WebGPU中很常见，除了纹理之外所有在CPU和GPU传输的数据都是GPUBuffer。在历经数次迭代后，我们可以用一段简短的代码来创建它：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;function&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;createGPUBuffer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;array&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;TTypedArray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;usage&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;GPUBufferUsageFlags&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;size&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;byteLength&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;byteLength&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;%&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;buffer&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;renderEnv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;device&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;createBuffer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;size&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;usage&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;usage&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;GPUBufferUsage&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;COPY_DST&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;mappedAtCreation&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;true&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;

  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;view&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;constructor&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;buffer&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;ArrayBuffer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;TTypedArray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;})(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;buffer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getMappedRange&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;size&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;view&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;set&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

  &lt;span class=&amp;quot;nx&amp;quot;&gt;buffer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;unmap&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;buffer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这段代码通过传入的&lt;code&gt;TypedArray&lt;/code&gt;创建一个同尺寸（但需要字节对齐）的GPUBuffer，并将其数据的值在初始化的时候拷贝给它。&lt;/p&gt;
&lt;h4&gt;宏和Shader&lt;/h4&gt;
&lt;p&gt;在创建Geometry的过程中还会生成别的重要数据：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;一张宏的表&lt;code&gt;_marcos&lt;/code&gt;，所有用到的顶点属性都会以&lt;code&gt;USE_XXX&lt;/code&gt;的形式存在，然后作用在Shader中，比如上面顶点着色器示例那样。  &lt;/li&gt;
&lt;li&gt;顶点相关的Shader类型定义数据&lt;code&gt;_attributesDef&lt;/code&gt;，自动组装出需要的拼接到Shader头部的字符串。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Texture&lt;/h3&gt;
&lt;p&gt;前面提到了BindingGroup的各种类型，其中有一大类就是纹理&lt;code&gt;Texture&lt;/code&gt;，而纹理在引擎实现中又可以分为2D纹理和Cube纹理。&lt;/p&gt;
&lt;h4&gt;2D纹理&lt;/h4&gt;
&lt;p&gt;纹理在WebGPU中的创建很简单：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_gpuTexture&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;renderEnv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;device&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;createTexture&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;label&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;this.hash&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;size&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;this._width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;this._height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;depthOrArrayLayers&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;this._arrayCount&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;format&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;_format&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;||&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;rgba8unorm&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;usage&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;GPUTextureUsage.TEXTURE_BINDING&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;GPUTextureUsage&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;COPY_DST&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;GPUTextureUsage&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;RENDER_ATTACHMENT&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;isTextureSourceArray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_src&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;_src&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;forEach&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;((&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;src&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_load&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;src&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_gpuTextureView&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_gpuTexture&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;createView&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;dimension&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;2d-array&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;arrayLayerCount&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;this._arrayCount&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_load&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_src&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_gpuTextureView&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_gpuTexture&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;createView&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;首先我们需要用&lt;code&gt;device.createTexture&lt;/code&gt;来创建一个纹理，这里需要注意的参数是&lt;code&gt;size&lt;/code&gt;中的&lt;code&gt;depthOrArrayLayers&lt;/code&gt;，其在类型为&lt;code&gt;2d-array&lt;/code&gt;的时候是数组的数量。创建了纹理后用&lt;code&gt;_load&lt;/code&gt;方法来加载并上传纹理数据，最终调用&lt;code&gt;createView&lt;/code&gt;方法创建&lt;code&gt;view&lt;/code&gt;并缓存。而对于&lt;code&gt;_load&lt;/code&gt;的实现，则根据来源是&lt;code&gt;Buffer&lt;/code&gt;或图像有不同的做法：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_loadImg&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;img&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;ImageBitmap&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;layer&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;renderEnv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;device&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;queue&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;copyExternalImageToTexture&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;source&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;img&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;texture&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;this._gpuTexture&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;origin&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;this._isArray&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;z&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;layer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;undefined&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;this._width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;this._height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;depthOrArrayLayers&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;nx&amp;quot;&gt;_loadBuffer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;buffer&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;ArrayBuffer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;layer&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;renderEnv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;device&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;queue&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;writeTexture&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;texture&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;this._gpuTexture&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;origin&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;this._isArray&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;z&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;layer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;undefined&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;buffer&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;ArrayBuffer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;bytesPerRow&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;this._width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;this._width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;this._height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;depthOrArrayLayers&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;通过图像提供纹理数据，主要使用&lt;code&gt;device.queue.copyExternalImageToTexture&lt;/code&gt;方法，但要求传入的是一个&lt;code&gt;ImageBitMap&lt;/code&gt;，这个可以使用以下代码生成：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;img&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;document&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;createElement&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;img&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;img&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;src&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;src&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;await&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;img&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;decode&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;bitmap&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;await&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;createImageBitmap&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;img&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;而使用Buffer的话，则直接用&lt;code&gt;device.queue.writeTexture&lt;/code&gt;即可。&lt;/p&gt;
&lt;h4&gt;Cube纹理&lt;/h4&gt;
&lt;p&gt;Cube纹理和2D纹理大差不差，区别在于其初始化的时候&lt;code&gt;depthOrArrayLayers&lt;/code&gt;为6，并且需要在初始化的时候提交六张纹理，并且在&lt;code&gt;origin&lt;/code&gt;参数中的&lt;code&gt;z&lt;/code&gt;为1~6。&lt;/p&gt;
&lt;h3&gt;RenderTexture&lt;/h3&gt;
&lt;p&gt;说完Texture，顺便可以直接说说可渲染纹理&lt;code&gt;RenderTexture&lt;/code&gt;，顾名思义，这是一种可以用于用于绘制或写入数据的纹理。和通常的RenderTexture的设计一样，为了和MRT（多渲染目标）技术相容，本引擎的RenderTexture也设计为多个&lt;code&gt;colorTexture&lt;/code&gt;和一个&lt;code&gt;depthTexture&lt;/code&gt;的形式，来看看它的构造参数：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;interface&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;IRenderTextureOptions&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;forCompute?&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;boolean&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;colors&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;name?&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;format?&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;GPUTextureFormat&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}[];&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;depthStencil&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;?:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;format?&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;GPUTextureFormat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;needStencil?&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;boolean&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;具体的实现不贴了，就是根据&lt;code&gt;colors&lt;/code&gt;和&lt;code&gt;depthStencil&lt;/code&gt;中的参数去创建不同的&lt;code&gt;GPUTexture&lt;/code&gt;，然后使用每个&lt;code&gt;color&lt;/code&gt;的&lt;code&gt;name&lt;/code&gt;建表索引，之后创建&lt;code&gt;view&lt;/code&gt;缓存。这里有个特别的参数&lt;code&gt;forCompute&lt;/code&gt;（是否用于计算着色器），主要决定了创建&lt;code&gt;GPUTexture&lt;/code&gt;时的&lt;code&gt;usage&lt;/code&gt;，如果是则需要开启&lt;code&gt;GPUTextureUsage.STORAGE_BINDING&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;创建好的RenderTexture可以和Texture直接一样直接用于渲染来源数据，但其最重要的功能是用于绘制，关于如何绘制，已经在前面的Camera章节说过了，将其按照顺序设置到&lt;code&gt;GPURenderPassDescriptor&lt;/code&gt;即可。&lt;/p&gt;
&lt;h3&gt;UBTemplate&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;UBTemplate&lt;/code&gt;，可以认为是前面提了无数次的&lt;code&gt;UniformBlock&lt;/code&gt;的模板，当然到这里读者应该察觉到了——这个UniformBlock远不止管理了Uniform，也管理了纹理、采样器、SSBO等等，只不过出于习惯这么称呼。而这也可以认为是整个引擎最复杂的一部分，因为它不仅涉及到了这些数据的创建和更新，还涉及到了Shader相关定义的生成。而在WebGPU和WGSL规范中，尤其是Uniform部分的规范又非常繁琐，比如各种字节对齐（align、stride），在方便使用的前提下，UBTemplate需要自动抹平这些复杂性，对外暴露足够简单的构造和接口，当然这是有代价的——会造成部分的内存浪费。&lt;/p&gt;
&lt;p&gt;将UBTemplate所做的全部工作列出来实在会篇幅过长，而且必要性不大，这里就说一些核心的地方，剩下的读者自己去看代码即可。首先让我们看一下它的构造参数：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;constructor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;protected&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_uniformDesc&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IUniformsDescriptor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;protected&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_groupId&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;EUBGroup&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;protected&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_visibility?&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;enum&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;EUBGroup&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;Global&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;Material&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;Mesh&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;TUniformValue&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;TUniformTypedArray&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Texture&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;CubeTexture&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;GPUSamplerDescriptor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;RenderTexture&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;interface&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;IUniformsDescriptor&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;uniforms&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;number&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;vec2&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;vec3&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;vec4&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;mat2x2&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;mat3x3&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;mat4x4&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;format&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;?:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;f32&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;u32&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;i32&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;size?&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;customType&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;?:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;code&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;len&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;defaultValue&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;TUniformTypedArray&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}[],&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;textures&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;?:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;format?&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;GPUTextureSampleType&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;defaultValue&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Texture&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;CubeTexture&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;storageAccess?&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;GPUStorageTextureAccess&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;storageFormat?&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;GPUTextureFormat&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}[],&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;samplers&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;?:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;defaultValue&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;GPUSamplerDescriptor&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}[],&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;storages&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;?:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;number&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;vec2&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;vec3&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;vec4&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;format&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;?:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;f32&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;u32&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;i32&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;customStruct&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;?:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;code&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;writable?&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;boolean&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;defaultValue&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;TUniformTypedArray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;gpuValue?&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;GPUBuffer&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}[]&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;可见主要是&lt;code&gt;_uniformDesc&lt;/code&gt;和&lt;code&gt;_groupId&lt;/code&gt;，后者很简单，决定UB将用于哪个级别，这决定生成的Shader定义中Uniform部分的是&lt;code&gt;global&lt;/code&gt;、&lt;code&gt;mesh&lt;/code&gt;还是&lt;code&gt;material&lt;/code&gt;。而前者就相对复杂了，其主要是四个部分：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;uniforms：Uniform部分，这一部分的数据一般都是细粒度的向量、矩阵等，支持数组，在实际生成最后，会将他们都打包成一个大的Buffer，这个Buffer是&lt;strong&gt;严格16字节对齐的&lt;/strong&gt;，这是什么意思呢？比如一个&lt;code&gt;Vector3数组&lt;/code&gt;，将会被生成为&lt;code&gt;[[align(16)]] ${name}: [[stride(16)]] array&amp;lt;vec3&amp;lt;f32&amp;gt;, 4&amp;gt;;&lt;/code&gt;。也就是说虽然这个数据只占用&lt;code&gt;4 x 3 x 4&lt;/code&gt;个字节的空间即可描述，但这里强制使其占用&lt;code&gt;4 x 4 x 4&lt;/code&gt;的空间，在每个&lt;code&gt;vec3&lt;/code&gt;元素后都填了一位。在使用&lt;code&gt;setUniform&lt;/code&gt;设置值的时候也会自动按照这个对齐规则来设置。&lt;/li&gt;
&lt;li&gt;textures：纹理部分，和前面给出的创建&lt;code&gt;BindingGroup&lt;/code&gt;时使用基本一致，但注意这里有参数&lt;code&gt;storageAccess&lt;/code&gt;和&lt;code&gt;storageFormat&lt;/code&gt;，用于生成&lt;code&gt;storageTexture&lt;/code&gt;的定义，一般用于给CS提供可写入的RenderTexture。&lt;/li&gt;
&lt;li&gt;samplers：采样器部分，和前面给出的创建&lt;code&gt;BindingGroup&lt;/code&gt;时使用完全一致。&lt;/li&gt;
&lt;li&gt;storages：SSBO部分，这是一种可以在CPU和GPU共享的特殊Buffer，其可以存储相对大量的数据，并可以在GPU的CS中写入和读取、也可以在CPU中写入和读取。在本项目中我一般用于调试CS。&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;uniforms和storage都提供了&lt;code&gt;customStruct&lt;/code&gt;或&lt;code&gt;customType&lt;/code&gt;参数来让用户自定义结构，而非默认生成，提供了自由度。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;每次创建UBTemplate的时候，实际上都只是生成了&lt;code&gt;BindingGroup&lt;/code&gt;中的&lt;code&gt;layout&lt;/code&gt;部分和CPU端的默认值等，于此同时还生成了Shader中对应的Uniform相关的定义字符串。而在后续实际渲染中，我们将使用&lt;code&gt;createUniformBlock&lt;/code&gt;方法实际创建UB时，返回的是：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;interface&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;IUniformBlock&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;isBufferDirty&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;boolean&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;isDirty&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;boolean&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;layout&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;GPUBindGroupLayout&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;entries&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;GPUBindGroupEntry&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[];&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;cpuBuffer&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Uint32Array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;gpuBuffer&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;GPUBuffer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;values&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;value&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;TUniformValue&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;gpuValue&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;GPUBuffer&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;GPUSampler&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;GPUTextureView&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;后续便可以使用&lt;code&gt;setUniform(ub: IUniformBlock, name: string, value: TUniformValue, rtSubNameOrGPUBuffer?: string | GPUBuffer);&lt;/code&gt;方法和&lt;code&gt;getUniform(ub: IUniformBlock, name: string)&lt;/code&gt;给用到UBTemplate的对象提供修改和获取的支持。还记得前面说到的几个UniformBlock吗？其实就是用UBTemplate生成的，其中&lt;code&gt;renderEnv&lt;/code&gt;管理了全局的UniformBlock，&lt;code&gt;Mesh&lt;/code&gt;/&lt;code&gt;ImageMesh&lt;/code&gt;/&lt;code&gt;ComputeUint&lt;/code&gt;管理了单元级别的UniformBlock，而即将说到的Material管理了材质级别的UniformBlock，它们被设置到不同的地址，共同完成渲染。&lt;/p&gt;
&lt;p&gt;在最终，也就是后续会提到的创建BindingGroup的那一步，使用的是&lt;code&gt;getBindingGroup&lt;/code&gt;方法，在上面说到的几类对象上都有&lt;code&gt;bindingGroup&lt;/code&gt;访问器代理到这里：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;getBindingGroup&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ub&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IUniformBlock&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;preGroup&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;GPUBindGroup&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ub&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;isBufferDirty&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;renderEnv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;device&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;queue&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;writeBuffer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;ub&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;gpuBuffer&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;GPUBuffer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
      &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;ub&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cpuBuffer&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

    &lt;span class=&amp;quot;nx&amp;quot;&gt;ub&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;isBufferDirty&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;false&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ub&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;isDirty&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;preGroup&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;renderEnv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;device&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;createBindGroup&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;layout&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;ub.layout&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;entries&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;ub.entries&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;ub&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;isDirty&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;false&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;preGroup&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;可见这里主要做了两件事，第一件事是检查脏位更新Uniform部分的数据，第二则是检查并更新&lt;code&gt;group&lt;/code&gt;缓存返回。&lt;/p&gt;
&lt;h3&gt;Effect&lt;/h3&gt;
&lt;p&gt;效果&lt;code&gt;Effect&lt;/code&gt;可以认为是对Shader、UniformBlock、渲染状态和宏的一个管理器，也可以认为是一个模板，以供后面的Material实例化，其构造参数为：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;interface&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;IRenderStates&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;cullMode?&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;GPUCullMode&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;primitiveType?&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;GPUPrimitiveTopology&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;blendColor?&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;GPUBlendComponent&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;blendAlpha?&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;GPUBlendComponent&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;depthCompare?&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;GPUCompareFunction&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;interface&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;IEffectOptionsRender&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;vs&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;fs&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;uniformDesc&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IUniformsDescriptor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;marcos&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;?:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;key&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;boolean&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;renderState?&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IRenderStates&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;interface&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;IEffectOptionsCompute&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;cs&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;uniformDesc&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IUniformsDescriptor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;marcos&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;?:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;key&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;boolean&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;TEffectOptions&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;IEffectOptionsRender&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;IEffectOptionsCompute&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;除去&lt;code&gt;vs/fs&lt;/code&gt;（用于渲染）和&lt;code&gt;cs&lt;/code&gt;（用于计算）的区分，通用的部分是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;UBTemplate构造参数&lt;code&gt;uniformDesc&lt;/code&gt;：指定这个Effect提供的默认UniformBlock结构来创建UBTemplate。&lt;/li&gt;
&lt;li&gt;宏对象&lt;code&gt;marcos&lt;/code&gt;：Effect能够支持的宏特性列表，后续生成Shader的时候会使用。&lt;/li&gt;
&lt;li&gt;渲染状态&lt;code&gt;renderState&lt;/code&gt;：Effect提供的默认渲染状态，这里只定义了几个我用到过的，实际上还有不少。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Effect的功能并不多，其最重要的是对外暴露了&lt;code&gt;createDefaultUniformBlock&lt;/code&gt;和&lt;code&gt;getShader&lt;/code&gt;两个方法，以供最后的渲染使用。前者将会在Material中用到，后者则会在最后的Pipeline中用到。&lt;/p&gt;
&lt;h3&gt;Material&lt;/h3&gt;
&lt;p&gt;材质&lt;code&gt;Material&lt;/code&gt;可以看做是实例化后的Effect，其构造如下：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;constructor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;protected&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_effect&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Effect&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;values&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;?:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;TUniformValue&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;marcos&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;?:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;key&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;boolean&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;renderStates?&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IRenderStates&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;super&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_uniformBlock&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_effect&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;createDefaultUniformBlock&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;values&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nb&amp;quot;&gt;Object&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;keys&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;values&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;forEach&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setUniform&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;values&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]));&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_marcos&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;marcos&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;||&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{};&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_renderStates&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;renderStates&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;||&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{};&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;可见其实很简单，就是利用拥有的Effect构建了一个材质级别的UniformBlock并设置初始值，然后提供了宏&lt;code&gt;marcos&lt;/code&gt;和渲染状态&lt;code&gt;renderState&lt;/code&gt;，后续也可以修改和获取这些宏和渲染状态。需要注意的是，Material还提供了&lt;code&gt;version&lt;/code&gt;（number）类型，来记录版本，以便于后续Pipeline的更新。&lt;/p&gt;
&lt;h3&gt;Pipeline&lt;/h3&gt;
&lt;p&gt;讲了这么多，基本所有的要素都极其了，终于到了整个流程的最后一步——管线&lt;code&gt;Pipeline&lt;/code&gt;。Pipeline的创建是分别实现在&lt;code&gt;Mesh&lt;/code&gt;、&lt;code&gt;ImageMesh&lt;/code&gt;、&lt;code&gt;ComputeUint&lt;/code&gt;中的，也就是前面在论述这三者时提到的&lt;code&gt;_createPipeline&lt;/code&gt;方法，如果读者还记得，其实这里就利用了上面说到的&lt;code&gt;material.version&lt;/code&gt;来判断版本做缓存。这是因为和BindingGroup一样，创建Pipeline的开销并不低。&lt;/p&gt;
&lt;p&gt;在Mesh中，创建的实现为：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;device&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;renderEnv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_geometry&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_ubTemplate&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_bindingGroup&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_ubTemplate&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getBindingGroup&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_uniformBlock&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_bindingGroup&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;marcos&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;Object&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;assign&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({},&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_geometry&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;marcos&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;marcos&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;vs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;fs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;effect&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getShader&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;marcos&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_geometry&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;attributesDef&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;renderEnv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;shaderPrefix&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_ubTemplate&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;shaderPrefix&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_pipelines&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rt&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;pipelineHash&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;device&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;createRenderPipeline&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;layout&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;device.createPipelineLayout&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;bindGroupLayouts&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;renderEnv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;uniformLayout&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;_material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;effect&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;uniformLayout&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;_ubTemplate&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;uniformLayout&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;]}),&lt;/span&gt;

  &lt;span class=&amp;quot;nx&amp;quot;&gt;vertex&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;module&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;vs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;entryPoint&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;main&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;buffers&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;_geometry.vertexLayouts&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;

  &lt;span class=&amp;quot;nx&amp;quot;&gt;fragment&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;module&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;fs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;targets&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;rt.colorFormats.map&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;format&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;format&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;blend&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;_material.blendColor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;color&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;_material.blendColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;alpha&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;_material.blendAlpha&lt;/span&gt;
      &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;undefined&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;})),&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;entryPoint&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;main&amp;quot;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;

  &lt;span class=&amp;quot;nx&amp;quot;&gt;primitive&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;topology&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;_material.primitiveType&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;cullMode&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;_material.cullMode&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;

  &lt;span class=&amp;quot;nx&amp;quot;&gt;depthStencil&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;rt.depthStencilFormat&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;format&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;rt.depthStencilFormat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;depthWriteEnabled&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;depthCompare&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;_material.depthCompare&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;可见这里将之前所有部分基本都串起来了，首先合并两个级别的宏，通过他们和Geometry的顶点信息Shader定义、三个级别的UniformBlock的Shader定义来生成最终的&lt;code&gt;vs&lt;/code&gt;和&lt;code&gt;fs&lt;/code&gt;，然后使用&lt;code&gt;device.createRenderPipeline&lt;/code&gt;方法将这一切都组装起来，生成最终的Pipeline。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ImageMesh&lt;/code&gt;和上面基本一致，只不过省去了不需要的&lt;code&gt;vertex&lt;/code&gt;部分，而&lt;code&gt;ComputeUint&lt;/code&gt;由于没有顶点、片元，也没有渲染状态和渲染目标，更为简单：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;protected&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_createPipeline() {&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;device&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;renderEnv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;marcos&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;Object&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;assign&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({},&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;marcos&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;effect&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getShader&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;marcos&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;renderEnv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;shaderPrefix&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_pipeline&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;device&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;createComputePipeline&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;layout&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;device.createPipelineLayout&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;bindGroupLayouts&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;renderEnv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;uniformLayout&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;_material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;effect&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;uniformLayout&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;]}),&lt;/span&gt;

    &lt;span class=&amp;quot;nx&amp;quot;&gt;compute&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;module&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;cs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;entryPoint&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;main&amp;quot;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;至此，整个渲染引擎部分就此结束。&lt;/p&gt;
&lt;h2&gt;glTF和工作流&lt;/h2&gt;
&lt;p&gt;本来这里想顺便说说资源和工作流部分的，但篇幅已经太长了，就放在下一个章节讲吧。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Monday, 13 Sep 2021 01:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2021.09.13 01:00:article/Skill-2021_09_13_a</guid>
<category>WebGPU</category>
<category>图形学</category>
</item>

<item>
<title>【WebGPU实时光追美少女】概览介绍</title>
<link>http://dtysky.moe/article/Skill-2021_09_06_a</link>
<description>&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/webgpu-rt/1/0.png&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;本系列文章将会论述如何用WebGPU来实现一个实时路径追踪渲染器，从一个简单渲染器为开端层层深入，了解经典路径追踪渲染器的各个部分，以及BRDF模型在路径追踪中的实现。&lt;/p&gt;
&lt;p&gt;Github仓库在这里：&lt;a href=&amp;quot;https://github.com/dtysky/webgpu-renderer&amp;quot;&gt;dtysky/webgpu-renderer&lt;/a&gt;，Demo在：&lt;a href=&amp;quot;https://dtysky.github.io/webgpu-renderer/&amp;quot;&gt;Demo&lt;/a&gt;，注意目前需要最新的&lt;strong&gt;Chrome Canary&lt;/strong&gt;版本才能打开。&lt;/p&gt;
&lt;p&gt;这个项目是我三个月前一边学习闫神的GAMES202一边搞的，当时搞了大半的进度后迫于搞给自己庆生的游戏&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/401016548&amp;quot;&gt;Double;14&lt;/a&gt;所以暂时搁置，游戏搞完了肝也废了缓了一阵子又出了一些头疼现实问题，最近都解决了才重新捡起来。&lt;/p&gt;
&lt;h2&gt;大纲&lt;/h2&gt;
&lt;p&gt;下面这些是这系列教程的一个大纲，都是通过GAMES101和202相关章节、阅读资料以及自己的理解做出来的，由于个人水平有限，所以难免有错漏之处，欢迎指出。同时迫于毕业时许诺的“三十岁之前一定要出一个商业品质的独立游戏，而现在只有两年时间了”以及做好可能RUN的准备带来的焦虑，所以这系列文章主要目的是&lt;strong&gt;基于现有的进度尽快收尾&lt;/strong&gt;而非&lt;strong&gt;达到最优化&lt;/strong&gt;，所以下面章节有的部分可能&lt;strong&gt;并不会完美实现&lt;/strong&gt;，这些部分我会特别注明。&lt;/p&gt;
&lt;p&gt;这个系列的文章预计如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;概览介绍：本章节，将会对整个项目涉及的知识做一个综述。  &lt;/li&gt;
&lt;li&gt;WebGPU基础与简单渲染器：通过自己实现的一个简单渲染器来论述WebGPU的能力。  &lt;/li&gt;
&lt;li&gt;路径追踪-场景数据组织：针对路径追踪，如何组织场景数据，涉及到PBR材质、glTF、场景合并等。  &lt;/li&gt;
&lt;li&gt;路径追踪-管线组织与GBuffer：针对路径追踪，如何组织渲染管线，同时论述GBuffer的生成。  &lt;/li&gt;
&lt;li&gt;路径追踪-BVH构建与求交：如何构建BVH，以及如何在CS中求交。  &lt;/li&gt;
&lt;li&gt;路径追踪-随机采样与BRDF：论述如何在路径追踪中运用蒙特卡洛采样实现直接光照和间接光照，以及运用BRDF光照模型。  &lt;/li&gt;
&lt;li&gt;路径追踪-降噪与色调映射：如何对充满噪点的图像进行空间和时间的滤波，最后输出如何进行色调映射。  &lt;/li&gt;
&lt;li&gt;路径追踪-毛玻璃材质与BSDF：如何运用BSDF（BRDF+BTDF）模型实现毛玻璃材质。  &lt;/li&gt;
&lt;li&gt;踩坑与调试心得：对于WebGPU这样一个实验性的API（至少当时），我是如何进行调试的血泪史（主要是CS部分）。  &lt;/li&gt;
&lt;li&gt;性能优化：包括BVH并行构建、WebGPU管线优化等。  &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;在这其中，章节7的降噪部分，目前大概率只会涉及到&lt;strong&gt;静态场景的简单滤波&lt;/strong&gt;，而不会有&lt;strong&gt;Motion Vector&lt;/strong&gt;、&lt;strong&gt;SVGF&lt;/strong&gt;之类的进阶实现；章节8的BSDF&lt;strong&gt;至今仍未实现，并且不一定会实现&lt;/strong&gt;，但关于透射整个框架已经搭好，会尽量做完吧；章节10的性能优化&lt;strong&gt;大概率不做了&lt;/strong&gt;，如果梦想推进顺利可能会在未来某一天忽然补完。&lt;/p&gt;
&lt;p&gt;（以上其实就是风险声明，烂尾别找我（逃&lt;/p&gt;
&lt;h2&gt;概览介绍&lt;/h2&gt;
&lt;p&gt;本项目主要分为两部分，第一是WebGPU部分，第二是路径追踪部分。&lt;/p&gt;
&lt;h3&gt;WebGPU&lt;/h3&gt;
&lt;p&gt;实时渲染在工业领域的应用离不开各个图形API，主流的API主要被各个系统厂商和标准委员会（实际上背后也是厂商）所支持，比如过去的OpenGL，OpenGL是一个跨平台的图形API，其迭代了漫长的岁月并在这些岁月中占据了这个领域大部分份额。在十年前，Web作为一个通用的平台也支持了图形接口，其名为WebGL，是OpenGL ES2在Web上的一个实现，在当时为Web带来了3D的可能。&lt;/p&gt;
&lt;p&gt;但这也是十年前的事情了，在今天，图形API也遵循了“分久必合、合久必分”的规律，分裂成了DX12、Metal和Vulkan，这些API都更加底层、给开发者提供了更深层的可控性（当然同时也带来了更高的开发难度），相比于OpenGL，可谓是“自动挡”和“手动挡”的区别。而反观Web平台，这十年来却似乎毫无长进，在OpenGL迭代了如此多的版本后仍然停留在ES2的时代，只能打些扩展补丁缝缝补补，这当然很大一部分苹果不支持WebGL2的锅（虽然宣布将支持，但太晚了）。但苹果虽然在支持WebGL2上摸鱼，却也同时推进了更激进的下一代Web图形接口方案——WebGPU。关于WebGPU标准这几年的不断延期、标准的不断变更、API直到近两个月还在改、Shader变了一次又一次的槽就不吐了，总之，作为Web开发者的我们终于也有对标DX、Metal、Vulkan的图形API了，而事实上其API设计确实是借鉴了它们，并且用起来也足够简单。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;值得欣慰的是，谷歌宣布Chrome 94的正式版将会正式支持WebGPU。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;WebGPU标准目前的状态基本已经稳定，可见&lt;a href=&amp;quot;https://www.w3.org/TR/webgpu&amp;quot;&gt;WebGPU标准&lt;/a&gt;。同时其使用的着色器也已确定，名为&lt;strong&gt;wgsl&lt;/strong&gt;，详见&lt;a href=&amp;quot;https://www.w3.org/TR/WGSL&amp;quot;&gt;WebGPU Shading Language&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;在此项目中，我使用WebGPU实现了一个渲染器，其拥有&lt;code&gt;UniformBlock&lt;/code&gt;、&lt;code&gt;Effect&lt;/code&gt;、&lt;code&gt;Material&lt;/code&gt;、&lt;code&gt;Geometry&lt;/code&gt;、&lt;code&gt;Texture&lt;/code&gt;、&lt;code&gt;RenderTexture&lt;/code&gt;等资源的抽象，也有&lt;code&gt;Scene&lt;/code&gt;、&lt;code&gt;Node&lt;/code&gt;、&lt;code&gt;Camera&lt;/code&gt;、&lt;code&gt;Light&lt;/code&gt;的场景管理，还有&lt;code&gt;Mesh&lt;/code&gt;、&lt;code&gt;ImageMesh&lt;/code&gt;、&lt;code&gt;ComputeUnit&lt;/code&gt;这样负责渲染和计算的单元，基本可以说是麻雀虽小五脏俱全。&lt;/p&gt;
&lt;p&gt;除此之外，项目也支持由我之前写的&lt;a href=&amp;quot;https://github.com/hiloteam/SeinJSUnityToolkit&amp;quot;&gt;seinjs-unity-toolkit&lt;/a&gt;导出的PBR材质的glTF文件，来作为内置的路径追踪渲染器的资源。&lt;/p&gt;
&lt;h3&gt;路径追踪&lt;/h3&gt;
&lt;p&gt;光线追踪是不同于光栅化的另一种渲染思路，或者说才是最符合直觉的思路。但由于其性能开销较高，所以很少在实时领域使用，基本都用于离线渲染。但在硬件性能日益变强的今天，实时光线追踪已然成为可能。光线追踪有很多种实现方式，最适合做实时的便是路径追踪：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/webgpu-rt/1/1.png&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;如图，其本质上是从人眼（或者说相机）发射射线，和场景中的物体求交，然后利用“光沿直线传播”的原理反向追踪计算光路，然后再计算直接或间接光照，实际上现在的光栅化流程可以看做是路径追踪中的第一步。路径追踪主要优点是可以实时计算出间接光照，即便是由于性能问题目前往往只能做到一次间接光照计算，但效果也已经很不错了。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;但注意，路径追踪并非是万能的，其假设的“光沿直线传播”在遇到透镜时可能会失效。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;所以，路径追踪的第一步就是要将整个场景的数据组织起来。我们现在往往将模型视为三角面的集合，并且用“材质”来描写模型的渲染方式。这在光栅化的流程中很自然，但对于路径追踪，由于光线的求交是针对整个场景的，所以必然涉及到整个场景模型的合并，也涉及到如何将我们熟悉的“模型-材质”模式转换成路径追踪适用的模式，这也就涉及到场景的合并，以及合并中材质、贴图等资源的管理。&lt;/p&gt;
&lt;p&gt;有了合并过的场景，我们还要有一个方法把整个渲染流程管理起来，路径追踪和传统的光栅化流程不太一样，其前面部分和传统的延迟管线有点类似，但后面却大相径庭，所以如何合理得组织渲染流程也是个问题。&lt;/p&gt;
&lt;p&gt;接下来便是光线和三角面如何求交的问题，诚然我们可以直接暴力求交，这对于小场景也不是不可以，但场景稍微大一点便是巨量的性能开销，这就要求我们去寻找更快的求教方法，也就引出一个问题——如何划分场景的层次来加速求交。这里我使用的是现在最常见的BVH，所以这一步就是论述如何构建BVH以及如何在CS中对BVH求交。&lt;/p&gt;
&lt;p&gt;取得了光线和物体表面的交点，接下来要解决的便是光照计算。这里做的是真实渲染，走的是PBR流程（这么说可能有点不严谨），而如果要做相对物理正确的渲染，就会涉及到目前大家都在用的BRDF模型。在BRDF的描述中，一个着色点的最终结果是由其法线半球内各个方向的光线的贡献总和，而我们一次只能发射一条射线，并且每次间接光反射也只能反射或透射出一条，这就引出了“蒙特卡洛采样”，也引出了直接光照、间接漫反射、间接高光反射、能量衰减、重要性采样等等。&lt;/p&gt;
&lt;p&gt;解决了采样问题，随之而来的是噪声和性能问题，首先是性能问题，诚然我们可以在同一帧发射大量射线来直接收敛出最终结果，但这往往意味着极高的开销，但如果不这样做，就又会导致极大的噪声。而这就引出了非常经典的“时间换空间”思想——将一帧多次拆成一次多帧，然后再来解决降噪。降噪首先要考虑时域的滤波，即“Temporal Filter”，使用一定的权重将当前帧和前一帧加权混合，不断加重上一帧的权重来使得结果最终收敛。以时间滤波为基准，还可以加入空间滤波进行辅助，本项目用到了联合双边滤波。对于静态场景这基本已经足够，但对于动态场景还需要做的更多，比如引入“Motion Vector”之类的，也会面临更多问题。除此之外，由于整个场景是HDR的，还需要进行色调映射防止过曝。&lt;/p&gt;
&lt;p&gt;如果只考虑非透明物体，功能方面到此基本可以说结束了，若要考虑毛玻璃这种材质，必须要引入BSDF模型，同时要考虑BTDF和BRDF。除了光照模型之外，透射也会影响到求交的计算，以及终止条件的判定。&lt;/p&gt;
&lt;p&gt;然后就是对于WebGPU这么一个比较新的图形API如何调试的问题，尤其是如何调试CS，经过血泪尝试我构建了一套使用Storage Buffer来调试的方法。。。哎。。。&lt;/p&gt;
&lt;p&gt;最后是优化部分，其实项目中还是有很多地方可以优化的，首当其冲的就是BVH的构建和求交部分，可以使用莫顿码进行GPU加速。其次WebGPU部分也可以做一些优化，比如Binding Group部分。其他好像。。。想不起来了。。。&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;我好菜啊感觉还是有很多地方没有理解深刻很容易忘尽力了，但是为了美少女也值得了（虽然发现最后性能好像并搞不定美少女...），但是开坑了就得含泪填完这是我作为码农一面的底线，希望大家多多指正。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Monday, 06 Sep 2021 03:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2021.09.06 03:00:article/Skill-2021_09_06_a</guid>
<category>WebGPU</category>
<category>路径追踪</category>
<category>光线追踪</category>
<category>图形学</category>
</item>

<item>
<title>记一次向WebAssembly的移植:gl-matrix</title>
<link>http://dtysky.moe/article/Skill-2019_06_23_a</link>
<description>&lt;p&gt;项目地址，欢迎Star（如果你觉得还行）：&lt;a href=&amp;quot;https://github.com/dtysky/gl-matrix-wasm&amp;quot;&gt;gl-matrix-wasm&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;由于某些工作上的原因，以及个人兴趣，配合之前用Rust+WASM写软件渲染器的踩坑，端午花了三天将&lt;strong&gt;gl-matrix&lt;/strong&gt;移植到了WASM（但因为各种奇奇怪怪的事情一直没时间发文）。此移植包含gl-matrix的&lt;strong&gt;所有功能&lt;/strong&gt;，同时具备完整的&lt;strong&gt;单元测试&lt;/strong&gt;。库本身以Rust + wasm-bindgen + wasm-pack + webpack4的形式开发，使用TS + Karma + Mocha来写单元测试（当然前两年还能扯一扯“优雅”啥的，现在这些都常识了也没啥说的）。在使用方面，我提供了两种使用模式，来应对不同的场合。同时还使用了很多Trick来在不破坏工程性的同时优化性能，某种意义上可以当做Rust来写WASM的工程的模板。  &lt;/p&gt;
&lt;p&gt;当然，新技术（或许也不算新了）总是看似很美好，坑却也无限大。具体详细的经历我就不说了（也没空），下面就大概说说一些坑，以及个人认为的WASM的优缺点吧。&lt;/p&gt;
&lt;h2&gt;坑（缺点）&lt;/h2&gt;
&lt;p&gt;坑其实和WASM本身关系不是特别大，主要是工具本身的坑，而有的缺点则是WASM目前切实的缺点了：  &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;功能限制。如果你以为有了Rust就可以使用Rust所有能力快速开发一个WASM项目就太天真了，如果你的项目完全使用Rust编写或许可以，但一旦要和JS端进行交互，那就只能使用Rust的一个不大的子集（没有生命周期，没有trait等等...），函数无法返回引用（全走裸指针然后&lt;code&gt;unsafe&lt;/code&gt;吧www要么就全走移动（其实是拷贝））。&lt;/li&gt;
&lt;li&gt;内存管理。没有GC，需要手动内存管理。其实这确实也不是什么坑，但对于大多数被JS惯坏的前端也确实是个问题。而且如果你完全使用wasm-bindgen来托管内存管理（虽然还是要在js端&lt;code&gt;xx.free()&lt;/code&gt;释放），那么你对内存的掌握又会下降，这可能会造成一些性能坑（比如在本库中，由于完全托管给Rust创建结构体，比自己在JS端直接操作&lt;code&gt;WebAssembly.Memory&lt;/code&gt;实例进行&lt;code&gt;ArrayBuffer&lt;/code&gt;的操作要慢的多，当然这个还有#4的问题）。&lt;/li&gt;
&lt;li&gt;灵活性。这个其实是Rust的问题，其实仍然不算问题，只是JS这边确实有时候有的场景过于灵活，而Rust的借用规则由比较严格。举个例子，gl-matrix中经常会有&lt;code&gt;vec3.add(a, a, b)&lt;/code&gt;这种使用方式，而这个是无法通过Rust的借用规则的，但它编译到WASM后又没办法在编译期检查，咋整呢——它搞了个运行时检查，这不但会使得逻辑无法实现还会使得性能有所下降（除此之外还有空指针检查）。当然这正是Rust可靠的表现，所以我选择把它黑掉（划掉）。&lt;/li&gt;
&lt;li&gt;工程灵活性。这个主要是Rust工具链的可定制性问题。目前这套流程可以生成wasm + js + ts头的组合，在够用的情况下很完美。但...如果要做一些魔改（比如修改生成的JS来优化性能或自定义功能），就比较难受了，我这边采用的是超级Hack的方法，详见&lt;strong&gt;wasm-opt.js&lt;/strong&gt;的黑魔法。&lt;/li&gt;
&lt;li&gt;互操作开销。Rust本身通过ABI和JS互调用，但这个开销其实还蛮高的，这个可能会在某些状况打消掉WASM本身计算性能的优势。&lt;/li&gt;
&lt;li&gt;异步。不错，目前WASM本身编译是强行异步的，这个可能会对代码组织有些坑（主要是库），如果是工程代码使用Webpack和异步模块其实也不是啥问题。&lt;/li&gt;
&lt;li&gt;性能。这个其实不是WASM的问题，而是Rust工具链的问题（应该是），导致同样代码编译出来比CPP + EMCC编译出来慢两倍左右，具体见&lt;a href=&amp;quot;https://github.com/rustwasm/wasm-bindgen/issues/1585&amp;quot;&gt;Issue 1585&lt;/a&gt;。这个问题我还没查明到底是啥（没空），如果有大佬帮忙再感谢不过。&lt;/li&gt;
&lt;li&gt;体积。不错你没看错，同等功能WASM的体积比JS大两倍左右（至少在这个纯粹的库）。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;优点&lt;/h2&gt;
&lt;p&gt;当然WASM本身优点也是有很多的（要不要它干嘛）：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;可控。在WASM中，内存对于开发者是完全透明的，它就是一块线性的ArrayBuffer。Rust在其之上帮我们抽象出了堆和栈，帮我们做了一些事情，但其本质还是非常透明的，而精确的内存控制对于游戏这种复杂应用的好处是巨大的。&lt;/li&gt;
&lt;li&gt;可预测。在WASM中，性能是可以预测的。你不用担心GC（The world！）或是JIT带来的迷失感。同一个函数执行100次和1000次稳稳差10倍左右。&lt;/li&gt;
&lt;li&gt;低开销。GPU和内存开销都比JS更低，这个不用多说。&lt;/li&gt;
&lt;li&gt;性能好。根据不同业务属性，性能提升不太一样。但是&lt;strong&gt;纯计算性能&lt;/strong&gt;毋庸置疑是很高的，当然JIT后的JS也不差，这个下面会详细说。&lt;/li&gt;
&lt;li&gt;深入融合Webpack工作流。这算是一个工程上可用的进步吧（比以前），Webpack4中WASM已经是一等公民了。&lt;/li&gt;
&lt;li&gt;不用再写JS啦！对部分开发者这个可能是最重要的（当然有TS这个属性可能弱了点）。&lt;/li&gt;
&lt;li&gt;完整。这个是说Rust这套流程的，它的&lt;strong&gt;web_sys&lt;/strong&gt;和&lt;strong&gt;js_sys&lt;/strong&gt;实现了WebIDL的完整Port，可用性还是很不错的（如果你不在意性能的话）。&lt;/li&gt;
&lt;li&gt;还有的请补充......&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;性能&lt;/h2&gt;
&lt;p&gt;性能方面前面也说了，WASM的运算性能是可预测的，而JS则由于JIT和GC会比较难以预测。对于本应用，由于JS版本使用了&lt;code&gt;TypedArrayBuffer&lt;/code&gt;，以及进行的都是非常容易优化的&lt;strong&gt;简单纯数学运算&lt;/strong&gt;，所以JIT后的JS版本性能可以说达到了JS可达到的上限。但即使在这种状况下，对于大多数的测试WASM版本还是要稳稳压&lt;strong&gt;1.5倍&lt;/strong&gt;左右：  &lt;/p&gt;
&lt;p&gt;详见[Benchmark(Matrix4, 2015 RMBP, Chrome)]（https://github.com/dtysky/gl-matrix-wasm/blob/master/Benchmark.md。  &lt;/p&gt;
&lt;p&gt;而对于真实世界，性能测试就没有Benchmark这么简单了，我写了DEMO来进行这个测试（当然对于Web 1000个物体的场景已经很大了）：&lt;a href=&amp;quot;http://gl-matrix-wasm.dtysky.moe/&amp;quot;&gt;http://gl-matrix-wasm.dtysky.moe/&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;可见其实WASM版本在真实场景中还是有优势的（当然，Safari除外，其WASM现在优化得还不行）。  &lt;/p&gt;
&lt;p&gt;当然，除了这个库本身我也测试过别的应用，比如CRC32、数字图像处理、DOM操作等等，大致结论就是在&lt;strong&gt;无法简单优化的密集计算场景下，WASM有显著优势（比如模型压缩解压缩，加密解密）&lt;/strong&gt;，而在其他场合目前看来就差了点意思，投入产出比较低。&lt;/p&gt;
&lt;h2&gt;未来&lt;/h2&gt;
&lt;p&gt;未来来看，我觉得WASM有几点很值得关注：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;WASI。WebAssembly System Interface，不用多说，这说明我们以前对WASM本身都有很深的误解，它&lt;strong&gt;并不是为Web而生的汇编语言&lt;/strong&gt;，而是个&lt;strong&gt;恰好可以跑在Web上的通用虚拟机&lt;/strong&gt;。这是个好东西，对未来WASM在更多场景可用而言很重要。&lt;/li&gt;
&lt;li&gt;SIMD。&lt;strong&gt;simd.js&lt;/strong&gt;提案被废弃后，&lt;strong&gt;SIMD on WebAssembly&lt;/strong&gt;就成了Web使用SIMD唯一可预计的方式了，而SIMD可以为图形应用带来的好处也不用多说。&lt;/li&gt;
&lt;li&gt;多线程。相比于阉割了&lt;code&gt;SharedArrayBuffer&lt;/code&gt;的&lt;code&gt;Worker&lt;/code&gt;（虽然有&lt;code&gt;Transferable&lt;/code&gt;对象），这东西在某些场合很重要。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;结论&lt;/h2&gt;
&lt;p&gt;一个项目是否要选择WASM，还是要从其适用性出发。如果你的项目是&lt;strong&gt;对计算要求较高&lt;/strong&gt;，而且&lt;strong&gt;没有频繁的WASM &amp;lt;-&amp;gt; JS互操作&lt;/strong&gt;，同时能保证&lt;strong&gt;内存都是申请在WASM虚拟机中&lt;/strong&gt;最好，那么你的项目是比较适合使用WASM的。否则至少在目前时点不太建议。&lt;/p&gt;
&lt;p&gt;特别要求性能，目前暂时考虑CPP版本，Rust版本在工程化的角度做的很好，而且团队足够重视，应该是未来的主流。&lt;/p&gt;
&lt;p&gt;当然WASM本身目前起步也不是很久，它也在不断变好，相信未来它的能力和性能都会更强，也能适用于更多的领域（虽然大多领域应该还是干不过JS，“我为啥要功夫学这玩意，JS两下撸完回家打游戏不好吗？”）。&lt;/p&gt;
&lt;p&gt;踩坑或许还不是特别深入，大佬觉得有问题或者建议请指正。&lt;/p&gt;
&lt;h2&gt;招人&lt;/h2&gt;
&lt;p&gt;本来想发招人的但目前也比较难，不过你有P7的实例肯定是没问题的，我们是支付宝互动图形团队，致力于在这个前端迷失的时代，在前端老本行的方向走的更远，给用户带来更好的体验。我们有自研Web3D/2D游戏引擎、巨量用户和业务场景，还和小程序团队直接相邻，有想法请直接联系我。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;完。&lt;/strong&gt;&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Sunday, 23 Jun 2019 22:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2019.06.23 22:00:article/Skill-2019_06_23_a</guid>
<category>WebAssembly</category>
<category>Rust</category>
<category>Math</category>
<category>Matrix</category>
<category>gl-matrix</category>
<category>图形学</category>
</item>

<item>
<title>前端视觉交互——全屏泛光</title>
<link>http://dtysky.moe/article/Skill-2018_09_09_a</link>
<description>&lt;p&gt;Code: &lt;a href=&amp;quot;http://github.com/dtysky/paradise/tree/master/src/collection/GlobalBloom&amp;quot;&gt;github.com/dtysky/paradise/tree/master/src/collection/GlobalBloom&lt;/a&gt;，&lt;a href=&amp;quot;http://github.com/dtysky/paradise/tree/master/src/collection/GlowEmissiveMap&amp;quot;&gt;github.com/dtysky/paradise/tree/master/src/collection/GlowEmissiveMap&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;Demo: &lt;a href=&amp;quot;http://paradise.dtysky.moe/effect/global-bloom&amp;quot;&gt;paradise.dtysky.moe/effect/global-bloom&lt;/a&gt;，&lt;a href=&amp;quot;http://paradise.dtysky.moe/effect/glow-emissive-map&amp;quot;&gt;paradise.dtysky.moe/effect/glow-emissive-map&lt;/a&gt;  &lt;/p&gt;
&lt;h2&gt;原理&lt;/h2&gt;
&lt;p&gt;本次的效果是全局辉光（Bloom），又称泛光。它其实是一种作用于特定区域的外发光效果。&lt;/p&gt;
&lt;h3&gt;外发光&lt;/h3&gt;
&lt;p&gt;在游戏中，我们经常可以见到外发光的效果。典型的比如在室内场景的吊灯、电子设备的屏幕、室外夜晚的路灯，还有车灯等。这些场景的共性是它们提供了亮度和气氛的强烈视觉信息。当我们观看屏幕等物体时，它们到达眼睛的光强是有限的，所以我们便通过其周围的辉光/泛光/光晕来辨别它们。在现实中，这些辉光是由于光线在大气或我们眼睛中产生散射而造成的。而利用数字图像处理的方法，我们可以比较简单得模拟这种效果。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2018_09_09a/0.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;h3&gt;RTT和后处理&lt;/h3&gt;
&lt;p&gt;一个最通用并且效果最佳的实现辉光的方法是&lt;strong&gt;后处理&lt;/strong&gt;，它要求我们并不直接把主场景渲染的结果显示到屏幕上，而是将这个结果保存到一张纹理上，这个过程称为&lt;strong&gt;RTT(渲染到纹理)&lt;/strong&gt;。拿到这个纹理之后，我们再渲染另一个只有一个全屏的Plane的场景，将前面保存的纹理作为贴图传入这个Plane的材质中进行渲染，在这个渲染的过程中我们可以添加一些特殊的效果，本质上，这些效果其实是应用于第一次渲染的主场景的图像的，所以这其实是一种&lt;strong&gt;对当前渲染结果的数字图像处理&lt;/strong&gt;，这也就是后处理的本质。  &lt;/p&gt;
&lt;p&gt;对于这里要说的全局辉光，下面这张图比较清晰得描述了整个过程：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2018_09_09a/1.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;首先我们将主场景渲染到一张纹理上，而后将其应用到后处理专用场景中Plane的材质中，我们不断地重复RTT和后处理过程，最终得到了一张模糊后的纹理，最终我们将这个纹理和最初主场景的原始渲染纹理进行alpha混合，便可以得到最终的结果。下面让我们细致地分析一下整个过程。&lt;/p&gt;
&lt;h3&gt;渲染主场景&lt;/h3&gt;
&lt;p&gt;首先我们将主场景渲染到纹理：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rendererTarget0&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;THREE&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;WebGLRenderTarget&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;window&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;innerWidth&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;window&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;innerHeight&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;renderer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;render&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;scene&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;camera&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rendererTarget0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;我们将this.rendererTarget0保存下来，作为最后混合的原始图像和阈值化的输入。&lt;/p&gt;
&lt;h3&gt;阈值化&lt;/h3&gt;
&lt;p&gt;当将主场景渲染到纹理后，我们要做的第一步就是阈值化。阈值化是图像处理中的一个概念，其本质是是将灰度高于某个值的像素颜色设为1，反之设为0，这是针对灰度图像的阈值化。而在本例中，我们对这个阈值化进行特例化——我们设定一个阈值，将纹理中灰度低于这个阈值的像素设为(0, 0, 0)，而灰度高于此阈值的像素，则保留，如此一来我们便可以得到一张只有“辉光”处色彩信息的纹理。将这张纹理保留下来，进行下一阶段：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2018_09_09a/2.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;这里有两个注意点，其一是&lt;strong&gt;阈值的选取&lt;/strong&gt;，其二是&lt;strong&gt;降采样&lt;/strong&gt;。  &lt;/p&gt;
&lt;p&gt;阈值的选取决定着辉光像素的筛选，一般有两种方法——全局阈值和局部阈值，全局阈值又有玄学调参、直方图选取等方式，局部阈值要和局部滤波器结合，这些内容在我大学时的论文中有详细分析，详见&lt;a href=&amp;quot;http://dtysky.moe/article/Skill-2015_05_16_a&amp;quot;&gt;FPGA/图像处理】点操作—阈值化&lt;/a&gt;、&lt;a href=&amp;quot;http://dtysky.moe/article/Skill-2015_05_22_a&amp;quot;&gt;FPGA/图像处理】局部滤波器-局部阈值化&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;降采样，则是指降低纹理分辨率，降低后处理运算的开销。一般来讲是将要后处理的纹理降低到屏幕大小的四分之一，这样也可以使得模糊运算用更小的窗口模糊更大的范围。当然降采样也会带来&lt;strong&gt;走样&lt;/strong&gt;的问题，这个的解决方法需要具体项目具体考量，这里不再赘述。  &lt;/p&gt;
&lt;p&gt;下面来看看代码：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rendererTarget1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;THREE&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;WebGLRenderTarget&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;window&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;innerWidth&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;window&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;innerHeight&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;quad&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;material&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;thresholdMaterial&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;thresholdMaterial&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;uniforms&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;tDiffuse&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;value&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rendererTarget0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;texture&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;renderer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;render&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;finalScene&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;finalCamera&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rendererTarget1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;uniform&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;threshold&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;uniform&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;sampler2D&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;tDiffuse&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;varying&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;vec2&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;vUv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;void&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;main&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;vec4&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;color&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;texture2D&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tDiffuse&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;vUv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;gray&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;.299&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;g&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;.587&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;b&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;.114&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;th_color&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;gray&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;threshold&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;1.&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;gray&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;threshold&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;gl_FragColor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;gl_FragColor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;可见，这里我通过阈值&lt;code&gt;threshold&lt;/code&gt;来控制哪些像素和辉光像素。&lt;/p&gt;
&lt;h3&gt;分步模糊&lt;/h3&gt;
&lt;p&gt;得到了阈值化并降采样后的纹理&lt;code&gt;rendererTarget1&lt;/code&gt;后，便可以进行下一步的模糊了。模糊本质上是一种利用卷积计算的局部滤波器，这一点可以参考我之前对均值滤波器的分析：&lt;a href=&amp;quot;http://dtysky.moe/article/Skill-2015_05_20_b&amp;quot;&gt;【FPGA/图像处理】局部滤波器-均值滤波器&lt;/a&gt;。一般而言，此种模糊使用的都是&lt;a href=&amp;quot;https://zh.wikipedia.org/wiki/%E9%AB%98%E6%96%AF%E6%A8%A1%E7%B3%8A&amp;quot;&gt;高斯模糊&lt;/a&gt;，但我这里为了代码简单实例，用了一个XJB搞的均值滤波器。  &lt;/p&gt;
&lt;p&gt;如果看完了上面的文章，读者应该了解到局部滤波器本质上都是在一个&lt;strong&gt;窗口&lt;/strong&gt;中完成的，窗口的大小一般为3x3、5x5等，而处理，则是以当前像素为中心取得的窗口中所有像素做一个卷积运算，当然在此DEMO的做法中是全部相加然后除以一个玄学值&lt;code&gt;N&lt;/code&gt;，这相当于乘了一个所有元素都是&lt;code&gt;1/N&lt;/code&gt;的卷积。  &lt;/p&gt;
&lt;p&gt;一般而言，这种操作对于每个像素都要做一个双重循环，毕竟要遍历每一行的每一列嘛，这样一来，如果是一个&lt;code&gt;n x n&lt;/code&gt;的窗口，运算的总时间复杂度是&lt;code&gt;O(n^2)&lt;/code&gt;（GPU运算先天并行，不考虑纹理大小）。这里我们就可以想到，能不能拆分行和列的两次模糊，来降低时间复杂度呢？当然是可以的，这也就是所谓的&lt;strong&gt;分布模糊&lt;/strong&gt;。  &lt;/p&gt;
&lt;p&gt;分布模糊的想法很简单，就是&lt;strong&gt;以空间换时间&lt;/strong&gt;，我们先对每个像素进行横向的模糊，渲染到一个纹理，然后再输入这个纹理，对每个像素进行纵向的模糊。这样一来，我们仍然可以得到最后的模糊结果，而时间复杂度却降到了&lt;code&gt;O(n)&lt;/code&gt;：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2018_09_09a/3.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2018_09_09a/4.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;让我们看看代码：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rendererTarget2&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;THREE&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;WebGLRenderTarget&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;window&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;innerWidth&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;window&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;innerHeight&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;quad&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;material&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;localFilterMaterial&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;localFilterMaterial&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;uniforms&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;vDirection&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;value&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;false&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;localFilterMaterial&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;uniforms&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;tThreshold&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;value&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rendererTarget1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;texture&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;renderer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;render&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;finalScene&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;finalCamera&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rendererTarget2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;localFilterMaterial&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;uniforms&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;vDirection&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;value&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;localFilterMaterial&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;uniforms&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;tThreshold&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;value&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rendererTarget2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;texture&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;renderer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;render&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;finalScene&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;finalCamera&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rendererTarget1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;cp&amp;quot;&gt;#define WINDOW_SIZE 5.&lt;/span&gt;
&lt;span class=&amp;quot;cp&amp;quot;&gt;#define DOUBLE_WINDOW_SIZE 10.&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;uniform&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;stepSize&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;uniform&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;bool&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;vDirection&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;uniform&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;sampler2D&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;tThreshold&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;varying&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;vec2&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;vUv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;void&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;main&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;vec4&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;blur&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;texture2D&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tThreshold&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;vUv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vDirection&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;1.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;WINDOW_SIZE&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;1.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;n&amp;quot;&gt;blur&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;texture2D&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tThreshold&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;vUv&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;stepSize&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;
      &lt;span class=&amp;quot;n&amp;quot;&gt;blur&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;texture2D&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tThreshold&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;vUv&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;stepSize&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;1.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;WINDOW_SIZE&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;1.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;n&amp;quot;&gt;blur&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;texture2D&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tThreshold&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;vUv&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;stepSize&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;
      &lt;span class=&amp;quot;n&amp;quot;&gt;blur&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;texture2D&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tThreshold&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;vUv&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;stepSize&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;0.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

  &lt;span class=&amp;quot;n&amp;quot;&gt;blur&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rgb&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;blur&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rgb&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;DOUBLE_WINDOW_SIZE&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

  &lt;span class=&amp;quot;n&amp;quot;&gt;gl_FragColor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;blur&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这里我用&lt;code&gt;vDirection&lt;/code&gt;控制是横向模糊还是纵向模糊，用&lt;code&gt;stepSize&lt;/code&gt;控制窗口中每个像素的uv偏移，用&lt;code&gt;WINDOW_SIZE&lt;/code&gt;定义窗口大小。通过这两次运算，便得到了模糊后的高光纹理图像。&lt;/p&gt;
&lt;h3&gt;辉光和色调映射&lt;/h3&gt;
&lt;p&gt;有了原始的渲染纹理和模糊后的辉光纹理，我们便可以进行最后的混合了：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;quad&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;material&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;bloomMaterial&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;bloomMaterial&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;uniforms&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;tDiffuse&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;value&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rendererTarget0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;texture&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;bloomMaterial&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;uniforms&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;tBlur&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;value&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rendererTarget1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;texture&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;renderer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;render&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;finalScene&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;finalCamera&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;uniform&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;sampler2D&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;tDiffuse&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;uniform&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;sampler2D&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;tBlur&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;uniform&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;toneExp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;varying&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;vec2&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;vUv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;void&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;main&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;vec3&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;diffuseColor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;texture2D&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tDiffuse&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;vUv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rgb&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;vec3&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;blurColor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;texture2D&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tBlur&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;vUv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rgb&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;vec3&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;result&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;diffuseColor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;blurColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

  &lt;span class=&amp;quot;n&amp;quot;&gt;result&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;exp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;result&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;toneExp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

  &lt;span class=&amp;quot;n&amp;quot;&gt;gl_FragColor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;result&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;1.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;可见我分别对两个纹理进行了采样，而后将它们简单地混合到了一起，当然我这里没有用alpha混合而是简单的色彩混合，这和我前面模糊时的做法有关。由于是色彩混和，所以相加得到的结果&lt;code&gt;result&lt;/code&gt;中的色彩值可能会超过&lt;code&gt;0 ~ 1&lt;/code&gt;的色域，也就是说，我们得到的是一个高动态范围的结果。这就需要我们用&lt;strong&gt;Tone Mapping（色调映射）&lt;/strong&gt;方法，来将高动态的渲染结果归一化到有限动态范围中，这也是&lt;strong&gt;HDR&lt;/strong&gt;的原理之一。  &lt;/p&gt;
&lt;p&gt;色调映射本身是一个大话题，其发展历史和种类都十分繁多，在这里我们使用的是一种简单高效效果又不错的方法，即公式&lt;code&gt;result = vec3(1.) - exp(-result * toneExp);&lt;/code&gt;，这个公式利用定义域在&lt;code&gt;-∞ ~ 0&lt;/code&gt;的指数函数来进行映射，配合一个参数&lt;code&gt;toneExp&lt;/code&gt;来调整曲线。  &lt;/p&gt;
&lt;p&gt;至此，我们便得到了最终的结果：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2018_09_09a/5.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;h3&gt;非后处理实现&lt;/h3&gt;
&lt;p&gt;对于少量模型的局部辉光也是可行的，但技术和上面的描述完全不同。它本质上是创建一个Billboard，上面渲染着平滑的“辉光纹理”，而后让这个Billboard跟随着要进行辉光的模型。但这种方法通常还是比较适用于2D物体，对于要时长变换视角的3D物体比较困难，适用面比较窄吧。&lt;/p&gt;
&lt;h3&gt;更可控&lt;/h3&gt;
&lt;p&gt;一般的辉光流程按照上一章的&lt;strong&gt;全局辉光-后处理+色调映射&lt;/strong&gt;即可实现，不过那个实现方法有个弊端，就是阈值化这一步，辉光部分的筛选是程序决定的，很大程度上不可控，在现实应用中，我们需要更可控的方法来操纵辉光达到想要的效果。&lt;/p&gt;
&lt;h3&gt;自发光贴图&lt;/h3&gt;
&lt;p&gt;决定最终渲染风格的是美术人员，所以我们当然希望美术能决策整个场景，甚至将这种决策细化到单个模型。方法也很简单，和其他很多效果一样，我们可以建立一张纹理，通过采样这个纹理来决定辉光的强度和颜色，借此来取代阈值化的过程。  &lt;/p&gt;
&lt;p&gt;比如在这个DEMO中，我渲染了一个宇宙战舰（Unity免费模型），为了让战舰的特定部分辉光，我直接用EmissiveMap，即自发光纹理作为辉光的来源，由于战舰整体分为三个部分，我这里只给出一个部分的EmissiveMap作为实例：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2018_09_09a/6.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;整体的渲染流程和标准的全局辉光并无区别，在第一步还是正常地渲染整个场景，只是在筛选辉光像素这一部分，我修改了模型的材质，这个材质只以&lt;code&gt;emissive&lt;/code&gt;和&lt;code&gt;emissiveMap&lt;/code&gt;为基础色，渲染的结果即为标准流程中阈值化的结果：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// init&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;scene&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;traverse&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;node&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;node&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;instanceof&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;THREE&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Mesh&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;material&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;material&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;THREE&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;MeshStandardMaterial&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;emissive&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;emissiveMap&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;originMaterials&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;uuid&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;emissiveMaterials&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;uuid&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;THREE&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;MeshBasicMaterial&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;color&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;emissive&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;map&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;emissiveMap&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;

&lt;span class=&amp;quot;c1&amp;quot;&gt;// loop&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;scene&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;traverse&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;node&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;node&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;instanceof&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;THREE&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Mesh&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;originMaterials&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;uuid&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;])&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;material&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;emissiveMaterials&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;node&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;uuid&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;renderer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;render&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;scene&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;camera&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rendererTarget1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2018_09_09a/7.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;接下来的流程和标准辉光流程一致。最终渲染效果如下：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2018_09_09a/8.jpg&amp;quot; /&gt;&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Sunday, 09 Sep 2018 22:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2018.09.09 22:00:article/Skill-2018_09_09_a</guid>
<category>HTML5</category>
<category>WebGL</category>
<category>Shader</category>
<category>Bloom</category>
<category>Light</category>
<category>Glow</category>
<category>Emissive</category>
<category>PostProcessing</category>
</item>

<item>
<title>前端视觉交互——轮廓边缘照明-菲涅尔反射</title>
<link>http://dtysky.moe/article/Skill-2018_09_08_a</link>
<description>&lt;p&gt;Code: &lt;a href=&amp;quot;http://github.com/dtysky/paradise/tree/master/src/collection/RimLightFresnel&amp;quot;&gt;github.com/dtysky/paradise/tree/master/src/collection/RimLightFresnel&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;Demo: &lt;a href=&amp;quot;http://paradise.dtysky.moe/effect/rim-light-fresnel&amp;quot;&gt;paradise.dtysky.moe/effect/rim-light-fresnel&lt;/a&gt;  &lt;/p&gt;
&lt;h2&gt;原理&lt;/h2&gt;
&lt;p&gt;本次实现的效果是轮廓边缘内发光。顾名思义，其指的是在模型轮廓边缘内部的发光效果，核心是“边缘”和“内部”这两个词，也适合“外发光”（辉光、泛光）最大的区别。由于是在模型内部的发光，所以我们完全可以针对模型自身的材质去实现，不必使用后处理，这一点在性能上有所优势。当然，最重要的还是它本身能达到的效果的适用范围。&lt;/p&gt;
&lt;h3&gt;现实中的考察&lt;/h3&gt;
&lt;p&gt;计算机图形学很大一部分都是对现实的仿真，所以我们需要先考量一下这个效果在现实中的表现和原理。在现实中，边缘轮廓外发光最常见的例子就是一个平整而深远的水面，比如湖面。  &lt;/p&gt;
&lt;p&gt;当我们站在湖边看着湖面时，你会发现在脚下的湖面中的水是透明的，其反射并不强烈，而当望向远处之时，却会发现水并不透明，反射十分强烈，有一种泛光的效果。也就是说，当你的视线和观察的具有此效应的物质表面的夹角越小时，反射约明显。  &lt;/p&gt;
&lt;p&gt;这个现象也可以从玻璃珠观察到。当看向玻璃珠的中心之时，会发现反射比较弱，视线越靠近珠子两侧则反射越明显。&lt;/p&gt;
&lt;h3&gt;菲涅尔反射&lt;/h3&gt;
&lt;p&gt;上面的这种现象在光学中叫做&lt;a href=&amp;quot;https://zh.wikipedia.org/wiki/%E8%8F%B2%E6%B6%85%E8%80%B3%E6%96%B9%E7%A8%8B&amp;quot;&gt;“菲涅尔反射”&lt;/a&gt;。其本质上是由光从一种介质传播到另一种介质中的反射和折射造成的。一般来讲，对于金属外的绝大多数介质，光总在法线法线入射时&lt;strong&gt;反射比最小&lt;/strong&gt;，即反光最少，而在和法线垂直的方向入射时，其反射比达到最大（不透射）。  &lt;/p&gt;
&lt;p&gt;这也就是说，我们可以在计算机中很简单地模拟这种现象，只要有模型上某顶点的法线和当前摄像机的视线，便可以通过很小的计算量计算出光强，从而得到这种轮廓边缘内发光的效果。&lt;/p&gt;
&lt;h3&gt;法线与视线&lt;/h3&gt;
&lt;p&gt;对于是一直在关注此系列的同学，对于法线和视线应当不陌生了。法线信息存储在模型的顶点数据中，携带着对某一图元所在平面的垂直向量的方向信息。而视线，则指的是摄像机前向向量（对于THREEJS是&lt;code&gt;forward&lt;/code&gt;属性）。  &lt;/p&gt;
&lt;p&gt;有了法线和视线，结合上面所述的菲涅尔发射原理，我们便可以视线效果，首先在顶点着色器中计算光强：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;uniform&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;vec3&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;view_vector&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;uniform&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;c&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;uniform&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;varying&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;vec2&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;vUv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;varying&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;intensity&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;void&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;main&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;vec3&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;v_normal&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;normalize&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normalMatrix&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;vec3&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;v_view&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;normalize&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;normalMatrix&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;view_vector&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

    &lt;span class=&amp;quot;n&amp;quot;&gt;intensity&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;pow&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;c&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;dot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;v_normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;v_view&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;n&amp;quot;&gt;vUv&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

  &lt;span class=&amp;quot;n&amp;quot;&gt;gl_Position&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;projectionMatrix&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;modelViewMatrix&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;片段着色器：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;uniform&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;sampler2D&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;diffuse&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;uniform&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;vec3&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;glow_color&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;varying&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;intensity&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;varying&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;vec2&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;vUv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;void&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;main&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;vec3&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;glow&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;glow_color&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;intensity&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

  &lt;span class=&amp;quot;n&amp;quot;&gt;gl_FragColor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;glow&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;texture2D&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;diffuse&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;vUv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;以上shader中，&lt;code&gt;view_vector&lt;/code&gt;为视线向量。我们首先将视线和法线和法线变换矩阵&lt;code&gt;normalMatrix&lt;/code&gt;求叉积并归一化，得到正确的视线和法线向量。之后再对这二者求点积，这个点积本质上其实就是两个向量夹角的余弦，由于余弦函数的性质，可知实现和法线平行时其值最大，垂直时其值最小。这一点和我们需要的恰恰相反，考虑到只用关心模型朝向视线这一边的渲染，我们只需要考虑夹角在&lt;strong&gt;-90 ~ 90deg&lt;/strong&gt;时的状况，显然得知其值域实际上是0 ~ 1，所以我们只需要简单得换算一下，便可以得到最终的结果：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;intensity&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;dot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;v_normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;v_view&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这便是最朴素的菲涅尔反射的光强计算。在此公式的计算下，当我们将这个材质运用到球体上时，球体上将会叠加一层“光罩”，光的强度在球心处为0，在球的边缘处为1。&lt;/p&gt;
&lt;h3&gt;更细致的控制&lt;/h3&gt;
&lt;p&gt;然而仅有朴素的效果是不够的，我们还希望对发光的效果有着更细致的控制，比如反光的范围、方向、光强增强的速度等。相比读者已经注意到了在顶点着色器中那个最终的公式了：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;intensity&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;pow&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;c&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;dot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;v_normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;v_view&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这个公式中有两个参数&lt;code&gt;c&lt;/code&gt;和&lt;code&gt;p&lt;/code&gt;，它们实际上就是提供效果细致控制的参数。其中&lt;code&gt;c&lt;/code&gt;控制的是发光的方向和范围，&lt;code&gt;p&lt;/code&gt;控制的是光强增强的速度和光强的极限。你可以在DEMO的调参控制器中调节它们来查看效果。&lt;/p&gt;
&lt;h3&gt;限制&lt;/h3&gt;
&lt;p&gt;菲涅尔反射是根据法线和视线的夹角来计算最终照明的光强的，所以对于&lt;strong&gt;立方体&lt;/strong&gt;、&lt;strong&gt;棱柱&lt;/strong&gt;这种平整模型，由于模型的每个面的法线都一致，所以无法达到想要的效果。如果一定要使用这种效果，可以考虑曲面细分平滑或者利用法线贴图来修改顶点法线。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Saturday, 08 Sep 2018 22:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2018.09.08 22:00:article/Skill-2018_09_08_a</guid>
<category>HTML5</category>
<category>WebGL</category>
<category>Shader</category>
<category>Rim</category>
<category>Light</category>
<category>Fresnel</category>
</item>

<item>
<title>前端视觉交互——顶点动画实现图片过度</title>
<link>http://dtysky.moe/article/Skill-2018_07_21_a</link>
<description>&lt;p&gt;Code: &lt;a href=&amp;quot;http://github.com/dtysky/paradise/tree/master/src/collection/ImageFragmentTransition&amp;quot;&gt;github.com/dtysky/paradise/tree/master/src/collection/ImageFragmentTransition&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;Demo: &lt;a href=&amp;quot;http://paradise.dtysky.moe/effect/image-fragment-transition&amp;quot;&gt;paradise.dtysky.moe/effect/image-fragment-transition&lt;/a&gt;  &lt;/p&gt;
&lt;h2&gt;原理&lt;/h2&gt;
&lt;p&gt;这是一个非典型的顶点着色器实现的顶点动画的例子。它构造了一个可被打碎的平面，使得我可以在碎片化的过程中对两张图片做平滑过渡。&lt;/p&gt;
&lt;h3&gt;使用顶点构成三角片&lt;/h3&gt;
&lt;p&gt;看到这里，需要读者对OpenGL图形绘制底层有基本的了解。我们知道，OpenGL绘制一个曲面，本质上绘制的是一个个顶点构成的一个个三角面，这一个个三角面组合起来，便形成了整体的曲面。而这些顶点数据都是存储在buffer中的，我们需要一开始生成一个buffer，然后将其作为attributes一次性提交给GPU，之后就可以通过少量uniform变量控制渲染啦。  &lt;/p&gt;
&lt;p&gt;以ThreeJS为例，为了绘制一个三角形，我们需要进行几个步骤：  &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;构造基于Buffer的几何体并生成顶点数据：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;function&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;disposeArray() {&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;array&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;null&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;geometry&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;THREE&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;BufferGeometry&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;positions&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;x0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;y0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;z0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;x1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;y1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;z1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;x2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;y2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;z2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;

&lt;span class=&amp;quot;nx&amp;quot;&gt;geometry&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;addAttribute&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;position&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;THREE&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Float32BufferAttribute&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;positions&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;onUpload&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;disposeArray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;注意这里最后一句，我们将&lt;code&gt;positions&lt;/code&gt;中的每三个数据为一组(x, y, z)，将其作为attribute &lt;code&gt;position&lt;/code&gt; 提交给GPU，并在提交后回收掉CPU这边的数组，避免内存浪费。提交之后，我们便可以在shader中使用数据了，注意shader中是针对每个顶点做处理，所以拿到的自然是&lt;code&gt;vec3&lt;/code&gt;变量：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;attribute&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;vec3&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;编写shader，构造材质：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;material&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;THREE&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;RawShaderMaterial&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;uniforms&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;vertexShader&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;shaders.vertex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;fragmentShader&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;shaders.fragment&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;needsUpdate&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;  
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;在这里，我们传入构造好的uniforms（主要是为了传入纹理和各个矩阵，有时也是为了动画），加之vertexShader和fragmentShader，便可以完成材质的构造。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;生成曲面：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;mesh&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;THREE&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Mesh&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;geometry&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;将曲面添加到场景后，启动渲染，便可以看到一个三角片了。&lt;/p&gt;
&lt;h3&gt;使用三角片构造平面&lt;/h3&gt;
&lt;p&gt;光有一个三角片当然不够，接下来我们要构造一个平面。这其实也很简单，依样画葫芦重复构造三角片就OK。让我们想想——一个矩形该如何用三角形构造？当然是用两个对称的三角形拼起来啦：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;positions&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;l&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;l&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;l&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这里我构造了两个拼在一起的三角形，拼在一起就构成了矩形，其中l、r、t、b分别表示矩形的左右上下边界，由于是在xy平面，z都是0。&lt;/p&gt;
&lt;p&gt;然而只是这样还是不够，我们需要的是碎片，很多很多碎片，不过这也不难办，只要将一个大矩形分隔成若干个小矩形，再将每个小矩形分割成两个三角片不就好了嘛：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;stepX&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;stepY&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;05&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;hStepX&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;stepX&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;hStepY&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;stepY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;left&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;right&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;stepX&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;top&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;bottom&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;stepY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;xL&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;xR&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;hStepX&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;yT&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;yB&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;hStepY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

    &lt;span class=&amp;quot;nx&amp;quot;&gt;positions&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;push&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;xL&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;yT&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;positions&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;push&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;xL&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;yB&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;positions&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;push&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;xR&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;yB&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;positions&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;push&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;xL&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;yT&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;positions&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;push&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;xR&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;yT&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;positions&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;push&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;xR&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;yB&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;其中stepX和stepY分别为小矩形的宽度和高度，通过这段代码，我们便生成了很多个小碎三角片构成的大矩形。&lt;/p&gt;
&lt;h3&gt;着色&lt;/h3&gt;
&lt;p&gt;上面说的都是如何生成顶点，但还有很重要的一步没说——如何给三角片着色？有过一定基础的同学应该都知道，在fragment shader中我们一般是通过uv坐标来采样纹理输出颜色，大家有没想过这个uv是从哪来的？不错，这个uv实际上是从vertex shader中的attribute变量传来的，而这个attribute和&lt;code&gt;position&lt;/code&gt;一样，都是在CPU中算好（存储在模型顶点数据中）的：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;left&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;right&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;stepX&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;top&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;bottom&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;stepY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;c1&amp;quot;&gt;// positions&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;

    &lt;span class=&amp;quot;nx&amp;quot;&gt;uvs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;push&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;((&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;xL&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;right&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;yT&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;bottom&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;uvs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;push&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;((&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;xL&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;right&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;yB&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;bottom&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;uvs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;push&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;((&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;xR&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;right&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;yB&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;bottom&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;uvs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;push&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;((&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;xL&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;right&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;yT&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;bottom&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;uvs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;push&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;((&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;xR&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;right&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;yT&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;bottom&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;uvs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;push&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;((&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;xR&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;right&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;yB&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;bottom&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;nx&amp;quot;&gt;geometry&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;addAttribute&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;uv&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;THREE&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Float32BufferAttribute&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;uvs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;onUpload&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;disposeArray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;在这里，我们计算了每个顶点的uv坐标，并一并作为&lt;code&gt;uv&lt;/code&gt;这个attribute提交给了GPU，然后便可以在shader中使用：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;attribute&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;vec2&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;打碎！添加attribute连接顶点&lt;/h3&gt;
&lt;p&gt;到此为止，我们应该可以渲染出来一张正常的图片了，我知道你们想说什么：费这么大事就为了渲染张图片？不要急，我们接下来只需要一点小技巧，便可以实现一个简单的碎片化效果：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;vec3&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;new_position&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;new_position&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;n&amp;quot;&gt;gl_Position&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;projectionMatrix&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;modelViewMatrix&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;new_position&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;通过这几句代码，我们将每个顶点的z坐标位移了其x坐标的距离，理论上，我这么写想达到一个“从左到右，三角片一层一层铺开”的效果。然而事与愿违，如果你去运行这段代码，会发现整个图片还是连续的，只不过发生了在x-z平面的斜切罢了。想一想，会发生这种状况的原因是什么？其实很简单，对于两个相邻的三角片，它们的三个顶点中有两个是重合的，重合的顶点自然会如果不加处理，它们的位置变换将会保持一致，这样一来，所有重合的顶点其实可以视为一个顶点，所以才会导致变换后的图像仍然连续。  &lt;/p&gt;
&lt;p&gt;为了解决这个问题，我们要给同不同三角片的各个顶点不同的新attribute变量，来表明它们不同于重合点的属性。比如在3D模型中，有法线&lt;code&gt;normal&lt;/code&gt;这个属性，它表明顶点的法线方向。在这个例子中，我们可以构造个叫做&lt;code&gt;centre&lt;/code&gt;的属性，其表明每个三角片的中心点，然后再让三个顶点都拥有同样的&lt;code&gt;centre&lt;/code&gt;属性：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;left&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;right&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;stepX&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;top&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;bottom&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;stepY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;c1&amp;quot;&gt;// positions, uvs&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;centres&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;push&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;xL&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;xR&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;xL&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;yT&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;yB&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;centres&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;push&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;xR&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;xR&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;xL&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;yT&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;yB&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;nx&amp;quot;&gt;geometry&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;addAttribute&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;centre&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;THREE&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Float32BufferAttribute&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;centres&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;onUpload&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;disposeArray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;之后顶点的变换都以这个中心点的位置为基准，如此一来，便可以区分每个重合但不在同一个三角面的顶点了：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;new_position&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;centre&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;动起来&lt;/h3&gt;
&lt;p&gt;现在，我们已经可以静态地打碎一张我们为其赋予纹理的图片了，但怎么让这个打碎的过程动起来呢？  &lt;/p&gt;
&lt;p&gt;这里的方案是引入外部uniform变量&lt;code&gt;progress&lt;/code&gt;，这个变量是一个范围是0~1的自增变量，它表明运动的进度。结合这个变量和一些其他的变量，加之自己喜欢的顶点变换逻辑（公式），我们便可以实现很多惊艳的效果，比如此例中，我以图片中心点和顶点的距离为基准，结合&lt;code&gt;progress&lt;/code&gt;，使用三角函数来控制x、y坐标，并赋予每个顶点的z坐标一定的差值，最终加上旋转让整个效果更富有动感：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;attribute&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;vec3&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;attribute&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;vec3&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;centre&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;attribute&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;vec2&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;uniform&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;mat4&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;modelViewMatrix&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;uniform&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;mat4&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;projectionMatrix&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;uniform&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;progress&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;uniform&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;top&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;uniform&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;left&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;uniform&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;uniform&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;varying&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;vec2&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;vUv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;vec3&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;rotate_around_z_in_degrees&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;vec3&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;vertex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;degree&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;alpha&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;degree&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;3.14&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;180.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;sina&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;sin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;alpha&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;cosa&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;cos&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;alpha&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;mat2&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;m&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;mat2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;cosa&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sina&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;sina&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;cosa&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;m&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;vertex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xy&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;vertex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xyz&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;void&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;main&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;vUv&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;vec3&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;new_position&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;vec3&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;center&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;vec3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;left&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;0.5&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;top&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;height&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;0.5&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mo&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;vec3&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;dist&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;center&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;centre&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;len&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;length&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dist&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;factor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;progress&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;0.5&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;factor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;progress&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;factor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;progress&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;factor1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;len&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;factor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;10.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;new_position&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;sin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dist&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;factor1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;new_position&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;sin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dist&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;factor1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;new_position&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;factor1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;new_position&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;rotate_around_z_in_degrees&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;new_position&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;progress&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;360.&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

    &lt;span class=&amp;quot;n&amp;quot;&gt;gl_Position&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;projectionMatrix&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;modelViewMatrix&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;vec4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;new_position&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;两张图片自然过渡&lt;/h3&gt;
&lt;p&gt;顶点动画到这里就结束了，对于本效果，最后还需要考虑的一点是如何让两张图片能自然得过渡。其实这一点在上面的vertex shader中就有所体现了——我以0.5为分界点，将整个运动的周期分为了两部分，第一部分平面逐渐被打碎，第二部分碎片逐渐收缩回平面。  &lt;/p&gt;
&lt;p&gt;而配合vertex shader，适当得编写fragment shader便可以轻松完成两个纹理的自然过渡：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;uniform&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;sampler2D&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;image1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;uniform&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;sampler2D&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;image2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;uniform&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;progress&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;varying&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;vec2&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;vUv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;void&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;main&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;vec4&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;t_image&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;vec4&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;t_image1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;texture2D&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;image1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;vUv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;vec4&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;t_image2&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;texture2D&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;image2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;vUv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

  &lt;span class=&amp;quot;n&amp;quot;&gt;t_image&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;progress&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;t_image1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;progress&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;t_image2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

  &lt;span class=&amp;quot;n&amp;quot;&gt;gl_FragColor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;t_image&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;利用&lt;code&gt;progress&lt;/code&gt;，将两个纹理按照不同的权重混色起来即可。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Saturday, 21 Jul 2018 22:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2018.07.21 22:00:article/Skill-2018_07_21_a</guid>
<category>HTML5</category>
<category>WebGL</category>
<category>Shader</category>
<category>Vertex</category>
<category>Image</category>
<category>Fragment</category>
</item>

<item>
<title>前端视觉交互——Shader实现扩散水纹涟漪效果</title>
<link>http://dtysky.moe/article/Skill-2018_07_01_a</link>
<description>&lt;p&gt;Code: &lt;a href=&amp;quot;http://github.com/dtysky/paradise/tree/master/src/collection/ShaderWaterRipple&amp;quot;&gt;github.com/dtysky/paradise/tree/master/src/collection/ShaderWaterRipple&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;Demo: &lt;a href=&amp;quot;http://paradise.dtysky.moe/effect/shader-water-ripple&amp;quot;&gt;paradise.dtysky.moe/effect/shader-water-ripple&lt;/a&gt;  &lt;/p&gt;
&lt;h2&gt;原理&lt;/h2&gt;
&lt;p&gt;涟漪，是指现实世界中水面上落入物体时、以物体为圆心向四周不断生成扩散圆形式的涟漪并逐渐衰减的现象。真实世界的涟漪十分复杂，在图形学中，我们只能模拟它来按达到一个近似效果。而为了模拟涟漪，图形学也提供了各种各样的方法，像是各种map（flow map）等，但在这里，为了最简化资源和达到最好的动态效果、并阐明原理，本例由纯Shader绘制。  &lt;/p&gt;
&lt;p&gt;像是如何绘制一个平面并将贴图铺满这种前置工作我就不在赘述了。在这个效果中，我们首先要考虑的是如何模拟涟漪那&lt;strong&gt;一圈一圈&lt;/strong&gt;的效果，这其实分为两个部分——其一是如何让贴图的像素进行偏移，另外则是如何绘制一个一个的圆，来使得贴图的像素按照这些圆进行偏移。  &lt;/p&gt;
&lt;p&gt;第一部分的原理在之前的作品《2D Pixel Displacement》中已经说过了，其实就是利用某个值当做当前像素uv坐标的偏移量，再用偏移后的uv去采样纹理即可。而第二部分，则需要用到一些平面解析几何的知识了。  &lt;/p&gt;
&lt;h3&gt;基础&lt;/h3&gt;
&lt;p&gt;由于Shader中基本无法存储状态，所以只能由当前时刻的uv坐标和外部传入的变量（一般是当前时间time）来通过确定的公式算出，所以我们需要一个方程来描述距离某中心点不同距离的圆的信息：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;vec2&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;tc&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;vUv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xy&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tc&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;centre&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;aspect&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;len&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;length&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;首先，我们拿到了当前像素的uv坐标&lt;code&gt;tc&lt;/code&gt;，它是一个二维向量，对其和外部传入的uniform中心&lt;code&gt;centre&lt;/code&gt;求差，得到其和中心的差值向量&lt;code&gt;p&lt;/code&gt;，&lt;code&gt;p&lt;/code&gt;携带了方向和距离两个信息，&lt;code&gt;len&lt;/code&gt;即为其中的距离信息。（注意第三句，这里我用&lt;code&gt;p&lt;/code&gt;的&lt;code&gt;x&lt;/code&gt;分量乘了外部变量纵横比&lt;code&gt;aspect&lt;/code&gt;，这是因为我们需要的是一个标准的圆，而要渲染的平面并非总是正方形，这就会导致uv坐标的比例不同）。  &lt;/p&gt;
&lt;p&gt;有了每一点相对于中点的距离，那么计算哪些点属于同一个圆也就十分简单了——只要它们的&lt;code&gt;len&lt;/code&gt;相同即可。从最简化的层面而言，为了模拟涟漪，我可以用下面的公式：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;uv_offset&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;len&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;cos&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;len&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;3.14&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;amp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;余弦函数的基本形式是&lt;code&gt;y = cos(x)&lt;/code&gt;，它的周期为2π，值域为-1 ~ 1，为了便于理解，这里将&lt;code&gt;len&lt;/code&gt;作为自变量并乘以π，将周期标准化为1。如此一来，所有和中心点距离为len的采样点，其偏移向量的模均一致，但方向各不相同，为大小归一化过得方向向量&lt;code&gt;p / len&lt;/code&gt;，这便构成了一个由平面法向量定义的圆环。  &lt;/p&gt;
&lt;p&gt;之后将这个偏移向量加给原始的uv坐标，即可得到真实采样的坐标，计算颜色输出即可：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;vec4&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;t_image&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;texture2D&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;image&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;fract&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tc&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;gl_FragColor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;t_image&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;进阶&lt;/h3&gt;
&lt;p&gt;通过以上基础方法可以绘制得到一个从中心点向外扩散的、静态的涟漪，这个涟漪由若干个圆环构成，圆环的偏移从1 到 -1 再到 1，即包含一个波长。然而真实情况下，我们的一片涟漪显然不止一个波长，换言之，它是由许多个周期的圆环构成的；除此之外，我们还要给涟漪限定半径，让它能被控制在一定的范围内：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;waves_factor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;waves&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;len&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;radius&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;uv_offset&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;len&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;cos&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;((&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;waves&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;len&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;radius&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;3.14&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这里我引入了两个变量波数&lt;code&gt;waves&lt;/code&gt;和半径&lt;code&gt;radius&lt;/code&gt;，这个变换体现在函数曲线上，就是将上面公式中的函数曲线在定义域收缩了&lt;code&gt;waves / radius&lt;/code&gt;，控制这二者可以让我们得到理想中的、一定半径下且具有一定数量的波的涟漪。  &lt;/p&gt;
&lt;p&gt;然而有了这些还是不能让涟漪运动起来，我们还需要一个外部变量引入一个递增的概念，这个一般是时间&lt;code&gt;time&lt;/code&gt;，然而二了精确控制涟漪出现和消失的整个过程，这里我引入了进度&lt;code&gt;current_progress&lt;/code&gt;和速度&lt;code&gt;speed&lt;/code&gt;，进度的范围是0 ~ 1，表明着一个涟漪整个生命周期，速度则表明波形波动的速度：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;waves_factor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;waves&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;len&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;radius&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;uv_offset&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;len&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;cos&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;((&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;waves_factor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;current_progress&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;speed&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;3.14&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;通过这两个变量，即可让涟漪从中线点向四周扩散，运动起来。但我们说这样还是不够的，它只能形成一个不断循环由瞬间消失的鬼畜效果，为了使得效果更佳真实，我这里引入一个通带&lt;code&gt;band&lt;/code&gt;的变量，其范围是0~1，它定义了一个范围，只有在这个范围内的波才能被看到，否则将被裁剪：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;wave_width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;band&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;radius&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;current_radius&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;radius&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;current_progress&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;cut_factor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;clamp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;wave_width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;abs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;current_radius&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;len&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;0.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;uv_offset&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;len&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;cos&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;((&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;waves_factor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;current_progress&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;speed&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;3.14&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;cut_factor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这里我将&lt;code&gt;band&lt;/code&gt;首先归一到了最大半径的范围内，之后将其和当前采样点与当前波运动到的半径比较，算出一个裁剪系数&lt;code&gt;cut_factor&lt;/code&gt;，如果采样点和当前半径的距离在归一化后的通带内，则为1，否则为0。  &lt;/p&gt;
&lt;p&gt;然而这样会有个问题，若通带一直未一个静态量，那么最后涟漪还是会有在通带宽度的状态下突然消失的感觉。为了解决这个问题，这里再计算一个衰减系数&lt;code&gt;damp_factor&lt;/code&gt;，其在一定进度前为1，之后逐渐变为0，将其和通带结合即可得出最后的裁剪系数，其含义是在某个时间段后逐渐压缩通带，直到进度为1时通带也被压缩为0：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;damp_factor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;current_progress&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;.5&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;n&amp;quot;&gt;damp_factor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;current_progress&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;2.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;cut_factor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;clamp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;wave_width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;damp_factor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;abs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;current_radius&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;len&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;0.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;1.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;注意这里我计算了两个值，当前扩散半径&lt;code&gt;current_radius&lt;/code&gt;和当前衰减系数&lt;code&gt;damp_factor&lt;/code&gt;，这两个变量一个负责约束当前&lt;/p&gt;
&lt;p&gt;在最后的最后，我们在配合一个可配置的振幅&lt;code&gt;amp&lt;/code&gt;，其用于定义偏移量的峰值，即可得到最终的uv偏移：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;uv_offset&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;len&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;cos&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;((&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;waves_factor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;current_progress&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;speed&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;3.14&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;amp&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;cut_factor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Sunday, 01 Jul 2018 16:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2018.07.01 16:00:article/Skill-2018_07_01_a</guid>
<category>HTML5</category>
<category>WebGL</category>
<category>Shader</category>
<category>Ripple</category>
<category>Water</category>
<category>Liquid</category>
</item>

<item>
<title>Web方向传感器的正确使用姿势</title>
<link>http://dtysky.moe/article/Skill-2018_06_25_a</link>
<description>&lt;p&gt;现在移动设备普遍提供了重力传感器和方向传感器来测量设备方向，HTML5标准也提供了标准的API来进行方向信息的获取，获取方式是通过两个事件，他们分别是“orientationchange”和“deviceorientation”。  &lt;/p&gt;
&lt;p&gt;规范可以参考&lt;a href=&amp;quot;https://www.w3.org/html/ig/zh/wiki/DeviceOrientation%E4%BA%8B%E4%BB%B6%E8%A7%84%E8%8C%83&amp;quot;&gt;DeviceOrientation事件规范&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;DEMO + NPM&lt;/h2&gt;
&lt;p&gt;我封装了一个模块用以方便得使用重力传感器和陀螺仪。  &lt;/p&gt;
&lt;p&gt;DEMO在：&lt;a href=&amp;quot;http://paradise.dtysky.moe/effect/panorama-image-viewer&amp;quot;&gt;PanoramaImageViewer&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;NPM包：&lt;a href=&amp;quot;https://www.npmjs.com/settings/dtysky/packages&amp;quot;&gt;DeviceOrientationManager&lt;/a&gt;  &lt;/p&gt;
&lt;h2&gt;Orientation Change&lt;/h2&gt;
&lt;p&gt;监听此事件可以获得设备当前的屏幕方向，通过&lt;code&gt;window.orientation&lt;/code&gt;获取，正常竖屏为0，按顺时针渲染依次为：0 -&amp;gt; 90 -&amp;gt; 180 -&amp;gt; -90。&lt;/p&gt;
&lt;h2&gt;Device Orientation&lt;/h2&gt;
&lt;p&gt;监听此事件可以获取当前的设备的三个偏转角&lt;strong&gt;alpha&lt;/strong&gt;、&lt;strong&gt;beta&lt;/strong&gt;和&lt;strong&gt;gamma&lt;/strong&gt;。&lt;/p&gt;
&lt;h3&gt;基础&lt;/h3&gt;
&lt;p&gt;本质上重力传感器和方向传感器都是派生传感器。重力传感器基于加速度传感器；方向传感器主要基于陀螺仪和磁力计，但主要还是基于陀螺仪。  &lt;/p&gt;
&lt;p&gt;陀螺仪本质上是用于测量角速度的仪器，其原理和我们日常生活中的陀螺基本一致——在陀螺高速旋转时，若没有外加力矩，其总是围绕一个固定转轴稳定转动（转动惯量大），这被称为定轴性，这也是陀螺仪最重要的一个特性，其基于角动量守恒原理。日常中的陀螺其实是一个具有两个自由度的偏轴陀螺仪，而在实际运用中的则是多为三个自由度的定轴陀螺仪，我们只要给不同的轴框不同的力，就可以使得对应的轴框转动，从而测出不同的姿态角：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;undefined&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2018_06_25a/0.png&amp;quot; /&gt; &lt;/p&gt;
&lt;p&gt;移动设备上的陀螺仪基本都是MEMS（微机械）陀螺仪。对于HTML5标准，设备方向官方定义的坐标系为：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;undefined&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2018_06_25a/1.png&amp;quot; /&gt; &lt;/p&gt;
&lt;p&gt;这其实是一个&lt;strong&gt;Z-UP的左手坐标系&lt;/strong&gt;，而其中三个角度的定义为绕三个轴旋转的欧拉角：  &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;围绕 Z 轴的旋转。当设备的顶部指向正北时 alpha 值为 0°。 当设备逆时针旋转时，alpha 值增加。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt=&amp;quot;undefined&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2018_06_25a/2.png&amp;quot; /&gt; &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;围绕 X 轴的旋转。当设备的顶部和底部与地球表面等距时 beta 值为 0°。 当设备的顶部倾向地球表面时，此值增加。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt=&amp;quot;undefined&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2018_06_25a/3.png&amp;quot; /&gt; &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;围绕 Y 轴的旋转。当设备的左右边缘与地球表面等距时 gamma 值为 0°。 当设备的右侧倾向地球表面时，此值增加。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt=&amp;quot;undefined&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2018_06_25a/4.png&amp;quot; /&gt; &lt;/p&gt;
&lt;h3&gt;实践&lt;/h3&gt;
&lt;p&gt;从W3C标准来看，alpha的角度应该是 0 ~ 360，beta是 -180 ~ 180，gamma是 -90 ~ 90，并且这个变化应当是平滑的，但真实情况确实如此吗？&lt;/p&gt;
&lt;p&gt;以下符号&lt;strong&gt;-&amp;gt;&lt;/strong&gt;表示逐渐变化，比如&lt;strong&gt;0 -&amp;gt; 180&lt;/strong&gt;表示从0度逐渐变化到180度，&lt;strong&gt;|&amp;gt;&lt;/strong&gt;表示瞬变，比如&lt;strong&gt;360 |&amp;gt; 0&lt;/strong&gt;表示从360瞬变0。&lt;/p&gt;
&lt;p&gt;alpha：0 -&amp;gt; 360 &amp;gt;| 0 度，设备顶部朝向正北时为0度，注意在X轴旋转到竖屏的边界时会有强烈抖动。
beta：其值的变化和手机顶部朝向有关。顶部背向用户时，屏幕水平向上为0度，面向用户旋转每九十度变化为 0 -&amp;gt; 180 |&amp;gt; -180 -&amp;gt; 0；顶部面向用户时，面向用户旋转每九十度变化为 0 -&amp;gt; -180 |&amp;gt; 180 -&amp;gt; 0。
gamma：其值的变化和手机顶部朝向有关。顶部背向用户时，水平向上时为0度，顺时针旋转每九十度变化为0 -&amp;gt; 90 |&amp;gt; -90 -&amp;gt; 0 -&amp;gt; 90 |&amp;gt; -90 &amp;gt; 0度；顶部面向用户时，水平 0 -&amp;gt; -90 |&amp;gt; 90 -&amp;gt; 0 -&amp;gt; -90 |&amp;gt; 90 -&amp;gt; 0。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: 这个是三星S9 Chrome实机测试结果，不同设备可能各有差异，比如实机测试和chrome sensors功能模拟的结果中alpha和gamma的变化就有所不同，这个非常坑爹，后面会详细说道。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;实际换算&lt;/h3&gt;
&lt;p&gt;然而一般的实时渲染引擎（尤其是游戏引擎）使用的坐标系与以上坐标系不同，它们中的大多数都是使用&lt;strong&gt;Y-UP右手坐标系&lt;/strong&gt;的，所以以下为基准，即： &lt;/p&gt;
&lt;p&gt;屏幕朝向用户，横向为x轴、右侧为正，纵向为y轴、上侧为正，屏幕朝向方向为z轴、朝向屏幕外侧为正。   &lt;/p&gt;
&lt;p&gt;不仅如此，在需要使用到方向传感器的应用中（VR、AR），我们一般是使用其去控制一个场景中观察世界的相机，相机自身的坐标系的一般初始化为&lt;strong&gt;以屏幕正向面对用户时的Y-UP右手坐标系&lt;/strong&gt;：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;undefined&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2018_06_25a/5.png&amp;quot; /&gt; &lt;/p&gt;
&lt;p&gt;此时设备纵向旋转绕X轴（俯仰角pitch），横向旋转绕Y轴（偏航角yaw），剩下一个维度即为Z轴（翻滚角roll）:  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;undefined&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2018_06_25a/6.png&amp;quot; /&gt; &lt;/p&gt;
&lt;p&gt;这么一对比，不难发现当设备在不同朝向时摄像机的坐标系和方向传感器定义的坐标系其实是有一些差异的，他们存在一些对应关系。在下面，我将用&lt;code&gt;pitch&lt;/code&gt;、&lt;code&gt;yaw&lt;/code&gt;、&lt;code&gt;roll&lt;/code&gt;为三个偏转角，给出它们与&lt;code&gt;alpha&lt;/code&gt;、&lt;code&gt;beta&lt;/code&gt;、&lt;code&gt;gamma&lt;/code&gt;的关系。&lt;/p&gt;
&lt;h4&gt;window.orientation = 0&lt;/h4&gt;
&lt;p&gt;渲染场景竖屏，设备顶部朝上。&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;pitch&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;beta&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;yaw&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;alpha&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;roll&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;gamma&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h4&gt;window.orientation = 90&lt;/h4&gt;
&lt;p&gt;渲染场景横屏，设备顶部朝左。  &lt;/p&gt;
&lt;p&gt;在0度基础上，将整个物体坐标系绕Z轴旋转-90度。&lt;/p&gt;
&lt;h4&gt;window.orientation = 180&lt;/h4&gt;
&lt;p&gt;渲染场景竖屏，设备顶部朝下。  &lt;/p&gt;
&lt;p&gt;在0度基础上，将整个物体坐标系绕Z轴旋转-180度。&lt;/p&gt;
&lt;h4&gt;window.orientation = -90&lt;/h4&gt;
&lt;p&gt;渲染场景横屏，设备顶部朝右。  &lt;/p&gt;
&lt;p&gt;在0度基础上，将整个物体坐标系绕Z轴旋转90度。&lt;/p&gt;
&lt;h3&gt;设备差异&lt;/h3&gt;
&lt;p&gt;原则上来讲，以上的换算非常完美，但这是建立在所有角度都正常，也就是一个循环一个正常的周期（0 -&amp;gt; 360 -&amp;gt; 0）的情况下的。当然，工程实践和理想状况总是有巨大的差异，有差异怎么办？还能怎么办，解决呗......  &lt;/p&gt;
&lt;p&gt;首先，经过测试后，发现设备普遍差异化为两种，第一种就是一开始的实机测试，而第二种（比如Chrome模拟器）中，alpha的变化是 0 -&amp;gt; -180 |&amp;gt; 180 -&amp;gt; 0，gamma变化是 0 -&amp;gt; 90 -&amp;gt; 0 -&amp;gt; -90 -&amp;gt; 0。这就导致在有的设备下alpha的值很完美，有的设备下gamma的值比较好，这就很尴尬了。&lt;/p&gt;
&lt;p&gt;这就导致我要做一些兼容，首先让我们看看我们期望的三个偏转角和设备实机偏转（以Z-UP坐标系为准）的函数图像，以及它们实际的函数图像：  &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;第一种设备：&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt=&amp;quot;undefined&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2018_06_25a/7.png&amp;quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;第二种设备：&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt=&amp;quot;undefined&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2018_06_25a/8.png&amp;quot; /&gt; &lt;/p&gt;
&lt;p&gt;是不是发现都有挺大的很大差异？但其实也没什么差异，因为在正常使用中，方向传感器返回的三个欧拉角是互有关联的，比如第一种设备虽然看起来有很大的突变，但其实其突变的画风是这样的：  &lt;/p&gt;
&lt;p&gt;{alpha: -10, beta: 40, gamma: -90} -&amp;gt; {alpha: 170, beta: 170, gamma: 90}  &lt;/p&gt;
&lt;p&gt;这两对欧拉角在三维旋转中其实是等价的。&lt;/p&gt;
&lt;h3&gt;滤波去抖&lt;/h3&gt;
&lt;p&gt;真实世界中总是会有抖动的，所以我们需要一定的滤波算法来去抖。当然，不仅是alpha，其他两个角度也可以用同样的算法使得更加平滑，在这里我们一般使用LPF（低通滤波器，这里其实是一个拥有一定大小窗口的FIR实现的，学过信号处理的同学是不是很熟悉www）来滤除，用于滤除高频噪声（突变），其本质是加权平均滤波，会有一定的迟滞，窗口越大迟滞越大，但在一定范围内可以接受，同时注意&lt;code&gt;smoothing&lt;/code&gt;参数，其越大表明当前值对结果影响越大。  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;result&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;buffer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;reduce&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;last&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;current&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;smoothing&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;current&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;smoothing&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;last&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;removed&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;理想状况下，只需要对原始角度进行单个数字的滤波即可，即窗口中存的是&lt;code&gt;[alpha1, alpha2, alpha3, alpha4, ...]&lt;/code&gt;，然而考虑前面的分析，所有角度都可能出现瞬变 360 &amp;lt;-&amp;gt; 0，虽然在角度层面上 360 和 0度是等价的，但对于滤波则不是。在滤波过程中，由于本方案涉及到加权平均值的计算，会导致当前值和实际值有一定的迟滞，所以在360 -&amp;gt; 0 度这种情境下，计算值看起来像是出现了几次的插值，比如 360 -&amp;gt; 280 -&amp;gt; ... -&amp;gt; 0，这会严重影响实际使用。解决方案也不是没有，考虑相隔一个周期（2π）两个角度，它们的&lt;code&gt;sin(angle)&lt;/code&gt;和&lt;code&gt;cos(angle)&lt;/code&gt;的值完全相等且这两个值可以确定在一个周期的定义域内的唯一角度。所以我们不直接对角度进行滤波，而是先变换为sin和cos进行滤波，最后再通过&lt;code&gt;atan2(y, x)&lt;/code&gt;这个值域为&lt;code&gt;-180 ~ 180&lt;/code&gt;的函数进行反变换即可。以下是&lt;code&gt;atan2(cos(angle), sin(angle))&lt;/code&gt;这个函数的图像（弧度）：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;undefined&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2018_06_25a/9.png&amp;quot; /&gt; &lt;/p&gt;
&lt;p&gt;&lt;code&gt;atan2&lt;/code&gt;的值域是&lt;code&gt;-180 ~ 180&lt;/code&gt;度，正好在一个周期，所以结果就是将&lt;code&gt;alpha&lt;/code&gt;和&lt;code&gt;beta&lt;/code&gt;归一化到了&lt;code&gt;-180 ~ 180&lt;/code&gt;度内，而我们实际使用时一般会用正弦和预先函数处理，所以完全不影响使用。而对于&lt;code&gt;gamma&lt;/code&gt;，其值域和原来保持一致。  &lt;/p&gt;
&lt;h3&gt;突变注意&lt;/h3&gt;
&lt;p&gt;以上滤波可以解决大多数情况下的抖动，但对于《设备差异》一节最后的&lt;strong&gt;反转&lt;/strong&gt;和&lt;strong&gt;补角&lt;/strong&gt;突变，它也无能为力，我们可以设定低一些的获取频率、或者调高当前值得权重来解决这个问题。如果要求再高一些，建议关闭上面的滤波，自己去对四元数进行平滑。&lt;/p&gt;
&lt;h2&gt;和Camera结合&lt;/h2&gt;
&lt;p&gt;对于应用最为广泛的方向传感器 + 3D摄像头组合，直接将&lt;code&gt;pitch&lt;/code&gt;、&lt;code&gt;yaw&lt;/code&gt;、&lt;code&gt;roll&lt;/code&gt;运用到就上旋转举矩阵或者四元数上即可：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;zee&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Vector3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;screenTransform&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Quaternion&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;worldTransform&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Quaternion&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;Math&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;sqrt&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.5&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;Math&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;sqrt&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.5&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;

&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;euler&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Euler&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;span class=&amp;quot;c1&amp;quot;&gt;// order = &amp;#39;YXZ&amp;#39;&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;euler&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;order&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;order&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;euler&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;set&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;pitch&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;yaw&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;roll&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;order&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

&lt;span class=&amp;quot;nx&amp;quot;&gt;camera&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;quaternion&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setFromEuler&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;euler&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;camera&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;quaternion&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;multiply&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;worldTransform&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;camera&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;quaternion&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;multiply&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;screenTransform&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setFromAxisAngle&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;zee&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;orientation&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Monday, 25 Jun 2018 10:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2018.06.25 10:00:article/Skill-2018_06_25_a</guid>
<category>HTML5</category>
<category>Javascript</category>
<category>Orientation</category>
<category>方向传感器</category>
</item>

<item>
<title>前端视觉交互——Particle3DByThree</title>
<link>http://dtysky.moe/article/Skill-2018_06_24_a</link>
<description>&lt;p&gt;&lt;img alt=&amp;quot;Cover&amp;quot; src=&amp;quot;//paradise.dtysky.moe/assets/Particle3DByThree/cover.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;Code: &lt;a href=&amp;quot;http://github.com/dtysky/paradise/tree/master/src/collection/Particle3DByThree&amp;quot;&gt;https://github.com/dtysky/paradise/tree/master/src/collection/Particle3DByThree&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;Demo: &lt;a href=&amp;quot;http://paradise.dtysky.moe/effect/particle-3d-by-three&amp;quot;&gt;http://paradise.dtysky.moe/effect/particle-3d-by-three&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;原理&lt;/h2&gt;
&lt;p&gt;这个效果是&lt;a href=&amp;quot;https://tympanus.net/Development/3d-particle-explorations/&amp;quot;&gt;3D Particle Explorations&lt;/a&gt;其中之一的实现。觉得蛮有意思满酷炫就研究实现了一下。  &lt;/p&gt;
&lt;h3&gt;基元&lt;/h3&gt;
&lt;p&gt;首先，顾名思义，这是一个粒子效果。和大多的粒子效果一样，它都是由一个一个小的“基元”组合而成的，而在本作品中这个基元就是“球体”，也就是说，那一个个粒子本质上是由SphereGeometry加上不同的Material形成的Mesh。对于本例的粒子，主要被设置的显示属性是半径、位置、颜色和透明度。  &lt;/p&gt;
&lt;h3&gt;生长&lt;/h3&gt;
&lt;p&gt;光有粒子本身是无法实现效果的，我们还需要让它们按照一定的规律动起来。这个动起来包括两部分——其一是粒子自身的约束，其二是粒子间的约束。  &lt;/p&gt;
&lt;p&gt;首先是粒子自身的约束，这其实就是一个生命周期中，生长和消亡的过程。看到这个效果，我们很自然的可以想到——是不是有一个摄像机在对着粒子群，而粒子是在随着时间向着摄像头运动呢？当然是的，粒子群确实是在进行着“出生 -&amp;gt; 向摄像头运动 -&amp;gt; 消失”的生命循环，但实际上你会发现，仅仅是让粒子改变位置是无法达到这种效果的，我们还必须在粒子的一次生命周期中改变它的大小和透明度，使得变化更加自然：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;mesh&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;scale&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;set&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;scale&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;scale&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;scale&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;mesh&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setZ&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;z&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;c1&amp;quot;&gt;// 4为设定的边界，可以调整&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;mesh&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;material&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;THREE&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Material&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;opacity&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;Math&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;abs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;z&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;config&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;opacity&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;但仅仅这样还是不够的，如果所有粒子的行为都一致，那么实际的效果也只是粒子群同时运动到了某一平面，并没有实际效果的那种层次感。所以必须有一种细致操控每一个粒子的方法，来使得它们的生长具有差异化。这个方法也很简单，就是——给每个粒子设置不同的出生时间&lt;code&gt;born&lt;/code&gt;和生命&lt;code&gt;life&lt;/code&gt;，然后通过这两个参数控制粒子运动的整个周期，这就可以是的粒子群在宏观上拥有一致的运动曲线和终点，而在微观上每一个粒子又都各有自己的特点：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;initialized&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;current&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;born&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;current&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;deltaTime&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;initialized&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;current&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;current&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;deltaTime&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;life&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;current&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;deltaTime&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这段代码说明了&lt;code&gt;born&lt;/code&gt;、&lt;code&gt;life&lt;/code&gt;的作用，它们决定了每个粒子开始运动和结束运动的时间。&lt;/p&gt;
&lt;h3&gt;运动曲线&lt;/h3&gt;
&lt;p&gt;光有时间无法让粒子运动起来，我们还需要根据时间去算粒子的&lt;code&gt;z&lt;/code&gt;和&lt;code&gt;scale&lt;/code&gt;，这就需要插值。一个简单的方式是线性插值，即值和时间成正比，但这显然无法达到我们的效果，我们需要的是一个Q弹的运动曲线，让每个粒子的运动有一种平滑自然的效果。  &lt;/p&gt;
&lt;p&gt;一般而言，为了实现曲线，大家都会选择&lt;code&gt;Tween&lt;/code&gt;或者近似品，但为了从全局严格管控每一个粒子的行为，我引入了更底层的&lt;strong&gt;d3-ease&lt;/strong&gt;和&lt;strong&gt;d3-interpolate&lt;/strong&gt;，前者是运动曲线生成库，后者是插值库，二者配合就可以算出粒子起始和终点属性中任意的平滑的值。  &lt;/p&gt;
&lt;p&gt;现在让我们回到曲线本身，看着实例，想想我们究竟需要怎样的曲线？看起来像是&lt;code&gt;QuadInOut&lt;/code&gt;对不对？不错，大概没错，但其实还是有些不同。&lt;code&gt;QuadInOut&lt;/code&gt;的曲线是这样的：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;                           -
                          -
                        --
                      --
                   ---
                ---   
            ----
         ---
      ---
    --  
  --       
 -      
-        
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;但我们需要的曲线是这样的：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;                                          -
                                          -
                                        --
                                      --
                                  ---
                                ---   
            -------------------
         ---
      ---
    --  
  --       
 -      
-        
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;事实上，&lt;code&gt;QuadInOut&lt;/code&gt;也不过是&lt;code&gt;QuadIn&lt;/code&gt;和&lt;code&gt;QuadOut&lt;/code&gt;的合成，它把两者各scale了0.5，之后再将二者拼起来，我们当然也可以这样做：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;function&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;customInOut&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;duration&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;point1&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;point2&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;current&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;current&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;point1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;d3Ease&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;easeQuadIn&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;current&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;point1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;current&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;point2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;5&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;d3Ease&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;easeQuadOut&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;((&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;current&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;point2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;duration&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;point2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;5&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这里我将曲线分成了三个阶段，第一阶段用&lt;code&gt;QuadIn&lt;/code&gt;，第二阶段保持状态，第三阶段用&lt;code&gt;QuadOut&lt;/code&gt;，这就是我们需要的曲线。有了曲线，插值就十分简单了，根据阶段选择不同的插值器即可，这里不再赘述：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;percent&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;customInOut&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;config&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;life&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;life&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;edgeTime1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;life&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;edgeTime2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;current&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;percent&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;5&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;percent&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;5&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;0&lt;/span&gt; : &lt;span class=&amp;quot;kt&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;realPercent&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;percent&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;5&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;percent&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;2&lt;/span&gt; : &lt;span class=&amp;quot;kt&amp;quot;&gt;percent&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;scale&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;z&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;interpolates&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;](&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;realPercent&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;斥力&lt;/h3&gt;
&lt;p&gt;到了这一步，粒子可以运动起来，也可以有层次感，但是不是觉得还少了些什么？对，还少了&lt;strong&gt;散开&lt;/strong&gt;的效果。从实例中不难发现，粒子间的间距实际上是动态变化，当一个新的粒子插入粒子群时，所有的粒子开始动态调整间隙，一开始逐渐变大，到了一定的间隙之后基本保持不变。  &lt;/p&gt;
&lt;p&gt;这个现象很自然得可以想到用&lt;strong&gt;斥力&lt;/strong&gt;来描述，废话不多说放代码：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;count&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;p1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;particles&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;p1Pos&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;p1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;mesh&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;p1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;initialized&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;continue&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;j&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;j&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;count&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;j&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;p2&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;particles&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;j&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;p1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;p2&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;||&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;p2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;initialized&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;k&amp;quot;&gt;continue&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;p2Pos&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;p2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;mesh&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;dx&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;p1Pos&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;p2Pos&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;dy&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;p1Pos&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;p2Pos&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;dist&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;dx&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;dx&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;dy&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;dy&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;radii&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;p1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;size&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;p2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;size&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;p1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;size&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;p2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;size&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;spacing&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;dist&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;radii&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;angle&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;Math&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;atan2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;dy&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;dx&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;generateRandom&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.05&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;0.05&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
      &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;diff&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;radii&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;dist&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
      &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;Math&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cos&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;angle&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;diff&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;0.05&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
      &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;Math&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;sin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;angle&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;diff&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;0.05&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;p1Pos&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;dTime&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;p1Pos&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;dTime&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;p2Pos&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;dTime&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;p2Pos&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;dTime&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这段代码简单明（cu）了（bao），就是遍历每一个粒子，然后去计算其他粒子对该粒子的力的作用，这里要注意两个约束：只有出生了的粒子才会进行计算，只有在两个粒子的间隙小于约束的间隙时才会进行调整，这样可以避免无效和错误的计算。&lt;/p&gt;
&lt;p&gt;至此，整个系统就可以完美运行了。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Sunday, 24 Jun 2018 02:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2018.06.24 02:00:article/Skill-2018_06_24_a</guid>
<category>HTML5</category>
<category>WebGL</category>
<category>3D</category>
<category>Particle</category>
<category>粒子</category>
<category>曲线</category>
<category>力</category>
</item>

<item>
<title>前端视觉交互——PixelDisplacement2D</title>
<link>http://dtysky.moe/article/Skill-2018_06_18_a</link>
<description>&lt;p&gt;Code: &lt;a href=&amp;quot;http://github.com/dtysky/paradise/tree/master/src/collection/PixelDisplacement2D&amp;quot;&gt;https://github.com/dtysky/paradise/tree/master/src/collection/PixelDisplacement2D&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;Demo: &lt;a href=&amp;quot;http://paradise.dtysky.moe/effect/pixel-displacement-2d&amp;quot;&gt;paradise.dtysky.moe/effect/pixel-displacement-2d&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;Cover&amp;quot; src=&amp;quot;//paradise.dtysky.moe/assets/PixelDisplacement2D/cover.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;h2&gt;原理&lt;/h2&gt;
&lt;p&gt;2D pixel displacement mapping，即2D像素位移映射（其实pixel换成fragment更合适，毕竟像素只是片段的一个子集），可以算作是3D vertex displacement mapping的一个变种。  &lt;/p&gt;
&lt;p&gt;3D顶点位移映射，比如&lt;a href=&amp;quot;https://threejs.org/examples/webgl_materials_displacementmap.html&amp;quot;&gt;displacement map&lt;/a&gt;中的效果，本质上是用一个球体，加上一张灰度图（下称dMap）作为参考，在每一帧渲染时，通过dMap上的采样结果来决定3D模型上顶点的位移，而这一变换是在顶点着色器中实现的，所以其真正得改变了模型的数据。这个映射后面会专门有个作品来展示，这里不多说。  &lt;/p&gt;
&lt;p&gt;回到2D像素位移映射，其实就是讲这种位移应用到了光栅化后的片段着色器中。其原理也很简单，让我们回想一下，现实生活我们去到了一个泳池，当水面有波动时，我们透过水面去看里面的东西其实从感官上而言是有偏移的，这个偏移则是相对于我们在空气中看到物体的实际位置，这种现象就是“光的折射”，而我们这个映射就是要模拟这种折射的效果。  &lt;/p&gt;
&lt;p&gt;那么折射现象应该怎么用片段着色器描述呢？在片段着色器中，我们可以获得从顶点着色器传来的uv坐标，对于纯2D图像，这个就是归一化过的采样坐标，比如(0.5, 0.5)这个坐标就意味着着色器正在处理的点就是图像的中心。一般而言，我们可以通过这个坐标到对应的贴图上去采样：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;vec4&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;t_image&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;texture2D&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;image&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这里t_image得到的就是通过uv坐标在image贴图上采样到的rgba像素值。这时候位移映射要做的事情就非常明确了——只要在计算的时候给uv坐标一个偏移，那么我采样到的就是贴图上另一个坐标的像素：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;vec4&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;t_image&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;texture2D&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;image&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;0.2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;通过这句代码，我们将图像所有的像素值都向左偏移了(10%, 20%)。接下来，我们只需要对每一个不同uv坐标的片段做不同的位移就好了，这个位移的来源，其实就是dMap。在处理每个片段的时候，先采样dMap，用它的灰度加上处理作为位移值，之后将这个值应用到image贴图的采样中，就搞定啦：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;  &lt;span class=&amp;quot;k&amp;quot;&gt;vec4&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;t_map&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;texture2D&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;map&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vUv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mo&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;vUv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]));&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;offset&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;t_map&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;g&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;vec2&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vUv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;offset&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;vUv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;offset&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;vec4&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;t_image&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;texture2D&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;image&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;当然有这些还是不够的，目前我们只能得到一张静态的位移后的图，下一步我们需要让它动起来并且能控制其位移的程度。为了实现这些，我们需要三个uniform变量&lt;code&gt;u_time&lt;/code&gt;, &lt;code&gt;scaleX&lt;/code&gt;和&lt;code&gt;scaleY&lt;/code&gt;，通过在js中更新这些变量，便可以间接地控制着色器中具体的坐标偏移量，比如下面这段代码就是通过一个不断递增的&lt;code&gt;u_time&lt;/code&gt;去达到循环位移dMap的目的：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;uniform&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;sampler2D&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;image&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;uniform&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;sampler2D&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;map&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;uniform&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;scaleX&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;uniform&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;scaleY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;uniform&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;u_time&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;varying&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;vec2&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;vUv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;void&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;main&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;vec4&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;t_map&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;texture2D&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;map&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;fract&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;vUv&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;u_time&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;offset&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;t_map&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;g&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;vec2&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;vec2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;vUv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;offset&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;scaleX&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;vUv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;offset&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;scaleY&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;vec4&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;t_image&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;texture2D&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;image&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;uv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

  &lt;span class=&amp;quot;n&amp;quot;&gt;gl_FragColor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;t_image&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;如此一来，dMap在渲染过程中不断平移，每个片段本次的uv偏移都是它邻点上一次的偏移，这样就可以有一种自然的波动效果了。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Monday, 18 Jun 2018 20:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2018.06.18 20:00:article/Skill-2018_06_18_a</guid>
<category>HTML5</category>
<category>WebGL</category>
<category>Shader</category>
<category>Displacement</category>
<category>Water</category>
<category>Liquid</category>
</item>

<item>
<title>Lottie原理与源码解析</title>
<link>http://dtysky.moe/article/Skill-2018_06_15_a</link>
<description>&lt;p&gt;&lt;a href=&amp;quot;https://github.com/airbnb/lottie-web&amp;quot;&gt;Lottie（Bodymivin）&lt;/a&gt;是一个动画库，其和GSAP这类专注动画曲线、插值等js动画库不同，它本质上是一套跨平台的平面动画解决方案。其提供了一套完整得从AE到各个终端的工具流，通过AE的插件将设计师做的动画导出成一套定义好的json文件，之后再通过渲染器进行渲染，它提供了“SVG”、“Canvas”和“HTML”三种渲染模式，最常用的是第一种和第二种。&lt;/p&gt;
&lt;h2&gt;特性&lt;/h2&gt;
&lt;p&gt;Lottie提供了如下特性，并给出了AE的功能支持度与限制：&lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;http://airbnb.io/lottie/supported-features.html&amp;quot;&gt;http://airbnb.io/lottie/supported-features.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;每个渲染器均有各自的实现，其复杂度也各有不同，但毫无例外的是图形越多、参与运算的属性越多、帧率越高，其对性能的消耗也就越高，这些要看实际的状况。  &lt;/p&gt;
&lt;h2&gt;lottie-web优势&lt;/h2&gt;
&lt;p&gt;以上列表可以看出在web上使用lottie的巨大优势：支持特性最多，可以做出最丰富的效果。而且得意于Web的特性，lottie-web较为灵活，迭代快，修改和添加功能也较为容易。&lt;/p&gt;
&lt;h2&gt;目前问题&lt;/h2&gt;
&lt;p&gt;没有发现官方或第三方对&lt;strong&gt;sprites map&lt;/strong&gt;（如果有请务必告诉我！），而lottie动画的每个图片又相对较小，这个对于图片资源较多的情况下会有碎片化问题，对于移动端web在线资源是比较难接受的。所以需要想个办法让它支持，要么加一个前置Loader，要么进行修改源码，前者好处是不动源码，作为插件比较灵活，但有可能的性能问题，后者需要改动的可能较多（有可能要修改AE插件）但可能性能好一点，这个可以随着使用逐步研究起来。&lt;/p&gt;
&lt;h2&gt;代码分析&lt;/h2&gt;
&lt;p&gt;从lottie-web的Git源Clone代码，其中&lt;strong&gt;player/js&lt;/strong&gt;目录下的即为播放器代码，其中&lt;strong&gt;module.js&lt;/strong&gt;即为入口，让我们从这个文件开始分析，以一个文件被加载到播放以及各种其他操作为例，看看lottie到底做了什么工作：  &lt;/p&gt;
&lt;h3&gt;Lottie模块&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;module.js&lt;/strong&gt;中是标准的各种模式下模块的导出流程，其中模块在&lt;code&gt;factory&lt;/code&gt;函数中被初始化并导出，在这个函数中，它定义了一个&lt;code&gt;lottiejs&lt;/code&gt;，这个就是实际挂载模块的对象。  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;lottiejs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;play&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;animationManager&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;play&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;lottiejs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;pause&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;animationManager&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;pause&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;lottiejs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setLocationHref&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;setLocationHref&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;lottiejs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;togglePause&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;animationManager&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;togglePause&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;lottiejs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setSpeed&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;animationManager&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setSpeed&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;lottiejs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setDirection&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;animationManager&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setDirection&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;静态方法&lt;/h3&gt;
&lt;p&gt;静态方法是指直接挂载在&lt;code&gt;lottiejs&lt;/code&gt;上的方法，我们可以在导入lottie后直接调用，比如最基础的&lt;code&gt;loadAnimation&lt;/code&gt;方法，其用法是：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;import&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Lottie&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;from&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;lottie-web&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;nx&amp;quot;&gt;Lottie&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;loadAnimation&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({......});&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;lottie提供了很多这样的静态方法，比如&lt;code&gt;setSpeed&lt;/code&gt;、&lt;code&gt;inBrowser&lt;/code&gt;、&lt;code&gt;installPlugin&lt;/code&gt;等，这些方法一般有两个作用：其一是设置所有动画对象的对应的属性值，比如我通过&lt;code&gt;Lottie.setSpeed(.5)&lt;/code&gt;修改了速度，那么所有对象的速度就被修改为了的这个全局的速度；其二，静态方法还可以在没有拿到实际动画对象的时候对其进行控制，比如&lt;code&gt;Lottie.play(&amp;apos;animation1&amp;apos;)&lt;/code&gt;就可以播放注册过的命名为&lt;code&gt;&amp;apos;animation1&amp;apos;&lt;/code&gt;的对象，其等价于&lt;code&gt;lottieAnimation1.play()&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;初始化动画&lt;/h3&gt;
&lt;p&gt;lottie可以通过&lt;code&gt;loadAnimation&lt;/code&gt;或&lt;code&gt;registerAnimation&lt;/code&gt;来初始化动画。后者需要预先插入一个Tag，这个Tag上有一个&lt;code&gt;data-animation-path&lt;/code&gt;属性，其的值需要指向你的动画的json文件，这个一般在你有许多lottie动画，预先对动画进行了布局时很有用，但一般我们使用的是第一种做法：通过一个对象来初始化一个动画对象。  &lt;/p&gt;
&lt;p&gt;可以在源码中看到，&lt;code&gt;loadAnimation&lt;/code&gt;或&lt;code&gt;registerAnimation&lt;/code&gt;这两个方法，以及其他大部分静态方法实际上都被委托到了一个叫&lt;code&gt;animationManager&lt;/code&gt;的对象中同名的方法实现，那么这个对象在哪呢？在&lt;strong&gt;animation/AnimationManager&lt;/strong&gt;中。  &lt;/p&gt;
&lt;p&gt;可见，这个对象可以说是实际上的模块顶层，大多重要方法都在其中。让我们找到上述两个初始化方法，看看它们都干了啥。首先是&lt;code&gt;registerAnimation&lt;/code&gt;。&lt;/p&gt;
&lt;h4&gt;registerAnimation&lt;/h4&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;function&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;registerAnimation&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;element&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;animationData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;element&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;null&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;kd&amp;quot;&gt;var&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;while&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;len&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;registeredAnimations&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;elem&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;==&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;element&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;registeredAnimations&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;elem&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!==&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;null&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
            &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;registeredAnimations&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;animation&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;kd&amp;quot;&gt;var&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;animItem&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;AnimationItem&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;setupAnimation&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;animItem&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;element&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;animItem&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;element&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;animationData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;animItem&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;首先这个对象的闭包中定义了几个全局的变量&lt;code&gt;len&lt;/code&gt;、&lt;code&gt;registeredAnimations&lt;/code&gt;等，用于判断和缓存已注册的动画元素。在调用这个方法时需要传入DOM对象&lt;code&gt;element&lt;/code&gt;和动画数据&lt;code&gt;animationData&lt;/code&gt;，先判断缓存中有没有对应对象，有的话直接返回，否则new一个&lt;code&gt;AnimationItem&lt;/code&gt;类为&lt;code&gt;animItem&lt;/code&gt;对象，&lt;code&gt;AnimationItem&lt;/code&gt;这个类是动画容器的基类，之后会详细说到。新建对象后，lottie又将其和通过参数传入的DOM元素作为参数传入&lt;code&gt;setupAnimation&lt;/code&gt;方法，这个方法中会进行一些自定义事件的绑定。在之后新建的&lt;code&gt;animItem&lt;/code&gt;将会调用&lt;code&gt;setData&lt;/code&gt;方法将&lt;code&gt;animationData&lt;/code&gt;传入为对象设置动画数据，最后将对象返回：  &lt;/p&gt;
&lt;h4&gt;setData&lt;/h4&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;AnimationItem&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;prototype&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setData&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;function&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;wrapper&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;animationData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;kd&amp;quot;&gt;var&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;wrapper&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;wrapper&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;animationData&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;animationData&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;typeof&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;animationData&lt;/span&gt;  &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;object&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;animationData&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;JSON&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;parse&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;animationData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;null&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
    &lt;span class=&amp;quot;kd&amp;quot;&gt;var&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;wrapperAttributes&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;wrapper&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;attributes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

    &lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;path&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;wrapperAttributes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getNamedItem&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;data-animation-path&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;wrapperAttributes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getNamedItem&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;data-animation-path&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;value&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;wrapperAttributes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getNamedItem&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;data-bm-path&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;wrapperAttributes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getNamedItem&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;data-bm-path&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;value&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;  &lt;span class=&amp;quot;nx&amp;quot;&gt;wrapperAttributes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getNamedItem&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;bm-path&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;wrapperAttributes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getNamedItem&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;bm-path&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;value&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

   &lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setParams&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这个方法其实就是初始化animItem的参数&lt;code&gt;params&lt;/code&gt;（通过&lt;code&gt;this.setParams&lt;/code&gt;），参数中除了&lt;code&gt;wrapper&lt;/code&gt;和&lt;code&gt;animationData&lt;/code&gt;外还有&lt;code&gt;path&lt;/code&gt;、&lt;code&gt;autoplay&lt;/code&gt;这些参数，都是从&lt;code&gt;wrapper&lt;/code&gt;、也就是传给&lt;code&gt;registerAnimation&lt;/code&gt;的那个DOM元素的属性中获取的。让我们看看&lt;code&gt;setParams&lt;/code&gt;干了啥：  &lt;/p&gt;
&lt;h4&gt;setParams&lt;/h4&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;AnimationItem&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;prototype&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setParams&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;function&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;
    &lt;span class=&amp;quot;kd&amp;quot;&gt;var&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;animType&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;animType&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;animType&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;renderer&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;renderer&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;svg&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;switch&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;animType&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;case&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;canvas&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;
            &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;renderer&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;CanvasRenderer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rendererSettings&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
            &lt;span class=&amp;quot;k&amp;quot;&gt;break&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;case&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;svg&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;
            &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;renderer&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;SVGRenderer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rendererSettings&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
            &lt;span class=&amp;quot;k&amp;quot;&gt;break&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;default&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;
            &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;renderer&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;HybridRenderer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rendererSettings&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
            &lt;span class=&amp;quot;k&amp;quot;&gt;break&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;animationData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;configAnimation&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;animationData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;path&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;path&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;substr&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;json&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
            &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;path&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;substr&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
                &lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;path&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
            &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
            &lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;path&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;data.json&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

        &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;path&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;lastIndexOf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;\\&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!=&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
            &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;path&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;path&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;substr&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;path&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;lastIndexOf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;\\&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
            &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;path&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;path&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;substr&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;path&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;lastIndexOf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fileName&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;path&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;substr&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;path&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;lastIndexOf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fileName&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fileName&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;substr&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fileName&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;lastIndexOf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;.json&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;

        &lt;span class=&amp;quot;nx&amp;quot;&gt;assetLoader&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;load&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;path&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;configAnimation&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;bind&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;不太重要的地方就略去了，此方法中最重要的其实就两点——一是确定渲染方式并创建响应的渲染器，而是通过是否有&lt;code&gt;animationData&lt;/code&gt;参数来确定数据来源并初始化数据，如果有则直接用这个数据，如果没有则去寻找&lt;code&gt;path&lt;/code&gt;参数，之后调用&lt;code&gt;assetLoader.load&lt;/code&gt;来初始化数据，最终数据都会被传给&lt;code&gt;configAnimation&lt;/code&gt;方法。  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: 这里要注意如果路径后缀不是&lt;code&gt;json&lt;/code&gt;将会自动加上&lt;code&gt;data.json&lt;/code&gt;！  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;configAnimation&lt;/h4&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;AnimationItem&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;prototype&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;configAnimation&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;function&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;animData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;renderer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;animationData&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;animData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;totalFrames&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;Math&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;floor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;animationData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;op&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;animationData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ip&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;renderer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;configAnimation&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;animData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;animData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;assets&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;animData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;assets&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[];&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;renderer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;searchExtraCompositions&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;animData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;assets&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;assets&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;animationData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;assets&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;frameRate&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;animationData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fr&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;firstFrame&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;Math&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;round&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;animationData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ip&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;frameMult&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;animationData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fr&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1000&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;trigger&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;config_ready&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;preloadImages&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;loadSegments&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;updaFrameModifier&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;waitForFontsLoaded&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;在这个方法中将会初始化更多动画对象的属性，比如totalFrames等，此外最重要的是去加载一些其他资源，比如图像、字体等，也会去加载预先定义的片段&lt;code&gt;segments&lt;/code&gt;（默认有一个），片段是lottie提供的一种动画控制手段，可以将多个动画统一在一个动画对象中，其本质上是不断调用&lt;code&gt;loadNextSegments&lt;/code&gt;方法，获取片段路径然后不断调用&lt;code&gt;assetsLoader.load&lt;/code&gt;方法去加载新的片段。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;片段的路径被写死为了&lt;code&gt;${basePath}/${filename}_${segementsPos}.json&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;注意在加载片段后&lt;code&gt;includeLayers&lt;/code&gt;方法被调用，其中主要涉及到一些静态资源的追加合成，以及利用&lt;code&gt;utils/dataManager.completeData&lt;/code&gt;进行的&lt;code&gt;animationData&lt;/code&gt;的检查和标准化（在别的阶段也会被调用，比如&lt;code&gt;waitForFontsLoaded&lt;/code&gt;），还会调用&lt;code&gt;expressionsPlugin.initExpressions&lt;/code&gt;（如果有的话）方法对数据中的表达式进行初始化。&lt;/p&gt;
&lt;p&gt;一切都结束后一个&lt;code&gt;data_ready&lt;/code&gt;事件将会被触发，至此，&lt;code&gt;registerAnimation&lt;/code&gt;方式的动画Load便结束。  &lt;/p&gt;
&lt;h4&gt;loadAnimation&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;loadAnimation&lt;/code&gt;的大部分流程和&lt;code&gt;registerAnimation&lt;/code&gt;是一样的，不同的是它直接接受一个包含了初始化参数的&lt;code&gt;animationData&lt;/code&gt;对象：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;function&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;loadAnimation&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
    &lt;span class=&amp;quot;kd&amp;quot;&gt;var&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;animItem&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;AnimationItem&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;setupAnimation&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;animItem&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;null&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;animItem&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setParams&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;animItem&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;把&lt;code&gt;setupAnimation&lt;/code&gt;的第二个参数直接置为null，去掉了&lt;code&gt;setData&lt;/code&gt;阶段（因为不需要），直接跳到&lt;code&gt;setParams&lt;/code&gt;阶段，剩下的都一样。&lt;/p&gt;
&lt;h4&gt;全局搜索初始化&lt;/h4&gt;
&lt;p&gt;除了以上两种方式初始化动画对象，还有个方法&lt;code&gt;searchAnimations&lt;/code&gt;用于初始化所有dom元素定义的动画。它默认会搜索所有classList中有&lt;code&gt;lottie&lt;/code&gt;或&lt;code&gt;bodymovin&lt;/code&gt;的dom元素，将它们全部用&lt;code&gt;registerAnimation&lt;/code&gt;进行初始化。&lt;/p&gt;
&lt;h3&gt;动画参数&lt;/h3&gt;
&lt;p&gt;Lottie通过一系列参数完成对动画对象的初始化，然而可悲的是他们被分散在源码的各个地方也没注释，好在官网还有一些例子加上拼凑我总结了一下定义：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 要挂载动画的DOM对象，默认为空，新建一个。&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;container&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;HTMLElement&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 渲染模式，默认为canvas。&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 对应DOM属性 data-anim-type | data-bm-type | bm-type | data-anim-renderer&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;renderer&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;svg&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;canvas&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;html&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 循环参数，ture/false以开关量控制，若为数字，则0~max为循环次数，负数为无限循环。默认为true&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 对应DOM属性 data-anim-loop | data-bm-loop | bm-loop&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;loop&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;boolean&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 自动播放，默认为true。&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 对应DOM属性 data-anim-autoplay | data-bm-autoplay | bm-autoplay&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;autoplay&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;boolean&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 动画数据，需要符合数据格式（见下），优先级高于path参数，默认为null。&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;animationData&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;AnimationData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 动画json文件路径，如果末尾不是.json则会自动加上/data.json，默认为null。&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 对应DOM属性 data-animation-path | data-bm-path | bm-path&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;path&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 渲染设置。&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;rendererSettings&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;c1&amp;quot;&gt;// 指定canvasContext&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;context&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;canvasContext&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;c1&amp;quot;&gt;// 是否先清除canvas画布，canvas模式独占，默认false。&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;clearCanvas&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;boolean&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;c1&amp;quot;&gt;// 是否开启渐进式加载，只有在需要的时候才加载dom元素，在有大量动画的时候会提升初始化性能，但动画显示可能有一些延迟，svg模式独占，默认为false。&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;progressiveLoad&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;boolean&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;c1&amp;quot;&gt;// 当元素opacity为0时隐藏元素，svg模式独占，默认为true。&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;hideOnTransparent&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;boolean&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;c1&amp;quot;&gt;// 容器追加class，默认为&amp;#39;&amp;#39;&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;className&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;播放控制&lt;/h3&gt;
&lt;p&gt;Lottie提供了一系列API来控制播放流程，其控制的基准都分为两个层次——时间(time)和帧(frame)。一般而言像是&lt;code&gt;goToAndPlay&lt;/code&gt;这种方法除了第一个&lt;code&gt;value&lt;/code&gt;参数之外，还谁有一个&lt;code&gt;isFrame&lt;/code&gt;参数，其表明前面这个值是&lt;code&gt;time&lt;/code&gt;还是&lt;code&gt;frame&lt;/code&gt;，一般而言默认都是时间，但我强烈建议用帧来控制，对应AE比较清晰。下面我列举一下常用的方法。&lt;/p&gt;
&lt;h4&gt;常用的方法&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;play()&lt;/code&gt;: 播放动画。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pause()&lt;/code&gt;: 暂停动画。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;togglePause()&lt;/code&gt;: 切换播放和暂停的状态。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;stop()&lt;/code&gt;: 停止动画。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;goToAndStop(value: number, isFrame: boolean)&lt;/code&gt;: 跳转到某时间/帧并播放。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;goToAndPlay(value: number, isFrame: boolean)&lt;/code&gt;: 跳转到某时间/帧并停止。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;setSegment(init: number, end: number)&lt;/code&gt;: 设置当前的segment区间。 &lt;/li&gt;
&lt;li&gt;&lt;code&gt;playSegments(range: [number, number], force: boolean)&lt;/code&gt;: 设置并播放当前的segment区间，&lt;code&gt;force&lt;/code&gt;为true的时候讲立即播放，否则会等当前一循环播放完再切换。如果&lt;code&gt;range[0] &amp;gt; range[1]&lt;/code&gt;则会设置方向为反播放。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;resetSegments(force: boolean)&lt;/code&gt;: 重置当前的segment区间，&lt;code&gt;force&lt;/code&gt;依然是是否立即生效的标志。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;setSpeed(value: number)&lt;/code&gt;: 设置播放速度，基准为1。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;setDirection(value: -1 | 1)&lt;/code&gt;: 设置播放正反，1为正，-1为反。&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;play&lt;/h4&gt;
&lt;p&gt;让我们从&lt;code&gt;animationItem.play&lt;/code&gt;方法开始吧，了解一下整个播放流程的细节：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;isPaused&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;isPaused&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;false&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_idle&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_idle&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;false&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;trigger&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;_active&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;除去前置的各种flag判断，这个方法中最重要的操作就是触发了&lt;code&gt;_active&lt;/code&gt;事件，让我们看看这个事件在哪里被监听了：  &lt;/p&gt;
&lt;h4&gt;addPlayingCount -&amp;gt; activate -&amp;gt; first -&amp;gt; resume&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;_avtive&lt;/code&gt;事件只在一处被监听，其为&lt;code&gt;AnimationManager.addPlayingCount&lt;/code&gt;，之后再传递到&lt;code&gt;active&lt;/code&gt;方法中，之后再&lt;code&gt;first(nowTime)&lt;/code&gt;方法中初始化播放时间，设置起点，然后就到了&lt;code&gt;resume&lt;/code&gt;方法：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;function&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;resume&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;nowTime&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;kd&amp;quot;&gt;var&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;elapsedTime&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;nowTime&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;initTime&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kd&amp;quot;&gt;var&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;len&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;registeredAnimations&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;animation&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;advanceTime&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;elapsedTime&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;initTime&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;nowTime&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;playingAnimationsNum&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_isFrozen&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;nb&amp;quot;&gt;window&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;requestAnimationFrame&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;resume&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;_stopped&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这个方法中首先会计算当前时间和初始化时间的一个diff&lt;code&gt;elapsedTime&lt;/code&gt;，之后将&lt;code&gt;elapsedTime&lt;/code&gt;依次传入所有动画对象的&lt;code&gt;advanceTime&lt;/code&gt;方法中，之后更新初始时间并根据状态看是否要进行下一次循环。可见，这里在首帧后lottie做的事情其实就是不断和上一次RAF的时间算diff，并不断利用这个diff去进行下一步操作。&lt;/p&gt;
&lt;h4&gt;advanceTime -&amp;gt; setCurrentRawFrameValue -&amp;gt; gotoFrame&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;advanceTime&lt;/code&gt;是animItem的一个方法，其主要工作是根据上一个方法传来的diff以及一些flag进行一些中间控制，比如判断是否播放结束，播放结束是否要Loop，同时在结束的时候触发响应的事件通知开发者。除此之外，其最重要的作用是算出&lt;code&gt;nextValue = this.currentRawFrame + value * this.frameModifier;&lt;/code&gt;，即下一帧的帧数，然后传给&lt;code&gt;animItem.setCurrentRawFrameValue&lt;/code&gt;方法，这个方法也只是记录下当前rawFrame（原始Frame）的值，然后直接跳到&lt;code&gt;goToFrame&lt;/code&gt;方法。  &lt;/p&gt;
&lt;p&gt;在&lt;code&gt;goToFrame&lt;/code&gt;方法中，lottie计算出了事实上的&lt;code&gt;currentFrame&lt;/code&gt;，也就是实际用于渲染的frame值，触发进入frame的事件后，就调用动画对象自身的&lt;code&gt;renderFrame&lt;/code&gt;方法进行渲染。&lt;/p&gt;
&lt;h3&gt;事件&lt;/h3&gt;
&lt;p&gt;以上无数次提到了lottie的事件机制，lottie提供了许多事件用于控制动画的播放和初始化等，以下是暴露出来的一些事件和触发时机：  &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;complete：在播放结束后被触发（一次Loop结束不算）。&lt;/li&gt;
&lt;li&gt;loopComplete：在没次Loop结束后触发。&lt;/li&gt;
&lt;li&gt;enterFrame：在进入每一帧的时候触发（注意stop的时候也会触发一次）。&lt;/li&gt;
&lt;li&gt;segmentStart：当一段segment开始播放的时候触发。&lt;/li&gt;
&lt;li&gt;config_ready：当所有的参数配置解析完毕后触发。&lt;/li&gt;
&lt;li&gt;data_ready：在所有的segments被加载完毕后触发。&lt;strong&gt;注意这个当你直接传入animationData而非path的时候，解析是同步的，所以执行完loadAnimation的时候实际上已经被触发过了，并且这个事件是超前与image这种资源的加载的，所以建议加上if (animation.loaded)的判断或绑定DOMLoaded事件。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;data_failed：虽然文档写了但代码里并没有这个事件嗯。&lt;/li&gt;
&lt;li&gt;loaded_images：所有图片资源加载完毕的时候会触发，虽然代码里写了 &lt;code&gt;if (!error)&lt;/code&gt;然而当你详细去看的时候只会发现err永远为null。&lt;/li&gt;
&lt;li&gt;DOMLoaded：当元素被添加到DOM的时候触发，这个是比较可靠的可以替换&lt;code&gt;data_ready&lt;/code&gt;的事件。&lt;/li&gt;
&lt;li&gt;destroy：当元素被释放的时候触发。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;数据结构&lt;/h3&gt;
&lt;p&gt;接下来要讲最大头的渲染了，不过在这之前，我们需要先了解一下lottie所定义的数据结构，否则看到那些奇怪的&lt;code&gt;ip&lt;/code&gt;、&lt;code&gt;op&lt;/code&gt;、&lt;code&gt;fr&lt;/code&gt;这些字段不知道是啥意思就尴尬了。  &lt;/p&gt;
&lt;p&gt;其实Lottie官方是给出了详细的定义的，在项目根目录下的&lt;strong&gt;docs/json&lt;/strong&gt;内，随便打开一个animation.json可以看到：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;layers&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;title&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;Layers&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;description&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;List of Composition Layers&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;items&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;oneOf&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;
      &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;$ref&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;#/layers/shape&amp;quot;&lt;/span&gt;
      &lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
      &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;$ref&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;#/layers/solid&amp;quot;&lt;/span&gt;
      &lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
      &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;$ref&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;#/layers/comp&amp;quot;&lt;/span&gt;
      &lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
      &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;$ref&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;#/layers/image&amp;quot;&lt;/span&gt;
      &lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
      &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;$ref&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;#/layers/null&amp;quot;&lt;/span&gt;
      &lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
      &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;$ref&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;#/layers/text&amp;quot;&lt;/span&gt;
      &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt;
    &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;object&amp;quot;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
  &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;array&amp;quot;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;key为在实际的json文件中对应的Key，&lt;code&gt;title&lt;/code&gt;为标题，&lt;code&gt;description&lt;/code&gt;为说明，&lt;code&gt;type&lt;/code&gt;为其在程序中的数据类型，&lt;code&gt;items&lt;/code&gt;则是其作为array类型的元素，&lt;code&gt;oneOf&lt;/code&gt;以及其下的数组表示其元素为以下几种之一，&lt;code&gt;$ref&lt;/code&gt;表示这种元素对应的文档地址。&lt;/p&gt;
&lt;p&gt;通过这个，我们便可以很方便得查找每个字段的含义，这样也方便与理解渲染。&lt;/p&gt;
&lt;h3&gt;渲染&lt;/h3&gt;
&lt;p&gt;承接播放控制一节的最后，渲染从动画对象的&lt;code&gt;renderFrame&lt;/code&gt;开始：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;AnimationItem&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;prototype&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;renderFrame&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;function&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;isLoaded&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;false&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;renderer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;renderFrame&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;currentFrame&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;firstFrame&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;可见其首先做了下防卫，防止没有加载完成的动画的播放，然后就调用通过参数生成绑定的渲染器&lt;code&gt;this.renderer&lt;/code&gt;的&lt;code&gt;renderFrame&lt;/code&gt;，将当前帧数传给其进行渲染。  &lt;/p&gt;
&lt;h4&gt;configAnimation -&amp;gt; includeLayers&lt;/h4&gt;
&lt;p&gt;让我们回顾一下动画初始化时的&lt;code&gt;setParams&lt;/code&gt;阶段的&lt;code&gt;configAnimation&lt;/code&gt;方法，其中有一句是：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;renderer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;configAnimation&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;animData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这一句就是通过相应的renderer去通过动画数据初始化渲染的&lt;strong&gt;容器&lt;/strong&gt;，比如对于svg渲染模式，就是去生成初始DOM节点，对于canvas，就是去生成初始化的画布。  &lt;/p&gt;
&lt;p&gt;在之后的初始化过程中，&lt;code&gt;animItem.includeLayers&lt;/code&gt;方法还会调用到&lt;code&gt;renderer.includeLayers&lt;/code&gt;方法，这个方法是在所有renderer继承的baseRenderer中的，它负责将所有动画数据中的图层保存到renderer中备用。&lt;/p&gt;
&lt;h4&gt;renderFrame&lt;/h4&gt;
&lt;p&gt;对于每个render，最核心的方法就是&lt;code&gt;renderFrame&lt;/code&gt;，它最重要的代码如下：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;globalData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_mdf&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;false&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;kd&amp;quot;&gt;var&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;len&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;layers&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;length&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;completeLayers&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;checkLayers&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;num&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;len&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;--&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;completeLayers&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;||&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;elements&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]){&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;elements&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;prepareFrame&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;num&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;layers&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;st&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;globalData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_mdf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;len&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;completeLayers&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;||&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;elements&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]){&lt;/span&gt;
            &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;elements&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;renderFrame&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这个渲染流程分为了三个步骤，分别是&lt;code&gt;checkLayers&lt;/code&gt;、&lt;code&gt;prepareFrame&lt;/code&gt;和&lt;code&gt;renderFrame&lt;/code&gt;。  &lt;/p&gt;
&lt;p&gt;在&lt;code&gt;checkLayers&lt;/code&gt;中，实际上做的事情是检查图层是否被建立完毕，如果没有则去使用图层原信息通过&lt;code&gt;renderer.buildItem&lt;/code&gt;方法初始化真正的渲染数据，也可以认为是生成对应的虚拟渲染元素树，这些元素的实现都在&lt;strong&gt;elements&lt;/strong&gt;目录下，对于这些元素之后会详细说道。注意对于svg渲染器，这个也会递归地去初始化DOM树，填充图形元素。  &lt;/p&gt;
&lt;p&gt;在&lt;code&gt;prepareFrame&lt;/code&gt;中，实际上是调用每个初始化过的元素的&lt;code&gt;prepareFrame&lt;/code&gt;，这个其实就是为渲染做准备，检查元素的一些状态然后决定&lt;code&gt;globalData._mdf&lt;/code&gt;或是&lt;code&gt;isInRange&lt;/code&gt;这些状态，为之后的渲染做准备。要注意的是每种元素（像是图形、文本等）的准备过程各有不同，而且官方这部分明显过度设计多继承加上没有注释也没有ts导致不太好阅读，所以读的时候要格外注意。  &lt;/p&gt;
&lt;p&gt;通过上一步得到的&lt;code&gt;globalData._mdf&lt;/code&gt;属性，我们便可以判断是否要进行下一步的渲染，如果没有修改就不渲染，节省性能，其实不仅仅是全局的&lt;code&gt;_mdf&lt;/code&gt;，每个子节点元素也自己会维护一个，来防止可以避免的渲染发生，这么来看，其实&lt;code&gt;prepareFrame&lt;/code&gt;算是类似于React的前置Diff吧。  &lt;/p&gt;
&lt;p&gt;渲染阶段即调用每个节点元素的&lt;code&gt;renderFrame&lt;/code&gt;实现，这个实现同样在每个渲染器也各有不同。细节太过丰富这里就不在细化解析了，只说下每一种的大致架构：  &lt;/p&gt;
&lt;h4&gt;SVGRenderer&lt;/h4&gt;
&lt;p&gt;SVGRenderer的实际渲染实际上是委托给每个&lt;strong&gt;elements/svgElements&lt;/strong&gt;下的每个元素进行的，其中最基础的是&lt;code&gt;SVGBaselement&lt;/code&gt;，其他的几种Element都继承自它和其他的几种通用Element（比如&lt;strong&gt;elements/BaseElement&lt;/strong&gt;）。对于SVGRenderer而言，所有元素本质上都被分成了两种：&lt;strong&gt;shape&lt;/strong&gt;
和&lt;strong&gt;text&lt;/strong&gt;，而这两种元素有各自的渲染方法进行渲染。注意这两种元素的实现也有一些共性，那就是对于动画中的变换以及滤镜等，他们的实现中都有类似这么一句：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;extendPrototype&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;([&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;BaseElement&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;TransformElement&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;SVGBaseElement&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;IShapeElement&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;HierarchyElement&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;FrameElement&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;RenderableDOMElement&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;SVGShapeElement&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;前面这一大长串的列表就是指这个元素类继承了多少种特性，这些特性中有来自于通用变换特性的像是&lt;code&gt;TransformElement&lt;/code&gt;、&lt;code&gt;RenderableDOMElement&lt;/code&gt;（在&lt;strong&gt;elements/helpers&lt;/strong&gt;中），也有SVG的基础类&lt;code&gt;SVGBaseElement&lt;/code&gt;，还有像是Shape元素的专用接口&lt;code&gt;IShapeElement&lt;/code&gt;。而在实际的动画中，元素就是靠将具体的变化派发到这些基类的方法中去实现的。  &lt;/p&gt;
&lt;p&gt;比如我在AE做了一个shape的transform动画，那么最终这个动画会被派发给&lt;code&gt;SVGShapeElement&lt;/code&gt;渲染逻辑，之后利用&lt;code&gt;TransformElement&lt;/code&gt;中的计算逻辑计算每一帧对应的transform矩阵，然后在通过设置给具体的DOM元素上的属性或样式来完成修改。  &lt;/p&gt;
&lt;p&gt;而对于滤镜（主要是颜色效果），SVGElement则会将其派发给&lt;strong&gt;elements/svgElements/SVGEffect&lt;/strong&gt;进行管理，而&lt;code&gt;SVGEffect&lt;/code&gt;又会通过类型创建对应的&lt;strong&gt;effects&lt;/strong&gt;下的对象，从而真正派发给这些对象进行渲染，其本质上其实也是生成色彩矩阵，归一化成矩阵进行管理。&lt;/p&gt;
&lt;h4&gt;CanvasRenderer&lt;/h4&gt;
&lt;p&gt;相较于SVGRenderer，CanvasRenderer受限于其能力，所以要简洁许多，当然这也造成了一些效果它不支持，比如滤镜效果（可以看到&lt;code&gt;CanvasEffects.prototype.renderFrame = function(){}&lt;/code&gt;）。  &lt;/p&gt;
&lt;p&gt;CanvasRenderer本质上就是根据动画数据将每一帧的对象不断重绘出来，没有特别好说的。像是tranform变换这些和SVG的归一化处理基本一致。&lt;/p&gt;
&lt;h4&gt;HTMLRenderer&lt;/h4&gt;
&lt;p&gt;HTMLRenderer就没什么好说的了，受限于其功能，支持的特性最少，只能做一些很简单的图形或者文字，也不支持滤镜效果。其生成变换的原理倒是和SVG有些类似，毕竟都是DOM嘛。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Friday, 15 Jun 2018 19:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2018.06.15 19:00:article/Skill-2018_06_15_a</guid>
<category>HTML5</category>
<category>SVG</category>
<category>Lottie</category>
</item>

<item>
<title>前端视觉交互——TreesGenerator2D</title>
<link>http://dtysky.moe/article/Skill-2018_04_22_b</link>
<description>&lt;p&gt;利用约束参数随机通过点击生成一颗颗抽象化的树。&lt;br /&gt;
这个效果是我在去年（2017）的the best of codepen发现的，觉得是很有意思，非常具有艺术感，于是学了下原理并在这里实现了一下。&lt;br /&gt;
&lt;a href=&amp;quot;http://github.com/dtysky/paradise/tree/master/src/collection/TreesGenerator2D&amp;quot;&gt;code&lt;/a&gt;, &lt;a href=&amp;quot;http://paradise.dtysky.moe/effect/trees-generator-2d&amp;quot;&gt;demo&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;Cover&amp;quot; src=&amp;quot;//paradise.dtysky.moe/assets/DigitalClock3D/cover.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;h2&gt;原理&lt;/h2&gt;
&lt;p&gt;此效果原理稍微有点复杂，需要分为“树的生成”以及“树的绘制”两部分，下面让我们来一步一步分析：&lt;/p&gt;
&lt;h3&gt;从一棵树开始&lt;/h3&gt;
&lt;p&gt;这个效果是可以支持多棵树同时生长的，但其基础还是一颗颗独立的树，所以为了弄清整个的原理，让我们先从一棵树开始。&lt;/p&gt;
&lt;h4&gt;树的抽象&lt;/h4&gt;
&lt;p&gt;为了绘制一棵树，我们首先要构建一个合适的对象来描述它。根据在现实世界的观察，不难得知一棵树最重要由三部分构成——“干”、“枝”、“叶”/“花”，所以再要构建的&lt;code&gt;Tree&lt;/code&gt;对象中，理应有三个成员来描述它们。但考虑到在在一棵树的快进的生命周期中，其主干和分枝的生长应该是可以并行进行的，所以本质上它们是一个东西，而叶子不同，仍然是独立的，所以在&lt;code&gt;Tree&lt;/code&gt;中我们只需要&lt;code&gt;points&lt;/code&gt;和&lt;code&gt;leaves&lt;/code&gt;两个成员来描述，前一个描述枝干的点集，后一个描述叶子，那么为什么是“点集”而不是别的呢，接下来就让我们分析一下。  &lt;/p&gt;
&lt;h4&gt;树的绘制&lt;/h4&gt;
&lt;p&gt;有了树的抽象结构之后，要考虑的就是这样一个问题——假设我们已经有了一颗由以上的树，如何来描述其枝干和叶子，使其可以被最方便得绘制？考虑到绘制的媒介是canvas，我们能使用的工具也无非以下几个：  &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;rect&lt;/li&gt;
&lt;li&gt;image&lt;/li&gt;
&lt;li&gt;arc&lt;/li&gt;
&lt;li&gt;ellipse&lt;/li&gt;
&lt;li&gt;line&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;rect和line对于一棵树的绘制而言太生硬，image不予考虑，剩下的也就只有arc和ellipse了。这二者其实没有本质上的区别，arc绘制的是圆弧、ellipse绘制的是椭圆，前者是后者的一个子集。相对而言，arc控制起来更为简单，所以在这里我们选择了arc——等等，有这么简单吗？  &lt;/p&gt;
&lt;p&gt;让我们仔细想想，arc绘制的是圆弧，设定一定的参数后便可以画出一个圆，而出去本身确实可以用圆来描述的抽象的叶子，枝干如何用圆来描述呢？显然我们不可能去真的绘制一个个很小的点并将其密集拼成像素化的树，这无论是性能还是效果都不会好。那么究竟应该如何去做？  &lt;/p&gt;
&lt;p&gt;答案其实很简单——拼起来即可。不错，你自己去尝试一下便可知，适当调整几个圆的绘制位置、半径、颜色和透明度，将它们叠在一起，你变可以得到一个类似于真实世界中的树干——它并不那么规整，但却乱得很合适，比如：  &lt;/p&gt;
&lt;p&gt;&lt;code&gt;js
ctx.fillStyle = &amp;apos;hsla(208, 80%, 91.5%, 1)&amp;apos;;
ctx.beginPath();
ctx.arc(400, 560, 16, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(400, 555, 15, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(400, 550, 14, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(400, 545, 13, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(400, 540, 13, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(400, 535, 11, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(400, 530, 12, 0, Math.PI * 2);
ctx.fill();&lt;/code&gt;  &lt;/p&gt;
&lt;p&gt;当然，这里的位置、半径、颜色和透明度等约束参数如何确定也是一门技术活，这个后面会讲到。  &lt;/p&gt;
&lt;p&gt;如此一来，我们便可以通过一个点集来绘制出一棵树，接下来的重点就是如何生成这些点集，换言之，就是“如何让一棵树从某个位置的一个点生长出来”。&lt;/p&gt;
&lt;h4&gt;生长&lt;/h4&gt;
&lt;p&gt;树是从一点生长出来的，也就是说，在初始化树的时候我会传给它一个坐标，就像是埋下了一个种子，它以这个种子为基点长出来，并且在合适的地方生长和分叉。这就需要一套算法去计算这种“生长”结果，显然，这是一个和历史状态相关的函数，我们可以将此函数描述为：  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;points[n] = f(points[n - 1])&lt;/strong&gt;&lt;br /&gt;
&lt;strong&gt;tree = points + leaves&lt;/strong&gt;  &lt;/p&gt;
&lt;p&gt;其中&lt;code&gt;points&lt;/code&gt;即为枝干的点集，那么我们如何来描述一个点呢？其实很简单，如前面所述，树的绘制是以点为基本单位的，所以这个点要能描述一个圆，所以其基本的属性也就是&lt;code&gt;x&lt;/code&gt;、&lt;code&gt;y&lt;/code&gt;、&lt;code&gt;color&lt;/code&gt;、&lt;code&gt;radius&lt;/code&gt;、&lt;code&gt;opacity&lt;/code&gt;。也就是说，以上这个函数的本质，就是通过前一个点的这些属性，生成下一个点的这些属性。  &lt;/p&gt;
&lt;p&gt;有了这个思路，接下来就这个函数的实现了，这个实现需要一些数学知识，比如&lt;strong&gt;&lt;a href=&amp;quot;https://zh.wikipedia.org/wiki/Perlin%E5%99%AA%E5%A3%B0&amp;quot;&gt;PerlinNoise&lt;/a&gt;&lt;/strong&gt;。不过我们先不管这个，先回顾并理顺一下树生成的主逻辑：  &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;首先确定一个种子的位置，生成种子，也就是第一个点。&lt;/li&gt;
&lt;li&gt;然后不断调用函数f，生成下一个点、下下个点......&lt;/li&gt;
&lt;li&gt;树的生长是有其极限的，这个极限应该是我们可以定义的。&lt;/li&gt;
&lt;li&gt;生长到极限以后长出叶子。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;可见，除去函数f，我们首先要定义一个树的生长极限并强制让它在这个时候停止生长。这个实现起来比较简单，只要在初始化的时候记录一个&lt;code&gt;age&lt;/code&gt;，之后在点生成将其传入给点，保证每个点的&lt;code&gt;age&lt;/code&gt;独立计算，并在生成新点的时候的时候不断检查这个&lt;code&gt;age&lt;/code&gt;，看下一步是生成新的点还是叶子，到极限后在去遍历&lt;code&gt;leaves&lt;/code&gt;数组绘制叶子，这个后面再说。  &lt;/p&gt;
&lt;p&gt;现在让我们回到函数f本身，看看它是怎么运作的。&lt;/p&gt;
&lt;h4&gt;生成函数&lt;/h4&gt;
&lt;p&gt;生成函数的核心是&lt;code&gt;age&lt;/code&gt;、&lt;code&gt;variance&lt;/code&gt;这两个点的属性，第一个前面说过了，是树的年龄，而一个则是分歧系数。其中年龄主要用于和&lt;code&gt;PerlinNoise&lt;/code&gt;以及之前点的&lt;code&gt;x&lt;/code&gt;和&lt;code&gt;y&lt;/code&gt;组合计算出&lt;code&gt;dirX&lt;/code&gt;和&lt;code&gt;dirY&lt;/code&gt;，而&lt;code&gt;variance&lt;/code&gt;则负责和&lt;code&gt;degrees&lt;/code&gt;组合计算出&lt;code&gt;randX&lt;/code&gt;和&lt;code&gt;randY&lt;/code&gt;，它们一起组合生成下一个点的&lt;code&gt;x&lt;/code&gt;和&lt;code&gt;y&lt;/code&gt;：  &lt;/p&gt;
&lt;p&gt;```js
const reduce = 0.01;
const n = (noise.at((point.x + point.age) * reduce, (point.y + point.age) * reduce) - 0.5) * 4 * Math.PI;
const mag = noise.at((point.y + point.age) * reduce, (point.x + point.age) * reduce);
const dirX = Math.cos(n) * mag;
const dirY = Math.sin(n) * mag;&lt;/p&gt;
&lt;p&gt;const diff = variance * point.opacity;
const degrees = point.degrees + (-diff + Math.random() * diff * 2);&lt;/p&gt;
&lt;p&gt;const randX = Math.cos(radians(degrees)) * Tree.DRAW_DISTANCE;
const randY = Math.sin(radians(degrees)) * Tree.DRAW_DISTANCE;&lt;/p&gt;
&lt;p&gt;const x = point.x + dirX + randX;
const y = point.y + dirY + randY;
```&lt;/p&gt;
&lt;p&gt;我们不去细究&lt;code&gt;PerlinNoise&lt;/code&gt;的细节，它本质上就是为了生成微小随机噪声，为树的生长提供一定接近真实的随机性，而&lt;code&gt;variance&lt;/code&gt;这个分歧系数则是为了提供一个大幅度改变树生长朝向的能力，也就是为树提供产生分支的能力，其具体是通过修改&lt;code&gt;degrees&lt;/code&gt;这个角度来实现的。  &lt;/p&gt;
&lt;p&gt;进一步研究其细节，会发现在树的整个生命周期中，主干一定会一直生长，而分枝是否会出现则是依据一个随机数是否在某一个范围内，同时随着年龄的增长，点的直径会越来越小，这就是最终生成的一颗一颗树是那个样子的理由。&lt;/p&gt;
&lt;h4&gt;叶子&lt;/h4&gt;
&lt;p&gt;当树生长到极限并满足一定的随机数之后，便会生成叶子，其实叶子和枝干的构成基本一致，只不过其受到的约束没有这么强，只要在其依附的分支周边一定范围内、并满足一定范围的大小即可。  &lt;/p&gt;
&lt;p&gt;这里程序设定了&lt;code&gt;LEAF_DISTANCE&lt;/code&gt;和&lt;code&gt;LEAFE_SIZE&lt;/code&gt;两个静态变量来约束叶子的范围和大小。  &lt;/p&gt;
&lt;p&gt;生成了叶子后，绘制也和枝干一样，用同样的方法将其画到对应区域即可。&lt;/p&gt;
&lt;h3&gt;到整片森林&lt;/h3&gt;
&lt;p&gt;完成了一棵树的绘制，要考虑的便是一个森林中许多树的并行生长了，所谓并行生长，就是说一颗树的生长不能阻塞其它树的生长，这个分解开来，便是并行计算与并行绘制。  &lt;/p&gt;
&lt;p&gt;为了维护多棵树，我们需要一个&lt;code&gt;trees&lt;/code&gt;数组。这个数组负责在生成一棵树的时候将其入队，并在其绘制完成时将其剔除，不但如此，它还要管理每一棵树的“生长 -&amp;gt; 绘制”流程。这就要求树类&lt;code&gt;Tree&lt;/code&gt;本身有一个生命周期的机制，来维护和管理其自身的初始化、更新、绘制和释放。  &lt;/p&gt;
&lt;p&gt;这个机制做起来并不困难，首先我设置了两个标记属性&lt;code&gt;generated&lt;/code&gt;和&lt;code&gt;drawn&lt;/code&gt;，分别表示树的生成状态和绘制状态。同时我也设计了两个方法&lt;code&gt;update&lt;/code&gt;和&lt;code&gt;draw&lt;/code&gt;分别用于更新计算和绘制。这两个生命周期函数在外部RAF触发的绘制循坏下不断执行，同时利用并更新两个标志位，便可以完成一棵树的整体的管理。  &lt;/p&gt;
&lt;p&gt;而得益于这种机制，树的并行绘制也水到渠成——只需要在每一帧轮询&lt;code&gt;trees&lt;/code&gt;里的每一个对象，调用其对应的生命周期即可。当然，这也是绝大多数库包括游戏引擎的流行做法。 &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Sunday, 22 Apr 2018 19:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2018.04.22 19:00:article/Skill-2018_04_22_b</guid>
<category>HTML5</category>
<category>Canvas</category>
<category>Tree</category>
<category>Growth</category>
</item>

<item>
<title>前端视觉交互——DigitalClock3D</title>
<link>http://dtysky.moe/article/Skill-2018_04_22_a</link>
<description>&lt;p&gt;一个基于THREE和GSAP(Tween)实现的3D数字时钟。 
这个也是以前见过的一个效果，觉得很不错就尝试做出来了。&lt;br /&gt;
&lt;a href=&amp;quot;http://github.com/dtysky/paradise/tree/master/src/collection/DigitalClock3D&amp;quot;&gt;code&lt;/a&gt;,&lt;a href=&amp;quot;http://paradise.dtysky.moe/effect/digital-clock-3d&amp;quot;&gt;demo&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;Cover&amp;quot; src=&amp;quot;//paradise.dtysky.moe/assets/DigitalClock3D/cover.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;h2&gt;原理&lt;/h2&gt;
&lt;p&gt;数字时钟想必大家基本都实现过，其逻辑模型很简单，就是用JS的API获取时间，然后将时分秒分别拆分计算出来，最后渲染到页面上。而本效果的重点是在于这个渲染。&lt;/p&gt;
&lt;h3&gt;效果拆解&lt;/h3&gt;
&lt;p&gt;首先让我们拆开这个时钟，不难发现其主要由六个数字和两对点构成，数字表明时间，由一个类似于魔方的立方体构成，点则用于分隔，由两个反色的cube构成，这个是单纯静态渲染方面的问题。而另一方面，如何让数字和点动起来也是个问题。也就是说，整个效果被规约为了以下几个小问题：  &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;如何在平面渲染一个数字&lt;/li&gt;
&lt;li&gt;如何在同一个立方体渲染多个数字&lt;/li&gt;
&lt;li&gt;如何渲染点&lt;/li&gt;
&lt;li&gt;如何让数字和点动起来&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;下面就这几点来做一下分析：  &lt;/p&gt;
&lt;h3&gt;在平面上渲染一个数字&lt;/h3&gt;
&lt;p&gt;渲染一个数字并不困难，有过硬件背景或者做过数电实验的同学，应该都接触过数码管这东西。而在本效果中渲染一个数字和数码管其实没什么区别，本质上都是设置几个格子，通过将格子染上不同的颜色来表现出一个数字。在本例中，采用标准的&lt;strong&gt;3x5&lt;/strong&gt;的格子阵列来表示一个数字，其中前景为黑色的格子，背景为白色的格子，倘若用1代表黑色格子，0代表白色格子，则数字&lt;strong&gt;4&lt;/strong&gt;将有如下表现：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;1 0 1&lt;br /&gt;
1 0 1&lt;br /&gt;
1 1 1&lt;br /&gt;
0 0 1&lt;br /&gt;
0 0 1  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这就是一个数字在平面上的模型，换言之，只要立方体面对用户的这一面的格子颜色分配如上，用户就会看到一个&lt;strong&gt;4&lt;/strong&gt;。&lt;/p&gt;
&lt;h3&gt;在同一个立方体渲染多个数字&lt;/h3&gt;
&lt;p&gt;以上的模型可以在平面渲染0-9的任意一个数字，让我们将思想扩展到三维，会发现每个格子都是组成数字的那个&lt;strong&gt;3x5x3&lt;/strong&gt;的立方体中的每一个小cube在x-y平面上的投影。  &lt;/p&gt;
&lt;p&gt;那么如何让一个立方体能表现出多个数字呢？你的第一想法一定是：“每次更新这些格子，让它的数字变化不就可以了”。这当然是一种思路，但这种思路过于粗暴，无法实现本例中的效果，让我们仔细观察本例中数字的变换方式，它并非瞬变，而是通过不同行绕Y轴的渲染实现的。  &lt;/p&gt;
&lt;p&gt;这就产生了一个难题：我们需要合理得设置立方体中每一个cube的颜色，来使其可以通过绕Y轴的旋转来达到在x-y平面上投影出不同数字的目的。  &lt;/p&gt;
&lt;p&gt;这段话看起来拗口，其实说的问题很简单，就是说我们要在一定的约束下，构造一个立方体，这个立方体在Y轴方向上有5行，每一行都是一个有&lt;strong&gt;3x3&lt;/strong&gt;个cube构成的阵列，每一行的旋转是独立的，每次旋转都会选择其一面朝向用户（即投影到 x-y平面），所有行投影的组合就构成了一个数字。这样一来，如何构造这个立方体就成了问题的症结所在，让我们仔细思考一下有哪些约束：  &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;立方体由五行构成，每一行都是3x3的cube阵列&lt;/li&gt;
&lt;li&gt;每行通过旋转构造一个x-y平面投影，投影可以组合出任意数字&lt;/li&gt;
&lt;li&gt;每行的每个面之间有强关联，边角上的cube是重合的，投影后每一面的边角的那个格子会和相邻面耦合&lt;/li&gt;
&lt;li&gt;需要构造每个数字的格子组合固定，总共十种&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;进行分析后，不难发现这其实可以通过枚举（也没别的办法）得出每一个数字需要的格子组合，然后反推出每一行需要的投影，最后再通过这些投影反推出每一行的cube颜色构成，最后再推算需要渲染一个数字时，每一行需要以那一面出现（即旋转到什么角度）。  &lt;/p&gt;
&lt;p&gt;根据这个思想，我写了个脚本&lt;strong&gt;num_helper.py&lt;/strong&gt;来计算每一行可能出现的投影的组合，并将反算结果存在了&lt;strong&gt;NumbersLUT&lt;/strong&gt;内，不难发现，这过程中我其实最终将问题规约成了一个对九宫格的染色问题：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;0 0 1&lt;br /&gt;
0 0 0&lt;br /&gt;
1 1 1&lt;br /&gt;
（第一行的顶视图，旋转0度对应111，旋转90度对应001，旋转270度对应101）  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;如此一来，后续便可以通过每一行的转角来控制显示哪一个数字了。  &lt;/p&gt;
&lt;h3&gt;渲染点&lt;/h3&gt;
&lt;p&gt;点的渲染就十分简单了，其实就是两个一半单位深度的、不同色的立方体拼起来，加个定位，并在每次时间更新的时候旋转180度罢了，不再赘述。&lt;/p&gt;
&lt;h3&gt;动起来&lt;/h3&gt;
&lt;p&gt;即便解决了渲染问题，倘若只是在每次时间更新的时候切换一下转角，也太过生硬。为了不那么生硬，我们需要对这种转角的变换做一个动画，这本质上其实就是在一定时间内对这个变换进行插值。这里我引入了通用的解决方案&lt;strong&gt;Tween&lt;/strong&gt;来进行插值，使用起来只要设定好目标角度和duration即可：  &lt;/p&gt;
&lt;p&gt;&lt;code&gt;js
Tween.to(num.rotation, .4, {y: degree});&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;不过这里有一点需要注意，为了在时间变换时达到一个“前进”的观感，数字的每一行和点每次旋转的方向应该都是顺时针的。对于点而言这很好办，但对于数字就不同了，比如上面我们计算出的第一行，其三个角度是0、90和270，那么如果上一次的角度是270，这一次是90，直接旋转就会变成逆时针。这个解决起来也很简单，分两步实现：  &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;使转角始终保持在0~360度内，在每次变换计算前先判断&lt;code&gt;rotation.y&lt;/code&gt;的值，若大于360则减去360&lt;/li&gt;
&lt;li&gt;判断当前角度和上一次角度的大小，若大于，则走正常逻辑，若小于，则给当前的角度加上360度&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如此便可以保证顺时针的旋转。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Sunday, 22 Apr 2018 18:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2018.04.22 18:00:article/Skill-2018_04_22_a</guid>
<category>HTML5</category>
<category>WebGL</category>
<category>3D</category>
<category>ThreeJS</category>
<category>Clock</category>
</item>

<item>
<title>编译TypeScript、C++和Rust到WebAssembly</title>
<link>http://dtysky.moe/article/Skill-2017_12_31_a</link>
<description>&lt;p&gt;心得和例子已被整合到工程&lt;a href=&amp;quot;https://github.com/dtysky/wasm-usage-guide&amp;quot;&gt;wasm-usage-guide&lt;/a&gt;中。  &lt;/p&gt;
&lt;p&gt;当下，wasm已被大多浏览器实现，其设计很巧妙，理论上任何可以编译成LLVM字节码的语言都可以被编译到wasm（借助llc和binary），在这个项目中，我对几种编译方式进行了实践并总结出了一些心得：  &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;TypeScript: 现在有一个叫&lt;strong&gt;AssemblyScript&lt;/strong&gt;的项目在做，借助Binaryen，已经可以跑通一些例子了。&lt;/li&gt;
&lt;li&gt;C++ without Emscripten：直接用clang的前端编译到LLVM的bc，然后llc编译到汇编文件s，再用Binaryen的工具s2wasm从汇编文件编译到wasm的ast文件wast，最后用wasm-as编译到wasm。&lt;/li&gt;
&lt;li&gt;Rust without Emscripten：用rustc通过编译选项编译到LLVM的bc，剩下的和CPP一样。&lt;/li&gt;
&lt;li&gt;C++ with Emscripten： 用Emscripten的emcc进行编译，附送一个runtime，实现了很多实用的方法，挺有用的，推荐使用。&lt;/li&gt;
&lt;li&gt;Rust with Emscripten：rustc的nightly版本现在支持一个target&lt;strong&gt;wasm32-unknown-emscripten&lt;/strong&gt;，可以借助emscripten直接编译出一个runtime的js和wasm，但似乎emcc自身的arguments传不过去...看着用吧。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;有兴趣的话，可以直接clone下来研究，写的都很全了。  &lt;/p&gt;
&lt;p&gt;如有疏忽请指出。  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Sunday, 31 Dec 2017 18:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2017.12.31 18:00:article/Skill-2017_12_31_a</guid>
<category>WebAssembly</category>
<category>C++</category>
<category>Rust</category>
<category>TypeScript</category>
</item>

<item>
<title>Bilibili圣诞游戏剖析-用pixi.js实现鬼畜音游</title>
<link>http://dtysky.moe/article/Skill-2017_12_25_a</link>
<description>&lt;p&gt;在正式看这篇文章之前，希望大家先去此处体验一下游戏，这样才能更好地理解我的此次分享：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;Jingle Beats&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2017_12_25a/0.png&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;继七夕活动之后，我又负责了圣诞活动&lt;a href=&amp;quot;https://www.bilibili.com/blackboard/jingel-beats.html&amp;quot;&gt;Jingel Beats&lt;/a&gt;的开发，这次放弃了白鹭引擎，使用了侵入性比较低的&lt;code&gt;pixi.js&lt;/code&gt;。下面我就此次活动的一些心得。  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;不过在了解到有&lt;strong&gt;Phaser&lt;/strong&gt;之后，其实本游戏应该用它来做，比起&lt;strong&gt;PIXI&lt;/strong&gt;这个渲染引擎，它才是一个实际的游戏引擎，自带音频、物理引擎、摄像机、输入归一化、全局画布管理、内置状态管理等等设计也更符合游戏的需求，希望大家可以尝试一下。不过对于一般的轻量级游戏，还是可以直接用PIXI，毕竟Phaser的mini版本也有1.7m了...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;前言&lt;/h2&gt;
&lt;p&gt;在活动开发方面，我一直致力于形式的探索，而由于国内审美环境特别，现在国际主流的设计无法很好使用（这个也和跳过了桌面端而直接到移动端有关），所以需要另辟蹊径。不难想到，交互形式的极致其实就是游戏，而游戏同时也是受众最为广泛的一种形式，加之本人本身就对游戏有着极高的兴趣和爱好，所以这次在这个方向又做了一个尝试。  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;当然，如果大家谁有有趣的、纯公益或者纯艺术性与新媒体结合的路子，可以找我，视情况我可以提供无偿的技术支持，不仅仅是web前端，硬件我也可以视情况奉陪。&lt;/strong&gt;  &lt;/p&gt;
&lt;p&gt;这次的游戏形式是MUG，即音乐节奏类游戏，这类游戏的代表有&lt;a href=&amp;quot;https://zh.wikipedia.org/wiki/%E5%88%9D%E9%9F%B3%E6%9C%AA%E6%9D%A5_-%E6%AD%8C%E5%A7%AC%E8%AE%A1%E5%88%92-&amp;quot;&gt;Project Diva&lt;/a&gt;、&lt;a href=&amp;quot;https://zh.wikipedia.org/wiki/%E5%A4%AA%E9%BC%93%E4%B9%8B%E9%81%94%E4%BA%BA%E7%B3%BB%E5%88%97&amp;quot;&gt;太鼓达人&lt;/a&gt;、&lt;a href=&amp;quot;https://zh.wikipedia.org/wiki/DJMax&amp;quot;&gt;DJMAX&lt;/a&gt;、&lt;a href=&amp;quot;https://osu.ppy.sh/&amp;quot;&gt;OSU&lt;/a&gt;等。其形式各有所长，输入方式也千门百类，且每种都有大量fans（当然，真fan无论哪种都很喜欢）。而由于是此游戏主打移动端，并且是运行在浏览器内，所以考虑到输入方式和一些边边角角的问题后，我初步给出了两种形式：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;玩法一&lt;/strong&gt;：太鼓达人式的游戏模式，其核心在于一个和音频同步、不断向一侧移动的元素序列，由于整体是一个长条，所以绘制较为简单，但玩家控制并非直接作用于元素之上，而是利用两个按钮进行输入，所以逻辑有些复杂。在正常模式之后，还会有一个随意点击屏幕可视区域的奖励模式。这种玩家操作都规约到两个按钮的游戏模式比较亲民，而且操作上也不会有和浏览器以及设备本身有过多的冲突，中规中矩，比较保险。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;玩法二&lt;/strong&gt;：有些像PSV上的那一作DJMAX，或者是触控模式的OSU。元素不像太鼓达人在一个长条内，而是多出一个空间的属性，铺满了整个屏幕。随后，我将给每个元素赋予不同的类型，每种类型代表一个手势，当元素出现时，玩家需要去定位元素，之后遵守元素自身的手势去拖动它，完成判定，最终也有个奖励模式，就是模仿地雷社的&lt;strong&gt;限界凸骑&lt;/strong&gt;中的撸动屏幕，带来整个游戏的高潮——这个方案充分挖掘了触屏的优势，可玩性也很高，不过也有其自身的问题。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;玩法二的问题在于当玩家投入游玩后，可能会手势越界，唤醒浏览器自身的手势行为，亦或是唤醒设备本身的通知栏等。而MUG本身是一种不能被中断的游戏，所以为了保险，最终还是选择了玩法一——这也是在浏览器中做游戏的限制。不过，忙过这阵子我将尝试实现这种模式（作为个人项目）。&lt;/p&gt;
&lt;h2&gt;架构&lt;/h2&gt;
&lt;p&gt;一个MUG，或者说几乎任何一个游戏，都可以被拆解为&lt;strong&gt;逻辑&lt;/strong&gt;和&lt;strong&gt;展示&lt;/strong&gt;这两个部分，而逻辑部分和试图部分的分离也是复杂度得以控制的根源。拿此游戏为例，运行时的状态和判定等，并非是由元素的位置决定，而是由当前的时间决定的，而这个时间也同时控制着元素位置和状态的绘制——这样一种状态视图解耦、状态驱动视图、单向数据流的模型，和现代前端开发的MVC等视图框架没有太大区别。所以接下来，我将从&lt;strong&gt;控制层&lt;/strong&gt;和&lt;strong&gt;表示层&lt;/strong&gt;这两层来描述。&lt;/p&gt;
&lt;h3&gt;玩法核心&lt;/h3&gt;
&lt;p&gt;控制层的核心便是围绕玩法的逻辑，本游戏逻辑相对单纯却也不失丰富：  &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;元素的类型，有&lt;strong&gt;左按&lt;/strong&gt;、&lt;strong&gt;右按&lt;/strong&gt;、&lt;strong&gt;一起按&lt;/strong&gt;三种。&lt;/li&gt;
&lt;li&gt;在操作维度上，有&lt;strong&gt;点击&lt;/strong&gt;和&lt;strong&gt;长按&lt;/strong&gt;两种。&lt;/li&gt;
&lt;li&gt;判定方面，有&lt;strong&gt;miss&lt;/strong&gt;、&lt;strong&gt;good&lt;/strong&gt;和&lt;strong&gt;perfect&lt;/strong&gt;三种。&lt;/li&gt;
&lt;li&gt;游戏流程，则有&lt;strong&gt;普通模式&lt;/strong&gt;、&lt;strong&gt;奖励模式&lt;/strong&gt;，还有基于&lt;strong&gt;能量&lt;/strong&gt;的&lt;strong&gt;fever机制&lt;/strong&gt;和&lt;strong&gt;失败判定&lt;/strong&gt;，以及&lt;strong&gt;得分&lt;/strong&gt;、&lt;strong&gt;Combo&lt;/strong&gt;和&lt;strong&gt;判定类型&lt;/strong&gt;的统计。&lt;/li&gt;
&lt;li&gt;难度方面，我们提供了&lt;strong&gt;easy&lt;/strong&gt;、&lt;strong&gt;normal&lt;/strong&gt;和&lt;strong&gt;hard&lt;/strong&gt;三种模式。&lt;/li&gt;
&lt;li&gt;而在数值设计上，参照魔界战记大额伤害带来的爽快感，这里一个&lt;strong&gt;good&lt;/strong&gt;积分666、&lt;strong&gt;perfect&lt;/strong&gt;积分1000，fever状态翻倍。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;以上要素加起来，其实已然不仅仅是一个HTML5小游戏，而可以说是一个完整版音游了。加之后面会说的可以灵活切换的谱面设计，套个壳再多加几首歌都可以上架了（逃。&lt;/p&gt;
&lt;h3&gt;图层管理&lt;/h3&gt;
&lt;p&gt;作为一个MUG，玩家的注意力应当是高度集中在玩法本身上的，所以应当给节奏条和控制器很高的优先度，而其它元素则是以客场的身份——它们只是作为背景，并且不宜过度复杂，否则将会干扰视觉（这一点在歌姬计划的某些曲目有所体现），所以经过讨论，加之B站背景的限制，我们选择了&lt;a href=&amp;quot;https://www.bilibili.com/video/av1747345&amp;quot;&gt;今天妈妈不在家&lt;/a&gt;这个鬼畜的梗（bgm本身也是鬼畜版的），让玩家的操作来控制22和33的动作，并利用一些小的元素（比如烤箱喷射的食物、fever时候降下的灯）等来烘托氛围。基于以上，图层实际被分割为：  &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;background: 背景图层，用于放置在最底层元素的基础元素，比如屋子的图片、闪烁的灯等。&lt;/li&gt;
&lt;li&gt;chars: 人物图层，放置22和33.&lt;/li&gt;
&lt;li&gt;magic: 核心元素图层，用于放置节奏条和控制器。&lt;/li&gt;
&lt;li&gt;ui: UI图层，记分板、引导页和结果页放在这里。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这些图层自底而上，一层一层构成了整个游戏的视觉部分。&lt;/p&gt;
&lt;h3&gt;自适应&lt;/h3&gt;
&lt;p&gt;本游戏和七夕游戏一样，也遇到了强制横屏的问题，但pixi并没有提供官方的强制竖屏和自适应方案。一开始我按照egret的方案去做，但遇到了一些问题，而解决方案我确实也找到了，如有需要，请参照我写的这篇文章：&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/31619385&amp;quot;&gt;在pixi.js实现设备自适应和强制竖屏&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;实现&lt;/h2&gt;
&lt;p&gt;架构说完了，让我们进入细节来看看具体的实现吧。&lt;/p&gt;
&lt;h3&gt;谱面设计&lt;/h3&gt;
&lt;p&gt;将可配置的尽量提出来变成可配置的，是降低工程复杂度的最佳方式之一，无论这个配置看起来和程序主题耦合的多么深——在此游戏中，这个最大的配置就是谱面本身。如果我只是为了一个谱面来单纯写一个游戏，那它只是一段是毫无价值的堆砌式代码，而倘若我将谱面作为一个配置项来服务于程序主体，那我就做了了一个引擎似的东西——它高度复用，以后有新的需求或者需求的修改，只需要修改配置即可。做好架子，将核心逻辑和形式内容分离，当产品或内容想修改的时候将只需要将配置拿来，这是最合理的分工。  &lt;/p&gt;
&lt;p&gt;好，废话不多说，先来看一看下面的类型定义：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;TRhythm&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;level&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;bps&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;direction&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;TDirection&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;award&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;start&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;end&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 0 ~ 1&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;fever&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;duration&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;sequence&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;TRhythmElement&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[]&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;code&gt;TRhythm&lt;/code&gt;即为谱面的数据结构，它最核心的几个要素是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;bps: 一秒的拍数，用于控制谱面整体的速度。&lt;/li&gt;
&lt;li&gt;award: 奖励模式的起始和结束拍数定义。&lt;/li&gt;
&lt;li&gt;fever: fever阈值定义，其为一个比例，这个比例代表的是fever所需能量占据游戏可获取的所有能量的比例。&lt;/li&gt;
&lt;li&gt;duration: 谱面长度，以拍数记。&lt;/li&gt;
&lt;li&gt;sequence：核心，谱面序列，这个定义其实是解析后的元素序列，解析前则是一个字符串，解析过程将会在下一节讲到。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这些属性加起来就定义了一个谱面，若有需求变更，只需要修改这个谱面即可。&lt;/p&gt;
&lt;h3&gt;谱面解析&lt;/h3&gt;
&lt;p&gt;有了原始的谱面定义，程序便可以将其解析，转换为运行时实际的谱面。而这个解析的核心，实际上是对&lt;code&gt;sequence&lt;/code&gt;这个序列的转换。让我们来看看一段原始的、字符串描述的谱面和转换后的&lt;code&gt;TRhythmElement[]&lt;/code&gt;序列是怎样的：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;TRhythmElement&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;TElement&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// int, beats from last element&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;start&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 1 is short, others are long-touch element&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;beats&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;TBeats&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;

&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;squence&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;010|121|233&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;c1&amp;quot;&gt;// bps = 1&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;convertedSequence&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;beats&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;start&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;beats&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;start&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;beats&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;start&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;可以看到，原始的谱面实际上是由一段由&lt;code&gt;|&lt;/code&gt;分割的字符串描述的，其定义为&lt;code&gt;${type}${beats}${start}|...&lt;/code&gt;，它们正好对应&lt;code&gt;TRhythmElement&lt;/code&gt;中的三个属性，分别表示节奏元素的&lt;strong&gt;类型&lt;/strong&gt;、&lt;strong&gt;拍数&lt;/strong&gt;和&lt;strong&gt;距离上一个元素结束时的拍数&lt;/strong&gt;，在本游戏这种类型的MUG中，这三个属性足以描述一个元素。  &lt;/p&gt;
&lt;p&gt;从标准化的字符串向&lt;code&gt;TRhythmElement[]&lt;/code&gt;的转换只是第一步，在拿到了转换后的&lt;code&gt;TRhythm&lt;/code&gt;后，对谱面的解析才真正开始。第二步解析的目的是——将拍数描述的谱面转换为时间描述，并同时生成每个元素对应的视觉容器，之后将它们放入顶层容器中。视觉方面的实现下一节再讲，这里先说一下逻辑上的实现，来看看下面的这个新类型：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;TRhythmPlayElement&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;TElement&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;start&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;end&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;beats&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;TBeats&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;element&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;PIXI.Container&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这是经过解析后元素的最终形态，和&lt;code&gt;TRhythmElement&lt;/code&gt;不同，这里的&lt;code&gt;start&lt;/code&gt;和新多出的&lt;code&gt;end&lt;/code&gt;的单位都是&lt;code&gt;ms&lt;/code&gt;，并且它们的值都不再是相对于上一个元素的偏移，而是绝对时间，这是游戏运行时进行判定操作所必要的。与此同时，这里还多出了一个&lt;code&gt;element&lt;/code&gt;属性，它用于保存元素在视图层容器的引用。&lt;/p&gt;
&lt;h3&gt;元素序列条&lt;/h3&gt;
&lt;p&gt;谱面被解析后，生成了被各种属性描述的元素，被保存在一个序列中。这个序列除了逻辑，还保存着其视图容器的引用。而这个视图容器其实就是游戏过程中的那一个个圆形或者长条的元素，这些容器的也是伴随着谱面解析同步生成的。下面我就来说说这些容器生成的细节。  &lt;/p&gt;
&lt;p&gt;节奏元素以及其外部容器分为四个部分，其一是最底层的那个半透明轨道，其二是圆形的判定框，第三是那一个个元素构成的序列条，第四就是元素被命中时判定框环测的闪光和星星。在游戏过程中，元素在轨道上不断从右向左运动，到了判定框后，随着玩家的操作或者自发发起操作来引起游戏判定，判定后元素消失，如果是长按型的元素（长条状），则要保证该元素不会穿过判定框，即产生一个&lt;strong&gt;进洞&lt;/strong&gt;的效果。这三部分中，半透明轨道直接使用了&lt;code&gt;PIXI.Graphics&lt;/code&gt;来绘制：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rail&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;beginFill&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;config&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;railColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;config&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;railAlpha&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;rail&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;drawRect&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;config&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rhythmRailHeight&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;rail&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;endFill&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;而判定框，则是一个&lt;code&gt;PIXI.Sprite&lt;/code&gt;，它的宽高是和轨道相关的，比轨道的高略小。这里要注意的是判定框并非真的主导了“判定”操作，“判定”操作实际上是和核心逻辑、也就是每个元素自身存储的&lt;code&gt;start&lt;/code&gt;和&lt;code&gt;end&lt;/code&gt;决定的，这里的判定框不过是给了玩家一个视觉上的预期，让他们有了操作的依据。  &lt;/p&gt;
&lt;p&gt;在这四个部分当中，元素序列条最为重要。在游戏过程中，从玩家角度来看，元素似乎是一个一个从屏幕右侧生成的，但实际上，实时生成元素对于性能和复杂度而言都是不太可取的。我这里采用的策略是预生成，也就是说，我先生成了一个名为&lt;code&gt;elements&lt;/code&gt;的容器，之后在谱面解析时、对一个一个节奏点的循环中去生成元素&lt;code&gt;element&lt;/code&gt;，并按照它们的拍数去计算相对位置，然后将它们放入&lt;code&gt;elements&lt;/code&gt;中：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;element&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Element&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;textures&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;`&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;elements&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;`&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;beats&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;diam&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;diffBeats&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;start&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;preBeats&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;center&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;diffBeats&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;diam&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;element&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;center&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;radius&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;preBeats&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;beats&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;code&gt;Element&lt;/code&gt;是我自己定义的一个类，其通过类型、拍数和直径绘制出一个元素，绘制原理本质上也很简单，如果&lt;code&gt;beats&lt;/code&gt;为1，即一拍的元素，则直接当做一个&lt;code&gt;Sprite&lt;/code&gt;处理，如果是多拍的元素，则套上一个容器，在内部利用&lt;code&gt;Graphics&lt;/code&gt;画好白色的外框背景，然后再把一个&lt;code&gt;Sprite&lt;/code&gt;放到背景上即可。绘制好了之后便要调整她的位置，由于只有一个维度的运动，所以只需要调整&lt;code&gt;x&lt;/code&gt;的值。我是用了一个&lt;code&gt;center&lt;/code&gt;变量来存储每个元素中心的位置，利用中心同步而非边缘有利于降低心智负担，其中&lt;code&gt;radius&lt;/code&gt;和&lt;code&gt;diam&lt;/code&gt;是元素的半径，这个是可以调整的，&lt;code&gt;preBeats&lt;/code&gt;是上一个元素的拍数，&lt;code&gt;start&lt;/code&gt;是此元素距离上一个元素结束时的拍数，通过这样不断的循环计算，我们便可以得到一个&lt;code&gt;element&lt;/code&gt;的序列，在实际运行时，运动的实际上是外层的容器，而不是元素自身，这样也减少了计算和更新的逻辑。  &lt;/p&gt;
&lt;p&gt;至此，元素序列条本身的绘制已然完成，但如果仅仅如此，当这个序列条的容器运动时，元素还是会越过判定框，没有符合&lt;strong&gt;进洞&lt;/strong&gt;这个效果的预期。为了实现这一点，我是用了&lt;code&gt;mask&lt;/code&gt;这个属性：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;elementsContainer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;addChild&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;elementsMask&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;elementsContainer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;addChild&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;elements&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;elementsContainer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;mask&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;elementsMask&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;创建一个更顶层的容器&lt;code&gt;elementsContainer&lt;/code&gt;，将&lt;code&gt;elements&lt;/code&gt;作为child加入其中，之后对其设置一个&lt;code&gt;mask&lt;/code&gt;。容器的位置固定，和轨道的宽高一致，而&lt;code&gt;mask&lt;/code&gt;本身是一个&lt;code&gt;Graphics&lt;/code&gt;，其形状为一个半圆和一个长方形的拼接，直接将判定框以及其右侧的轨道纳入其中。如此一来，&lt;code&gt;elements&lt;/code&gt;这个序列条超出判定框左侧的内容将不会被展示，这样就达到了一个“进洞”的效果。  &lt;/p&gt;
&lt;p&gt;以上三部分完成后，便可以考虑第四部分的实现。第四部分本质上是一个光罩和其上的星星，光罩没什么特别的，就是一个&lt;code&gt;Sprite&lt;/code&gt;，而星星则是一个序列帧动画，这个可以用&lt;code&gt;PIXI.extras.AnimatedSprite&lt;/code&gt;实现。这二者的绘制和定位非常简单，唯一的麻烦在于如何让它们配合每一次的判定结果进行显示——即玩家命中后会有发光和星星散出的效果。这里有两种方案备选：其一，在命中时再去设置光罩和星星的&lt;code&gt;visiable&lt;/code&gt;属性，并开始播放星星的动画，这种方式要频繁触发&lt;strong&gt;播放&lt;/strong&gt;动作，需要进行变动的地方也较多；其二，将这两部分放入一个容器中，初始化完毕后不变动它们，而是变动容器的&lt;code&gt;visiable&lt;/code&gt;属性，这意味着星星始终在闪，只不过由外层的容器控制它是否可见，这里也有一个潜在的问题——加入在切换容器显示状态时，动画只播了一半怎么办？所以这种控制实际上虽然简单，但不精确。我最终选择了第二种方案，虽然有风险，但考虑星星播放速度较快，并且切换也足够迅速，人眼几乎无法觉察，所以这个障眼法可行。&lt;/p&gt;
&lt;h3&gt;动起来&lt;/h3&gt;
&lt;p&gt;准备工作做完，便要考虑如何让序列条动起来了。上面讲到过，序列中元素的运动其实就是其外层容器的运动，所以我只需要在游戏过程中不断根据时间修改容器的&lt;code&gt;x&lt;/code&gt;即可，而时间，实际上是由一个计时器&lt;code&gt;ticker&lt;/code&gt;驱动的：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ticker&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;PIXI&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ticker&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Ticker&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ticker&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;add&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;update&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ticker&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;start&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这段代码中，我创建了一个&lt;code&gt;ticker&lt;/code&gt;，而后在其中注册了一个回调&lt;code&gt;update&lt;/code&gt;，最后启动它，开始计时。之后在计时器的每一次更新时，它都回去调用&lt;code&gt;update&lt;/code&gt;，我便在其中更新容器的&lt;code&gt;x&lt;/code&gt;：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;preTime&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;currentTime&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;diffTime&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ticker&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;elapsedMS&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;currentTime&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;diffTime&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;diff&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;pixelsPerMs&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;diffTime&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;elements&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;diff&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;其中&lt;code&gt;this.ticker.elapsedMS&lt;/code&gt;为距离上一次回调经过的时间，而&lt;code&gt;this.pixelsPerMs&lt;/code&gt;是一个定量，表示容器每毫秒需要移动的像素值，它是在元素序列被初始化完毕时计算的。如此便可以完成容器位置的更新，不过需要注意的是当浏览器被切出时，&lt;code&gt;ticker&lt;/code&gt;的计时将会&lt;strong&gt;休眠&lt;/strong&gt;，在这个过程中，虽然其计时没有停止，但将不会触发回调。当再次切回页面时，回调会再次被触发，但此时的&lt;code&gt;this.ticker.elapsedMS&lt;/code&gt;将会是一个很大的值，这对于本MUG是无法接受的，所以我这里用了一个相对粗暴的手段——直接弹出提示让玩家重新开始。这也是无奈之举，如果有更好的方案请务必告诉我。&lt;/p&gt;
&lt;h3&gt;玩家输入&lt;/h3&gt;
&lt;p&gt;玩家输入指的便是开始说过的那六种操作——左按钮、右按钮、一起按，以及这三个的长按模式。将这六种操作映射到玩家这边的两个按钮、两对事件（分别的&lt;code&gt;touchstart&lt;/code&gt;和&lt;code&gt;touchend&lt;/code&gt;）上，并不是一件特别简单的事情，会涉及到一个稍显复杂的状态机。  &lt;/p&gt;
&lt;p&gt;首先我们定义&lt;strong&gt;原始输入&lt;/strong&gt;，&lt;strong&gt;原始输入&lt;/strong&gt;指的通过监听玩家对两个按钮的&lt;code&gt;touchstart&lt;/code&gt;和&lt;code&gt;touchend&lt;/code&gt;事件得到的输入。在这一步，程序将会对玩家的输入进行预处理，将其处理为&lt;strong&gt;左按开始&lt;/strong&gt;、&lt;strong&gt;左按结束&lt;/strong&gt;、&lt;strong&gt;右按开始&lt;/strong&gt;、&lt;strong&gt;右按结束&lt;/strong&gt;、&lt;strong&gt;同时按开始&lt;/strong&gt;、&lt;strong&gt;同时按结束&lt;/strong&gt;，来为下一步处理做准备。其具体的实现见状态机：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;User Input&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2017_12_25a/1.svg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;当接收到玩家对某个按钮的输入后，先缓存输入状态，将其保存为一个类的属性中，之后合理利用&lt;code&gt;setTimeout&lt;/code&gt;函数，在delta时间(这个时间自行调整，此处为30ms，基本是两个主循环周期)后做一次延迟判断，如果在这段时间内玩家没有另一个按钮的输入、还保持着这个操作的状态，则直接触发其对应按钮的全局事件（这个全局事件是本游戏状态同步机制的基础，下面会细说）。但如果在delta时间内，用户又触发了另一个按钮的操作，则在另一个按钮的事件回调中先获取当前缓存的输入状态，此时可以检测到在短短时间内，玩家前后对两个按钮触发了&lt;code&gt;touchstart&lt;/code&gt;或者&lt;code&gt;touchend&lt;/code&gt;事件，这在宏观上可以等价为用户同时触发了两个按钮的相同事件，那么此时我就可以将两个事件合并成一个事件，触发全局的&lt;strong&gt;同时touchstart&lt;/strong&gt;或者&lt;strong&gt;touchend&lt;/strong&gt;事件。&lt;/p&gt;
&lt;p&gt;这一段可能有点晕，但原理其实很简单。比如，玩家按了左按钮，并且delta内没有其他操作，则触发&lt;strong&gt;左按钮的touchstart&lt;/strong&gt;的全局事件；如果delta内玩家又按了右按钮，则触发&lt;strong&gt;一起按的touchstart&lt;/strong&gt;事件。  &lt;/p&gt;
&lt;p&gt;合并了玩家的输入后，便可以进入下一步的判定，从而真正实现输入和六种操作的映射，这将会在下一节说到。&lt;/p&gt;
&lt;h3&gt;背景和前景人物动画&lt;/h3&gt;
&lt;p&gt;在具体的判定逻辑分析之前，让我们来点轻松的——画面中随着玩家输入而动的22、33是如何实现的呢？其实并不复杂，合理运用序列帧动画&lt;code&gt;AnimatedSprite&lt;/code&gt;和补间动画库&lt;code&gt;Tween.js&lt;/code&gt;便可。  &lt;/p&gt;
&lt;p&gt;22和33分别都是一个&lt;code&gt;AnimatedSprite&lt;/code&gt;，只需要按照其规范初始化和定位即可，但其中33需要特别注意，细心的玩家应该注意到了她所持烤箱的盖子其实是和她一体化的——这是为了在后续食物喷射出的时候，让食物能处于烤箱和盖子之间。所以33和背景上烤箱的定位其实有一定的耦合关系。  &lt;/p&gt;
&lt;p&gt;初始化只是第一步，要让22和33能响应玩家的输入做出动作，就要实现一个跟随玩家输入播放/停止动画的逻辑。我实现了&lt;code&gt;act22&lt;/code&gt;和&lt;code&gt;act33&lt;/code&gt;方法来完成这个逻辑，以22的为例：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;act22&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;mode&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;start&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;end&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;mode&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;start&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;playing&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;two&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;loop&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;two&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;two&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;play&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;playing&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;two&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;loop&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;two&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;false&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这里的&lt;code&gt;mode&lt;/code&gt;参数对应前面合成时间中的&lt;code&gt;touchstart&lt;/code&gt;和&lt;code&gt;touchend&lt;/code&gt;(以及&lt;code&gt;touchendoutside&lt;/code&gt;)，22对应左按钮，33对应右按钮，一起按的操作则两个都对应。可以看到，这里本质上是利用&lt;code&gt;AnimatedSprite&lt;/code&gt;的&lt;code&gt;play&lt;/code&gt;方法来控制播放的，如果输入只有短按一种，那么每次在&lt;code&gt;touchstart&lt;/code&gt;的时候直接调用&lt;code&gt;play&lt;/code&gt;方法即可，但输入不止短按，还有长按，而长按时我的设计是让动画不断loop，当&lt;code&gt;touchend&lt;/code&gt;的时候取消loop即可。一个显而易见的方法是利用&lt;code&gt;AnimatedSprite&lt;/code&gt;自身的&lt;code&gt;loop&lt;/code&gt;属性和其&lt;code&gt;stop&lt;/code&gt;方法、&lt;code&gt;onComplete&lt;/code&gt;回调方法进行控制，然而这种方式在当前版本的PIXI下会出现难以理解的行为（我认为这是一个bug），所以必须用替代方案。我这里是利用一个自己保存的&lt;code&gt;this.loop&lt;/code&gt;属性结合&lt;code&gt;AnimatedSprite&lt;/code&gt;的&lt;code&gt;onLoop&lt;/code&gt;回调方法，来实现一个自定义的&lt;code&gt;loop&lt;/code&gt;方案，并使用&lt;code&gt;this.playing&lt;/code&gt;方法来防治动画播放过程中用户的意外输入：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;two&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;loop&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;two&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;onLoop&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;loop&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;two&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;two&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;stop&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;playing&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;two&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;false&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;如此一来，便实现了22和33响应用户输入的功能。除此之外，还有一个在fever状态下、33拉烤箱门时同时喷射食物的效果，食物总共有五种，需求是随机选取其中的一个喷射而出，喷射位置也最好有所不同。我的做法是建立一个用于容纳食物的容器，当进入fever状态后，在触发33动画的同时调用一个&lt;code&gt;actFood&lt;/code&gt;方法，在这个方法中，我先随机取出五种食物中的一种texture，之后生成其对应的&lt;code&gt;Sprite&lt;/code&gt;，，为其指定初始位置和大小，将其添加到食物的容器中。之后用&lt;code&gt;Tween.js&lt;/code&gt;生成一个&lt;code&gt;Tween&lt;/code&gt;对象，为其添加变换位置（&lt;code&gt;x&lt;/code&gt;和&lt;code&gt;y&lt;/code&gt;，从预定义目标位置集合中随机选取）和大小（&lt;code&gt;scale&lt;/code&gt;）的动画，然后在&lt;code&gt;onComplete&lt;/code&gt;回调中将其从容器中移除即可：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;tw&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;TWEEN&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Tween&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;food.x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;food.y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;scale&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;food.scale.x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;})&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;to&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;end.x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;end.y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;scale&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;food.scale.x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;500&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;onUpdate&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(({&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;scale&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;})&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;food&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;food&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;food&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;scale&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;scale&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;food&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;scale&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;scale&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;})&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;onComplete&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(()&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;foods&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;removeChild&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;food&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;})&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;start&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;操作判定&lt;/h3&gt;
&lt;p&gt;好，让我们回到用户输入到操作判定的逻辑。在前面，程序已经完成了对用户输入的合并，那么接下来这一步就是对已经合并的输入进一步处理了。在这一步，用户输入将会被转为&lt;code&gt;miss&lt;/code&gt;、&lt;code&gt;good&lt;/code&gt;或者&lt;code&gt;perfect&lt;/code&gt;的判定，而这个判定对于短按和长按元素的逻辑又不相同，我们来一个一个分析：  &lt;/p&gt;
&lt;h4&gt;判定流程&lt;/h4&gt;
&lt;p&gt;首先理清整个判定的流程，如下图：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;Judge flow&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2017_12_25a/2.svg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;元素序列条随着时间的推进不断更新位置，而在更新的同时，玩家或者程序自发（这个后面会说到）触发输入事件，去诱发判定逻辑，在判定结束后视情况更新玩家本次游戏状态，然后将当次判定过的元素从队列中移除......如此如此，进入一次又一次的判定循环。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. 取出要判定元素&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;判定的第一步是要得知接下来要判定的是短按还是长按，由于已然判定过的元素会被从队列中移除，所以当下从队列中取出的第一个元素一定是接下来要判定的新元素，而这个新元素中有它自身的所有属性，其中一个就是&lt;code&gt;beats&lt;/code&gt;属性——它决定着当前元素的长度，若为1，则按照短按判定，否则按照长按判定。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. 对操作进行判定&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;若按照短按判定，那么很简单，首先，程序将比较当前输入事件的&lt;strong&gt;操作&lt;/strong&gt;是否和待判定元素的&lt;code&gt;type&lt;/code&gt;一致（比如元素&lt;code&gt;type&lt;/code&gt;为&lt;code&gt;0&lt;/code&gt;对应左按，那它对应的输入类型就是左按钮的&lt;code&gt;touchstart&lt;/code&gt;事件，在本游戏中我设定为&lt;code&gt;MAGIC.FIRE_START&lt;/code&gt;），之后在将当前游戏时间（即前文所示的&lt;code&gt;this.currentTime&lt;/code&gt;）和元素的&lt;code&gt;start&lt;/code&gt;和&lt;code&gt;end&lt;/code&gt;属性进行比对，在距离中心&lt;code&gt;±1/4&lt;/code&gt;的时间差内时，判定为&lt;code&gt;perfect&lt;/code&gt;，在&lt;code&gt;±1/2&lt;/code&gt;的时间差内时，判定为&lt;code&gt;good&lt;/code&gt;。如果时间差在范围内但类型不对，则判定为&lt;code&gt;miss&lt;/code&gt;。  &lt;/p&gt;
&lt;p&gt;长按判定比起短按要复杂许多。程序维护一个状态&lt;code&gt;actionPre&lt;/code&gt;，这个状态用于存储上一个输入，而输入分别为三种操作的&lt;code&gt;start&lt;/code&gt;和&lt;code&gt;end&lt;/code&gt;。当接收到&lt;code&gt;start&lt;/code&gt;输入时，先判定其是否和当前元素的&lt;code&gt;type&lt;/code&gt;一致，如果一致则直接将状态存储下来，否则忽略。而当接收到&lt;code&gt;end&lt;/code&gt;输入时，首先也判定是否和当前元素的&lt;code&gt;type&lt;/code&gt;一致，倘若是则进入下一轮判断，否则忽略。下一轮判断对比的是当前输入的&lt;code&gt;type&lt;/code&gt;和&lt;code&gt;actionPre&lt;/code&gt;中存储的&lt;code&gt;type&lt;/code&gt;是否一致，如果是，则表明它们是同一类型操作的开始和结束，这也正好描述了一个“长按”的过程，如果不是，则说明此次长按失败，判定为&lt;code&gt;miss&lt;/code&gt;。  &lt;/p&gt;
&lt;p&gt;长按的&lt;code&gt;good&lt;/code&gt;和&lt;code&gt;perfect&lt;/code&gt;是&lt;code&gt;start&lt;/code&gt;和&lt;code&gt;end&lt;/code&gt;两次输入的结合，在&lt;code&gt;start&lt;/code&gt;和&lt;code&gt;end&lt;/code&gt;输入判定成功时，程序都会按照短按的逻辑对其进行一次状态的归类，并且当&lt;code&gt;start&lt;/code&gt;时，会有一个属性&lt;code&gt;statPre&lt;/code&gt;来保存其状态，当&lt;code&gt;end&lt;/code&gt;判定为成功后，便会结合&lt;code&gt;statPre&lt;/code&gt;和当前的状态综合计算出这次的状态，两次都是&lt;code&gt;perfect&lt;/code&gt;则最终判定为&lt;code&gt;perfect&lt;/code&gt;，否则为&lt;code&gt;good&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. 自发的输入&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;光依靠用户输入并不能构成一个完备的有限状态机，还需要考虑用户不输入这种异常的状况。为此，我加入了一个保留输入类型&lt;code&gt;None&lt;/code&gt;，这种类型的输入事件将在满足一定条件时、在&lt;code&gt;ticker&lt;/code&gt;触发的&lt;code&gt;update&lt;/code&gt;回调中被触发。而这个条件，就是&lt;strong&gt;当当前时间大于当前元素的&lt;code&gt;end&lt;/code&gt;时&lt;/strong&gt;。此时可以认为为用户错过了一个节奏元素，那么接下来可以直接跳过所有判定逻辑、直接判定为&lt;code&gt;miss&lt;/code&gt;即可。&lt;/p&gt;
&lt;h3&gt;状态同步&lt;/h3&gt;
&lt;p&gt;在上面的分析中，不止一次说到了&lt;strong&gt;自定义全局事件&lt;/strong&gt;这个关键词，这其实是本游戏实现&lt;strong&gt;状态同步&lt;/strong&gt;的机制。而&lt;strong&gt;状态同步&lt;/strong&gt;，其实是&lt;strong&gt;全局跨组件状态同步&lt;/strong&gt;，它的目的是解决不同组件间的通信，比如上面说到的“用户控制器中用户的输入事件”和“节奏元素条的判定”问题，在游戏逻辑中实际代码如下：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// main.ts&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;eventManger&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;on&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;EVENTS&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;HERO_ACTION&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;magic&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;})&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;boss&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;judge&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;magic&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;

&lt;span class=&amp;quot;c1&amp;quot;&gt;//Hero.ts&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;eventManger&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;emit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;EVENTS&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;HERO_ACTION&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;magic&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;......&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;对于前端同学，这段代码的含义言简意赅——这其实就是事件机制，在设计模式上被称为&lt;strong&gt;发布订阅模型&lt;/strong&gt;、&lt;strong&gt;观察者模式&lt;/strong&gt;，也可以说是&lt;strong&gt;RXJS&lt;/strong&gt;的理念来源。当然，这种模式和&lt;strong&gt;redux&lt;/strong&gt;等的思想也是一样的，只不过它做的事情更多，而这个比较灵活，当然灵活也会带来碎片化的问题，不过这个问题在本游戏这种规模的工程上，尚可接受。  &lt;/p&gt;
&lt;p&gt;在游戏过程中，我定义了很多个类似的事件，利用事件的监听和触发维护着全局的状态同步。而为了减轻自己的心智负担，我并没有把事件监听的逻辑去中心化地分散到各个组件，而是将其集中在一个文件中管理，像这样：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;eventManger&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;on&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;EVENTS&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;HERO_ACTION&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;magic&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;})&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;boss&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;judge&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;magic&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;chars&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;act&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;magic&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;

&lt;span class=&amp;quot;nx&amp;quot;&gt;eventManger&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;on&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;EVENTS&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;BOSS_ACTION&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;stat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;beats&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;})&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;scoreboard&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;judge&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;stat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;beats&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;boss&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;feverPower&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;

&lt;span class=&amp;quot;nx&amp;quot;&gt;eventManger&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;on&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;EVENTS&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;CALCULATE&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;summary&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;TSummary&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;summary&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;currentCombo&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;chars&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;withFood&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;chars&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;withFood&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;

&lt;span class=&amp;quot;nx&amp;quot;&gt;eventManger&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;on&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;EVENTS&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;GAME_END&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;boss&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;stop&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;result&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;show&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;scoreboard&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;summary&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;

&lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;如此一来，整体的流程便十分清晰，一目了然。  &lt;/p&gt;
&lt;h3&gt;计分板&lt;/h3&gt;
&lt;p&gt;在上一节中，可以看到&lt;code&gt;EVENTS.BOSS_ACTION&lt;/code&gt;这个事件的订阅中，有一个&lt;code&gt;scoreboard&lt;/code&gt;对象触发了自身的&lt;code&gt;judge&lt;/code&gt;方法。这个对象就是&lt;strong&gt;计分板&lt;/strong&gt;，而这个方法就是对一次判定进行&lt;strong&gt;结算&lt;/strong&gt;的逻辑，它接受当前判定状态&lt;code&gt;stat&lt;/code&gt;（即&lt;code&gt;perfect&lt;/code&gt;、&lt;code&gt;good&lt;/code&gt;或&lt;code&gt;miss&lt;/code&gt;）和当前判定元素的拍数&lt;code&gt;beats&lt;/code&gt;，按照一开始说的规则结算生成&lt;code&gt;summary: TSummary&lt;/code&gt;，&lt;code&gt;TSummary&lt;/code&gt;的定义如下：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;TSummary&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;power&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;goals&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;maxCombo&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;currentCombo&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;miss&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;good&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;perfect&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;currentStat&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;TMagicStat&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这里面定义了一些结算需要的属性，像是玩家每个命中判定每个状态的统计、最大连击数、总得分等。结算完成后，方法内部将会触发&lt;code&gt;EVENTS.CALCULATE&lt;/code&gt;事件，将结果广播到全局。  &lt;/p&gt;
&lt;p&gt;以上是计分板在逻辑上的工作，而在视图上，它也控制了一些元素的显示，其分为左上角的&lt;strong&gt;能量球&lt;/strong&gt;、右上角的&lt;strong&gt;积分&lt;/strong&gt;和中间的&lt;strong&gt;判定状态&lt;/strong&gt;：  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. 能量球：&lt;/strong&gt;  &lt;/p&gt;
&lt;p&gt;如果你细心观察，会发现能量球其实是由背景的玻璃球，一层白色的水、一层蓝色的水和夹在其中的小电视构成的，其中水的高度和&lt;code&gt;summary.power&lt;/code&gt;绑定。其中背景和小电视都很简单，直接用&lt;code&gt;Sprite&lt;/code&gt;元素做好定位即可，水的实现则相对复杂，因为要实现一个波动的效果。一开始，我准备试试用&lt;strong&gt;置换映射&lt;/strong&gt;来做这件事，不过发现得不偿失，所以最后还是用老路子，首先利用两种颜色的、有水面波动效果的长方形&lt;code&gt;texture&lt;/code&gt;初始化两层水的&lt;code&gt;Sprite&lt;/code&gt;，之后借用&lt;code&gt;Tween&lt;/code&gt;分别对两层水实现了无限循环的、方向相反错位的旋转和位移，比如蓝色那一层的实现：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;blue1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;to&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rotation&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;wave&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;blue.x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;12&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;time&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;easing&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;TWEEN&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Easing&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Sinusoidal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;InOut&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;chain&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;blue2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;blue2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;to&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rotation&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;wave&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;blue.x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;12&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;time&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;easing&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;TWEEN&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Easing&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Sinusoidal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;InOut&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;chain&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;blue1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;start&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这样便有了水面波动的效果。而两层水本身超出背景玻璃球的部分，则和节奏条一样，用一个圆形的&lt;code&gt;mask&lt;/code&gt;来解决。在&lt;code&gt;power&lt;/code&gt;更新时，只需要在加上一个短暂的动画，去修改两层水的&lt;code&gt;y&lt;/code&gt;即可。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. 积分：&lt;/strong&gt;  &lt;/p&gt;
&lt;p&gt;积分比较简单，直接用&lt;code&gt;PIXI.Text&lt;/code&gt;展示，在更新时去修改其&lt;code&gt;text&lt;/code&gt;属性即可，这里需要注意的是左侧&lt;strong&gt;0&lt;/strong&gt;字符的补齐。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. 判定状态：&lt;/strong&gt;  &lt;/p&gt;
&lt;p&gt;判定状态即当判定结束时，在屏幕中间出现的&lt;code&gt;state&lt;/code&gt; + &lt;code&gt;combo&lt;/code&gt;的组合，这里唯一值得一提的是combo的绘制用到了&lt;code&gt;BitmapText&lt;/code&gt;，一个坑是PIXI支持的&lt;code&gt;BitmapText&lt;/code&gt;描述文件是一个&lt;code&gt;xml&lt;/code&gt;文件二分&lt;code&gt;fnt&lt;/code&gt;，这个&lt;code&gt;xml&lt;/code&gt;文件可以用&lt;a href=&amp;quot;https://renderhjs.net/shoebox/&amp;quot;&gt;ShoesBox&lt;/a&gt;生成。&lt;/p&gt;
&lt;h3&gt;奖励模式&lt;/h3&gt;
&lt;p&gt;到这里完成的逻辑已然足以支撑一个正常的游戏流程。然而在正常流程外，还有一个奖励模式，这类似于太鼓的&lt;strong&gt;连打&lt;/strong&gt;。在这种模式下，玩家交互方式不变，还是&lt;strong&gt;点击触摸&lt;/strong&gt;，但输入将从&lt;strong&gt;按钮&lt;/strong&gt;变为&lt;strong&gt;全屏幕&lt;/strong&gt;，并且不再有正常的判定，任何一个点击都会被记为&lt;strong&gt;good&lt;/strong&gt;算入积分，但&lt;strong&gt;combo&lt;/strong&gt;并不会更新，简而言之——就是刷分用的。  &lt;/p&gt;
&lt;p&gt;奖励模式的启动和结束依托于&lt;code&gt;TRhythm&lt;/code&gt;中的&lt;code&gt;award.start&lt;/code&gt;和&lt;code&gt;award.end&lt;/code&gt;，进入奖励模式时，先会有前置动画提示玩家，然后便进入玩家的操作流程。在这种模式下，正常流程的控制器将被隐藏，取而代之的是扭动的小电视、背景的闪光以及一个盖于其上的、透明的全屏遮罩，玩家此时的输入事件实际上由此遮罩监听，监听到输入后，控制器将会绕过正常的判定流程而直接触发&lt;code&gt;EVENTS.BOSS_ACTION&lt;/code&gt;事件，直接完成判定。&lt;/p&gt;
&lt;h3&gt;音频&lt;/h3&gt;
&lt;p&gt;除了逻辑和画面，音频对于MUG也至关重要，前面所言的&lt;strong&gt;音画同步&lt;/strong&gt;也主要是画面同步音频而非相反。本游戏中对音频的操作很简单——就是加载一个音乐然后播放和停止而已。我在这里使用的是&lt;a href=&amp;quot;https://github.com/pixijs/pixi-sound&amp;quot;&gt;pixi-sound&lt;/a&gt;这个插件，它本质上是利用&lt;strong&gt;WebAudio API&lt;/strong&gt;来操作音频。为了使PIXI支持音频，我们需要在资源加载之前导入这个包：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// main.ts&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;import&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;pixi.js&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;import&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;pixi-sound&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;之后便可以像其他资源一样，用&lt;code&gt;loader.add&lt;/code&gt;方法将音频加入队列，进行正常的预加载和使用。  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注意，ios设备并不原生支持&lt;strong&gt;ogg&lt;/strong&gt;格式的音频编码，为了保证兼容性（又是SB的兼容性），我在这里使用了&lt;strong&gt;mp3&lt;/strong&gt;这种落后的编码模式。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;虽然&lt;strong&gt;WebAudio API&lt;/strong&gt;支持已然很广泛，但为了防止一些兼容问题，我们一开始要调用&lt;code&gt;PIXI.sound.supported&lt;/code&gt;来检查当前环境是否支持音频播放，不支持的话就...只有无声游戏了，这也是我为什么使用&lt;code&gt;ticker&lt;/code&gt;进行音画同步而不是用音频播放时的&lt;code&gt;onUpdate&lt;/code&gt;回调的原因之一（另一个原因是这个回调在不同设备下调用周期差距过大，难以使用）。  &lt;/p&gt;
&lt;p&gt;当然，你可能会疑惑——如果不利用音频的播放时间来同步进度，那么如何保证真正的音画同步呢？这个问题很好，我也有所考虑，但经过测试和思考，我发现这个一个没有必要烦恼的问题——音频已然预加载完成，出现卡顿的概率微乎其微，如果因为这点小担心而去采用风险更大的音频同步手段（风险已在上面声明），非常得不偿失。再者，如果一定要关注音频同步问题，那其实可以在&lt;code&gt;onUpdate&lt;/code&gt;回调中对&lt;code&gt;this.currentTime&lt;/code&gt;属性进行修正，但这又会带来潜在的竞争问题，加大了系统复杂度，所以我认为，对于这个游戏而言，现在的处理逻辑已然妥当。  &lt;/p&gt;
&lt;p&gt;经过以上分析，音频在本游戏中做的事情很简单，就是在开始的时候&lt;code&gt;play&lt;/code&gt;一下，结束的时候&lt;code&gt;stop&lt;/code&gt;一下，结束。&lt;/p&gt;
&lt;h2&gt;结算&lt;/h2&gt;
&lt;p&gt;有开始，有经过，有结束，才构成一个完整的游戏闭环。虽然有些高贵的独立游戏去挑战这个定则，但本游戏毕竟还是一个商业作品，自然也免不了俗——所以，在游戏过后，就进入了结算页面：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;Result&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2017_12_25a/3.png&amp;quot; /&gt;  &lt;/p&gt;
&lt;h3&gt;结算页绘制&lt;/h3&gt;
&lt;p&gt;最终结算在游戏结束时，由一个事件触发：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;eventManger&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;on&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;EVENTS&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;GAME_END&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;boss&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;stop&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;result&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;show&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;scoreboard&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;summary&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这里我创建了一个&lt;code&gt;Result&lt;/code&gt;组件来绘制结算页，在组件被实例化并加入&lt;code&gt;ui&lt;/code&gt;容器时，结算页的所有元素会被绘制一遍，它们分为&lt;strong&gt;解算部分&lt;/strong&gt;、&lt;strong&gt;分享部分&lt;/strong&gt;、&lt;strong&gt;抽奖部分&lt;/strong&gt;和&lt;strong&gt;STAFF部分&lt;/strong&gt;。而&lt;code&gt;show&lt;/code&gt;方法被调用时，实际上会调用一个私有方法&lt;code&gt;update&lt;/code&gt;，这个方法将会更新结算部分的一些元素的值，比如最大连击数、分数等，并重新计算定位来使其保持一个相对合理的位置。在修改完毕后，组件内部将会触发一个复杂的动画去将绘制后的元素显示出来。  &lt;/p&gt;
&lt;p&gt;不难发现，和前面的组件不同，结算组件的长度超过了屏幕长度，所以需要能够拖动，而和DOM页面不同，这里并没有原生的&lt;strong&gt;scrollbar&lt;/strong&gt;，只能自己去模拟，我写了一个&lt;strong&gt;ScrollableContainer&lt;/strong&gt;去捕获&lt;code&gt;touchmove&lt;/code&gt;事件来完成&lt;code&gt;scroll&lt;/code&gt;操作，也不复杂。  &lt;/p&gt;
&lt;h3&gt;分享图生成&lt;/h3&gt;
&lt;p&gt;在一次结算完成后，玩家如果点击分享到其他平台，会发现分享图就是自己当前得分对应的结算部分，这个本质上和&lt;strong&gt;BML2017&lt;/strong&gt;那次一样，是先生成base64的图、上传到后端实现的，不过和那次不同，如何&lt;strong&gt;将PIXI内的一个DOC转换为base64的图&lt;/strong&gt;其实是一个问题。在寻觅许久后，我找到了在当前版本&lt;strong&gt;V4.6.1&lt;/strong&gt;完成这个操作的方法：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;base64&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;game&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;renderer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;extract&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;canvas&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;goalsLayers&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;container&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;toDataURL&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;image/png&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;如此一来，结算页的逻辑基本就搞定了。&lt;/p&gt;
&lt;h2&gt;后话&lt;/h2&gt;
&lt;p&gt;不得不说现在国内直接跳过桌面端直接到移动端，使得很多有趣的设计效果无法实现，移动端由于性能和标准等各种问题，表现力也很有限，而WebAR/VR尚未Ready也进一步加大了这种尴尬。&lt;strong&gt;当然，如果大家谁有有趣的、纯公益或者纯艺术性与新媒体结合的路子，可以找我，视情况我可以提供无偿的技术支持，不仅仅是web前端，硬件我也可以视情况奉陪。&lt;/strong&gt;  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Monday, 25 Dec 2017 00:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2017.12.25 00:00:article/Skill-2017_12_25_a</guid>
<category>前端</category>
<category>Bilibili</category>
<category>圣诞</category>
<category>pixi.js</category>
<category>HTML5</category>
<category>MUG</category>
<category>音游</category>
</item>

<item>
<title>在pixi.js实现设备自适应和强制竖屏</title>
<link>http://dtysky.moe/article/Skill-2017_12_02_a</link>
<description>&lt;h2&gt;问题&lt;/h2&gt;
&lt;p&gt;最近在用&lt;code&gt;pixi.js&lt;/code&gt;写一个2D音游，出现了关于适配的三个问题。  &lt;/p&gt;
&lt;p&gt;其一，由于游戏是竖屏模式，而某APP在IPAD下是强制横屏的，所以需要一个方式去适配。  &lt;/p&gt;
&lt;p&gt;其二，游戏本身需要保持一定的纵横比，而且在不同分辨率的屏幕下都要保证内容显示正常，即需要一个适当的缩放。  &lt;/p&gt;
&lt;p&gt;其三，并非游戏内的所有元素都是有绝对位置定位的，有些元素的位置需要根据当前实际的游戏屏幕中的可视范围来确定，比如用户控制按钮必须在屏幕可视区域内置低，计分板必须置顶等，这需要在渲染时动态计算。&lt;/p&gt;
&lt;h2&gt;分析和实现&lt;/h2&gt;
&lt;p&gt;对于这三个问题，让我们先分析一下，其本质是什么，以及有哪些方式可以运用。&lt;/p&gt;
&lt;h3&gt;强制竖屏&lt;/h3&gt;
&lt;p&gt;竖屏适配的方式有很多种，一种普遍的方法是在PAD的横屏下向两侧添加背景，将游戏画布居中，如下图。这种方式虽然可行，但却不是那么完美。  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;1&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2017_12_02a/0.png&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;而另一种方法则是利用对容器的旋转等操作，让游戏画布始终保持一个真正意义上的竖屏，如图所示。这种方法比较完美，能最大化地利用设备的可视区域。    &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;2&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2017_12_02a/1.png&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;那么这种模式如何实现呢？关注过我之前的文章的朋友，想必知道之前我用&lt;code&gt;egret.js&lt;/code&gt;写过一个Galgame，它实现强制竖屏的方式是当设备横屏时，直接利用&lt;code&gt;transform&lt;/code&gt;的&lt;code&gt;rotate&lt;/code&gt;属性对&lt;code&gt;canvas&lt;/code&gt;容器进行变换，而后通过调整&lt;code&gt;left&lt;/code&gt;和&lt;code&gt;top&lt;/code&gt;属性来重置容器位置。进行调整后，我们获得了一个旋转过的、仍然铺满屏幕的&lt;code&gt;canvas&lt;/code&gt;容器，由于只是修改了容器的属性，所以容器内部绘制的场景仍然是相对不变的：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nn&amp;quot;&gt;#container&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nt&amp;quot;&gt;transform&lt;/span&gt;&lt;span class=&amp;quot;nd&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nt&amp;quot;&gt;rotate&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;90deg&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;nt&amp;quot;&gt;top&lt;/span&gt;&lt;span class=&amp;quot;nd&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;err&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;device-height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;nt&amp;quot;&gt;left&lt;/span&gt;&lt;span class=&amp;quot;nd&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nt&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这样做画面确实完成了适配，一切看起来很完美，但进一步操作后你会发现这个在&lt;code&gt;pixi.js&lt;/code&gt;中是行不通的——touch事件的位置判定出现了问题，也就是说旋转后，你给DOC(DisplayaObjectContainer)的绑定的事件无法在触碰它时被正确触发。经过查看&lt;code&gt;egret&lt;/code&gt;的源代码并进行分析后，我确认是&lt;code&gt;egret&lt;/code&gt;在旋转容器后重置了全局的touch事件判定，应该是在&lt;code&gt;transform&lt;/code&gt;变换后引擎对触碰位置与画面内DOC的映射出现了问题，所以需要重置，但在&lt;code&gt;pixi&lt;/code&gt;中，我并没有找到这个方法（也可能看漏了，有找到的请务必告诉我），所以只能用另一个方法来绕过——既然根级容器无法完成需求，我就利用引擎自身提供的&lt;code&gt;Container&lt;/code&gt;方法自己造一个根级容器，由于对这个容器的所有变换都是引擎内部管理的，所以并不会出现touch事件的映射错误，也就完美绕过了之前的问题。具体实现看以下代码：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;root&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;PIXI&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Container&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;game&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;stage&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;addChild&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;root&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;screenRect&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;container&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getBoundingClientRect&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;renderer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;resize&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;screenRect&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;screenRect&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;window&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;orientation&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;90&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;||&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;window&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;orientation&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;90&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;root&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rotation&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;Math&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;PI&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;root&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;newWidth&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;root&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rotation&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;Math&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;PI&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;root&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;screenRect&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这个本质上，是始终保持&lt;code&gt;canvas&lt;/code&gt;容器全屏，然后重置&lt;code&gt;renderer&lt;/code&gt;的尺寸让根级的&lt;code&gt;stage&lt;/code&gt;容器全屏。之后构建一个&lt;code&gt;root&lt;/code&gt;容器作为实际的根级容器，并将其添加到&lt;code&gt;stage&lt;/code&gt;中作为唯一的&lt;code&gt;children&lt;/code&gt;。之后判断&lt;code&gt;window.orientation&lt;/code&gt;属性，这个属性用于判断当前设备的方向，当其为&lt;code&gt;±90&lt;/code&gt;时即为横屏，此时只需要将&lt;code&gt;root&lt;/code&gt;容器旋转90度，而后将其&lt;code&gt;transform.y&lt;/code&gt;进行调整。&lt;/p&gt;
&lt;h3&gt;缩放&lt;/h3&gt;
&lt;p&gt;为了强制竖屏，我们已然有了&lt;code&gt;root&lt;/code&gt;容器，如此一来，缩放也就变得十分简单了——只需要设置&lt;code&gt;root&lt;/code&gt;容器的缩放即可:  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;当然，如果仅仅是需要缩放，直接对&lt;code&gt;canvas&lt;/code&gt;进行缩放，并始终保持&lt;code&gt;renderer&lt;/code&gt;的&lt;code&gt;size&lt;/code&gt;为游戏画布本身的绝对大小也可以。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;options&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;screenRect&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;container&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getBoundingClientRect&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;renderer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;resize&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;screenRect&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;screenRect&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;offsetWidth&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;offsetHeight&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;window&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;orientation&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;90&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;||&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;window&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;orientation&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;90&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;offsetWidth&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;screenRect&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;offsetHeight&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;screenRect&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;offsetWidth&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;screenRect&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;offsetHeight&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;screenRect&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;newWidth&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;offsetWidth&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;scale&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;newWidth&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;newHeight&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;scale&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;nx&amp;quot;&gt;root&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;scale&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;newWidth&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;root&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;scale&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;newHeight&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这里直接对&lt;code&gt;root&lt;/code&gt;容器进行了缩放，这样一来，之后所有子级元素的定位布局仍然是按照游戏画布的绝对大小来的。也就是说，这一层的修改相对于后面的编程是无感知的，做到了自适应逻辑的无痛植入。我在这里实现的是&lt;code&gt;fixedWidth&lt;/code&gt;这种自适应模式，它表现为宽度适应屏幕，高度将会跟随宽度进行缩放。这种模式是最为常见的一种模式，但它往往也会带来一个问题——游戏缩放后的实际画布仍然有可能会超出设备可视区域，这就引出了一个概念——可视范围。&lt;/p&gt;
&lt;h3&gt;可视范围&lt;/h3&gt;
&lt;p&gt;解决了游戏画布的自适应，还有可视元素自适应的问题需要解决。如一开始分析，在游戏中有些元素是不能越出可视区域的，所以就需要在编写布局代码时拿到游戏可视区域的定义，以此为基准来动态确定元素的位置。为此，我定义了一个&lt;code&gt;realScreen&lt;/code&gt;的公有属性，里面存储着游戏实际的可视范围：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;何为越过可视区域？比如在一个16:9的设备上，我使用了以上的强制竖屏和&lt;code&gt;fixedWidth&lt;/code&gt;模式。此时倘若我设计了一个作为玩家控制器的元素，其定位为游戏画布置底，那么当设备的可视区域比例小于16:9时，这个控制器的一部分便会越出可视区域，这显然并不是我们预期的，我们需要的，应该是无论设备如何改变，控制器都始终在设备可视区域的底部。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;TScreen&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 可视区域的上边界，对标游戏画布尺寸&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;top&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 可视区域的下边界，对标游戏画布尺寸&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;bottom&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 可视区域的左边界，对标游戏画布尺寸&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;left&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 可视区域的右边界，对标游戏画布尺寸&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;right&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 可视区域的宽度，对标游戏画布尺寸&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 可视区域的高度，对标游戏画布尺寸&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 可视区域的宽度，对标设备尺寸&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;realWidth&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 可视区域的高度，对标设备尺寸&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;realHeight&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// 设备纵横比&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;aspectRatio&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;如此一来，后续元素便可以动态确定它们的位置了。&lt;/p&gt;
&lt;h2&gt;完整代码&lt;/h2&gt;
&lt;p&gt;我将以上三种特性集成到了一个&lt;code&gt;resize&lt;/code&gt;函数中，并将其放入了一个&lt;code&gt;Game&lt;/code&gt;类中，以下便是封装好的&lt;code&gt;Game&lt;/code&gt;类，使用时只需要从类的实例中获取&lt;code&gt;realScreen&lt;/code&gt;属性便可。  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;default&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;class&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Game&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;extends&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;PIXI&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Application&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;private&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;container&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;HTMLElement&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;private&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;options&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;PIXI.ApplicationOptions&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;root&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;PIXI.Container&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;realScreen&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;TScreen&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

  &lt;span class=&amp;quot;kr&amp;quot;&gt;constructor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;element&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;options?&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;PIXI.ApplicationOptions&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;super&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;options&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;options&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;options&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;container&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;document&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getElementById&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;element&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;container&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;appendChild&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;view&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;root&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;PIXI&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Container&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;addChild&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;root&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

  &lt;span class=&amp;quot;kr&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;resize&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;options&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;screenRect&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;container&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getBoundingClientRect&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;renderer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;resize&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;screenRect&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;screenRect&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

    &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;offsetWidth&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;offsetHeight&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;window&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;orientation&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;90&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;||&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;window&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;orientation&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;90&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;offsetWidth&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;screenRect&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;offsetHeight&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;screenRect&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;offsetWidth&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;screenRect&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;offsetHeight&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;screenRect&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;aspectRatio&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;offsetHeight&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;offsetWidth&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

    &lt;span class=&amp;quot;c1&amp;quot;&gt;// fixWidth, orientation=&amp;quot;portrait&amp;quot;&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;newWidth&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;offsetWidth&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;scale&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;newWidth&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;newHeight&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;scale&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;root&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;layers&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;root&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;scale&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;newWidth&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;root&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;scale&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;newHeight&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;window&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;orientation&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;90&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;||&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;window&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;orientation&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;90&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;root&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rotation&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;Math&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;PI&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;root&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;newWidth&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;root&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rotation&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;root&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;realScreen&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;realWidth&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;offsetWidth&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;realHeight&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;offsetHeight&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;top&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;bottom&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;offsetHeight&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;scale&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;left&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;right&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;aspectRatio&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;realScreen&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;realScreen&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;right&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;realScreen&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;left&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;realScreen&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;realScreen&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;bottom&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;realScreen&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;top&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Saturday, 02 Dec 2017 10:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2017.12.02 10:00:article/Skill-2017_12_02_a</guid>
<category>前端</category>
<category>pixi.js</category>
<category>适配</category>
</item>

<item>
<title>Bilibili《七夕之约 - Double;7》技术剖析</title>
<link>http://dtysky.moe/article/Skill-2017_08_27_a</link>
<description>&lt;p&gt;今年B站七夕活动的Galgame&lt;a href=&amp;quot;https://www.bilibili.com/blackboard/double7.html&amp;quot;&gt;Double;7&lt;/a&gt;由我负责原案、程序和演出三部分，深度使用了白鹭游戏引擎的2D部分并为之写了一个galgame插件&lt;a href=&amp;quot;https://github.com/dtysky/egret-galgame&amp;quot;&gt;egret-galgame&lt;/a&gt;，下面就这次活动的各个方面我将输出一篇心得，各位稍安勿躁，待我娓娓道来。  &lt;/p&gt;
&lt;h2&gt;前言&lt;/h2&gt;
&lt;p&gt;本人的开发之路算是比较崎岖，到现在为止从底层硬件开发到WEB前端都干过一些，而现在基本确定了WEB前端的道路，虽说如此，这条道路却和我梦想的道路并非一致，本质上来讲，我还是想做游戏、尤其是做GAL。但这并不代表我舍弃了这个追求，而是一直在需找契机，看是否有机会争取到资源进行尝试。  &lt;/p&gt;
&lt;p&gt;众所周知，B站去年七夕就有这样一个尝试，然而那时候我入职尚早，尚在后台开发组中，在公司内也没有资源和话语权，所以并未能参与。而这次的七夕，我终于有机会并果断抓住了机会，和一帮同样很有想法和干劲的同事进行了这次游戏的开发，虽然受制于H5活动本身的性质，此次也有所遗憾，但终归还是做出来了不错的作品，对这些同事表示由衷的感谢。  &lt;/p&gt;
&lt;p&gt;对于本人而言，负责GAL的原案实在是一件非常具有诱惑力的事情，自己过去也做过很多尝试，加之也有不少GAL的游戏经验，所以对游戏质量自然有一定要求。但和正常游戏不同，这次游戏本质上是一个H5活动页，所以有存在着很多问题。  &lt;/p&gt;
&lt;p&gt;其中一个问题，就是如何在有限的时长和资源内做出一个比较合格的GAL，而这一点是比较困难的。对于一个H5的活动页面，本次活动预计的十分钟的平均通关时间实际上已是超常规，上面也有所微词，但考虑这其实又是一个游戏，所以最终还是争取到了这个时间。虽然十分钟对于一个正统GAL还是太短，但尚可比较完整得走完一套流程。  &lt;/p&gt;
&lt;p&gt;第二个问题是游戏风格倾向。作为资深GAL玩家，虽然对废萌也不反感，但自身还是倾向于深刻向的正剧或者电波作，所以一开始基调定的是偏向悲剧中透着淡淡的关怀的、对死宅人文关怀的类型。但和编剧讨论后，最终还是由于这个活动的官方欢乐性质妥协了，这一点确实没有办法，但编剧菊苣很好得使用了之前B站相关的一些梗，无厘头也算是一个比较安全的倾向吧。  &lt;/p&gt;
&lt;p&gt;第三个则是设备问题。此次平台是移动端，而且是WEB上，加之在APP内主推，所以横屏模式下可用空间有限，最终选择了竖屏模式。竖屏模式和传统GAL的演出是相悖的，因为其不符合一个舞台相对于人眼的空间成像习惯，同时也限制了一个舞台（屏幕）中出现的任务、物体数量，这相对于传统GAL是一个瓶颈，而白鹭也只提供了锁定竖屏而非横屏自适应模式。所以必须有一个方案来在竖屏模式下模拟横屏演出，我做到了这一点，具体细节会在下文中说明。  &lt;/p&gt;
&lt;h2&gt;原案&lt;/h2&gt;
&lt;p&gt;本游戏主标题是《Double;7》，这一个标题承载着多重的含义，其直接影响到了游戏的主题和形式，也算是对某两个经典系列的致敬。  &lt;/p&gt;
&lt;p&gt;首先，“Double7”即“双7”也即“77”，即七夕，这意味着这个游戏是一个七夕的游戏。  &lt;/p&gt;
&lt;p&gt;其次，Word + Number形式的标题是业界眼泪KID的的无限轮回系列每一作的命名方式，其中最著名的而是《Ever17》，然后是首作《Never7》，后面还有《Remeber11》、《12River》等。这一系列的主要特征就是带有轮回概念的科学幻想。这也意味着本作有轮回的要素。  &lt;/p&gt;
&lt;p&gt;最后，XX;YY形式的标题是KID的精神后继者5PB开发的科学妄想系列每一作的命名方式，比如最著名的《Steins;Gate》，本人最爱的《CHAOS;HEAD》、《Chaos;Child》，以及某RN和社长亲自下笔的《Occultic;Nine》等。这一系列核心是对扭曲真死宅的人文关怀与自我救赎。这一点，是本作最初的核心立意。  &lt;/p&gt;
&lt;p&gt;本作的核心立意除了源于科学妄想系列外，也源于《四叠半神话大系》这一作品。本质上，我还是想传达一个“走出去尝试看看吧，无论如何，只要去做了，即便是后悔，也比什么都不做强”的观点。无论是轮回前期的尝试还是最后放弃时的反差、以及最后的最后的配对、和另一个玩家的相遇和协作，都是为了这一个主题而服务的。&lt;/p&gt;
&lt;p&gt;当然，由于本作特殊的官方活动性质，剧情无法加入偏悲剧和些许恶意的情节，无法参入毒电波，更无法表现阴暗的放弃挣扎和自我的憎恶与反思，只能走无厘头的道路，所以自然就无法有最后反思爆发的高潮。这一点确实有些遗憾，但考虑基本走向和玩法还是保留了，也难为编剧了。&lt;/p&gt;
&lt;h2&gt;程序架构&lt;/h2&gt;
&lt;p&gt;技术方面可说的点比较多。此次活动虽然是一个WEB页面，但本质上其实是游戏开发。作为一个游戏，自己从头写一套渲染虽未尝不可，但毕竟是开发周期有限的商业活动，所以我选用了现成H5引擎&lt;a href=&amp;quot;http://developer.egret.com/cn/&amp;quot;&gt;Egret&lt;/a&gt;，这个引擎用下来虽然有一些小问题，但整体还是不错的。不过如果现在让我重选，我应该会选择对工作流侵入性不高的&lt;code&gt;PIXI&lt;/code&gt;等框架吧——毕竟对于一个H5应用，Egret做的事情还是太多了，这导致使用第三方库、打包等都难以和现在的标准工作流结合，最后打包合并、版本控制还是我自己写了个小脚本做的，这点确实不怎么合理。  &lt;/p&gt;
&lt;p&gt;由于本人自身有着比较丰富的GAL开发经验，所以借助于一些优秀的设计（比如&lt;a href=&amp;quot;https://www.renpy.org/&amp;quot;&gt;Renpy&lt;/a&gt;）后，我将自己在此次游戏中使用的核心代码封装成了一个&lt;code&gt;Egret&lt;/code&gt;的扩展，其按照规范，被封装在&lt;code&gt;GAL&lt;/code&gt;的namespace内，开箱即用，读者可以在此处&lt;a href=&amp;quot;https://github.com/dtysky/egret-galgame&amp;quot;&gt;egret-galgame&lt;/a&gt;来获取它。但做一个非常完美的扩展毕竟耗时巨大，本人工作较忙，所以此物有很多地方并不完整，如果要用在自己项目中，请读完下面的经验介绍并按照自己需求对其做出更改即可。&lt;/p&gt;
&lt;h3&gt;顶层框架&lt;/h3&gt;
&lt;p&gt;由于游戏的形式是GAL，所以和传统游戏相比，其交互的侧重点和表现形式大为不同。GAL大多都是2D的，其基本表现方式是一个作为场景的背景、一个或多个背景前的人物、一个NVL或是ADV模式的对话框，通过人物的形态、表情、动画和对话框中的文本、还有场景间的过度效果来进行话剧式的演出。所以比起传统的游戏，我更愿意将其视作一个“虚拟的话剧”。针对这样的情境，一个简洁有效的&lt;strong&gt;图层分层&lt;/strong&gt;方案必须被提供，这也就是本扩展中&lt;code&gt;Scene&lt;/code&gt;的概念。  &lt;/p&gt;
&lt;p&gt;这种演出方式也决定了它和一般游戏交互的不同。GAL的主要流程是由一个单一的输入决定的，在本作中，这个输入是“对屏幕的点击”。用户不断触摸屏幕，游戏程序响应这个触摸进行对应的演出。换言之，GAL的操作方式是相对单一的，而建立在这种单一之上的，则是由大量文本堆积出的巨量演出，针对这种简单的输入和复杂的效果，一个大幅简化流程的&lt;strong&gt;脚本DSL&lt;/strong&gt;毫无疑问是必要的。  &lt;/p&gt;
&lt;p&gt;下面就针对以上两个问题做详细的说明。&lt;/p&gt;
&lt;h3&gt;图层&lt;/h3&gt;
&lt;p&gt;图层设计是建立在Egret本身的图层概念上的，其图层排序是按照子级加入到父级容器的顺序来确定的，并且其图层的维护是一个数组，所以并不能简单得设置一个很大的值和很小的值以及中间值来简单排序，只能在得知原图层&lt;code&gt;index&lt;/code&gt;的情况下进行交换或是简单的置顶和置底，所以一个最佳的策略其实是在运行过程中尽量不要去更改图层顺序，这也就要求这个扩展的设计要一开始将层次区分明确。我对图层的区分如下图所示：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;图层设计&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2017_08_27a/0.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;&lt;code&gt;Scene&lt;/code&gt;作为整个扩展的核心，负责维护游戏场景中的所有子元素，这其中包括主要元素&lt;code&gt;main&lt;/code&gt;、对话框&lt;code&gt;dialog&lt;/code&gt;、纯文本&lt;code&gt;text&lt;/code&gt;和选择支&lt;code&gt;branch&lt;/code&gt;，&lt;code&gt;main&lt;/code&gt;下面又有背景&lt;code&gt;bg&lt;/code&gt;和人物&lt;code&gt;chars&lt;/code&gt;。其中&lt;code&gt;main&lt;/code&gt;层级最低，而其中的&lt;code&gt;bg&lt;/code&gt;又是最低的，接下来是&lt;code&gt;chars&lt;/code&gt;、&lt;code&gt;text&lt;/code&gt;、&lt;code&gt;dialog&lt;/code&gt;、&lt;code&gt;branch&lt;/code&gt;，这个顺序是按照功能性排列的。这些子元素都以对象的形式存在，而这些对象的类中必有一个&lt;code&gt;addToScene&lt;/code&gt;方法，此方法使得这些类可以保存&lt;code&gt;Scene&lt;/code&gt;对象的引用并将其自身添加到场景中。  &lt;/p&gt;
&lt;p&gt;这其中，其他的顺序都比较容易理解，而有一个特殊的点是背景图层&lt;code&gt;bg&lt;/code&gt;和人物图层&lt;code&gt;chars&lt;/code&gt;并未打平，而是合并放在&lt;code&gt;main&lt;/code&gt;下面，这是因为游戏中既有单独移动背景和人物，也有让背景和人物同步移动的需求。  &lt;/p&gt;
&lt;p&gt;在实际运行中，&lt;code&gt;Scene&lt;/code&gt;先初始化，清空当前所有显示的内容，然后通过背景图片以及一个过渡效果创建一个新的场景，之后根据需求对&lt;code&gt;bg&lt;/code&gt;进行位置或缩放的动画控制，当需要人物出现时，便将人物对应的对象添加到&lt;code&gt;chars&lt;/code&gt;图层中，并在后续控制其位置、缩放、姿势和表情等。如果有对话、分支等需求，则只要再在对应的图层中添加对象即可。&lt;/p&gt;
&lt;h3&gt;脚本化&lt;/h3&gt;
&lt;p&gt;场景管理只解决了基本的绘制问题，但并未解决开发效率的问题。在理想的开发策略中，当我想进行演出，比如想切换背景、显示一个人物、或是让某人说一段话的时候，我并不需要去写复杂的类似：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;scene&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;addChild&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;charABodyA&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;scene&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;addChild&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;charAFaceA&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;charABodyA&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;charBBodyA&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;charAFaceA&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;200&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;text&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;A&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;300&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;words&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;text&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;蛤蛤蛤蛤&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;words&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;200&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这样的代码。而是想去写出类似这样的简单的脚本：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;charA&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Character&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;A&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;charA&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;addToScene&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;scene&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;charA&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;posture&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;A&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;face&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;A&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;at&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;show&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;charA&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;say&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;蛤蛤蛤蛤&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这种脚本无疑更加简洁明晰，也更符合人类的思维模式。事实上，很多老牌GAL游戏引擎都是这么做的，比如前面提到的Renpy，其提供的脚本时这样的：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;scene bg SE02C at truecenter with move
show Song AB01A at GridXfL(3,0.5)
SongA &amp;#39;偶尔也要释放一下嘛。&amp;#39;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;相比我上面的写法，Renpy语法更加简洁，属于标准的DSL（作者自己写了一个解释器来处理这个DSL）。但无论是从开发周期、还是从稳定性、扩展性而言，这样的DSL都显然不适用于我现在的场景，硬上的话也不过是杀鸡焉用牛刀，我需要的实际上是类似于&lt;code&gt;JQuery&lt;/code&gt;那样的&lt;strong&gt;语言内DSL&lt;/strong&gt;，即通过链式调用来达到近似DSL的描述能力，但却有可以灵活地插入原生js代码中进行混用，并且开发和维护也较为便捷。  &lt;/p&gt;
&lt;p&gt;此扩展的DSL核心是一句一句的描述性语句，像是上面第二段代码的第三和第四行那样，我通过面向对象的思想将其设计为了由链式调用构成的语句。比如第三句，就表示人物A将展示A姿势A表情、并显示在(0, 0)的位置，第四局则是这个人物A将会说出&lt;strong&gt;蛤蛤蛤蛤&lt;/strong&gt;这句话，至于具体渲染出的逻辑，则由这个扩展引擎的后端部分来实现。  &lt;/p&gt;
&lt;p&gt;有了基本的DSL，就需要一个集合来管理它们，完成它们的&lt;strong&gt;解析&lt;/strong&gt;，而这一切则需要一个集合的&lt;strong&gt;描述结构&lt;/strong&gt;和对应的&lt;strong&gt;解释器&lt;/strong&gt;来完成。我选择的结构是最简单的数组，数组中的每一个元素都是一个匿名函数，而解释器的功能就是不断地读取这些匿名函数执行，并通过执行结果来决定下一步的行为。匿名函数集合在实际的编写中如下所示：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;map1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;scene&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;with&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;600&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;fade&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;then&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(()&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;next&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;toMode&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;NVL&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;at&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;370&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;create&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;home&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;boy&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;think&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;什么...我是主角？&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;boy&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;think&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;开什么玩笑，当主人公岂不是会很累...我可没那么多精力。&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;boy&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;think&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;蛤？还是让我当，我...&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;branch&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;open&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;([&lt;/span&gt;
      &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;content&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;搞什么玩意，我就是不想当怎么着了？&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;callback&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
          &lt;span class=&amp;quot;nx&amp;quot;&gt;scene&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;with&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;().&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;toMode&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;ADV&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
          &lt;span class=&amp;quot;nx&amp;quot;&gt;interpreter&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;next&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
      &lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
      &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;content&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;没办法，看来还是逃不过命运啊~&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;callback&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
          &lt;span class=&amp;quot;nx&amp;quot;&gt;scene&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;with&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;().&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;toMode&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;ADV&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
          &lt;span class=&amp;quot;nx&amp;quot;&gt;interpreter&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;next&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
      &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;]),&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;boy&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;say&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;...不对，我是和谁在说话？&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;在以上这段代码中，我将数组&lt;code&gt;map1&lt;/code&gt;称为一段&lt;strong&gt;剧本&lt;/strong&gt;，其中的每一元素都是一个匿名函数。解释器读取剧本中的每一句进行执行，而这个执行方法的触发源就是用户的触摸事件，用户触摸一下，解释器便将内部维护的指针加一并读取执行，执行的效用由每个对象的内部实现，比如场景切换、人物显示、说话等。而每一句函数执行的返回值，也决定着用户下一次行为的功能，这也是解释器运行的核心。&lt;/p&gt;
&lt;h3&gt;解释器&lt;/h3&gt;
&lt;p&gt;由于是语言内的DSL，所以解释器构造很简单，不同于传统的语法分析需要构造语法树，此扩展的解释器的核心功能只需要维护一个内部列表和一个数组的指针便可实现。为了进行内部剧本的载入，我实现了&lt;code&gt;load: (script: TScript[]) =&amp;gt; void&lt;/code&gt;方法，其加载一个剧本并准备对齐进行解析；解释器中的指针则由&lt;code&gt;next: () =&amp;gt; void&lt;/code&gt;和&lt;code&gt;goto: (line: number) =&amp;gt; void&lt;/code&gt;方法控制，&lt;code&gt;next&lt;/code&gt;方法将指针加一，之后执行指针指向的那句脚本，而&lt;code&gt;goto&lt;/code&gt;方法则是&lt;code&gt;next&lt;/code&gt;方法的一个扩展，可以跳转到并执行指定行。  &lt;/p&gt;
&lt;p&gt;除了以上基本功能之外，解释器还必须提供现场保护和恢复的功能。比如在一段剧本中，有一个语句是使用&lt;code&gt;load&lt;/code&gt;方法加载别的剧本并执行，而新加载的剧本执行完后我们显然希望回到原来的剧本并继续执行下一句，这个状况在&lt;strong&gt;分支&lt;/strong&gt;存在的情况下非常常见。这就要求解释器内部维护的并非一个单纯的剧本，而是一个剧本构成的&lt;strong&gt;栈&lt;/strong&gt;，这和传统底层开发的汇编中保护现场的姿势一致。每当加载一个新的剧本，我就会把当前剧本以及对应指针分别入栈，然后再执行新的剧本，当新剧本执行完毕后我便从栈中出栈一个剧本和对应的指针，并继续执行。  &lt;/p&gt;
&lt;p&gt;至此，一个满足需求的基本的解释器便已完成，但考虑GAL的特殊演出形式，此扩展必须提供一个类似于&lt;strong&gt;快进&lt;/strong&gt;的功能来使得玩家可以加速语句显示或是动画的表现，使其快速到达结束的状态。比如我在角色说话的过程中再触摸一下屏幕，当前的这句话就会被直接跳过逐字动画而直接显示；除此之外，有时候我们还希望一些效果无法被略过，也即能够有一些方法卡住脚本的执行，在不频繁使用事件的绑定与解绑的情况下，一个统一的解决方案需要被提出，而我的方案，就是依赖于剧本中每一句匿名函数的返回值——  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;TSPScriptResult&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;check&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;boolean&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;exec&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;void&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;TScript&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;any&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;TSPScriptResult&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;此处类型定义并非BUG，这只是表明匿名函数的返回值可以是任意类型，但其中有一个类型是&lt;code&gt;TSPScriptResult&lt;/code&gt;。当返回值的类型是其他时，解释器不做操作，正常响应接下来的所有行为，而如果是&lt;code&gt;TSPScriptResult&lt;/code&gt;，则会进入特殊的处理流程：  &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;如果&lt;code&gt;check&lt;/code&gt;的执行结果是&lt;code&gt;true&lt;/code&gt;，则正常响应接下来的操作。&lt;/li&gt;
&lt;li&gt;如果&lt;code&gt;check&lt;/code&gt;的执行结果是&lt;code&gt;false&lt;/code&gt;，则执行&lt;code&gt;exec&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这个简单的规则可以解决上面提出的几乎所有的问题，当有可以被跳过的动画相关的语句被执行时，可以返回一个&lt;code&gt;TSPScriptResult&lt;/code&gt;的结果，其中由&lt;code&gt;check&lt;/code&gt;方法告知解释器是否要卡住解释流程，而&lt;code&gt;exec&lt;/code&gt;方法则在允许快速跳过的情形下、给出一个快速跳过的方法。比如在一开始的性别选择页面，我就是通过&lt;code&gt;check&lt;/code&gt;卡住接下来的操作，直到用户选择完毕才改变其返回结果，重启解释流程。&lt;/p&gt;
&lt;h3&gt;资源管理&lt;/h3&gt;
&lt;p&gt;资源管理也是游戏中非常重要的一部分，我在这里基本就用了Egret自身的资源管理模式。核心是用它提供的&lt;a href=&amp;quot;http://developer.egret.com/cn/github/egret-docs/tools/TextureMerger/update/update163/index.html&amp;quot;&gt;TextureMeger&lt;/a&gt;来进行图像资源的合并，而后利用其&lt;code&gt;RES&lt;/code&gt;单例进行资源加载和管理。比如如下这张图和其对应的json文件：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;merge后的图&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2017_08_27a/1.png&amp;quot; /&gt;  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;file&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;dialog.png&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;frames&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:{&lt;/span&gt;
&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;altair-s&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:{&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;x&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;542&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;y&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;289&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;w&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;540&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;h&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;287&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;offX&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;offY&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;sourceW&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;540&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;sourceH&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;287&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;altair-t&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:{&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;x&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;542&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;y&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;867&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;w&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;540&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;h&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;287&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;offX&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;offY&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;sourceW&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;540&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;sourceH&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;287&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;boy-s&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:{&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;x&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1084&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;y&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;w&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;540&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;h&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;287&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;offX&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;offY&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;sourceW&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;540&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;sourceH&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;287&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;boy-t&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:{&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;x&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;542&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;y&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;867&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;w&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;540&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;h&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;287&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;offX&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;offY&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;sourceW&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;540&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;sourceH&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;287&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;fff-s&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:{&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;x&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1084&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;y&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;289&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;w&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;540&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;h&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;287&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;offX&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;offY&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;sourceW&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;540&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;sourceH&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;287&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;fff-t&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:{&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;x&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;542&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;y&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;867&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;w&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;540&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;h&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;287&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;offX&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;offY&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;sourceW&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;540&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;sourceH&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;287&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;girl-s&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:{&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;x&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1084&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;y&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;578&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;w&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;540&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;h&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;287&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;offX&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;offY&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;sourceW&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;540&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;sourceH&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;287&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;girl-t&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:{&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;x&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;542&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;y&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;867&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;w&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;540&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;h&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;287&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;offX&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;offY&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;sourceW&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;540&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;sourceH&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;287&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;npc-s&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:{&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;x&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;542&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;y&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;w&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;540&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;h&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;287&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;offX&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;offY&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;sourceW&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;540&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;sourceH&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;287&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;npc-t&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:{&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;x&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;542&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;y&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;867&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;w&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;540&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;h&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;287&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;offX&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;offY&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;sourceW&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;540&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;sourceH&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;287&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;nvl&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:{&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;x&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;y&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;w&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;540&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;h&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;960&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;offX&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;offY&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;sourceW&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;540&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;sourceH&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;960&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;vega-s&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:{&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;x&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;542&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;y&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;578&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;w&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;540&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;h&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;287&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;offX&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;offY&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;sourceW&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;540&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;sourceH&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;287&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;vega-t&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:{&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;x&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;542&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;y&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;867&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;w&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;540&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;h&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;287&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;offX&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;offY&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;sourceW&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;540&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;sourceH&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;287&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}}}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这样能够大幅减少网络请求，提高响应速度。对于大部分资源这样处理都是没有问题的，但对于人物的立绘资源，由于我的特别设计，需要另一套方式而非工具合并，这一点会在下面说明。  &lt;/p&gt;
&lt;p&gt;资源管理没问题以后，便会出现资源预加载的问题。对于一个游戏，资源量肯定不会少，然而对于一个WEB应用，响应速度又很重要。为了去一个平衡，本游戏的处理方式是——将资源加载分为两个部分。用户在JS加载完毕后首先只预加载标题和基础UI相关的素材，加载完毕后先展示首屏，用户操作后再加载剩下的资源，这样至少能够保证用户能在短时间内看到东西，不至于快速流失。除此之外，我还是用了&lt;a href=&amp;quot;http://optimizilla.com/&amp;quot;&gt;optimizilla&lt;/a&gt;来压缩图片，普遍能压缩60%+，来达到效果和大小的平衡。&lt;/p&gt;
&lt;h2&gt;细节实现&lt;/h2&gt;
&lt;p&gt;顶层的大框架设计说完了，下面就来说一部分细节的实现吧。&lt;/p&gt;
&lt;h3&gt;场景&lt;/h3&gt;
&lt;p&gt;场景类是&lt;code&gt;Scene&lt;/code&gt;，其负责管理场景图层（已在上文说过）和控制背景以及整体的位置、动画等操作，其最基本的方法就是&lt;code&gt;create&lt;/code&gt;方法，配合&lt;code&gt;with&lt;/code&gt;、&lt;code&gt;at&lt;/code&gt;、&lt;code&gt;from&lt;/code&gt;、&lt;code&gt;show&lt;/code&gt;等方法能达到更丰富的控制。以下是一句基本的脚本：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;scene&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;with&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;600&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;normal&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;at&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;from&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;right&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;then&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(()&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;next&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;create&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;home&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这句话代表我要新建一个场景，这个场景的背景是&lt;code&gt;home&lt;/code&gt;，以&lt;code&gt;normal&lt;/code&gt;模式、600ms过渡，并且背景的位置是(0, 0)，过渡从右侧开始，并且在场景创建完成后执行作为&lt;code&gt;then&lt;/code&gt;参数的回调函数。这几个操作基本能实现场景管理的所有功能，下面来一个一个分析：  &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;with&lt;/code&gt;和&lt;code&gt;from&lt;/code&gt;方法：&lt;code&gt;with&lt;/code&gt;的第一个参数是时间，第二个是模式，决定着下一次创建场景时使用的过渡模式，而&lt;code&gt;from&lt;/code&gt;则决定着过渡方向。过渡方法此处暂时有&lt;code&gt;normal&lt;/code&gt;、&lt;code&gt;fade&lt;/code&gt;和&lt;code&gt;none&lt;/code&gt;三种，&lt;code&gt;fade&lt;/code&gt;顾名思义是先渐隐第一个场景，再渐显第二个场景；&lt;code&gt;none&lt;/code&gt;是直接变换场景，没有任何过渡效果；&lt;code&gt;normal&lt;/code&gt;则是一种模仿传统GAL的过渡的效果（就是游戏中选择完大场景后的切换效果），在传统引擎中，这个效果是由一张渐变图加上&lt;code&gt;ImageDissolve&lt;/code&gt;函数定义，并在过渡过程中实时生成中间图像的，但考虑WEB的性能问题，我此处选择的方式是利用一个具有羽化毛边黑色图作为mask，先将其移入，然后在场景被盖住的时候变换场景，之后再移出，便可模拟那种传统的效果。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;at&lt;/code&gt;方法用于定位场景，决定下一次将场景移动到某个坐标(x, y)处。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;then&lt;/code&gt;接受一个回调函数，这个回调函数将在场景变换结束后调用，这是为了提供一个调度的方法。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;create&lt;/code&gt;方法是一个终结方法，其不是一个链式调用的节点，功能是利用参数中的标示，去寻找资源中的&lt;code&gt;bg_${mark}&lt;/code&gt;图像，作为新场景的背景进行变换，在变化之时，对象还会调用一个&lt;code&gt;clear&lt;/code&gt;方法，来清空子图层的所有对象。  &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如上所言，像是&lt;code&gt;at&lt;/code&gt;这种方法其实只是决定下一次场景在&lt;code&gt;create&lt;/code&gt;或是另一个方法&lt;code&gt;show&lt;/code&gt;后的状态，换言之，这些方法是&lt;strong&gt;惰性&lt;/strong&gt;的，它们只会修改&lt;code&gt;Scene&lt;/code&gt;对象的成员变量，这些成员变量真正生效要等到下一次场景的实际变换。除了上面的几个方法，还有两个方法对于场景（特别是此游戏）十分重要：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;scene&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;toMode&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;ADV&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;at&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;100&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;100&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;animate&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;600&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;t&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;show&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;&lt;code&gt;show&lt;/code&gt;方法，此方法会在不进行过渡的情况下对场景进行变换，也就是说，不改变背景，但可以修改场景的位置，这在某些场景下非常有用。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;animate&lt;/code&gt;方法，此方法参数是一个&lt;strong&gt;时间函数&lt;/strong&gt;和一个&lt;strong&gt;时间&lt;/strong&gt;，它可以为下一次变换指定一个动画，如果其被指定，则下一次&lt;code&gt;show&lt;/code&gt;的时候，背景的位置在移动两点之间将会有一段平滑的动画。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;scale&lt;/code&gt;方法，和&lt;code&gt;at&lt;/code&gt;类似，但提供的是缩放功能。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;toMode&lt;/code&gt;方法则是切换对话模式，这一点在下面会详细说明。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;除了以上的功能，我在实现背景初始化时，并非直接使用素材和场景左上角对齐的模式，而是令其中心对齐，这对于人类而言比较合理，但代价就是要去实时算&lt;code&gt;anchor&lt;/code&gt;和背景的位置。&lt;/p&gt;
&lt;h3&gt;人物&lt;/h3&gt;
&lt;p&gt;人物由&lt;code&gt;Character&lt;/code&gt;类生成，其比较复杂。一方面，它决定着人物自身的固有属性，比如对话时的字体、对话框背景等，另一方面它又组织着人物的资源，而人物的资源又和别的资源不太一样。这里我就详细说说这个类的具体实现。  &lt;/p&gt;
&lt;p&gt;说详细实现，不得不先提一下人物的素材，为了使一个人物能有生动的演出，最基本的方法就是对其立绘的姿态和表情进行变更，结合上面提到的惰性方法结合&lt;code&gt;show&lt;/code&gt;方法的模式，我显示一个人物立绘的脚本如下：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;vega&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;posture&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;普通&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;face&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;微笑&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;at&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;from&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;right&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;400&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;show&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这里的&lt;code&gt;posture&lt;/code&gt;方法定义了立绘姿态，&lt;code&gt;face&lt;/code&gt;定义表情，这两者将会在实际显示时先和上一次的状态做一次差分，只有真正有更新的时候才会进行实际的图层重绘。在更新时，对象先拼接出两个资源标识，然后通过这个标识去引擎获取对应的素材，素材分为两部分——一部分是躯体，其标识是&lt;code&gt;${resName}-${posture}.body&lt;/code&gt;，一部分是表情，标识是&lt;code&gt;${resName}-${posture}.${face}&lt;/code&gt;。  &lt;/p&gt;
&lt;p&gt;这里就要说起人物资源的详细定义了，根据Egret的资源规范，我将人物的每个姿势以及其表情集合拼合到了一张图内，然后将其资源命名为&lt;code&gt;${resName}-${posture}&lt;/code&gt;的模式，之后在这个资源对应的json内写好身体和各个表情的偏移：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;vege人物图&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2017_08_27a/2.png&amp;quot; /&gt;&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;frames&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;body&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
            &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;offY&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
            &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;sourceW&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;724&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
            &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;w&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;724&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
            &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;x&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
            &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;y&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
            &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;h&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1280&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
            &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;sourceH&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1280&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
            &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;offX&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
        &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;生气&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
            &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;offY&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;144&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
            &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;sourceW&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;136&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
            &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;w&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;136&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
            &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;x&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;764&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
            &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;y&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;324&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
            &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;h&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;95&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
            &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;sourceH&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;95&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
            &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;offX&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;289&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
        &lt;span class=&amp;quot;err&amp;quot;&gt;......&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
    &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;file&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;vega.png&amp;quot;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;通过这些定义，我便可以轻易得从资源图中获取到身体和表情的图像，并将它们绘制到对应的&lt;code&gt;body&lt;/code&gt;和&lt;code&gt;face&lt;/code&gt;子图层去。  &lt;/p&gt;
&lt;p&gt;这些操作实现了基本的姿势和表情切换，但如果直接切换又太过生硬，连若干年前GAL的演出标准都无法达到，为了达到GAL的底线，姿势或者表情变化时的&lt;code&gt;fade&lt;/code&gt;效果必须被实现。我实现了一个类&lt;code&gt;Transition&lt;/code&gt;，专门用来做这个效果，其可以传入之前和之后的两个图像，直接实现二者的交错&lt;code&gt;fade&lt;/code&gt;效果。  &lt;/p&gt;
&lt;p&gt;当然，除了主要角色，我们还可能有一些次要角色，这些角色并不需要姿势和表情的变化，而仅仅需要一张图作为基本立绘即可，对于这种角色，我在构造函数中设定了参数&lt;code&gt;multiPos&lt;/code&gt;和&lt;code&gt;multiFace&lt;/code&gt;来做细节控制，若设置为&lt;code&gt;false&lt;/code&gt;，则不会去进行姿势和表情的区分，而仅仅是绘制&lt;code&gt;resKey&lt;/code&gt;对应的基本图像：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;boy&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;GAL&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Character&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;npc.boy&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;少年&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;boy&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x0078a4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x0078a4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;cps&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;false&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;false&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;在姿势和表情之后，&lt;code&gt;at&lt;/code&gt;方法用于定义下一次显示的位置，不过和&lt;code&gt;Scene&lt;/code&gt;不同，它的两个参数是&lt;strong&gt;格点&lt;/strong&gt;而非&lt;strong&gt;像素&lt;/strong&gt;，这是由于立绘定位比较特殊，设计为格点模式比较方便，而格点的约束则写在&lt;code&gt;types.ts&lt;/code&gt;中。&lt;code&gt;from&lt;/code&gt;方法定义的是立绘从何处出现，这个和&lt;code&gt;Scene&lt;/code&gt;也不同，它实际上决定的是当立绘从无到有是、从某一侧的某个偏移边移动边逐渐显示的效果（这一点在游戏中处处都有体现），我认为这也是一个合格的GAL应该拥有的底线演出。  &lt;/p&gt;
&lt;p&gt;除了以上方法，&lt;code&gt;Character&lt;/code&gt;类同样提供了很多其它方法以供显示的细节控制：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;vega&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;size&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;large&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;opacity&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(.&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;5&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;face&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;生气&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;at&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;with&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;600&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;t&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;animate&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;().&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;show&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;vega&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;hide&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;code&gt;size&lt;/code&gt;方法的参数可以是&lt;code&gt;large&lt;/code&gt;、&lt;code&gt;middle&lt;/code&gt;、&lt;code&gt;small&lt;/code&gt;等，它本质上用于控制立绘的远近感，具体的效果也是在&lt;code&gt;types.ts&lt;/code&gt;中定义的，&lt;code&gt;opaticy&lt;/code&gt;用于修改下一次的透明度，&lt;code&gt;with&lt;/code&gt;则决定了姿势和表情变换的时间和时间函数，&lt;code&gt;animate&lt;/code&gt;决定在角色&lt;code&gt;size&lt;/code&gt;和&lt;code&gt;position&lt;/code&gt;变换时是否要有动画，&lt;code&gt;hide&lt;/code&gt;则是隐藏当前立绘，和&lt;code&gt;show&lt;/code&gt;相同，它也会有淡出的效果。  &lt;/p&gt;
&lt;p&gt;显示问题解决后便是角色的另一个重要行为——对话，角色的对话分为两种，一种是&lt;strong&gt;说&lt;/strong&gt;，一种是&lt;strong&gt;想&lt;/strong&gt;，并且对话形式也分为&lt;code&gt;ADV&lt;/code&gt;模式（对话框置底，一句话占据一个对话框，主要用于普通对话）和&lt;code&gt;NVL&lt;/code&gt;模式（对话框居中且大，一屏有多段对话，主要用于独白和描述），其脚本如下：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;scene&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;toMode&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;NVL&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;vega&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;think&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;我，我为什么会在这里？&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;scene&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ADV&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;NVL&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;boy&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;say&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;因为你是笨蛋啊。&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;vega&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;say&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;我才不是！&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;vega&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;think&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;这家伙是谁啊，这么没礼貌......&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;脚本写起来非常简洁优雅，而这简洁的背后是大量的工作，这工作依赖于另两个类——&lt;code&gt;ADVDialog&lt;/code&gt;和&lt;code&gt;NVLDialog&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;对话框&lt;/h3&gt;
&lt;p&gt;对话框分为&lt;code&gt;ADVDialog&lt;/code&gt;和&lt;code&gt;NVLDialog&lt;/code&gt;，这两个都继承自基类&lt;code&gt;Dialog&lt;/code&gt;，其核心方法有两个：&lt;code&gt;say: (id: string, words: string) =&amp;gt; TSPScriptResult&lt;/code&gt;和&lt;code&gt;think: (id: string, words: string) =&amp;gt; TSPScriptResult&lt;/code&gt;，它们分别对应&lt;code&gt;Character&lt;/code&gt;类的&lt;code&gt;say&lt;/code&gt;和&lt;code&gt;think&lt;/code&gt;方法。  &lt;/p&gt;
&lt;p&gt;在实际的调用链中，首先执行&lt;code&gt;Character&lt;/code&gt;对象的&lt;code&gt;say&lt;/code&gt;方法，然后再内部通过&lt;code&gt;Scene&lt;/code&gt;对象中的&lt;code&gt;say&lt;/code&gt;方法将该对象的&lt;code&gt;id&lt;/code&gt;和当前说的话&lt;code&gt;words&lt;/code&gt;传递给&lt;code&gt;Dialog&lt;/code&gt;类的&lt;code&gt;say&lt;/code&gt;方法：  &lt;/p&gt;
&lt;p&gt;vega.say(&amp;apos;蛤蛤&amp;apos;);&lt;br /&gt;
↓&lt;br /&gt;
scene.say(&amp;apos;vega&amp;apos;, &amp;apos;蛤蛤&amp;apos;);&lt;br /&gt;
↓&lt;br /&gt;
dialog.say(&amp;apos;vega&amp;apos;, &amp;apos;蛤蛤&amp;apos;);  &lt;/p&gt;
&lt;p&gt;最后对话框的绘制和文字的显示实际上由&lt;code&gt;dialog&lt;/code&gt;完成，而对话框的样式与当前文本的样式，一方面由&lt;code&gt;types.ts&lt;/code&gt;中定义的静态变量决定，比如文本偏移、字体大小，另一方面则在&lt;code&gt;Character&lt;/code&gt;对象被构造时、由其构造函数的参数决定，比如&lt;code&gt;cps&lt;/code&gt;（每秒显示多少个字符）、文字颜色等，这些变量被存在一个全局单例中，使用时直接从中拉取。  &lt;/p&gt;
&lt;p&gt;在绘制时，两种对话框的形式截然不同。对于&lt;code&gt;ADVDialog&lt;/code&gt;，实际上是先绘制底层的对话框图片，然后在直接绘制姓名，然后在文本区启动文本绘制动画，动画则是利用&lt;code&gt;requestAnimationFrame&lt;/code&gt;加上和绘制开始的时间差来实现的，加上一个快速结束动画的方法，便可以实现功能。  &lt;/p&gt;
&lt;p&gt;对于&lt;code&gt;NVLDialog&lt;/code&gt;，显示情况就复杂得多。由于可以容纳多段对话，需要考虑的问题就有很多：首先是名字和文本的排版、包含多行文本如何定位的问题，涉及到这个问题，就必须引入各种变量来保存位置信息；其次是清屏的问题，也就是判断文本将会超出时清空当前对话框，并在下一个对话框中显示新内容，这本质上是一个预判的问题，就是必须在绘制前先判断是否会超出，然后再按需绘制。这两个问题只能通过无尽的小学数学和&lt;strong&gt;文本测量&lt;/strong&gt;来解决，好在并不是特别复杂，最终都获取了比较完美的解决。&lt;/p&gt;
&lt;h3&gt;选择支&lt;/h3&gt;
&lt;p&gt;选择支是GAL最基本也是最核心的操作，本质上就是一个一个的按钮，每个按钮都有自己的背景图片和文本还有回调，而文本和容器有可能有不同的样式，但这些样式在大多数状况下又是中规中矩、基本相同的。所以我提供了两种方式——基本模式和用户自定义模式。  &lt;/p&gt;
&lt;p&gt;在基本模式中，用户可以只给一个文本或背景图、加上一个回调便可定义一个选项，像是这样：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;branch&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;open&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;([&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;content&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;成神，我不做人啦！&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;callback&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;loadAndNext&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;god&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;content&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;不，我想回归那平凡的生活&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;callback&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;loadAndNext&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;human&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;]);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;在这种状况下，选择支所有的属性都会又&lt;code&gt;types.ts&lt;/code&gt;中的一个静态变量控制，不过在此处还可以传入一个&lt;code&gt;autoClose&lt;/code&gt;参数，用于确定选择完成后是否应当直接关闭选择支，其默认为&lt;code&gt;true&lt;/code&gt;，当有特殊需求时可以将其设为&lt;code&gt;false&lt;/code&gt;；另外还有一个参数&lt;code&gt;eachTransform&lt;/code&gt;，可以为每一个选项指定同样的属性，这些属性将会覆盖默认属性。  &lt;/p&gt;
&lt;p&gt;而在自定义模式时，每个选择支中的&lt;code&gt;transform&lt;/code&gt;属性便会生效，用户可以通过此精确控制每个分支的一个属性，来达到一些特殊的效果：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;branch&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;custom&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;().&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;open&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;([&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;content&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;我是好人&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;callback&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;loadAndNext&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;good&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;bg&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;transform&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;200&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;100&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;120&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;200&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;rotation&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;30&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;content&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;我是坏人&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;callback&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;interpreter&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;load&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;bad&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;bg&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;2&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;transform&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;200&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;100&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;240&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;400&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;]);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;再配合类似于上面类的&lt;code&gt;animate&lt;/code&gt;和&lt;code&gt;from&lt;/code&gt;方法，便可以实现丰富的出现动画，不过现在每个选择支的单独动画尚未完成，做起来也不难，不过我觉得没有必要——选择支一起出现，黄油都是这么干的。&lt;/p&gt;
&lt;h3&gt;音乐&lt;/h3&gt;
&lt;p&gt;由于此次游戏没有用到音乐，所以只是做了一个基本的BGM播放，按理说BGM和效果音应该是分轨的，效果音也可以多音轨，基本使用方法如下，不再过多介绍：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;bgm&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;play&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;bgm-home&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;bgm&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;pause&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;bgm&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;resume&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;其他&lt;/h3&gt;
&lt;p&gt;其他一些游戏中的演出，比如&lt;strong&gt;标题&lt;/strong&gt;、&lt;strong&gt;轮回画面&lt;/strong&gt;、&lt;strong&gt;银河&lt;/strong&gt;都是该游戏特例化的对象，业务耦合度过深，基本不可能抽象，对于这些对象，我都是直接在扩展外另写几个类，并按照解释器要求的形式进行方法的编写，使其能够无缝融入剧本，所以这几个类可以看做是这个GAL扩展的扩展。这几个类除了银河之外没什么好说的，难度基本都在玄学调动画而非设计上，银河我会在下面单独说明。&lt;/p&gt;
&lt;h2&gt;演出&lt;/h2&gt;
&lt;p&gt;上面的实现细节其实已经说了大部分演出的原理，这里还要单独说的就是这个竖屏的问题。由于游戏是移动端游戏，并且移动端浏览器千奇百怪，顶部的bar很可能无法隐藏，如此情况下纵向可视区域就非常之效了。所以即便是违背传统GAL的模式，我还是选择了竖屏。竖屏的话就有两个问题——一个问题是演出画布过窄，很难做到多人同台；另一个问题则是用户横屏的问题，比如在B站IPAD客户端内就是强制横屏。&lt;/p&gt;
&lt;h3&gt;适配&lt;/h3&gt;
&lt;p&gt;对于画布过窄的问题，我是通过演出的一个trick来解决的。读者是否记得，在游戏中经常出现人物和场景同步移动的情景？这就是我的解决方案——既然画布过窄，我就变相扩展画布，将画布扩展到屏幕之外，然后通过移动屏幕中显示的内容来决定现在舞台上的对象。这个操作的核心是在&lt;code&gt;Scene&lt;/code&gt;的&lt;code&gt;at&lt;/code&gt;方法后加上第三个参数，这个参数是一个bool值，决定下一次移动是否要背景带着人物一起移动，这也就是一开始说的那个&lt;code&gt;main&lt;/code&gt;子图层存在的意义，当一起移动时，移动的是&lt;code&gt;main&lt;/code&gt;图层，否则移动的是&lt;code&gt;bg&lt;/code&gt;图层自身。  &lt;/p&gt;
&lt;p&gt;而对于强制竖屏的问题，我利用的是Egret自身的方法：&lt;a href=&amp;quot;http://developer.egret.com/cn/2d/screenAdaptation/explanation&amp;quot;&gt;缩放模式和旋转模式说明&lt;/a&gt;。我将&lt;code&gt;data-orientation&lt;/code&gt;设置为&lt;code&gt;portrait&lt;/code&gt;来强制竖屏，然后选用&lt;code&gt;fixedWidth&lt;/code&gt;这种缩放模式来进行不同分辨率的适配，加之在渲染时我的图层实时计算，基本完美解决了适配问题。&lt;/p&gt;
&lt;h3&gt;银河&lt;/h3&gt;
&lt;p&gt;银河也就是最后的CG和一系列操作，前面的CG动画就不说了，调参罢了。值得说明的主要是留言板和仰望星空的实现。  &lt;/p&gt;
&lt;p&gt;留言板其实就是一条条文字数据，我拿到数据后把这些文字画到画布上而已。但为了在均匀铺满星空的同时又尽量避免那种人工的规则感，我先将星空那张图的画布区域分割成了&lt;strong&gt;7x7&lt;/strong&gt;块，然后再每一块中放置一条留言，每条留言的位置会在这个区块内进行随机，同时随机的还有留言的&lt;code&gt;scale&lt;/code&gt;，这样就可以造成一种不规则却又铺满的感觉（虽然还是有点假）。  &lt;/p&gt;
&lt;p&gt;仰望星空这一个操作显然是用了陀螺仪，在做这一个功能并将其作为查看留言的唯一方式时我就已经预料到了争议，但为了一些情怀的仪式感，我还是这么做了。其基本原理很简单，其实就是在那个&lt;strong&gt;仰望星空&lt;/strong&gt;的按钮出现时，程序就开始不断记录用户当前陀螺仪的三个角度偏移值，当用户按下按钮时，程序判断当前的偏移值是否在合理范围内，如果合理，则以当前位置为起点开始进行观测，横向依赖的是&lt;code&gt;alpha&lt;/code&gt;的变换，纵向则依赖于&lt;code&gt;beta&lt;/code&gt;。这里有一个坑是不同的系统和浏览器对陀螺仪角度的解释不一样，只能UA判断做适配了= =。  &lt;/p&gt;
&lt;p&gt;观测时移动的背景实际上是二维图像进行的伪全景模拟，而并非三维的&lt;strong&gt;Skybox&lt;/strong&gt;，这一点我深表遗憾——毕竟为了这样一个功能再将&lt;strong&gt;Egret3D&lt;/strong&gt;引入，还是得不偿失，而且也由于时间原因无暇再改，这一点下一次会有所计划。  &lt;/p&gt;
&lt;p&gt;另外后端临时和我商量加入了一个留言板实时更新的功能，这是一个新的功能，基于弹幕系统的实时推送，基于&lt;strong&gt;WebSocket&lt;/strong&gt;。其难点在于我每次收到后端通知后，总是要找到用户当前观看区域中、随机的一块，将其中的留言移除再更新，以此保证用户的基本体验。这个功能在此处只是做实验，后续活动会有更大应用。&lt;/p&gt;
&lt;h2&gt;彩蛋&lt;/h2&gt;
&lt;p&gt;这次彩蛋比较少，一个是在用户留言不足、或是一定概率下，星空留言的一部分将由我mock的数据填充，这些数据都是一些ACG作品中的人物以及其名言。另一个就是我给我女朋友的专属菜单啦，我们只能互相匹配到对方，并且结尾有一段专门的彩蛋剧情，虐下狗（逃  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;彩蛋&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2017_08_27a/3.jpg&amp;quot; /&gt;&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Sunday, 27 Aug 2017 00:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2017.08.27 00:00:article/Skill-2017_08_27_a</guid>
<category>前端</category>
<category>Bilibili</category>
<category>七夕</category>
<category>Egret</category>
<category>HTML5</category>
<category>Galgame</category>
</item>

<item>
<title>BML2017主视觉技术剖析</title>
<link>http://dtysky.moe/article/Skill-2017_06_12_a</link>
<description>&lt;p&gt;今年&lt;a href=&amp;quot;https://bml.bilibili.com/2017/index.html&amp;quot;&gt;BML宣发页面的主视觉&lt;/a&gt;由我负责，使用了比较新的技术栈，包含大量动画技巧以及视频运用，下面大概分析一下这些技术和遇到的一些坑。  &lt;/p&gt;
&lt;h2&gt;对IE的处理&lt;/h2&gt;
&lt;p&gt;将这一点放在文章之首是为了充分表达我个人（应该也是世界上所有有追求的前端工程师）对IE的厌恶，经过一番较为艰难的沟通，我们终于就“在此活动干掉IE全家”达成了共识，所以如果用IE访问此页面，你应该会看到一个配有33娘背景图的页面，其上的文字敦促你去下载现代浏览器，我觉得这和苹果强推HTTPS一样，有一定影响力的公司应当为业界的技术革新产生一些贡献。  &lt;/p&gt;
&lt;p&gt;为了实现这一点，我用了两个手段，对于IE9以下（包括自身）的IE，可以在HTML加入一段这样的注释&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;c&amp;quot;&gt;&amp;lt;!--[if IE]&amp;gt;&lt;/span&gt;
&lt;span class=&amp;quot;c&amp;quot;&gt;&amp;lt;div class=&amp;quot;ie-must-die&amp;quot;&amp;gt;&lt;/span&gt;
&lt;span class=&amp;quot;c&amp;quot;&gt;  &amp;lt;div class=&amp;quot;ie-container&amp;quot;&amp;gt;&lt;/span&gt;
&lt;span class=&amp;quot;c&amp;quot;&gt;    &amp;lt;h1&amp;gt;&lt;/span&gt;
&lt;span class=&amp;quot;c&amp;quot;&gt;      您正在使用IE&lt;/span&gt;
&lt;span class=&amp;quot;c&amp;quot;&gt;    &amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class=&amp;quot;c&amp;quot;&gt;    &amp;lt;p&amp;gt;&lt;/span&gt;
&lt;span class=&amp;quot;c&amp;quot;&gt;      少女H也曾依赖过IE，借此来窥探这个世界。&lt;/span&gt;
&lt;span class=&amp;quot;c&amp;quot;&gt;      &amp;lt;br /&amp;gt;&lt;/span&gt;
&lt;span class=&amp;quot;c&amp;quot;&gt;      但IE毕竟已然老去，早已无法适应这个绚丽的时代。&lt;/span&gt;
&lt;span class=&amp;quot;c&amp;quot;&gt;      &amp;lt;br /&amp;gt;&lt;/span&gt;
&lt;span class=&amp;quot;c&amp;quot;&gt;      他的职责既已完成，不如就任其安然睡去吧——&lt;/span&gt;
&lt;span class=&amp;quot;c&amp;quot;&gt;      &amp;lt;br /&amp;gt;&lt;/span&gt;
&lt;span class=&amp;quot;c&amp;quot;&gt;      毕竟，这个时代是属于现代浏览器的。&lt;/span&gt;
&lt;span class=&amp;quot;c&amp;quot;&gt;    &amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class=&amp;quot;c&amp;quot;&gt;    &amp;lt;a href=&amp;quot;https://browsehappy.com&amp;quot; target=&amp;quot;_blank&amp;quot;&amp;gt;&lt;/span&gt;
&lt;span class=&amp;quot;c&amp;quot;&gt;      您可以点击此处，下载Chrome后再次打开本页面。&lt;/span&gt;
&lt;span class=&amp;quot;c&amp;quot;&gt;    &amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class=&amp;quot;c&amp;quot;&gt;  &amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class=&amp;quot;c&amp;quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class=&amp;quot;c&amp;quot;&gt;&amp;lt;![endif]--&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这段代码在IE中会取消注释并插入到DOM中，如果想判断IE版本，可以在&lt;code&gt;if&lt;/code&gt;条件处修改，比如要在IE8以下显示，可以写&lt;code&gt;if lt IE9&lt;/code&gt;。  &lt;/p&gt;
&lt;p&gt;但这种方法毕竟只能支持IE10以下的版本，诚然IE10以上的IE已然友善许多，但还是有很多奇怪的坑（尤其是有很多动画的时候），这时候就需要别的方法来对其进行屏蔽。由于我使用的是React，所以只需要设定一个状态，并更具这个状态决定要渲染的DOM即可：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// config.ts&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;isIE&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!!&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;window&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ActiveXObject&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;||&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;ActiveXObject&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;in&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;window&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;c1&amp;quot;&gt;// App.tsx -&amp;gt; render&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;config&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;isIE&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
    &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;div&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;className&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cx&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;ie-must-die&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)}&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;
    &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;/div&amp;gt;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;Preload&lt;/h2&gt;
&lt;h3&gt;结构&lt;/h3&gt;
&lt;p&gt;IE说完，下面切到正文。BML主视觉属于大型活动页面，视频和图像资源比较多，在对首屏展示有着严格要求的情况下，预加载是必须的。我写了一个&lt;code&gt;ResourceManager&lt;/code&gt;单例来管理所有资源，它构造时接受一个数组，数组中的每个对象可用参数配置&lt;code&gt;src&lt;/code&gt;、&lt;code&gt;type&lt;/code&gt;、是否需要&lt;code&gt;preload&lt;/code&gt;以及preload时的权重&lt;code&gt;weight&lt;/code&gt;，除此之外，还可以设置一个超时时间&lt;code&gt;timeout&lt;/code&gt;来强制一定时间内加载完成（即使资源尚未真正加载完），这是为了确保访问体验的下限。  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;world&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;src&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;resSrc&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;world&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;video&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;preload&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;isPC&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;weight&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;20&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;guide-img&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;src&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;resSrc&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;guideImg&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;image&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;

&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;resourceManager&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;ResourceManager&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;resources&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;8000&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;当单例初始化结束后，便可以在任何地方调用它的&lt;code&gt;load&lt;/code&gt;方法来进行加载，在期间可以用&lt;code&gt;loadDone&lt;/code&gt;这个访问器来确定是否已经加载完成。除此之外，我还定义了一个&lt;code&gt;registerOnProgress&lt;/code&gt;方法来注册一个回调，此回调在加载进度变更的时候会被调用，它可以灵活得被用于和React组件结合刷新视图：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;resourceManager&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;registerOnProgress&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;((&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;progress&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setState&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;progress&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}));&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;resourceManager&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;load&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;而在使用时，只需要调用&lt;code&gt;getSrc(name)&lt;/code&gt;方法即可拿到资源的url。&lt;/p&gt;
&lt;h3&gt;数据结构&lt;/h3&gt;
&lt;p&gt;加载队列中的每个资源都有相同的的数据结构，其为：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;interface&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;IResourceElement&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;extends&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;IResourceEntry&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;preload?&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;boolean&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;src&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// image or video&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;image&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;video&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;weight?&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;element?&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;HTMLImageElement&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;HTMLVideoElement&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;progress?&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;其中&lt;code&gt;progress&lt;/code&gt;即为该资源的加载进度，取&lt;code&gt;0 ~ 1&lt;/code&gt;，当不需要预加载时，其恒定为1。  &lt;/p&gt;
&lt;p&gt;当资源加载进度变更时，通过&lt;code&gt;progress&lt;/code&gt;访问器可以获取到资源的实际进度，其基本思路就是遍历所有资源对象，将每一个的&lt;code&gt;progress&lt;/code&gt;乘以权重&lt;code&gt;weight&lt;/code&gt;相加，最后除以权重和。&lt;/p&gt;
&lt;h3&gt;图像预加载&lt;/h3&gt;
&lt;p&gt;图像预加载比较简单，只要创建&lt;code&gt;Image&lt;/code&gt;对象，绑上&lt;code&gt;onload&lt;/code&gt;事件并设置&lt;code&gt;src&lt;/code&gt;，在事件执行时将其对应的&lt;code&gt;progress&lt;/code&gt;设为1即可。  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// element为一个Image对象&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;element&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;resources&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;element&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;element&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;onload&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;resources&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;progress&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;loaded&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;onProgress&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;progress&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;element&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;src&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;resources&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;src&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;视频预加载&lt;/h2&gt;
&lt;p&gt;视频预加载比起图像麻烦的一点在于，我们无法通过向&lt;code&gt;new Video()&lt;/code&gt;这样的方法创建一个视频对象然后如图像那样处理，而是必须用&lt;code&gt;document.createElement(&amp;apos;video&amp;apos;)&lt;/code&gt;并用其API，此外，为了兼容，还必须将其插入到DOM中来保证其正常加载，于是代码就变成了这样：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// element为视频对象&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;resources&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;element&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;canplaythrough&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;handleVideoProgress&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;resources&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;element&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;muted&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;muted&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;resources&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;element&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;preload&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;auto&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;resources&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;element&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;src&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;resources&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;src&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;resources&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;element&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;style&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;position&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;fixed&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;resources&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;element&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;style&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;transform&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;scale(-10000)&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;resources&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;element&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;style&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;0&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;resources&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;element&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;style&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;0&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;nb&amp;quot;&gt;document&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;body&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;appendChild&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;resources&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;element&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;resources&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;element&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;play&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;即，将视频绑事件，插入到DOM中并使其不可见，然后播放它。我这里绑的事件是&lt;code&gt;canplaythrough&lt;/code&gt;，这个事件将会在每一次&lt;code&gt;视频可以一段时间&lt;/code&gt;时触发，这个机制也影响到了对应回调函数的实现。  &lt;/p&gt;
&lt;p&gt;视频和图像不同，我们可以拿到其总长和加载期间&lt;code&gt;已经加载了多少&lt;/code&gt;：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 总时长&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;duration&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;resources&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;element&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;duration&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;c1&amp;quot;&gt;// 已经加载的时长&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;buffered&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;resources&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;element&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;buffered&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;end&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;通过这二者便可以算出加载的比例，也就是&lt;code&gt;progress&lt;/code&gt;的值：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;resources&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;progress&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;buffered&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;resources&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;element&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;duration&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;算完后触发&lt;code&gt;onProgress&lt;/code&gt;回调，基本就完成了，别忘了在加载结束时将&lt;code&gt;element&lt;/code&gt;从DOM中移除www&lt;/p&gt;
&lt;h2&gt;Guide-首屏&lt;/h2&gt;
&lt;p&gt;前置处理做完，接下来便是视图的展示了。  &lt;/p&gt;
&lt;p&gt;首先是首屏，也就是一进来的那段酷炫的特效，其原理很简单，其实就是背景视频 + SVG + 几个DOM。下面分PC和移动两个平台来阐释其中的重点。  &lt;/p&gt;
&lt;h3&gt;PC&lt;/h3&gt;
&lt;p&gt;PC的H5视频支持基本完美，需要注意的只有。  &lt;/p&gt;
&lt;p&gt;而这视频虽然有两段，其实是一个视频，只不过我监听了&lt;code&gt;timeupdate&lt;/code&gt;方法，在视频播放进度变更的时候设置不同的状态来确保新视图的渲染，跳过功能也不过是修改&lt;code&gt;video.currentTime&lt;/code&gt;而已。这里比较需要注意的是样式问题，因为是将视频作为背景，所以我们需要一个类似于&lt;code&gt;background-size: cover&lt;/code&gt;的效果，当知道原始视频宽高比的情况下，样式可以这么写：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nc&amp;quot;&gt;.cover-video&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;o&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;media&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;min&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;aspect&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ratio&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;16&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;9&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;nb&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;100%&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;nb&amp;quot;&gt;height&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;auto&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;o&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;media&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;aspect&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ratio&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;16&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;9&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;nb&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;auto&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;nb&amp;quot;&gt;height&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;100%&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;nb&amp;quot;&gt;position&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;absolute&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;nb&amp;quot;&gt;top&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;50%&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;nb&amp;quot;&gt;left&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;50%&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;transform&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;translate&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;m&amp;quot;&gt;-50%&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;,-&lt;/span&gt;&lt;span class=&amp;quot;m&amp;quot;&gt;50%&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;至于SVG动画，由于一开始还试图考虑兼容IE，这里使用的是&lt;a href=&amp;quot;https://github.com/maxwellito/vivus&amp;quot;&gt;Vivus&lt;/a&gt;库，不过它也有一些问题，能用原生的SVG动画还是原生吧。  &lt;/p&gt;
&lt;h3&gt;移动端&lt;/h3&gt;
&lt;p&gt;移动端的背景视频播放绝对是超级大坑，由于移动平台设备奇多，良莠不齐，所以我付出很多努力后，最后还是在指示下将其取消了，但我研究出的这个方法应该还是可以兼容绝大多数现代设备的，所以就将经验写在这里吧。  &lt;/p&gt;
&lt;p&gt;第一个问题是有些版本的设备不支持背景视频，很多设备即使支持，浏览器也会劫持视频，对于这些设备和浏览器我建议直接放弃治疗，禁掉视频吧。比如5.0以下的安卓啦，移动端的QQ浏览器（不包括QQ和微信的内置浏览器以及HD版本）。  &lt;/p&gt;
&lt;p&gt;对于iOS设备，10是可以直接支持背景视频的（Safari或者基于原生Webview的），但8和9需要一个插件来支持&lt;a href=&amp;quot;https://github.com/bfred-it/iphone-inline-video&amp;quot;&gt;iphone-inline-video&lt;/a&gt;。此外，还需要在&lt;code&gt;video&lt;/code&gt;标签上加上&lt;code&gt;playsInline&lt;/code&gt;属性。  &lt;/p&gt;
&lt;p&gt;对于QQ和微信内置浏览器，只需要在&lt;code&gt;video&lt;/code&gt;标签上再加上两个属性即可：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;video&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;webkit-playsinline&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;true&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;video&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;x5-video-player-type&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;h5&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;下面给出我用的几个平台的判据：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;isQQ&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;sr&amp;quot;&gt;/TBS/&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;test&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;navigator&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;userAgent&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;isIos&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;sr&amp;quot;&gt;/iPad|iPhone|iPod/&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;test&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;navigator&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;userAgent&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;isIpad&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;sr&amp;quot;&gt;/iPad/&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;test&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;navigator&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;userAgent&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;isAndroid&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;sr&amp;quot;&gt;/Android/&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;test&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;navigator&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;userAgent&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;isQQBroswer&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;isIpad&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;sr&amp;quot;&gt;/MQQBrowser/&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;test&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;navigator&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;userAgent&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;isQQ&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;getAndroidVersion&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ua&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;u&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;ua&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;||&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;navigator&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;userAgent&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;match&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;u&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;match&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;sr&amp;quot;&gt;/Android\s([0-9\.]*)/&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;match&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;parseFloat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;match&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;])&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;Home-分会场选择&lt;/h2&gt;
&lt;p&gt;酷炫的特技后是分会场选择页面，此页面结构虽看似简单，但其实是最耗性能的一页，这也怪我当时想的太简单全用DOM动画吧（用canvas可能麻烦点但性能应该会好很多）。  &lt;/p&gt;
&lt;p&gt;该页面主要由背景的&lt;code&gt;星轨&lt;/code&gt;、前面的&lt;code&gt;无限轮播图&lt;/code&gt;和其他一些小按钮构成。那些小按钮暂且不论，星轨和轮播图的动画相对复杂，值得单独拿出来说一说。  &lt;/p&gt;
&lt;h3&gt;星轨&lt;/h3&gt;
&lt;p&gt;星轨分两部分，一个是在完全进入页面后的行星转动动画，一个是在从别的页面切换回来时的逐环扩散效果。  &lt;/p&gt;
&lt;h4&gt;转动&lt;/h4&gt;
&lt;p&gt;前者比较简单，用无限循环的&lt;code&gt;keyframes&lt;/code&gt;实现即可，要注意这里最好让行星自己动，不要带轨道一起，性能会好点。但既然要行星自己动，那么行星自己的&lt;code&gt;transform-origin&lt;/code&gt;就要特别注意，其需要注意的样式为：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nc&amp;quot;&gt;.star-item&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;transform&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;origin&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;star&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;size&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;2&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rail&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;size&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rail&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;star&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;size&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;nb&amp;quot;&gt;left&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rail&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;size&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;star&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;size&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;nb&amp;quot;&gt;top&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;star&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;size&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rail&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;其中&lt;code&gt;star-size&lt;/code&gt;是行星直径，&lt;code&gt;rail-size&lt;/code&gt;是轨道直径，&lt;code&gt;rail-width&lt;/code&gt;是轨道的border宽度。这个再加上&lt;code&gt;keyframes&lt;/code&gt;，整个效果就出来了（当然轨道居中什么的还有些额外样式，很简单，这里不多说）。  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;@keyframes&lt;/span&gt; &lt;span class=&amp;quot;nt&amp;quot;&gt;rotate-stars&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;n&amp;quot;&gt;from&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;transform&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;rotate&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;m&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;deg&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;translateZ&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;m&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);}&lt;/span&gt;
  &lt;span class=&amp;quot;n&amp;quot;&gt;to&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;transform&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;rotate&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;m&amp;quot;&gt;360&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;deg&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;translateZ&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;m&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;nc&amp;quot;&gt;.star-item&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;animation&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;rotate&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;stars&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;step&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;4s&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;linear&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;0s&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;infinite&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;normal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h4&gt;扩散&lt;/h4&gt;
&lt;p&gt;扩散的效果就比较不友好了，由于我想使用纯CSS实现，所以又不想借助于&lt;code&gt;ReactCssTransitionGroup&lt;/code&gt;，所以又只能用&lt;code&gt;keyframes&lt;/code&gt;，这里利用了它的一个特性，就是&lt;code&gt;keyframes&lt;/code&gt;（所在的&lt;code&gt;class&lt;/code&gt;）被新加入到DOM上时，其必定会被触发一遍，这一点在带有这种样式的DOM被新插入页面中时也一样，利用这一点，我们就可以使用&lt;code&gt;keyframes&lt;/code&gt;加上不同的&lt;code&gt;插入时点&lt;/code&gt;、或者利用其本身的&lt;code&gt;阶段特性&lt;/code&gt;，来实现这样的动画效果。我选用的是后者，九个星轨实际上是九个DOM，每一个都拥有自己独立的动画，而它们是同时插入到页面中的，也拥有同样的时间：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nc&amp;quot;&gt;.star-rail&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;animation&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;1.6s&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;~&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;home-star-@{step}&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;ease&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;out&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;然后针对不同的&lt;code&gt;step&lt;/code&gt;写动画：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;@keyframes&lt;/span&gt; &lt;span class=&amp;quot;nt&amp;quot;&gt;home-star-0&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;m&amp;quot;&gt;0%&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;opacity&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;}&lt;/span&gt;
  &lt;span class=&amp;quot;m&amp;quot;&gt;5%&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;opacity&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;o&amp;quot;&gt;......&lt;/span&gt;

&lt;span class=&amp;quot;nv&amp;quot;&gt;@keyframes&lt;/span&gt; &lt;span class=&amp;quot;nt&amp;quot;&gt;home-star-5&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;m&amp;quot;&gt;0%&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;opacity&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;}&lt;/span&gt;
  &lt;span class=&amp;quot;m&amp;quot;&gt;40%&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;opacity&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;}&lt;/span&gt;
  &lt;span class=&amp;quot;m&amp;quot;&gt;60%&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;opacity&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;o&amp;quot;&gt;......&lt;/span&gt;

&lt;span class=&amp;quot;nv&amp;quot;&gt;@keyframes&lt;/span&gt; &lt;span class=&amp;quot;nt&amp;quot;&gt;home-star-9&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;m&amp;quot;&gt;0%&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;opacity&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;}&lt;/span&gt;
  &lt;span class=&amp;quot;m&amp;quot;&gt;80%&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;opacity&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;}&lt;/span&gt;
  &lt;span class=&amp;quot;m&amp;quot;&gt;100%&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;opacity&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;不错，核心就是肝疼的微调，也正因为此，渲染性能消耗变得很高。  &lt;/p&gt;
&lt;h3&gt;轮播&lt;/h3&gt;
&lt;p&gt;轮播和星轨一样，其实也是分两部分的，入场时是一个额外的DOM，结束后展示的是另外一个DOM，只不过这两个DOM切换时完全重合，所以造成了一种无缝的错觉。&lt;/p&gt;
&lt;h4&gt;入场&lt;/h4&gt;
&lt;p&gt;入场和星轨的入场一样，也是利用&lt;code&gt;keyframes&lt;/code&gt;分阶段实现的，这个比较简单，基本就是微调样式，对四个场子的bg进行&lt;code&gt;translate&lt;/code&gt;和&lt;code&gt;scale&lt;/code&gt;、&lt;code&gt;margin&lt;/code&gt;的变换，在这里我强行开了&lt;code&gt;translateZ&lt;/code&gt;来进行GPU加速，但在一些机器上还是会有些卡和抖动，这个大概是&lt;code&gt;transform&lt;/code&gt;变换中的渲染性能消耗和计算出的小数导致。  &lt;/p&gt;
&lt;p&gt;在入场完成后，js这边控制状态切换到入场后的DOM，实现无缝切换。&lt;/p&gt;
&lt;h4&gt;入场后&lt;/h4&gt;
&lt;p&gt;入场后会有一段前景左侧图片和右侧文字的小动画，以及其他一些小按钮的动画，这些都是&lt;code&gt;keyframes&lt;/code&gt;实现的。同时，在进行轮播的左右切换之时你会发现前景和背景、前景的图像和文字之间都有明显的视差（即不是同步移动），这个也是通过添加和删除&lt;code&gt;keyframes&lt;/code&gt;相关的样式实现的。  &lt;/p&gt;
&lt;p&gt;而轮播自身，因为是无限循环的轮播，所以每次切换结束总是要将移动侧边缘的DOM删除后插入到另一边，这也导致无法用纯CSS实现，必须上JS，于是这就涉及到了React中的JS动画实现问题。在这里我选用了和&lt;a href=&amp;quot;https://github.com/chenglou/react-motion&amp;quot;&gt;ReactMotion&lt;/a&gt;相似的原理，自己写了一个&lt;code&gt;Animation&lt;/code&gt;组件，用于实现需求的动画。&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;Animation&lt;/span&gt;
    &lt;span class=&amp;quot;na&amp;quot;&gt;enable=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;{enableAnimation}&lt;/span&gt;
    &lt;span class=&amp;quot;na&amp;quot;&gt;animation=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;{next.styles}&lt;/span&gt;
    &lt;span class=&amp;quot;na&amp;quot;&gt;duration=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;{duration}&lt;/span&gt;
    &lt;span class=&amp;quot;na&amp;quot;&gt;easing=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;{easing}&lt;/span&gt;
    &lt;span class=&amp;quot;na&amp;quot;&gt;onEnd=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;{this.handleAnimationEnd}&lt;/span&gt;
&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    styles =&amp;gt; (
        ......
    )
&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;/Animation&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;此组件用法如上，&lt;code&gt;enable&lt;/code&gt;表示是否允许动画；&lt;code&gt;duration&lt;/code&gt;是起始到终止态的时间；&lt;code&gt;easing&lt;/code&gt;是缓动函数的类型，这里我们使用了&lt;a href=&amp;quot;https://github.com/d3/d3-ease&amp;quot;&gt;d3-ease&lt;/a&gt;和&lt;a href=&amp;quot;https://github.com/d3/d3-interpolate&amp;quot;&gt;d3-interpolate&lt;/a&gt;来实现起始到终止态之间的插值；&lt;code&gt;onEnd&lt;/code&gt;是一个回调，其在每次动画结束后触发；最后是这个&lt;code&gt;animation&lt;/code&gt;属性，它是整个组件的核心，用于动画的触发，触发方式是要求两次传入的值不同，这个值可以是一个数字、一个数组或是一个对象。触发动画后，组件的&lt;code&gt;children&lt;/code&gt;（要求是一个函数）将会每个一段时间（取决于&lt;code&gt;requestAnimationFrame&lt;/code&gt;）接收到一个经过插值计算后的值，然后通过这个值进行组件的渲染。  &lt;/p&gt;
&lt;p&gt;有了这个组件，轮播的动画逻辑就很清晰了：用户每次操作时算出每个场次的下一个位置，扔到一个数组里（这里是&lt;code&gt;next.styles&lt;/code&gt;），然后根据每一次的&lt;code&gt;styles&lt;/code&gt;进行渲染，并监听&lt;code&gt;onEnd&lt;/code&gt;事件，在动画结束后完成DOM的删除和插入即可。&lt;/p&gt;
&lt;h2&gt;World-地域选择和分享&lt;/h2&gt;
&lt;p&gt;第三屏是地域选择和分享，其可以划分为两个主要部分——“地区选择器”和“分享图片生成”。  &lt;/p&gt;
&lt;h3&gt;地区选择器&lt;/h3&gt;
&lt;p&gt;地区选择器说来也简单，其实就是三个关联选择框，其核心是数据来源。这方面我是选择了网上找到的一个地区区号和名字的列表的xml文件，之后自己转成了两个json文件&lt;code&gt;locationListTable.json&lt;/code&gt;和&lt;code&gt;locationLookupTable.json&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;locationListTable&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;11&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;北京&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;children&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;1101&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;北京&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;children&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;null&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}}},&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;......}&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;locationLookupTable&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;11&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;11&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;北京&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;parent&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;kc&amp;quot;&gt;null&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;1101&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;1101&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;北京&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;parent&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;11&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;......}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;第一个用于生成关联选择器，第二个用于利用&lt;code&gt;id&lt;/code&gt;反向查询名字和父级。有了这两个表，地区选择器的实现基本就是搬砖难度了。  &lt;/p&gt;
&lt;h3&gt;分享&lt;/h3&gt;
&lt;p&gt;分享图是根据用户选择的地区实时生成的，基于&lt;code&gt;canvas&lt;/code&gt;。原理是先载入一张背景图，利用&lt;code&gt;drawImage&lt;/code&gt;方法将其绘制到canvas中并将canvas引用保存。之后每次用户点击生成时，只需要用特定的字体将地区的名字用&lt;code&gt;fillText&lt;/code&gt;方法绘制上去，之后再用&lt;code&gt;toDataUrl&lt;/code&gt;方法即可生成预览图。这是第一步。  &lt;/p&gt;
&lt;p&gt;生成完预览图后我们并不能直接分享，因为几乎所有社交应用的分享接口都是不支持&lt;code&gt;base64&lt;/code&gt;接口的，所以这里由后端提供了一个通过&lt;code&gt;base64&lt;/code&gt;为参数上传图片的接口。用户确认后再点击一次生成才会真正得生成图像供用户分享。这也大大降低了后端的压力，提升了用户的体验。&lt;/p&gt;
&lt;h3&gt;其他&lt;/h3&gt;
&lt;p&gt;你可能会注意到这个分享流程在任意次访问中只会走一遍，但换个浏览器却又要重新输入。这是由于我此处使用了一个变量来保存用户的访问状态，而此变量存到了&lt;code&gt;localStorage&lt;/code&gt;内。在使用&lt;code&gt;localStorage&lt;/code&gt;的时候，一定要注意用户是否是在隐身模式下，如果是则需要降级（这里的降级方案是直接存到内存，至少保证该次访问正常），下面是判断用户&lt;code&gt;localStorage&lt;/code&gt;是否可用的方法：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;storeEnabled&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;try&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;localStorage&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setItem&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;test&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;test&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;catch&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;e&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;storeEnabled&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;false&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;Comments-讨论区&lt;/h2&gt;
&lt;p&gt;最后就是讨论区了，你们一定以为我只是主站评论区换了个皮肤吧www  &lt;/p&gt;
&lt;p&gt;错，由于要把评论区改造成贴吧，为了可控和与现在ts + react的技术栈相容，我将评论区重写了一遍，并在外面套了一层别的东西做成了这种微博式的体验。得益于ts和less的优秀，这一部分只花了一周基本就搞定www  &lt;/p&gt;
&lt;p&gt;虽说一周搞定，但其实讨论区的逻辑工作量是最大的，还好我最先开发的是这一块，那时候脑子最清楚（后面都被各种样式、兼容、媒体查询搞的精疲力竭）  &lt;/p&gt;
&lt;p&gt;这一块没什么特别的心得，大部分都是业务逻辑和后端对接口，要说真有什么建议的话：  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;先把类型和数据结构定好再写代码，像这样子都写好回来查后来改都好弄，还有类型校验防止SB错误，你们还有什么理由不用TS！&lt;/strong&gt;  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;interface&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;IMember&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;mid&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;uname&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;sex&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;avatar?&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;face&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;sign&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;rank&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;level_info&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;current_level&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;结语&lt;/h2&gt;
&lt;p&gt;基本就这些了，要说遗憾也有，这次本来想上WEBGL的后来觉得不太稳也没上，由于一开始没想太多所以后面发现性能有问题想改成canvas也来不及了。  &lt;/p&gt;
&lt;p&gt;说到底还是经验不太够，这次成长了很多，下次大型活动我们再见。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Monday, 12 Jun 2017 20:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2017.06.12 20:00:article/Skill-2017_06_12_a</guid>
<category>前端</category>
<category>BML</category>
<category>React</category>
<category>TypeScript</category>
<category>ReactMotion</category>
<category>Keyframes</category>
<category>Less</category>
<category>IE</category>
<category>Preload</category>
<category>HTML5</category>
<category>Canvas</category>
</item>

<item>
<title>现代前端-近年的发展与有趣实践</title>
<link>http://dtysky.moe/article/Skill-2017_01_08_a</link>
<description>&lt;p&gt;这个是我在B站内部技术分享做的PPT，本人水平还很菜所以难免有不少纰漏，但脉络上应该还是没有问题的，抱着一切能开源就开源的心态，我就扔这里了www（跑
年轻还是要多折腾啊，要不以后多后悔www&lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://www.jianguoyun.com/p/DeKZlWAQn-T6BRi3vCQ&amp;quot;&gt;H光-前端.pptx&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;br/&gt;
&lt;br/&gt;
&lt;br/&gt;
&lt;br/&gt;
&lt;br/&gt;
&lt;br/&gt;
&lt;br/&gt;
&lt;br/&gt;
&lt;br/&gt;
&lt;br/&gt;
&lt;br/&gt;
&lt;br/&gt;
&lt;br/&gt;
&lt;br/&gt;
&lt;br/&gt;
&lt;br/&gt;
&lt;br/&gt;
&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;照骗（逃&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;dtysky&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2017_01_08a/1.png&amp;quot; /&gt;&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Sunday, 08 Jan 2017 20:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2017.01.08 20:00:article/Skill-2017_01_08_a</guid>
<category>前端</category>
<category>PPT</category>
</item>

<item>
<title>【Less】实现可选参数以及各种AutopreFixer</title>
<link>http://dtysky.moe/article/Skill-2016_10_14_a</link>
<description>&lt;p&gt;Less和Sass、Scss、Stylus等都是CSS的预处理语言，比起CSS，它们更接近于一门完整的程序语言。变量、引用、函数、Mixin等，使得它们提供更强的逻辑功能来使得样式代码也可以复用、并且便于维护。Less相较其它几种，更加接近于CSS原生的思维模式和写法。本篇就我的使用心得，论述一下如何可选参数函数，以及介绍一些我实现的各种AutopreFixer，比如keyframes的AutopreFixer。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;可选参数函数&lt;/h2&gt;
&lt;p&gt;在less中实现可选参数函数，只需要给参数设定一个不会用到的默认值，然后在内部检查是否为这个值并开分支即可：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nc&amp;quot;&gt;.background&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;@all&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nt&amp;quot;&gt;undefined&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;@size&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nt&amp;quot;&gt;undefined&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;@pos&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nt&amp;quot;&gt;undefined&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;@re&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nt&amp;quot;&gt;undefined&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;@clip&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nt&amp;quot;&gt;undefined&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;@image&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nt&amp;quot;&gt;undefined&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;@color&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nt&amp;quot;&gt;undefined&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
  &lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;when&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;not&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;size&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;undefined&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nb&amp;quot;&gt;background&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;size&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;size&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
  &lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;when&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;not&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pos&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;undefined&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nb&amp;quot;&gt;background-position&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pos&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
  &lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;when&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;not&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;re&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;undefined&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nb&amp;quot;&gt;background-repeat&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;re&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
  &lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;when&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;not&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;clip&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;undefined&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nb&amp;quot;&gt;background&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;clip&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;clip&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
  &lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;when&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;not&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;image&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;undefined&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nb&amp;quot;&gt;background-image&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;image&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
  &lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;when&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;not&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;color&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;undefined&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nb&amp;quot;&gt;background-color&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
  &lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;when&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;not&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;undefined&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nb&amp;quot;&gt;background&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;all&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;如此，我们使用它时便可以达到如下效果：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nc&amp;quot;&gt;.test&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;background&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;size&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;cover&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;green&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;转换为：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nc&amp;quot;&gt;.test&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nb&amp;quot;&gt;background&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;size&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;cover&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;nb&amp;quot;&gt;background-color&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;green&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;AutoPrefixer&lt;/h2&gt;
&lt;p&gt;主要用于处理浏览器兼容问题，一般的AutoPrefixer比较简单，主要利用将参数作为属性名的一部分的特性，比如这个：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nc&amp;quot;&gt;.autoprefix-some&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;@name&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;@t&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;webkit&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-@&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;moz&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-@&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;o&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-@&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;o&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;nc&amp;quot;&gt;.autoprefix-transition&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;@t&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;autoprefix&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;some&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;transition&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;下面代码：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nc&amp;quot;&gt;.test&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;autoprefix&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;transition&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;all&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;.5s&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;esae&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;out&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;将被转换为：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nc&amp;quot;&gt;.test&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;webkit&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;transition&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;all&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;.5s&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;esae&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;out&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;moz&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;transition&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;all&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;.5s&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;esae&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;out&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;o&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;transition&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;all&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;.5s&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;esae&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;out&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;transition&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;all&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;.5s&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;esae&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;out&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;但如果想要实现&lt;code&gt;keyframes&lt;/code&gt;的AutopreFixer，就需要用到一些奇淫巧技了，比如字符串转移、拼接等，有点绕，自行体会吧：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;autoprefix&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;keyframe&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;transform&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;func&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;nl&amp;quot;&gt;t3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;undefined&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;use&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nl&amp;quot;&gt;prefix&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;base&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;prefix&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;bind&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;l&amp;quot;&gt;@{&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;prefix&lt;/span&gt;&lt;span class=&amp;quot;l&amp;quot;&gt;}@{&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;func&lt;/span&gt;&lt;span class=&amp;quot;l&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;l&amp;quot;&gt;@{&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;prefix&lt;/span&gt;&lt;span class=&amp;quot;l&amp;quot;&gt;}@{&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;func&lt;/span&gt;&lt;span class=&amp;quot;l&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;l&amp;quot;&gt;@{&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;prefix&lt;/span&gt;&lt;span class=&amp;quot;l&amp;quot;&gt;}@{&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;func&lt;/span&gt;&lt;span class=&amp;quot;l&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;nl&amp;quot;&gt;start&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;~&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;@{bind}@@{prefix}keyframes @{name} { `&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;se&amp;quot;&gt;\n&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;#39;` 0%&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;l&amp;quot;&gt;@{&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;start&lt;/span&gt;&lt;span class=&amp;quot;l&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;when&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t3&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;undefined&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;mi&amp;quot;&gt;100&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;%&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;when&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;not&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t3&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;undefined&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;mi&amp;quot;&gt;50&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;%&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
      &lt;span class=&amp;quot;mi&amp;quot;&gt;100&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;%&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;@end&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;  &lt;span class=&amp;quot;o&amp;quot;&gt;~&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;} `&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;se&amp;quot;&gt;\n&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;#39;`&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;end&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;l&amp;quot;&gt;@{&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;end&lt;/span&gt;&lt;span class=&amp;quot;l&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;wtf&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nl&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;err&amp;quot;&gt;#&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;fff&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}}&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;base&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;~&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;~&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;base&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;o&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;@end&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;base&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;webkit&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;@end&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;base&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;moz&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;@end&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;base&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ms&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;@end&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;end&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;下列代码：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nc&amp;quot;&gt;.autoprefix-keyframe-transform&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;test&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nt&amp;quot;&gt;top&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nt&amp;quot;&gt;30px&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nt&amp;quot;&gt;60px&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nt&amp;quot;&gt;30px&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;将被转换为：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;@keyframes&lt;/span&gt; &lt;span class=&amp;quot;nt&amp;quot;&gt;test&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nt&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;%&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;nb&amp;quot;&gt;top&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;30px&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;nt&amp;quot;&gt;50&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;%&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;nb&amp;quot;&gt;top&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;60px&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;nt&amp;quot;&gt;to&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;nb&amp;quot;&gt;top&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;30px&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;@-webkit-keyframes&lt;/span&gt; &lt;span class=&amp;quot;nt&amp;quot;&gt;test&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nt&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;%&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;webkit&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;top&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;30px&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;nt&amp;quot;&gt;50&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;%&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;webkit&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;top&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;60px&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;nt&amp;quot;&gt;to&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;webkit&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;top&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;30px&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;@-o-keyframes&lt;/span&gt; &lt;span class=&amp;quot;nt&amp;quot;&gt;test&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nt&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;%&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;webkit&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;top&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;30px&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;nt&amp;quot;&gt;50&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;%&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;webkit&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;top&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;60px&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;nt&amp;quot;&gt;to&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;webkit&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;top&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;30px&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;@-moz-keyframes&lt;/span&gt; &lt;span class=&amp;quot;nt&amp;quot;&gt;test&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nt&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;%&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;webkit&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;top&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;30px&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;nt&amp;quot;&gt;50&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;%&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;webkit&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;top&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;60px&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;nt&amp;quot;&gt;to&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;webkit&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;top&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;30px&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;@-ms-keyframes&lt;/span&gt; &lt;span class=&amp;quot;nt&amp;quot;&gt;test&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nt&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;%&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;webkit&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;top&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;30px&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;nt&amp;quot;&gt;50&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;%&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;webkit&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;top&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;60px&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;nt&amp;quot;&gt;to&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;webkit&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;top&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;30px&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Friday, 14 Oct 2016 20:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2016.10.14 20:00:article/Skill-2016_10_14_a</guid>
<category>Less</category>
<category>AutoPrefixer</category>
<category>Css</category>
<category>Keyframes</category>
</item>

<item>
<title>【Flask/React】此博客服务端的缓存实现</title>
<link>http://dtysky.moe/article/Skill-2016_10_13_a</link>
<description>&lt;p&gt;承接上篇&lt;a href=&amp;quot;http://dtysky.moe/article/Skill-2016_10_11_a&amp;quot;&gt;【React/Redux】深入理解React服务端渲染&lt;/a&gt;，由于React服务端渲染带来的开销，在内存尚可的状况下使用缓存是个不错的选择，而对于这个项目，由于前后端是分离的，所以需要在Node这边和Flask那边来协作完成最完备的缓存，本片文章就将记录一下我的思路。  &lt;/p&gt;
&lt;p&gt;前端架构在前面两篇文章均已说清，而后端架构则和这篇差不多（&lt;a href=&amp;quot;http://dtysky.moe/article/Create-2016_03_12_a&amp;quot;&gt;【Flask/React/MongoDB】BlogReworkIII-如何搭建一个动态Blog&lt;/a&gt;），没怎么改过。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;问题&lt;/h2&gt;
&lt;p&gt;由于前端服务器实现了服务端渲染，考虑这个博客的文章修改不会很频繁，其修改的频率远远小于访问频率，所以用&lt;code&gt;Cache&lt;/code&gt;来减少这个渲染的开销是合适的。为了设计这个Cache，需要明确现在前后端的设计下一次客户端请求的完整响应流程：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;前后端交互&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2016_10_13a/1.svg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;客户端发起请求，服务端开始渲染，进入相应组件的生命周期并向后端发起ajax请求，后端响应后进行下一步操作。由于前后端是分离的，Blog内容管理的核心在后端，所以核心就在于判定后端返回什么状态时从Cache中拿东西，在什么状态时修改Cache，这就要求后端提供给前端一个约定的协议。  &lt;/p&gt;
&lt;h2&gt;后端&lt;/h2&gt;
&lt;p&gt;在本Blog项目中，Cache这个方面，后端需要提供给前端的实际上是一个状态，这个状态表明前端向后端请求的数据是否发生了改动。一个显而易见的方法是由后端先缓存所哟已经被请求过的数据，在下次请求时再拿从数据库中得到的新数据来对比，根据比对的结果返回一个状态码（比如304），这样后端响应前端的结果可以表述为：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;content&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;......&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;code&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;200&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;304&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;注意这个200和304虽然和HTTP标准中的状态码值一致，但却是两个东西，在两种状态下它们返回的状态都是200，只不过数据中有个&lt;code&gt;code&lt;/code&gt;字段来标示状态。  &lt;/p&gt;
&lt;p&gt;这种做法很直观，但每次响应除了原先的数据库查询操作外，还要多一步对象对比的过程，这显然是不合适的，而且在这种设计下后端的这个Cache也失去了本来的意义，必须有其他的方案来解决这个问题。这里可以回顾一下后端的实现：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;后端框架&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2016_10_13a/2.svg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;可以发现，数据库只会在程序监听&lt;code&gt;pages&lt;/code&gt;文件夹内文件的改动后才会发生，并且这个改动事件是可以被程序捕获到的，这就提供了另一个解决方案——同时维护一个&lt;code&gt;cache&lt;/code&gt;和与其对应的&lt;code&gt;state&lt;/code&gt;，cache中存储响应内容，state中存储改内容对应的修改状态，而这个state中的内容除了初始化之外，只能伴随文章改动的事件而修改。  &lt;/p&gt;
&lt;p&gt;在这个思想的基础上，一个抽象的Cache和其成员便可以被构造出来：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;class&lt;/span&gt; &lt;span class=&amp;quot;nc&amp;quot;&gt;WebCache&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;():&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;def&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;__init__&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;bp&amp;quot;&gt;self&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;):&lt;/span&gt;
        &lt;span class=&amp;quot;bp&amp;quot;&gt;self&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;_cache&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{}&lt;/span&gt;
        &lt;span class=&amp;quot;bp&amp;quot;&gt;self&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;_state&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{}&lt;/span&gt;

    &lt;span class=&amp;quot;nd&amp;quot;&gt;@property&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;def&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;flag&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;bp&amp;quot;&gt;self&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;):&lt;/span&gt;
        &lt;span class=&amp;quot;o&amp;quot;&gt;......&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;def&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;updateContent&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;bp&amp;quot;&gt;self&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;parameters&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;content&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;):&lt;/span&gt;
        &lt;span class=&amp;quot;o&amp;quot;&gt;......&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;def&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;modifyState&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;bp&amp;quot;&gt;self&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;parameters&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;):&lt;/span&gt;
        &lt;span class=&amp;quot;o&amp;quot;&gt;.....&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;def&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;get&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;bp&amp;quot;&gt;self&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;parameters&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;):&lt;/span&gt;
        &lt;span class=&amp;quot;o&amp;quot;&gt;.....&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;def&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;delete&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;bp&amp;quot;&gt;self&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;parameters&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;):&lt;/span&gt;
        &lt;span class=&amp;quot;o&amp;quot;&gt;.....&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;def&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;has&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;bp&amp;quot;&gt;self&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;parameters&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;):&lt;/span&gt;
       &lt;span class=&amp;quot;o&amp;quot;&gt;......&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;def&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;is_modified&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;bp&amp;quot;&gt;self&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;parameters&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;):&lt;/span&gt;
        &lt;span class=&amp;quot;o&amp;quot;&gt;......&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这是一个Cache的基类，它的作用和后端架构中其他基类基本一致（详见后端设计的那篇文章），它提供了一系列方法来维护内部的&lt;code&gt;_cache&lt;/code&gt;和&lt;code&gt;_state&lt;/code&gt;，其中&lt;code&gt;updateContent&lt;/code&gt;、&lt;code&gt;modifyState&lt;/code&gt;、&lt;code&gt;delete&lt;/code&gt;是维护它们的核心方法，其他方法则负责在请求到来时判定状态和取值，由此，整个设计便完成如下：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;后端cache&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2016_10_13a/3.svg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;文本解释如下：  &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;当前端请求到来时，如果cache中没有请求对应的内容，则从数据库查出内容并调用&lt;code&gt;updateContent&lt;/code&gt;放入缓存，此时&lt;code&gt;_cache&lt;/code&gt;将被更新，同时&lt;code&gt;_state&lt;/code&gt;中的对应的值将被设为&lt;code&gt;False&lt;/code&gt;，表明未被修改，并直接返回一个&lt;code&gt;code&lt;/code&gt;为200的响应。&lt;/li&gt;
&lt;li&gt;当前端请求到来时，如果cache中拥有对应的内容，并且&lt;code&gt;_state&lt;/code&gt;中对应状态为&lt;code&gt;False&lt;/code&gt;（表明未被修改），则直接返回一个&lt;code&gt;code&lt;/code&gt;为304的响应。&lt;/li&gt;
&lt;li&gt;当前端请求到来时，如果cache中拥有对应的内容，并且&lt;code&gt;_state&lt;/code&gt;中对应状态为&lt;code&gt;True&lt;/code&gt;（表明已被修改），则重复1。&lt;/li&gt;
&lt;li&gt;当文件监控器发现文件被更改、并进行到写入数据库这一步时，直接根据当前写入的条目判断该条目是否在cache中，如果在，则调用&lt;code&gt;modifyState&lt;/code&gt;方法将&lt;code&gt;_state&lt;/code&gt;中对应的值设为&lt;code&gt;True&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如此，后端的缓存以及和前端的协议便已完成。&lt;/p&gt;
&lt;h2&gt;前端&lt;/h2&gt;
&lt;p&gt;前端服务器的Cache结构和后端基本一致，但也有一些不同，最明显在于前端的请求会有&lt;strong&gt;分页&lt;/strong&gt;这个参数而后端没有，并且Redux中维护的状态和最终渲染的页面也并非对应，比如：  &lt;/p&gt;
&lt;p&gt;在&lt;strong&gt;文章列表&lt;/strong&gt;、也就是&lt;code&gt;/category/Skill/0&lt;/code&gt;这种页面中，其对应Redux的store中的state是一个维护着&lt;code&gt;list&lt;/code&gt;和&lt;code&gt;payload&lt;/code&gt;的变量，&lt;code&gt;list&lt;/code&gt;存储着所有的文章，&lt;code&gt;payload&lt;/code&gt;负责分页，由于Blog自身的性质，我设计的是在一次用于访问中只会获取一次&lt;code&gt;list&lt;/code&gt;，分页通过设置&lt;code&gt;payload&lt;/code&gt;中的&lt;code&gt;page&lt;/code&gt;加之组件渲染逻辑来实现（本质上就是数组切片）。  &lt;/p&gt;
&lt;p&gt;在这种情况下，如果两次请求的路由中分类都是一致的，只是分页不同，那么将其作为两种请求来多做一次服务端渲染显然是不合适的，因为我们完全可以利用第一次请求的&lt;code&gt;list&lt;/code&gt;、加之对&lt;code&gt;payload&lt;/code&gt;的修改来直接进行二次渲染，而不必再走一次状态的初始化，这也就要求将Redux中的store和最终页面的内容分别进行缓存：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;cacheStore&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Immutable&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fromJS&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({});&lt;/span&gt;
&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;cachePage&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Immutable&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fromJS&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({});&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;code&gt;cacheStore&lt;/code&gt;用于缓存不同请求下的store，以后端接口url为key；&lt;code&gt;cachePage&lt;/code&gt;则用于缓存最终的结果，以客户端请求url为key。有了Cache基本框架，便可以结合客户端请求和后端响应来进行最后的设计：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;前端Cache&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2016_10_13a/4.svg&amp;quot; /&gt;  &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;当一次客户端请求来到时，先解析出此次请求对应的后端接口url，并向后端发出请求。&lt;/li&gt;
&lt;li&gt;得到后端响应，判断状态码&lt;code&gt;code&lt;/code&gt;，如果是200，直接重新走一遍完整的服务端渲染流程，如果是304，则先去查&lt;code&gt;cachePage&lt;/code&gt;中有没有对应的数据，如果有，则直接返回其中的内容。&lt;/li&gt;
&lt;li&gt;如果&lt;code&gt;code&lt;/code&gt;是304但&lt;code&gt;cachePage&lt;/code&gt;中没有对应的内容，假如&lt;code&gt;cacheStore&lt;/code&gt;中也没有对应的&lt;code&gt;store&lt;/code&gt;，则走一遍完整的服务端渲染流程，如果有，则直接取出&lt;code&gt;store&lt;/code&gt;执行相应的&lt;code&gt;store.dispatch&lt;/code&gt;修改&lt;code&gt;payload&lt;/code&gt;，接着直接进入二次渲染。&lt;/li&gt;
&lt;li&gt;在二次渲染结束后，更新两个Cache。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;改进&lt;/h2&gt;
&lt;p&gt;Cache带来的性能提升以内存消耗为代价，典型的空间换时间，所以能减少内存消耗的事情该做的还是要做。这一点也很简单，考虑到&lt;code&gt;cachePage&lt;/code&gt;中存的是最终返回给客户端的页面，而这种页面终归是要被压缩的，所以在放入缓存之前就可以直接将其压缩，这样最后从里面取到的也是压缩后的数据，不但节省了空间还省去了每次的压缩带来的损耗：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cachePage&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;cachePage&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;set&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;frontUrl&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;zlib&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;gzipSync&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;page&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Thursday, 13 Oct 2016 21:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2016.10.13 21:00:article/Skill-2016_10_13_a</guid>
<category>React.js</category>
<category>Redux</category>
<category>React-router</category>
<category>Flask</category>
<category>缓存</category>
<category>Cache</category>
</item>

<item>
<title>【React/Redux】深入理解React服务端渲染</title>
<link>http://dtysky.moe/article/Skill-2016_10_11_a</link>
<description>&lt;p&gt;在上一篇文章&lt;a href=&amp;quot;http://dtysky.moe/article/Skill-2016_10_09_a&amp;quot;&gt;【React/Redux/Router/Immutable】React最佳实践的正确食用姿势&lt;/a&gt;中，已经论述了React最佳实践的前端部分，但在最后也已说明，那种基本实现对SEO并不友好，并且由于首屏渲染依赖于ajax所以在JS禁用状态下基本也就废了，所以我们需要利用服务端渲染（Server side rendering）来对首屏进行优化。虽然React官方提供服务端渲染的API，React-router也支持，但在渲染依赖于ajax请求的状况下仍然聊胜于无，但这并不是无解的，和Redux的合作便可以相对完美地解决这个问题。  &lt;/p&gt;
&lt;p&gt;接下来将以&lt;a href=&amp;quot;https://github.com/dtysky/BlogReworkPro&amp;quot;&gt;我的博客&lt;/a&gt;为例，论述一下如何正确进行React的服务端渲染，这一部分的代码基本都在&lt;a href=&amp;quot;https://github.com/dtysky/BlogReworkPro/blob/master/Front/server/server.bin.js&amp;quot;&gt;server/server.bin.js&lt;/a&gt;中。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;问题&lt;/h2&gt;
&lt;p&gt;如果没有服务端渲染，那么一个React架构的SPA的渲染是这样的：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;客户端请求资源 -&amp;gt; 返回index.html模板 -&amp;gt; 请求js文件并加载 -&amp;gt; React执行，挂载组件 -&amp;gt; 进入路由 -&amp;gt; 一些根据组件生命周期而做的初始化操作 -&amp;gt; 渲染组件&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在这种模式下，一开始客户端只会收到一个只有架子、没有内容的Html文件，等待js文件加载完毕后才会执行后续操作并渲染出期望中的页面。这也就是说，无论是哪个路由下的页面，一开始都会先进入&lt;code&gt;index.html&lt;/code&gt;这个入口，然后再客户端进行接下来的渲染，这对于大部分用户确实是没有什么问题，但对于部分场景和搜索引擎就不是这样了。  &lt;/p&gt;
&lt;h3&gt;某些场景&lt;/h3&gt;
&lt;p&gt;主要是JS被禁用的场景，比如是微信，在某些情况下，你在微信分享的页面可能会被判定为广告页面，在不做认证（备案）的情况下，JS脚本会被禁用，这会使得后续的渲染都无法执行。  &lt;/p&gt;
&lt;h3&gt;SEO&lt;/h3&gt;
&lt;p&gt;SEO相较前面是更为严重和急迫的问题，搜索引擎这个绝大多数网站的主要流量入口，如果不加以重视，除非网站是故意不想被曝光，否则是一笔很大的损失。而根据前面的介绍不难得知，如果每次都将&lt;code&gt;index.html&lt;/code&gt;作为入口，利用JS和ajax来渲染，搜索引擎是基本无法获得你的真正页面内容的，除了下面这个粗暴的解决方案。  &lt;/p&gt;
&lt;h3&gt;一个粗暴的解决方案&lt;/h3&gt;
&lt;p&gt;这也是我上次重构最终选择的方案，对于一些搜索引擎（谷歌明确支持，必应似乎近期也支持了，百度万年技术最差不用等了），有着明确的对ajax页面的一个折中解决方案，我们可以在&lt;code&gt;index.html&lt;/code&gt;的head里加上这么一句：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;meta&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;fragment&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;content&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;!&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;搜索引擎解析出这个meta信息后，便会在原始url后面加上一个&lt;code&gt;?_escaped_fragment_&lt;/code&gt;来再次发起请求，这时候我们只需要在服务端备好另一套SEO专用的页面返回给其即可。这种方法虽然有效，但本质上还是要求我们另外准备一套界面，并且这一套和原来的代码还不能复用。比如，我在那时候就是专门写了一套jade模板来做SEO：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// Express.js的中间件&lt;/span&gt;
&lt;span class=&amp;quot;kd&amp;quot;&gt;function&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;SEO&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;req&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;next&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;escapedFragment&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;req&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;query&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_escaped_fragment_&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;escapedFragment&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;parsedUrl&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;parse&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;req&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
        &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;rdPath&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;sb&amp;quot;&gt;`/&lt;/span&gt;&lt;span class=&amp;quot;si&amp;quot;&gt;${&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;jade&lt;/span&gt;&lt;span class=&amp;quot;si&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;sb&amp;quot;&gt;/parsedUrl.pathname`&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;logInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;Redirect: from&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;req&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;path&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;to &amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;rdPath&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;redirect&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rdPath&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;next&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这相当于将工作量翻了两倍。不仅如此，这还会使得server更加复杂，对于我这个Blog项目，这种妥协似乎还勉强可以接受，但对于一个复杂一些的网站就不是这样了，我们必须用一种新的方法来用最少的代码解决这个问题。  &lt;/p&gt;
&lt;h2&gt;React服务端渲染&lt;/h2&gt;
&lt;p&gt;React+React-router+Redux便是解决这个问题的一套相对的完美的方案，虽然我们还是要对一些渲染逻辑做修改，但相较别的方案，仍然好得多。&lt;/p&gt;
&lt;h3&gt;ReactDom&lt;/h3&gt;
&lt;p&gt;React提供了一个API用于将虚拟DOM树在服务端环境下进行渲染，这个API是&lt;code&gt;ReactDom/server&lt;/code&gt;中的&lt;code&gt;renderToString&lt;/code&gt;。这个方法接受一个虚拟DOM树，并返回一个渲染后的HTML字符串，例如我有一个根级组件&lt;code&gt;Root&lt;/code&gt;，我便可以使用下列语句得到结果：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kn&amp;quot;&gt;import&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;renderToString&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;kn&amp;quot;&gt;from&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;ReactDom/server&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;markup&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;renderToString&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
    &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Root&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;code&gt;markup&lt;/code&gt;即为渲染后的结果。&lt;code&gt;renderToString&lt;/code&gt;这个方法是服务端渲染的基础，但如果只是单纯这样使用，那么基本等于将React作为一个复杂很多的模板语言来写而已，因为这个渲染并不会理会任何的ajax请求，也不会根据url来做任何路由，它只会在第一次&lt;code&gt;render&lt;/code&gt;方法调用后结束。这也就是说&lt;code&gt;render&lt;/code&gt;方法之后的所有生命周期函数都不会被触发，再一次服务端渲染中，只有&lt;code&gt;constructor&lt;/code&gt;、&lt;code&gt;componentWillMount&lt;/code&gt;和&lt;code&gt;render&lt;/code&gt;会被各触发一次，并且在期间使用&lt;code&gt;setState&lt;/code&gt;也是没有意义的。  &lt;/p&gt;
&lt;p&gt;这显然不是我们期望的，为了愉快得满足我们的须有，这里有两个问题需要解决：  &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;路由。&lt;/li&gt;
&lt;li&gt;ajax请求。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;React-router&lt;/h3&gt;
&lt;p&gt;正如上一篇文章所言，路由问题可以交由React-router解决。它提供了一系列方法来在服务端捕获请求参数，并和&lt;code&gt;renderToString&lt;/code&gt;结合来渲染出路由最终对应的组件，其基本原理是通过一个&lt;code&gt;match&lt;/code&gt;方法来根据客户端请求的url解析出已经定义好的&lt;code&gt;routes&lt;/code&gt;的&lt;code&gt;props&lt;/code&gt;，解析结果是&lt;code&gt;error&lt;/code&gt;（错误）、&lt;code&gt;redirectLocation&lt;/code&gt;（如果有重定向）和&lt;code&gt;renderPorps&lt;/code&gt;（正常状况下解析到的props），我们只需要根据这些参数来执行对应的操作即可：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;import&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;match&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;RouterContext&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;from&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;react-router&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;import&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;routes&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;from&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;../src/routes&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;match&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;routes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;location&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;req&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;error&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;redirectLocation&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;renderProps&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;error&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;logError&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;Match routes failed: &amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;error&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;error&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;stack&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;status&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;500&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;sendFile&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;config&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;error50xFile&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;redirectLocation&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;logError&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;Match routes redirectLocation: &amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;redirectLocation&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;redirect&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;302&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;sb&amp;quot;&gt;`&lt;/span&gt;&lt;span class=&amp;quot;si&amp;quot;&gt;${&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;redirectLocation&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;pathname&lt;/span&gt;&lt;span class=&amp;quot;si&amp;quot;&gt;}${&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;redirectLocation&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;search&lt;/span&gt;&lt;span class=&amp;quot;si&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;sb&amp;quot;&gt;`&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;renderProps&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;render&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;req&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;renderProps&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;logError&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;Miss match routes: &amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;error&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;error&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;stack&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;status&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;404&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;sendFile&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;config&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;error404File&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;除了前两种特殊情况和最后没有匹配到执行的404响应，一般情况下我们都会进入到&lt;code&gt;render&lt;/code&gt;这个方法中根据&lt;code&gt;renderProps&lt;/code&gt;来进行正常状况下的响应。&lt;code&gt;renderProps&lt;/code&gt;的结构如下（以blog项目，并以当前这个页面的路由为例）：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;routes&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;path&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;component&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;APP&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;......}&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;path&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;article/:name&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;components&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;content&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Article&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}}&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Skill&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2016&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_10_11_a&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;],&lt;/span&gt;
&lt;span class=&amp;quot;s1&amp;quot;&gt;    location: {&lt;/span&gt;
&lt;span class=&amp;quot;s1&amp;quot;&gt;        pathname: &amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;article&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Skill&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2016&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_10_09_a&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;,&lt;/span&gt;
&lt;span class=&amp;quot;s1&amp;quot;&gt;        ......&lt;/span&gt;
&lt;span class=&amp;quot;s1&amp;quot;&gt;    },&lt;/span&gt;
&lt;span class=&amp;quot;s1&amp;quot;&gt;    components: [&lt;/span&gt;
&lt;span class=&amp;quot;s1&amp;quot;&gt;        {&lt;/span&gt;
&lt;span class=&amp;quot;s1&amp;quot;&gt;            [Function: Connect] displayName: &amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Connect&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;APP&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
            &lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;content&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Article&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;history&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{......},&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;router&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{......},&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;matchContext&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{......}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这里面对我们有用的信息很多，但如果只是单纯用React-router配合来做服务端渲染，我们并不用了解这些，而是直接把&lt;code&gt;renderProps&lt;/code&gt;传给待渲染的组件即可：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kn&amp;quot;&gt;import&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;renderToString&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;kn&amp;quot;&gt;from&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;react-dom/server&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;kn&amp;quot;&gt;import&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;match&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;RouterContext&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;kn&amp;quot;&gt;from&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;react-router&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;markup&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;renderToString&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
    &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;RouterContext&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;...&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;renderProps&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;通过使用&lt;code&gt;RouterContext&lt;/code&gt;，我们便得到了和客户端渲染时相似的&lt;code&gt;Router&lt;/code&gt;组件，将其作为根组件并传入&lt;code&gt;renderProps&lt;/code&gt;，再调用&lt;code&gt;renderToString&lt;/code&gt;，一次具有路由的服务端渲染便完成。在此例中，它最终会返回主要区域是&lt;code&gt;Article&lt;/code&gt;组件的真实DOM。  &lt;/p&gt;
&lt;p&gt;通过和React-router的结合，我们解决了第一个、也是相对容易的问题，但这仍然无法解决ajax调用的问题，这时候，Redux就该出场了。&lt;/p&gt;
&lt;h2&gt;Redux&lt;/h2&gt;
&lt;p&gt;Redux配合React进行服务端渲染的教程已经有许多，但这些教程大都不过是官网那篇例子的搬运，例子中所述方法的基本思想如下：  &lt;/p&gt;
&lt;h3&gt;基本思想&lt;/h3&gt;
&lt;p&gt;首先要明确我们真正的问题是什么，由于服务端渲染只会走一遍生命周期，并且在第一次&lt;code&gt;render&lt;/code&gt;后便会停止，所以想要真正渲染出最终的页面，我们必须在第一次渲染前就将状态准备好我们期望中的状态，这也就是说，我们必须要有一次超前的ajax请求实现获取状态，然后来根据这个状态渲染：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;function&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;serverSideRender&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;req&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;request&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;get&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;then&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;response&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;normalRender&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;response&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;catch&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;err&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;render500&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;err&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;在自己定义的&lt;code&gt;normalRender&lt;/code&gt;这个方法里，我们可以通过Redux提供的&lt;code&gt;createStore&lt;/code&gt;方法的第二个参数来进行创建带有初始状态的&lt;code&gt;store&lt;/code&gt;，然后将这个状态送入根组件，并执行后续的渲染：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;function normalRender(res, response) {
    // 假设response的响应体中就包含了所有状态信息
    const initState = response.body;
    const store = createStore(reducers, initState);
    const markup = renderToString(
        &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;Provider&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;store=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;{store}&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;APP&lt;/span&gt; &lt;span class=&amp;quot;nt&amp;quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;/Provider&amp;gt;&lt;/span&gt;
    );
    // 将markup和最终的state塞到模板内渲染，这个模板取决于使用的模板引擎，也可以直接字符串替换
    return res.render(template, {
        markup,
        finalState: store.getState().toJSON()
    });
}
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这个方法以响应结果为初始化状态渲染DOM，并将渲染后的结果塞入模板，值得注意的是，渲染参数里面有个&lt;code&gt;finalState&lt;/code&gt;，这是初次渲染后、&lt;code&gt;store&lt;/code&gt;的最终状态，我们需要将其序列化后强制写到返回的HTML的&lt;code&gt;script&lt;/code&gt;标签中，将其赋予一个、例如叫&lt;code&gt;initState&lt;/code&gt;的变量中，这样最终返回的HTML结构如下：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;html&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;head&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
        ......
        &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;script&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;window&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;initState&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;finalState&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}}&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;script&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;head&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;body&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;id&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;react-container&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;{{markup}}&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;body&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;html&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;code&gt;window.initState&lt;/code&gt;便拥有了我们服务端渲染后的状态，如此，客户端便有了一个途径来根据这个状态来初始化客户端的&lt;code&gt;store&lt;/code&gt;，并接续接下来的操作，这实质上是完成了服务端和客户端之间&lt;strong&gt;状态的对接&lt;/strong&gt;：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;store&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;createStore&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;reducers&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;window&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;initState&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;特别注意，在将&lt;code&gt;markup&lt;/code&gt;写入模板时我在React顶层DOM中又插入了一层&lt;code&gt;div&lt;/code&gt;，这是为了解决一个特殊的警告，详细请看这里：&lt;a href=&amp;quot;http://stackoverflow.com/questions/33521047/warning-react-attempted-to-reuse-markup-in-a-container-but-the-checksum-was-inv&amp;quot;&gt;Warning: React attempted to reuse markup in a container but the checksum was invalid&lt;/a&gt;。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;要注意，我们可以延续状态，&lt;strong&gt;并非说Redux可以直接帮我们解决重复渲染的问题&lt;/strong&gt;，无论如何，在每次重新执行初始渲染的时候，&lt;strong&gt;组件的生命周期还是会走一遍&lt;/strong&gt;，如果阻止重复ajax请求、重复渲染，就是我们要在逻辑里自己把握的事情了，比如，我们的&lt;code&gt;state&lt;/code&gt;里如果有一个字段是&lt;code&gt;list&lt;/code&gt;，而这个&lt;code&gt;list&lt;/code&gt;在这个应用的一次访问中只会初始化一次，那么我们便可以在初始化它的&lt;code&gt;action&lt;/code&gt;中这么写：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;function&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;getList&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;currentList&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;currentList&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;())&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;那么&lt;code&gt;list&lt;/code&gt;便不会被二次初始化，也不会进行进一步的无效渲染。  &lt;/p&gt;
&lt;p&gt;这样，官方建议的服务端渲染便完成。但这其实有另外两个问题，一个是路由的问题，还有一个，按照这种说法，我们难道要写另一套专门的逻辑来在渲染前初始化状态？一个两个状态还好，但一般SPA都不会只有这么点，而且这种ajax请求我们一般都会写在专门的&lt;code&gt;action&lt;/code&gt;中，并在&lt;code&gt;action&lt;/code&gt;成功或失败后交给&lt;code&gt;ruducer&lt;/code&gt;，这些代码难道不能复用吗？客户端和服务端的同构化开发就不能实现吗？  &lt;/p&gt;
&lt;p&gt;当然可以。&lt;/p&gt;
&lt;h3&gt;路由&lt;/h3&gt;
&lt;p&gt;和React-router的结合实际上不是Redux的问题，而是React-router自身的问题，其核心在于如何根据匹配后的&lt;code&gt;renderProps&lt;/code&gt;来获取用于初始化状态的信息，在上面一章我们已经拿到了&lt;code&gt;renderProps&lt;/code&gt;的详细信息，接下来便可以妥善地利用这些信息。  &lt;/p&gt;
&lt;p&gt;虽然如何利用它们取决于项目的设计，但一个显而易见的策略是利用匹配后的&lt;code&gt;params&lt;/code&gt;来作为根据进行ajax请求来获取初始数据：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;renderProps&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;request&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;get&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;sb&amp;quot;&gt;`&lt;/span&gt;&lt;span class=&amp;quot;si&amp;quot;&gt;${&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;backendHost&lt;/span&gt;&lt;span class=&amp;quot;si&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;sb&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;si&amp;quot;&gt;${&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt;&lt;span class=&amp;quot;si&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;sb&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;si&amp;quot;&gt;${&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;si&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;sb&amp;quot;&gt;`&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这只是一个例子，表明如何根据信息来获取响应数据来初始化状态。  &lt;/p&gt;
&lt;p&gt;在这个Blog项目中，我采用了一些特别的方法来进行这一步的操作。在我定义一个和路由相关的组件，比如&lt;code&gt;Article&lt;/code&gt;时，我会为其赋予静态变量&lt;code&gt;type&lt;/code&gt;：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;export default class Article extends Base {
    static type = &amp;#39;article&amp;#39;;
    ......
}
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;再加上上面分析的内容，我们可以从&lt;code&gt;renderProps.components[1].content&lt;/code&gt;（&lt;code&gt;content&lt;/code&gt;是该路由对应的组件，在此处是&lt;code&gt;Article&lt;/code&gt;，请见上一篇文章）中拿到这个&lt;code&gt;type&lt;/code&gt;:  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;renderProps&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;components&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;content&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;然后根据这个type进行接下来的操作即可。&lt;/p&gt;
&lt;h3&gt;进阶&lt;/h3&gt;
&lt;p&gt;有了路由和基础的服务端渲染，要解决的问题便只剩同构化开发、避免多余代码的同构开发问题。这个问题解决的核心在于——&lt;code&gt;store&lt;/code&gt;在渲染过程中是可变的，并且在服务端渲染进行过程中，有一个&lt;code&gt;componentWillMount&lt;/code&gt;的阶段可供我们和客户端平等使用。这二者是接下来内容的基础。  &lt;/p&gt;
&lt;p&gt;由于&lt;code&gt;componentWillMount&lt;/code&gt;会在服务端渲染过程中执行，并且执行时&lt;code&gt;this.props&lt;/code&gt;中的内容已经根据路由信息被修改，我们可以将ajax请求放入其中：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;componentWillMount&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;dispatch&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;props&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;dispatch&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getList&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;我们可以在其中执行任意个ajax请求，由于&lt;code&gt;store&lt;/code&gt;是可改变的，所以在请求结束后、&lt;code&gt;reducer&lt;/code&gt;生效后将会我们便可以取到完整的、初始化过的&lt;code&gt;store&lt;/code&gt;。也就是说，我们只要如此写好组件的生命周期，而后进行正常的服务端渲染，然后只需等待便可以得到一个初始化后的&lt;code&gt;store&lt;/code&gt;，随后只需要将这个&lt;code&gt;store&lt;/code&gt;作为初始化数据送入模板即可。&lt;/p&gt;
&lt;p&gt;这听起来是不是很简单？确实很简单，但仍然有一点需要注意——ajax请求时异步的，一次服务端渲染结束后，并不代表ajax请求就结束了，我们仍然需要一些方法来确定所哟请求确实结束，这里就需要一点设计了——我们可以在&lt;code&gt;store&lt;/code&gt;中创建一个&lt;code&gt;state&lt;/code&gt;专门用于表示初始化是否结束，并配以相应的&lt;code&gt;action&lt;/code&gt;和&lt;code&gt;reducer&lt;/code&gt;来修改它：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;componentWillMount&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;dispatch&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;props&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;dispatch&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getList&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;then&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(()&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;dispatch&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;actionTypes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;init&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;all&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;successful&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}))&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;catch&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(()&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;dispatch&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;actionTypes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;init&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;all&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;failed&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}));&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;如此，我们便可以不断检查&lt;code&gt;store.getState().state.initDone&lt;/code&gt;来确定所有请求是否结束：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;function&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;responseWithCheck&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;store&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;renderProps&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;setImmediate&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(()&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;store&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getState&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;().&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;state&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;get&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;initDone&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
            &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;setImmediate&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;responseWithCheck&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;store&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;renderProps&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;render&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(......);&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;setImmediate方法用于设置一个定时任务，此定时任务将在下一次mainLoop中被触发。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这样，我们便可以在基本不添加多余逻辑的情况下复用客户端代码。但这样还是有问题没有被解决，我们送到客户端的仍然只是一堆状态加上等待初始化的页面，所以还需要更近一步。  &lt;/p&gt;
&lt;h3&gt;二次渲染&lt;/h3&gt;
&lt;p&gt;想要让服务端直接渲染出初始化&lt;code&gt;store&lt;/code&gt;后的完整页面，方法很简单，只需要把第一次渲染后的&lt;code&gt;finalState&lt;/code&gt;作为初始状态进行二次渲染即可：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;const store = createStore(reducers, finalState);
const markup = renderToString(
    &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;Provider&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;store=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;{store}&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;APP&lt;/span&gt; &lt;span class=&amp;quot;nt&amp;quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;/Provider&amp;gt;&lt;/span&gt;
);
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;如果设计足够规范，做了上述&lt;code&gt;getList&lt;/code&gt;那样的检查，第二次渲染时是不会进行再次请求的，而是直接根据&lt;code&gt;finalState&lt;/code&gt;渲染出真正的首屏页面。  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;store的&lt;code&gt;dispatch&lt;/code&gt;方法在服务端同样可以使用，我们可以直接用&lt;code&gt;store.dispatch&lt;/code&gt;来触发某些&lt;code&gt;action&lt;/code&gt;来满足一些特别的需求。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;至此，一个完整的、SEO友好的服务端渲染便已完成，但这样还是会有问题——客户端的方便是以服务端的消耗为代价的，客户端的每次请求都会导致服务端重新进行两次渲染和伴随的若干次ajax请求，这并不是我们期望的，所以这里就要用到&lt;strong&gt;Memory cache&lt;/strong&gt;来缓存&lt;code&gt;store&lt;/code&gt;和渲染后的页面，至于我的blog中是如何实现的，将在下一篇文章详细说明。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Tuesday, 11 Oct 2016 21:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2016.10.11 21:00:article/Skill-2016_10_11_a</guid>
<category>React.js</category>
<category>Redux</category>
<category>React-router</category>
<category>Express.js</category>
<category>服务端渲染</category>
</item>

<item>
<title>【React/Redux/Router/Immutable】React最佳实践的正确食用姿势</title>
<link>http://dtysky.moe/article/Skill-2016_10_09_a</link>
<description>&lt;p&gt;现代前端框架基本都是对传统系统应用框架的搬运，React虽定位为一个View层的框架，实际上却包含了MVVM中的每一环，每一个组件都可以看做是拥有所有环节的结合体。其激进的设计不但体现在JSX这个融合了HTML+JS+CSS的语法糖，也体现在了对MVVM的杂糅，然而和直觉不同，这并没有使得三者混乱分离，或者说M V VM这三者的聚合并不会带来什么问题，反而有一些益处。真正的问题在于组件嵌套带来的组件通信和VDOM使用不当带来的性能问题，而Redux和Immutable就是来解决这个问题的。此外，React-router的出现使得前端路由成为可能，这几者结合起来大幅加强了一个SPA的开发效率和可维护性。  &lt;/p&gt;
&lt;p&gt;接下来将以&lt;a href=&amp;quot;https://github.com/dtysky/BlogReworkPro&amp;quot;&gt;我的博客&lt;/a&gt;为例，论述一下这个最佳实践。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;React&lt;/h2&gt;
&lt;p&gt;React的出现首先打破的是HTML、JS、CSS分离的格局，许多遗老遗少大呼又回到了JSP时代，宣称这是在逆历史而行在开倒车，而新晋分子则都不以为然，认为这才是趋势。当然历史已经告诉我们二元论是站不住脚的，所以优劣必然并存，一切大都是博弈之下的选择。  &lt;/p&gt;
&lt;h3&gt;Preview&lt;/h3&gt;
&lt;p&gt;传统前端开发主要是堆砌HTML结构，加之调优的样式并赋予小部分JS代码用于交互，逻辑大头基本都在后端。然而随着WEB业务的日趋复杂，前端逻辑的复杂度已不可同日而语，SPA的出现、以及后续一堆基于WEBKIT浏览器的加壳应用使得传统方式难以抗住需求，新的设计方法必须被提出。然而传统系统应用领域实际上早就对这些东西研究的十分深入，所以直接搬来即可。React搬来的是经典的MVC架构几次演化后迭代而成的MVVM，即“模型-视图-视图模型”模型，这种模型的一个经典实现就是MS的WPF，React的许多理念与其也十分相似，比如单向数据流，方法绑定等等，虽没有WPF这种经典完善，但也做的很好。其基本理念是：  &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;组件化，每一个组件都拥有自己独立的namespace。&lt;/li&gt;
&lt;li&gt;内部维护一个状态模型，称为&lt;code&gt;state&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;组件可以嵌套，并通过&lt;code&gt;props&lt;/code&gt;变量完成和父级的通信，可以向此变量挂载方法来使得父级响应子级的请求。&lt;/li&gt;
&lt;li&gt;内部状态可以影响视图层的渲染结果，视图层可以绑定事件控制状态，从而影响状态。&lt;/li&gt;
&lt;li&gt;完备的生命周期，可以预测并控制整个组件的状态流向。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如此，便实现了一个健壮的、组件化的视图框架。通过这种设计，加之JSX的混写，我们可以对一个组件的所有状态进行非常细粒度的控制，也可以将整个组件封装好粗粒度调用，伸缩性非常好。不仅如此，内联样式的优先度可以使得整个页面渲染更快，通过一些lib也可以实现伪类样式，唯一的缺点大概就是和设计人员的分工和IDE的智能补全了（笑）。  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;要注意的是，对于数据的流向，React是强制单向数据流的，也就是说，数据只能由状态单向得传递到视图层，而不能直接由视图层传回来，视图层对状态的影响只能通过绑定事件来实现。这可以预防双向绑定带来的状态预测困难问题，当然，双向绑定也有其好处，但个人认为其好处是远小于负面影响的。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;对React基本原理和应用具体如何编写，此文章不做多余探讨，阅读者应当有一些基本的功底。下面主要讨论标题所论述的“最佳实践”。  &lt;/p&gt;
&lt;p&gt;有一定React使用经验的的程序员，应当都或多或少遇到过工程庞大后的性能、以及当组件树嵌套过深时组件间的通信问题。前者主要由VDOM处理不当导致的反复render造成，而后者，则是React自身的设计特性造成的。  &lt;/p&gt;
&lt;h3&gt;DOM相关优化&lt;/h3&gt;
&lt;p&gt;VDOM是React的核心概念之一，“虚拟DOM树”，相对于真实的DOM树，它由用户创建后只存在于预处理阶段，只有在满足一定条件的状况下才会被渲染为真实的DOM。比起操作真实的DOM，虚拟DOM多了一步differ的过程，所以其实是要比直接操作慢的，但考虑到其带来的工程性的提升，这点性能损失根本不足为道——本因如此，但现实的很多使用中却并非总是如此。除了differ之外，不适当的重绘都会使得性能的显著下降，这大都是因为使用不当造成的。在组件的生命周期中，有一个阶段是&lt;code&gt;shouldComponentUpdate&lt;/code&gt;，这个方法的参数是&lt;code&gt;nextProps&lt;/code&gt;和&lt;code&gt;nextState&lt;/code&gt;，返回的是一个bool值，React会根据这个返回值来允许或阻止组件重绘。这是一个影响性能的核心方法，其基本使用如下：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;shoudComponentUpdate&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;nextProps&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;nextState&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;nextPropppps&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!==&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;props&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;当然，这一句只是伪代码，在真实情况下这么写是没用的（JS的机制）。通过设置合适的条件，我们可以防止绝大部分的无效重绘，这也是React应有的使用方法。这要求我们在设计之初就对组件的功能有着清晰的了解，但好在如果设计的尚可，组件应该是有一个合适的粒度的，这一部分也不会太复杂。  &lt;/p&gt;
&lt;p&gt;除了自己判断之外，如果结合下面所言的Redux，还可以用其提供的辅助函数&lt;code&gt;connect&lt;/code&gt;来决定子级组件需要件听哪些父级状态，这个下面说。  &lt;/p&gt;
&lt;p&gt;在此之外，我们还需要根据React的建议对组件设计好&lt;code&gt;key&lt;/code&gt;，这是其进行VDOM differ的核心凭据，尤其在以数组的形式描写一个序列的子级组件之时，这个key是必须提供的：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;Parent&amp;gt;&lt;/span&gt;
    [
        &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;Child0&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;key=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;0/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;gt;&lt;/span&gt;,
        &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;Child1&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;key=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;1/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;gt;&lt;/span&gt;,
        ......
    ]
&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;/Parent&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;组件间状态传递&lt;/h3&gt;
&lt;p&gt;组件间的状态传递是原生React的一个老大难问题，第一节已经介绍过，原生的方法只能使得状态在相邻的父级和子级之间传递，如果嵌套超过三层，便很容易出现下面的情况：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;子级组件通信&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2016_10_09a/1.svg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;表现为代码即：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// child.js&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;handleTextChange&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;value&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;handleTextChangeParent&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;value&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;c1&amp;quot;&gt;// parent.js&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;handleTextChangeParent&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;value&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;handleTextChangeGrandparent&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;value&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;如果组件的层级再增长，这无疑是灾难性的，但很遗憾在现代开发中这种状况很常见。解决这种问题的方法之一是自己定义全局单例和事件管理器来共享变量，就像&lt;a href=&amp;quot;http://dtysky.moe/article/Skill-2016_07_01_b&amp;quot;&gt;这篇文章写得这样&lt;/a&gt;，我们可以在组件初始化的时候注册若干事件，并在合适的地方&lt;code&gt;dispatch&lt;/code&gt;来实现组件间的跨级方法调用，而状态，则可以用全局单例来共享：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// child1.js&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;import&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;globalState&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;from&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;./globalState&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;import&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;eventManager&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;from&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;./globalState&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;default&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;class&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Child1&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;extends&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;React&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Component&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;constructor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;props&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
        &lt;span class=&amp;quot;kr&amp;quot;&gt;super&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;props&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;state&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;playing&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;false&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;eventManager&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;register&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;ChangeMode&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;::&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;refresh&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;nx&amp;quot;&gt;refresh&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setState&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;globalState&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;c1&amp;quot;&gt;// child2.js&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;default&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;class&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Child2&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;extends&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;React&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Component&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;refresh&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;eventManager&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;dispatch&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;ChangeMode&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;c1&amp;quot;&gt;// globalState.js&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;default&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;playing&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;false&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这样实际上也有一些问题，我们可以发现，如果将&lt;code&gt;globalState&lt;/code&gt;的变换交给一个单独的模块，将其称为&lt;code&gt;controller&lt;/code&gt;，便又成了经典的MVC模式。这种模式最后C这一层一般会非常臃肿庞大，所以小应用也就罢了，大应用.......&lt;br /&gt;
但好在我们还有Redux，并且不难发现，Redux和以上这种策略其实有一些思想上的相似。&lt;/p&gt;
&lt;h2&gt;Redux&lt;/h2&gt;
&lt;h3&gt;原理&lt;/h3&gt;
&lt;p&gt;在Redux之前，FB官方出过一个配套的库叫做&lt;code&gt;Flux&lt;/code&gt;，它从一定程度上解决了上述的问题，但无奈过于复杂，使得很多场景下难以使用。所幸不久后Redux应运而出，它脱胎于Flux，并借鉴了一些elm语言的思想，整体走函数式风格——单向数据流，无状态编程。Redux的基本思想是这样的：  &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;维护一个顶层数据仓库，称为&lt;code&gt;store&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;全局只能存在一个store，所有的状态都存在于其中。&lt;/li&gt;
&lt;li&gt;有一些被称为&lt;code&gt;reducer&lt;/code&gt;的方法，例如&lt;a href=&amp;quot;https://github.com/dtysky/BlogReworkPro/blob/master/Front/src/reducers/theme.js&amp;quot;&gt;reducer/theme.js&lt;/a&gt;，它是唯一可以准许被修改store中state的方法，每个reducer对应一个state，他接受上一次的state、一个&lt;code&gt;action&lt;/code&gt;的&lt;code&gt;type&lt;/code&gt;和可选的修改量，根据type来进行操作并返回下一级的state。&lt;/li&gt;
&lt;li&gt;一些被称为action的东西，例如&lt;a href=&amp;quot;https://github.com/dtysky/BlogReworkPro/blob/master/Front/src/actions/index.js&amp;quot;&gt;action/index.js&lt;/a&gt;，至于说&lt;code&gt;东西&lt;/code&gt;，因为这个东西比较抽象，只能说它定义了一些可预测的行为，他可以是&lt;code&gt;type&lt;/code&gt;，也可以是一些带有副作用的方法，一般和&lt;code&gt;dispatch&lt;/code&gt;方法配合使用。&lt;/li&gt;
&lt;li&gt;dispatch方法是Redux的核心方法，它是触发action的唯一途径。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;store&lt;/code&gt;、&lt;code&gt;action&lt;/code&gt;、&lt;code&gt;reducer&lt;/code&gt;和&lt;code&gt;dispatch&lt;/code&gt;，加上一个&lt;code&gt;getState&lt;/code&gt;，便构成了Redux。我们用store作为顶层实例，action定义行为，dispatch触发行为，reducer改变状态，getState获取状态，如下：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;Redux-原理&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2016_10_09a/2.svg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;这便是Redux的基本原理，十分简单。在默认状态下，所有使得数据的变动方法都是pure的，没有任何副作用，一切都是同步可预测的（所谓同步可预测，就是可以精确掌控在什么时刻的状态时什么样的），我们可以在初始化时赋予它任何状态，以此作为整个应用的开端，这也是Redux下的服务端渲染的基础。  &lt;/p&gt;
&lt;h3&gt;和React绑定&lt;/h3&gt;
&lt;p&gt;虽然Redux是一个通用性的库，但目前还是和React结合使用最多，在安装了&lt;code&gt;react-redux&lt;/code&gt;这个绑定之后，我们便可以利用它提供的&lt;code&gt;Provider&lt;/code&gt;组件和&lt;code&gt;connect&lt;/code&gt;方法将store和React的根级组件进行连接：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;const store = createStore(
    reducers,
    initState,
    applyMiddleware(...middleware)
);
const Root = &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;Provider&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;store=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;{store}&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;APP&lt;/span&gt; &lt;span class=&amp;quot;nt&amp;quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;/Provider&amp;gt;&lt;/span&gt;;

// APP
@connect(
    state =&amp;gt; ({...state})
)
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这里面，&lt;code&gt;reducers&lt;/code&gt;的定义可见&lt;a href=&amp;quot;https://github.com/dtysky/BlogReworkPro/blob/master/Front/src/reducers/index.js&amp;quot;&gt;reducers.js&lt;/a&gt;，它利用&lt;code&gt;combineReducers&lt;/code&gt;这个语法糖合并许多二级的reducer到根级reducer，并自动根据其生成二级的state树，注意里面的&lt;code&gt;initState&lt;/code&gt;，这是一个可选的初始状态，在服务端渲染中至关重要，而&lt;code&gt;applyMiddleware&lt;/code&gt;这个方法则是用于应用中间件的，中间件在下一节会有详解。&lt;code&gt;Provider&lt;/code&gt;组件将&lt;code&gt;APP&lt;/code&gt;这个应用根级组件包起来，作为新的根级组件。&lt;code&gt;connect&lt;/code&gt;这个方法通过一个方法（这里可以理解为&lt;code&gt;filter&lt;/code&gt;）筛选要传给下一级的state，由于是根级组件，所有状态都要传，所以将其解包即可。  &lt;/p&gt;
&lt;p&gt;这里不难发现，如果每一次状态改变都要从根组件向下传递，那么默认状况下，所有子级组件都会响应改变事件，可能会引起大量不必要的重绘或逻辑操作，这除了用上一章所言的&lt;code&gt;shouldComponentUpdate&lt;/code&gt;来屏蔽之外，还可以注意&lt;code&gt;connect&lt;/code&gt;这个方法提供的筛选，它实际上会帮我们做一次类似于内部的&lt;code&gt;shouldComponentUpdate&lt;/code&gt;，合理利用它甚至可以帮我们完全解决子级组件重绘的问题。  &lt;/p&gt;
&lt;p&gt;如此一来，我们便可以将一个应用的文件目录组织为这样：&lt;a href=&amp;quot;https://github.com/dtysky/BlogReworkPro/tree/master/Front/src&amp;quot;&gt;src&lt;/a&gt;，&lt;code&gt;components&lt;/code&gt;是React组件，&lt;code&gt;actions&lt;/code&gt;里面定义行为，&lt;code&gt;reducers&lt;/code&gt;里定义改变状态的方法。  &lt;/p&gt;
&lt;h3&gt;执行action&lt;/h3&gt;
&lt;p&gt;在理想状况下，数据的改变都是pure的，无副作用，整个过程中没有IO操作也没有异步调用，这样数据的流向非常清晰，不会混乱。我们只需要不断使用以下语句来改变状态即可：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;dispatch&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;actionTypes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;change&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;theme&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;current&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;theme&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;home&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;接下来的事情交给Redux自己去解决就好，它会根据这个action的type去适配reducer并修改相应的state，并从根组件经过一路筛选将状态传递到需要的组件中，有需求的子级组件监听到了变化便可以执行相应的操作。  &lt;/p&gt;
&lt;p&gt;这种理想的状况确实很美好，但是现实世界是复杂的，这种情况对于绝大多数SPA是不可能存在的——ajax是基本需求。所以我们便需要一些具有副作用的action来帮我们执行操作，这里就需要提到Redux的中间件机制了，他的中间件充分利用了高阶函数的特性，有兴趣的可以在官网自行查看原理，这里我们需要了解的仅仅是——中间件本质上就是类似于流水线的东西，一个action请求被发出后，Redux会将其轮流送入被注册的中间件，经过这些中间件之后再最终进入reducer之内改变状态。利用这个特点，我们便可以使用中间件来拦截action请求，实现副作用。  &lt;/p&gt;
&lt;p&gt;Redux官方提供了这样的一个中间件——&lt;a href=&amp;quot;https://github.com/gaearon/redux-thunk&amp;quot;&gt;redux-thunk&lt;/a&gt;，其原理十分简单，代码只有寥寥几行，不再赘述。通过它，我们便可以定义一种特殊的action，并在其中执行有副作用的操作：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;function&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;getListSource&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;dispatch&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;dispatch&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;actionTypes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;get&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;list&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;waiting&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;request&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;get&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
            &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;timeout&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1000&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
            &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;then&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
                &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;list&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;body&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;content&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;||&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[];&lt;/span&gt;
                &lt;span class=&amp;quot;nx&amp;quot;&gt;dispatch&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;actionTypes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;get&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;list&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;successful&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;list&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
                &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;Promise&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;resolve&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
            &lt;span class=&amp;quot;p&amp;quot;&gt;})&lt;/span&gt;
            &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;catch&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;err&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
                &lt;span class=&amp;quot;nx&amp;quot;&gt;dispatch&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;actionTypes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;get&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;list&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;failed&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
                &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;Promise&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;reject&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;err&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
            &lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;c1&amp;quot;&gt;// 执行&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;dispatch&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getListSource&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;/someting&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;如此，异步操作也可以在Redux中实现了，我们在action中发起了一次ajax请求，并根据返回结果执行不同的pure-acition，来改变最终的状态。这种操作在Blog项目中共有三个——&lt;a href=&amp;quot;https://github.com/dtysky/BlogReworkPro/blob/master/Front/src/actions/source.js&amp;quot;&gt;action/source.js&lt;/a&gt;，读者可以通过这些了解更多。  &lt;/p&gt;
&lt;p&gt;中间件的用处很多，除了这个之外，有一个很常用的中间件是&lt;a href=&amp;quot;https://github.com/evgenyrodionov/redux-logger&amp;quot;&gt;redux-logger&lt;/a&gt;，它用于调试模式下的状态路径追踪，将其应用后可以在浏览器控制台看到如下输出：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;redux-logger&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2016_10_09a/3.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;这会使得调试变得方便清晰起来，也是Redux使得数据可预测的一个直观体现。&lt;/p&gt;
&lt;h3&gt;reducer改变state&lt;/h3&gt;
&lt;p&gt;在接收到dispatch的请求之后，Redux会将此次的action分发到对应的reducer，它实现的分发方式本质上就是“轮询”，正如前面所言，&lt;code&gt;combinReducers&lt;/code&gt;只是一个语法糖，实际上在分发是还是会走一遍所有的reducer，reducer接收到参数后进入适当的分支，从而完成对状态的修改。  &lt;/p&gt;
&lt;p&gt;这里要特别注意，Redux的函数式特性决定了它的上一次状态是不可变的，你不能去通过修改上一次状态然后将其返回作为下一次的状态，而是必须建立一个新的状态来修改并返回它。这在一些情况下非常容易实现，但如果状态层级比较深、本身比较复杂，JS自己的对象内部又是地址引用的，所以新建并维护状态就可能变得格外复杂。虽然可以用&lt;code&gt;lodash&lt;/code&gt;这样的工具库来简化一些操作，但仍然没有根本上解决问题，而这时候，FB官方的&lt;code&gt;Immutable&lt;/code&gt;库便出现了，它和Redux的相容性非常好。&lt;/p&gt;
&lt;h2&gt;Immutable.js&lt;/h2&gt;
&lt;p&gt;Immutable.js是FB出的一个不可变数据结构库，它提供了一系列的数据结构和丰富的API，其数据结构有自己的内部实现，和JS原生对象的原理有所不同。作为使用者，我们需要关心的只是他提供的接口和特性。它有一个非常重要的特性就是不可变性，也就是说，Immutable对象自身是不可被修改的，一切对它的修改只会体现在修改它的方法的返回值上，它本身是不变的，这和Redux的理念不谋而合，加之其丰富得不亚于lodash的API，和Redux配套使用基本可以算是最佳组合。  &lt;/p&gt;
&lt;p&gt;在实际使用中，Immutable最常见的是下列API：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;import&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Immutable&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;from&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;immutable&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;c1&amp;quot;&gt;// 将JS对象转换为Immutable对象&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;im&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Immutable&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fromJS&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;obj&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;c1&amp;quot;&gt;// 将Immutable对象转换为JS对象&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;obj&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;im&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;toJS&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;span class=&amp;quot;c1&amp;quot;&gt;// 将Immutable对象转换为JSON对象&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;json&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;im&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;toJSON&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;span class=&amp;quot;c1&amp;quot;&gt;// 判断对象中是否有值&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;bool&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;im&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;has&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;c1&amp;quot;&gt;// 判断两个对象是否相等&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;bool&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;im1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;equals&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;im2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;c1&amp;quot;&gt;// 赋值/深度赋值，SetIn方法将依次查找到最后一层的key并执行赋值&lt;/span&gt;
&lt;span class=&amp;quot;c1&amp;quot;&gt;// 要注意这两个方法并不会自动将obj转换为immutable对象存储！&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;newIm&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;im&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;set&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;key&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;value&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;newIm&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;im&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setIn&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;([&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;key1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;key2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;......],&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;value&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;c1&amp;quot;&gt;// 取值/深度取值&lt;/span&gt;
&lt;span class=&amp;quot;c1&amp;quot;&gt;// 深度取值第二个参数是一个可选的默认值，这是一种optional的实现，如果找不到值，将会返回默认值&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;value&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;im&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;get&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;key&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;value&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;im&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getIn&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;([&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;key1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;key2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;......],&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;defaultValue&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;c1&amp;quot;&gt;// 归并，obj可以是Immutable对象也可以是JS对象&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;newIm&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;im&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;merge&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;obj&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;在绝大多数应用中，这几个API可以撑起半边天，尤其是&lt;code&gt;merge&lt;/code&gt;这个方法会被经常使用（我们最好保证可以用Immutable的地方都用它）。此外&lt;code&gt;equals&lt;/code&gt;方法也可以在&lt;code&gt;shouldComponentUpdate&lt;/code&gt;中配合使用，使得两次props的对比变得非常简单。  &lt;/p&gt;
&lt;p&gt;React+Redux+Immutable这个组合由此完成了，合理使用它们，可以使我们的开发变得条理明晰，维护方便（当前，前提是配好那一堆恶心的环境，这里不再赘述，详情可以看这个Blog项目的配置文件（还没有单元测试相关的东西，如果加上单元测试还需要趟更多的坑，比如&lt;a href=&amp;quot;https://github.com/dtysky/MoeNotes&amp;quot;&gt;这个项目&lt;/a&gt;））。&lt;/p&gt;
&lt;h2&gt;React-router&lt;/h2&gt;
&lt;p&gt;至此，一个简单的SPA的构造条件便都满足，但这又会带来一个问题——SEO相关的问题。在这种SPA的模式下，页面是没有自己的独立URL的，这样不利于伪静态页面的生成，不便于搜索引擎索引，也不便于用户访问，&lt;strong&gt;前端路由&lt;/strong&gt;就是来解决这个问题的。其核心在于利用js代码截获浏览器history中的url，通过捕获跳转或回退事件来完成应用内部的跳转行为，并同步浏览器的地址栏。  &lt;/p&gt;
&lt;p&gt;有了前端路由，我们便可以实现对SPA的分页，基于此，我们可以指定各自的url给各个页面，这使得页面的静态化成为可能。React-router则是FB官方给React提供的前端路由库，使用它，无论是搜索引擎还是用户都可以直接通过静态的url来访问单个页面。同时值得一提的是，React-router也可以在服务端使用，这样便可以实现WEB和服务端渲染的同构实现。在Blog项目中，这一部分在这个文件中&lt;a href=&amp;quot;https://github.com/dtysky/BlogReworkPro/blob/master/Front/src/routes.js&amp;quot;&gt;src/routes&lt;/a&gt;，其一个经典的实现如下：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;const routes = (
    &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;Route&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;path=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;/&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;component=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;{App}&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;IndexRoute&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;components=&lt;/span&gt;&lt;span class=&amp;quot;cp&amp;quot;&gt;{{&lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;content&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;Home&lt;/span&gt;&lt;span class=&amp;quot;cp&amp;quot;&gt;}}&lt;/span&gt; &lt;span class=&amp;quot;s&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;Route&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;path=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;archives(/:index)&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;components=&lt;/span&gt;&lt;span class=&amp;quot;cp&amp;quot;&gt;{{&lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;content&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;Archives&lt;/span&gt;&lt;span class=&amp;quot;cp&amp;quot;&gt;}}&lt;/span&gt; &lt;span class=&amp;quot;s&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;Route&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;path=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;category/:name(/:index)&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;components=&lt;/span&gt;&lt;span class=&amp;quot;cp&amp;quot;&gt;{{&lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;content&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;Category&lt;/span&gt;&lt;span class=&amp;quot;cp&amp;quot;&gt;}}&lt;/span&gt; &lt;span class=&amp;quot;s&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;Route&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;path=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;article/:name&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;components=&lt;/span&gt;&lt;span class=&amp;quot;cp&amp;quot;&gt;{{&lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;content&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;Article&lt;/span&gt;&lt;span class=&amp;quot;cp&amp;quot;&gt;}}&lt;/span&gt; &lt;span class=&amp;quot;s&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;gt;&lt;/span&gt;
        ......
        &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;Route&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;path=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;*&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;components=&lt;/span&gt;&lt;span class=&amp;quot;cp&amp;quot;&gt;{{&lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;content&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;NotFound&lt;/span&gt;&lt;span class=&amp;quot;cp&amp;quot;&gt;}}&lt;/span&gt; &lt;span class=&amp;quot;s&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;/Route&amp;gt;&lt;/span&gt;
);
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;code&gt;Route&lt;/code&gt;是React-router的核心组件，他的第一个属性&lt;code&gt;path&lt;/code&gt;是相对于根域名的路由地址，第二个&lt;code&gt;component&lt;/code&gt;或者&lt;code&gt;components&lt;/code&gt;是路由对应的组件。在这个路由中，根路径对应的组件是&lt;code&gt;APP&lt;/code&gt;，所以网站会将APP作为整个应用的入口，我们必须实现APP这个组件，并让它能够在不同路由下接受不同的components，来渲染出对应的DOM树。  &lt;/p&gt;
&lt;p&gt;在此Blog工程中，这个APP组件在&lt;a href=&amp;quot;https://github.com/dtysky/BlogReworkPro/blob/master/Front/src/app.js&amp;quot;&gt;src/app&lt;/a&gt;，它的&lt;code&gt;render&lt;/code&gt;方法中定义了许多基础的视图组件，并根据当前路由传来的&lt;code&gt;params&lt;/code&gt;和由对应路由中&lt;code&gt;components&lt;/code&gt;参数传来的&lt;code&gt;content&lt;/code&gt;来确认主视图中应当显示哪些组件，这个主视图实际上就是你当前在看的这个文章主体的位置。  &lt;/p&gt;
&lt;p&gt;比如在本页中，url为&lt;code&gt;http://dtysky.moe/article/Skill-2016_10_09_a&lt;/code&gt;，相对于根域名的地址是&lt;code&gt;/article/Skill-2016_10_09_a&lt;/code&gt;，它会走到&lt;code&gt;article/:name&lt;/code&gt;这个路由中，而这个路由中有个必选参数&lt;code&gt;name&lt;/code&gt;（形如&lt;code&gt;(/:index)&lt;/code&gt;这种参数就是非必须选的），当进入这个路由后，APP组件首先会接收到参数&lt;code&gt;this.props.param={name: Skill-2016_10_09_a}&lt;/code&gt;和&lt;code&gt;this.props.content=Article&lt;/code&gt;，接下来我们只需要使用&lt;code&gt;cloneElement&lt;/code&gt;方法将&lt;code&gt;content&lt;/code&gt;组件加上需要传入的参数放入DOM树中即可：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;render() {
    const {params, content} = this.props;
    return (
        &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
            ......
            {cloneElement(content, {params, ......})}
            ......
        &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    )
}
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;如此，当用户访问当前的这个url后，Article这个组件将会被装入DOM树，接受&lt;code&gt;params&lt;/code&gt;和其他的一些参数并渲染出响应的页面。  &lt;/p&gt;
&lt;p&gt;完成了这一切后，我们将Routes作为根级组件挂载到React-redux的&lt;code&gt;Provider&lt;/code&gt;下便可以完成和Redux的链接：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;const Root = (
    &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;Provider&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;store=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;{store}&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;Router&lt;/span&gt;
            &lt;span class=&amp;quot;na&amp;quot;&gt;routes=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;{routes}&lt;/span&gt;
            &lt;span class=&amp;quot;na&amp;quot;&gt;history=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;{browserHistory}&lt;/span&gt;
        &lt;span class=&amp;quot;nt&amp;quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;/Provider&amp;gt;&lt;/span&gt;
);
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;使用React-router要特别注意组件自身生命周期的问题，清晰了解路由跳转时组件的行为非常重要，请务必通读官方的这篇说明&lt;a href=&amp;quot;https://github.com/ReactTraining/react-router/blob/master/docs/guides/ComponentLifecycle.md&amp;quot;&gt;Component Lifecycle&lt;/a&gt;。  &lt;/p&gt;
&lt;h2&gt;More&lt;/h2&gt;
&lt;p&gt;至此，React最佳实践的第一部分就差不多了，之所以说第一部分，是因为上面这些并没有真正解决SEO、即首屏渲染的问题，这会涉及到React/Redux/React-router协作的服务端渲染，下一篇文章将会详细说明如何去操作。  &lt;/p&gt;
&lt;p&gt;知识可以学习，技术只能练习，这里只能说个脉络，更多请参考我的BlogReworkPro工程。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Sunday, 09 Oct 2016 21:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2016.10.09 21:00:article/Skill-2016_10_09_a</guid>
<category>React.js</category>
<category>Redux</category>
<category>React-router</category>
<category>Immutable</category>
<category>MVVM</category>
<category>ES6</category>
</item>

<item>
<title>【CSAPP】笔记-Cp3-3-控制与过程数据存储</title>
<link>http://dtysky.moe/article/Skill-2016_07_04_a</link>
<description>&lt;p&gt;CSAPP（深入理解计算机系统）第三章“程序的机器级表示”第二部分“控制、过程、和数据存储”的笔记和课后习题。 &lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/CSAPP&amp;quot;&gt;Github的同步工程在这&lt;/a&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;控制&lt;/h2&gt;
&lt;p&gt;汇编中使用控制语句来实现线性程序之外的跳转、选择等功能。 &lt;/p&gt;
&lt;h3&gt;条件码&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;条件码&lt;/strong&gt;用于表示一些CPU最近的状态，它们被存在一个单独的&lt;strong&gt;条件寄存器&lt;/strong&gt;中，它们是：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;CF：进位标志，最近操作发生了进位，可以检查无符号数的溢出。&lt;br /&gt;
ZF：零标志，最近操作结果为0。&lt;br /&gt;
SF：符号标志，最近操作的结果为负数。&lt;br /&gt;
OF：溢出标志，最近操作导致补码溢出。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;要注意的是，&lt;code&gt;lea&lt;/code&gt;系列指令不会不会改变这些标志，因为它本质上不是ALU运算而是地址运算；&lt;code&gt;inc&lt;/code&gt;和&lt;code&gt;dec&lt;/code&gt;会设置溢出标志但不会设置进位标志等......   &lt;/p&gt;
&lt;p&gt;还有一些特殊的指令只改变条件码而不发生实际运算，比如&lt;code&gt;test&lt;/code&gt;、&lt;code&gt;cmp&lt;/code&gt;，前者对应&lt;code&gt;sub&lt;/code&gt;、后者对应&lt;code&gt;and&lt;/code&gt;。  &lt;/p&gt;
&lt;p&gt;要访问这些条件码，有一系列的&lt;code&gt;set d&lt;/code&gt;指令被提供，其中&lt;code&gt;d&lt;/code&gt;是单字节寄存器，这组指令直接利用条件码去做出一些常用的判断，比如&lt;code&gt;setge d&lt;/code&gt;相当于给&lt;code&gt;d&lt;/code&gt;赋予&lt;code&gt;~(SF ^ OF)&lt;/code&gt;，即前一个运算的两个操作数是否存在大于关系。  &lt;/p&gt;
&lt;h3&gt;跳转&lt;/h3&gt;
&lt;p&gt;跳转指令&lt;code&gt;jmp .L&lt;/code&gt;和&lt;code&gt;jmp *e*&lt;/code&gt;会导致程序在执行时切换到另一个位置。前一种方式中，它的参数&lt;code&gt;L&lt;/code&gt;是一个&lt;strong&gt;标示符&lt;/strong&gt;，这个标示符类似于高级语言中的函数名，用于标示一段程序的开始，汇编中就是用这种方式来管理子程序的；后一种方式中，它的参数&lt;code&gt;e&lt;/code&gt;是一个寄存器或者以寄存器为地址的&lt;code&gt;(%eax)&lt;/code&gt;这种存储器的值，这些值将会给当做跳转地址。&lt;br /&gt;
还有一些跳转叫做&lt;strong&gt;条件跳转&lt;/strong&gt;，它们是基本跳转语句的特化版本，比如&lt;code&gt;je&lt;/code&gt;就是当&lt;code&gt;ZF&lt;/code&gt;位为1时跳转之类的。它们也是高级语言条件语句的基础。  &lt;/p&gt;
&lt;p&gt;至于C语言的条件语句翻译成汇编，一般都是先开一个条件跳转语句，然后后面更上else的内容。&lt;/p&gt;
&lt;h3&gt;循环&lt;/h3&gt;
&lt;p&gt;循环语句在C中有&lt;code&gt;do-while&lt;/code&gt;、&lt;code&gt;while&lt;/code&gt;和&lt;code&gt;for&lt;/code&gt;，太基础就不细说了。  &lt;/p&gt;
&lt;p&gt;&lt;code&gt;do-while&lt;/code&gt;它们可以对应汇编中的&lt;code&gt;goto&lt;/code&gt;和条件跳转指令的组合，像是：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nl&amp;quot;&gt;loop:&lt;/span&gt;
    &lt;span class=&amp;quot;nf&amp;quot;&gt;xxx&lt;/span&gt;
    &lt;span class=&amp;quot;nf&amp;quot;&gt;je&lt;/span&gt; &lt;span class=&amp;quot;no&amp;quot;&gt;next&lt;/span&gt;
    &lt;span class=&amp;quot;nf&amp;quot;&gt;goto&lt;/span&gt; &lt;span class=&amp;quot;no&amp;quot;&gt;loop&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;code&gt;while&lt;/code&gt;和&lt;code&gt;do-while&lt;/code&gt;类似，但它的判断放在loop段的开始，这允许产生零长度的循环：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nl&amp;quot;&gt;loop:&lt;/span&gt;
    &lt;span class=&amp;quot;nf&amp;quot;&gt;je&lt;/span&gt; &lt;span class=&amp;quot;no&amp;quot;&gt;next&lt;/span&gt;
    &lt;span class=&amp;quot;nf&amp;quot;&gt;xxx&lt;/span&gt;
    &lt;span class=&amp;quot;nf&amp;quot;&gt;goto&lt;/span&gt; &lt;span class=&amp;quot;no&amp;quot;&gt;loop&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;code&gt;for&lt;/code&gt;一般等价于&lt;code&gt;while&lt;/code&gt;形式，是另一种写法。但当和&lt;code&gt;continue&lt;/code&gt;语句合作是可能有例外。详见习题24。&lt;/p&gt;
&lt;h3&gt;条件传送语句&lt;/h3&gt;
&lt;p&gt;条件传送语句是现代CPU都有的一条指令，它本质上是以下C代码的翻译：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;absdiff&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;nl&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;在汇编中，会先计算好&lt;code&gt;y-x&lt;/code&gt;和&lt;code&gt;x-y&lt;/code&gt;，然后通过一条&lt;code&gt;cmovl&lt;/code&gt;指令（&lt;code&gt;l&lt;/code&gt;表示小于）直接判断前面&lt;code&gt;cmp&lt;/code&gt;的比较结果即条件码然后送走&lt;code&gt;y-x&lt;/code&gt;或者&lt;code&gt;x-y&lt;/code&gt;而不是通过条件跳转。  这样做的效率一般会很高，这是由于CPU内部的流水线采用的分支预测机制。因为对于此指令，处理器只是读数据、检查条件码、然后更新或者不动目的寄存器，不会有额外的跳转。  &lt;/p&gt;
&lt;p&gt;当然这个指令并非时时有效，由于它要事先计算两个分支的结果，所以可能会造成无谓的计算浪费，也受编译器的影响。  &lt;/p&gt;
&lt;h3&gt;switch指令&lt;/h3&gt;
&lt;p&gt;对应于C中的switch语句，当分支较多并且每个分支的条件间隔较小时，会生成一个&lt;strong&gt;跳转表&lt;/strong&gt;，有个这个表，可以使得分支实现和复杂度无关，相对于经典的条件转移，利用查表的方式可以说复杂度都是固定的，是并行判断。  &lt;/p&gt;
&lt;h2&gt;过程&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;过程调用&lt;/strong&gt;将数据和控制进行跳转，而后又可以恢复现场继续执行刚才的操作。这是通过&lt;strong&gt;转移到控制&lt;/strong&gt;和&lt;strong&gt;转移出控制&lt;/strong&gt;实现的。  &lt;/p&gt;
&lt;h3&gt;栈帧&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;栈帧&lt;/strong&gt;实际上就是栈的一种应用，它是为单个过程所分配的栈，由存放于&lt;code&gt;%ebp&lt;/code&gt;中的帧指针和存放于&lt;code&gt;%esp&lt;/code&gt;中的栈指针控制。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;栈帧&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2016-07-04a/1.png&amp;quot; /&gt;  &lt;/p&gt;
&lt;h3&gt;转移控制&lt;/h3&gt;
&lt;p&gt;当一个过程被&lt;code&gt;call&lt;/code&gt;指令调用时，首先将返回地址入栈，然后当前一些需要保护的局部变量什么的&lt;code&gt;入栈&lt;/code&gt;，保护现场，随后跳转到被调用过程 的首地址，调用结束后再使用&lt;code&gt;leave&lt;/code&gt;指令做好准备，之后&lt;code&gt;ret&lt;/code&gt;指令恢复现场并返回跳转前的地址继续。 &lt;/p&gt;
&lt;p&gt;调用控制中的寄存器分配是约定俗成的&lt;code&gt;%eax&lt;/code&gt;、&lt;code&gt;%edx&lt;/code&gt;和&lt;code&gt;ecx&lt;/code&gt;被分配给被调用者，可以被覆盖，而&lt;code&gt;%ebx&lt;/code&gt;、&lt;code&gt;%esi&lt;/code&gt;和&lt;code&gt;%edi&lt;/code&gt;则分配给调用者，再覆盖之前要先入栈以便恢复。  &lt;/p&gt;
&lt;p&gt;对于&lt;code&gt;递归过程&lt;/code&gt;，编译器会将每次函数对自身的调用都视为调用一个其他的函数，本质上并无区别。&lt;/p&gt;
&lt;h2&gt;数组和结构体&lt;/h2&gt;
&lt;p&gt;数组是一种聚合数据类型，它在C中实现很简单，为&lt;code&gt;T A[N]&lt;/code&gt;的形式，这段代码会在内存中分配N个数据类型T需要的内存空间，A作为一个标示符，是一个指向数组开头的指针，而A和下标的组合即可给这个指针加上偏移来访问数组内的任意数据。  &lt;/p&gt;
&lt;p&gt;比如定义数组&lt;code&gt;char A[8]&lt;/code&gt;，首先分配8个字节的内存，然后将A指向这八个字节的开头，&lt;code&gt;A[4]&lt;/code&gt;则构造&lt;code&gt;A+4&lt;/code&gt;的指针，可以用其访问数组中的第四个数据。这在汇编中是&lt;code&gt;movl (%edx, %ecx, 4), %eax&lt;/code&gt;。  &lt;/p&gt;
&lt;p&gt;本质上，我们使用&lt;code&gt;A[i]&lt;/code&gt;相当于先得到元素&lt;code&gt;A[i]&lt;/code&gt;的引用，然后用&lt;code&gt;*A[i]&lt;/code&gt;来获得该地址的值。  &lt;/p&gt;
&lt;h3&gt;嵌套数组&lt;/h3&gt;
&lt;p&gt;即&lt;strong&gt;多维数组&lt;/strong&gt;，比如&lt;code&gt;int A[5][5]&lt;/code&gt;，这和一般数组没什么两样，只不过在计算地址的时候会多一套工序，考虑你是在一个二维矩阵中取值就可以了，比如取值&lt;code&gt;A[i][j]&lt;/code&gt;，那么地址就是&lt;code&gt;A + 4(5i + j)&lt;/code&gt;。  &lt;/p&gt;
&lt;h3&gt;定长和变长数组&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;定长数组&lt;/strong&gt;是指大小在编译期就能就确定的的数组，而&lt;strong&gt;变长数组&lt;/strong&gt;则不然，可能会在运行时动态改变。对于前者，尤其是多维的情况下，在编译的时候可以将其地址计算优化为常数乘法，而后者不但不能事先分配内存，必须在需要的时候才动态运算和分配，并且在取值时也要动态使用乘法来计算，所以性能会有所下降。&lt;/p&gt;
&lt;h3&gt;结构&lt;/h3&gt;
&lt;p&gt;C中的结构&lt;code&gt;struct&lt;/code&gt;声明创建一个数据类型，将可能不同类型的对象聚合到一个对象，不同的对象用名字来引用。它本质上是方便编程的，因为对于数据本身而言，其在内存上是不变的，结构仅仅是改变去解释他们的方式，每个名字对应一个偏移，而这个偏移用于计算地址，比如：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;struct&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;S&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kt&amp;quot;&gt;char&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;c&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kt&amp;quot;&gt;short&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;s&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;code&gt;S&lt;/code&gt;总共占据7个字节，如果其首地址为0，则&lt;code&gt;S.i&lt;/code&gt;地址为0，&lt;code&gt;S.c&lt;/code&gt;为4，&lt;code&gt;S.s&lt;/code&gt;为5。  &lt;/p&gt;
&lt;p&gt;由于结构的存在，我们可以非常方便得管理聚合型数据，合理利用它也可以免除数据类型转换、新建内存的开销。  &lt;/p&gt;
&lt;p&gt;结构中的对象也可以是指针，这可以用于仿造OOP，比如将指针指向一个函数。  &lt;/p&gt;
&lt;h3&gt;联合&lt;/h3&gt;
&lt;p&gt;联合&lt;code&gt;union&lt;/code&gt;和&lt;code&gt;struct&lt;/code&gt;不同，它用于用不同的类型去解释相同的数据，联合中所有部分的地址偏移都是一致的，它们只是用不同的方式去解释一段数据。比如：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;union&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;U&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kt&amp;quot;&gt;char&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;c&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kt&amp;quot;&gt;double&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;code&gt;U&lt;/code&gt;会占据8字节，即&lt;code&gt;double&lt;/code&gt;的长度，其中&lt;code&gt;i&lt;/code&gt;、&lt;code&gt;c&lt;/code&gt;、&lt;code&gt;d&lt;/code&gt;的地址偏移都是0。&lt;/p&gt;
&lt;h3&gt;数据对齐&lt;/h3&gt;
&lt;p&gt;数据对齐对于提高存储器读写性能很关键，原则来讲，如果一个类型占用字节数是&lt;code&gt;K&lt;/code&gt;，那么其地址应当是&lt;code&gt;K&lt;/code&gt;的倍数。这就要求&lt;code&gt;short&lt;/code&gt;类型的地址最低位为0而&lt;code&gt;int&lt;/code&gt;型为00。对于Windows系统，对齐非常严格按照上述规则执行，而对于unix，8字节只需对齐4字节。&lt;br /&gt;
由于&lt;code&gt;SSE&lt;/code&gt;指令要求存储器地址是16的倍数，所以栈帧的长度都是16的整数倍。  &lt;/p&gt;
&lt;h2&gt;存储器越界引用和缓冲区溢出&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;缓冲区溢出&lt;/strong&gt;是一个问题，比如在存储一个字符串时，如果没有分配足够的空间，便会发生溢出。发生溢出时，会错误覆盖栈帧中寄存器的值，这会导致被保存的现场不可恢复、代码无法正确返回等等，可能受到严重的攻击。比如库函数&lt;code&gt;gets&lt;/code&gt;、&lt;code&gt;strcpy&lt;/code&gt;等都有这个问题。好一点的方法是使用他们的具有限制最大长度的替代函数&lt;code&gt;fgets&lt;/code&gt;等。&lt;/p&gt;
&lt;p&gt;这种错误常被用于网络攻击，比如给字符串内巧妙加入一些可执行代码的字节编码，然后让其溢出，覆盖返回地址，这样就可以执行攻击代码。  &lt;/p&gt;
&lt;h3&gt;对抗&lt;/h3&gt;
&lt;p&gt;对抗溢出攻击有几种方式。其中一种是&lt;strong&gt;栈随机化&lt;/strong&gt;，它给程序端之前分配一些随机的、不使用的空闲空间，来是的程序的实际地址发生偏移，这样可以让程序字段的地址不可预测，是的攻击程序对地址的把握困难，这可以消除一部分影响。&lt;strong&gt;栈随机化&lt;/strong&gt;被扩展后是&lt;strong&gt;地址空间布局随机化&lt;/strong&gt;(ASLR)，除了栈之外，全局变量、堆等都要随机化。  &lt;/p&gt;
&lt;p&gt;另一种方法是&lt;strong&gt;栈破坏检测&lt;/strong&gt;，也就是在栈帧的局部缓冲区与栈状态之间存储一个&lt;strong&gt;哨兵&lt;/strong&gt;，被称为&lt;strong&gt;栈保护者&lt;/strong&gt;，程序运行中不断检测它，当其被改变，即破坏时便会识别出被入侵。  &lt;/p&gt;
&lt;p&gt;还有一种就是&lt;strong&gt;限制可执行区域&lt;/strong&gt;，即限定代码可以存储在什么区域。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;习题&lt;/h2&gt;
&lt;h3&gt;13&lt;/h3&gt;
&lt;p&gt;A. &lt;code&gt;l&lt;/code&gt;为32位，比较为对补码的小于，所以为&lt;code&gt;int&lt;/code&gt;。&lt;br /&gt;
B. &lt;code&gt;w&lt;/code&gt;为16位，比较对补码，所以为&lt;code&gt;short&lt;/code&gt;。&lt;br /&gt;
C. &lt;code&gt;b&lt;/code&gt;为8位，比较对无符号数，所以为&lt;code&gt;uchar&lt;/code&gt;。&lt;br /&gt;
D. &lt;code&gt;l&lt;/code&gt;为32位，比较是不等于或非零操作，所以为&lt;code&gt;int&lt;/code&gt;活&lt;code&gt;uint&lt;/code&gt;。  &lt;/p&gt;
&lt;h3&gt;14&lt;/h3&gt;
&lt;p&gt;和13类似。  &lt;/p&gt;
&lt;h3&gt;15&lt;/h3&gt;
&lt;p&gt;A. 0x8048291 + (0x05 = 5)&lt;br /&gt;
B. 0x8048359 + (0xe7 = -25) 
C. 0x8048391 - (0x12 = 18)&lt;br /&gt;
D. 0x80482c4 + (0xffffffe0 = -32)&lt;br /&gt;
E. 0x8049ffc的小端是0xfc9f0408&lt;/p&gt;
&lt;h3&gt;16&lt;/h3&gt;
&lt;p&gt;A. 不想写goto，略过。&lt;br /&gt;
B. 本质上是吧&lt;code&gt;p &amp;amp;&amp;amp; a &amp;gt; 0&lt;/code&gt;拆分为了两部分来实现。&lt;/p&gt;
&lt;h3&gt;17&lt;/h3&gt;
&lt;p&gt;A. 简单，略过。&lt;br /&gt;
B. 两种方法等价，但是&lt;code&gt;!t&lt;/code&gt;这种判断在没有&lt;code&gt;else&lt;/code&gt;的状况下可以节省代码量，和&lt;code&gt;if (!x) return;&lt;/code&gt;一样。  &lt;/p&gt;
&lt;h3&gt;18&lt;/h3&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;test&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
    &lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;val&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
            &lt;span class=&amp;quot;n&amp;quot;&gt;val&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt;
            &lt;span class=&amp;quot;n&amp;quot;&gt;val&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;；&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;val&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;val&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;19&lt;/h3&gt;
&lt;p&gt;A. 这是一个数学问题，&lt;code&gt;$n! \le 2^{32} - 1$&lt;/code&gt;即可。&lt;br /&gt;
B. &lt;code&gt;$n! \le 2^{63} - 1$&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;20&lt;/h3&gt;
&lt;p&gt;A.   &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;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;%eax&lt;/td&gt;
&lt;td&gt;x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;%ecx&lt;/td&gt;
&lt;td&gt;y&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;%edx&lt;/td&gt;
&lt;td&gt;n&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;B. &lt;code&gt;do&lt;/code&gt;的是&lt;code&gt;body&lt;/code&gt;，&lt;code&gt;while&lt;/code&gt;后的是&lt;code&gt;test&lt;/code&gt;，前者对应&lt;code&gt;.L2&lt;/code&gt;中的&lt;code&gt;addl&lt;/code&gt;、&lt;code&gt;imull&lt;/code&gt;、&lt;code&gt;subl&lt;/code&gt;，后者则是&lt;code&gt;testl&lt;/code&gt;、&lt;code&gt;jle&lt;/code&gt;、&lt;code&gt;cmpl&lt;/code&gt;、&lt;code&gt;jl&lt;/code&gt;。&lt;br /&gt;
C. 略过。&lt;/p&gt;
&lt;h3&gt;21&lt;/h3&gt;
&lt;p&gt;A. 这是&lt;code&gt;a+b&lt;/code&gt;的结果。&lt;br /&gt;
剩下的题略过。&lt;/p&gt;
&lt;h3&gt;22&lt;/h3&gt;
&lt;p&gt;基础，略过。  &lt;/p&gt;
&lt;h3&gt;23&lt;/h3&gt;
&lt;p&gt;基础，略过。  &lt;/p&gt;
&lt;p&gt;此函数的目的在于将传入参数&lt;code&gt;x&lt;/code&gt;进行按位镜像。  &lt;/p&gt;
&lt;h3&gt;24&lt;/h3&gt;
&lt;p&gt;A. 会导致无限循环。&lt;br /&gt;
B. 很简单，在body之前加上判断跳转回loop。&lt;/p&gt;
&lt;h3&gt;25&lt;/h3&gt;
&lt;p&gt;概率题，套上面公式可得&lt;code&gt;$T_MP = (31 - 16)*2 = 30$&lt;/code&gt;个周期，错误时需要46个周期。  &lt;/p&gt;
&lt;h3&gt;26&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;\&lt;/code&gt;运算符，本质上是为了解决负数情况下的偏置问题。&lt;/p&gt;
&lt;h3&gt;27&lt;/h3&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;test&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
    &lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;val&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
            &lt;span class=&amp;quot;n&amp;quot;&gt;val&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
            &lt;span class=&amp;quot;n&amp;quot;&gt;val&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;^&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;val&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;val&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;28&lt;/h3&gt;
&lt;p&gt;略过。&lt;/p&gt;
&lt;h3&gt;29&lt;/h3&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;switcher&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;c&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
    &lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;answerl&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;switch&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;case&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;5&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;
            &lt;span class=&amp;quot;n&amp;quot;&gt;c&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;b&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;^&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;15&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;case&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;
            &lt;span class=&amp;quot;n&amp;quot;&gt;answer&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;c&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;112&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
            &lt;span class=&amp;quot;k&amp;quot;&gt;break&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;case&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;case&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;7&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;
            &lt;span class=&amp;quot;n&amp;quot;&gt;answer&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;c&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
            &lt;span class=&amp;quot;k&amp;quot;&gt;break&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;case&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;
            &lt;span class=&amp;quot;n&amp;quot;&gt;answer&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
            &lt;span class=&amp;quot;k&amp;quot;&gt;break&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;default&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;
            &lt;span class=&amp;quot;n&amp;quot;&gt;answer&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;answer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;30&lt;/h3&gt;
&lt;p&gt;A. 被设置为&lt;code&gt;pop&lt;/code&gt;指令的地址。&lt;br /&gt;
B. 这不是一个过程调用，他没有&lt;code&gt;ret&lt;/code&gt;，控制和指令顺序一致，返回值从栈中弹出。&lt;br /&gt;
C. 这是IA32将PC的值放到整数寄存器中唯一的方法。  &lt;/p&gt;
&lt;h3&gt;31&lt;/h3&gt;
&lt;p&gt;除了必须被保存的三个寄存器之外，其他寄存器随意被被更改，不会影响调用者的行为。  &lt;/p&gt;
&lt;h3&gt;32&lt;/h3&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;fun&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;short&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;c&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;char&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;33&lt;/h3&gt;
&lt;p&gt;略过。这道题主要让我们明白编译器会分配大量不会使用的空间，这可能是为了&lt;strong&gt;数据对齐&lt;/strong&gt;。&lt;/p&gt;
&lt;h3&gt;34&lt;/h3&gt;
&lt;p&gt;A. &lt;code&gt;%ebx&lt;/code&gt;保存x的值，所以它可以被用于计算表达式结果。&lt;br /&gt;
B. &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;rfun&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;unsigned&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;==&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;kt&amp;quot;&gt;unsigned&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;nx&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;rv&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;rfun&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;nx&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x01&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;rv&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; 
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;C. 它计算x中所有位的和。&lt;/p&gt;
&lt;h3&gt;35&lt;/h3&gt;
&lt;p&gt;GCC为&lt;code&gt;long double&lt;/code&gt;分配12个字节。&lt;br /&gt;
指针类型固定占用4个字节。&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;th&gt;起始位置&lt;/th&gt;
&lt;th&gt;元素i&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;short S[7]&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;$x_S$&lt;/td&gt;
&lt;td&gt;$x_S+2i$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;short *T[3]&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;$x_T$&lt;/td&gt;
&lt;td&gt;$x_T+4i$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;short **U[6]&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;$x_U$&lt;/td&gt;
&lt;td&gt;$x_U+i$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;long double V[8]&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;96&lt;/td&gt;
&lt;td&gt;$x_Y$&lt;/td&gt;
&lt;td&gt;$x_Y+12i$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;long double *W[4]&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;$x_W$&lt;/td&gt;
&lt;td&gt;$x_W+4i$&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;36&lt;/h3&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;th&gt;汇编代码&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;S+1&lt;/td&gt;
&lt;td&gt;short *&lt;/td&gt;
&lt;td&gt;$x_S+2$&lt;/td&gt;
&lt;td&gt;leal 2(%edx), %eax&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S[3]&lt;/td&gt;
&lt;td&gt;short&lt;/td&gt;
&lt;td&gt;$M[x_S+6]$&lt;/td&gt;
&lt;td&gt;movw 6(%edx), %ax&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;amp;S[i]&lt;/td&gt;
&lt;td&gt;short *&lt;/td&gt;
&lt;td&gt;$x_S+2i$&lt;/td&gt;
&lt;td&gt;leal (%edx, %ecx, 2), %eax&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S[4*i+1]&lt;/td&gt;
&lt;td&gt;short&lt;/td&gt;
&lt;td&gt;$[x_S+8i+2]$&lt;/td&gt;
&lt;td&gt;movw 2(%edx, %ecx, 8), %ax&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S+i-5&lt;/td&gt;
&lt;td&gt;short *&lt;/td&gt;
&lt;td&gt;$[x_S+2i-10]$&lt;/td&gt;
&lt;td&gt;leal -10(%edx, %ecx, 2), %eax&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;37&lt;/h3&gt;
&lt;p&gt;M = 5, N = 7&lt;/p&gt;
&lt;h3&gt;38&lt;/h3&gt;
&lt;p&gt;详见代码。  &lt;/p&gt;
&lt;p&gt;这样的写法对特定的规则优化，能够消除乘法，提高效率。  &lt;/p&gt;
&lt;h3&gt;39&lt;/h3&gt;
&lt;p&gt;A. 0，4，8，16&lt;br /&gt;
B. 24个字节&lt;br /&gt;
C. &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;void&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;sp_init&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;struct&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;prob&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;sp&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;s&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;sp&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;s&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;sp&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;err&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sp&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;s&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;sp&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;next&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;sp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;40&lt;/h3&gt;
&lt;p&gt;略过。&lt;/p&gt;
&lt;h3&gt;41&lt;/h3&gt;
&lt;p&gt;A. 4字节对齐。&lt;br /&gt;
B. 4字节对齐。&lt;br /&gt;
C. 2字节对齐。&lt;br /&gt;
D. 4字节对齐。&lt;br /&gt;
E. 4字节对齐。  &lt;/p&gt;
&lt;p&gt;看基本类型里最大的&lt;code&gt;K&lt;/code&gt;。  &lt;/p&gt;
&lt;h3&gt;42&lt;/h3&gt;
&lt;p&gt;A. 基础，略过。&lt;br /&gt;
B. 4字节对齐。&lt;br /&gt;
C. 按照字节大小降序排列即可，最终总和是32。  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Monday, 04 Jul 2016 21:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2016.07.04 21:00:article/Skill-2016_07_04_a</guid>
<category>CSAPP</category>
<category>计算机系统</category>
<category>汇编</category>
<category>控制</category>
<category>过程</category>
<category>数据</category>
</item>

<item>
<title>【Javascript】实现事件管理器</title>
<link>http://dtysky.moe/article/Skill-2016_07_01_b</link>
<description>&lt;p&gt;描述如何在Js中事件事件管理器，在别的语言中也差不多。  &lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;在Js中实现事件机制，可以用以下的一个类（ES6写法）实现。这是一个事件管理器，包含了注册、解除注册、发送事件三个功能，这三个功能一般足以满足轻量的事件驱动需求。  &lt;/p&gt;
&lt;p&gt;事件驱动器核心是一个hash table&lt;code&gt;_events&lt;/code&gt;，他的键是事件名称，值是一个由方法构成的数组。&lt;br /&gt;
&lt;code&gt;register&lt;/code&gt;函数用于注册事件，新建事件或者向事件中添加方法；&lt;code&gt;unregister&lt;/code&gt;解除注册，从事件中移除方法或者自动移除事件；&lt;code&gt;dispatch&lt;/code&gt;方法通过事件的名字执行事件，按照注册的顺序顺序执行。&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;class&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;EventManager&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;constructor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(){&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_events&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{};&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;nx&amp;quot;&gt;register&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;event&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;method&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_events&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;event&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;undefined&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
            &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_events&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;event&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[];&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_events&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;event&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;push&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;method&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;nx&amp;quot;&gt;unregister&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;event&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;method&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_events&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;event&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;undefined&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
            &lt;span class=&amp;quot;k&amp;quot;&gt;throw&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;NoEventException&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
                &lt;span class=&amp;quot;sb&amp;quot;&gt;`Can not unregister method, the event named &amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;si&amp;quot;&gt;${&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;event&lt;/span&gt;&lt;span class=&amp;quot;si&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;sb&amp;quot;&gt;&amp;quot; is not existed!`&lt;/span&gt;
            &lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
        &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_events&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;event&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;indexOf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;method&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
            &lt;span class=&amp;quot;k&amp;quot;&gt;throw&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;NoEventException&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
                &lt;span class=&amp;quot;sb&amp;quot;&gt;`Can not unregister method, the method named &amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;si&amp;quot;&gt;${&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;method&lt;/span&gt;&lt;span class=&amp;quot;si&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;sb&amp;quot;&gt;&amp;quot; is not in event &amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;si&amp;quot;&gt;${&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;event&lt;/span&gt;&lt;span class=&amp;quot;si&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;sb&amp;quot;&gt;&amp;quot;!`&lt;/span&gt;
            &lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_events&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;event&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;splice&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_events&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;event&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;length&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
            &lt;span class=&amp;quot;k&amp;quot;&gt;delete&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_events&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;event&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;nx&amp;quot;&gt;dispatch&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;event&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_events&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;event&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;undefined&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
            &lt;span class=&amp;quot;k&amp;quot;&gt;throw&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;NoEventException&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
                &lt;span class=&amp;quot;sb&amp;quot;&gt;`Can not dispatch event, the event named &amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;si&amp;quot;&gt;${&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;event&lt;/span&gt;&lt;span class=&amp;quot;si&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;sb&amp;quot;&gt;&amp;quot; is not existed!`&lt;/span&gt;
            &lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;_events&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;event&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;].&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;map&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
            &lt;span class=&amp;quot;nx&amp;quot;&gt;method&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
                &lt;span class=&amp;quot;nx&amp;quot;&gt;method&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
            &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Friday, 01 Jul 2016 21:20:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2016.07.01 21:20:article/Skill-2016_07_01_b</guid>
<category>Javascript</category>
<category>事件</category>
<category>事件管理</category>
</item>

<item>
<title>【CSAPP】笔记-Cp3-1-程序编码、数据格式、访问信息和数据操作</title>
<link>http://dtysky.moe/article/Skill-2016_07_01_a</link>
<description>&lt;p&gt;CSAPP（深入理解计算机系统）第三章“程序的机器级表示”第一部分“程序编码、数据格式、访问信息和数据操作”的笔记和课后习题。 &lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/CSAPP&amp;quot;&gt;Github的同步工程在这&lt;/a&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;计算机执行的是&lt;strong&gt;机器码&lt;/strong&gt;，即一条一条的01序列，这些序列经过CPU的&lt;strong&gt;控制译码器&lt;/strong&gt;译码后以数字电路的形式操作CPU的其他部件进行工作。但机器码对人的可读性极其差，所以有了&lt;strong&gt;汇编代码&lt;/strong&gt;。汇编代码可以看做是机器码的文本表现，十分接近于机器码的原本性质，但其抽象程度过低，难以构建大型工程，所以又有了&lt;strong&gt;C&lt;/strong&gt;这样系统级语言，他可读性和抽象性更高，同时不会损失太多性能，但对于日渐膨胀的效率需求，系统及语言很多时候也无法更上迭代的速度，所以便有了&lt;strong&gt;C#&lt;/strong&gt;这种编译型的高级语言和&lt;strong&gt;Python&lt;/strong&gt;这种解释型的脚本语言，他们比&lt;strong&gt;C&lt;/strong&gt;更上层，开发效率更高，门槛更低。但一般而言，开发效率和运行效率是不可兼得的。  &lt;/p&gt;
&lt;p&gt;对于C，需要一个&lt;strong&gt;编译器&lt;/strong&gt;将其编译到汇编，&lt;strong&gt;汇编器&lt;/strong&gt;再将汇编转换为机器码，而不同硬件平台的汇编代码往往有差异，所以高级语言还有一个重要的特点就是帮我们屏蔽了硬件底层，将兼容工作交给工具去做。  &lt;/p&gt;
&lt;p&gt;“精通细节是理解更深和更基本概念的先决条件。”  &lt;/p&gt;
&lt;h2&gt;程序编码&lt;/h2&gt;
&lt;p&gt;在xinx系统中，可以使用以下代码编译一个C文件：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;gcc -o1 -o p p1.c p2.c
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;编译两个C文件到&lt;code&gt;p&lt;/code&gt;中，使用第一级优化。优化随着等级上升性能提高，但和原始代码的对应会下降，并且会增加编译时间。  &lt;/p&gt;
&lt;p&gt;在编译过程中，编译器文件首先会将代码中的&lt;code&gt;#include&lt;/code&gt;和&lt;code&gt;#define&lt;/code&gt;扩展插入代码，然后编译成汇编文件&lt;code&gt;.s&lt;/code&gt;，随后汇编为机器码&lt;code&gt;.o&lt;/code&gt;文件，然后和预制库进行连接，生成最后的可执行文件。  &lt;/p&gt;
&lt;p&gt;程序中的代码段存储在程序存储器中，比如操作系统信息、运行时栈等，其使用虚拟地址来寻址，操作系统负责将虚拟地址转换成物理地址。  &lt;/p&gt;
&lt;h3&gt;一个例子&lt;/h3&gt;
&lt;p&gt;如果我们有一段C代码&lt;code&gt;code.c&lt;/code&gt;，运行&lt;code&gt;gcc -O1 -S code.c&lt;/code&gt;将其编译为汇编文件后便是&lt;code&gt;code.s&lt;/code&gt;，其中是这段代码对应的汇编代码，这段代码一般会用&lt;code&gt;push&lt;/code&gt;指令保护现场，然后执行函数，最后&lt;code&gt;pop&lt;/code&gt;指令恢复现场。运行&lt;code&gt;gcc -O1 -c code.s&lt;/code&gt;会得到&lt;code&gt;code.o&lt;/code&gt;，这是二进制的机器码，其大小很大因为包含了执行开始等附加信息。可以通过反汇编器来从机器码中得到原始函数的字节长度并用&lt;code&gt;gdb&lt;/code&gt;来检查。  &lt;/p&gt;
&lt;p&gt;作为&lt;strong&gt;CISC&lt;/strong&gt;，即复杂指令集的CPU，&lt;strong&gt;IA32&lt;/strong&gt;和&lt;strong&gt;X86&lt;/strong&gt;架构的机器指令长度均不一致，有的长有的短。&lt;/p&gt;
&lt;h2&gt;数据格式&lt;/h2&gt;
&lt;p&gt;机器中的数据格式和C中的数据类型有一定的对应关系。整形数据格式有8bits的&lt;strong&gt;字节&lt;/strong&gt;，16bits的&lt;strong&gt;字&lt;/strong&gt;，32bits的&lt;strong&gt;双字&lt;/strong&gt;和64bits的&lt;strong&gt;四字&lt;/strong&gt;等，浮点数据格式有32bits的&lt;strong&gt;单精度&lt;/strong&gt;，64bits的&lt;strong&gt;双精度&lt;/strong&gt;和60~72bits的&lt;strong&gt;扩展精度&lt;/strong&gt;。这些格式在汇编中拥有各自专属的指令，会在一般指令后加上后缀，比如两个双字相加就是&lt;code&gt;addl&lt;/code&gt;等。  &lt;/p&gt;
&lt;p&gt;对于IA32处理器，由于四字不被直接支持，所以四字的运算和操作会被扩展。  &lt;/p&gt;
&lt;h2&gt;访问信息&lt;/h2&gt;
&lt;p&gt;新时代的CPU对寄存器的专用性需求不再存在，所以CPU中的寄存器都是通用寄存器，但一般有一种约定，IA32中每一个CPU包含八个32bits的寄存器，有些指令会使用固定的源寄存器和目的寄存器，&lt;code&gt;eax&lt;/code&gt;、&lt;code&gt;ecx&lt;/code&gt;、&lt;code&gt;edx&lt;/code&gt;特殊，&lt;code&gt;ebp&lt;/code&gt;和&lt;code&gt;esp&lt;/code&gt;保存栈指针和帧指针，等等。  &lt;/p&gt;
&lt;p&gt;每一个指令都有其操作数，一些指令又分为&lt;strong&gt;源操作数&lt;/strong&gt;和&lt;strong&gt;目的操作数&lt;/strong&gt;，这些操作数可以是&lt;strong&gt;立即数&lt;/strong&gt;、&lt;strong&gt;存储器地址&lt;/strong&gt;和&lt;strong&gt;寄存器&lt;/strong&gt;，对于存储器，可以用许多种方式进行寻址，比如&lt;strong&gt;绝对寻址&lt;/strong&gt;、&lt;strong&gt;变址寻址&lt;/strong&gt;等，详细请自行查找CPU寻址相关。  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;数据传送&lt;/strong&gt;指令将数据从一个位置复制到另一个位置，&lt;code&gt;mov&lt;/code&gt;系列传送数据，&lt;code&gt;movs&lt;/code&gt;系列传送符号扩展字节，&lt;code&gt;movz&lt;/code&gt;系列传送零扩展字节，&lt;code&gt;push&lt;/code&gt;和&lt;code&gt;pop&lt;/code&gt;分别压栈和出栈。需要注意的是，传送指令的两个操作数不能都为存储器。  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;栈&lt;/strong&gt;是一种特殊的数组，遵循先入后出原则，但由于其和一般数据的存储本质上并无不同之处，所以还是也可以使用一般的寻址方式访问其中的任意元素。  &lt;/p&gt;
&lt;p&gt;C中的&lt;strong&gt;间接引用&lt;/strong&gt;运算符&lt;code&gt;*&lt;/code&gt;可以将一个指针指向地址的值取出放入一个局部变量之内，这和&lt;strong&gt;取地址&lt;/strong&gt;操作符&lt;code&gt;&amp;amp;&lt;/code&gt;相反，后者返回一个变量的地址，而&lt;code&gt;*p=x&lt;/code&gt;这种形式则是指针间接引用形式，它把&lt;code&gt;x&lt;/code&gt;复制到&lt;code&gt;x&lt;/code&gt;指向的位置。  &lt;/p&gt;
&lt;p&gt;C中的&lt;strong&gt;局部变量&lt;/strong&gt;一般保存在寄存器中，访问起来比在存储器中要快得多。  &lt;/p&gt;
&lt;h2&gt;算术和操作逻辑&lt;/h2&gt;
&lt;p&gt;算术像是加减乘除移位这些操作大多都可以对应汇编里的一条单独指令，比如&lt;code&gt;ADD S, D&lt;/code&gt;就是将&lt;code&gt;S+D&lt;/code&gt;的结果放到&lt;code&gt;D&lt;/code&gt;中，&lt;code&gt;INC D&lt;/code&gt;就是将&lt;code&gt;D&lt;/code&gt;中的数据自加1等。  &lt;/p&gt;
&lt;p&gt;除此之外，还有&lt;code&gt;leal&lt;/code&gt;指令，即&lt;strong&gt;加载有效地址&lt;/strong&gt;指令，是&lt;code&gt;movl&lt;/code&gt;指令的变形，它可以看做是普通计算，&lt;code&gt;lea 7(%edx, %edx, 4)， %eax&lt;/code&gt;表示将&lt;code&gt;%eax&lt;/code&gt;中的值设定为&lt;code&gt;5x+7&lt;/code&gt;，括号中三个操作数记为x1、x2、x3则表示&lt;code&gt;x1 + x2 * x3&lt;/code&gt;。此指令最大的好处在于他不通过ALU，可以&lt;strong&gt;单周期&lt;/strong&gt;进行计算，效率极高。&lt;/p&gt;
&lt;p&gt;移位操作&lt;code&gt;SAR&lt;/code&gt;、&lt;code&gt;SHR&lt;/code&gt;等的源操作数是立即数或&lt;code&gt;cl&lt;/code&gt;寄存器中的数，并且范围在&lt;code&gt;0~31&lt;/code&gt;，功用基本和C中的一一对应。&lt;/p&gt;
&lt;p&gt;对于乘除法，除了提供截断到32位的乘除法之外，IA32指令集同样提供&lt;code&gt;mull&lt;/code&gt;、&lt;code&gt;imull&lt;/code&gt;这样的存储64位结果的无符号或者有符号指令，分别有单操作数和双操作数。对于单操作数，其中一个参数在&lt;code&gt;%eax&lt;/code&gt;中，对于乘法，结果高位存储在&lt;code&gt;%edx&lt;/code&gt;、地位存储到&lt;code&gt;%eax&lt;/code&gt;；对于除法，商在&lt;code&gt;%eax&lt;/code&gt;，余数在&lt;code&gt;%edx&lt;/code&gt;。还有像是&lt;code&gt;cltd&lt;/code&gt;这种指令，设置除数，将&lt;code&gt;%eax&lt;/code&gt;符号扩展到&lt;code&gt;%edx&lt;/code&gt;。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;习题&lt;/h2&gt;
&lt;p&gt;所有有代码的练习都以以题号为名字的单个文件内。  &lt;/p&gt;
&lt;p&gt;代码位于&lt;a href=&amp;quot;https://github.com/dtysky/CSAPP/tree/master/CSAPP/Chapter3&amp;quot;&gt;CSAPP-Chapter3&lt;/a&gt;内。&lt;/p&gt;
&lt;h3&gt;1&lt;/h3&gt;
&lt;p&gt;基础，略过。&lt;/p&gt;
&lt;h3&gt;2&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;movl&lt;/li&gt;
&lt;li&gt;movw&lt;/li&gt;
&lt;li&gt;movb&lt;/li&gt;
&lt;li&gt;movb&lt;/li&gt;
&lt;li&gt;pushl&lt;/li&gt;
&lt;li&gt;movw&lt;/li&gt;
&lt;li&gt;popl&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;3&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;%bl不能作为存储器地址&lt;/li&gt;
&lt;li&gt;应该使用movw&lt;/li&gt;
&lt;li&gt;源操作数和目的操作数不应该都为存储器地址&lt;/li&gt;
&lt;li&gt;寄存器sh不存在&lt;/li&gt;
&lt;li&gt;不能将立即数作为目的操作数&lt;/li&gt;
&lt;li&gt;指令和目的寄存器的大小不符&lt;/li&gt;
&lt;li&gt;应该是movw&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;4&lt;/h3&gt;
&lt;p&gt;基础，略过。&lt;/p&gt;
&lt;p&gt;只要认清8bitsyong&lt;code&gt;al&lt;/code&gt;，符号扩展用&lt;code&gt;movs&lt;/code&gt;等即可。  &lt;/p&gt;
&lt;h3&gt;5&lt;/h3&gt;
&lt;p&gt;这段汇编代码本质上是将&lt;code&gt;xp&lt;/code&gt;的内容复制到&lt;code&gt;yp&lt;/code&gt;，&lt;code&gt;yp&lt;/code&gt;的内容复制到&lt;code&gt;zp&lt;/code&gt;，&lt;code&gt;zp&lt;/code&gt;的内容复制到&lt;code&gt;xp&lt;/code&gt;。  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;void&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;decode1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;yp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;zp&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
    &lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;yp&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;zp&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xp&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;6&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;x + 6&lt;/li&gt;
&lt;li&gt;x + y&lt;/li&gt;
&lt;li&gt;x + 4y&lt;/li&gt;
&lt;li&gt;9x + 7&lt;/li&gt;
&lt;li&gt;4y + 10&lt;/li&gt;
&lt;li&gt;x + 2y + 9&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;7&lt;/h3&gt;
&lt;p&gt;基本操作，略过。&lt;/p&gt;
&lt;h3&gt;8&lt;/h3&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;movl&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;8&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;%ebp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;%eax&lt;/span&gt;
&lt;span class=&amp;quot;nf&amp;quot;&gt;sal&lt;/span&gt; &lt;span class=&amp;quot;no&amp;quot;&gt;$2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;%eax&lt;/span&gt;
&lt;span class=&amp;quot;nf&amp;quot;&gt;movl&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;12&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;%ebp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;%ecx&lt;/span&gt;
&lt;span class=&amp;quot;nf&amp;quot;&gt;sar&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;%cl&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;%eax&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;9&lt;/h3&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;arith&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
    &lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;t1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;t2&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;t1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;^&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;t3&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;t2&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;t4&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;t3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;t4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;10&lt;/h3&gt;
&lt;p&gt;A. 两个相同的数异或，结果恒为0，本质上是将&lt;code&gt;%edx&lt;/code&gt;寄存器置零&lt;br /&gt;
B. 更直接的操作是&lt;code&gt;movl $0, %ebx&lt;/code&gt;&lt;br /&gt;
C. &lt;code&gt;xorl&lt;/code&gt;版本只需要2个字节，&lt;code&gt;mov&lt;/code&gt;版本需要5个&lt;/p&gt;
&lt;h3&gt;11&lt;/h3&gt;
&lt;p&gt;将&lt;code&gt;cltd&lt;/code&gt;指令替换为&lt;code&gt;mov $0, %edx&lt;/code&gt;，并且将&lt;code&gt;idivl&lt;/code&gt;替换为&lt;code&gt;divl&lt;/code&gt;即可。  &lt;/p&gt;
&lt;h3&gt;12&lt;/h3&gt;
&lt;p&gt;A. 这显然是64位数的运算并且第四行使用的是无符号运算，所以&lt;code&gt;num_t&lt;/code&gt;是&lt;code&gt;unsigned long long&lt;/code&gt;类型。&lt;br /&gt;
B. 首先将&lt;code&gt;x&lt;/code&gt;和&lt;code&gt;yh&lt;/code&gt;相乘，结果记为&lt;code&gt;s&lt;/code&gt;，&lt;code&gt;sl&lt;/code&gt;存入&lt;code&gt;%ecx&lt;/code&gt;，而后将&lt;code&gt;x&lt;/code&gt;与&lt;code&gt;yl&lt;/code&gt;相乘得&lt;code&gt;t&lt;/code&gt;，&lt;code&gt;th&lt;/code&gt;存入&lt;code&gt;%edx&lt;/code&gt;，&lt;code&gt;tl&lt;/code&gt;存入&lt;code&gt;%eax&lt;/code&gt;，而后将&lt;code&gt;s&lt;/code&gt;和&lt;code&gt;th&lt;/code&gt;相加得到的&lt;code&gt;r&lt;/code&gt;存入&lt;code&gt;%edx&lt;/code&gt;，最后将&lt;code&gt;$dest=2^{32}tl + r$&lt;/code&gt;。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Friday, 01 Jul 2016 21:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2016.07.01 21:00:article/Skill-2016_07_01_a</guid>
<category>CSAPP</category>
<category>计算机系统</category>
<category>汇编</category>
<category>程序编码</category>
<category>数据</category>
</item>

<item>
<title>【CSAPP】笔记-Cp2-2-数的表示与运算</title>
<link>http://dtysky.moe/article/Skill-2016_06_22_a</link>
<description>&lt;p&gt;CSAPP（深入理解计算机系统）第二章“信息的表示和处理”第二部分“数的表示与运算”的笔记和课后习题。&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/CSAPP&amp;quot;&gt;Github的同步工程在这&lt;/a&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;整形&lt;/h2&gt;
&lt;p&gt;整形数据顾名思义，是指&lt;code&gt;整数&lt;/code&gt;，整数在计算机中的表示是精确的，也是有限的。一般而言，如果一个整数的位宽为&lt;code&gt;w&lt;/code&gt;，则其表示的无符号整数所能表示数的范围为&lt;code&gt;$0 \sim 2^w-1$&lt;/code&gt;，而有符号整数则能表示&lt;code&gt;$-2^{w/2} \sim 2^{w/2}-1$&lt;/code&gt;。比如，对于一个32位的系统，其&lt;code&gt;int&lt;/code&gt;型有符号整数的位宽是32，所以其范围是&lt;code&gt;-2147483648 ~ 2147483647&lt;/code&gt;。  &lt;/p&gt;
&lt;p&gt;C中的整形数据类型一般有&lt;code&gt;char&lt;/code&gt;、&lt;code&gt;short&lt;/code&gt;、&lt;code&gt;int&lt;/code&gt;、&lt;code&gt;long&lt;/code&gt;、&lt;code&gt;long long&lt;/code&gt;，位宽逐级递增，具体位宽取决于系统，在这些类型前加上&lt;code&gt;unsigned&lt;/code&gt;便可以定义一个无符号数。  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;再次声明注意，在依赖于通信的应用中，字节序和位宽的匹配是&lt;strong&gt;特别重要&lt;/strong&gt;的，如果可以，尽量使用c++中的&lt;code&gt;stdint&lt;/code&gt;中的类型，或者自己定义一些&lt;code&gt;uint_8t&lt;/code&gt;之类的类型。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;编码&lt;/h3&gt;
&lt;p&gt;整数的编码可以区分为有符号和无符号两种，无符号编码整数对应的值即为其二进制对应的值，无需多言。有符号编码整数对应的值，一般是其二进制对应的补码。&lt;br /&gt;
补码是一种表示有符号数的编码形式，对于一个n位的有符号数A，其最高位为符号位，为1表示负数，为0表示正数，其他位为数值位。当A为正数时，数值位对应的二进制数即为A所代表的值，比如&lt;code&gt;0111&lt;/code&gt;所代表的有符号数为&lt;code&gt;7&lt;/code&gt;；A为负数时，数值位对应的二进制数的补码加上负号为A的值，比如&lt;code&gt;1111&lt;/code&gt;表示&lt;code&gt;-1&lt;/code&gt;。其计算规则为：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A的补码 = A的取反加1。比如当&lt;code&gt;A = 111&lt;/code&gt;时，其补码为&lt;code&gt;000 + 1 = 001&lt;/code&gt;。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;当然，这并非最简单和最原始的算法，书中给出的方法是，对于有符号数&lt;code&gt;A = abcd&lt;/code&gt;，其中abcd均为0或者1：  &lt;/p&gt;
&lt;p&gt;$$A = -a*2^3 + b*2^2 + c*2 + d $$&lt;/p&gt;
&lt;p&gt;除了补码之外，有符号数的表示方式还有反码和原码，反码即为补码减1，而原码即为将数值位直接作为其绝对值的负数。在整数的表示形式中，这二者除了在某些底层硬件应用外并不常用，不再过多讨论。需要注意的是这二者的&lt;code&gt;0&lt;/code&gt;都有&lt;code&gt;+0&lt;/code&gt;和&lt;code&gt;-0&lt;/code&gt;两种形式。  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;补码的英文为&lt;code&gt;Two&amp;apos;s complement&lt;/code&gt;，反码为&lt;code&gt;Ones&amp;apos; complement&lt;/code&gt;。我们可以用&lt;code&gt;$2^w - x$&lt;/code&gt;来计算一个位宽w并且原码为x的数的补码，可以用&lt;code&gt;$2^w\{1\} - x$&lt;/code&gt;来计算其反码。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;转换&lt;/h3&gt;
&lt;p&gt;C中有符号数和无符号的转换在不同基础的计算机上不同，但都遵循同样的原则——不改变其最底层的字节本身，而只是改变解释的方式。这样会导致这种转换出现反直觉的结果，比如下面代码：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;111&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;kt&amp;quot;&gt;unsigned&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;unsigned&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;printf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;%u&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;输出为&lt;code&gt;4294967185&lt;/code&gt;。  &lt;/p&gt;
&lt;p&gt;通过以上定义，不难推出一系列的所谓补码和无符号数转换公式，但目测作用不大。&lt;/p&gt;
&lt;h3&gt;在C中&lt;/h3&gt;
&lt;h4&gt;转换&lt;/h4&gt;
&lt;p&gt;C中的基础数据类型转换可以显示进行，也可以隐式进行。前者表现为&lt;code&gt;float y = (float) x;&lt;/code&gt;这样的语句，其中&lt;code&gt;x&lt;/code&gt;是&lt;code&gt;int&lt;/code&gt;型变量；后者的表现则丰富的多，有&lt;code&gt;float y = x;&lt;/code&gt;这种，也有在格式化输出时的&lt;code&gt;printf(&amp;quot;%u&amp;quot;, x);&lt;/code&gt;这种，他会把&lt;code&gt;int&lt;/code&gt;型变量&lt;code&gt;x&lt;/code&gt;转换为无符号数输出。  &lt;/p&gt;
&lt;p&gt;在运算中，如果一个数是有符号而另一个为无符号的，则有符号数会被隐式转换为无符号数，这体现在加减乘除个各种比较运算中，比如：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;kt&amp;quot;&gt;unsigned&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;printf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;%d&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;得到的结果是&lt;code&gt;0&lt;/code&gt;，即&lt;code&gt;false&lt;/code&gt;，因为此时x被当做无符号数处理了，是一个很大的正数。  &lt;/p&gt;
&lt;p&gt;在标准库文件&lt;code&gt;limits.h&lt;/code&gt;中定义了&lt;code&gt;int&lt;/code&gt;的最大和最小值，其中：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;cp&amp;quot;&gt;#define INT_MIN (-INT_MAX - 1)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这是由于一些比较隐晦的原因。&lt;/p&gt;
&lt;h4&gt;扩展和截断&lt;/h4&gt;
&lt;p&gt;数的&lt;code&gt;位扩展&lt;/code&gt;一般发生在不同字长的整数的转换时，例如当一个整数从&lt;code&gt;char&lt;/code&gt;型转换为&lt;code&gt;int&lt;/code&gt;型时，就需要将8bits的数转换为32bits（在32bits的系统下）。对于无符号数，直接将低八位保留，高八位补零即可，但对于有符号数，则需要根据符号位的情况选择补零还是补一。&lt;/p&gt;
&lt;p&gt;数的&lt;code&gt;截断&lt;/code&gt;类似于&lt;code&gt;扩展&lt;/code&gt;，不过与后者相反。截断在位级对于有符号数和无符号数都是相同的，都是高位裁剪，但结果还保留着原先的性质，无需多说。  &lt;/p&gt;
&lt;h4&gt;一个利用符号数的漏洞&lt;/h4&gt;
&lt;p&gt;C中，&lt;code&gt;memcpy&lt;/code&gt;函数将一块内存从一个地址拷贝到另一个地址，这个函数有一个参数&lt;code&gt;n&lt;/code&gt;，表示要拷贝内存的长度，&lt;code&gt;n&lt;/code&gt;的类型是&lt;code&gt;size_t&lt;/code&gt;，是一个无符号数。如果给&lt;code&gt;n&lt;/code&gt;赋予一个有符号的负数，将会导致被复制的内存大小改变，这显然会导致一些意外的结果。  &lt;/p&gt;
&lt;p&gt;这个现象导致了曾经&lt;code&gt;FreeBSD&lt;/code&gt;中&lt;code&gt;getpeername&lt;/code&gt;函数的漏洞。  &lt;/p&gt;
&lt;p&gt;一般而言，高级语言都尽量避免无符号数直接被程序员操作。这也是为了防止错误。  &lt;/p&gt;
&lt;h2&gt;整数运算&lt;/h2&gt;
&lt;p&gt;计算机中的整数运算本质上和数学上的整数运算等价，但由于计算机中数值的有限性，所以可能会出现一些反直觉的现象。  &lt;/p&gt;
&lt;h3&gt;无符号加法&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;无符号加法&lt;/code&gt;最大的一个问题就是&lt;code&gt;溢出&lt;/code&gt;，虽然在Lisp系等语言中无限精度的运算是可能的，但像是C系的语言，运算还是有限精度的，这就会出现溢出问题。比如两个32bits的数进行运算，运算结果超过了32bits，这就造成了一次溢出。  &lt;/p&gt;
&lt;p&gt;对于有限精度的等宽w位无符号整数，其加法本质上等于两个数的&lt;code&gt;算术和&lt;/code&gt;对&lt;code&gt;$2^w$&lt;/code&gt;取模，这表现为对溢出位&lt;code&gt;w+1&lt;/code&gt;的舍弃。比如两个四位整数&lt;code&gt;9&lt;/code&gt;和&lt;code&gt;16&lt;/code&gt;，其算术和为25，二进制表示为&lt;code&gt;11001&lt;/code&gt;，将最高为舍弃后为&lt;code&gt;1001&lt;/code&gt;，即&lt;code&gt;9 = 25 % 16&lt;/code&gt;。  &lt;/p&gt;
&lt;h3&gt;补码加法&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;有符号加法&lt;/code&gt;在此处指补码的加法，其最大的问题同样是溢出。对于一个两个&lt;code&gt;w&lt;/code&gt;位的补码有符号数相加，当其和&lt;code&gt;sum&lt;/code&gt;正向超出&lt;code&gt;w&lt;/code&gt;位可表示的补码有符号数范围时，会发生正溢出，最终结果为&lt;code&gt;$sum - 2^w$&lt;/code&gt;；反之，当逆向超过时发生负溢出，最终结果为&lt;code&gt;$sum + 2^w$&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;补码的非&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;补码的非&lt;/code&gt;由于&lt;code&gt;INT_MIN&lt;/code&gt;的存在变得有点奇怪，对于不等于&lt;code&gt;INT_MIN&lt;/code&gt;的数，其取反就是自己的相反数，反之就是其自身。  &lt;/p&gt;
&lt;h3&gt;无符号乘法&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;无符号乘法&lt;/code&gt;就是加法的叠加，对于两个&lt;code&gt;w&lt;/code&gt;位的补码有符号数&lt;code&gt;x&lt;/code&gt;和&lt;code&gt;y&lt;/code&gt;，其乘法本质为：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;x * y = (x * y) % $2^w$  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;补码乘法&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;补码乘法&lt;/code&gt;在位级上与无符号乘法完全一致，截断也一样，只是在最后的解释上有所不同。  &lt;/p&gt;
&lt;h3&gt;乘以常数&lt;/h3&gt;
&lt;p&gt;一种常用的优化，可以将乘法拆为左移和加减法的组合来提高性能。比如：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;y = x * 3 = x * 1 + x * 2 = x + x &amp;lt;&amp;lt; 2&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;2的幂的除法&lt;/h3&gt;
&lt;p&gt;和常数乘法类似，常数除法也可以拆分，不过拆分到的是右移运算。在正整数除法的前提下，无论是算术还是逻辑右移1位和除以2是等价的，虽然在除不尽的情况下，结果应该是一个小数，但在整数除法中定义将&lt;code&gt;商+余数&lt;/code&gt;这种结果中的商，也就是小数结果的&lt;code&gt;向下舍入&lt;/code&gt;后的舍入值作为最终结果。&lt;br /&gt;
而对于负数，则应当是&lt;code&gt;向上舍入&lt;/code&gt; ，并不和算术右移等价。修正的方法是：  &lt;/p&gt;
&lt;p&gt;$$\lceil x/y \rceil = \lfloor (x+y-1)/y \rfloor$$  &lt;/p&gt;
&lt;p&gt;本质上，无论对于正数还是负数，原则都是&lt;code&gt;向零舍入&lt;/code&gt;。它们在计算时唯一的区别是如何构造那个偏置值是&lt;code&gt;y-1&lt;/code&gt;还是&lt;code&gt;0&lt;/code&gt;。详见习题42的解答。&lt;/p&gt;
&lt;h2&gt;浮点数&lt;/h2&gt;
&lt;p&gt;浮点数是计算机内部采用的用于表示实数的系统，他可以表示整形无法表示的小数部分，当然，在某些特殊领域中也常用定点数，但浮点数还是最一般的。但理解定点数仍然对理解浮点数有着不小的好处。  &lt;/p&gt;
&lt;h3&gt;定点数&lt;/h3&gt;
&lt;p&gt;首先考虑十进制实数的表达，一个拥有m位整数位和n为小数位的实数，其本质上是：  &lt;/p&gt;
&lt;p&gt;$$x = \sum_{i=0}^ma_i10^i + \sum_{i=n}^0a_i10^i$$  &lt;/p&gt;
&lt;p&gt;二进制也类似，为：  &lt;/p&gt;
&lt;p&gt;$$x = \sum_{i=0}^ma_i2^i + \sum_{i=n}^0a_i2^i$$  &lt;/p&gt;
&lt;p&gt;在实数的表示中，我们用&lt;code&gt;.&lt;/code&gt;这个符号来分隔整数和小数部分，成为&lt;code&gt;点&lt;/code&gt;。   在计算机中，一个数的位宽确定的情况下，当点的位置固定时，我们就称这个数为定点数，以下便是一个4位整数4位小数的二进制定点数：  &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;0101&lt;/td&gt;
&lt;td&gt;.&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;它代表的数为&lt;code&gt;5.5&lt;/code&gt;。  &lt;/p&gt;
&lt;p&gt;需要注意的是，计算机中除了整形和部分小数之外，很多数都是不精确的，这其实相当于十进制中有理数和无理数。  &lt;/p&gt;
&lt;h3&gt;IEEE浮点表示&lt;/h3&gt;
&lt;p&gt;IEEE浮点表示规则是一种当前几乎所有计算机遵循的通用标准，它规定一个浮点数的构造如下：  &lt;/p&gt;
&lt;p&gt;$$(-1)^s * M * 2^e$$  &lt;/p&gt;
&lt;p&gt;其中，s为符号，M为尾数，是一个二进制小数，E为阶码，其作用为对浮点数加权，可以为负数。我们可以很清晰看到，这实际上是一种科学计数法。下表表示了一个32位浮点数：  &lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;31&lt;/th&gt;
&lt;th&gt;30~23&lt;/th&gt;
&lt;th&gt;22~0&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;s&lt;/td&gt;
&lt;td&gt;exp&lt;/td&gt;
&lt;td&gt;frac&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;其中&lt;code&gt;s&lt;/code&gt;为符号位，&lt;code&gt;exp&lt;/code&gt;为为模式或者阶码，&lt;code&gt;frac&lt;/code&gt;为尾数，通过设置&lt;code&gt;exp&lt;/code&gt;可以将浮点数设为几种不同的模式：  &lt;/p&gt;
&lt;h4&gt;规格化的值&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;exp&lt;/code&gt;各位不全为0或1时，为规格化的值。此时，阶码被解释为一个以偏置形式表示的有符号整数，为阶码&lt;code&gt;E=exp-Bias&lt;/code&gt;，&lt;code&gt;exp&lt;/code&gt;被作为无符号整数，而&lt;code&gt;Bias&lt;/code&gt;为exp位数减1所能代表的最大有符号数减1。同时，此时&lt;code&gt;frac&lt;/code&gt;被作为一个整数位为0的小数的小数位，尾数为&lt;code&gt;frac+1&lt;/code&gt;，即将整数位替换为1，这种方式被称作&lt;code&gt;隐含的以1开头&lt;/code&gt;的表示。于是，此时的数为：  &lt;/p&gt;
&lt;p&gt;$$x = (-1)^s * (1 + frac) * 2^{exp - Bias}$$&lt;/p&gt;
&lt;h4&gt;非规格化的值&lt;/h4&gt;
&lt;p&gt;这种格式的一个作用是表示&lt;code&gt;0&lt;/code&gt;，&lt;code&gt;exp&lt;/code&gt;和&lt;code&gt;frac&lt;/code&gt;全为0时，当&lt;code&gt;s&lt;/code&gt;为0时，表示数&lt;code&gt;+0.0&lt;/code&gt;，&lt;code&gt;s&lt;/code&gt;为1时，表示数&lt;code&gt;-0.0&lt;/code&gt;。  &lt;/p&gt;
&lt;p&gt;另一个作用是表示非常接近于0的数，此时&lt;code&gt;exp&lt;/code&gt;仍然为0，但是&lt;code&gt;frac&lt;/code&gt;却不一定，这提供了一种叫做&lt;code&gt;逐渐溢出&lt;/code&gt;的属性。此时的&lt;code&gt;E=1 - Bias&lt;/code&gt;，&lt;code&gt;M=frac&lt;/code&gt;，使得从最小规格数到最大非规格数的平滑过渡。  &lt;/p&gt;
&lt;h4&gt;特殊值&lt;/h4&gt;
&lt;p&gt;当&lt;code&gt;exp&lt;/code&gt;各位全为1时，表示特殊值。若&lt;code&gt;frac&lt;/code&gt;全为0，得到的值表示无穷，&lt;code&gt;s&lt;/code&gt;为0为正无穷，否则为负无穷。若&lt;code&gt;frac&lt;/code&gt;不为0，则值为&lt;code&gt;NaN&lt;/code&gt;，这表示运算结果不是一个实数或者无穷值。  &lt;/p&gt;
&lt;h4&gt;为何如此设计&lt;/h4&gt;
&lt;p&gt;一切设计都是为了平滑、方便。比如如果将浮点数解释为无符号整数，会发现在浮点数自身升序排列时，此整数也是升序的，它们有着一致性，这意味着可以用一套排序解决两个问题。&lt;/p&gt;
&lt;h3&gt;舍入&lt;/h3&gt;
&lt;p&gt;由于浮点数无法准确表示很多数字，所以舍入就显得有些重要，IEEE浮点格式规定了四种不同的摄入方式，分别为&lt;code&gt;向偶数舍入&lt;/code&gt;、&lt;code&gt;向上舍入&lt;/code&gt;、&lt;code&gt;向下舍入&lt;/code&gt;、&lt;code&gt;向零舍入&lt;/code&gt;四种。  &lt;/p&gt;
&lt;p&gt;后三种已经在前面的有符号乘法探讨过，看似最符合直觉，但实际上浮点数中默认使用的却是向偶数舍入。对于一个二进制数，最低位为1则为奇数，否则为偶数。&lt;br /&gt;
当&lt;code&gt;XXX...YYY...Y100&lt;/code&gt;这种形式时，向偶数舍入才有效，我们总是倾向于让最低位为0。&lt;/p&gt;
&lt;h3&gt;运算&lt;/h3&gt;
&lt;p&gt;浮点数运算基本上可以看做是两个浮点数精确运算后的舍入解，但考虑到浮点数种有规格外和特殊的值（&lt;code&gt;Nan&lt;/code&gt;、&lt;code&gt;inf&lt;/code&gt;等），所以运算并非总是可以这么去做。&lt;br /&gt;
由于舍入的存在，浮点数运算也不具备结合性。例如，对于&lt;code&gt;0.000001 + 1000000 - 1000000&lt;/code&gt;和&lt;code&gt;0.000001 + (1000000 - 1000000)&lt;/code&gt;，其结果就完全不同，前一个式子在第一运算中由于舍入会将0.000001丢失。  &lt;/p&gt;
&lt;h3&gt;C中的浮点数&lt;/h3&gt;
&lt;p&gt;在C中，浮点数有&lt;code&gt;float&lt;/code&gt;个&lt;code&gt;double&lt;/code&gt;两种类型，前者单精度，32bits，后者双精度，64bits。但由于C中不要求机器使用IEEE标准，所以对于特殊值，并没有标准要求，不同编译器定义不同。  &lt;/p&gt;
&lt;p&gt;需要注意的是强制类型转换的时候，会发生许多期望之外的状况：  &lt;/p&gt;
&lt;p&gt;&lt;code&gt;int&lt;/code&gt;转为&lt;code&gt;float&lt;/code&gt;或者&lt;code&gt;double&lt;/code&gt;时不会溢出但可能舍入，&lt;code&gt;double&lt;/code&gt;转为&lt;code&gt;float&lt;/code&gt;时可能发生溢出为正负无穷的状况，&lt;code&gt;double&lt;/code&gt;和&lt;code&gt;float&lt;/code&gt;到&lt;code&gt;int&lt;/code&gt;时发生向零舍入。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;习题&lt;/h2&gt;
&lt;p&gt;所有有代码的练习都以以题号为名字的单个文件内。  &lt;/p&gt;
&lt;p&gt;代码位于&lt;a href=&amp;quot;https://github.com/dtysky/CSAPP/tree/master/CSAPP/Chapter2&amp;quot;&gt;CSAPP-Chapter2&lt;/a&gt;内。&lt;/p&gt;
&lt;h3&gt;17&lt;/h3&gt;
&lt;p&gt;基础计算，略过。  &lt;/p&gt;
&lt;h3&gt;18&lt;/h3&gt;
&lt;p&gt;A: 0b0000000110111000 = 440&lt;br /&gt;
B: 0b0000000000010100 = 20&lt;br /&gt;
C: 0b1111111001011000 = - (0b0000000110100111 + 1) = -424&lt;br /&gt;
......  &lt;/p&gt;
&lt;h3&gt;19&lt;/h3&gt;
&lt;p&gt;将十进制的二进制补码表示按照二进制无符号数解释即可。&lt;/p&gt;
&lt;h3&gt;20&lt;/h3&gt;
&lt;p&gt;比如对于&lt;code&gt;-8&lt;/code&gt;，其补码为&lt;code&gt;1000&lt;/code&gt;，位宽&lt;code&gt;w = 4&lt;/code&gt;，所以，补码转换为无符号数的结果为&lt;code&gt;$-8 + 2^4 = 8$&lt;/code&gt;，结果和直接将二进制解释为无符号数时一致。  &lt;/p&gt;
&lt;h3&gt;21&lt;/h3&gt;
&lt;p&gt;只需要注意有符号数和无符号是在计算式会被隐式转换为无符号数即可。  &lt;/p&gt;
&lt;h3&gt;22&lt;/h3&gt;
&lt;p&gt;对于A：  &lt;/p&gt;
&lt;p&gt;$$x = -1 * 2^3 + 1 * 2 + 1 * 1 = -5$$  &lt;/p&gt;
&lt;p&gt;对于其他也一样。  &lt;/p&gt;
&lt;h3&gt;23&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;fun1&lt;/code&gt;先对&lt;code&gt;word&lt;/code&gt;左移24位，再逻辑右移24位，之后转换为有符号数。&lt;br /&gt;
&lt;code&gt;fun2&lt;/code&gt;先将&lt;code&gt;word&lt;/code&gt;转换为有符号数，之后左移24位，再算术右移24位。  &lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;w&lt;/th&gt;
&lt;th&gt;fun1(2)&lt;/th&gt;
&lt;th&gt;fun2(w)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0x00000076&lt;/td&gt;
&lt;td&gt;0x00000076&lt;/td&gt;
&lt;td&gt;0x00000076&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0x87654321&lt;/td&gt;
&lt;td&gt;0x00000021&lt;/td&gt;
&lt;td&gt;0x00000021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0x000000c9&lt;/td&gt;
&lt;td&gt;0x000000c9&lt;/td&gt;
&lt;td&gt;0xffffffc9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0xedcba987&lt;/td&gt;
&lt;td&gt;0x00000087&lt;/td&gt;
&lt;td&gt;0xffffff87&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;24&lt;/h3&gt;
&lt;p&gt;位级去掉最高位，将剩余解释为有符号或者无符号数即可。  &lt;/p&gt;
&lt;h3&gt;25&lt;/h3&gt;
&lt;p&gt;详见代码。  &lt;/p&gt;
&lt;p&gt;当数组长度为0时，由于&lt;code&gt;length&lt;/code&gt;为无符号数，所以&lt;code&gt;length - 1&lt;/code&gt;语句将得到一个无符号数的结果，在32bits的机器上，此结果为&lt;code&gt;0xffffffff&lt;/code&gt;对应的无符号数，是一个很大的正数。所以在循环时，会有数组越界发生，即访问未初始化的存储区域，发生错误。  &lt;/p&gt;
&lt;p&gt;将&lt;code&gt;i &amp;lt;= length - 1&lt;/code&gt;改为&lt;code&gt;i &amp;lt; length&lt;/code&gt;可破。&lt;/p&gt;
&lt;h3&gt;26&lt;/h3&gt;
&lt;p&gt;详见代码。  &lt;/p&gt;
&lt;p&gt;&lt;code&gt;size_t&lt;/code&gt;为无符号数，&lt;code&gt;s&lt;/code&gt;与&lt;code&gt;t&lt;/code&gt;的长度的相减也会的到一个无符号结果，这使得改结果与0相比较大或相等，无法得到正确结果。&lt;br /&gt;
将相减后与0比较直接改为两个长度相比较即可。  &lt;/p&gt;
&lt;h3&gt;27&lt;/h3&gt;
&lt;p&gt;比较x + y 和 x（或y）的大小即可。&lt;/p&gt;
&lt;h3&gt;28&lt;/h3&gt;
&lt;p&gt;套公式，略过。  &lt;/p&gt;
&lt;h3&gt;29&lt;/h3&gt;
&lt;p&gt;基础计算，略过。  &lt;/p&gt;
&lt;h3&gt;30&lt;/h3&gt;
&lt;p&gt;详见代码。  &lt;/p&gt;
&lt;p&gt;溢出只有可能在&lt;code&gt;x&lt;/code&gt;和&lt;code&gt;y&lt;/code&gt;同号时出现，都为正时可能发生正溢出，即最终和为负，都为负时则可能发生负溢出，即和最终为正。&lt;/p&gt;
&lt;h3&gt;31&lt;/h3&gt;
&lt;p&gt;溢出是可逆的，即便&lt;code&gt;sum&lt;/code&gt;溢出了，但&lt;code&gt;sum - x = y&lt;/code&gt;却还是成立的。  &lt;/p&gt;
&lt;h3&gt;32&lt;/h3&gt;
&lt;p&gt;因为补码的值域不是关于y轴对称的，所以当&lt;code&gt;y&lt;/code&gt;为&lt;code&gt;INT_MIN&lt;/code&gt;时，&lt;code&gt;-y&lt;/code&gt;会直接发生溢出，变为&lt;code&gt;y&lt;/code&gt;，此时如果&lt;code&gt;x&lt;/code&gt;为0，题目所示的函数将不会认为溢出，但实际上已经溢出了。  &lt;/p&gt;
&lt;p&gt;在原先的代码加一个分支即可。&lt;/p&gt;
&lt;h3&gt;33&lt;/h3&gt;
&lt;p&gt;0xF的补码是其自身，其他都是相反数。  &lt;/p&gt;
&lt;h3&gt;34&lt;/h3&gt;
&lt;p&gt;基础运算，略过。&lt;/p&gt;
&lt;h3&gt;35&lt;/h3&gt;
&lt;h3&gt;1&lt;/h3&gt;
&lt;p&gt;$$B2T_{2w}(x) = -x_{2w-1} 2^{2w-1} + x_{2w-2} 2^{2w-2} + ... + x_w * 2^2 + \sum_{i=0}^{w-1}x_i2^i$$  &lt;/p&gt;
&lt;p&gt;$$=2^w({-x_{2w-1} 2^{w-1} + x_{2w-2} 2^{w-2} + ... + x_w}) + \sum_{i=0}^{w-1}x_i2^i$$  &lt;/p&gt;
&lt;p&gt;$$=2^w({-x_{2w-1} 2^{w-1} + \sum_{i=0}^{w-2} x_i2^i}) + \sum_{i=0}^{w-1}x_i2^i$$  &lt;/p&gt;
&lt;p&gt;$$=2^wv + u$$  &lt;/p&gt;
&lt;p&gt;已知有符号和无符号乘法在位形式上一致，令上述公式的&lt;code&gt;x=x*y&lt;/code&gt;，其中&lt;code&gt;p&lt;/code&gt;是乘积&lt;code&gt;x*y&lt;/code&gt;在w位时的补码形式，则有：  &lt;/p&gt;
&lt;p&gt;$$u=p_{w-1}2^w+p$$  &lt;/p&gt;
&lt;p&gt;于是有：  &lt;/p&gt;
&lt;p&gt;$$x*y = 2^w(v+p_{w-1})+p$$  &lt;/p&gt;
&lt;p&gt;令&lt;code&gt;$t = v+p_{w-1}$&lt;/code&gt;则：  &lt;/p&gt;
&lt;p&gt;$$x*y = t2^w+p$$  &lt;/p&gt;
&lt;p&gt;即证。  &lt;/p&gt;
&lt;h3&gt;2&lt;/h3&gt;
&lt;p&gt;由于&lt;code&gt;p&lt;/code&gt;是一个整数，所以其总是可以被分解为一个非零整数、商和余数的和，即：  &lt;/p&gt;
&lt;p&gt;$$p = xq + r$$  &lt;/p&gt;
&lt;p&gt;即证。  &lt;/p&gt;
&lt;h3&gt;3&lt;/h3&gt;
&lt;p&gt;令&lt;code&gt;q=y&lt;/code&gt;，则有：  &lt;/p&gt;
&lt;p&gt;$$p = xy + r$$  &lt;/p&gt;
&lt;p&gt;而&lt;code&gt;$p=xy-t2^w$&lt;/code&gt;，固有：  &lt;/p&gt;
&lt;p&gt;$$r=-t2^w$$  &lt;/p&gt;
&lt;p&gt;由于&lt;code&gt;r&lt;/code&gt;的绝对值小于&lt;code&gt;x&lt;/code&gt;的绝对值，而&lt;code&gt;x&lt;/code&gt;又是一个补码，所以&lt;code&gt;x&lt;/code&gt;的最大值为&lt;code&gt;$2^w$&lt;/code&gt;，同时&lt;code&gt;t&lt;/code&gt;为一个大于等于1的数，所以要使此等式成立，&lt;code&gt;r&lt;/code&gt;和&lt;code&gt;t&lt;/code&gt;都必须为0，即证。&lt;/p&gt;
&lt;h3&gt;36&lt;/h3&gt;
&lt;p&gt;详见代码。  &lt;/p&gt;
&lt;h3&gt;37&lt;/h3&gt;
&lt;p&gt;改进是这段代码的乘法计算部分不再溢出，但由于malloc的最后一个参数是&lt;code&gt;size_t&lt;/code&gt;，所以还是存在问题的。  &lt;/p&gt;
&lt;p&gt;解决方法就是将相乘的两个数都声明为int型，这样它们都为正数时，相乘放入一个&lt;code&gt;size_t&lt;/code&gt;中时便不会溢出。&lt;br /&gt;
而实际上，这两个值的确都是正数。&lt;/p&gt;
&lt;h3&gt;38&lt;/h3&gt;
&lt;p&gt;b为0时，可以计算1、2、3、8倍，为a时，可以计算2、3、5、9倍。  &lt;/p&gt;
&lt;h3&gt;39&lt;/h3&gt;
&lt;p&gt;$$(2^{n+1} - 2^m)x = 2^m(2^{n+1-m}-1)x$$  &lt;/p&gt;
&lt;p&gt;即：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;((x &amp;lt;&amp;lt; n+1-m) - x) &amp;lt;&amp;lt; m&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;40&lt;/h3&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;x &amp;lt;&amp;lt; 3 - x &amp;lt;&amp;lt; 1 &lt;/li&gt;
&lt;li&gt;x &amp;lt;&amp;lt; 5 - x&lt;/li&gt;
&lt;li&gt;x &amp;lt;&amp;lt; 1 - x &amp;lt;&amp;lt; 3&lt;/li&gt;
&lt;li&gt;y = x &amp;lt;&amp;lt; 2 + x; y &amp;lt;&amp;lt; 1 + y &amp;lt;&amp;lt; 3&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;41&lt;/h3&gt;
&lt;p&gt;如果n有可能超过位宽，选A。&lt;br /&gt;
否则，选B。&lt;/p&gt;
&lt;h3&gt;42&lt;/h3&gt;
&lt;p&gt;详见代码。  &lt;/p&gt;
&lt;p&gt;可以使用31位的符号右移确定&lt;code&gt;x&lt;/code&gt;是正数或者负数，而后使用&lt;code&gt;mask&lt;/code&gt;得到合适的偏置，最后直接根据公式求解。&lt;/p&gt;
&lt;h3&gt;43&lt;/h3&gt;
&lt;p&gt;$$M = 2^6 - 1$$  &lt;/p&gt;
&lt;p&gt;$$N = 2^3$$&lt;/p&gt;
&lt;h3&gt;44&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;要使该式为假则必须要找到整数&lt;code&gt;x&lt;/code&gt;，使得&lt;code&gt;x-1&amp;gt;=0&lt;/code&gt;且&lt;code&gt;x&amp;lt;=0&lt;/code&gt;，这样的&lt;code&gt;x&lt;/code&gt;在C中是存在的，即&lt;code&gt;x=INT_MIN&lt;/code&gt;，减1运算后会发生溢出。  &lt;/li&gt;
&lt;li&gt;若&lt;code&gt;x&amp;amp;7&lt;/code&gt;为7则&lt;code&gt;x&lt;/code&gt;的低三位全为1，而&lt;code&gt;x&amp;lt;&amp;lt;29&lt;/code&gt;大于0则&lt;code&gt;x&lt;/code&gt;的倒数第3位为0，这样的&lt;code&gt;x&lt;/code&gt;不存在，所以原式必然为真。  &lt;/li&gt;
&lt;li&gt;当结果会溢出的时候，原式便可能为假。  &lt;/li&gt;
&lt;li&gt;事实上，整数&lt;code&gt;x&lt;/code&gt;如果不是负数就是0要么就是正数，并且原式没有特别的边界情况，恒成立。  &lt;/li&gt;
&lt;li&gt;与4基本一致，但当&lt;code&gt;x=INT_MIN&lt;/code&gt;的时候，&lt;code&gt;-x&lt;/code&gt;由于溢出仍然为&lt;code&gt;x=INT_MAX&lt;/code&gt;,还是为负，这可以使得原式为假。  &lt;/li&gt;
&lt;li&gt;恒成立，C中，当无符号数和有符号数做运算时，会把有符号数转为无符号数，而有符号数和无符号数在位级上运算又是等价的。  &lt;/li&gt;
&lt;li&gt;&lt;code&gt;-y=~y+1&lt;/code&gt; -&amp;gt; &lt;code&gt;~y=-y-1&lt;/code&gt;，所以这个式子本质上和6中的一致，原式成立。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;45&lt;/h3&gt;
&lt;p&gt;基础运算，略过。  &lt;/p&gt;
&lt;h3&gt;46&lt;/h3&gt;
&lt;p&gt;A. 为0.{23个0}1100110011001100.......  &lt;/p&gt;
&lt;p&gt;B. 为$\sum_{i=-\inf}^{-24}a_i2^i$，其中，当&lt;code&gt;i % 4 == 0 or 1&lt;/code&gt;时，&lt;code&gt;$a_i=1$&lt;/code&gt;，否则为&lt;code&gt;0&lt;/code&gt;。求和如下：  &lt;/p&gt;
&lt;p&gt;$$sum = 2^{-24} + 2^{-25} + 2^{-28} + 2^{-29} + ......$$  &lt;/p&gt;
&lt;p&gt;$$=2^{-24}(1 + 1/2) + 2^{-28}(1 + 1/2) + ......$$  &lt;/p&gt;
&lt;p&gt;$$=3 * 2^{-25} * (1 + 2^{-4} + ......)$$  &lt;/p&gt;
&lt;p&gt;$$=1/(2^{20} * 10) \approx 9.53 * 10^{-8}$$  &lt;/p&gt;
&lt;p&gt;C. 约为 $9.53 * 10^{-8} * 36000 = 0.034$秒。  &lt;/p&gt;
&lt;p&gt;D. 约为0.034 * 2000 = 68米。  &lt;/p&gt;
&lt;h3&gt;47&lt;/h3&gt;
&lt;p&gt;基础计算，略过。  &lt;/p&gt;
&lt;h3&gt;48&lt;/h3&gt;
&lt;p&gt;整形数的二进制表示为&lt;code&gt;00000000001 101011001000101000001&lt;/code&gt;。&lt;br /&gt;
浮点数的二进制表示为&lt;code&gt;0 10010100  10101100100010100000100&lt;/code&gt;。  &lt;/p&gt;
&lt;p&gt;浮点数的&lt;code&gt;s = 1&lt;/code&gt;，&lt;code&gt;exp = 148&lt;/code&gt; -&amp;gt; &lt;code&gt;e = 148 - 127 = 21&lt;/code&gt;，&lt;code&gt;frac = 0.6739811897277832&lt;/code&gt; -&amp;gt; &lt;code&gt;M = 1.6739811897277832&lt;/code&gt;。  &lt;/p&gt;
&lt;p&gt;通过上面对比可见，整形表示的最后21位和浮点数&lt;code&gt;frac&lt;/code&gt;中的前21位是一致的。  &lt;/p&gt;
&lt;h3&gt;49&lt;/h3&gt;
&lt;p&gt;A.   &lt;/p&gt;
&lt;p&gt;$$min = 2^{n + 1} + 1$$  &lt;/p&gt;
&lt;p&gt;B. &lt;/p&gt;
&lt;p&gt;$$2^24 + 1 = 16777217$$&lt;/p&gt;
&lt;h3&gt;50&lt;/h3&gt;
&lt;p&gt;基本计算，略过。&lt;/p&gt;
&lt;h3&gt;51&lt;/h3&gt;
&lt;p&gt;A. 由于末尾0后两个1，原数加1舍入更为合理，即&lt;code&gt;x&amp;apos; = 0.00011001100110011001101&lt;/code&gt;。  &lt;/p&gt;
&lt;p&gt;B. 为&lt;code&gt;$\sum_{i=-\inf}^{-24}a_i2^i$&lt;/code&gt;，其中，当&lt;code&gt;i % 4 == 0 or 1&lt;/code&gt;时，&lt;code&gt;$a_i=0$&lt;/code&gt;，否则为&lt;code&gt;1&lt;/code&gt;。求和如下：  &lt;/p&gt;
&lt;p&gt;$$sum = 2^{-26} + 2^{-27} + 2^{-30} + 2^{-31} + ......$$  &lt;/p&gt;
&lt;p&gt;$$=2^{-26}(1 + 1/2) + 2^{-28}(1 + 1/2) + ......$$  &lt;/p&gt;
&lt;p&gt;$$=3 * 2^{-27} * (1 + 2^{-4} + ......)$$  &lt;/p&gt;
&lt;p&gt;$$=1/(2^{22} * 10) \approx  2.38 * 10^{-8}$$  &lt;/p&gt;
&lt;p&gt;C. 约为0.0008秒。  &lt;/p&gt;
&lt;p&gt;D. 约为1.71米。  &lt;/p&gt;
&lt;h3&gt;52&lt;/h3&gt;
&lt;p&gt;基础计算，略过。&lt;/p&gt;
&lt;h3&gt;53&lt;/h3&gt;
&lt;p&gt;假设1e400溢出位正无穷，则有：  &lt;/p&gt;
&lt;p&gt;POS_INFINITY = 1e400&lt;br /&gt;
NEG_INFINITY = -POS_INFINITY&lt;br /&gt;
NEG_ZERO = -1.0 / POS_INFINITY&lt;/p&gt;
&lt;h3&gt;54&lt;/h3&gt;
&lt;p&gt;A. 真，&lt;code&gt;int&lt;/code&gt;到&lt;code&gt;double&lt;/code&gt;的转换不会有任何精度损失，而转回时不会发生舍入。&lt;br /&gt;
B. &lt;code&gt;x&lt;/code&gt;为&lt;code&gt;INT_MAX&lt;/code&gt;时会发生溢出。&lt;br /&gt;
C. &lt;code&gt;d&lt;/code&gt;超过了&lt;code&gt;float&lt;/code&gt;的范围数会发生舍入。&lt;br /&gt;
D. 真，&lt;code&gt;float&lt;/code&gt;到&lt;code&gt;double&lt;/code&gt;的转换不会有任何精度损失，而转回时不会发生舍入。&lt;br /&gt;
E. 真，浮点数取反只影响符号位。&lt;br /&gt;
F. 真，整形和浮点数计算是会被转换为浮点数。&lt;br /&gt;
G. 真，即便&lt;code&gt;d*d&lt;/code&gt;发生了溢出到正无穷，但规则保证此不等式成立。&lt;br /&gt;
H. 当&lt;code&gt;f&lt;/code&gt;远大于&lt;code&gt;d&lt;/code&gt;时，会发生舍入。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Wednesday, 22 Jun 2016 21:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2016.06.22 21:00:article/Skill-2016_06_22_a</guid>
<category>CSAPP</category>
<category>计算机系统</category>
<category>信息存储</category>
<category>数据类型</category>
<category>整形</category>
<category>浮点数</category>
</item>

<item>
<title>【Python】内建函数</title>
<link>http://dtysky.moe/article/Skill-2016_06_08_c</link>
<description>&lt;p&gt;Python中一些很有用的内建函数，包括zip、类型转换、反射等。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;zip&lt;/h2&gt;
&lt;p&gt;zip函数用于改变数据的构造，它将会返回一个元组，元组的构造视传入的参数而定，在一般的应用中，一般会传入几个长度相等的可迭代量，比如列表。  &lt;/p&gt;
&lt;p&gt;例子：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;zip&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;([&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;])&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;print&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;输出为：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[(1, 3, 2), (2, 2, 1), (3, 1, 3)]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;可见，zip本质上是将三个列表进行了某种合并。  &lt;/p&gt;
&lt;p&gt;那么如何将已经被合并的三个列表还原呢？  &lt;/p&gt;
&lt;p&gt;加上不定参数符号&lt;code&gt;*&lt;/code&gt;即可，如以下代码，其本质上是将test中的三个元组作为参数传入：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;print&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;zip&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;输出为：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[(1, 2, 3), (3, 2, 1), (2, 1, 3)]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;bin/hex/oct/chr/str&lt;/h2&gt;
&lt;p&gt;一组相似的函数，可以将一个数组转换为某种进制下的字符串：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x40&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;print&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;bin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;print&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;hex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;print&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;oct&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;print&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;chr&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;分别输出：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;bin&lt;/code&gt;为二进制： &amp;apos;0b1000000&amp;apos;
&lt;code&gt;hex&lt;/code&gt;为十六进制： &amp;apos;0x40&amp;apos;&lt;br /&gt;
&lt;code&gt;oct&lt;/code&gt;为八进制： &amp;apos;0100&amp;apos;&lt;br /&gt;
&lt;code&gt;chr&lt;/code&gt;为ascii码： &amp;apos;@&amp;apos;  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;当不在ascii码范围内时，&lt;code&gt;chr&lt;/code&gt;输出和&lt;code&gt;hex&lt;/code&gt;一致。  &lt;/p&gt;
&lt;p&gt;&lt;code&gt;str&lt;/code&gt;函数将任意数据类型转换为字符串：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;print&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;str&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;([&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;输出为&lt;code&gt;[1, 2, 3]&lt;/code&gt;。&lt;/p&gt;
&lt;h2&gt;ord/int/float&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;ord&lt;/code&gt;和&lt;code&gt;int&lt;/code&gt;、&lt;code&gt;float&lt;/code&gt;函数的功能皆为数据类型转换，不过三者对象有所不同。  &lt;/p&gt;
&lt;p&gt;&lt;code&gt;ord&lt;/code&gt;是&lt;code&gt;chr&lt;/code&gt;的逆函数，将一个ascii码还原为整形数字：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;print&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;ord&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;@&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;输出是&lt;code&gt;40&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;int&lt;/code&gt;可以看作是&lt;code&gt;bin&lt;/code&gt;、&lt;code&gt;hex&lt;/code&gt;和&lt;code&gt;oct&lt;/code&gt;等的逆函数，其形式为&lt;code&gt;int(c, base)&lt;/code&gt;，第一个参数是字符，第二个是数的基，一般是2（十六进制）、8（八进制）、10（十进制）以及16（十六进制），默认是十进制。  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;print&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;int&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;10&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;# 10&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;print&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;int&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;10&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;# 2&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;print&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;int&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;10&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;16&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;# 15&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;print&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;int&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;10&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;8&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;# 8&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;当然，&lt;code&gt;int&lt;/code&gt;函数也有数字类型转换的功能，它可以将浮点等类型等转换为整形：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;print&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;int&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;# 1&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;print&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;int&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.9&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;# 1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;可见，int对于浮点数的转换使用的是&lt;code&gt;向下取整&lt;/code&gt;。  &lt;/p&gt;
&lt;p&gt;&lt;code&gt;float&lt;/code&gt;函数接受形式为小数的字符串或者整形等：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;print&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;float&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;1.1&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;# 1.1&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;print&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;float&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;# 1.0&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;isinstance/issubclass/dir/getattr/type&lt;/h2&gt;
&lt;p&gt;Python的反射特性，详见此处：  &lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;http://dtysky.moe/article/Skill-2015_02_22_b&amp;quot;&gt;【Python】反射&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;eval/execfile&lt;/h2&gt;
&lt;p&gt;这两个函数本质上是一回事，不过一个参数是字符串，一个是文件。&lt;code&gt;execfile&lt;/code&gt;可以看做是将文件的文本完整读入后传入&lt;code&gt;eval&lt;/code&gt;函数执行。  &lt;/p&gt;
&lt;p&gt;&lt;code&gt;eval&lt;/code&gt;函数是一种高级特性，它将文本解析后作为python的表达式执行：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;
&lt;span class=&amp;quot;nb&amp;quot;&gt;eval&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;x += 1&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;print&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;输出是2，可见&lt;code&gt;x += 1&lt;/code&gt;作为表达式被执行了。  &lt;/p&gt;
&lt;h2&gt;map/filter&lt;/h2&gt;
&lt;p&gt;这两个函数都针对可迭代量，比如元组和列表。&lt;br /&gt;
要注意可迭代量&lt;code&gt;iterable&lt;/code&gt;和迭代器&lt;code&gt;iterators&lt;/code&gt;是不同的东西。这两个可能会在后面的文章说到。  &lt;/p&gt;
&lt;p&gt;&lt;code&gt;map&lt;/code&gt;函数的参数是一个可迭代量&lt;code&gt;I&lt;/code&gt;和一个函数&lt;code&gt;F&lt;/code&gt;，调用时，&lt;code&gt;L&lt;/code&gt;中的元素&lt;code&gt;Li&lt;/code&gt;被逐个取出，送入&lt;code&gt;F&lt;/code&gt;中，&lt;code&gt;F&lt;/code&gt;需要返回处理后的值&lt;code&gt;Lia&lt;/code&gt;，然后&lt;code&gt;Li&lt;/code&gt;将会被&lt;code&gt;Lia&lt;/code&gt;替换。  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;def&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;inc&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;):&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;print&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;map&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;inc&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;结果是&lt;code&gt;[2, 3, 4]&lt;/code&gt;。  &lt;/p&gt;
&lt;p&gt;&lt;code&gt;filter&lt;/code&gt;函数顾名思义，是一个滤波器，他的参数和&lt;code&gt;map&lt;/code&gt;函数一致，不过函数&lt;code&gt;F&lt;/code&gt;的返回值是一个bool量，表示是否要保留该元素，如果为&lt;code&gt;True&lt;/code&gt;则会保留。&lt;code&gt;filter&lt;/code&gt;返回的是一个被筛选过的数组：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;def&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;odd&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;):&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;==&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;print&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;filter&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;odd&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;结果是&lt;code&gt;[1, 3]&lt;/code&gt;。  &lt;/p&gt;
&lt;h2&gt;open/file&lt;/h2&gt;
&lt;p&gt;属于文件操作的一部分，后面开文章单写。&lt;/p&gt;
&lt;h2&gt;sorted/reversed&lt;/h2&gt;
&lt;p&gt;该函数用于排序，比较复杂，以后开文章单写。  &lt;/p&gt;
&lt;h2&gt;abs/max/min/pow/divmod/round/sum&lt;/h2&gt;
&lt;p&gt;一些运算。  &lt;/p&gt;
&lt;p&gt;&lt;code&gt;abs&lt;/code&gt;对数字取绝对值： &lt;code&gt;abs(-1) = 1&lt;/code&gt;。&lt;br /&gt;
&lt;code&gt;max/min&lt;/code&gt;对列表或者元组取最大/最小值： &lt;code&gt;max([1, 2, 3]) = 3&lt;/code&gt;，&lt;code&gt;min([1, 2, 3]) = 1&lt;/code&gt;，这两个函数和&lt;code&gt;sorted&lt;/code&gt;函数一样，也可以自定义排序规则。&lt;br /&gt;
&lt;code&gt;pow&lt;/code&gt;是乘方： &lt;code&gt;pow(2, 3) = 8&lt;/code&gt;。&lt;br /&gt;
&lt;code&gt;divmod&lt;/code&gt;返回两个参数的商和余 ： &lt;code&gt;divmod(3, 2) = (1, 1)&lt;/code&gt;。&lt;br /&gt;
&lt;code&gt;round&lt;/code&gt;是取整，采用四舍五入： &lt;code&gt;round(1.4) = 1.0&lt;/code&gt;，&lt;code&gt;round(1.5) = 2.0&lt;/code&gt;。&lt;br /&gt;
&lt;code&gt;sum&lt;/code&gt;是求和： &lt;code&gt;sum([1, 2, 3]) = 6&lt;/code&gt;，&lt;code&gt;sum(1, 2, 3) = 6&lt;/code&gt;。&lt;/p&gt;
&lt;h2&gt;range/xrange&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;range&lt;/code&gt;生成一个列表：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;print&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;range&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;输出&lt;code&gt;[1, 2, 3]&lt;/code&gt;。  &lt;/p&gt;
&lt;p&gt;&lt;code&gt;xrange&lt;/code&gt;生成一个&lt;code&gt;xrange&lt;/code&gt;对象：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;print&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;xrange&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;输出&lt;code&gt;xrange(1, 4)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;二者皆可用于循环，以&lt;code&gt;for i in xrange(1, 10)&lt;/code&gt;类似的形式，但是&lt;code&gt;xrange&lt;/code&gt;对大规模循环的支持更好，效率也更高。&lt;/p&gt;
&lt;h2&gt;input/raw_input&lt;/h2&gt;
&lt;p&gt;这两个函数用于读取控制台输入，将开文章单写。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Wednesday, 08 Jun 2016 17:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2016.06.08 17:00:article/Skill-2016_06_08_c</guid>
<category>Python</category>
<category>内建函数</category>
<category>zip</category>
<category>迭代器</category>
<category>类型转换</category>
<category>反射</category>
</item>

<item>
<title>【CSAPP】笔记-Cp2-1-信息存储</title>
<link>http://dtysky.moe/article/Skill-2016_06_08_b</link>
<description>&lt;p&gt;CSAPP（深入理解计算机系统）第二章“信息的表示和处理”第一部分“信息存储”的笔记和课后习题。&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/CSAPP&amp;quot;&gt;Github的同步工程在这&lt;/a&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;计算机本质上是开关电路，底层就是MOSFET等的打开、关断和饱和为基础所构成的一系列变化，“开”和“关”可以抽象为0和1两种状态，计算机中的一切都是有0和1构成的，信息的存储也不例外。  &lt;/p&gt;
&lt;p&gt;信息在计算机中以0和1的系列表示，再高一点就是bytes序列，不过没有本质区别。单纯的0、1序列没有任何意义，意义是我们所赋予的，我们人为地定义一些数据类型，再通过这种定义01序列转义，便可以使它们表现出具体的含义。&lt;/p&gt;
&lt;p&gt;比如，&lt;code&gt;1111&lt;/code&gt;这个二进制数，如果是无符号整形，它代表&lt;code&gt;15&lt;/code&gt;，如果是有符号整形，则是&lt;code&gt;-1&lt;/code&gt;，如果是定点位为3的定点数，则是&lt;code&gt;7.5&lt;/code&gt;。&lt;br /&gt;
同时，这种由4个bit构成的一个数，其值域为0~15，是一个十六进制数，十六进制数在计算机中十分重要。&lt;/p&gt;
&lt;p&gt;一般来讲，有符号数用补码表示，小数由浮点数表示，不过在一些特殊的领域，比如FPGA，定点数使用地更为广泛，定点化问题也是值得关注的。  &lt;/p&gt;
&lt;h2&gt;原始数据类型&lt;/h2&gt;
&lt;p&gt;和代数定义基本相同，可以分为：  &lt;/p&gt;
&lt;p&gt;整数： 有符号整数和无符号整数，在C中，可以按照位宽分为很多类型，有8bits的&lt;code&gt;char&lt;/code&gt;，有32bits的&lt;code&gt;int&lt;/code&gt;，也有64bits的&lt;code&gt;int&lt;/code&gt;等，但它们都遵循一个规则。  &lt;/p&gt;
&lt;p&gt;小数： 在计算机软件中，小数都是有符号的，也可以根据位数进行划分，在C中，有32bits的&lt;code&gt;float&lt;/code&gt;，有64bits的&lt;code&gt;double&lt;/code&gt;等。  &lt;/p&gt;
&lt;p&gt;注：在C中，指针本身定义为一个全字长的变量。其它详见下面几节。&lt;/p&gt;
&lt;h2&gt;溢出和误差&lt;/h2&gt;
&lt;p&gt;溢出是指——运算结果的大小超越了该类型的数所能表示的最大值。比如对于一个无符号char型变量，如果给他赋予一个超过255的值，则发生了溢出，结果会被截断（当然有的编译器会发现这个错误）。  &lt;/p&gt;
&lt;p&gt;误差则是指浮点数和定点数系统的有限，对于整数，总是存在一个二进制数可以作为十进制数的对应，但对于小数或者分数则并非如此，例如&lt;code&gt;0.2&lt;/code&gt;这个数，你就无法找到一个二进制的数来表达他，只可能在有限的精度下非常接近它。  &lt;/p&gt;
&lt;p&gt;总而言之，整数在计算机中是有限但精确的，而小数是无限却近似的。  &lt;/p&gt;
&lt;h2&gt;指针&lt;/h2&gt;
&lt;p&gt;指针提供了引用数据结构的元素的机制，它指向某个存储块的第一个字节的虚拟地址。&lt;br /&gt;
指针包含一个值和一个类型，值就是指向对象的地址，而类型则是对象的类型。  &lt;/p&gt;
&lt;p&gt;在C中，用&lt;code&gt;T *p&lt;/code&gt;定义一个名为p的指针，T可以使任意基本的数据类型，用&lt;code&gt;p&lt;/code&gt;可以获取p的首字节地址，&lt;code&gt;*p&lt;/code&gt;可以直接获取该变量。而使用&lt;code&gt;&amp;amp;&lt;/code&gt;可以获取任何一个变量的首字节地址，它返回一个指针变量，如果定义的是一个int型指针变量，那么p指向的对象实际占据的地址空间就是p、p+1、p+2和p+3。&lt;br /&gt;
根据不同的系统，这四个字节的排序各有不同。低字节在前的称为&lt;strong&gt;小端LE&lt;/strong&gt;，反之则为&lt;strong&gt;大端BE&lt;/strong&gt;，这可能是一个坑，要注意。  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;比如&amp;amp;p=0x100，则同时p指向的对象为int型，表示0x01234567，则：
1. 大端表示，0x100 ~ 0x103地址分别为 0x01 ~ 0x67。
2. 小端表示，0x100 ~ 0x103地址分别为 0x67 ~ 0x01。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;一般来说，x86 系列 CPU 都是 little-endian 的字节序，PowerPC 通常是 big-endian，网络字节顺序也是 big-endian还有的CPU 能通过跳线来设置 CPU 工作于 Little endian 还是 Big endian 模式。  &lt;/p&gt;
&lt;p&gt;指针的引用可以用数组表示法来表示，比如对于&lt;code&gt;int *p&lt;/code&gt;，&lt;code&gt;p[1]&lt;/code&gt;取得是&lt;code&gt;&amp;amp;p&lt;/code&gt;后1*4个字节开始的int型变量，和数组的使用十分相似。
指针的只允许被赋值指针型变量，也就是一个&lt;code&gt;&amp;amp;&lt;/code&gt;返回的一个变量的地址或者由&lt;code&gt;*&lt;/code&gt;定义的数据。  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;char&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;kt&amp;quot;&gt;char&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;10&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;printf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;%d&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]);&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;printf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;%d&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;结果都是10。&lt;/p&gt;
&lt;h2&gt;强制类型转换&lt;/h2&gt;
&lt;p&gt;cast，强制类型转换，可以将一个类型的数据以另一种类型的形式表现出来。在系统编程中极为重要。&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;void&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;show_as_float&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;printf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;%f&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;float&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;位宽&lt;/h2&gt;
&lt;p&gt;处理器位宽决定了虚拟存储器的寻址范围，n位的寻址范围是 2&lt;sup&gt;n&lt;/sup&gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sizeof(T)&lt;/code&gt;函数可以给出当前系统中，类型T占据的字节数。由于不同系统对各种基本类型赋予的位宽可能不同，为了避免麻烦，在c++中应尽量使用&lt;code&gt;stdint&lt;/code&gt;包中的&lt;code&gt;uint_8t&lt;/code&gt;等类型。&lt;/p&gt;
&lt;h2&gt;字符串&lt;/h2&gt;
&lt;p&gt;字符串也是对二进制流的一种编码，它被编码为一个以null(值为0)字符结尾的字符(char)数组。每个字符都需要指定编码，常见的为ASCII码，当然，多用unicode，善待程序员（对Python我就是在说你）。&lt;/p&gt;
&lt;h2&gt;逻辑运算与位运算&lt;/h2&gt;
&lt;p&gt;逻辑运算是布尔代数中的一部分，基本包括与预算、或运算、非运算、异或运算，可以用于进行一些基本的逻辑运算。这也是数字电路的基础，其实很简单。&lt;br /&gt;
逻辑运算也有各种运算定律、化简方法（卡诺图）等，可以自行研究。  &lt;/p&gt;
&lt;p&gt;一般的，在软件中，逻辑运算通常有两种，单目运算&lt;code&gt;&amp;amp;&lt;/code&gt;等和双目运算&lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;等，前者是位运算，返回原始类型的值，后者是逻辑运算，返回逻辑值。所以单目运算符常常可以被用于位屏蔽和掩码等，比如&lt;code&gt;0xff &amp;amp; 0x0f&lt;/code&gt;就屏蔽了前者的前四位。&lt;/p&gt;
&lt;p&gt;除以上，还有一种位相关的操作，被称为&lt;code&gt;移位操作&lt;/code&gt;，移位操作一般分为逻辑移位（无符号扩展）和算数移位（有符号扩展），也有被称为循环移位的存在但不在此讨论。移位即将一个二进制数的所有位向左或向右平移若干位，他可以用于替换一些特殊的乘除法，速度快很多。比如除以二可以用左移两位表示。  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;需要注意的是，在C中，如果移动的位数超过了字长w，可能会造成类似循环移位的异常状况，所以要尽量避免。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;一般情况下，加减乘除逻辑运算位运算移位操作都可以直接对应为CPU的一条指令。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;习题&lt;/h2&gt;
&lt;p&gt;所有有代码的练习都以以题号为名字的单个文件内。  &lt;/p&gt;
&lt;p&gt;代码位于&lt;a href=&amp;quot;https://github.com/dtysky/CSAPP/tree/master/CSAPP/Chapter2&amp;quot;&gt;CSAPP-Chapter2&lt;/a&gt;内。&lt;/p&gt;
&lt;h3&gt;1，2，3，4，&lt;/h3&gt;
&lt;p&gt;略过。&lt;/p&gt;
&lt;h3&gt;5&lt;/h3&gt;
&lt;p&gt;A：&lt;br /&gt;
BE -&amp;gt; 0x87&lt;br /&gt;
LE -&amp;gt; 0x21  &lt;/p&gt;
&lt;p&gt;B:&lt;br /&gt;
BE -&amp;gt; 0x8765&lt;br /&gt;
LE -&amp;gt; 0x2143  &lt;/p&gt;
&lt;p&gt;C:&lt;br /&gt;
BE -&amp;gt; 0x876543&lt;br /&gt;
LE -&amp;gt; 0x214365  &lt;/p&gt;
&lt;h3&gt;6&lt;/h3&gt;
&lt;p&gt;这实际上是浮点数和整形的模型问题，略过。&lt;/p&gt;
&lt;h3&gt;7&lt;/h3&gt;
&lt;p&gt;为各个字符对应的ascii码，略过。&lt;/p&gt;
&lt;h3&gt;8&lt;/h3&gt;
&lt;p&gt;基本逻辑运算，略过。  &lt;/p&gt;
&lt;h3&gt;9&lt;/h3&gt;
&lt;p&gt;颜色的补就是把每一位取反，B是基本逻辑运算，略过。  &lt;/p&gt;
&lt;h3&gt;10&lt;/h3&gt;
&lt;p&gt;异或运算满足自反率：  &lt;/p&gt;
&lt;p&gt;$$A \oplus A \oplus B = B$$&lt;br /&gt;
$$A \oplus B \oplus B = A$$  &lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;步骤&lt;/th&gt;
&lt;th&gt;*x&lt;/th&gt;
&lt;th&gt;*y&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;初始&lt;/td&gt;
&lt;td&gt;a&lt;/td&gt;
&lt;td&gt;b&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;a&lt;/td&gt;
&lt;td&gt;a ^ b&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;b&lt;/td&gt;
&lt;td&gt;a ^ b&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;b&lt;/td&gt;
&lt;td&gt;a&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;11&lt;/h3&gt;
&lt;p&gt;原来函数只能对偶数个数元素的数组工作，是因为对于奇数个数元素的数组，会出现&lt;code&gt;first = last&lt;/code&gt;的状况，这种状况下实际上是数组中间的元素自己和自己做运算，即&lt;code&gt;inplace(&amp;amp;a[first], &amp;amp;a[first])&lt;/code&gt;，此运算将返回0。&lt;br /&gt;
返回0是因为以上三步运算中，步骤1使得&lt;code&gt;a = a, b = 0&lt;/code&gt;，步骤2使得&lt;code&gt;a = a, b = a&lt;/code&gt;，步骤3使得&lt;code&gt;a = a, b = 0&lt;/code&gt;，由于&lt;code&gt;inplace&lt;/code&gt;函数的两个参数指向同一个元素，所以对b赋值的结果会覆盖对a的赋值，所以结果为0。&lt;br /&gt;
如何改动请见代码，将奇数作为特殊情况即可，也就是，将&lt;code&gt;first &amp;lt;= last&lt;/code&gt;改为&lt;code&gt;first &amp;lt; last&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;12&lt;/h3&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// A&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x000000ff&lt;/span&gt;
&lt;span class=&amp;quot;c1&amp;quot;&gt;// B&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;^&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0xffffff00&lt;/span&gt;
&lt;span class=&amp;quot;c1&amp;quot;&gt;// C&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x000000ff&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;13&lt;/h3&gt;
&lt;p&gt;本质上：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;bis(x, y) = or(x, y)&lt;br /&gt;
bic(x, y) = and(x, ~y)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;bool_or&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;bis&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;可以用逻辑代数通过&lt;code&gt;or&lt;/code&gt;求得&lt;code&gt;xor&lt;/code&gt;：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;xor(x, y) = or(and(x, ~y), and(y, ~x))&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;int bool_xor(int x, int y){
    return bis(bic(x, y), bic(y, x));
}
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;14&lt;/h3&gt;
&lt;p&gt;基本运算，略过。&lt;/p&gt;
&lt;h3&gt;15&lt;/h3&gt;
&lt;p&gt;详见代码。&lt;/p&gt;
&lt;h3&gt;16&lt;/h3&gt;
&lt;p&gt;基本运算，略过。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Wednesday, 08 Jun 2016 12:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2016.06.08 12:00:article/Skill-2016_06_08_b</guid>
<category>CSAPP</category>
<category>计算机系统</category>
<category>信息存储</category>
<category>数据类型</category>
<category>指针</category>
<category>逻辑运算</category>
</item>

<item>
<title>【CSAPP】笔记-Cp1-计算机系统漫游</title>
<link>http://dtysky.moe/article/Skill-2016_06_08_a</link>
<description>&lt;p&gt;CSAPP（深入理解计算机系统）第一章“计算机系统漫游”的笔记和课后习题。&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/CSAPP&amp;quot;&gt;Github的同步工程在这&lt;/a&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;img alt=&amp;quot;抽象&amp;quot; src=&amp;quot;http://src.dtysky。moe/image/csapp/cp1/1.png&amp;quot; /&gt;&lt;/p&gt;
&lt;h2&gt;编译系统&lt;/h2&gt;
&lt;h3&gt;步骤&lt;/h3&gt;
&lt;p&gt;一次编译分为以下步骤：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;预处理cpp：根据#开头的命令修改和注入代码，处理完的代码一般为“.i”文件。  &lt;/li&gt;
&lt;li&gt;编译ccl：将“.i”文件翻译成文本“.s”文件，里面是汇编代码。  &lt;/li&gt;
&lt;li&gt;汇编as：将汇编代码翻译成机器语言，保存在“.o”文件中。  &lt;/li&gt;
&lt;li&gt;连接ld：将某些预编译好的代码合并到目标程序中，比如标准库中的一些函数，最终得到可执行文件。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;CNU项目&lt;/h3&gt;
&lt;p&gt;GNU&amp;apos;s Not Unix。&lt;br /&gt;
开发出一个完整的类unix系统，内核是linux。包括EMACS、GCC、GDB、汇编器、连接器等等，GCC支持许多种编译型语言。  &lt;/p&gt;
&lt;h2&gt;系统硬件&lt;/h2&gt;
&lt;h3&gt;构成&lt;/h3&gt;
&lt;p&gt;CPU、存储器（磁盘存储器算外设）和外设构成，现代中一般是CPU和存储器中由存储器总线连接，和外设则是IO总线，还有加速计算的GPU等。  &lt;/p&gt;
&lt;p&gt;学学HDL，写个简单的CPU可以加深理解，一个简单的CPU的例子，使用Verilog编写：  &lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://github.com/dtysky/SIMPLE_MIPS_CPU&amp;quot;&gt;SIMPLE_MIPS_CPU&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;GPU一般由用于计算的ALU（包括加减乘除法器、逻辑运算单元），寄存器堆（特殊的高速存储器，存储特殊数据），控制译码单元（将机器码转译为各个模块的控制信号），指令存储器（存储代码数据），数据存储区（存储数据）以及连接各个模块的数据路径（指令地址计数器PC算一部分）。运行时，PC借由ALU的运算来使自己跳转，而后CPU取出PC指向的指令存储器中的数据作为指令送入控制译码单元，而后调动各模块执行操作。&lt;/p&gt;
&lt;h2&gt;运行开销&lt;/h2&gt;
&lt;p&gt;比如运行一个简单的打印字符串的程序，加载时，复制到主存（SDRAM），运行时，主存复制到CPU的寄存器堆中，显示时，又复制到显示设备，这些“复制”就是主要开销之一。  &lt;/p&gt;
&lt;h3&gt;高速缓存&lt;/h3&gt;
&lt;p&gt;高速缓存是为了解决“复制”开销而构造的，它们由SRAM构成，容量介于寄存器堆和主存之间，速度也介于寄存器堆和主存之间。  &lt;/p&gt;
&lt;p&gt;一般来讲，会有一个CPU片上的L1缓存，容量高于寄存器堆但访问速度等同于寄存器堆，但速度高于主存两个数量级；L2缓存容量高于L1缓存，速度为L1的五分之一；一些系统拥有L3缓存。  &lt;/p&gt;
&lt;p&gt;高速缓存中会存放一些经常会使用到的指令，合理提高cache的命中率将会有效提高性能。（大概现在还有必要？）&lt;br /&gt;
&lt;img alt=&amp;quot;高速缓存&amp;quot; src=&amp;quot;http://src.dtysky。moe/image/csapp/cp1/2.png&amp;quot; /&gt;&lt;/p&gt;
&lt;h2&gt;操作系统&lt;/h2&gt;
&lt;p&gt;操作系统隔离了上层应用和底层硬件，它提供两个功能：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;防止硬件被失控的应用程序滥用。  &lt;/li&gt;
&lt;li&gt;向应用程序提供简单一致的机制来控制复杂而多样的硬件设备。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;简而言之，OS通过自身高度的复杂度对上层应用提供了简单而良好的抽象，使得程序员不需要关心乱七八糟的兼容问题，使得分工和高效率成为了可能。  &lt;/p&gt;
&lt;p&gt;这些抽象基本包括：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;文件：对IO设备的抽象。&lt;/li&gt;
&lt;li&gt;虚拟存储器：对主存和IO设备的抽象。  &lt;/li&gt;
&lt;li&gt;进程：对IO设备、主存和CPU的抽象。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;进程&lt;/h3&gt;
&lt;p&gt;进程是操作系统对一个正在运行的程序的一种抽象。一个系统可以运行多个进程。&lt;br /&gt;
但本质上，一个CPU在同一个时刻只能执行一个进程，多进程的假象（或者说也并不完全是假象）是通过交叉执行实现的，这种交叉执行的流程如下：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;正在执行A进程，发现需要切换到B进程。&lt;/li&gt;
&lt;li&gt;保存A进程的状态（上下文），将执行控制权转移给B进程，执行B进程。&lt;/li&gt;
&lt;li&gt;B进程执行一段时间，需要切换到A进程，保存B进程上下文，恢复A进程的上下文。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;线程&lt;/h3&gt;
&lt;p&gt;线程是对进程的进一步分割，它们可以是同一进程中的若干执行单元，共享进程的上下文、代码和全局数据，所以一般也比多进程更加高效，也适合多并发场景。  &lt;/p&gt;
&lt;h3&gt;虚拟存储器&lt;/h3&gt;
&lt;p&gt;虚拟存储器是一种抽象，它使得进程可以认为它们都是在独占地使用主存，即主存对于它们而言都是一致的。&lt;br /&gt;
虚拟存储器提供了一个抽象的&lt;strong&gt;虚拟地址空间&lt;/strong&gt;，每一个地址段中存储器的内容都有着严格的定义，它们包括：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;程序代码和数据：对所有进程，代码从同一固定地址开始，然后是数据。  &lt;/li&gt;
&lt;li&gt;堆：代码和数据区后就是运行时的堆，堆是可以动态改变大小的。&lt;/li&gt;
&lt;li&gt;共享库：存储库。  &lt;/li&gt;
&lt;li&gt;栈：编译器使用它进行函数调用，也是可以动态改变大小的。&lt;/li&gt;
&lt;li&gt;内核虚拟存储器：这是OS的一部分，为内核保留。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;写几段汇编，调试调试可以加深理解。&lt;/p&gt;
&lt;h3&gt;文件&lt;/h3&gt;
&lt;p&gt;文件就是二进制序列，在类unix系统中，一切皆为文件，系统所有的输入输出都是抽象成文件读写来实现的。&lt;/p&gt;
&lt;h2&gt;通信&lt;/h2&gt;
&lt;p&gt;通信通过网络实习，网络也可以被视为一个IO设备。&lt;/p&gt;
&lt;h2&gt;一些重要概念&lt;/h2&gt;
&lt;h3&gt;并发和并行&lt;/h3&gt;
&lt;p&gt;并发只一个系统同时具有多个活动，并行只用并发使得一个系统更快地运行。&lt;/p&gt;
&lt;h4&gt;线程级并发&lt;/h4&gt;
&lt;p&gt;在单处理器系统中，并发是通过多个进程的快速切换实现的。&lt;br /&gt;
一个处理器上如果拥有多个带有独立处理单元和高速缓存的处理器，则为多核处理器，它们共享更高层的高速缓存，但相互保持独立。&lt;br /&gt;
超线程，即让一个处理器可以“同时”执行多个线程。要注意的是，这个同时也并非真同时，这种处理器的某些硬件有多个备份，可以在单个周期级别去决定执行哪一个线程，这不同于常规的处理器——它们需要大约20000个时钟周期去做不同线程转换，所以基本可以看做是拥有可以同时执行两个线程的能力。&lt;/p&gt;
&lt;h4&gt;指令级并行&lt;/h4&gt;
&lt;p&gt;一个周期可以执行多个指令，这个理论效率可以通过一些不那么疯狂和疯狂、不那么玄学和玄学的技术达到。通常，这些手段包括“流水线”、“分支预测”等。&lt;br /&gt;
“超标量”这个术语指处理器可以比一个周期一个指令的速度更快。  &lt;/p&gt;
&lt;h4&gt;单指令、多数据并行&lt;/h4&gt;
&lt;p&gt;允许一条指令产生多个可以并行执行的操作称为“单指令、多数据”，即SIMD并行。比如并行对多对浮点数做加法的特殊指令。&lt;br /&gt;
一般建议用特殊的数据类型来编写需要这些运算的程序，比如在GCC中的向量数据和SIMD优化配置。  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Wednesday, 08 Jun 2016 11:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2016.06.08 11:00:article/Skill-2016_06_08_a</guid>
<category>CSAPP</category>
<category>计算机系统</category>
</item>

<item>
<title>【Python】二进制字节流处理库struct</title>
<link>http://dtysky.moe/article/Skill-2016_06_06_b</link>
<description>&lt;p&gt;struct类用于打包和解包二进制字节流，对于二进制数据的处理至关重要。  &lt;/p&gt;
&lt;h2&gt;pack&lt;/h2&gt;
&lt;p&gt;pack方法用于将数据打包成字节流，数据可以是数字、字符串、数组，其使用方法为：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kn&amp;quot;&gt;import&lt;/span&gt; &lt;span class=&amp;quot;nn&amp;quot;&gt;struct&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;struct&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pack&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;fmt&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;data&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;fmt是格式化的格式，data为数据，fmt的规则为&lt;code&gt;[order][format]&lt;/code&gt;，order为字节顺序，比如小端LE、大端BE等，format就是字节的格式了，可以是字节、整形、浮点数等，比如：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;In&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;13&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;struct&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pack&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;&amp;gt;II&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;])&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;Out&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;13&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;se&amp;quot;&gt;\x00\x00\x00\x01\x00\x00\x00\x02&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;In&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;14&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;struct&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;pack&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;&amp;lt;II&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;])&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;Out&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;14&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;se&amp;quot;&gt;\x01\x00\x00\x00\x02\x00\x00\x00&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;以下是详细规则：  &lt;/p&gt;
&lt;h3&gt;字节顺序&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Character&lt;/th&gt;
&lt;th&gt;Byte order&lt;/th&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;th&gt;Alignment&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;@&lt;/td&gt;
&lt;td&gt;native&lt;/td&gt;
&lt;td&gt;native&lt;/td&gt;
&lt;td&gt;native&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;=&lt;/td&gt;
&lt;td&gt;native&lt;/td&gt;
&lt;td&gt;standard&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;&lt;/td&gt;
&lt;td&gt;little-endian&lt;/td&gt;
&lt;td&gt;standard&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;gt;&lt;/td&gt;
&lt;td&gt;big-endian&lt;/td&gt;
&lt;td&gt;standard&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;!&lt;/td&gt;
&lt;td&gt;network (= big-endian)&lt;/td&gt;
&lt;td&gt;standard&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;格式&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;th&gt;C Type&lt;/th&gt;
&lt;th&gt;Python type&lt;/th&gt;
&lt;th&gt;Standard size&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;x&lt;/td&gt;
&lt;td&gt;pad byte&lt;/td&gt;
&lt;td&gt;no value&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;c&lt;/td&gt;
&lt;td&gt;char&lt;/td&gt;
&lt;td&gt;string of length&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;b&lt;/td&gt;
&lt;td&gt;signed char&lt;/td&gt;
&lt;td&gt;integer&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;(3)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B&lt;/td&gt;
&lt;td&gt;unsigned char&lt;/td&gt;
&lt;td&gt;integer&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;(3)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;_Bool&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;h&lt;/td&gt;
&lt;td&gt;short&lt;/td&gt;
&lt;td&gt;integer&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;(3)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;H&lt;/td&gt;
&lt;td&gt;unsigned short&lt;/td&gt;
&lt;td&gt;integer&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;(3)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;i&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;integer&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;(3)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;I&lt;/td&gt;
&lt;td&gt;unsigned int&lt;/td&gt;
&lt;td&gt;integer&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;(3)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;l&lt;/td&gt;
&lt;td&gt;long&lt;/td&gt;
&lt;td&gt;integer&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;(3)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;L&lt;/td&gt;
&lt;td&gt;unsigned long&lt;/td&gt;
&lt;td&gt;integer&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;(3)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;q&lt;/td&gt;
&lt;td&gt;long long&lt;/td&gt;
&lt;td&gt;integer&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;(2), (3)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Q&lt;/td&gt;
&lt;td&gt;unsigned long long&lt;/td&gt;
&lt;td&gt;integer&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;(2), (3)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;f&lt;/td&gt;
&lt;td&gt;float&lt;/td&gt;
&lt;td&gt;float&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;(4)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;d&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;float&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;(4)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;s&lt;/td&gt;
&lt;td&gt;char[]&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;p&lt;/td&gt;
&lt;td&gt;char[]&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;P&lt;/td&gt;
&lt;td&gt;void *&lt;/td&gt;
&lt;td&gt;integer&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;(5), (3)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;注意&lt;/h3&gt;
&lt;p&gt;首先要注意的是大端小端的问题，这个在上面的例子已经有所体现，如果不加order标志，默认使用的是&lt;code&gt;@&lt;/code&gt;这个order。&lt;br /&gt;
其次，使用时如果相对数组使用，必须要加上不定参数标志&lt;code&gt;*&lt;/code&gt;。  &lt;/p&gt;
&lt;h2&gt;unpack&lt;/h2&gt;
&lt;p&gt;用于解包二进制数据，基本和&lt;code&gt;pack&lt;/code&gt;的用法一致，不过是逆运算，&lt;code&gt;fmt&lt;/code&gt;的格式和&lt;code&gt;pack&lt;/code&gt;一模一样，第二个参数为二进制字节流，返回一个由结果构成的元祖。  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;In&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;15&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;struct&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;unpack&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;&amp;lt;II&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;se&amp;quot;&gt;\x01\x00\x00\x00\x02\x00\x00\x00&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;Out&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;15&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;效率&lt;/h2&gt;
&lt;p&gt;测试表明，打包和解包的性能的确会随着数据量的大小而发生变化。这个变化似乎并不是线性的，在一定数据量以下时，性能并不会成为瓶颈，但当数据量超过某个数量级时，性能会极速恶化，这一点需要注意。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Monday, 06 Jun 2016 13:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2016.06.06 13:00:article/Skill-2016_06_06_b</guid>
<category>Python</category>
<category>struct</category>
<category>二进制字节流</category>
</item>

<item>
<title>【Python】科学绘图库Matplotlib</title>
<link>http://dtysky.moe/article/Skill-2016_06_06_a</link>
<description>&lt;p&gt;这是一个Python的科学绘图库的使用心得，涉及平面绘制、子图绘制、格点、三维(3D)绘制以及动态绘图。&lt;br /&gt;
&lt;a href=&amp;quot;http://matplotlib.org/&amp;quot;&gt;http://matplotlib.org/&lt;/a&gt;  &lt;/p&gt;
&lt;h2&gt;基本绘制&lt;/h2&gt;
&lt;p&gt;直接导入库后，提供数组进行绘制即可：  &lt;/p&gt;
&lt;h3&gt;基础&lt;/h3&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kn&amp;quot;&gt;import&lt;/span&gt; &lt;span class=&amp;quot;nn&amp;quot;&gt;matplotlib.pyplot&lt;/span&gt; &lt;span class=&amp;quot;kn&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nn&amp;quot;&gt;plt&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;
&lt;span class=&amp;quot;c1&amp;quot;&gt;# 绘制线图&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;plt&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;plot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;...&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;c1&amp;quot;&gt;# 绘制散点图&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;plt&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;scatte&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;plt&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;show&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;img alt=&amp;quot;基本二维图&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2016-06-06a/1.png&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;还可以绘制别的图像，详见官网。  &lt;/p&gt;
&lt;h3&gt;进阶&lt;/h3&gt;
&lt;h4&gt;子图&lt;/h4&gt;
&lt;p&gt;可以添加多张子图进行绘制，并且修改图像的标题和坐标轴等：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;plt&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;subplot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;plt&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;title&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;子图像1&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;plt&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;subplot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;plt&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;title&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;子图像2&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;plt&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;subplot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;plt&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;title&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;子图像3&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;plt&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;subplot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;plt&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;title&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;子图像4&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;img alt=&amp;quot;基本二维图&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2016-06-06a/2.png&amp;quot; /&gt;  &lt;/p&gt;
&lt;h4&gt;面向对象&lt;/h4&gt;
&lt;p&gt;除了这种类似于matlab的用法，还可以用面向对象的用法来进行绘制：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kn&amp;quot;&gt;import&lt;/span&gt; &lt;span class=&amp;quot;nn&amp;quot;&gt;matplotlib.pyplot&lt;/span&gt; &lt;span class=&amp;quot;kn&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nn&amp;quot;&gt;plt&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;fig&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;plt&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;figure&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;axi&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;fig&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;add_subplot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;axi&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;set_title&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;子图1&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;axi&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;set_xlabel&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;x轴&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;axi&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;set_ylabel&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;y轴&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;ax&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;set_xlim&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;min&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;10&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;ax&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;set_ylim&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;10&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;......&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h4&gt;细化分隔&lt;/h4&gt;
&lt;p&gt;也可以进一步细化控制每一个子图所占区域范围：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kn&amp;quot;&gt;import&lt;/span&gt; &lt;span class=&amp;quot;nn&amp;quot;&gt;matplotlib.gridspec&lt;/span&gt; &lt;span class=&amp;quot;kn&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nn&amp;quot;&gt;gridspec&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;gs&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;gridspec&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;GridSpec&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;fig&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;plt&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;figure&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;figsize&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;16&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;9&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;dpi&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;72&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;fig&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;suptitle&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;Distance offset from start&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;ax1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;fig&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;add_subplot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;gs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[:,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;])&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;ax1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;set_title&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;Total&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;ax1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;set_xlabel&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;X(m)&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;ax1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;set_ylabel&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;Y(m)&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;ax2&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;fig&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;add_subplot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;gs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;])&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;ax2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;set_title&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;X&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;ax2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;set_xlabel&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;t(s)&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;ax2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;set_ylabel&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;0m&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;ax3&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;fig&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;add_subplot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;gs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;])&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;ax3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;set_title&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;Y&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;ax3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;set_xlabel&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;t(s)&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;ax3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;set_ylabel&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;0m&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;img alt=&amp;quot;基本二维图&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2016-06-06a/3.png&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;这种情况下，第一张子图将占据第一行，第二张子图占据第二行的第一列，第三张则是第二行的第二列。  &lt;/p&gt;
&lt;h4&gt;三维&lt;/h4&gt;
&lt;p&gt;三维图的绘制也是被支持的，只需要导入一个模块便可以在绘制的时候加上z轴的参数：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kn&amp;quot;&gt;import&lt;/span&gt; &lt;span class=&amp;quot;nn&amp;quot;&gt;matplotlib.pyplot&lt;/span&gt; &lt;span class=&amp;quot;kn&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nn&amp;quot;&gt;plt&lt;/span&gt;
&lt;span class=&amp;quot;kn&amp;quot;&gt;from&lt;/span&gt; &lt;span class=&amp;quot;nn&amp;quot;&gt;mpl_toolkits.mplot3d&lt;/span&gt; &lt;span class=&amp;quot;kn&amp;quot;&gt;import&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;Axes3D&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;fig&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;plt&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;figure&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;figsize&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;16&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;9&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;dpi&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;72&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;ax1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;fig&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;add_subplot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;projection&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;3d&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;ax1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;set_title&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;三维图&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;ax1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;set_xlabel&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;X&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;ax1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;set_ylabel&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;Y&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;ax1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;set_zlabel&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;Z&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;ax1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;scatter&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;([&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;img alt=&amp;quot;基本二维图&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2016-06-06a/4.png&amp;quot; /&gt;  &lt;/p&gt;
&lt;h3&gt;动画&lt;/h3&gt;
&lt;p&gt;动画也是被支持的，其核心在于一个模块和一个方法，以下是我常用的方案更详细可以看&lt;a href=&amp;quot;http://matplotlib.org/api/animation_api.html&amp;quot;&gt;这里&lt;/a&gt;：  &lt;/p&gt;
&lt;p&gt;导入核心库和使用核心方法：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;# 核心库&lt;/span&gt;
&lt;span class=&amp;quot;kn&amp;quot;&gt;import&lt;/span&gt; &lt;span class=&amp;quot;nn&amp;quot;&gt;matplotlib.animation&lt;/span&gt; &lt;span class=&amp;quot;kn&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nn&amp;quot;&gt;animation&lt;/span&gt;
&lt;span class=&amp;quot;c1&amp;quot;&gt;# 生成动画对象&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;ani&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;animation&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;FuncAnimation&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;fig_p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;draw_distances_run&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;draw_distances_data_gen&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;interval&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;200&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;其中&lt;code&gt;draw_distances_run&lt;/code&gt;方法是绘制每一帧更新时用于绘制的方法，&lt;code&gt;draw_distances_data_gen&lt;/code&gt;方法用于在绘制中修改绘制数据，而&lt;code&gt;interval&lt;/code&gt;参数则是一个毫秒的时间，是每两帧之间的间隔。
那么如何具体的来使用呢？  &lt;/p&gt;
&lt;p&gt;我此处选择的是一个并非那么合理但是快速的方法，当然，其实外面用个类包起来其实就好，满足需求就行。。。  &lt;/p&gt;
&lt;h4&gt;初始化数据&lt;/h4&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;def&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;draw_distances_start&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;():&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;fig&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;plt&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;figure&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;figsize&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;16&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;9&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;dpi&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;120&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;fig&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;suptitle&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;Distance offset from start&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;ax1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;fig&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;add_subplot&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;projection&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;3d&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;ax1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;set_title&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;图1&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;ax1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;set_xlabel&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;X&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;ax1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;set_ylabel&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;Y&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;ax1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;set_zlabel&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;Z&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;data1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;ax1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;scatter&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;([],&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[],&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[])&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;fig&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;ax1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;data1&lt;/span&gt;
&lt;span class=&amp;quot;c1&amp;quot;&gt;# 全局变量&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;fig&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;ax1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;data1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;draw_distances_start&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;data_now&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;x&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[],&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;y&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[],&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;z&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[]]&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;data_new&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;x&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[],&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;y&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[],&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;z&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[]]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h4&gt;更新数据&lt;/h4&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;def&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;draw_distances_data_gen&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;():&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;key&lt;/span&gt; &lt;span class=&amp;quot;ow&amp;quot;&gt;in&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;data_now&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;data_now&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;key&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;extend&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;data_new&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;key&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;])&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;yield&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;data_now&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h4&gt;更新绘制&lt;/h4&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;def&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;draw_distances_run&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;):&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;x&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;y&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;z&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;data1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;set_offsets&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;([(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;])])&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;data1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;set_3d_properties&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;([&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]],&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;z&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;ax1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;set_xlim&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;min&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;ax1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;set_ylim&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;min&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;ax1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;set_zlim&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;min&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;data1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;注意，这里更新x和y用的是&lt;code&gt;set_offsets&lt;/code&gt;方法，而更新z用的是&lt;code&gt;set_3d_properties&lt;/code&gt;方法。&lt;/p&gt;
&lt;h4&gt;提供新数据&lt;/h4&gt;
&lt;p&gt;以上代码中，一个全局的&lt;code&gt;data_now&lt;/code&gt;变量用于存储当前要绘制的数据，而&lt;code&gt;data_new&lt;/code&gt;则是该帧的新数据。在实际使用中，为了防止卡顿，一般选择用一个新的线程来更新&lt;code&gt;data_new&lt;/code&gt;的数据：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;def&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;refresh&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;():&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;while&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;True&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;data_new&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;get_new_data&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;

&lt;span class=&amp;quot;n&amp;quot;&gt;thread&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;start_new&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;refresh&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;())&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;一切准备就绪后，调用&lt;code&gt;plt.show()&lt;/code&gt;即可开始绘制动画。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Monday, 06 Jun 2016 11:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2016.06.06 11:00:article/Skill-2016_06_06_a</guid>
<category>Python</category>
<category>Matplotlib</category>
<category>科学绘图</category>
<category>三维绘图</category>
<category>动态绘图</category>
</item>

<item>
<title>【UWB/室内定位】坑和结论</title>
<link>http://dtysky.moe/article/Skill-2016_01_30_a</link>
<description>&lt;p&gt;年轻人，初入职场，没事不要给自己找坑。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;继续深入研究，身心俱疲，这里是一些坑和结论。&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;1 坑和结论&lt;/h1&gt;
&lt;p&gt;这里所用UWB芯片皆为Decawave家的DW1000，分别使用TREK1000套件和某公司的产品进行了测试。&lt;br /&gt;
这里首先声明，DWM1000这个小模块基本只能拿来玩玩,想要做正事的可以直接跳过。&lt;/p&gt;
&lt;h2&gt;1.0 算法&lt;/h2&gt;
&lt;p&gt;TREK1000套件的定位算法基本是直接解算，产品则对原始的测距信息有限幅，定位输出有卡尔曼滤波。&lt;br /&gt;
以下Python随便写的用最小二乘法通过同一个平面的若干个基站和tag的距离求xy坐标的函数，比较糙，有兴趣自己看看吧：&lt;br /&gt;
函数输入是每个anchor的位置构成的数组,以及每个anchor和tag的距离序列构成的数组.&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kn&amp;quot;&gt;from&lt;/span&gt; &lt;span class=&amp;quot;nn&amp;quot;&gt;scipy.optimize&lt;/span&gt; &lt;span class=&amp;quot;kn&amp;quot;&gt;import&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;leastsq&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;curve_fit&lt;/span&gt;
&lt;span class=&amp;quot;kn&amp;quot;&gt;from&lt;/span&gt; &lt;span class=&amp;quot;nn&amp;quot;&gt;numpy&lt;/span&gt; &lt;span class=&amp;quot;kn&amp;quot;&gt;import&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;sqrt&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;square&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;subtract&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;add&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;multiply&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;def&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;get_xyz_by_ls&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;anchors&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;distances&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;):&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;def&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;fun&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ps&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;ds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;):&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;res_x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;res_y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;ps&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;anchors_x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;anchors_y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;ds&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;add&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
            &lt;span class=&amp;quot;n&amp;quot;&gt;add&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;multiply&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;multiply&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;anchors_x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;res_x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)),&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;anchors_x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]),&lt;/span&gt;
            &lt;span class=&amp;quot;n&amp;quot;&gt;add&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;multiply&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;multiply&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;anchors_y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;res_y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)),&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;anchors_y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]))&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;def&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;error&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ps&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;ds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;dis&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;):&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;dis&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;fun&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ps&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;ds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;

    &lt;span class=&amp;quot;n&amp;quot;&gt;length&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;len&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;distances&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;ps&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt; &lt;span class=&amp;quot;ow&amp;quot;&gt;in&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;anchors&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt; &lt;span class=&amp;quot;ow&amp;quot;&gt;in&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;anchors&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;
    &lt;span class=&amp;quot;c1&amp;quot;&gt;# ps[0]: xi+1 - xi, xi+1 ^ 2 - xi ^ 2&lt;/span&gt;
    &lt;span class=&amp;quot;c1&amp;quot;&gt;# ps[1]: yi+1 - yi, yi+1 ^ 2 - y1 ^ 2&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;ps&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;
                 &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ps&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;][&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;ps&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;][&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;ow&amp;quot;&gt;in&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;xrange&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;length&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)],&lt;/span&gt;
                 &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;square&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ps&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;][&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;])&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;square&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ps&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;][&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;])&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;ow&amp;quot;&gt;in&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;xrange&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;length&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;ps&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;
                 &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ps&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;][&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;ps&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;][&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;ow&amp;quot;&gt;in&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;xrange&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;length&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)],&lt;/span&gt;
                 &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;square&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ps&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;][&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;])&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;square&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ps&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;][&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;])&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;ow&amp;quot;&gt;in&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;xrange&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;length&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;ps&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;array&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ps&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;c1&amp;quot;&gt;# ds: di+1 ^ 2 - di ^ 2&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;ds&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;square&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;distances&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;])&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;square&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;distances&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;])&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;ow&amp;quot;&gt;in&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;xrange&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;length&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;x0&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;leastsq&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;error&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;x0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;args&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ps&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;ds&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;1.1 适用范围&lt;/h2&gt;
&lt;p&gt;UWB一般应用场合是室内定位，低空定位，动态性能要求不太高的定位。&lt;br /&gt;
定位原理在上一篇说过了，都是测距后用三边法或者多基站下的线性拟合。而测距使用的原理一般是TOF（飞行时间），这个和微波雷达、激光测距等基本是一个原理，所以也面临相同的问题。&lt;br /&gt;
定位对基站的布局是有需求的，基本要求tag不能越过anchor所框的区域内，同时基站的布置和定位要求密切相关，有几个维度的需求，基站就应该有几个维度的变化，如果要三维定位，一般要求至少一个基站在空中。  &lt;/p&gt;
&lt;h2&gt;1.2 精度&lt;/h2&gt;
&lt;p&gt;精度方面，由于我们用于空中的水平定位，所以关心的是水平、也就是xy轴的精度，我分别进行了地面的静态测试和无人机上的动态测试，结论如下：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;单对精度可以认为始终在15cm之内，但要求天线位置最佳，天线的坑会在下面说道。与此同时，两米之内的测距误差可能较大，这个是原理所决定的，暂时无解。&lt;/li&gt;
&lt;li&gt;定位精度和基站围成的范围与tag到基站的距离都有着很大关系，一般要求需要定位的距离和基站围成的距离越接近越好。在13m x 6m的布局下，tag随着和基站绝对距离的增加，其定位精度会有所下降： 5-15m时，精度在20cm左右；15-25m时，精度在30cm左右；25-35m时，精度在40cm左右。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h2&gt;1.3 静态性能&lt;/h2&gt;
&lt;p&gt;静态性能指测距和定位可以达到的帧率，实测最高大概在三十多帧（产品），开发套件只有10帧左右（算法决定），不过对于一般应用也足够了。&lt;br /&gt;
在静态测试中，测距的定位均有一定的失效率，失效率大概在3%左右。失效率和tag到基站的距离密切相关，同时微波传播的路径也有不小关系，和电源、机械振动、电磁环境都有一定的关系。以上3%的失效率只是去除了机械振动与环境干扰后的结论。&lt;/p&gt;
&lt;h2&gt;1.4 动态性能&lt;/h2&gt;
&lt;p&gt;动态性能指的是在运动时的定位的失效率以及跟随性。&lt;br /&gt;
和无人机上的差分GPS进行对比，跟随性方面基本可以认为是实时跟随，这个和算法有很大的关系（这里采用限幅和卡尔曼滤波）。失效率方面，距离越远失效率越高，基本可以认为每十米会增加20%左右的失效率。  &lt;/p&gt;
&lt;h2&gt;1.5 失效分析&lt;/h2&gt;
&lt;p&gt;综上，影响UWB测距（这个是定位的基本）性能的元素如下：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;距离：距离越远，测距性能受环境的影响越明显，这是一个被动技能，可以理解为基础能力下降，受到异常状态的概率上升。&lt;/li&gt;
&lt;li&gt;传播路径：空中明显比地面性能差，室外明显比楼道性能差，这应该是由于墙面对微博的反射导致的信号增强造成的，虽然反射同时也会出现多径效应，但在实验中体现并不明显，所以基本可以认为有墙面对测距性能是有利的。&lt;/li&gt;
&lt;li&gt;电源：电源功率不足时，可能会导致信号发射接收时基站/TAG宕机，笔记本的USB口供电时出现过这个问题。  &lt;/li&gt;
&lt;li&gt;机械振动：这个不同于一定范围内运动时的失效，而是电机等造成的小幅度高频机械振动，这种振动会对二十米以上的测距性能造成毁灭性影响。&lt;/li&gt;
&lt;li&gt;电磁干扰：这个比较玄学了，我这没什么好的解释。 &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;影响定位，却无关于测距的元素就是算法方面了，由于算法的问题，所以基站的布局必须和tag和基站之间的最大距离同步，比如tag可能在空中100m，那么最好有一个100m x 100m的基站布局，如果要测三维，应该至少有两个基站将tag夹在中间。  &lt;/p&gt;
&lt;p&gt;完。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Saturday, 30 Jan 2016 23:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2016.01.30 23:00:article/Skill-2016_01_30_a</guid>
<category>UWB</category>
<category>DW1000</category>
<category>TREK1000</category>
<category>TOF</category>
<category>室内定位</category>
</item>

<item>
<title>【UWB/室内定位】DW1000大坑-基础系统搭建1</title>
<link>http://dtysky.moe/article/Skill-2015_11_21_a</link>
<description>&lt;p&gt;如果自嘲只不过是将真诚化为伪装的工具。&lt;br /&gt;
那么。&lt;br /&gt;
就让我将之坚持到底吧。  &lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;由于公司项目需求所以研究了一下UWB定位，UWB即超宽频定位，一般用于室内离线定位，在众多离线定位方法中它算是精度最高的之一，其定位原理一般为TOA活着TDOA。DW1000则是公司研发的一款UWB定位芯片，它同时也可以用于通信。此芯片据说可以达到300M、6.8mbps的传输速度，定位精度可以到达10cm，并且除了官方的开发套件外也有现成的开源方案。本章将会说明如何搭建一个基础的UWB硬件系统，着重于说明期间遇到的一些大坑，比如电源...  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;1 UWB&lt;/h1&gt;
&lt;p&gt;UWB定位，超宽频定位，本质上和GPS卫星定位的原理是一样的，它不过是将卫星放在了地面上，也就是自己搭建作为卫星的基站，然后去根据和GPS定位相同的算法计算出每个Anchor（基站）到Tag（移动站）的距离，从而解算出移动站的空间坐标，对于三维空间定位而言一般至少需要四个Anchor，Anchor的数量增加不但可以构建超定方程组使用一些算法提高精度，同时也可以作为通信质量不佳下的冗余设计，所以在可以接受的条件下，基站的数量一般越多越好。  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;UWB&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2015_11_22a/-1.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;h2&gt;1.1 DW1000&lt;/h2&gt;
&lt;p&gt;&lt;a href=&amp;quot;http://www.decawave.com/products/dw1000&amp;quot;&gt;DW1000&lt;/a&gt;是&lt;a href=&amp;quot;http://www.decawave.com/&amp;quot;&gt;DecaWave&lt;/a&gt;公司推出的一款UWB定位芯片，按照其DATASHEET说明，其有效距离可以达到300m，同时据代理商提供的信息，其定位精度可以到到10cm的程度，这在业内算是一个非常不错的参数。不过介于每个厂商总会吹一些牛的这个业界常识，我们还是要自己测试一下才知道具体的事实。&lt;br /&gt;
对于有钱的开发者，建议直接购买&lt;a href=&amp;quot;http://www.decawave.com/products/evk1000-evaluation-kit&amp;quot;&gt;EVK1000&lt;/a&gt;或者&lt;a href=&amp;quot;http://www.decawave.com/products/trek1000&amp;quot;&gt;TREK1000&lt;/a&gt;进行开发，这两者都是官方自己生产的开发板，无论是电路走线还是电源方面都有着很可靠的保证，操作上更是做到了去除对PC的依赖，开机即可在LCD屏上看到测距效果。对于一般的开发者，则建议购买&lt;a href=&amp;quot;http://www.decawave.com/products/dwm1000-module&amp;quot;&gt;DWM1000&lt;/a&gt;这个官方封装好的模块进行开发，相对于裸片，它集成了时钟和天线等，比较便于测试。  &lt;/p&gt;
&lt;p&gt;下面是我购买到的DWM1000模块，260一块~：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;DWM1000&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2015_11_22a/0.jpg&amp;quot; /&gt;    &lt;/p&gt;
&lt;p&gt;DW1000的控制比较简单，其指令和数据的传输全部是基于SPI总线的，这是最简单的一种总线，通过有SPI支持的单片机几乎可以进行无痛操作，而具体的寄存器介绍和配置请见&lt;a href=&amp;quot;http://www.decawave.com/support&amp;quot;&gt;DW1000 UserManual&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;1.2 TOA/TDOA&lt;/h2&gt;
&lt;p&gt;UWB定位的原理是TOA或者TODA，当然也有还有AOA等，不常见。  &lt;/p&gt;
&lt;h3&gt;1.2.1 TOA&lt;/h3&gt;
&lt;p&gt;TOA即“到达时间”，这种方式定位是通过Anchor和Tag之间的多次通信实现的，如下图：&lt;br /&gt;
&lt;img alt=&amp;quot;TOA&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2015_11_22a/1.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Anchor首先发给Tag一个包，同时记录下Anchor当前的时间信息，记为T1。  &lt;/li&gt;
&lt;li&gt;Tag收到基站的信息，返回一个ACK。  &lt;/li&gt;
&lt;li&gt;Anchor收到Tag的ACK，记录当前的时间信息，记为T2。  &lt;/li&gt;
&lt;li&gt;Anchor计算时间差Tr = T2 - T1，并且根据此计算出距离：  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;d = c * Tr / 2  &lt;/p&gt;
&lt;p&gt;其中c为光速。  &lt;/p&gt;
&lt;p&gt;当然，实际应用中为了更加靠谱，往往不仅仅是利用两次通信来测距，还会有更加复杂的多次通信来提高精度，详细的同样可以看DW1000的UserManual最后一节。&lt;br /&gt;
对于空间定位，只需要利用&lt;a href=&amp;quot;https://en.wikipedia.org/wiki/Trilateration&amp;quot;&gt;SX（球面相交法）&lt;/a&gt;便可以得出最后的坐标。&lt;br /&gt;
可见，为了一次定位，每个Anchor和Tag之间要进行两次通信，故DecaWave又将这种定位方式称为“Two-way-ranging”。这种定位的优势在于其实现的便捷性和对硬件的宽容，只需要有几个摆放在不同位置的Anchor和一个Tag便可进行定位，而缺点嘛...首先自然是定位速度了，其次，由于每次通信的质量无法保证，而一对Anchor/Tag又无法做自我的校准，精度自然也会受到相当的影响。&lt;/p&gt;
&lt;h3&gt;1.2.2 TDOA&lt;/h3&gt;
&lt;p&gt;TODA即“到达时间差”，这种方式的一次测距是由两个Anchor和一个Tag实现的。在这种模式下，多个时钟完全同步的Anchor同时接受来自一个Tag的包，对于不同位置的Anchor，同一个Tag的同一次广播包到达的时间是不同的，所以便有有以下算法：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Tag发出一个广播包。  &lt;/li&gt;
&lt;li&gt;两个Anchor接收到同一个包，Anchor1接收到的时间为T1，Anchor2接收到的时间为T1。  &lt;/li&gt;
&lt;li&gt;计算时间差Td = T2 - T1。&lt;/li&gt;
&lt;li&gt;对于至少四个Anchor，可以得到三组这样的两两之间的信息。&lt;/li&gt;
&lt;li&gt;通过数学方法（&lt;a href=&amp;quot;https://en.wikipedia.org/wiki/Multilateration&amp;quot;&gt;multi-lateration&lt;/a&gt;）可以解算出Tag的空间坐标。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;由于算法比较复杂，这里不再赘述。  &lt;/p&gt;
&lt;p&gt;由此可见，TDOA的优势首先在于一次定位的通信次数显著减少，其次由于是用时间差而非绝对时间进行测距，其精度也比TOA高出一些。但优势总是以一些代价换来的，TDOA系统中各个Anchor的时钟必须严格同步，由于这种定位本质上是依赖于光速的，所以1ns的固有时钟误差便可以造成30cm的固有距离误差，这一点显然是不可接受的。而要打造一个间距比较大的精确同步系统成本又是比较高昂的，所以从这个层面来讲，TDOA并非一般人或者公司可以玩得起的。&lt;br /&gt;
当然，DecaWave自身研究出了另一种TDOA的方式(详见APS003)，也就是在时钟之外另外加了一个同步位用于同步，不过看起来也是比较复杂的，暂时没有深究。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;2 基础系统搭建/电源需求&lt;/h1&gt;
&lt;p&gt;在搭建系统之前首先调研有没有现成的项目可以用于测试，寻找后首先进入视线的是&lt;a href=&amp;quot;https://github.com/thotro/arduino-dw1000&amp;quot;&gt;基于arduino的这个项目&lt;/a&gt;，此项目使用Arduino作为主控，提供了一系列的API和例子。  &lt;/p&gt;
&lt;h2&gt;2.1 基于Arduino的系统&lt;/h2&gt;
&lt;p&gt;首先在淘宝上购买了二十多一片的山寨Arduino Nano，用面包板搭建了一个最基础的两片测距系统进行测试，系统如图，测试遇到的一些问题如下：&lt;br /&gt;
&lt;img alt=&amp;quot;山寨&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2015_11_22a/2.jpeg&amp;quot; /&gt;  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;程序无法工作，重复setup或者跑着跑着跑飞了。   &lt;/li&gt;
&lt;li&gt;使用示波器检测SPI通信，表明正常；使用逻辑分析仪抓SPI包，分析正常。排除SPI接口问题。  &lt;/li&gt;
&lt;li&gt;可连接性测试例子调试，程序跑飞，操蛋过程略，在每次打印设备信息前加100ms的延迟，跑通。  &lt;/li&gt;
&lt;li&gt;基础收发例子，跑飞，在打印信息前加延迟，无效，全部注释逐个取消注释，发现代码超过一定量跑飞，初步定为为片子质量有问题。  &lt;/li&gt;
&lt;li&gt;基础收发例子，将缓冲数组的长度从1024改为了256，程序跑通，第二天到公司又跑不通。   &lt;/li&gt;
&lt;li&gt;经过提醒在电源和地之间加了个大电容，跑的时间长了点，粗略定位为供电问题。  &lt;/li&gt;
&lt;li&gt;不再使用山寨Arduino的3.3v输出口给DWM1000供电，而是换上了独立的线性电源，程序基本跑通，定位为电源问题。  &lt;/li&gt;
&lt;li&gt;测试基础收发，发现程序虽然可以跑但是收不到数据，于是对源代码进行了改造，不对数据进行判断直接读DW1000的RX_BUFFER之中的数据进行打印，发现一部分数据数对的，但后面都会乱码。  &lt;/li&gt;
&lt;li&gt;将“Hello DW1000”改成了“Fuck DW1000”，程序跑通，初步定位为山寨片子质量问题。  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;以下为测试过程中的一些现场图：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2015_11_22a/3.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2015_11_22a/4.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2015_11_22a/5.png&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2015_11_22a/6.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2015_11_22a/7.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2015_11_22a/8.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2015_11_22a/9.png&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;之后购买了Arduino中文网推荐的卖家之一的DFROBOT生产的正版Arduino Nano，搭建了系统进行测试：&lt;br /&gt;
&lt;img alt=&amp;quot;正版&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2015_11_22a/10.JPG&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;测试经历：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;测试基础收发，还是和之前一样的问题，看来山寨片子或许只是供电差了一些，其他也倒还好。  &lt;/li&gt;
&lt;li&gt;测试MessagePingPong，发现可以跑通，但通信质量并不好，有时还是会出现乱码。  &lt;/li&gt;
&lt;li&gt;测距测试，使用其中一个座位Anchor，一个作为Tag，初步测距成功，并且RX_POWER和开源项目自身的指标比较相近。  &lt;/li&gt;
&lt;li&gt;多个Anchor测距测试，通信质量急速下降，再次判定为电源问题。  &lt;/li&gt;
&lt;li&gt;改进电源后再次测试，发现测距很不稳定，总是某个Anchor信号很强，其他很弱，或者都很弱。  &lt;/li&gt;
&lt;li&gt;对Anchor和Tag之间距离进行了变换，发现当前系统的有效距离和稳定性很差。  &lt;/li&gt;
&lt;li&gt;由于电子工程师为了省钱买的线性电源，Arduino全烧了，实验中止。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;以下是这一段测试的一些现场图：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2015_11_22a/11.JPG&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2015_11_22a/12.png&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2015_11_22a/14.JPG&amp;quot; /&gt;  &lt;/p&gt;
&lt;h2&gt;2.2 基于MBED的系统&lt;/h2&gt;
&lt;p&gt;虽然Arduino全烧了，但我们还有山寨的MBED，虽然是山寨的，也比正版的Arduino Nano贵不了多少，但其性能还是要高出不少的，加上无意中发现了&lt;a href=&amp;quot;https://developer.mbed.org/users/manumaet/code/DecaWave/&amp;quot;&gt;这个开源项目&lt;/a&gt;，所以就不妨尝试了一下。  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;MBED&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2015_11_22a/15.JPG&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;基于MEBD的这套系统比起前面那个开源项目的例子要成熟一些，可以直接指定几个Anchor并且同步串口打印Tag到几个Anchor的距离，测试结果也比较喜人，稳定性和速度皆甩出Arduino一截，并且自身的3.3v输出可以带的动DW1000。注意，在实际使用中，三个MBED加上DW1000在测距时的功率达到了12W左右，可见功耗还是非常可观的，并且远远超出了官方文档标出的3.3v/500ma的极限。  &lt;/p&gt;
&lt;p&gt;以下是一些测试结果，比较不幸的是三号姬的DW1000在两次炸板事件中光荣牺牲了（后来换到了二号姬的面包板上），所以测试数据少一组：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2015_11_22a/16.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2015_11_22a/17.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/skill-2015_11_22a/18.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;3 结论&lt;/h1&gt;
&lt;p&gt;由于补充的片子暂时没有到货，实验暂时如此，以下是这次总结出的DW1000使用注意点：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;供电很重要，DW1000非常吃功率，其瞬间电流可能非常大，对电源的稳定性也有一定要求，这一点务必注意。  &lt;/li&gt;
&lt;li&gt;对于复杂一点的应用，低端Arduino还是不要用。  &lt;/li&gt;
&lt;li&gt;如果可能，电路问题还是交给专业的玄学工程师去解决，否则容易事倍功半。  &lt;/li&gt;
&lt;li&gt;对于某些开源项目，你还是要明白底层原理，要能改源代码，这是没有办法的。  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;Over，暂时进行距离-&amp;gt;坐标的算法实现。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Sunday, 22 Nov 2015 01:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.11.22 01:00:article/Skill-2015_11_21_a</guid>
<category>UWB</category>
<category>DW1000</category>
<category>Arduino</category>
<category>Mbed</category>
<category>TOA</category>
</item>

<item>
<title>【SICP】过程抽象:递归，迭代和高阶函数</title>
<link>http://dtysky.moe/article/Skill-2015_09_10_a</link>
<description>&lt;p&gt;银河疾走。&lt;br /&gt;
疾走，疾走的终点是死亡，盛宴却永不会落幕。  &lt;/p&gt;
&lt;p&gt;这个系列是在学习魔法导论SICP时某些精要部分的归纳和总结，本节是第一章的总结，第一章在复习了一些常见算法的同时，讲述了在极其简单的基本scheme环境下如何适当的对程序的过程进行抽象，道出了抽象的一般形式，并给出了如何写出高可读性的代码——这些代码是符合人类的逻辑思维过程的。&lt;br /&gt;
第一章所有的习题如下，使用DrRacket环境： &lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/SICP/tree/master/Chapter1&amp;quot;&gt;SICP-Chapter1&lt;/a&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;1 Scheme&lt;/h1&gt;
&lt;p&gt;Scheme是一种Lisp方言，Lisp，即表处理语言，他的基本编程思想是函数式和递归的，所以对数学的亲和性很高，但对于人类而言就没有这么容易理解了，和其他C系的编程语言不同，在写Lisp的时候往往需要首先进行人肉优化，这会带来两个结果：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;增加思考成本，降低短期效率&lt;/li&gt;
&lt;li&gt;让代码结构变得更为清晰，增加可读性，降低维护成本  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;当然，现在Lisp嫡系的编程语言境遇都不怎么好，所以虽然其有通用语言之名，但却没有实际生产之实，而本书用到的Scheme更是只用于教学领域，那么这本书讲的东西就毫无实践意义了么？并非如此，SICP并不是一本讲语言的书，他讲的是对程序设计进行抽象的思想，而使用Scheme只是恰好有许多优秀、无所不包的特性，同时还能方便使用者造轮子，而造轮子，则是学习的最高效手段之一。  &lt;/p&gt;
&lt;h2&gt;1.1 本节用到的Scheme语法&lt;/h2&gt;
&lt;p&gt;基本运算：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;+ &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;;3&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;- &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;;-1&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;* &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;;2&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;/ &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;;1/2&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;/ &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;2.0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;;0.5&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;&amp;lt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;;#t&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;&amp;gt; &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;;#f&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;逻辑运算：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;and &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;;#f&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;or &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;;#t&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;not &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;;#t&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;过程定义：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;define &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;inc&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;+ &lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;inc&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;;3&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;分支语句：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;define &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;if-test&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;&amp;lt; &lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;5&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;
    &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;if-test&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;;1&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;if-test&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;6&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;;2&lt;/span&gt;

&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;define &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;cond-test&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;cond &lt;/span&gt;  &lt;span class=&amp;quot;p&amp;quot;&gt;((&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;&amp;lt; &lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;5&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
            &lt;span class=&amp;quot;p&amp;quot;&gt;((&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;&amp;lt; &lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;10&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
            &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;else &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)))&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;cond-test&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;;1&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;cond-test&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;9&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;;2&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;cond-test&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;100&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;;3&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;匿名函数：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;((&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;lambda &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;+ &lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;;2&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;let &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;((&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;+ &lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)))&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;; 2&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;hr /&gt;
&lt;h1&gt;2 递归和迭代&lt;/h1&gt;
&lt;p&gt;递归和迭代是两种基本的过程，他们都只需要一个过程的&lt;strong&gt;递推公式&lt;/strong&gt;，通常而言，求得递推公式往往比求得通项公式要来的容易，所以这其实也是一种可以简化思考的方式。  &lt;/p&gt;
&lt;h2&gt;2.1 线性递归和迭代&lt;/h2&gt;
&lt;p&gt;考虑一个函数，其定义为：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;n! = 1 x 2 x 3 x ... x n  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;显然，这就是阶乘函数的定义，其递推公式是十分简单和浅显的，而通项公式则不是那么简单，下面我们可以分别用递归和迭代来实现它：  &lt;/p&gt;
&lt;p&gt;递归：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;define &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;factor&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;n&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if &lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;n&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;
    &lt;span class=&amp;quot;nv&amp;quot;&gt;n&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;factor&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;- &lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;n&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这个递归实现的实际运算过程如下：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;factor&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;5&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;5&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;factor&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;5&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;factor&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)))&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;......&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;5&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;factor&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))))&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;5&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))))&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;......&lt;/span&gt;
&lt;span class=&amp;quot;mi&amp;quot;&gt;120&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;可见，递归过程中，原先的表达式会先被展开，直到无法再继续展开之时再回归计算。同时注意到递归总是发生在条件分支的末尾，这种递归而形式叫做“尾递归”，也是最常用的一种递归。&lt;/p&gt;
&lt;p&gt;迭代：  &lt;/p&gt;
&lt;p&gt;用递归过程计算阶乘实际上是按照以下公式进行的：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;n! = n x (n - 1) x (n - 2) x ... x 2 x 1&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这有一个展开后回归的过程，所以必须使用一个特殊的存储结构（堆栈）去存储整个展开的轨迹，以用于之后的回归计算，虽然这种形式十分直观和便于理解，但往往也会造成大量资源的消耗，于是有了以下的迭代形式：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;define &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;factor&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;n&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;define &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;iter&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;count&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;result&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;max-count&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;= &lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;count&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;max-count&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
            &lt;span class=&amp;quot;nv&amp;quot;&gt;result&lt;/span&gt;
            &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;iter&lt;/span&gt;
                &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;+ &lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;count&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
                &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;* &lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;result&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;count&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
                &lt;span class=&amp;quot;nv&amp;quot;&gt;max-count&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)))&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;iter&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;n&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;迭代的实际运算过程如下：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;factor&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;5&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;iter&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;5&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;iter&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;5&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;iter&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;5&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;iter&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;6&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;5&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;iter&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;5&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;24&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;5&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;5&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;mi&amp;quot;&gt;120&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;可见，在迭代形式下，计算是实时的，没有展开的概念，也无需回归计算，计算过程中只需要维护count, result和max-count三个变量即可，需求的资源大大减少，然而这个的代价是代码的形式不够直观（阶乘这种小东西还好，一旦复杂起来...）&lt;/p&gt;
&lt;h2&gt;2.2 树形递归和迭代&lt;/h2&gt;
&lt;p&gt;树形递归实际上就是多个分支的线性递归，也可以说线性递归是单分支的树形递归，考虑以下函数：  &lt;/p&gt;
&lt;p&gt;$$Finb(n) = \begin{cases}0 &amp;&amp; n = 0  \\1 &amp;&amp; n = 1\\ Finb(n-1) + Finb(n-2) &amp;&amp; others\ \end{cases}$$&lt;/p&gt;

&lt;p&gt;这个函数即Fibonacci数列，它有许多有趣的性质和广泛的应用然而在这里只用它来讨论树形递归，它的递归形式如下：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;define &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;fib&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;n&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;cond&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;((&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;= &lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;n&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;((&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;= &lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;n&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;else &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;+ &lt;/span&gt;
                &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;fib&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;- &lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;n&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt;
                &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;fib&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;- &lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;n&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))))))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;你可以自行按照上面的展开过程进行展开，可以看到，由于最末的分支中有两个分支，这两个分支将会分别展开，并且每一次的进一步展开也是如此，所以最后将会形成一个树状的结构。  &lt;/p&gt;
&lt;p&gt;迭代形式：  &lt;/p&gt;
&lt;p&gt;与线性迭代不同，线性迭代中下一个状态只和当前状态有关，而fib函数下一个状态还和前一个状态有关，所以需要额外增加一个变量来保存前一个状态的值，其实现如下：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;define &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;fib&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;n&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;define &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;iter&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;count&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;result&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;last&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; 
        &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;= &lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;n&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; 
            &lt;span class=&amp;quot;nv&amp;quot;&gt;result&lt;/span&gt;
            &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;iter&lt;/span&gt; 
                &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;- &lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;count&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
                &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;+ &lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;result&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;last&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
                &lt;span class=&amp;quot;nv&amp;quot;&gt;result&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)))&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;iter&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;n&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;2.3 比较&lt;/h2&gt;
&lt;p&gt;由以上分析可见，递归过程一般比较直观，便于编写，但空间复杂度和时间复杂度均比迭代过程高，相反，迭代过程比较考验设计能力，但其复杂度相对迭代过程占有优势。&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;3 高阶函数&lt;/h1&gt;
&lt;p&gt;高阶函数是一类特殊的函数，得益于Lisp语系函数式一等公民的这个特性，让其得以实现，在Scheme中，函数也是一种数据类型，可以作为函数的参数或者函数的返回值，这也给我们提供了一种思维的方式——我们可以定义一些函数，它们以函数参数，返回一些函数，就像是某些用于生成“生产设备”的工厂一样，生产出用于加工其他部件的机器。  &lt;/p&gt;
&lt;p&gt;注：Scheme中也将“函数”称为“过程”。  &lt;/p&gt;
&lt;p&gt;比如，我们都知道“加法”是一种抽象，加法的对象不仅可以是数，还可以是各种函数（数也是一种特殊的函数，即f(x) = x），所以我们就可以对加法进行抽象：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;define &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;plus&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;a&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;lambda &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;+ &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;a&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;b&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))))&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;define &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;square&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;* &lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;define &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;cube&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;* &lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;((&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;plus&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;square&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;cube&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;;12&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;函数plus接受两个过程a和b，返回一个另一个过程，这个过程用匿名函数产生，它首先分别将变量x应用于过程a和b，随后将这两个过程的返回值进行相加运算。  &lt;/p&gt;
&lt;p&gt;在初步了解之后，我们可以做一些有趣的实验：  &lt;/p&gt;
&lt;p&gt;比如，我们知道“萌”是一个状态，也是一种行为，它可以表达为以下的含义——如果某某属性对于我而言是可以划分到“萌”的状态中的，那么我就是“moe”它的。这样一来，我们就可以定义为一个名为“moe”的过程，它接受一些属性，返回一个布尔值，如果返回值为真，则说明我是萌这个属性的，否则不萌：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;define &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;moe&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;xxx&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;yyy&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
        &lt;span class=&amp;quot;nv&amp;quot;&gt;true&lt;/span&gt;
        &lt;span class=&amp;quot;nv&amp;quot;&gt;false&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;在这里xxx和yyy均没有被定义，由于萌属性错综复杂，为了简化演示，我们将这个属性限定为“年龄”，如下：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;define &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;moe&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;age&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;&amp;lt; &lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;age&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;9&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;现在这个moe过程的含义是：如果对象的年龄小于9岁，那么我就是萌她的，否则不是。但这种做法局限性是很强的，如果我不仅仅想用9岁作为一个阈值，而是想自己去定义我萌的年龄段，该怎么办？在其他语言中，一般想到的做法或许是加一个参数作为标示，来确定自己究竟萌哪一个年龄段，然后在函数中加上若干的分支去判断以给出答案，比如以下的python版本：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;def&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;moe&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;type&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;age&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;):&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;type&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;==&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;loli&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;age&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;8&lt;/span&gt; &lt;span class=&amp;quot;ow&amp;quot;&gt;and&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;age&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;12&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;elif&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;type&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;==&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;girl&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;age&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;16&lt;/span&gt;
    &lt;span class=&amp;quot;o&amp;quot;&gt;......&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;但在Scheme中，我们可以使用高阶函数来完成同样的需求，我们可以定义一个参数为函数的函数moe：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;define &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;moe&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;tpye&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;lambda &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;type&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;接下来定义每个属性的年龄段：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;define &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;loli&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;age&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;and &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;&amp;gt; &lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;age&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;8&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;&amp;lt; &lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;age&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;12&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)))&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;define &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;girl&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;age&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;and &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;&amp;gt; &lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;age&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;11&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;&amp;lt; &lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;age&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;16&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;然后便可以使用moe函数来判定某个年龄是否符合你所萌的属性了：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;((&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;moe&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;loli&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;9&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;;#t&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;((&lt;/span&gt;&lt;span class=&amp;quot;nf&amp;quot;&gt;moe&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;girl&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;10&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;;#f&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;是不是有点人工智能的意思？这也就是Lisp语系的魅力所在——随时随地，无时不刻，只要你想，就可以建立你自己的DSL。  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Thursday, 10 Sep 2015 12:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.09.10 12:00:article/Skill-2015_09_10_a</guid>
<category>SICP</category>
<category>递归</category>
<category>迭代</category>
<category>高阶函数</category>
<category>Scheme</category>
</item>

<item>
<title>【FPGA/图像处理】板上验证</title>
<link>http://dtysky.moe/article/Skill-2015_05_31_a</link>
<description>&lt;p&gt;要证明模块的可靠性，需要对所有的设计进行板上验证，本次毕设课题与Xilinx的XUP(Xilinx University Program，Xilinx大学计划)合作，使用了XUP提供的Zybo开发板，Zybo是一个ZYNQ平台的开发板，片上搭载了一个双核的ARM，并且具有VGA等接口作为视屏输出，输入采用XUP提供的摄像头以及配套的驱动程序，测试硬件如图4-1所示。同时考虑到资源的压力，摄像头被配置为320x240的模式。本章将会说明如何设计一个测试框架，并对所有已实现的模块放入框架进行测试。&lt;br /&gt;
测试需要所有的模块，测试工程在这里： 
&lt;a href=&amp;quot;https://github.com/dtysky/FPGA-Imaging-Library/tree/Publish/TestOnBoard&amp;quot;&gt;TestOnBoard&lt;/a&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;4 板上验证&lt;/h1&gt;
&lt;p&gt;要证明模块的可靠性，需要对所有的设计进行板上验证，本次毕设课题与Xilinx的XUP(Xilinx University Program，Xilinx大学计划)合作，使用了XUP提供的Zybo开发板，Zybo是一个ZYNQ平台的开发板，片上搭载了一个双核的ARM，并且具有VGA等接口作为视屏输出，输入采用XUP提供的摄像头以及配套的驱动程序，测试硬件平台如图4-1所示。同时考虑到资源的压力，摄像头被配置为320x240的模式。本章将会说明如何设计一个测试框架，并对所有已实现的模块放入框架进行测试。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;img alt=&amp;quot;图4-1 测试硬件平台&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/4/1.jpg&amp;quot; /&gt;&lt;br /&gt;
图4-1 测试硬件平台
&lt;/center&gt;  &lt;/p&gt;
&lt;h2&gt;4.1 测试框架&lt;/h2&gt;
&lt;p&gt;测试框架可以分为输入、处理和输出三个部分，如图4-2所示。其中输入采用摄像头模块，在其后放置一个帧缓存来存储输入数据，但考虑到Zybo的片上RAM资源并不多，同时灰度图像已经足以满足测试，所以首先将摄像头输出的数据进行灰度化再存入帧缓存，这样可以节省一半的RAM资源。在帧缓存后使用读取模式帧控制器来读取数据，进入处理部分，在处理部分中，每一个处理模块对数据进行并行处理，并按照实际的需求对先后顺序做出一些调整，从而并行输出各自的处理结果。处理部分之后是输出部分，输出采用一个VGA控制器进行，由于VGA无法和处理模块同步，所以中间需要加一个帧缓存，帧缓存使用一个写入模式的帧控制器进行控制，由于需要接受多个处理模块的输入数据，所以必须有一个多路选通器来选择将那一个结果送给帧控制器，多路选通器接受外部控制，以此间接决定VGA的最终输出。&lt;br /&gt;
一部分处理模块需要外部进行配置参数的输入，比如TH核就需要输入阈值化模式和阈值，所以要考虑如何对其进行配置，此处利用ZYNQ的ARM平台和AXI协议，构建一个AXI总线的配置模块用于各个模块参数的配置，同时提供系统复位和多路选通器的输入，即用软件的方法配置硬件，可以大大提高灵活性和降低调试成本。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;img alt=&amp;quot;图4-2 测试框架&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/4/2.png&amp;quot; /&gt;&lt;br /&gt;
图4-2 测试框架
&lt;/center&gt;  &lt;/p&gt;
&lt;h2&gt;4.2 AXI总线模块的构建&lt;/h2&gt;
&lt;h3&gt;4.2.1 AXI总线模块的结构和原理&lt;/h3&gt;
&lt;p&gt;基于AXI总线的模块是一种特殊的模块，可以通过它利用AXI协议使得ARM平台(PS端)和可编程硬件平台(PL端)进行通信&lt;sup&gt;[28]&lt;/sup&gt;，Vivado中提供了一种便捷的方式来进行这种模块的创建&lt;sup&gt;[29]&lt;/sup&gt;，创建的时候有三种模式进行选择，分别是&amp;quot;AXI-Lite&amp;quot;，&amp;quot;AXI-Full&amp;quot;和&amp;quot;AXI-Stream&amp;quot;，三种模式各有各的用处，同时还可以选择有几个从模块，考虑到本次需要建立的模块实现的功能比较简单，所以采用了Lite模式，只有一个从模块。创建完成后可以看到在被创建的模块目录下有一些文件夹和文件，如图4-3所示，我们需要关心的只有这几个文件，其中XX为模块名：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;hdl/XXV1_0.v： 这是此模块的顶层HDL文件，用于管理所有从模块。  &lt;/li&gt;
&lt;li&gt;hdl/XXV1_0_S00_AXI.v： 从模块的HDL文件，用于功能的实现，如果有若各从模块，将有多个这样的文件。  &lt;/li&gt;
&lt;li&gt;drivers/XXV1_0/src/XX.c: ARM端驱动程序，用于定义上位机部分的功能。&lt;/li&gt;
&lt;li&gt;drivers/XXV1_0/src/XX.c: ARM端驱动程序的头文件。  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;对于Lite模式，Xilinx已经预先生成了模块的AXI协议部分模板，用户只需要在指定的地方编写自己需要的逻辑即可。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;img alt=&amp;quot;图4-3 AXI总线模块的结构&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/4/3.png&amp;quot; /&gt;&lt;br /&gt;
图4-3 AXI总线模块的结构
&lt;/center&gt;  &lt;/p&gt;
&lt;p&gt;AXI总线模块的工作原理如图4-4，PS端运行的是ARM环境下的C程序，它用于执行用户的软件指令，当需要和PL端通信时，它先通过Xilinx封装的IO函数，利用AXI总线将数据传输到PL端的寄存器中，随后PL端再通过寄存器中的数据来执行相应的操作，随后将需要输出的数据输出给其他PL端的模块。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;img alt=&amp;quot;图4-4 AXI总线模块的原理&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/4/4.png&amp;quot; /&gt;&lt;br /&gt;
图4-4 AXI总线模块的原理
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;4.2.2 配置模块的构建&lt;/h3&gt;
&lt;p&gt;配置模块有三类输出——配置参数信号、复位信号和选择信号，但本质上它们都是同样的信号，每一个信号都由PS端提供数据，并由一个单独的端口进行输出，端口的输出来自于PS端送入PL端中寄存器的数据。同时，配置模块还需要能够接受外界的硬件复位和PLL的锁定信号，这两个信号和AXI的复位信号、PS端的复位指令综合产生一个复位输出。配置模块的GUI如图4-5所示。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;img alt=&amp;quot;图4-5 配置模块的GUI&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/4/5.png&amp;quot; /&gt;&lt;br /&gt;
图4-5 配置模块的GUI
&lt;/center&gt;  &lt;/p&gt;
&lt;p&gt;配置模块的驱动部分需要考虑两点，即地址管理和函数设计。将配置模块命名为&amp;quot;BOARDINIT_AXI&amp;quot;，则模块创建成功时会提供一系列已经定义好的宏，如下：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;cp&amp;quot;&gt;#define BOARDINIT_AXI_S00_AXI_SLV_REG0_OFFSET 0&lt;/span&gt;
&lt;span class=&amp;quot;cp&amp;quot;&gt;#define BOARDINIT_AXI_S00_AXI_SLV_REG1_OFFSET 4&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这些宏定义了PL端寄存器的地址偏移量，为了便于管理，可以在上面再加一层宏定义：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;cp&amp;quot;&gt;#define BOARDINIT_RstN BOARDINIT_AXI_S00_AXI_SLV_REG0_OFFSET&lt;/span&gt;
&lt;span class=&amp;quot;cp&amp;quot;&gt;#define BOARDINIT_Sels BOARDINIT_AXI_S00_AXI_SLV_REG1_OFFSET&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;同时，在用到配置模块的工程生成完硬件数据流后，可以在SDK建立的项目工程中的BSP中找到&amp;quot;xparameters.h&amp;quot;文件，里面有此模块基地址的偏移量XPAR_BOARDINIT_AXI_0_S00_AXI_BASEADDR：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;cp&amp;quot;&gt;#define XPAR_BOARDINIT_AXI_0_DEVICE_ID 0&lt;/span&gt;
&lt;span class=&amp;quot;cp&amp;quot;&gt;#define XPAR_BOARDINIT_AXI_0_S00_AXI_BASEADDR 0x43C00000&lt;/span&gt;
&lt;span class=&amp;quot;cp&amp;quot;&gt;#define XPAR_BOARDINIT_AXI_0_S00_AXI_HIGHADDR 0x43C0FFFF&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;有了基地址偏移量和寄存器地址偏移量，便可以管理所有的输出地址了，通常将所有的地址放在一个结构体中备用：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;typedef&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;struct&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;u32&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;BaseAddress&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;u32&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;RstN&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;u32&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;Sels&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;BOARDINIT&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;函数设计要考虑到初始化、IO和数据转换。其中初始化用于将结构体中所有的地址初始化，数据转换则是将用户输入的数据转换为PL端可用的数据，比如对于一些模块，其参数要求指定位数的浮点数，但AXI协议传输的是无符号整数，所以不可以直接进行IO，必须进行转换。初始化部分可以使用下面的函数完成：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;void&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;BOARDINIT_Init&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;BOARDINIT&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;bdit&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;u32&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;BaseAddress&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;bdit&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;BaseAddress&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;BaseAddress&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;bdit&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;RstN&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;BaseAddress&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;BOARDINIT_RstN&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;bdit&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Sels&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;BaseAddress&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;BOARDINIT_Sels&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;IO部分，所有的IO都要依赖与Xilinx提供的IO函数：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;cp&amp;quot;&gt;#define BOARDINIT_Set(address, data) Xil_In32(address)&lt;/span&gt;
&lt;span class=&amp;quot;cp&amp;quot;&gt;#define BOARDINIT_Get(address) Xil_Out32(address, data)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;为了便于使用，在上面加上一层封装，这个函数的目的是保证输出的数据确实被写入了寄存器内。  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;void&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;BOARDINIT_SetWithCheck&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u32&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;Address&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;u32&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;data&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;while&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;BOARDINIT_Get&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Address&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;data&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;BOARDINIT_Set&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Address&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;data&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);}&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;一些参数需要被进行浮点数到指定位数的定点数的转换，以下函数完成了这个功能，其中rel_width为整数部分位数，dec_width为小数部分位数。  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;u32&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;Format2Fixed&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;num&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;rel_width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;dec_width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;u32&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;s&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;u32&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kt&amp;quot;&gt;float&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;dtmp&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;num&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;s&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rel_width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;dec_width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;num&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;dtmp&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;num&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;num&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;}&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;num&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;dtmp&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;num&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;}&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;dec_width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;u32&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;dec_width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;++&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;dtmp&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;dtmp&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dtmp&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;
            &lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dec_width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
            &lt;span class=&amp;quot;n&amp;quot;&gt;dtmp&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;--&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;}}&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;s&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;s&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;s&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);}&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;s&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;至此，配置模块的驱动部分完成。&lt;/p&gt;
&lt;h2&gt;4.3 测试-点操作&lt;/h2&gt;
&lt;p&gt;点操作的Board工程如图4-6，原始图像和灰度图像如图4-7，阈值化的结果如图4-8，对比度变换的结果如图4-9，亮度变换的结果如图4-10，色彩反转的结果如图4-11。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;img alt=&amp;quot;图4-6 点操作Board&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/4/6.png&amp;quot; /&gt;&lt;br /&gt;
图4-6 点操作Board
&lt;/center&gt;&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图4-7 原始图像和灰度图像&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/4/7.jpg&amp;quot; /&gt;&lt;br /&gt;
图4-7 原始图像和灰度图像，上侧为原始图像，下侧为灰度图像
&lt;/center&gt;&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图4-8 阈值化&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/4/8.jpg&amp;quot; /&gt;&lt;br /&gt;
图4-8 阈值化，上侧为一般全局阈值化，阈值为150，下侧为等高线阈值化，阈值为130和170
&lt;/center&gt;&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图4-9 对比度变换&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/4/9.jpg&amp;quot; /&gt;&lt;br /&gt;
图4-9 对比度变换，上侧系数为1.8，下侧系数为0.3
&lt;/center&gt;&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图4-10 亮度变换&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/4/10.jpg&amp;quot; /&gt;&lt;br /&gt;
图4-10 亮度变换，上侧系数为50，下侧系数为-50
&lt;/center&gt;&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图4-11 色彩反转&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/4/11.jpg&amp;quot; /&gt;&lt;br /&gt;
图4-11 色彩反转
&lt;/center&gt;  &lt;/p&gt;
&lt;h2&gt;4.4 测试-局部滤波器&lt;/h2&gt;
&lt;p&gt;局部滤波器的Board工程如图4-12，原始图像和灰度图像如图4-13，均值滤波器的结果如图4-14，排序滤波器的结果如图4-15，局部阈值化的结果如图4-16，腐蚀膨胀的结果如图4-17。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;img alt=&amp;quot;图4-12 局部滤波器Board&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/4/12.png&amp;quot; /&gt;&lt;br /&gt;
图4-12 局部滤波器Board
&lt;/center&gt;&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图4-13 原始图像和灰度图像&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/4/13.jpg&amp;quot; /&gt;&lt;br /&gt;
图4-13 原始图像和灰度图像，上侧为原始图像，下侧为灰度图像
&lt;/center&gt;&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图4-14 均值滤波器&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/4/14.jpg&amp;quot; /&gt;&lt;br /&gt;
图4-14 均值滤波器
&lt;/center&gt;&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图4-15 排序滤波器&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/4/15.jpg&amp;quot; /&gt;&lt;br /&gt;
图4-15 排序滤波器，上侧为中值滤波器，中间为最大值滤波器，下侧为最小值滤波器
&lt;/center&gt;&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图4-16 局部阈值化&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/4/16.bmp&amp;quot; /&gt;&lt;br /&gt;
图4-16 局部阈值化，上侧的前置滤波器为均值滤波器，下侧为中值滤波器
&lt;/center&gt;&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图4-17 腐蚀膨胀&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/4/17.bmp&amp;quot; /&gt;&lt;br /&gt;
图4-17 腐蚀膨胀，上侧为腐蚀，源为中值滤波后的局部阈值化图像，模板为000011010，下侧为膨胀，源为均值滤波后的局部阈值化图像，模板为000011011
&lt;/center&gt;  &lt;/p&gt;
&lt;h2&gt;4.5 测试-几何变换&lt;/h2&gt;
&lt;p&gt;几何变换的Board工程如图4-18，原始图像和灰度图像如图4-19，裁剪的结果如图4-20，平移的结果如图4-21，镜像的结果如图4-22，缩放的结果如图4-23，错切的结果如图4-24，旋转的结果如图4-25。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;img alt=&amp;quot;图4-18 几何变换Board&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/4/18.png&amp;quot; /&gt;&lt;br /&gt;
图4-18 几何变换Board
&lt;/center&gt;&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图4-19 原始图像和灰度图像&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/4/19.jpg&amp;quot; /&gt;&lt;br /&gt;
图4-19 原始图像和灰度图像，上侧为原始图像，下侧为灰度图像
&lt;/center&gt;&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图4-20 裁剪&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/4/20.jpg&amp;quot; /&gt;&lt;br /&gt;
图4-20 裁剪，上边界为40，下边界为240，左边界为0，右边界为200
&lt;/center&gt;&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图4-21 平移&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/4/21.jpg&amp;quot; /&gt;&lt;br /&gt;
图4-21 平移，上侧横向偏移100，纵向100，下侧横向偏移-100，纵向-100
&lt;/center&gt;&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图4-22 镜像&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/4/22.jpg&amp;quot; /&gt;&lt;br /&gt;
图4-22 镜像，上侧为横向，中间为纵向，下侧为全部
&lt;/center&gt;&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图4-23 缩放&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/4/23.jpg&amp;quot; /&gt;&lt;br /&gt;
图4-23 缩放，上侧横向比例为1.3，纵向为0.6，下侧横向为0.6，纵向为1.3
&lt;/center&gt;&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图4-24 错切&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/4/24.jpg&amp;quot; /&gt;&lt;br /&gt;
图4-24 错切，上侧横向比例为0.5，纵向为0.5，下侧横向为-1.671，纵向为0.539
&lt;/center&gt;&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图4-25 旋转&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/4/25.jpg&amp;quot; /&gt;&lt;br /&gt;
图4-25 旋转，上侧角度为90度，下侧角度为225度
&lt;/center&gt;  &lt;/p&gt;
&lt;h2&gt;4.6 结论&lt;/h2&gt;
&lt;p&gt;经过实际测试，所有的模块都可以正常工作，设计完全成功。 &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;参考文献&lt;/h1&gt;
&lt;p&gt;[28] Xilinx, AXI Reference Guide, UG761 v14.3[EB/OL], November 15, 2012&lt;br /&gt;
[29] Xilinx, Packaging Custom AXI IP for Vivado IP Integrator, XAPP1168 (v1.0)[EB/OL], June 01, 2013  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;感谢&lt;/h1&gt;
&lt;p&gt;硬件支持：&lt;br /&gt;
Xilinx上海  &lt;/p&gt;
&lt;p&gt;测试图像来源：&lt;br /&gt;
初心社-世界旅行 - 荷兰/阿姆斯特丹，英国/伦敦，希腊/爱琴海   &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Sunday, 31 May 2015 22:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.05.31 22:00:article/Skill-2015_05_31_a</guid>
<category>FPGA</category>
<category>FPGA-Imaging-Library</category>
<category>图像处理</category>
<category>板上验证</category>
<category>ZYNQ</category>
</item>

<item>
<title>【FPGA/图像处理】几何变换-旋转</title>
<link>http://dtysky.moe/article/Skill-2015_05_30_a</link>
<description>&lt;p&gt;旋转同样是仿射变换的一种特例，它接受一个角度，将图像绕着某个中心进行转动，适合逆向映射。旋转的实现有多种，比如两次错切的旋转&lt;sup&gt;[26]&lt;/sup&gt;、直接坐标变换&lt;sup&gt;[27]&lt;/sup&gt;等，中心点的选取方式也有许多，但对于FPGA采用图像的几何中心较为合适。。本节将会说明如何用FPGA实现旋转的模块。&lt;br /&gt;
这个IP核的资源在这里：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/FPGA-Imaging-Library/tree/Publish/Geometry/Rotate&amp;quot;&gt;Rotate&lt;/a&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;3 算法实现&lt;/h1&gt;
&lt;p&gt;明确了设计和架构，便可以进行算法的实现。本章将会说明如何实现图像处理的算法，以及如何运用它们。  &lt;/p&gt;
&lt;h2&gt;3.21 几何变换-旋转&lt;/h2&gt;
&lt;p&gt;旋转同样是仿射变换的一种特例，它接受一个角度，将图像绕着某个中心进行转动，适合逆向映射。旋转的实现有多种，比如两次错切的旋转&lt;sup&gt;[26]&lt;/sup&gt;、直接坐标变换&lt;sup&gt;[27]&lt;/sup&gt;等，中心点的选取方式也有许多，但对于FPGA采用图像的几何中心较为合适。。本节将会说明如何用FPGA实现旋转的模块。&lt;/p&gt;
&lt;h3&gt;3.21.1 原理&lt;/h3&gt;
&lt;p&gt;此设计中旋转的前向映射基本原理如式3-21-1，其中angle为旋转角度,$x_c$和$y_c$分别为图像中心横纵坐标。对于逆向映射应该对此式进行调整，变换为3-21-2的形式，可见旋转操作比较复杂，不仅涉及多次符号乘法、符号加法，还涉及到三角函数的计算。  &lt;/p&gt;
&lt;p&gt;$$\begin{cases}Q[x_t,y_t] = I[x,y]  \\x_t = xc + (x - xc) * cos(a) - (y - yc)*sin(a)\\ y_t = yc + (x - xc) * sin(a) + (y - yc)*cos(a)\\x \in[0, w) ,y \in[0, h)\ \end{cases}.\ \ \ \ \ \ \ \ (3-21-1)$$&lt;/p&gt;

&lt;p&gt;$$\begin{cases}I[x,y] = Q[x_t,y_t]  \\x = (x_t - xc) * cos(a) + (y_t - yc) * sin(a) + xc\\ y = (-x_t + xc) * sin(a) + (y_t - yc) * cos(a) + yc\\x_t \in[0, w) ,y_t \in[0, h)\ \end{cases}.\ \ \ \ \ \ \ \ (3-21-2)$$&lt;/p&gt;

&lt;p&gt;首先要考虑三角函数的计算，如3.1中所述，FPGA中的特殊函数计算需要用查找表来实现，所以需要编写一个脚本来生成查找表，同时由于三角函数的值可能为负数，并且verilog中符号系统是补码系统，所以需要生成的是角度和此角度下三角函数值的补码。由于正弦和余弦的值域为[-1, 1]，同时一般用于变换的角度为[0,359]，所以综合考虑，最终选择将角度划分为360个点，使用1位符号位、1位整数位和18位的小数位的数据来表示函数值，这其中的难点在于如何将一个浮点数转换为20位的定点数补码，实现函数如下。  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;  &lt;span class=&amp;quot;k&amp;quot;&gt;def&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;format&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;num&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;):&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;format&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;num&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;f&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;split&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;.&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;0&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;len&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;==&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;1&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;float&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;0.&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;&amp;#39;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;ow&amp;quot;&gt;in&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;xrange&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;18&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;):&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;1&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;0&amp;#39;&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;==&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;eval&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;==&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;
            &lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;0&amp;#39;&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;
            &lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;bin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;**&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;19&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;eval&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;0b&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:]&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;ow&amp;quot;&gt;in&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;xrange&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;19&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;len&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)):&lt;/span&gt;
            &lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;0&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;==&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;10000000000000000000&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;00000000000000000000&amp;#39;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;旋转变换同样需要符号舍入，但整数位只有1位，所以没有溢出风险，可以将其简化，仅仅裁剪输出即可。  &lt;/p&gt;
&lt;h3&gt;3.21.2 设计&lt;/h3&gt;
&lt;p&gt;根据原理可知，Rotate核(以下简称RTT核)需要四次符号乘法、四次符号舍入操作和四次符号加法，并且需要和基于行列计数的帧控制器进行合作。故其需要的配置参数与端口如表3-21-1和表3-21-2。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;td&gt;work_mode&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;0为流水线模式，1为请求响应模式&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;模块的工作模式。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;tr&gt;
&lt;td&gt;data_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;1 - 12&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;数据位宽。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;im_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;1 - 4096&lt;/td&gt;
&lt;td&gt;320&lt;/td&gt;
&lt;td&gt;图像宽度。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;im_height&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;1 - 4096&lt;/td&gt;
&lt;td&gt;240&lt;/td&gt;
&lt;td&gt;图像高度。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;im_width_bits&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;取决于图像宽度&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;图像宽度的位宽。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mul_delay&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;取决于乘法器配置，1-14&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;乘法器延迟。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ram_RL&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;取决于帧控制器&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;帧控制器输出延迟。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-21-1 配置参数
&lt;/center&gt;&lt;br /&gt;
&lt;br&gt;
&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;端口&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;td&gt;clk&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;Clock.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rst_n&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;复位，低有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;angle&lt;/td&gt;
&lt;td&gt;input&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;0 - 359&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;旋转角度。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_enable&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据使能，在流水线模式下，它是另一个复位信号，在请求响应模式下，只有在它有效的时候in_data才会被真正地改变。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;frame_in_ready&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;连接到帧控制器的out_ready。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;frame_in_data&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;data_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;连接到帧控制器的out_data。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;frame_enable&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;连接到帧控制器的in_enable。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;frame_out_count_x&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;im_width_bits - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;连接到帧控制器的in_count_x。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_count_y&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;im_width_bits - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;连接到帧控制器的in_count_y。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_ready&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据有效，在两种模式下，这个信号都会在out_data可以被读取的时候有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_data&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据，将会和out_ready同步输出。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-21-2 端口
&lt;/center&gt;&lt;br /&gt;
&lt;/center&gt;&lt;br /&gt;
&lt;br&gt;
&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&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;tr&gt;
&lt;td&gt;Sin&lt;/td&gt;
&lt;td&gt;SinLUT&lt;/td&gt;
&lt;td&gt;获取角度的正弦。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cos&lt;/td&gt;
&lt;td&gt;CosLUT&lt;/td&gt;
&lt;td&gt;获取角度的余弦。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MulX1&lt;/td&gt;
&lt;td&gt;Multiplier13Sx20SRTT&lt;/td&gt;
&lt;td&gt;13位有符号数和20位有符号数的乘法器，被用于定点数的乘法。你可以自己配置这个乘法器，然后更改&amp;quot;mul_delay&amp;quot;，但所有的乘法器必须拥有相同的流水线级数，并且不能更改端口的配置！&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MulX2&lt;/td&gt;
&lt;td&gt;Multiplier13Sx20SRTT&lt;/td&gt;
&lt;td&gt;13位有符号数和20位有符号数的乘法器，被用于定点数的乘法。你可以自己配置这个乘法器，然后更改&amp;quot;mul_delay&amp;quot;，但所有的乘法器必须拥有相同的流水线级数，并且不能更改端口的配置！&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MulY1&lt;/td&gt;
&lt;td&gt;Multiplier13Sx20SRTT&lt;/td&gt;
&lt;td&gt;13位有符号数和20位有符号数的乘法器，被用于定点数的乘法。你可以自己配置这个乘法器，然后更改&amp;quot;mul_delay&amp;quot;，但所有的乘法器必须拥有相同的流水线级数，并且不能更改端口的配置！&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MulY2&lt;/td&gt;
&lt;td&gt;Multiplier13Sx20SRTT&lt;/td&gt;
&lt;td&gt;13位有符号数和20位有符号数的乘法器，被用于定点数的乘法。你可以自己配置这个乘法器，然后更改&amp;quot;mul_delay&amp;quot;，但所有的乘法器必须拥有相同的流水线级数，并且不能更改端口的配置！&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FRSX1&lt;/td&gt;
&lt;td&gt;FixedRoundSigned&lt;/td&gt;
&lt;td&gt;用于有符号浮点数的舍入。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FRSX2&lt;/td&gt;
&lt;td&gt;FixedRoundSigned&lt;/td&gt;
&lt;td&gt;用于有符号浮点数的舍入。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FRSY1&lt;/td&gt;
&lt;td&gt;FixedRoundSigned&lt;/td&gt;
&lt;td&gt;用于有符号浮点数的舍入。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FRSY2&lt;/td&gt;
&lt;td&gt;FixedRoundSigned&lt;/td&gt;
&lt;td&gt;用于有符号浮点数的舍入。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-3-3 子模块
&lt;/center&gt;&lt;/p&gt;
&lt;h3&gt;3.21.3 实现&lt;/h3&gt;
&lt;p&gt;根据3.21.2的设计便可以实现一个RTT核，流水线模式和请求响应模式实现如下。  &lt;/p&gt;
&lt;h4&gt;3.21.3.1 流水线模式&lt;/h4&gt;
&lt;p&gt;在帧控制器的输出使能后1个周期第一个结果被输出，开始流水化工作，波形如图3-21-1。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-21-1 流水线模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/21/1.png&amp;quot; /&gt;&lt;br /&gt;
图3-21-1 流水线模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.21.3.2 请求响应模式读取&lt;/h4&gt;
&lt;p&gt;基本同3.21.3.1，但只有在in_enable上升沿时计数器才会加1才会被改变，波形如图3-21-2。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-21-1 流水线模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/21/2.png&amp;quot; /&gt;&lt;br /&gt;
图3-21-1 流水线模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.21.3.3 IP核GUI&lt;/h4&gt;
&lt;p&gt;完成功能后对RTT核进行了封装，封装如图3-21-3。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-21-3 RTT核的GUI&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/21/3.png&amp;quot; /&gt;&lt;br /&gt;
图3-21-3 RTT核的GUI
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.21.4 仿真&lt;/h3&gt;
&lt;p&gt;只对RGB图像和灰度图像进行测试，考虑到仿真设计模块比较多，出于仿真压力，我选择了一张图像的灰度模式进行三套参数的测试，原始图像如图3-21-4。&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;img alt=&amp;quot;图3-21-4 仿真原始图像&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/21/4.jpg&amp;quot; /&gt;&lt;br /&gt;
图3-21-4 仿真原始图像
&lt;/center&gt;  &lt;/p&gt;
&lt;p&gt;仿真参数如表3-16-4所示，选择原则是可以被二进制表示和不可以被表示的值都包含。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;angle&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;45&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;131&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;270&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-16-4 仿真参数
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;仿真并进行PSNR测试，仿真结果如图3-21-5所示。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-21-5 仿真结果&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/21/5.png&amp;quot; /&gt;&lt;br /&gt;
图3-21-5 仿真结果，左侧为流水线模式下的HDL功能仿真结果，中间为请求响应模式下的HDL功能仿真结果，右侧为软件仿真结果
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.21.5 资源和时序&lt;/h3&gt;
&lt;p&gt;最终实现与图像大小和数据位宽有关，这里只分析大小为512x512和数据位宽为8时的状况，根据Vivado生成的报表，主要资源耗费如表3-21-5。&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;Slice LUTs*&lt;/th&gt;
&lt;th&gt;Slice Registers&lt;/th&gt;
&lt;th&gt;DSP&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;543&lt;/td&gt;
&lt;td&gt;245&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-21-5 主要资源耗费
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;同时根据时序报告，最大的Data Path Delay(数据路径延迟)为4.414ns，即：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;FMax = 226.55MHz  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;即说明，Rotate核在流水线模式下，理论上在处理1080p全高清图像时可以达到109帧。&lt;br /&gt;
与3.20相同，此FMax低于期望值，也来自于舍入核的加法，考虑时间此处暂时不做优化。&lt;br /&gt;
由于数据路径延迟和应用的最终约束设置强相关，所以仅供参考。&lt;/p&gt;
&lt;h3&gt;3.21.6 分析与结论&lt;/h3&gt;
&lt;p&gt;PSNR如表3-21-6。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;1-131&lt;/th&gt;
&lt;th&gt;1-270&lt;/th&gt;
&lt;th&gt;1-45&lt;/th&gt;
&lt;th&gt;Total&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;54.89&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;666684.96&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-21-6 PSNR
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;PSNR对于某些参数为最大值，对于一些不是，最终误差来自于符号乘法和查找表自身的误差，但PSNR均为50以上，可见在测试范围内，RTT核可以满足处理需求，设计成功。&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;参考文献&lt;/h1&gt;
&lt;p&gt;[26] 陈芳.一种基于错切原理的图像旋转方法[J].淮阴师范学院学报（自然科学版）,2004,3(4):319-322.DOI:10.3969/j.issn.1671-6876.2004.04.016.&lt;br /&gt;
[27] 王金辉.实时图像旋转系统的研究与FPGA实现[D].华中科技大学,2012.  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;感谢&lt;/h1&gt;
&lt;p&gt;仿真图像来源：&lt;br /&gt;
&lt;a href=&amp;quot;http://www.pixiv.net/member_illust.php?mode=medium&amp;amp;illust_id=49603329&amp;quot;&gt;041-マツムシソウ&lt;/a&gt;  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Saturday, 30 May 2015 00:30:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.05.30 00:30:article/Skill-2015_05_30_a</guid>
<category>FPGA</category>
<category>FPGA-Imaging-Library</category>
<category>图像处理</category>
<category>几何变换</category>
<category>旋转</category>
</item>

<item>
<title>【FPGA/图像处理】几何变换-错切</title>
<link>http://dtysky.moe/article/Skill-2015_05_29_b</link>
<description>&lt;p&gt;错切也是仿射变换的一种特例，它接受水平和垂直两个方向的错切系数，将图像进行扭变，它同样适合进行逆向映射。错切常用于图像校正操作，同时也可以作为旋转变换的中间变换&lt;sup&gt;[26]&lt;/sup&gt;。本节将会说明如何用FPGA实现错切的模块。&lt;br /&gt;
这个IP核的资源在这里：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/FPGA-Imaging-Library/tree/Publish/Geometry/Shear&amp;quot;&gt;Shear&lt;/a&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;3 算法实现&lt;/h1&gt;
&lt;p&gt;明确了设计和架构，便可以进行算法的实现。本章将会说明如何实现图像处理的算法，以及如何运用它们。  &lt;/p&gt;
&lt;h2&gt;3.20 几何变换-错切&lt;/h2&gt;
&lt;p&gt;错切也是仿射变换的一种特例，它接受水平和垂直两个方向的错切系数，将图像进行扭变，它同样适合进行逆向映射。错切常用于图像校正操作，同时也可以作为旋转变换的中间变换&lt;sup&gt;[26]&lt;/sup&gt;。本节将会说明如何用FPGA实现错切的模块。&lt;/p&gt;
&lt;h3&gt;3.20.1 原理&lt;/h3&gt;
&lt;p&gt;错切变换的前向映射基本原理如式3-20-1，其中xsh和ysh为两个方向的错切系数。对于逆向映射应该对此式进行调整，变换为3-20-2的形式，$xsh_t$和$ysh_t$为逆向映射系数，没有使用前向映射的逆变换是考虑到错切本身意义模糊，如果采用逆变换会导致算法复杂度大大增加却无法得到理想的效果，所以直接根据前向映射的形式得出了逆向映射。  &lt;/p&gt;
&lt;p&gt;$$\begin{cases}Q[x_t,y_t] = I[x,y]  \\x_t = x * xsh + y\\ y_t = y * ysh + x\\x_t \in[0, w) ,y_t \in[0, h)\ \end{cases}.\ \ \ \ \ \ \ \ (3-20-1)$$&lt;/p&gt;

&lt;p&gt;$$\begin{cases}I[x,y] = Q[x_t,y_t]  \\x = x_t * xsh_t + y_t\\ y = y_t * ysh_t + x_t\\x_t \in[0, w) ,y_t \in[0, h)\ \end{cases}.\ \ \ \ \ \ \ \ (3-20-2)$$&lt;/p&gt;

&lt;p&gt;输出同样要进行边界判定，这和3.19中基本一致。同时由于涉及到定点数乘法，所以需要确定小数位，考虑到错切系数是有符号的定点数，所以此模块需要乘法器来实现一个无符号数(坐标)和与有符号定点数(错切系数)的乘法，最终确定了在3.19中设计的基础上加上一位符号位来保证精度，即错切系数的范围为(-64,64)。此模块同样涉及到舍入问题，并且是有符号数的舍入问题，故可以采用3.1中论述的FR核来完成舍入，舍入的原理如式3-20-3，$I_s$为符号位，首先判断输入的正负，随后根据正负来确定如何舍入。  &lt;/p&gt;
&lt;p&gt;$$Q = \begin{cases}I_r &amp;&amp; I_{d1} = 0  \\I_r + 1 &amp;&amp; I_{d1} = 1,\  I_s=0\\I_r - 1 &amp;&amp; I_{d1} = 1,\ I_s=1\ \end{cases}.\ \ \ \ \ \ \ \ (3-20-3)$$&lt;/p&gt;

&lt;p&gt;同时考虑到舍入核输出的数值将会进行一次加法，所以输出的位数越少越好，故此处设计了一个可选的输出位宽和一个溢出标志，同时在模块内部进行一次比较，来告知外部此次输入的值是否出现了溢出，溢出规则如式3-20-4，Orig为转换后的原码，Of为溢出标志，fp为定点位，resw为指定的输出位宽，numw为原始数据位宽，即在需求位数之外、被截取的高位数值不为0时则判定为溢出，最终只要把这个溢出标志加入到边界判定中即可。&lt;/p&gt;
&lt;p&gt;$$Of = \begin{cases}0 &amp;&amp; Orig[numw - 1 : resw] = 0  \\1 &amp;&amp; Others\ \end{cases}.\ \ \ \ \ \ \ \ (3-20-4)$$&lt;/p&gt;

&lt;h3&gt;3.20.2 设计&lt;/h3&gt;
&lt;p&gt;根据原理可知，Shear核(以下简称SHR核)需要两次符号乘法、两次符号舍入操作和两次符号加法，并且需要和基于行列计数的帧控制器进行合作。故其需要的配置参数与端口如表3-20-1和表3-20-2。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;td&gt;work_mode&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;0为流水线模式，1为请求响应模式&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;模块的工作模式。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;tr&gt;
&lt;td&gt;data_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;1 - 12&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;数据位宽。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;im_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;1 - 4096&lt;/td&gt;
&lt;td&gt;320&lt;/td&gt;
&lt;td&gt;图像宽度。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;im_height&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;1 - 4096&lt;/td&gt;
&lt;td&gt;240&lt;/td&gt;
&lt;td&gt;图像高度。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;im_width_bits&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;取决于图像宽度&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;图像宽度的位宽。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mul_delay&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;取决于乘法器配置，1-14&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;乘法器延迟。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ram_RL&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;取决于帧控制器&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;帧控制器输出延迟。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-20-1 配置参数
&lt;/center&gt;&lt;br /&gt;
&lt;br&gt;
&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;端口&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;td&gt;clk&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;Clock.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rst_n&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;复位，低有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sh_u&lt;/td&gt;
&lt;td&gt;input&lt;/td&gt;
&lt;td&gt;有符号&lt;/td&gt;
&lt;td&gt;定点数，1flag+6bits.18bits&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;横向错切系数。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sh_v&lt;/td&gt;
&lt;td&gt;input&lt;/td&gt;
&lt;td&gt;有符号&lt;/td&gt;
&lt;td&gt;定点数，1flag+6bits.18bits&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;纵向错切系数。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_enable&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据使能，在流水线模式下，它是另一个复位信号，在请求响应模式下，只有在它有效的时候in_data才会被真正地改变。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;frame_in_ready&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;连接到帧控制器的out_ready。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;frame_in_data&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;data_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;连接到帧控制器的out_data。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;frame_enable&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;连接到帧控制器的in_enable。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;frame_out_count_x&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;im_width_bits - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;连接到帧控制器的in_count_x。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_count_y&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;im_width_bits - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;连接到帧控制器的in_count_y。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_ready&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据有效，在两种模式下，这个信号都会在out_data可以被读取的时候有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_data&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据，将会和out_ready同步输出。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-20-2 端口
&lt;/center&gt;&lt;br /&gt;
&lt;/center&gt;&lt;br /&gt;
&lt;br&gt;
&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&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;tr&gt;
&lt;td&gt;MulU&lt;/td&gt;
&lt;td&gt;Multiplier12x25SSHR&lt;/td&gt;
&lt;td&gt;12位无符号数和25位有符号数的乘法器，被用于定点数的乘法。你可以自己配置这个乘法器，然后更改&amp;quot;mul_delay&amp;quot;，但所有的乘法器必须拥有相同的流水线级数，并且不能更改端口的配置！&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MulV&lt;/td&gt;
&lt;td&gt;Multiplier12x25SSHR&lt;/td&gt;
&lt;td&gt;12位无符号数和25位有符号数的乘法器，被用于定点数的乘法。你可以自己配置这个乘法器，然后更改&amp;quot;mul_delay&amp;quot;，但所有的乘法器必须拥有相同的流水线级数，并且不能更改端口的配置！&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FRSU&lt;/td&gt;
&lt;td&gt;FixedRoundSigned&lt;/td&gt;
&lt;td&gt;用于有符号浮点数的舍入。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FRSV&lt;/td&gt;
&lt;td&gt;FixedRoundSigned&lt;/td&gt;
&lt;td&gt;用于有符号浮点数的舍入。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-3-3 子模块
&lt;/center&gt;&lt;/p&gt;
&lt;h3&gt;3.20.3 实现&lt;/h3&gt;
&lt;p&gt;根据3.20.2的设计便可以实现一个SHR核，流水线模式和请求响应模式实现如下。  &lt;/p&gt;
&lt;h4&gt;3.20.3.1 流水线模式&lt;/h4&gt;
&lt;p&gt;在帧控制器的输出使能后1个周期第一个结果被输出，开始流水化工作，波形如图3-20-1。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-20-1 流水线模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/20/1.png&amp;quot; /&gt;&lt;br /&gt;
图3-20-1 流水线模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.20.3.2 请求响应模式读取&lt;/h4&gt;
&lt;p&gt;基本同3.20.3.1，但只有在in_enable上升沿时计数器才会加1才会被改变，波形如图3-20-2。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-20-1 流水线模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/20/2.png&amp;quot; /&gt;&lt;br /&gt;
图3-20-1 流水线模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.20.3.3 IP核GUI&lt;/h4&gt;
&lt;p&gt;完成功能后对SHR核进行了封装，封装如图3-20-3。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-20-3 SHR核的GUI&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/20/3.png&amp;quot; /&gt;&lt;br /&gt;
图3-20-3 SHR核的GUI
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.20.4 仿真&lt;/h3&gt;
&lt;p&gt;只对RGB图像和灰度图像进行测试，考虑到仿真设计模块比较多，出于仿真压力，我选择了一张图像的灰度模式进行三套参数的测试，原始图像如图3-20-4。&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;img alt=&amp;quot;图3-20-4 仿真原始图像&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/20/4.jpg&amp;quot; /&gt;&lt;br /&gt;
图3-20-4 仿真原始图像
&lt;/center&gt;  &lt;/p&gt;
&lt;p&gt;仿真参数如表3-16-4所示，选择原则是可以被二进制表示和不可以被表示的值都包含。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;ush&lt;/th&gt;
&lt;th&gt;vsh&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0.5&lt;/td&gt;
&lt;td&gt;0.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-1.671&lt;/td&gt;
&lt;td&gt;0.539&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0.824&lt;/td&gt;
&lt;td&gt;-1.793&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-16-4 仿真参数
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;仿真并进行PSNR测试，仿真结果如图3-20-5所示。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-20-5 仿真结果&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/20/5.png&amp;quot; /&gt;&lt;br /&gt;
图3-20-5 仿真结果，左侧为流水线模式下的HDL功能仿真结果，中间为请求响应模式下的HDL功能仿真结果，右侧为软件仿真结果
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.20.5 资源和时序&lt;/h3&gt;
&lt;p&gt;最终实现与图像大小和数据位宽有关，这里只分析大小为512x512和数据位宽为8时的状况，根据Vivado生成的报表，主要资源耗费如表3-20-5。&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;Slice LUTs*&lt;/th&gt;
&lt;th&gt;Slice Registers&lt;/th&gt;
&lt;th&gt;DSP&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;212&lt;/td&gt;
&lt;td&gt;166&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-20-5 主要资源耗费
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;同时根据时序报告，最大的Data Path Delay(数据路径延迟)为4.103ns，即：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;FMax = 243.72MHz  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;即说明，Shear核在流水线模式下，理论上在处理1080p全高清图像时可以达到117帧。&lt;br /&gt;
此FMax低于期望值，分析得知延迟主要来源于舍入核，有符号的舍入操作会涉及原码和补码转换，在此应用中每个周期会有一次35位的加法，如果将加法拆分会得到更好的FMax，但考虑时间此处暂时不做优化。&lt;br /&gt;
由于数据路径延迟和应用的最终约束设置强相关，所以仅供参考。&lt;/p&gt;
&lt;h3&gt;3.20.6 分析与结论&lt;/h3&gt;
&lt;p&gt;PSNR如表3-20-6。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;1--1.671x0.539&lt;/th&gt;
&lt;th&gt;1-0.5x0.5&lt;/th&gt;
&lt;th&gt;1-0.824x-1.793&lt;/th&gt;
&lt;th&gt;Total&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;52.36&lt;/td&gt;
&lt;td&gt;52.38&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;333368.25&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-20-6 PSNR
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;PSNR对于某些参数为最大值，对于一些不是，但均为50以上，可见在测试范围内，SHR核可以满足处理需求，设计成功。&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;参考文献&lt;/h1&gt;
&lt;p&gt;[26] 陈芳.一种基于错切原理的图像旋转方法[J].淮阴师范学院学报（自然科学版）,2004,3(4):319-322.DOI:10.3969/j.issn.1671-6876.2004.04.016.  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;感谢&lt;/h1&gt;
&lt;p&gt;仿真图像来源：&lt;br /&gt;
&lt;a href=&amp;quot;http://www.pixiv.net/member_illust.php?mode=medium&amp;amp;illust_id=18098777&amp;quot;&gt;ぜろきち-白の夢&lt;/a&gt;  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Friday, 29 May 2015 23:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.05.29 23:00:article/Skill-2015_05_29_b</guid>
<category>FPGA</category>
<category>FPGA-Imaging-Library</category>
<category>图像处理</category>
<category>几何变换</category>
<category>错切</category>
</item>

<item>
<title>【FPGA/图像处理】几何变换-缩放</title>
<link>http://dtysky.moe/article/Skill-2015_05_29_a</link>
<description>&lt;p&gt;缩放同样是仿射变换的一种特例，它接受水平和垂直两个方向的缩放值，将图像进行伸缩，缩放用前向映射实现会出现一个问题，就是在缩放值大于1的时候可能会出现空隙&lt;sup&gt;[3]&lt;/sup&gt;。这是由于在前向映射下，并非原图像的每一个像素都可以映射为目标图像的像素，在软件上这可以通过一些插值方法来实现，但对于FPGA而言同样的实现需要用更为复杂的逻辑，故此处暂且采用逆向映射，这实际上采用了最近邻插值算法。本节将会说明如何用FPGA实现缩放的模块。&lt;br /&gt;
这个IP核的资源在这里：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/FPGA-Imaging-Library/tree/Publish/Geometry/Scale&amp;quot;&gt;Scale&lt;/a&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;3 算法实现&lt;/h1&gt;
&lt;p&gt;明确了设计和架构，便可以进行算法的实现。本章将会说明如何实现图像处理的算法，以及如何运用它们。  &lt;/p&gt;
&lt;h2&gt;3.19 几何变换-缩放&lt;/h2&gt;
&lt;p&gt;缩放同样是仿射变换的一种特例，它接受水平和垂直两个方向的缩放值，将图像进行伸缩，缩放用前向映射实现会出现一个问题，就是在缩放值大于1的时候可能会出现空隙&lt;sup&gt;[3]&lt;/sup&gt;。这是由于在前向映射下，并非原图像的每一个像素都可以映射为目标图像的像素，在软件上这可以通过一些插值方法来实现，但对于FPGA而言同样的实现需要用更为复杂的逻辑，故此处暂且采用逆向映射，这实际上采用了最近邻插值算法。本节将会说明如何用FPGA实现缩放的模块。&lt;/p&gt;
&lt;h3&gt;3.19.1 原理&lt;/h3&gt;
&lt;p&gt;缩放变换的基本原理如式3-19-1，Q为输出，I为输入，x和y为输入像素坐标，$x_t$和$y_t$为输出像素坐标，xscale和yscale分别为两个方向的缩放比例，w和h为图像的宽和高，这是前向映射的一般定义，但对于逆向映射而言，应该对此式进行调整，变换为3-19-2的形式，$x_t$和$y_t$称为自己变换的输出坐标，x和y则通过$x_t$和$y_t$除以各自缩放比例得出，随后根据x和y得到需要输出的原图像像素，进行输出。  &lt;/p&gt;
&lt;p&gt;$$\begin{cases}Q[x_t,y_t] = I[x,y]  \\x_t = x * xscale &amp;&amp; x \in[0, w)\\ y_t = y * yscale &amp;&amp; y \in[0, h)\ \end{cases}.\ \ \ \ \ \ \ \ (3-19-1)$$&lt;/p&gt;

&lt;p&gt;$$\begin{cases}I[x,y] = Q[x_t,y_t]  \\x = x_t / xscale &amp;&amp; x_t \in[0, w)\\ y = y_t / yscale &amp;&amp; y_t \in[0, h)\ \end{cases}.\ \ \ \ \ \ \ \ (3-19-2)$$&lt;/p&gt;

&lt;p&gt;可见，这实际上是先得出需要输出的像素坐标，然后反向映射到原图像的像素坐标，最后查找到原图像的像素值进行输出。所以前面章节的方式不再适用，必须采用新的设计。考虑到输出坐标的生成依赖于外界没有意义，所以选用内部的计数器进行目标图像坐标的生成。最终采用的方案为，首先将原图像存入帧缓存，根据计数器生成的坐标进行两次并行乘法得到原图像对应的坐标，并联合3.15中的帧控制器，查找得到需要输出的像素值，同时进行边界判定，当求得的原图像的坐标超出了图像的范围，则输出为背景像素，算法如式3-19-3。&lt;/p&gt;
&lt;p&gt;$$Q[x_t,y_t] = \begin{cases}I[x,y] &amp;&amp; (x,y)\in I  \\0  &amp;&amp; (x,y) \notin I\ \end{cases}.\ \ \ \ \ \ \ \ (3-19-3)$$&lt;/p&gt;

&lt;p&gt;由于本库采用的是定点数乘法，所以需要一个确定的小数位来保证精度，经过实验和DSP资源的考虑(一个DSP48最多实现12x25的乘法)，最终采用拥有6位整数和18位小数的定点数，即缩放比例的范围为[0, 64)。同时由于涉及小数乘法，所以需要选择一个舍入方式，几何变换对于舍入方式是敏感的，为了符合最邻近插值的定义，几何变换中所有模块的舍入方式均为就近舍入，故需要一个舍入核来完成舍入操作，考虑到缩放变换中的乘法为无符号乘法，故舍入核设计比较简单，例如对于一个12bits.12bits的定点数，舍入原理如式3-19-4，其中Q为输出，$I_r$为输入的整数部分，$I_{d1}$为输入的小数部分第一位，第一位为1，则I的小数部分必然大于0.5，否则小于。  &lt;/p&gt;
&lt;p&gt;$$Q = \begin{cases}I_r &amp;&amp; I_{d1} = 0  \\I_r + 1 &amp;&amp; I_{d1} = 1\ \end{cases}.\ \ \ \ \ \ \ \ (3-19-4)$$&lt;/p&gt;

&lt;h3&gt;3.19.2 设计&lt;/h3&gt;
&lt;p&gt;根据原理可知，Scale核(以下简称SCL核)需要两次乘法，两次舍入操作，并且需要和基于行列计数的帧控制器进行合作。故其需要的配置参数与端口如表3-19-1和表3-19-2。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;td&gt;work_mode&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;0为流水线模式，1为请求响应模式&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;模块的工作模式。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;tr&gt;
&lt;td&gt;data_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;1 - 12&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;数据位宽。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;im_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;1 - 4096&lt;/td&gt;
&lt;td&gt;320&lt;/td&gt;
&lt;td&gt;图像宽度。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;im_height&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;1 - 4096&lt;/td&gt;
&lt;td&gt;240&lt;/td&gt;
&lt;td&gt;图像高度。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;im_width_bits&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;取决于图像宽度&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;图像宽度的位宽。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mul_delay&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;取决于乘法器配置，1-14&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;乘法器延迟。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ram_RL&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;取决于帧控制器&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;帧控制器输出延迟。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-19-1 配置参数
&lt;/center&gt;&lt;br /&gt;
&lt;br&gt;
&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;端口&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;td&gt;clk&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;Clock.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rst_n&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;复位，低有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;scale_x&lt;/td&gt;
&lt;td&gt;input&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;定点数，6bits.18bits&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;横向缩放比例，必须是正确缩放比例的倒数。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;scale_y&lt;/td&gt;
&lt;td&gt;input&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;定点数，6bits.18bits&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;纵向缩放比例，必须是正确缩放比例的倒数。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_enable&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据使能，在流水线模式下，它是另一个复位信号，在请求响应模式下，只有在它有效的时候in_data才会被真正地改变。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;frame_in_ready&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;连接到帧控制器的out_ready。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;frame_in_data&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;data_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;连接到帧控制器的out_data。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;frame_enable&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;连接到帧控制器的in_enable。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;frame_out_count_x&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;im_width_bits - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;连接到帧控制器的in_count_x。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_count_y&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;im_width_bits - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;连接到帧控制器的in_count_y。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_ready&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据有效，在两种模式下，这个信号都会在out_data可以被读取的时候有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_data&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据，将会和out_ready同步输出。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-19-2 端口
&lt;/center&gt;&lt;br /&gt;
&lt;/center&gt;&lt;br /&gt;
&lt;br&gt;
&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&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;tr&gt;
&lt;td&gt;MulX&lt;/td&gt;
&lt;td&gt;Multiplier12x24SCL&lt;/td&gt;
&lt;td&gt;12位无符号数和24位无符号数的乘法器，被用于定点数的乘法。你可以自己配置这个乘法器，然后更改&amp;quot;mul_delay&amp;quot;，但所有的乘法器必须拥有相同的流水线级数，并且不能更改端口的配置！&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MulY&lt;/td&gt;
&lt;td&gt;Multiplier12x24SCL&lt;/td&gt;
&lt;td&gt;12位无符号数和24位无符号数的乘法器，被用于定点数的乘法。你可以自己配置这个乘法器，然后更改&amp;quot;mul_delay&amp;quot;，但所有的乘法器必须拥有相同的流水线级数，并且不能更改端口的配置！&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FRUX&lt;/td&gt;
&lt;td&gt;FixedRoundUnsigned&lt;/td&gt;
&lt;td&gt;用于无符号浮点数的舍入。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FRUY&lt;/td&gt;
&lt;td&gt;FixedRoundUnsigned&lt;/td&gt;
&lt;td&gt;用于无符号浮点数的舍入。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-3-3 子模块
&lt;/center&gt;&lt;/p&gt;
&lt;h3&gt;3.19.3 实现&lt;/h3&gt;
&lt;p&gt;根据3.19.2的设计便可以实现一个SCL核，流水线模式和请求响应模式实现如下。  &lt;/p&gt;
&lt;h4&gt;3.19.3.1 流水线模式&lt;/h4&gt;
&lt;p&gt;输入使能后首先计数器工作，随后计算原图像的坐标，随后根据算出的坐标得到边界，同时将坐标输出到帧控制器，在帧控制器的输出使能后1个周期第一个结果被输出，开始流水化工作，波形如图3-19-1。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-19-1 流水线模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/19/1.png&amp;quot; /&gt;&lt;br /&gt;
图3-19-1 流水线模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.19.3.2 请求响应模式读取&lt;/h4&gt;
&lt;p&gt;基本同3.19.3.1，但只有在in_enable上升沿时计数器才会加1才会被改变，波形如图3-19-2。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-19-1 流水线模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/19/2.png&amp;quot; /&gt;&lt;br /&gt;
图3-19-1 流水线模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.19.3.3 IP核GUI&lt;/h4&gt;
&lt;p&gt;完成功能后对SCL核进行了封装，封装如图3-19-3。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-19-3 SCL核的GUI&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/19/3.png&amp;quot; /&gt;&lt;br /&gt;
图3-19-3 SCL核的GUI
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.19.4 仿真&lt;/h3&gt;
&lt;p&gt;只对RGB图像和灰度图像进行测试，考虑到仿真设计模块比较多，出于仿真压力，我选择了一张图像的灰度模式进行三套参数的测试，原始图像如图3-19-4。&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;img alt=&amp;quot;图3-19-4 仿真原始图像&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/19/4.jpg&amp;quot; /&gt;&lt;br /&gt;
图3-19-4 仿真原始图像
&lt;/center&gt;  &lt;/p&gt;
&lt;p&gt;仿真参数如表3-16-4所示，选择原则是可以被二进制表示和不可以被表示的值都包含。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;xscale&lt;/th&gt;
&lt;th&gt;yscale&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1.97&lt;/td&gt;
&lt;td&gt;0.213&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0.391&lt;/td&gt;
&lt;td&gt;2.17&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0.4&lt;/td&gt;
&lt;td&gt;0.4&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-16-4 仿真参数
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;仿真并进行PSNR测试，仿真结果如图3-19-5所示。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-19-5 仿真结果&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/19/5.png&amp;quot; /&gt;&lt;br /&gt;
图3-19-5 仿真结果，左侧为流水线模式下的HDL功能仿真结果，中间为请求响应模式下的HDL功能仿真结果，右侧为软件仿真结果
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.19.5 资源和时序&lt;/h3&gt;
&lt;p&gt;最终实现与图像大小和数据位宽有关，这里只分析大小为512x512和数据位宽为8时的状况，根据Vivado生成的报表，主要资源耗费如表3-19-5。&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;Slice LUTs*&lt;/th&gt;
&lt;th&gt;Slice Registers&lt;/th&gt;
&lt;th&gt;DSP&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;77&lt;/td&gt;
&lt;td&gt;69&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-19-5 主要资源耗费
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;同时根据时序报告，最大的Data Path Delay(数据路径延迟)为3.372ns，即：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;FMax = 296.55MHz  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;即说明，Scale核在流水线模式下，理论上在处理1080p全高清图像时可以达到143帧。&lt;br /&gt;
由于数据路径延迟和应用的最终约束设置强相关，所以仅供参考。&lt;/p&gt;
&lt;h3&gt;3.19.6 分析与结论&lt;/h3&gt;
&lt;p&gt;PSNR如表3-19-6。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;1-0.391x2.17&lt;/th&gt;
&lt;th&gt;1-0.4x0.4&lt;/th&gt;
&lt;th&gt;1-1.97x0.213&lt;/th&gt;
&lt;th&gt;Total&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-19-6 PSNR
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;PSNR均值为最大值，可见在测试范围内，SCL核与软件等效，同时可以达到不错的FMax，设计成功。&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;参考文献&lt;/h1&gt;
&lt;p&gt;[3] Donald G.Bailey.基于FPGA的嵌入式图像处理系统设计[M].原魁,何文浩,肖晗译.北京:电子工业出版社,2013.  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;感谢&lt;/h1&gt;
&lt;p&gt;仿真图像来源：&lt;br /&gt;
&lt;a href=&amp;quot;http://www.pixiv.net/member_illust.php?mode=medium&amp;amp;illust_id=37208606&amp;quot;&gt;あきのん-[C84]こもれび&lt;/a&gt;  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Friday, 29 May 2015 19:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.05.29 19:00:article/Skill-2015_05_29_a</guid>
<category>FPGA</category>
<category>FPGA-Imaging-Library</category>
<category>图像处理</category>
<category>几何变换</category>
<category>缩放</category>
</item>

<item>
<title>【FPGA/图像处理】几何变换-平移</title>
<link>http://dtysky.moe/article/Skill-2015_05_27_c</link>
<description>&lt;p&gt;图像处理理系列文章基本都是我本科毕设论文同步发布，这个项目以LGPL许可被发布在&lt;a href=&amp;quot;http://fil.dtysky.moe&amp;quot;&gt;F-I-L&lt;/a&gt;。平移是仿射变换的一种特例&lt;sup&gt;[25]&lt;/sup&gt;，仿射变换本质上是一个以输入坐标为自变量的线性函数，将在后面的章节详细介绍。平移变换每一个方向的变换只与该方向的输入坐标和偏移量有关，它将图像按照输入的偏移量向着水平和垂直两个方向进行移动，偏移量可正可负，所以会涉及越界的问题。大部分的仿射变换用前向映射实现都会比较复杂，但平移变换是一个例外，可以用特殊方法处理，本节将会介绍如何实现一个平移的模块。&lt;br /&gt;
这个IP核的资源在这里：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/FPGA-Imaging-Library/tree/Publish/Geometry/Pan&amp;quot;&gt;Pan&lt;/a&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;3 算法实现&lt;/h1&gt;
&lt;p&gt;明确了设计和架构，便可以进行算法的实现。本章将会说明如何实现图像处理的算法，以及如何运用它们。  &lt;/p&gt;
&lt;h2&gt;3.18 几何变换-平移&lt;/h2&gt;
&lt;p&gt;平移是仿射变换的一种特例&lt;sup&gt;[25]&lt;/sup&gt;，仿射变换本质上是一个以输入坐标为自变量的线性函数，将在后面的章节详细介绍。平移变换每一个方向的变换只与该方向的输入坐标和偏移量有关，它将图像按照输入的偏移量向着水平和垂直两个方向进行移动，偏移量可正可负，所以会涉及越界的问题。大部分的仿射变换用前向映射实现都会比较复杂，但平移变换是一个例外，可以用特殊方法处理，本节将会介绍如何实现一个平移的模块。&lt;/p&gt;
&lt;h3&gt;3.18.1 原理&lt;/h3&gt;
&lt;p&gt;平移变换的基本原理如式3-18-1，Q为输出，I为输入，x和y为输入像素坐标，$x_t$和$y_t$为输出像素坐标，xoffset和yoffset分别为两个方向的偏移量，w和h为图像的宽和高，可见其实现依赖于两次加法操作，由于偏移量可正可负，所以是有符号的加法操作，所以必须考虑到加法操作的结果大于图像最大边界或小于0的状况。  &lt;/p&gt;
&lt;p&gt;$$\begin{cases}Q[x_t,y_t] = I[x,y]  \\x_t = x + xoffset &amp;&amp; x \in[0, w)\\ y_t = y + yoffset &amp;&amp; y \in[0, h)\ \end{cases}.\ \ \ \ \ \ \ \ (3-18-1)$$&lt;/p&gt;

&lt;p&gt;由于平移变换的特殊性，故可以在加法得到的输出坐标越界时进行适当的加法或者减法操作，让输出坐标重新落到图像范围内，同时由于此时的像素是越界的，所以像素色彩置为背景，改进后的算法如式3-18-2、3-18-3、3-18-4和3-18-5，$sum_x$和$sum_y$分别为第一次加法的输出坐标。  &lt;/p&gt;
&lt;p&gt;$$\begin{cases}sum_x = x + xoffset &amp;&amp; x \in[0, w)\\ sum_y = y + yoffset &amp;&amp; y \in[0, h)\ \end{cases}.\ \ \ \ \ \ \ \ (3-18-2)$$&lt;/p&gt;

&lt;p&gt;$$x_t=\begin{cases} sum_x &amp;&amp; sum_x \in[0, w) \\sum_x + w  &amp;&amp; sum_x &lt; 0\\sum_x - w  &amp;&amp; sum_x &gt;= w \ \end{cases}.\ \ \ \ \ \ \ \ (3-18-3)$$&lt;/p&gt;

&lt;p&gt;$$y_t=\begin{cases} sum_y &amp;&amp; sum_y \in[0, h) \\sum_y + h  &amp;&amp; sum_y &lt; 0\\sum_y - h  &amp;&amp; sum_y &gt;= h \ \end{cases}.\ \ \ \ \ \ \ \ (3-18-4)$$&lt;/p&gt;

&lt;p&gt;$$Q[x_t,y_t]=\begin{cases} I[x,y] &amp;&amp; sum_x \in [0, w],sum_y \in[0, h) \\0  &amp;&amp; Others\ \end{cases}.\ \ \ \ \ \ \ \ (3-18-5)$$&lt;/p&gt;

&lt;p&gt;所以平移变换需要分两个阶段完成，第一个阶段用两次并行加法分别计算两个原生输出坐标，第二次则用四次并行加法得出假定越界后的输出，同时根据第一个周期得到的原生输出坐标判断输出点是否在边界之内，随后根据边界信息来确定选择原生输出坐标还是假定越界后的坐标进行最终的输出，这是一个二级流水线。  &lt;/p&gt;
&lt;h3&gt;3.18.2 设计&lt;/h3&gt;
&lt;p&gt;根据原理可知，Pan核(以下简称Pan核)需要两级流水线，若干次并行有符号加法操作和比较操作，同时需要两个端口来确定输出偏移量。故其需要的配置参数与端口如表3-18-1和表3-18-2。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;td&gt;work_mode&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;0为流水线模式，1为请求响应模式&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;模块的工作模式。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;tr&gt;
&lt;td&gt;data_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;1 - 12&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;数据位宽。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;im_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;1 - 4096&lt;/td&gt;
&lt;td&gt;320&lt;/td&gt;
&lt;td&gt;图像宽度。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;im_height&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;1 - 4096&lt;/td&gt;
&lt;td&gt;240&lt;/td&gt;
&lt;td&gt;图像高度。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;im_width_bits&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;取决于图像宽度&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;图像宽度的位宽。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-18-1 配置参数
&lt;/center&gt;&lt;br /&gt;
&lt;br&gt;
&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;端口&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;td&gt;clk&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;Clock.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rst_n&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;复位，低有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;offset_x&lt;/td&gt;
&lt;td&gt;input&lt;/td&gt;
&lt;td&gt;有符号&lt;/td&gt;
&lt;td&gt;如果是正数，则必须是原码，否则为补码。&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;横向偏移量。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;offset_y&lt;/td&gt;
&lt;td&gt;input&lt;/td&gt;
&lt;td&gt;有符号&lt;/td&gt;
&lt;td&gt;如果是正数，则必须是原码，否则为补码。&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;纵向偏移量。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_count_x&lt;/td&gt;
&lt;td&gt;input&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;im_width_bits - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;图像横向坐标输入。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_count_y&lt;/td&gt;
&lt;td&gt;input&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;im_width_bits - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;图像纵向坐标输入。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_enable&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据使能，在流水线模式下，它是另一个复位信号，在请求响应模式下，只有在它有效的时候in_data才会被真正地改变。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_data&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width * in_window_width * in_window_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据，必须和in_enable同步输入。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_ready&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据有效，在两种模式下，这个信号都会在out_data可以被读取的时候有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_data&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据，将会和out_ready同步输出。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_count_x&lt;/td&gt;
&lt;td&gt;input&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;im_width_bits - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;图像横向坐标输出。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_count_y&lt;/td&gt;
&lt;td&gt;input&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;im_width_bits - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;图像纵向坐标输出。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-18-2 端口
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.18.3 实现&lt;/h3&gt;
&lt;p&gt;根据3.18.2的设计便可以实现一个Pan核，流水线模式和请求响应模式实现如下。  &lt;/p&gt;
&lt;h4&gt;3.18.3.1 流水线模式&lt;/h4&gt;
&lt;p&gt;输入使能后2个周期第一个结果被输出，开始流水化工作，波形如图3-18-1。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-18-1 流水线模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/18/1.png&amp;quot; /&gt;&lt;br /&gt;
图3-18-1 流水线模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.18.3.2 请求响应模式读取&lt;/h4&gt;
&lt;p&gt;基本同3.18.3.1，但只有在in_enable上升沿时输入才会被改变，波形如图3-18-2。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-18-1 流水线模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/18/2.png&amp;quot; /&gt;&lt;br /&gt;
图3-18-1 流水线模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.18.3.3 IP核GUI&lt;/h4&gt;
&lt;p&gt;完成功能后对Pan核进行了封装，封装如图3-18-3。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-18-3 Pan核的GUI&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/18/3.png&amp;quot; /&gt;&lt;br /&gt;
图3-18-3 Pan核的GUI
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.18.4 仿真&lt;/h3&gt;
&lt;p&gt;只对RGB图像和灰度图像进行测试，我选择了一张图像的RGB和灰度模式进行两套参数的测试，原始图像如图3-18-4。&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;img alt=&amp;quot;图3-18-4 仿真原始图像&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/18/4.png&amp;quot; /&gt;&lt;br /&gt;
图3-18-4 仿真原始图像
&lt;/center&gt;  &lt;/p&gt;
&lt;p&gt;仿真参数如表3-16-3所示。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;xoffset&lt;/th&gt;
&lt;th&gt;yoffset&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-100&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;-50&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-16-3 仿真参数
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;仿真并进行PSNR测试，仿真结果如图3-18-5所示。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-18-5 仿真结果&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/18/5.png&amp;quot; /&gt;&lt;br /&gt;
图3-18-5 仿真结果，左侧为流水线模式下的HDL功能仿真结果，中间为请求响应模式下的HDL功能仿真结果，右侧为软件仿真结果
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.18.5 资源和时序&lt;/h3&gt;
&lt;p&gt;最终实现与图像大小和数据位宽有关，这里只分析大小为512x512个数据位宽为8时的状况，根据Vivado生成的报表，主要资源耗费如表3-18-4。&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;Slice LUTs*&lt;/th&gt;
&lt;th&gt;Slice Registers&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;55&lt;/td&gt;
&lt;td&gt;71&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-18-4 主要资源耗费
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;同时根据时序报告，最大的Data Path Delay(数据路径延迟)为3.153ns，即：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;FMax = 317.15MHz  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;即说明，Pan核在流水线模式下，理论上在处理1080p全高清图像时可以达到152帧。&lt;br /&gt;
由于数据路径延迟和应用的最终约束设置强相关，所以仅供参考。&lt;/p&gt;
&lt;h3&gt;3.18.6 分析与结论&lt;/h3&gt;
&lt;p&gt;PSNR如表3-18-5。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;1--100x100&lt;/th&gt;
&lt;th&gt;1-200x-50&lt;/th&gt;
&lt;th&gt;2--100x100&lt;/th&gt;
&lt;th&gt;2-200x-50&lt;/th&gt;
&lt;th&gt;Total&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-18-5 PSNR
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;PSNR均值为最大值，Pan核与软件等效，同时可以达到不错的FMax，设计成功。&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;参考文献&lt;/h1&gt;
&lt;p&gt;[25] 王金辉,陈冰,王建庄等.实时图像仿射变换系统的研究与实现[J].机械与电子,2012,(2):59-62.DOI:10.3969/j.issn.1001-2257.2012.02.016.  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;感谢&lt;/h1&gt;
&lt;p&gt;仿真图像来源：&lt;br /&gt;
&lt;a href=&amp;quot;http://www.pixiv.net/member_illust.php?mode=medium&amp;amp;illust_id=49779009&amp;quot;&gt;方向錯亂-Queen&lt;/a&gt;  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Wednesday, 27 May 2015 22:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.05.27 22:00:article/Skill-2015_05_27_c</guid>
<category>FPGA</category>
<category>FPGA-Imaging-Library</category>
<category>图像处理</category>
<category>几何变换</category>
<category>平移</category>
</item>

<item>
<title>【FPGA/图像处理】几何变换-镜像</title>
<link>http://dtysky.moe/article/Skill-2015_05_27_b</link>
<description>&lt;p&gt;图像处理理系列文章基本都是我本科毕设论文同步发布，这个项目以LGPL许可被发布在&lt;a href=&amp;quot;http://fil.dtysky.moe&amp;quot;&gt;F-I-L&lt;/a&gt;。镜像的目的是将图像进行翻转，与色彩反转类似，不过它变换的是坐标。镜像可以用前向映射实现，同时由于输出坐标必然落在原先的图像区域内，所以不用进行区域判断，属于比较简单的几何变换，本节将说明如何实现一个镜像的IP核。&lt;br /&gt;
这个IP核的资源在这里：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/FPGA-Imaging-Library/tree/Publish/Geometry/Mirror&amp;quot;&gt;Mirror&lt;/a&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;3 算法实现&lt;/h1&gt;
&lt;p&gt;明确了设计和架构，便可以进行算法的实现。本章将会说明如何实现图像处理的算法，以及如何运用它们。  &lt;/p&gt;
&lt;h2&gt;3.17 几何变换-镜像&lt;/h2&gt;
&lt;p&gt;镜像的目的是将图像进行翻转，与色彩反转类似，不过它变换的是坐标。镜像可以用前向映射实现，同时由于输出坐标必然落在原先的图像区域内，所以不用进行区域判断，属于比较简单的几何变换，本节将说明如何实现一个镜像的IP核。&lt;/p&gt;
&lt;h3&gt;3.17.1 原理&lt;/h3&gt;
&lt;p&gt;镜像有两种模式——水平镜像和垂直镜像，它的原理如式3-17-1所示，Q为输出，I为输入，x和y为输入像素坐标，$x_t$和$y_t$为输出像素坐标，width和height为图像宽度和高度。可见镜像的本质是将输入坐标和图像的宽度和高度做减法以得到输出坐标，同时由于减法的结果必然小于被减数，故这实际上是单纯的无符号数的减法。  &lt;/p&gt;
&lt;p&gt;$$\begin{cases}Q[x_t,y_t] = I[x,y]  \\x_t = width - 1 - x &amp;&amp; x \in[0, width)\\ y_t = height - 1 - y &amp;&amp; y \in[0, height)\ \end{cases}.\ \ \ \ \ \ \ \ (3-17-1)$$&lt;/p&gt;

&lt;p&gt;实际应用中会出现三种情况——水平镜像、垂直镜像和全镜像，所以需要一个模式选择来确定模块的工作方式，同时由于图像宽高的位宽均被限定到12位之内，所以只需要一个周期的流水便可以满足FMax。  &lt;/p&gt;
&lt;h3&gt;3.17.2 设计&lt;/h3&gt;
&lt;p&gt;根据原理可知，Mirror核(以下简称MR核)需要若干次并行减法操作，同时需要根据模式来确定减法操作的次数，所以需要一个mode端口来确定工作的模式。故其需要的配置参数与端口如表3-17-1和表3-17-2。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;td&gt;work_mode&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;0为流水线模式，1为请求响应模式&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;模块的工作模式。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;tr&gt;
&lt;td&gt;data_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;1 - 12&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;数据位宽。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;im_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;1 - 4096&lt;/td&gt;
&lt;td&gt;320&lt;/td&gt;
&lt;td&gt;图像宽度。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;im_height&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;1 - 4096&lt;/td&gt;
&lt;td&gt;240&lt;/td&gt;
&lt;td&gt;图像高度。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;im_width_bits&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;取决于图像宽度&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;图像宽度的位宽。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-17-1 配置参数
&lt;/center&gt;&lt;br /&gt;
&lt;br&gt;
&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;端口&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;td&gt;clk&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;Clock.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rst_n&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;复位，低有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mode&lt;/td&gt;
&lt;td&gt;input&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;0为水平镜像，1为垂直镜像，2和3为全镜像&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;工作模式。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_count_x&lt;/td&gt;
&lt;td&gt;input&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;im_width_bits - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;图像横向坐标输入。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_count_y&lt;/td&gt;
&lt;td&gt;input&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;im_width_bits - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;图像纵向坐标输入。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_enable&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据使能，在流水线模式下，它是另一个复位信号，在请求响应模式下，只有在它有效的时候in_data才会被真正地改变。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_data&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width * in_window_width * in_window_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据，必须和in_enable同步输入。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_ready&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据有效，在两种模式下，这个信号都会在out_data可以被读取的时候有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_data&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据，将会和out_ready同步输出。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_count_x&lt;/td&gt;
&lt;td&gt;input&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;im_width_bits - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;图像横向坐标输出。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_count_y&lt;/td&gt;
&lt;td&gt;input&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;im_width_bits - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;图像纵向坐标输出。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-17-2 端口
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.17.3 实现&lt;/h3&gt;
&lt;p&gt;根据3.17.2的设计便可以实现一个MR核，流水线模式和请求响应模式实现如下。  &lt;/p&gt;
&lt;h4&gt;3.17.3.1 流水线模式&lt;/h4&gt;
&lt;p&gt;输入使能后1个周期第一个结果被输出，开始流水化工作，波形如图3-17-1。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-17-1 流水线模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/17/1.png&amp;quot; /&gt;&lt;br /&gt;
图3-17-1 流水线模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.17.3.2 请求响应模式读取&lt;/h4&gt;
&lt;p&gt;基本同3.17.3.1，但只有在in_enable上升沿时输入才会被改变，波形如图3-17-2。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-17-1 流水线模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/17/2.png&amp;quot; /&gt;&lt;br /&gt;
图3-17-1 流水线模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.17.3.3 IP核GUI&lt;/h4&gt;
&lt;p&gt;完成功能后对MR核进行了封装，封装如图3-17-3。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-17-3 MR核的GUI&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/17/3.png&amp;quot; /&gt;&lt;br /&gt;
图3-17-3 MR核的GUI
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.17.4 仿真&lt;/h3&gt;
&lt;p&gt;只对RGB图像和灰度图像进行测试，我选择了一张图像的RGB和灰度模式进行三种模式的测试，原始图像如图3-17-4。&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;img alt=&amp;quot;图3-17-4 仿真原始图像&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/17/4.jpg&amp;quot; /&gt;&lt;br /&gt;
图3-17-4 仿真原始图像
&lt;/center&gt;  &lt;/p&gt;
&lt;p&gt;仿真并进行PSNR测试，仿真结果如图3-17-5所示。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-17-5 仿真结果&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/17/5.jpg&amp;quot; /&gt;&lt;br /&gt;
图3-17-5 仿真结果，从左上角分别为流水线模式下的HDL功能仿真结果，请求响应模式下的HDL功能仿真结果，软件仿真结果
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.17.5 资源和时序&lt;/h3&gt;
&lt;p&gt;最终实现与图像大小和数据位宽有关，这里只分析大小为512x512个数据位宽为8时的状况，根据Vivado生成的报表，主要资源耗费如表3-17-3。&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;Slice LUTs*&lt;/th&gt;
&lt;th&gt;Slice Registers&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;27&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-17-3 主要资源耗费
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;同时根据时序报告，最大的Data Path Delay(数据路径延迟)为1.941ns，即：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;FMax = 515.19MHz  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;即说明，MR核在流水线模式下，理论上在处理1080p全高清图像时可以达到248帧。&lt;br /&gt;
由于数据路径延迟和应用的最终约束设置强相关，所以仅供参考。&lt;/p&gt;
&lt;h3&gt;3.17.6 分析与结论&lt;/h3&gt;
&lt;p&gt;PSNR如表3-17-4。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;1-All&lt;/th&gt;
&lt;th&gt;1-Horizontal&lt;/th&gt;
&lt;th&gt;1-Vertical&lt;/th&gt;
&lt;th&gt;2-All&lt;/th&gt;
&lt;th&gt;2-Horizontal&lt;/th&gt;
&lt;th&gt;2-Vertical&lt;/th&gt;
&lt;th&gt;Total&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-17-4 PSNR
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;PSNR均值为最大值，MR核与软件等效，同时可以达到很高的FMax，设计成功。&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;参考文献&lt;/h1&gt;
&lt;hr /&gt;
&lt;h1&gt;感谢&lt;/h1&gt;
&lt;p&gt;仿真图像来源：&lt;br /&gt;
&lt;a href=&amp;quot;http://www.pixiv.net/member_illust.php?mode=medium&amp;amp;illust_id=18392982&amp;quot;&gt;LM7-nk&lt;/a&gt;  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Wednesday, 27 May 2015 18:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.05.27 18:00:article/Skill-2015_05_27_b</guid>
<category>FPGA</category>
<category>FPGA-Imaging-Library</category>
<category>图像处理</category>
<category>几何变换</category>
<category>镜像</category>
</item>

<item>
<title>【FPGA/图像处理】几何变换-裁剪</title>
<link>http://dtysky.moe/article/Skill-2015_05_27_a</link>
<description>&lt;p&gt;图像处理理系列文章基本都是我本科毕设论文同步发布，这个项目以LGPL许可被发布在&lt;a href=&amp;quot;http://fil.dtysky.moe&amp;quot;&gt;F-I-L&lt;/a&gt;。几何变换的基本操作单元是像素的坐标，它保留色彩信息，对坐标进行变换。几何变换有两种大方向的分类，分别是前向映射和逆向映射&lt;sup&gt;[3]&lt;/sup&gt;，对于FPGA而言，前向映射实现需要的资源较少，但是往往难度较高，但只能满足一部分简单的变换。裁剪操作可以用前向映射实现，它保留图像的一个区域的色彩信息，将其他部分的色彩置为背景，本节将说明如何实现一个裁剪模块。&lt;br /&gt;
这个IP核的资源在这里：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/FPGA-Imaging-Library/tree/Publish/Geometry/Crop&amp;quot;&gt;Crop&lt;/a&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;3 算法实现&lt;/h1&gt;
&lt;p&gt;明确了设计和架构，便可以进行算法的实现。本章将会说明如何实现图像处理的算法，以及如何运用它们。  &lt;/p&gt;
&lt;h2&gt;3.16 几何变换-裁剪&lt;/h2&gt;
&lt;p&gt;几何变换的基本操作单元是像素的坐标，它保留色彩信息，对坐标进行变换。几何变换有两种大方向的分类，分别是前向映射和逆向映射&lt;sup&gt;[3]&lt;/sup&gt;，对于FPGA而言，前向映射实现需要的资源较少，但是往往难度较高，但只能满足一部分简单的变换。裁剪操作可以用前向映射实现，它保留图像的一个区域的色彩信息，将其他部分的色彩置为背景，本节将说明如何实现一个裁剪模块。&lt;/p&gt;
&lt;h3&gt;3.16.1 原理&lt;/h3&gt;
&lt;p&gt;前向映射将原图像的像素坐标作为自变量，以某个变换函数得出目标图像的像素坐标，裁剪变换的变换函数如式3-16-1，Q为输出，I为输入，x和y为原图像坐标，t、b、l、r为四个边界，从某种角度来看，它实际上一种非线性滤波器，保留输入坐标的同时变换输出色彩。  &lt;/p&gt;
&lt;p&gt;$$Q=\begin{cases} I[x,y] &amp;&amp; x \in [l, r], y \in [t, b]  \\0 &amp; &amp; Others \ \end{cases}.\ \ \ \ \ \ \ \ (3-16-1)$$&lt;/p&gt;

&lt;p&gt;所以，实现一个裁剪模块实际上是要通过给定的边界信息来确定可以输出的一个区域，然后根据是否在这个区域内来确定输出。  &lt;/p&gt;
&lt;h3&gt;3.16.2 设计&lt;/h3&gt;
&lt;p&gt;根据原理可知，Crop核(以下简称CP核)需要进行区域判断，而判断是根据四次比较(上下左右)来完成的，故需要四个端口来输入四方的边界值，随后在模块内部进行并行比较，最后全部取与即可得到是否在区域内的信息。同时由于坐标被作为了基本的操作元素，所以输出中也应当包含坐标值。故其需要的配置参数与端口如表3-16-1和表3-16-2。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;td&gt;work_mode&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;0为流水线模式，1为请求响应模式&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;模块的工作模式。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;tr&gt;
&lt;td&gt;data_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;1 - 12&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;数据位宽。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;im_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;1 - 4096&lt;/td&gt;
&lt;td&gt;320&lt;/td&gt;
&lt;td&gt;图像宽度。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;im_height&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;1 - 4096&lt;/td&gt;
&lt;td&gt;240&lt;/td&gt;
&lt;td&gt;图像高度。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;im_width_bits&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;取决于图像宽度&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;图像宽度的位宽。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-16-1 配置参数
&lt;/center&gt;&lt;br /&gt;
&lt;br&gt;
&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;端口&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;td&gt;clk&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;Clock.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rst_n&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;复位，低有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;top&lt;/td&gt;
&lt;td&gt;input&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;取决于图像的高度，0 - im_height-1&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;裁剪区域的上边界。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;bottom&lt;/td&gt;
&lt;td&gt;input&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;取决于图像的高度，0 - im_height-1&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;裁剪区域的下边界。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;left&lt;/td&gt;
&lt;td&gt;input&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;取决于图像的高度，0 - im_widtht-1&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;裁剪区域的左边界。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;right&lt;/td&gt;
&lt;td&gt;input&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;取决于图像的高度，0 - im_widtht-1&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;裁剪区域的右边界。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_count_x&lt;/td&gt;
&lt;td&gt;input&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;im_width_bits - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;图像横向坐标输入。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_count_y&lt;/td&gt;
&lt;td&gt;input&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;im_width_bits - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;图像纵向坐标输入。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_enable&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据使能，在流水线模式下，它是另一个复位信号，在请求响应模式下，只有在它有效的时候in_data才会被真正地改变。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_data&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width * in_window_width * in_window_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据，必须和in_enable同步输入。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_ready&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据有效，在两种模式下，这个信号都会在out_data可以被读取的时候有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_data&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据，将会和out_ready同步输出。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_count_x&lt;/td&gt;
&lt;td&gt;input&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;im_width_bits - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;图像横向坐标输出。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_count_y&lt;/td&gt;
&lt;td&gt;input&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;im_width_bits - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;图像纵向坐标输出。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-16-2 端口
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.16.3 实现&lt;/h3&gt;
&lt;p&gt;根据3.16.2的设计便可以实现一个CP核，流水线模式和请求响应模式实现如下。  &lt;/p&gt;
&lt;h4&gt;3.16.3.1 流水线模式&lt;/h4&gt;
&lt;p&gt;输入使能后1个周期第一个结果被输出，开始流水化工作，波形如图3-16-1。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-16-1 流水线模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/16/1.png&amp;quot; /&gt;&lt;br /&gt;
图3-16-1 流水线模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.16.3.2 请求响应模式读取&lt;/h4&gt;
&lt;p&gt;基本同3.16.3.1，但只有在in_enable上升沿时输入才会被改变，波形如图3-16-2。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-16-1 流水线模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/16/2.png&amp;quot; /&gt;&lt;br /&gt;
图3-16-1 流水线模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.16.3.3 IP核GUI&lt;/h4&gt;
&lt;p&gt;完成功能后对CP核进行了封装，封装如图3-16-3。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-16-3 CP核的GUI&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/16/3.png&amp;quot; /&gt;&lt;br /&gt;
图3-16-3 CP核的GUI
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.16.4 仿真&lt;/h3&gt;
&lt;p&gt;CP核的变换与色彩空间无关，所以适合所有色彩的变换，但综合考虑测试的必要性和测试平台的编写复杂度，只对RGB图像和灰度图像进行测试，我选择了一张图像的RGB和灰度模式进行测试，原始图像如图3-16-4。&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;img alt=&amp;quot;图3-16-4 仿真原始图像&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/16/4.jpg&amp;quot; /&gt;&lt;br /&gt;
图3-16-4 仿真原始图像
&lt;/center&gt;  &lt;/p&gt;
&lt;p&gt;仿真参数如表3-16-3所示。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;Top&lt;/th&gt;
&lt;th&gt;Bottom&lt;/th&gt;
&lt;th&gt;Left&lt;/th&gt;
&lt;th&gt;Right&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;492&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;492&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;402&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;302&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-16-3 仿真参数
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;仿真并进行PSNR测试，仿真结果如图3-16-5所示。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-16-5 仿真结果&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/16/5.png&amp;quot; /&gt;&lt;br /&gt;
图3-16-5 仿真结果，左侧为流水线模式下的HDL功能仿真结果，中间为请求响应模式下的HDL功能仿真结果，右侧为软件仿真结果
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.16.5 资源和时序&lt;/h3&gt;
&lt;p&gt;最终实现与图像大小和数据位宽有关，这里只分析大小为512x512个数据位宽为8时的状况，根据Vivado生成的报表，主要资源耗费如表3-16-4。&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;Slice LUTs*&lt;/th&gt;
&lt;th&gt;Slice Registers&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;29&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-16-4 主要资源耗费
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;同时根据时序报告，最大的Data Path Delay(数据路径延迟)为2.113ns，即：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;FMax = 473.26MHz  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;即说明，CP核在流水线模式下，理论上在处理1080p全高清图像时可以达到228帧。&lt;br /&gt;
由于数据路径延迟和应用的最终约束设置强相关，所以仅供参考。&lt;/p&gt;
&lt;h3&gt;3.16.6 分析与结论&lt;/h3&gt;
&lt;p&gt;PSNR如表3-16-5。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;1-100x402x200x302&lt;/th&gt;
&lt;th&gt;1-20x492x20x492&lt;/th&gt;
&lt;th&gt;2-100x402x200x302&lt;/th&gt;
&lt;th&gt;2-20x492x20x492&lt;/th&gt;
&lt;th&gt;Total&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-16-5 PSNR
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;PSNR均值为最大值，CP核与软件等效，同时可以达到很高的FMax，设计成功。&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;参考文献&lt;/h1&gt;
&lt;p&gt;[3] Donald G.Bailey.基于FPGA的嵌入式图像处理系统设计[M].原魁,何文浩,肖晗译.北京:电子工业出版社,2013.  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;感谢&lt;/h1&gt;
&lt;p&gt;仿真图像来源：&lt;br /&gt;
&lt;a href=&amp;quot;http://www.pixiv.net/member_illust.php?mode=medium&amp;amp;illust_id=35231457&amp;quot;&gt;おにねこ-メカニック・ロンド&lt;/a&gt;  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Wednesday, 27 May 2015 09:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.05.27 09:00:article/Skill-2015_05_27_a</guid>
<category>FPGA</category>
<category>FPGA-Imaging-Library</category>
<category>图像处理</category>
<category>几何变换</category>
<category>裁剪</category>
</item>

<item>
<title>【FPGA/图像处理】生成器-帧控制2</title>
<link>http://dtysky.moe/article/Skill-2015_05_26_a</link>
<description>&lt;p&gt;图像处理理系列文章基本都是我本科毕设论文同步发布，这个项目以LGPL许可被发布在&lt;a href=&amp;quot;http://fil.dtysky.moe&amp;quot;&gt;F-I-L&lt;/a&gt;。和前面的帧控制器不同，这里的帧控制器主要为几何变换服务。几何变换操作的基本要素不是像素的色彩信息，而是它们的坐标信息，所以需要提供一个以坐标为输入的帧控制器，让几何变换模块可以控制帧缓存。本节将介绍如何实现一个以坐标为输入的帧控制器。&lt;br /&gt;
这个IP核的资源在这里：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/FPGA-Imaging-Library/tree/Publish/Generator/FrameController2&amp;quot;&gt;FrameController2&lt;/a&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;3 算法实现&lt;/h1&gt;
&lt;p&gt;明确了设计和架构，便可以进行算法的实现。本章将会说明如何实现图像处理的算法，以及如何运用它们。  &lt;/p&gt;
&lt;h2&gt;3.15 生成器-帧控制2&lt;/h2&gt;
&lt;p&gt;和前面的帧控制器不同，这里的帧控制器主要为几何变换服务。几何变换操作的基本要素不是像素的色彩信息，而是它们的坐标信息，所以需要提供一个以坐标为输入的帧控制器，让几何变换模块可以控制帧缓存。本节将介绍如何实现一个以坐标为输入的帧控制器。&lt;/p&gt;
&lt;h3&gt;3.15.1 原理&lt;/h3&gt;
&lt;p&gt;与3.2一致，此处的帧控制器也是以改变RAM地址和控制使能信号来实现的，但地址的来源不是模块内部的计数器，而是外部输入的坐标，所以需要一个部分将坐标转换为地址，如式3-15-1所示，x为横坐标，y为纵坐标，width为图像宽度，address为输出地址，可见这实际上是一次乘法运算和一次加法运算的结合，所以需要用到乘法器。同时考虑到加法实际上可以看做最多12位的加法，所以可以不用专用加法器，交给综合器自行处理。  &lt;/p&gt;
&lt;p&gt;$$address=width * y + x\ \ \ \ \ \ \ \ (3-15-1)$$&lt;/p&gt;

&lt;h3&gt;3.15.2 设计&lt;/h3&gt;
&lt;p&gt;根据原理可知，FrameController2核(以下简称FR2核)需要一个乘法器，所以需要的配置参数、端口和子模块如表3-15-1、表3-15-2和表3-15-3。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;td&gt;work_mode&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;0为流水线模式，1为请求响应模式&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;模块的工作模式。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;wr_mode&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;0为写，1为读。&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;模块的读写模式。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;data_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;1 - 12&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;数据位宽。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;im_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;1 - 4096&lt;/td&gt;
&lt;td&gt;320&lt;/td&gt;
&lt;td&gt;图像宽度。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;im_height&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;1 - 4096&lt;/td&gt;
&lt;td&gt;240&lt;/td&gt;
&lt;td&gt;图像高度。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;im_width_bits&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;取决于图像宽度&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;图像宽度的位宽。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;addr_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;取决于图像的宽度和高度。&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;存储帧缓存的RAM的地址位宽。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ram_read_latency&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;0 - 15，取决于RAM。&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;RAM的读延迟，在Xilin器件中，典型为2。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mul_delay&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;乘法器延迟。&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;乘法器的延迟，取决于配置。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-15-1 配置参数
&lt;/center&gt;&lt;br /&gt;
&lt;br&gt;
&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;端口&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;td&gt;clk&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;Clock.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rst_n&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;复位，低有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_count_x&lt;/td&gt;
&lt;td&gt;input&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;im_width_bits - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;图像宽度计数输入。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_count_y&lt;/td&gt;
&lt;td&gt;input&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;im_width_bits - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;图像高度输入计数。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_enable&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据使能，在流水线模式下，它是另一个复位信号，在请求响应模式下，只有在它有效的时候in_data才会被真正地改变。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_data&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width * in_window_width * in_window_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据，必须和in_enable同步输入。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_ready&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据有效，在两种模式下，这个信号都会在out_data可以被读取的时候有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_data&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据，将会和out_ready同步输出。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ram_addr&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;addr_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出到RAM的地址。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-15-2 端口
&lt;/center&gt;&lt;br /&gt;
&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&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;tr&gt;
&lt;td&gt;Mul&lt;/td&gt;
&lt;td&gt;Multiplier12x12FR2&lt;/td&gt;
&lt;td&gt;12位无符号数和12位无符号数的乘法器，用于得出帧地址。你可以自己配置乘法器，随后改变配置参数中的延迟。你不能改变端口的配置！&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-15-3 子模块
&lt;/center&gt;&lt;/p&gt;
&lt;h3&gt;3.15.3 实现&lt;/h3&gt;
&lt;p&gt;根据3.15.2的设计便可以实现一个FR2核，流水线模式和请求响应模式实现如下。  &lt;/p&gt;
&lt;h4&gt;3.15.3.1 流水线模式写入&lt;/h4&gt;
&lt;p&gt;输入使能后乘法器延迟+1个周期第一个结果被输出，开始流水化工作，波形如图3-15-1。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-15-1 流水线模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/15/1.png&amp;quot; /&gt;&lt;br /&gt;
图3-15-1 流水线模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.15.3.2 流水线模式读取&lt;/h4&gt;
&lt;p&gt;输入使能后乘法器延迟+2个周期第一个结果被输出，开始流水化工作，波形如图3-15-2。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-15-1 流水线模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/15/2.png&amp;quot; /&gt;&lt;br /&gt;
图3-15-1 流水线模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.15.3.3 请求响应模式写入&lt;/h4&gt;
&lt;p&gt;基本与3.15.3.1一致，但只有in_enable的上升沿时才会有计数值和数据被输入，波形如图3-15-3。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-15-3 请求响应模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/15/3.png&amp;quot; /&gt;&lt;br /&gt;
图3-15-3 请求响应模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.15.3.4 请求响应模式读取&lt;/h4&gt;
&lt;p&gt;基本与3.15.3.2一致，但只有in_enable的上升沿时才会有计数值和数据被输入，波形如图3-15-4。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-15-4 请求响应模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/15/4.png&amp;quot; /&gt;&lt;br /&gt;
图3-15-4 请求响应模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.15.3.3 IP核GUI&lt;/h4&gt;
&lt;p&gt;完成功能后对FR2核进行了封装，封装如图3-15-5。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-15-5 FR2核的GUI&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/15/5.png&amp;quot; /&gt;&lt;br /&gt;
图3-15-5 FR2核的GUI
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.15.4 仿真&lt;/h3&gt;
&lt;p&gt;FR2核没有软件仿真，将原始图像作为比较源进行PSNR测试，仿真结果如图3-15-6所示。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-15-6 仿真结果&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/15/6.jpg&amp;quot; /&gt;&lt;br /&gt;
图3-15-6 仿真结果，左侧为流水线模式下的HDL功能仿真结果，中间为请求响应模式下的HDL功能仿真结果，右侧为软件仿真结果
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.15.5 资源和时序&lt;/h3&gt;
&lt;p&gt;最终实现与乘法器配置和数据位宽有关，这里只分析使用DSP并且数据位宽为8的状况，根据Vivado生成的报表，主要资源耗费如表3-15-4。&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;Slice LUTs*&lt;/th&gt;
&lt;th&gt;Slice Registers&lt;/th&gt;
&lt;th&gt;DSP&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;38&lt;/td&gt;
&lt;td&gt;64&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-15-4 主要资源耗费
&lt;/center&gt;
&lt;br&gt;&lt;/p&gt;
&lt;p&gt;同时根据时序报告，最大的Data Path Delay(数据路径延迟)为2.485ns，即：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;FMax = 402.41MHz  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;即说明，FR2核在流水线模式下，理论上在处理1080p全高清图像时可以达到194帧。&lt;br /&gt;
由于数据路径延迟和应用的最终约束设置强相关，所以仅供参考。&lt;/p&gt;
&lt;h3&gt;3.15.6 分析与结论&lt;/h3&gt;
&lt;p&gt;PSNR如表3-15-5。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;1&lt;/th&gt;
&lt;th&gt;2&lt;/th&gt;
&lt;th&gt;3&lt;/th&gt;
&lt;th&gt;Total&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-15-5 PSNR
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;PSNR均值为最大值，FR2核与软件等效，同时可以达到很高的FMax，设计成功。&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;参考文献&lt;/h1&gt;
&lt;hr /&gt;
&lt;h1&gt;感谢&lt;/h1&gt;
&lt;p&gt;仿真图像来源：&lt;br /&gt;
&lt;a href=&amp;quot;http://www.pixiv.net/member_illust.php?mode=medium&amp;amp;illust_id=43409888&amp;quot;&gt;月岡月穂-ゆらゆら&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;http://www.pixiv.net/member_illust.php?mode=medium&amp;amp;illust_id=27556337&amp;quot;&gt;LM7-oxford eleKtricity&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;http://www.pixiv.net/member_illust.php?mode=medium&amp;amp;illust_id=45293430&amp;quot;&gt;cotta-池&lt;/a&gt;  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Tuesday, 26 May 2015 09:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.05.26 09:00:article/Skill-2015_05_26_a</guid>
<category>FPGA</category>
<category>FPGA-Imaging-Library</category>
<category>图像处理</category>
<category>帧控制</category>
</item>

<item>
<title>【FPGA/图像处理】局部滤波器-二值模板匹配</title>
<link>http://dtysky.moe/article/Skill-2015_05_25_b</link>
<description>&lt;p&gt;图像处理理系列文章基本都是我本科毕设论文同步发布，这个项目以LGPL许可被发布在&lt;a href=&amp;quot;http://fil.dtysky.moe&amp;quot;&gt;F-I-L&lt;/a&gt;。二值模板匹配也可以看做是一种形态学操作，比起腐蚀膨胀它的目的更为极端和明确，如果一个窗口与模板完全一致，则保留中心像素，否则消除。这种效果适用于一些细化算法&lt;sup&gt;[21,24]&lt;/sup&gt;，比如在某些迭代算法中作为最后迭代结束的一个参照。本节将会说明一个二值模板匹配模块的实现。&lt;br /&gt;
这个IP核的资源在这里：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/FPGA-Imaging-Library/tree/Publish/LocalFilter/MatchTemplateBin&amp;quot;&gt;MatchTemplateBin&lt;/a&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;3 算法实现&lt;/h1&gt;
&lt;p&gt;明确了设计和架构，便可以进行算法的实现。本章将会说明如何实现图像处理的算法，以及如何运用它们。  &lt;/p&gt;
&lt;h2&gt;3.14 局部滤波器-二值模板匹配&lt;/h2&gt;
&lt;p&gt;二值模板匹配也可以看做是一种形态学操作，比起腐蚀膨胀它的目的更为极端和明确，如果一个窗口与模板完全一致，则保留中心像素，否则消除。这种效果适用于一些细化算法&lt;sup&gt;[21,24]&lt;/sup&gt;，比如在某些迭代算法中作为最后迭代结束的一个参照。本节将会说明一个二值模板匹配模块的实现。&lt;/p&gt;
&lt;h3&gt;3.14.1 原理&lt;/h3&gt;
&lt;p&gt;二值模板匹配的基本原理很简单，如式3-14-1所示，如果窗口和模板完全一致，则中心像素保留，否则置0，效果如图3-14-1。  &lt;/p&gt;
&lt;p&gt;$$Q=\bigwedge_{i,j \in T}I[x + i, y + j]\ \ \ \ \ \ \ \ (3-14-1)$$&lt;/p&gt;

&lt;p&gt;&lt;center&gt;
&lt;img alt=&amp;quot;图3-14-1 模板匹配效果&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/14/1.png&amp;quot; /&gt;&lt;br /&gt;
图3-14-1 模板匹配效果
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.14.2 设计&lt;/h3&gt;
&lt;p&gt;根据原理可知，MatchTemplateBin核(以下简称MTB核)需要的配置参数和端口如表3-14-1与表3-14-2。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;work_mode&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;0为流水线模式，1为请求响应模式&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;工作模式&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;window_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;2 - 15&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;窗口宽度和高度。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-14-1 配置参数
&lt;/center&gt;&lt;br /&gt;
&lt;br&gt;
&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;端口&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;td&gt;clk&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;Clock.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rst_n&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;复位，低有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;template&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;window_width * window_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;用于操作的模板。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_enable&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据使能，在流水线模式下，它是另一个复位信号，在请求响应模式下，只有在它有效的时候in_data才会被真正地改变。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_data&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width * in_window_width * in_window_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据，必须和in_enable同步输入。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_ready&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据有效，在两种模式下，这个信号都会在out_data可以被读取的时候有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_data&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据，将会和out_ready同步输出。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-14-2 端口
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.14.3 实现&lt;/h3&gt;
&lt;p&gt;根据3.14.2的设计便可以实现一个MTB核，流水线模式和请求响应模式实现如下。  &lt;/p&gt;
&lt;h4&gt;3.14.3.1 流水线模式&lt;/h4&gt;
&lt;p&gt;输入使能后1个周期第一个结果被输出，开始流水化工作，波形如图3-14-2。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-14-2 流水线模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/14/2.png&amp;quot; /&gt;&lt;br /&gt;
图3-14-2 流水线模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.14.3.2 请求响应模式&lt;/h4&gt;
&lt;p&gt;基本与3.14.3.1一致，但只有in_enable的上升沿时才会有窗口被输入，波形如图3-14-3。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-14-3 请求响应模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/14/3.png&amp;quot; /&gt;&lt;br /&gt;
图3-14-3 请求响应模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.14.3.3 IP核GUI&lt;/h4&gt;
&lt;p&gt;完成功能后对MTB核进行了封装，封装如图3-14-4。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-14-4 MTB核的GUI&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/14/4.png&amp;quot; /&gt;&lt;br /&gt;
图3-14-4 MTB核的GUI
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.14.4 仿真&lt;/h3&gt;
&lt;p&gt;MTB核只对二值图像有意义，我选取了两张二值图像闭运算的二值图像作为仿真源，分别测试了两套模版，原始图像如图3-14-5。&lt;/p&gt;
&lt;p&gt;&lt;center&gt; 
&lt;img alt=&amp;quot;图3-14-5 原始测试图像&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/14/5.png&amp;quot; /&gt;&lt;br /&gt;
图3-14-5 原始测试图像
&lt;/center&gt;  &lt;/p&gt;
&lt;p&gt;使用HDL功能仿真和软件仿真的结果进行PSNR的计算，仿真结果如图3-14-6所示。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-14-6 仿真结果&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/14/6.png&amp;quot; /&gt;&lt;br /&gt;
图3-14-6 仿真结果，左侧为流水线模式下的HDL功能仿真结果，中间为请求响应模式下的HDL功能仿真结果，右侧为软件仿真结果
&lt;/center&gt;  &lt;/p&gt;
&lt;p&gt;仿真结果的清晰图像如图3-14-8，可见模板匹配在适当的情况下的确可以满足一些效果。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;img alt=&amp;quot;图3-14-7 部分仿真结果&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/14/7.png&amp;quot; /&gt;&lt;br /&gt;
图3-14-7 部分仿真结果，左侧为模板为000010000，右侧为111111111
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.14.5 资源和时序&lt;/h3&gt;
&lt;p&gt;最终实现与窗口大小关系很大，此处只对窗口为3x3的状况分析，根据Vivado生成的报表，主要资源耗费如表3-14-3。&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;Slice LUTs*&lt;/th&gt;
&lt;th&gt;Slice Registers&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-14-3 主要资源耗费
&lt;/center&gt;
&lt;br&gt;&lt;/p&gt;
&lt;p&gt;同时根据时序报告，最大的Data Path Delay(数据路径延迟)为2.199ns，即：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;FMax = 454.75MHz  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;即说明，MTB核在流水线模式下，理论上在处理1080p全高清图像时可以达到219帧。&lt;br /&gt;
由于数据路径延迟和应用的最终约束设置强相关，所以仅供参考。&lt;/p&gt;
&lt;h3&gt;3.14.6 分析与结论&lt;/h3&gt;
&lt;p&gt;PSNR如表3-14-5。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;1-000010000&lt;/th&gt;
&lt;th&gt;1-111111111&lt;/th&gt;
&lt;th&gt;2-000010000&lt;/th&gt;
&lt;th&gt;2-111111111&lt;/th&gt;
&lt;th&gt;Total&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-14-5 PSNR
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;PSNR均值为最大值，MTB核与软件等效，同时可以达到很高的FMax，设计成功。&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;参考文献&lt;/h1&gt;
&lt;p&gt;[21] 施启乐,王从军,黄树槐等.数学形态学图像细化算法在RE中的应用研究[J].华中科技大学学报(自然科学版),2004,32(7):37-39.DOI:10.3321/j.issn:1671-4512.2004.07.013.&lt;br /&gt;
[24] 曹玉龙. 线划图像的细化算法研究[D].长安大学,2011.&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;感谢&lt;/h1&gt;
&lt;p&gt;仿真图像来源：&lt;br /&gt;
&lt;a href=&amp;quot;http://www.pixiv.net/member_illust.php?mode=medium&amp;amp;illust_id=46169977&amp;quot;&gt;LM7-ｱｲｶﾂ&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;http://www.pixiv.net/member_illust.php?mode=medium&amp;amp;illust_id=10884473&amp;quot;&gt;ぜろきち-Blue star&lt;/a&gt;  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Monday, 25 May 2015 23:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.05.25 23:00:article/Skill-2015_05_25_b</guid>
<category>FPGA</category>
<category>FPGA-Imaging-Library</category>
<category>图像处理</category>
<category>模板匹配</category>
</item>

<item>
<title>【FPGA/图像处理】局部滤波器-二值形态学滤波</title>
<link>http://dtysky.moe/article/Skill-2015_05_25_a</link>
<description>&lt;p&gt;图像处理理系列文章基本都是我本科毕设论文同步发布，这个项目以LGPL许可被发布在&lt;a href=&amp;quot;http://fil.dtysky.moe&amp;quot;&gt;F-I-L&lt;/a&gt;。形态学滤波器是很常用的一种局部滤波器，顾名思义，它针对图像的形态进行操作，关注的是“形状”。形态学滤波一般分为腐蚀和膨胀，而在其之上又可以叠加为开运算和闭运算，它对二值图像、灰度图像均有效，但二者实现的方式不同，一般最常用的是二值形态学滤波，针对灰度的本质上是极大值和极小值排序滤波器，暂时不再赘述。形态学滤波常用于图像细化、骨架提取等&lt;sup&gt;[21,22,23,24]&lt;/sup&gt;，可以作为图像识别的一种基本的预处理操作。本节将会说明FPGA的形态学滤波模块实现。&lt;br /&gt;
这个IP核的资源在这里：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/FPGA-Imaging-Library/tree/Publish/LocalFilter/ErosionDilationBin&amp;quot;&gt;ErosionDilationBin&lt;/a&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;3 算法实现&lt;/h1&gt;
&lt;p&gt;明确了设计和架构，便可以进行算法的实现。本章将会说明如何实现图像处理的算法，以及如何运用它们。  &lt;/p&gt;
&lt;h2&gt;3.13 局部滤波器-二值形态学滤波&lt;/h2&gt;
&lt;p&gt;形态学滤波器是很常用的一种局部滤波器，顾名思义，它针对图像的形态进行操作，关注的是“形状”。形态学滤波一般分为腐蚀和膨胀，而在其之上又可以叠加为开运算和闭运算，它对二值图像、灰度图像均有效，但二者实现的方式不同，一般最常用的是二值形态学滤波，针对灰度的本质上是极大值和极小值排序滤波器，暂时不再赘述。形态学滤波常用于图像细化、骨架提取等&lt;sup&gt;[21,22,23,24]&lt;/sup&gt;，可以作为图像识别的一种基本的预处理操作。本节将会说明FPGA的形态学滤波模块实现。&lt;/p&gt;
&lt;h3&gt;3.13.1 原理&lt;/h3&gt;
&lt;p&gt;二值形态学滤波的操作单元与其他局部滤波器一致，都是包含了某一个中心像素点及其邻域的窗口，但比起其他操作，它还有一个作为参照的“模板”，并根据模板和窗口的运算来得到输出。所有的二值图像都可以分为主体像素和背景像素，一般以1为主体像素，0为背景像素，这样最基本的基本的二值形态学操作-腐蚀和膨胀便可以定义为式3-13-1和3-13-2，其中I为输入，T为模板，可见腐蚀是一种收缩的变换，它将窗口与模版进行对比，如果模板中每一个主体像素都落在窗口内，则此时窗口的中心像素置1，否则为0；而膨胀则是一个扩张的变换，只要模板中的任一主体像素落在窗口内，则中心像素置1。腐蚀和膨胀的原理示意如图3-13-1，空心的点表示0，否则表示1。  &lt;/p&gt;
&lt;p&gt;$$Q=\bigwedge_{i,j \in T}I[x + i, y + j]\ \ \ \ \ \ \ \ (3-13-1)$$&lt;/p&gt;

&lt;p&gt;&lt;p&gt;$$Q=\bigvee_{i,j \in T}I[x - i, y - j]\ \ \ \ \ \ \ \ (3-13-2)$$&lt;/p&gt;  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;img alt=&amp;quot;图3-13-1 腐蚀膨胀原理&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/13/1.png&amp;quot; /&gt;&lt;br /&gt;
图3-13-1 腐蚀膨胀原理
&lt;/center&gt;  &lt;/p&gt;
&lt;p&gt;腐蚀和膨胀还可以结合成开运算(先腐蚀后膨胀)和闭运算(先膨胀再腐蚀)，这可以利用两个基本操作的级联来完成。&lt;br /&gt;
在FPGA中，腐蚀膨胀可以由基本的逻辑运算来完成，并且可以用同一种结构完成&lt;sup&gt;[3]&lt;/sup&gt;，如图3-13-2所示，深色的方块代表窗口，浅色的代表模板，mode用于控制腐蚀和膨胀的模式，0为腐蚀，1为膨胀，Q为输出。如此，服饰和膨胀的逻辑运算形式便可以表示为式3-13-3，Q为输出，I为输入，T为模板，size为窗口宽度。可知，每一个像素点需要进行一次异或运算、一次非运算和一次或运算，之后所有的像素总共要进行NxN次的与运算，考虑FMax，所以选择和3.10一样的分级运算来完成。  &lt;/p&gt;
&lt;p&gt;$$Q=mode \oplus \bigwedge_{i,j = 0}^{size}I[i,j]\oplus{mode}\vee{\overline{T[i,j]}}\ \ \ \ \ \ \ \ (3-13-3)$$&lt;/p&gt;

&lt;p&gt;&lt;center&gt;
&lt;img alt=&amp;quot;图3-13-2 腐蚀膨胀逻辑结构&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/13/2.png&amp;quot; /&gt;&lt;br /&gt;
图3-13-2 腐蚀膨胀逻辑结构
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.13.2 设计&lt;/h3&gt;
&lt;p&gt;根据原理可知，ErosionDilationBin核(以下简称EDB核)还需要一个用于确定与运算级数的配置参数，以及在腐蚀模式和膨胀模式之间选择的端口，还有一个用于输入模板的端口，故一个EDB核需要的配置参数和端口如表3-13-1与表3-13-2。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;work_mode&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;0为流水线模式，1为请求响应模式&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;工作模式&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;window_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;2 - 15&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;窗口宽度和高度。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pipe_stage&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;取决于窗口的宽度，为log2(window_width^2)&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;流水线级数。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-13-1 配置参数
&lt;/center&gt;&lt;br /&gt;
&lt;br&gt;
&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;端口&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;td&gt;clk&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;Clock.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rst_n&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;复位，低有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mode&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;0为腐蚀，1为膨胀。&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;操作模式。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;template&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;window_width * window_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;用于操作的模板。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_enable&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据使能，在流水线模式下，它是另一个复位信号，在请求响应模式下，只有在它有效的时候in_data才会被真正地改变。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_data&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width * in_window_width * in_window_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据，必须和in_enable同步输入。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_ready&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据有效，在两种模式下，这个信号都会在out_data可以被读取的时候有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_data&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据，将会和out_ready同步输出。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-13-2 端口
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.13.3 实现&lt;/h3&gt;
&lt;p&gt;根据3.13.2的设计便可以实现一个EDB核，流水线模式和请求响应模式实现如下。  &lt;/p&gt;
&lt;h4&gt;3.13.3.1 流水线模式&lt;/h4&gt;
&lt;p&gt;输入使能后pipe_stage个周期第一个结果被输出，开始流水化工作，波形如图3-13-3。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-13-3 流水线模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/13/3.png&amp;quot; /&gt;&lt;br /&gt;
图3-13-3 流水线模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.13.3.2 请求响应模式&lt;/h4&gt;
&lt;p&gt;基本与3.13.3.1一致，但只有in_enable的上升沿时才会有窗口被输入，波形如图3-13-4。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-13-4 请求响应模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/13/4.png&amp;quot; /&gt;&lt;br /&gt;
图3-13-4 请求响应模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.13.3.3 IP核GUI&lt;/h4&gt;
&lt;p&gt;完成功能后对EDB核进行了封装，封装如图3-13-5。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-13-5 EDB核的GUI&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/13/5.png&amp;quot; /&gt;&lt;br /&gt;
图3-13-5 EDB核的GUI
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.13.4 仿真&lt;/h3&gt;
&lt;p&gt;EDB核只对二值图像有意义，我选取了两张二值图像作为仿真源，分别测试了对腐蚀和膨胀操作配置了两套模版，原始图像如图3-13-6。&lt;/p&gt;
&lt;p&gt;&lt;center&gt; 
&lt;img alt=&amp;quot;图3-13-6 原始测试图像&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/13/6.png&amp;quot; /&gt;&lt;br /&gt;
图3-13-6 原始测试图像
&lt;/center&gt;  &lt;/p&gt;
&lt;p&gt;使用HDL功能仿真和软件仿真的结果进行PSNR的计算，仿真结果如图3-13-7所示。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-13-7 仿真结果&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/13/7.bmp&amp;quot; /&gt;&lt;br /&gt;
图3-13-7 仿真结果，从左上依次为流水线模式下的HDL功能仿真结果，请求响应模式下的HDL功能仿真结果，软件仿真结果
&lt;/center&gt;  &lt;/p&gt;
&lt;p&gt;仿真结果的清晰图像如图3-13-8，可见膨胀的确有扩张的功能，腐蚀的确有收缩细化的功能。同时也对开运算和闭运算做了实验，结果如图3-13-9所示，开运算可以将一些原本断续的线条“打开”，使图像零散化，而闭运算则是将线条闭合，使图像连续化。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;img alt=&amp;quot;图3-13-8 部分仿真结果&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/13/8.png&amp;quot; /&gt;&lt;br /&gt;
图3-13-8 部分仿真结果，左侧为膨胀，右侧为腐蚀
&lt;/center&gt;  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;img alt=&amp;quot;图3-13-9 开闭运算结果&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/13/9.png&amp;quot; /&gt;&lt;br /&gt;
图3-13-9 开闭运算结果，左侧为开运算，右侧为闭运算
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.13.5 资源和时序&lt;/h3&gt;
&lt;p&gt;最终实现与窗口大小关系很大，此处只对窗口为3x3的状况分析，根据Vivado生成的报表，主要资源耗费如表3-13-3。&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;Slice LUTs*&lt;/th&gt;
&lt;th&gt;Slice Registers&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-13-3 主要资源耗费
&lt;/center&gt;
&lt;br&gt;&lt;/p&gt;
&lt;p&gt;同时根据时序报告，最大的Data Path Delay(数据路径延迟)为2.529ns，即：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;FMax = 395.41MHz  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;即说明，EDB核在流水线模式下，理论上在处理1080p全高清图像时可以达到190帧。&lt;br /&gt;
由于数据路径延迟和应用的最终约束设置强相关，所以仅供参考。&lt;/p&gt;
&lt;h3&gt;3.13.6 分析与结论&lt;/h3&gt;
&lt;p&gt;PSNR如表3-13-5。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;1-Dilation-110110000&lt;/th&gt;
&lt;th&gt;1-Dilation-111111000&lt;/th&gt;
&lt;th&gt;1-Erosion-110110000&lt;/th&gt;
&lt;th&gt;1-Erosion-111111000&lt;/th&gt;
&lt;th&gt;2-Dilation-110110000&lt;/th&gt;
&lt;th&gt;2-Dilation-111111000&lt;/th&gt;
&lt;th&gt;2-Erosion-110110000&lt;/th&gt;
&lt;th&gt;2-Erosion-111111000&lt;/th&gt;
&lt;th&gt;Total&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-13-5 PSNR
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;PSNR均值为最大值，EDB核与软件等效，同时可以达到不错的FMax，设计成功。&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;参考文献&lt;/h1&gt;
&lt;p&gt;[3] Donald G.Bailey.基于FPGA的嵌入式图像处理系统设计[M].原魁,何文浩,肖晗译.北京:电子工业出版社,2013.&lt;br /&gt;
[21] 施启乐,王从军,黄树槐等.数学形态学图像细化算法在RE中的应用研究[J].华中科技大学学报(自然科学版),2004,32(7):37-39.DOI:10.3321/j.issn:1671-4512.2004.07.013.&lt;br /&gt;
[22] 王怀群.二值图象的细化[J].无锡轻工大学学报,2001,20(3):315-318.DOI:10.3321/j.issn:1673-1689.2001.03.021.&lt;br /&gt;
[23] 李娜. 基于FPGA的图像实时检测系统识别系统设计[D].长春理工大学,2013.&lt;br /&gt;
[24] 曹玉龙. 线划图像的细化算法研究[D].长安大学,2011.&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;感谢&lt;/h1&gt;
&lt;p&gt;仿真图像来源：&lt;br /&gt;
&lt;a href=&amp;quot;http://www.pixiv.net/member_illust.php?mode=medium&amp;amp;illust_id=46169977&amp;quot;&gt;LM7-ｱｲｶﾂ&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;http://www.pixiv.net/member_illust.php?mode=medium&amp;amp;illust_id=10884473&amp;quot;&gt;ぜろきち-Blue star&lt;/a&gt;  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Monday, 25 May 2015 19:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.05.25 19:00:article/Skill-2015_05_25_a</guid>
<category>FPGA</category>
<category>FPGA-Imaging-Library</category>
<category>图像处理</category>
<category>腐蚀膨胀</category>
<category>形态学</category>
</item>

<item>
<title>【FPGA/图像处理】局部滤波器-局部阈值化</title>
<link>http://dtysky.moe/article/Skill-2015_05_22_a</link>
<description>&lt;p&gt;图像处理理系列文章基本都是我本科毕设论文同步发布，这个项目以LGPL许可被发布在&lt;a href=&amp;quot;http://fil.dtysky.moe&amp;quot;&gt;F-I-L&lt;/a&gt;。局部阈值化有别于全局阈值化，它并非利用一个全局的阈值作用于整张图像，而是对每一个单独的像素都使用一个单独的阈值，这个阈值通常来源于局部滤波器的输出。不同局部滤波器给出的阈值会产生非常不同的效果，而无论是哪一种阈值，最终的目的都是给出一个比较清晰而明确的边缘，通常这个效果比较容易达到，所以局部阈值化是一个不错的边缘检测子，本节将会说明如何实现一个局部阈值化的IP核。&lt;br /&gt;
这个IP核的资源在这里：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/FPGA-Imaging-Library/tree/Publish/LocalFilter/ThresholdLocal&amp;quot;&gt;ThresholdLocal&lt;/a&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;3 算法实现&lt;/h1&gt;
&lt;p&gt;明确了设计和架构，便可以进行算法的实现。本章将会说明如何实现图像处理的算法，以及如何运用它们。  &lt;/p&gt;
&lt;h2&gt;3.12 局部滤波器-局部阈值化&lt;/h2&gt;
&lt;p&gt;局部阈值化有别于全局阈值化，它并非利用一个全局的阈值作用于整张图像，而是对每一个单独的像素都使用一个单独的阈值，这属于自适应二值化的一种&lt;sup&gt;[3]&lt;/sup&gt;这个阈值通常来源于局部滤波器的输出。不同局部滤波器给出的阈值会产生非常不同的效果，而无论是哪一种阈值，最终的目的都是给出一个比较清晰而明确的边缘，通常这个效果比较容易达到，所以局部阈值化是一个不错的边缘检测子，本节将会说明如何实现一个局部阈值化的IP核。&lt;/p&gt;
&lt;h3&gt;3.12.1 原理&lt;/h3&gt;
&lt;p&gt;局部阈值化大致与全局阈值化相同，唯一不同的是其阈值并非静态而是随着像素的输入而变化的。对于流水线模式，这个动态的变换有两种处理方式，要么要求外部提供的像素和该像素对应的阈值是同步的，要么将同步做到模块的内部，内建缓存进行同步。考虑到对用户的便利性，本模块选择了第二种模式，提供额外的配置参数来确定缓存级数，并且由额外的使能信号来确定输出值。如图3-12-1，buffer是缓冲器，ConOut是输出计数器的输出值，当像素数据输入使能时计数器开始计数，直到阈值数据输入使能计数停止，并由当前的ConOut确定选择Buffer中的哪一个数据与输入的阈值进行比较，之后流水化输出。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;img alt=&amp;quot;图3-12-1 内建缓存流水线&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/12/1.png&amp;quot; /&gt;&lt;br /&gt;
图3-12-1 内建缓存流水线
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.12.2 设计&lt;/h3&gt;
&lt;p&gt;根据原理可知，ThresholdLocal核(以下简称THL核)还需要有一个用于确定从像素数据有效到阈值数据有效的最大周期的配置参数max_delay，一个阈值数据输入端口ref_data，阈值数据使能端口ref_enable，故一个THL核需要的配置参数和端口如表3-12-1与表3-12-2。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;work_mode&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;0为流水线模式，1为请求响应模式&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;工作模式&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_window_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;1 - 15&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;如果输入像素以窗口中心的形式输入，则是窗口宽度，否则为1。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;color_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;1 - 12&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;色彩位宽。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;max_delay&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;取决于可能的延迟&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;像素使能到阈值使能之间可能的最大延迟周期。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;max_delay_bits&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;取决于延迟周期&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;延迟周期的位宽。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-12-1 配置参数
&lt;/center&gt;&lt;br /&gt;
&lt;br&gt;
&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;端口&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;td&gt;clk&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;Clock.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rst_n&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;复位，低有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_enable&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据使能，在流水线模式下，它是另一个复位信号，在请求响应模式下，只有在它有效的时候in_data才会被真正地改变。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_data&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width * in_window_width * in_window_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据，必须和in_enable同步输入。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ref_enable&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;阈值数据使能。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ref_data&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;阈值数据，必须和ref_enable同步输入。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_ready&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据有效，在两种模式下，这个信号都会在out_data可以被读取的时候有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_data&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据，将会和out_ready同步输出。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-12-2 端口
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.12.3 实现&lt;/h3&gt;
&lt;p&gt;根据3.12.2的设计便可以实现一个THL核，流水线模式和请求响应模式实现如下。  &lt;/p&gt;
&lt;h4&gt;3.12.3.1 流水线模式&lt;/h4&gt;
&lt;p&gt;保证地址和数据同步流水化输出，在复位时二者皆输出为0，实现如下：&lt;br /&gt;
in_enable或rst_n为低时，系统复位，输出无效；否则阈值使能后1个周期第一个结果被输出，并且每一个clk的上升沿都会送入一个窗口进行处理，第一个输出有效之后，每一个周期都将送出一个结果，波形如图3-12-2。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-12-2 流水线模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/12/2.png&amp;quot; /&gt;&lt;br /&gt;
图3-12-2 流水线模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.12.3.2 请求响应模式&lt;/h4&gt;
&lt;p&gt;基本与3.12.3.1一致，但只有in_enable的上升沿时才会有窗口被输入，波形如图3-12-3。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-12-3 请求响应模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/12/3.png&amp;quot; /&gt;&lt;br /&gt;
图3-12-3 请求响应模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.12.3.3 IP核GUI&lt;/h4&gt;
&lt;p&gt;完成功能后对THL核进行了封装，封装如图3-12-4。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-12-4 THL核的GUI&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/12/4.png&amp;quot; /&gt;&lt;br /&gt;
图3-12-4 THL核的GUI
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.12.4 仿真&lt;/h3&gt;
&lt;p&gt;THL核只对灰度图有意义，我选取了两张灰度图像仿真源，分别测试了3x3和5x5窗口、均值滤波和中值滤波作为阈值来源的情况，原始图像如图3-12-5。&lt;/p&gt;
&lt;p&gt;&lt;center&gt; 
&lt;img alt=&amp;quot;图3-12-5 原始测试图像&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/12/5.png&amp;quot; /&gt;&lt;br /&gt;
图3-12-5 原始测试图像
&lt;/center&gt;  &lt;/p&gt;
&lt;p&gt;使用HDL功能仿真和软件仿真的结果进行PSNR的计算，仿真结果如图3-12-6所示。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-12-6 仿真结果&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/12/6.bmp&amp;quot; /&gt;&lt;br /&gt;
图3-12-6 仿真结果，从左上依次为流水线模式下的HDL功能仿真结果，请求响应模式下的HDL功能仿真结果，软件仿真结果
&lt;/center&gt;  &lt;/p&gt;
&lt;p&gt;仿真结果的清晰图像如图3-12-7与3-12-8，并且同样用软件仿真进行了大窗口的测试，结果如图3-12-9。可见无论是哪一种阈值来源，对边缘的保留都比3.4中基本的阈值化算法的效果要好，同时中值滤波作为阈值来源时边缘保留的更好，但不如均值滤波干净，而均值滤波的干净是以牺牲某些细节换来的，但对于突出主体的应用很有效。同时，窗口并非越大越好，也并非越小越好，应当根据需求适当选取。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;img alt=&amp;quot;图3-12-7 均值滤波来源阈值&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/12/7.png&amp;quot; /&gt;&lt;br /&gt;
图3-12-7 均值滤波来源阈值，左侧为3x3窗口，右侧为5x5窗口
&lt;/center&gt;&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-12-8 中值滤波来源阈值&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/12/8.png&amp;quot; /&gt;&lt;br /&gt;
图3-12-8 中值滤波来源阈值，左侧为3x3窗口，右侧为5x5窗口
&lt;/center&gt;&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-12-9 11x11的窗口滤波来源阈值&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/12/9.png&amp;quot; /&gt;&lt;br /&gt;
图3-12-9 11x11的窗口滤波来源阈值，左侧为均值滤波，右侧为中值滤波
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.12.5 资源和时序&lt;/h3&gt;
&lt;p&gt;最终实现与窗口大小关系很大，此处只对色彩位宽为8，最大延迟为8的状况分析，根据Vivado生成的报表，主要资源耗费如表3-12-3。&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;Slice LUTs*&lt;/th&gt;
&lt;th&gt;Slice Registers&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;39&lt;/td&gt;
&lt;td&gt;70&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-12-3 主要资源耗费
&lt;/center&gt;
&lt;br&gt;&lt;/p&gt;
&lt;p&gt;同时根据时序报告，最大的Data Path Delay(数据路径延迟)为3.187ns，即：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;FMax = 313.77MHz  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;即说明，THL核在流水线模式下，理论上在处理1080p全高清图像时可以达到151帧。&lt;br /&gt;
由于数据路径延迟和应用的最终约束设置强相关，所以仅供参考。&lt;/p&gt;
&lt;h3&gt;3.12.6 分析与结论&lt;/h3&gt;
&lt;p&gt;PSNR如表3-12-5。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;1-3-mean&lt;/th&gt;
&lt;th&gt;1-3-mid&lt;/th&gt;
&lt;th&gt;1-5-mean&lt;/th&gt;
&lt;th&gt;1-5-mid&lt;/th&gt;
&lt;th&gt;2-3-mean&lt;/th&gt;
&lt;th&gt;2-3-mid&lt;/th&gt;
&lt;th&gt;2-5-mean&lt;/th&gt;
&lt;th&gt;2-5-mid&lt;/th&gt;
&lt;th&gt;Total&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-12-5 PSNR
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;PSNR均值为最大值，THL核与软件等效，同时可以达到不错的FMax，设计成功。&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;参考文献&lt;/h1&gt;
&lt;p&gt;[3] Donald G.Bailey.基于FPGA的嵌入式图像处理系统设计[M].原魁,何文浩,肖晗译.北京:电子工业出版社,2013  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;感谢&lt;/h1&gt;
&lt;p&gt;仿真图像来源：&lt;br /&gt;
&lt;a href=&amp;quot;http://www.pixiv.net/member_illust.php?mode=medium&amp;amp;illust_id=10599535&amp;quot;&gt;H2SO4-蝶&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;http://www.pixiv.net/member_illust.php?mode=medium&amp;amp;illust_id=28762438&amp;quot;&gt;パセリ-Favour&lt;/a&gt;  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Friday, 22 May 2015 23:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.05.22 23:00:article/Skill-2015_05_22_a</guid>
<category>FPGA</category>
<category>FPGA-Imaging-Library</category>
<category>图像处理</category>
<category>局部阈值化</category>
<category>自动阈值化</category>
</item>

<item>
<title>【FPGA/图像处理】局部滤波器-排序滤波器</title>
<link>http://dtysky.moe/article/Skill-2015_05_21_a</link>
<description>&lt;p&gt;图像处理理系列文章基本都是我本科毕设论文同步发布，这个项目以LGPL许可被发布在&lt;a href=&amp;quot;http://fil.dtysky.moe&amp;quot;&gt;F-I-L&lt;/a&gt;。排序滤波器是一种非线性局部滤波器，它首先将窗口内的每个像素根据色彩的值进行排序，随后根据所给的序号得出最后的结果。排序滤波器的应用十分广泛，其中最常用的是中值滤波器，即为序号为窗口总大小的一半时的情况，此时滤波的结果是原窗口所有像素的中值，除此之外常用的还有极值滤波器，即得出窗口中的极大值或者极小值。排序滤波器常被用于去除椒盐噪声，或者作为后续处理的预处理，相比于均值滤波，排序滤波器能够比较好得保留边界特征。本节将会介绍如何用FPGA实现排序滤波器。&lt;br /&gt;
这个IP核的资源在这里：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/FPGA-Imaging-Library/tree/Publish/LocalFilter/RankFilter&amp;quot;&gt;RankFilter&lt;/a&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;3 算法实现&lt;/h1&gt;
&lt;p&gt;明确了设计和架构，便可以进行算法的实现。本章将会说明如何实现图像处理的算法，以及如何运用它们。  &lt;/p&gt;
&lt;h2&gt;3.11 局部滤波器-排序滤波器&lt;/h2&gt;
&lt;p&gt;排序滤波器是一种非线性局部滤波器，它首先将窗口内的每个像素根据色彩的值进行排序，随后根据所给的序号得出最后的结果。排序滤波器的应用十分广泛，其中最常用的是中值滤波器，即为序号为窗口总大小的一半时的情况，此时滤波的结果是原窗口所有像素的中值，除此之外常用的还有极值滤波器，即得出窗口中的极大值或者极小值。排序滤波器常被用于去除椒盐噪声&lt;sup&gt;[16]&lt;/sup&gt;，或者作为后续处理的预处理，相比于均值滤波，排序滤波器能够比较好得保留边界特征。本节将会介绍如何用FPGA实现排序滤波器。&lt;/p&gt;
&lt;h3&gt;3.11.1 原理&lt;/h3&gt;
&lt;p&gt;排序滤波器的基本原理见图3-11-1，将像素作为基本元素，一个窗口可以看做是一个列表，对于一个3x3的均值滤波器，列表大小为9。排序滤波器列表中所有的元素进行排序，随后根据序号“rank”得出结果，即式3-11-1所示，之后用这个结果来取代窗口的中心像素点。故可知，排序滤波器的核心在于给一个列表的所有元素进行排序。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-11-1 排序滤波器原理&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/11/1.png&amp;quot; /&gt;&lt;br /&gt;
图3-11-1 排序滤波器原理
&lt;/center&gt;  &lt;/p&gt;
&lt;p&gt;$$Q=sorted(I)[rank]\ \ \ \ \ \ \ \ (3-11-1)$$&lt;/p&gt;

&lt;p&gt;传统的软件排序算法有许多种，例如冒泡排序、插入排序、选择排序等[17]，这些算法有一个共同的特点是在最好的状况下时间复杂度为O(n)，同时根据不同状况需求的排序次数不定，对于FPGA而言这是不可接受的。用FPGA实现时，在流水线模式下，必须要保证排序次数对于拥有任何元素的列表都是确定的，并且由于直到第一个结果输出前每次都要保留整个列表的元素，如果排序周期过长，所造成的资源消耗是巨大的，所以必须找到一个周期确定并且非常快速的排序算法。  &lt;/p&gt;
&lt;h4&gt;3.11.1.1 使用奇偶互换网络的冒泡排序&lt;/h4&gt;
&lt;p&gt;一种排序算法是使用奇偶互换网络的冒泡排序&lt;sup&gt;[3]&lt;/sup&gt;，它在冒泡排序的基础上进行改进，如图3-11-2所示，这是一个大小为9的列表进行排序的结构，每个周期都进行若干次两两排序，并将首或者尾元素进行保留，在9个周期后得到结果，可见，这种排序结构可以保证对于任何周期的排序周期一定，但时间复杂度仍然为O(n)，这意味着对于n个元素的列表，需要经过n个周期才能得到排序的结果，并且需要消耗$n^2$个像素的寄存器资源，当n值变大时这个消耗往往是难以接受的。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;img alt=&amp;quot;图3-11-2 使用奇偶互换网络的冒泡排序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/11/2.png&amp;quot; /&gt;&lt;br /&gt;
图3-11-2 使用奇偶互换网络的冒泡排序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.11.1.2 基于3点比较器的排序&lt;/h4&gt;
&lt;p&gt;另一种适用于FPGA的排序算法是基于3点比较器的排序&lt;sup&gt;[18]&lt;/sup&gt;，它的原理如图3-11-3所示，可见，这实际上是对3.11.1.1中算法的改进，对于大小为9的列表，它将排序周期从9减少到了3，大大提升了效率。但这种排序算法适用性太为狭窄，基本只可能用于3x3的窗口，考虑到本库的通用性，无法满足需求。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;img alt=&amp;quot;图3-11-3 基于3点比较器的排序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/11/3.png&amp;quot; /&gt;&lt;br /&gt;
图3-11-3 基于3点比较器的排序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.11.1.3 并行全比较排序&lt;/h4&gt;
&lt;p&gt;适用于本库的排序算法为并行全比较排序&lt;sup&gt;[19]&lt;/sup&gt;，其基本原理如图3-11-4所示，首先为列表中的每个元素建立一个比较计数寄存器，在第一个周期将此元素和其他的元素进行并行比较，将比较的结果写入比较计数寄存器的对应位，之后对每一次寄存器中“1”的个数进行统计，统计得到的结果即为该元素经过排序后的序号。&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;img alt=&amp;quot;图3-11-4 并行全比较排序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/11/4.png&amp;quot; /&gt;&lt;br /&gt;
图3-11-4 并行全比较排序
&lt;/center&gt;  &lt;/p&gt;
&lt;p&gt;可见，此排序算法的核心是两次统计，第一次是某一元素和其他元素大小关系的统计，第二次是比其他元素大的情况的次数统计。第一次统计可以用一个二维数组来实现，但有一个边界的问题，就是如果出现相同的元素该如何处理&lt;sup&gt;[20]&lt;/sup&gt;，这里的处理方式是原序号在前优先的原则，序号为0的元素和所有元素进行比较，根据比较结果修改比较计数寄存器0的第n位和比较计数寄存器n的第0位，而后序号为1的元素便不和序号为0的元素进行比较，只比教剩下的元素，将比较计数寄存器定义为二维数组big_flag，则代码如下：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;no&amp;quot;&gt;`full_win_width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;begin&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;always&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;@(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;posedge&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;clk&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;big_flag&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;][&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;end&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;no&amp;quot;&gt;`full_win_width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;begin&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;j&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;j&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;no&amp;quot;&gt;`full_win_width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;j&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;j&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;begin&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;always&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;@(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;posedge&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;clk&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;begin&lt;/span&gt;
            &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;reg_in_data&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;reg_in_data&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;j&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;])&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;begin&lt;/span&gt;
                &lt;span class=&amp;quot;n&amp;quot;&gt;big_flag&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;][&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;j&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
                &lt;span class=&amp;quot;n&amp;quot;&gt;big_flag&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;j&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;][&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
            &lt;span class=&amp;quot;k&amp;quot;&gt;end&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;begin&lt;/span&gt; 
                &lt;span class=&amp;quot;n&amp;quot;&gt;big_flag&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;][&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;j&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
                &lt;span class=&amp;quot;n&amp;quot;&gt;big_flag&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;j&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;][&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
            &lt;span class=&amp;quot;k&amp;quot;&gt;end&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;end&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;end&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;第二个实质上是若干次的加法，加法的个数由列表的大小确定，考虑到每个相加的数据位数均为1，所以一个周期内的多次加法是可以被接受的，但由于FMax的权衡，仍然要进行同3.10中一样的加法分级拆分，在若干次试验之后，最终将这个次数定为了8次，即一个周期内可以进行八次加法，由此，加法的级数由公式3-11-2确定。  &lt;/p&gt;
&lt;p&gt;综上，此排序的时间复杂度为O(log8(n)，加上第一次的统计时间，本模块最终的时间复杂度和空间复杂度如式3-11-3和3-11-4所示。  &lt;/p&gt;
&lt;p&gt;$$O(n)=\lfloor{log_8(n - 1)}\rfloor + 2\ \ \ \ \ \ \ \ (3-11-2)$$&lt;/p&gt;

&lt;p&gt;&lt;p&gt;$$O(n)=n * (\lfloor{log8(n - 1)}\rfloor + 2) * ColorWidth\ \ \ \ \ \ \ \ (3-11-3)$$&lt;/p&gt;  &lt;/p&gt;
&lt;p&gt;最终的排序查找输出相当于利用一个RAM完成，这之前需要使用一个编码器来完成最终序号的查找，编码器的输入位数为窗口的全大小，当窗口宽度大于5时综合器无法实现，所以此模块的窗口宽度被限定为2-5。  &lt;/p&gt;
&lt;h3&gt;3.11.2 设计&lt;/h3&gt;
&lt;p&gt;根据原理可知，RankFilter核(以下简称RF核)核心是两次统计，它们分别由二维数组和分级加法实现。加法的分级可以使用verilog中的generate语句来批量生成，生成的流水线级数依赖于根据窗口宽度计算得出的某个参数，我将此参数命名为sum_stage。另外，还需要一个rank端口来确定输出的序号值。综上，一个RF核需要的配置参数和端口如表3-11-1与表3-11-2。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;work_mode&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;0为流水线模式，1为请求响应模式&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;工作模式&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;window_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;2 - 15&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;窗口的宽度和高度。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;color_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;1 - 12&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;色彩位宽。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sum_stage&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;取决于窗口宽度，为int(log8(window_width^2)) + 1。&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;加法级数。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;full_win_bits&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;取决于窗口大小&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;窗口总大小的位宽。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-11-1 配置参数
&lt;/center&gt;&lt;br /&gt;
&lt;br&gt;
&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;端口&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;td&gt;clk&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;Clock.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rst_n&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;复位，低有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rank&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;full_win_bits - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出序号，如果是整个窗口大小的一半，模块工作为中值滤波器，等等。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_enable&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据使能，在流水线模式下，它是另一个复位信号，在请求响应模式下，只有在它有效的时候in_data才会被真正地改变。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_data&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width * window_width * window_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据，必须和in_enable同步输入。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_ready&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据有效，在两种模式下，这个信号都会在out_data可以被读取的时候有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_data&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据，将会和out_ready同步输出。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-11-2 端口
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.11.3 实现&lt;/h3&gt;
&lt;p&gt;根据3.11.2的设计便可以实现一个RF核，流水线模式和请求响应模式实现如下。  &lt;/p&gt;
&lt;h4&gt;3.11.3.1 流水线模式&lt;/h4&gt;
&lt;p&gt;保证地址和数据同步流水化输出，在复位时二者皆输出为0，实现如下：&lt;br /&gt;
in_enable或rst_n为低时，系统复位，输出无效；否则加法级数加2个周期后第一个结果被输出，并且每一个clk的上升沿都会送入一个窗口进行处理，第一个输出有效之后，每一个周期都将送出一个结果，波形如图3-11-5。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-11-5 流水线模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/11/5.png&amp;quot; /&gt;&lt;br /&gt;
图3-11-5 流水线模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.11.3.2 请求响应模式&lt;/h4&gt;
&lt;p&gt;基本与3.11.3.1一致，但只有in_enable的上升沿时才会有窗口被输入，波形如图3-11-6。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-11-6 请求响应模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/11/6.png&amp;quot; /&gt;&lt;br /&gt;
图3-11-6 请求响应模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.11.3.3 IP核GUI&lt;/h4&gt;
&lt;p&gt;完成功能后对RF核进行了封装，封装如图3-11-7。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-11-7 RF核的GUI&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/11/7.png&amp;quot; /&gt;&lt;br /&gt;
图3-11-7 RF核的GUI
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.11.4 仿真&lt;/h3&gt;
&lt;p&gt;RF核虽然对于所有图像都有意义，但一般用于灰度图像的处理，所以我选取了两张灰度图像仿真源，并且考虑到仿真压力，仅测试了宽度为3和5的窗口，分别为中值和极大值的情况，原始图像如图3-11-8。&lt;/p&gt;
&lt;p&gt;&lt;center&gt; 
&lt;img alt=&amp;quot;图3-11-8 原始测试图像&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/11/8.jpg&amp;quot; /&gt;&lt;br /&gt;
图3-11-8 原始测试图像
&lt;/center&gt;  &lt;/p&gt;
&lt;p&gt;使用HDL功能仿真和软件仿真的结果进行PSNR的计算，仿真结果如图3-11-9所示。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-11-9 仿真结果&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/11/9.jpg&amp;quot; /&gt;&lt;br /&gt;
图3-11-9 仿真结果，从左上依次为流水线模式下的HDL功能仿真结果，请求响应模式下的HDL功能仿真结果，软件仿真结果
&lt;/center&gt;  &lt;/p&gt;
&lt;p&gt;仿真结果的清晰图像如图3-11-10、3-11-11与3-11-12，可见中值滤波器的确可以在模糊的同时保留边缘，并且窗口越大越模糊，而极值滤波器将会损失更多细节，但能够让边缘更加明晰。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;img alt=&amp;quot;图3-11-10 3x3窗口的中值滤波结果&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/11/10.jpg&amp;quot; /&gt;&lt;br /&gt;
图3-11-10 3x3窗口的中值滤波结果
&lt;/center&gt;&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-11-11 5x5窗口的中值滤波结果&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/11/11.jpg&amp;quot; /&gt;&lt;br /&gt;
图3-11-11 5x5窗口的中值滤波结果
&lt;/center&gt;&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-11-12 3x3窗口的极值滤波结果&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/11/12.jpg&amp;quot; /&gt;&lt;br /&gt;
图3-11-12 3x3窗口的极值滤波结果
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.11.5 资源和时序&lt;/h3&gt;
&lt;p&gt;最终实现与窗口大小关系很大，此处只对色彩位宽为8，窗口大小为3和5时的情况进行分析，根据Vivado生成的报表，主要资源耗费如表3-11-3。&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;窗口大小&lt;/th&gt;
&lt;th&gt;Slice LUTs*&lt;/th&gt;
&lt;th&gt;Slice Registers&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3x3&lt;/td&gt;
&lt;td&gt;377&lt;/td&gt;
&lt;td&gt;228&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5x5&lt;/td&gt;
&lt;td&gt;2715&lt;/td&gt;
&lt;td&gt;1262&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-11-3 主要资源耗费
&lt;/center&gt;
&lt;br&gt;&lt;/p&gt;
&lt;p&gt;同时根据时序报告，3x3窗口下最大的Data Path Delay(数据路径延迟)为3.705ns，即：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;FMax = 269.90MHz  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;即说明，RF核在流水线模式下，理论上在处理1080p全高清图像时可以达到130帧。 &lt;/p&gt;
&lt;p&gt;5x5窗口下最大的Data Path Delay(数据路径延迟)为6.196ns，即：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;FMax = 161.39MHz  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;即说明，RF核在流水线模式下，理论上在处理1080p全高清图像时可以达到77帧。&lt;br /&gt;
可见二者的资源耗费可以接受，但FMax均不理想，这是由于最后的输出实际上实现的是一个读延迟周期的分布式RAM，这种RAM的FMax本身就比较低，并且可以加上一级流水线进行优化，便可以大大提升FMax，考虑时效问题，此处暂且不再进行讨论。 
由于数据路径延迟和应用的最终约束设置强相关，所以仅供参考。&lt;/p&gt;
&lt;h3&gt;3.11.6 分析与结论&lt;/h3&gt;
&lt;p&gt;PSNR如表3-11-5。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;1-3-4&lt;/th&gt;
&lt;th&gt;1-3-8&lt;/th&gt;
&lt;th&gt;1-5-12&lt;/th&gt;
&lt;th&gt;1-5-24&lt;/th&gt;
&lt;th&gt;2-3-4&lt;/th&gt;
&lt;th&gt;2-3-8&lt;/th&gt;
&lt;th&gt;2-5-12&lt;/th&gt;
&lt;th&gt;2-5-24&lt;/th&gt;
&lt;th&gt;Total&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-11-5 PSNR
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;PSNR均值为最大值，RF核与软件等效，同时资源消耗可以接受，设计成功。&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;参考文献&lt;/h1&gt;
&lt;p&gt;[3] Donald G.Bailey.基于FPGA的嵌入式图像处理系统设计[M].原魁,何文浩,肖晗译.北京:电子工业出版社,2013&lt;br /&gt;
[17] 达文姣,任志国,王龙平等.静态链表上排序算法的研究[J].自动化与仪器仪表,2011,(2):12-14.DOI:10.3969/j.issn.1001-9227.2011.02.006.&lt;br /&gt;
[18] 李新春,赵璐.基于中值滤波算法滤波器的FPGA实现[J].计算机系统应用,2011,20(9):82-85,72.DOI:10.3969/j.issn.1003-3254.2011.09.018.&lt;br /&gt;
[19] 师廷伟,金长江.基于FPGA的并行全比较排序算法[J].数字技术与应用,2013,(10):126-127.&lt;br /&gt;
[20] 李飞飞,刘伟宁,王艳华等.改进的中值滤波算法及其FPGA快速实现[J].计算机工程,2009,35(14):175-177.DOI:10.3969/j.issn.1000-3428.2009.14.061.&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;感谢&lt;/h1&gt;
&lt;p&gt;仿真图像来源：&lt;br /&gt;
&lt;a href=&amp;quot;http://www.pixiv.net/member_illust.php?mode=medium&amp;amp;illust_id=27054426&amp;quot;&gt;LM7-.410&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;http://www.pixiv.net/member_illust.php?mode=medium&amp;amp;illust_id=28580798&amp;quot;&gt;色原みたび-夏空間&lt;/a&gt;  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Thursday, 21 May 2015 23:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.05.21 23:00:article/Skill-2015_05_21_a</guid>
<category>FPGA</category>
<category>FPGA-Imaging-Library</category>
<category>图像处理</category>
<category>中值滤波</category>
<category>快速排序</category>
</item>

<item>
<title>【FPGA/图像处理】局部滤波器-均值滤波器</title>
<link>http://dtysky.moe/article/Skill-2015_05_20_b</link>
<description>&lt;p&gt;图像处理理系列文章基本都是我本科毕设论文同步发布，这个项目以LGPL许可被发布在&lt;a href=&amp;quot;http://fil.dtysky.moe&amp;quot;&gt;F-I-L&lt;/a&gt;。均值滤波器是局部滤波器的一种，又称为平滑线性滤波器&lt;sup&gt;[15]&lt;/sup&gt;，它通常用于去除图像中高斯噪声&lt;sup&gt;[16]&lt;/sup&gt;，其具有对图像的模糊效果。均值滤波器有两种，一种是算术均值滤波器，一种是加权均值滤波器，前者可以看做是后者的特例，但考虑到加权均值滤波需要消耗大量乘法运算，并且除法运算难以避免，对FPGA并不友好，故这里只讨论算术均值滤波器的实现，本节将说明如何用FPGA实现一个均值滤波器。&lt;br /&gt;
这个IP核的资源在这里：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/FPGA-Imaging-Library/tree/Publish/LocalFilter/MeanFilter&amp;quot;&gt;MeanFilter&lt;/a&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;3 算法实现&lt;/h1&gt;
&lt;p&gt;明确了设计和架构，便可以进行算法的实现。本章将会说明如何实现图像处理的算法，以及如何运用它们。  &lt;/p&gt;
&lt;h2&gt;3.10 局部滤波器-均值滤波器&lt;/h2&gt;
&lt;p&gt;均值滤波器是局部滤波器的一种，又称为平滑线性滤波器&lt;sup&gt;[15]&lt;/sup&gt;，它通常用于去除图像中高斯噪声&lt;sup&gt;[16]&lt;/sup&gt;，其具有对图像的模糊效果。均值滤波器有两种，一种是算术均值滤波器，一种是加权均值滤波器，前者可以看做是后者的特例，但考虑到加权均值滤波需要消耗大量乘法运算，并且除法运算难以避免，对FPGA并不友好，故这里只讨论算术均值滤波器的实现，本节将说明如何用FPGA实现一个均值滤波器。&lt;/p&gt;
&lt;h3&gt;3.10.1 原理&lt;/h3&gt;
&lt;p&gt;算术均值滤波器的基本原理见图3-10-1，这是一个3x3的均值滤波器，它对窗口中所有的像素求和，之后除以像素个数，最终得到的是整个窗口中像素的算术平均值，即式3-10-1所示，之后用这个均值来取代窗口的中心像素点。所以可知，一次均值运算需要若干次加法和一次除法，在FPGA中一个周期内实现这么多次加法对于FMax很不友好，所以需要考虑将其拆分，同时除法运算如果可以避免尽量避免，因为除法消耗的资源比较多，如果不使用DSP其对FMax的影响也会比较大。综上，本设计应当重点关注加法分段和除法替代的算法。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-10-1 算术均值滤波器原理&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/10/1.png&amp;quot; /&gt;&lt;br /&gt;
图3-10-1 算术均值滤波器原理
&lt;/center&gt;  &lt;/p&gt;
&lt;p&gt;$$Q=\frac{1}{n^2}\sum_{i=0}^{n^2}I_i\ \ \ \ \ \ \ \ (3-10-1)$$&lt;/p&gt;

&lt;h4&gt;3.10.1.1 加法&lt;/h4&gt;
&lt;p&gt;将像素视为基本元素，一个窗口可以看作是从左上到右下的元素构成的一个列表。当N为偶数时，列表中的N个元素的一次求和运算，可以用若干级求和运算来代替，每一级求和运算由若干组的两个元素的求和运算组成，每一级的求和运算次数为$\frac{n}2$、$\frac{n}{2^2}$、$\frac{n}{2^3}$......直到某一级的运算次数为1，该级的这唯一的运算结果便是列表中所有元素的和，此时求和结束。当N为奇数时，只需要让列表中的最后一个元素参与到第一组加法中即可，图3-10-2演示了3x3的窗口的求和过程。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;img alt=&amp;quot;图3-10-2 3x3窗口求和&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/10/2.png&amp;quot; /&gt;&lt;br /&gt;
图3-10-2 3x3窗口求和
&lt;/center&gt;  &lt;/p&gt;
&lt;p&gt;可见，一个N个元素的列表求和需要分为的级数S如式3-10-2。  &lt;/p&gt;
&lt;p&gt;$$Q=\log_2N\ \ \ \ \ \ \ \ (3-10-2)$$&lt;/p&gt;

&lt;h4&gt;3.10.1.2 除法&lt;/h4&gt;
&lt;p&gt;由于本库限定窗口大小为2-15，个数不多，所以除法运算替代为若干次的移位相加操作是可以接受的。综合考虑到FPGA加法操作的延迟和运算消耗的周期，以三次移位两次加法为上限，同时为了防止最终的运算结果溢出，需要保证使用替代后的算法计算出来的结果不大于标准操作的结果，经过试验和删选，最终确定的替代算法如下：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;case&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;window_width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;mh&amp;quot;&gt;2&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;reg_out_data&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;sum_all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;mh&amp;quot;&gt;3&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;reg_out_data&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sum_all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sum_all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;5&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sum_all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;6&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;mh&amp;quot;&gt;4&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;reg_out_data&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;sum_all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;mh&amp;quot;&gt;5&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;reg_out_data&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sum_all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;5&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sum_all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;7&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sum_all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;10&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;mh&amp;quot;&gt;6&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;reg_out_data&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sum_all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;6&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sum_all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;7&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sum_all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;8&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;mh&amp;quot;&gt;7&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;reg_out_data&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sum_all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;6&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sum_all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;8&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sum_all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;10&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;mh&amp;quot;&gt;8&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;reg_out_data&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;sum_all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;6&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;mh&amp;quot;&gt;9&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;reg_out_data&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sum_all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;7&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sum_all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;8&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sum_all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;11&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;mh&amp;quot;&gt;10&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;reg_out_data&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sum_all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;7&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sum_all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;9&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sum_all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;13&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;mh&amp;quot;&gt;11&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;reg_out_data&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sum_all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;7&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sum_all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;12&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sum_all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;13&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;mh&amp;quot;&gt;12&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;reg_out_data&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sum_all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;8&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sum_all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;9&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sum_all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;10&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;mh&amp;quot;&gt;13&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;reg_out_data&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sum_all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;8&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sum_all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;9&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sum_all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;14&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;mh&amp;quot;&gt;14&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;reg_out_data&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sum_all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;8&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sum_all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;10&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sum_all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;12&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;mh&amp;quot;&gt;15&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;reg_out_data&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sum_all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;8&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sum_all&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;11&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;default&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;cm&amp;quot;&gt;/* default */&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;endcase&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;使用Python进行软件测试，每个窗口大小所对应的误差为如表3-10-1。可见最大误差在2%左右，对于此应用可以接受。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;窗口宽度&lt;/th&gt;
&lt;th&gt;误差&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;0.00%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;2.35%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;0.00%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;0.39%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;2.35%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;0.00%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;0.00%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;1.57%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;1.57%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;1.57%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;2.35%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;0.39%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;0.00%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;1.18%&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-10-1 除法误差
&lt;/center&gt;&lt;/p&gt;
&lt;h3&gt;3.10.2 设计&lt;/h3&gt;
&lt;p&gt;根据原理可知，MeanFilter核(以下简称MF核)核心是若干级的加法和最后的除法，加法的分级可以使用verilog中的generate语句来批量生成，生成的流水线级数依赖于根据窗口宽度计算得出的某个参数，我将此参数命名为sum_stage。综上，一个MF核需要的配置参数和端口如表3-10-2与表3-10-3。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;work_mode&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;0为流水线模式，1为请求响应模式&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;工作模式&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;window_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;2 - 15&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;窗口的宽度和高度。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;color_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;1 - 12&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;色彩位宽。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sum_stage&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;取决于窗口宽度，为log2(window_width^2)。&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;加法级数。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-10-2 配置参数
&lt;/center&gt;&lt;br /&gt;
&lt;br&gt;
&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;端口&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;td&gt;clk&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;Clock.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rst_n&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;复位，低有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_enable&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据使能，在流水线模式下，它是另一个复位信号，在请求响应模式下，只有在它有效的时候in_data才会被真正地改变。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_data&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width * window_width * window_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据，必须和in_enable同步输入。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_ready&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据有效，在两种模式下，这个信号都会在out_data可以被读取的时候有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_data&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据，将会和out_ready同步输出。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-10-3 端口
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.10.3 实现&lt;/h3&gt;
&lt;p&gt;根据3.10.2的设计便可以实现一个MF核，流水线模式和请求响应模式实现如下。  &lt;/p&gt;
&lt;h4&gt;3.10.3.1 流水线模式&lt;/h4&gt;
&lt;p&gt;保证地址和数据同步流水化输出，在复位时二者皆输出为0，实现如下：&lt;br /&gt;
in_enable或rst_n为低时，系统复位，输出无效；否则加法级数加2个周期后第一个结果被输出，并且每一个clk的上升沿都会送入一个窗口进行处理，第一个输出有效之后，每一个周期都将送出一个结果，波形如图3-10-3。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-10-3 流水线模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/10/3.png&amp;quot; /&gt;&lt;br /&gt;
图3-10-3 流水线模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.10.3.2 请求响应模式&lt;/h4&gt;
&lt;p&gt;基本与3.10.3.1一致，但只有in_enable的上升沿时才会有窗口被输入，波形如图3-10-3。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-10-4 请求响应模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/10/4.png&amp;quot; /&gt;&lt;br /&gt;
图3-10-4 请求响应模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.10.3.3 IP核GUI&lt;/h4&gt;
&lt;p&gt;完成功能后对MF核进行了封装，封装如图3-10-5。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-10-5 MF核的GUI&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/10/5.png&amp;quot; /&gt;&lt;br /&gt;
图3-10-5 MF核的GUI
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.10.4 仿真&lt;/h3&gt;
&lt;p&gt;MF核虽然对于所有图像都有意义，但一般用于灰度图像的处理，所以我选取了两张灰度图像仿真源，并且考虑到仿真压力，仅测试了宽度为3和5的窗口，原始图像如图3-10-6。&lt;/p&gt;
&lt;p&gt;&lt;center&gt; 
&lt;img alt=&amp;quot;图3-10-6 原始测试图像&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/10/6.jpg&amp;quot; /&gt;&lt;br /&gt;
图3-10-6 原始测试图像
&lt;/center&gt;  &lt;/p&gt;
&lt;p&gt;使用HDL功能仿真和软件仿真的结果进行PSNR的计算，仿真结果如图3-10-7所示。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-10-7 仿真结果&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/10/7.jpg&amp;quot; /&gt;&lt;br /&gt;
图3-10-7 仿真结果，左侧为流水线模式下的HDL功能仿真结果，中间为请求响应模式下的HDL功能仿真结果，右侧为软件仿真结果
&lt;/center&gt;  &lt;/p&gt;
&lt;p&gt;仿真结果的清晰图像如图3-10-8与3-10-9，可见均值滤波的确有模糊图像的效果，窗口越大越明显，同时需要注意由于使用了窗口，所以图像出现了缺行现象，在实际使用时可以在写入帧缓存时利用row_init参数来配置初始写入行，当系统实际运行时，从第二张图像开始这个问题将会被自动弥补。&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;img alt=&amp;quot;图3-10-8 3x3窗口的滤波结果&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/10/8.jpg&amp;quot; /&gt;&lt;br /&gt;
图3-10-8 3x3窗口的滤波结果
&lt;/center&gt;&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-10-9 5x5窗口的滤波结果&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/10/9.jpg&amp;quot; /&gt;&lt;br /&gt;
图3-10-9 5x5窗口的滤波结果
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.10.5 资源和时序&lt;/h3&gt;
&lt;p&gt;最终实现与窗口大小关系很大，色彩位宽为8，窗口大小为3时的情况进行分析，根据Vivado生成的报表，主要资源耗费如表3-10-4。&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;Slice LUTs*&lt;/th&gt;
&lt;th&gt;Slice Registers&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;83&lt;/td&gt;
&lt;td&gt;78&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-10-4 主要资源耗费
&lt;/center&gt;
&lt;br&gt;&lt;/p&gt;
&lt;p&gt;同时由于根据时序报告，最大的Data Path Delay(数据路径延迟)为2.085ns，即：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;FMax = 479.61MHz  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;即说明，MF核在流水线模式下，理论上在处理1080p全高清图像时可以达到231帧。&lt;br /&gt;
由于数据路径延迟和应用的最终约束设置强相关，所以仅供参考。&lt;/p&gt;
&lt;h3&gt;3.10.6 分析与结论&lt;/h3&gt;
&lt;p&gt;PSNR如表3-10-5。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;1-3&lt;/th&gt;
&lt;th&gt;1-5&lt;/th&gt;
&lt;th&gt;2-3&lt;/th&gt;
&lt;th&gt;2-5&lt;/th&gt;
&lt;th&gt;Total&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-10-5 PSNR
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;PSNR均值为最大值，MF核与软件等效，同时可以达到很高的FMax，设计成功。&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;参考文献&lt;/h1&gt;
&lt;p&gt;[15] Rafael C. Gonzales, Richard E. Woods.数字图像处理(第三版)[M].阮秋琦,阮宇智等译.北京:电子工业出版社,2011.6&lt;br /&gt;
[16] 关新平,赵立兴,唐英干等.图像去噪混合滤波方法[J].中国图象图形学报,2005,10(3):332-337.DOI:10.3969/j.issn.1006-8961.2005.03.013.)&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;感谢&lt;/h1&gt;
&lt;p&gt;仿真图像来源：&lt;br /&gt;
&lt;a href=&amp;quot;http://www.pixiv.net/member_illust.php?mode=medium&amp;amp;illust_id=39186698&amp;quot;&gt;おにねこ-クロス.ホエン&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;http://www.pixiv.net/member_illust.php?mode=medium&amp;amp;illust_id=49171367&amp;quot;&gt;月岡月穂-春爛漫&lt;/a&gt;  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Wednesday, 20 May 2015 23:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.05.20 23:00:article/Skill-2015_05_20_b</guid>
<category>FPGA</category>
<category>FPGA-Imaging-Library</category>
<category>图像处理</category>
<category>均值滤波</category>
</item>

<item>
<title>【FPGA/图像处理】生成器-窗口生成器</title>
<link>http://dtysky.moe/article/Skill-2015_05_20_a</link>
<description>&lt;p&gt;图像处理理系列文章基本都是我本科毕设论文同步发布，这个项目以LGPL许可被发布在&lt;a href=&amp;quot;http://fil.dtysky.moe&amp;quot;&gt;F-I-L&lt;/a&gt;。窗口生成器跟在行缓存生成器之后，负责将行缓存生成的每一列扩展成以某个像素为中心、以这个像素的邻域为填充的窗口，以供后续的模块进行处理。原则上，窗口的生成可以在每个局部滤波器模块的内部生成，但考虑到可能有若干个模块复用同一个窗口，综合复用性和资源成本的考量后，我选择用这样的一个生成器来作为行缓存和处理模块的连接层，本节将介绍如何构造一个窗口生成器。&lt;br /&gt;
这个IP核的资源在这里：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/FPGA-Imaging-Library/tree/Publish/Generator/WindowGenerator&amp;quot;&gt;WindowGenerator&lt;/a&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;3 算法实现&lt;/h1&gt;
&lt;p&gt;明确了设计和架构，便可以进行算法的实现。本章将会说明如何实现图像处理的算法，以及如何运用它们。  &lt;/p&gt;
&lt;h2&gt;3.9 生成器-窗口生成器&lt;/h2&gt;
&lt;p&gt;窗口生成器跟在行缓存生成器之后，负责将行缓存生成的每一列扩展成以某个像素为中心、以这个像素的邻域为填充的窗口，以供后续的模块进行处理。原则上，窗口的生成可以在每个局部滤波器模块的内部生成，但考虑到可能有若干个模块复用同一个窗口，综合复用性和资源成本的考量后，我选择用这样的一个生成器来作为行缓存和处理模块的连接层，本节将介绍如何构造一个窗口生成器。&lt;/p&gt;
&lt;h3&gt;3.9.1 原理&lt;/h3&gt;
&lt;p&gt;如图3-9-1中所示，窗口生成器接受包含若干行像素的一列，输出整个窗口的像素，并且窗口应当包含当前操作的中心像素以及其邻域像素。所以需要构造一个二维数组，数组的每一维都包含了一行，初始状态下窗口中的每个像素均为0，之后最左侧的一列不断接收外部数据，而每行的各个像素之间不断进行移位操作，以此来构造一个在图像上不断滑动的窗口。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;img alt=&amp;quot;图3-9-1 窗口的原理&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/9/1.png&amp;quot; /&gt;&lt;br /&gt;
图3-9-1 窗口的原理
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.9.2 设计&lt;/h3&gt;
&lt;p&gt;根据原理可知，WindowGenerator核(以下简称WG核)核心是一个二维数组每一维的像素之间的移位操作。考虑对于NxN的窗口，只有当第一个在图像上的像素作为窗口中心时才能够真正的输出，所以在N/2个周期之前，窗口的输出应该是无效的。但即使在无效的时候，窗口仍然需要外部输入数据来保证其继续被填充到有效，这和其他模块的设计不同，必须采用专用的设计来完成。考虑流水线模式，每一个周期模块接收外部送入的一列进行处理，由于移位是连续的，所以当第一个窗口填充完毕后输出有效，这和一般的流水线模式基本一致，而对于请求响应模式则不同，这种模式下输入数据受到输入使能的控制，移位是不连续的，所以当第一个窗口被填充完毕、窗口输出有效之前，必须有一个信号通知外部需要继续写入数据，我设计了一个端口input_ack来传递这个信号。 
综上，一个WG核需要的配置参数和端口如表3-9-1与表3-9-2。&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;work_mode&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;0为流水线模式，1为请求响应模式&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;工作模式&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;window_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;2 - 15&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;窗口的宽度和高度。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;color_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;1 - 12&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;色彩位宽。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-9-1 配置参数
&lt;/center&gt;&lt;br /&gt;
&lt;br&gt;
&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;端口&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;td&gt;clk&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;Clock.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rst_n&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;复位，低有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_enable&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据使能，在流水线模式下，它是另一个复位信号，在请求响应模式下，只有在它有效的时候in_data才会被真正地改变。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_data&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width * window_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据，必须和in_enable同步输入。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_ready&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据有效，在两种模式下，这个信号都会在out_data可以被读取的时候有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_data&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width * window_width * window_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据，将会和out_ready同步输出。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;input_ack&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入响应，仅仅被用于请求响应模式，这个端口将在输入数据被接受时给出一个响应。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-9-2 端口
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.9.3 实现&lt;/h3&gt;
&lt;p&gt;根据3.9.2的设计便可以实现一个WG核，流水线模式和请求响应模式实现如下。  &lt;/p&gt;
&lt;h4&gt;3.9.3.1 流水线模式&lt;/h4&gt;
&lt;p&gt;保证地址和数据同步流水化输出，在复位时二者皆输出为0，实现如下：&lt;br /&gt;
in_enable或rst_n为低时，系统复位，输出无效；否则窗口宽度加1个周期后第一个窗口被输出，并且每一个clk的上升沿都会送入一列进行处理，第一个窗口有效之后，每一个周期都将送出一个窗口，波形如图3-9-2。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-9-2 流水线模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/9/2.png&amp;quot; /&gt;&lt;br /&gt;
图3-9-2 流水线模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.9.3.2 请求响应模式&lt;/h4&gt;
&lt;p&gt;基本与3.9.3.1一致，但只有in_enable的上升沿时才会有数据被输入，同时每一列输入后都会有一个input_ack信号响应此次输入，来提示外部进行下一次的输入，波形如图3-9-3。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-9-3 请求响应模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/9/3.png&amp;quot; /&gt;&lt;br /&gt;
图3-9-3 请求响应模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.9.3.3 IP核GUI&lt;/h4&gt;
&lt;p&gt;完成功能后对WG核进行了封装，封装如图3-9-4。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-9-4 WG核的GUI&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/9/4.png&amp;quot; /&gt;&lt;br /&gt;
图3-9-4 WG核的GUI
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.9.4 仿真&lt;/h3&gt;
&lt;p&gt;WG核虽然对于所有图像都有意义，但一般用于灰度和二值图像的处理，所以我选取了一张图像的灰度模式与二值模式(来源于局部二值化，在后面的章节将会介绍)作为仿真源，并且考虑到仿真压力，仅测试了宽度为3和5的窗口，原始图像如图3-9-5。&lt;/p&gt;
&lt;p&gt;&lt;center&gt; 
&lt;img alt=&amp;quot;图3-9-5 原始测试图像&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/9/5.png&amp;quot; /&gt;&lt;br /&gt;
图3-9-5 原始测试图像
&lt;/center&gt;  &lt;/p&gt;
&lt;p&gt;使用HDL功能仿真和软件仿真的结果进行PSNR的计算，仿真结果如图3-9-6所示。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-9-6 仿真结果&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/9/6.png&amp;quot; /&gt;&lt;br /&gt;
图3-9-6 仿真结果之一，行缓存宽度为5，左侧为流水线模式下的HDL功能仿真结果，中间为请求响应模式下的HDL功能仿真结果，右侧为软件仿真结果
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.9.5 资源和时序&lt;/h3&gt;
&lt;p&gt;最终实现与窗口大小关系很大，这里只对512x512，色彩位宽为8，窗口大小为3时的情况进行分析，根据Vivado生成的报表，主要资源耗费如表3-9-3。&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;Slice LUTs*&lt;/th&gt;
&lt;th&gt;Slice Registers&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;73&lt;/td&gt;
&lt;td&gt;74&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-9-3 主要资源耗费
&lt;/center&gt;
&lt;br&gt;&lt;/p&gt;
&lt;p&gt;同时由于根据时序报告，最大的Data Path Delay(数据路径延迟)为2.325ns，即：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;FMax = 430.10MHz  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;即说明，WG核在流水线模式下，理论上在处理1080p全高清图像时可以达到207帧。&lt;br /&gt;
由于数据路径延迟和应用的最终约束设置强相关，所以仅供参考。&lt;/p&gt;
&lt;h3&gt;3.9.6 分析与结论&lt;/h3&gt;
&lt;p&gt;由于WG核与一般的图像处理不同，所以不能采用PSNR来评估，只能对比软件仿真和硬件仿真每一行的文本，完全相同时则为正确(OK)，得到的数据如表3-9-4。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;1-3&lt;/th&gt;
&lt;th&gt;1-5&lt;/th&gt;
&lt;th&gt;2-3&lt;/th&gt;
&lt;th&gt;2-5&lt;/th&gt;
&lt;th&gt;Total&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OK&lt;/td&gt;
&lt;td&gt;OK&lt;/td&gt;
&lt;td&gt;OK&lt;/td&gt;
&lt;td&gt;OK&lt;/td&gt;
&lt;td&gt;OK&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-9-4 文本对比
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;可见WG核有效，同时可以达到很高的FMax，设计成功。&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;参考文献&lt;/h1&gt;
&lt;hr /&gt;
&lt;h1&gt;感谢&lt;/h1&gt;
&lt;p&gt;仿真图像来源：&lt;br /&gt;
&lt;a href=&amp;quot;http://www.pixiv.net/member_illust.php?mode=medium&amp;amp;illust_id=26503317&amp;quot;&gt;パセリ-COMITIA100&lt;/a&gt;  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Wednesday, 20 May 2015 16:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.05.20 16:00:article/Skill-2015_05_20_a</guid>
<category>FPGA</category>
<category>FPGA-Imaging-Library</category>
<category>图像处理</category>
<category>窗口</category>
</item>

<item>
<title>【FPGA/图像处理】生成器-行缓存生成器</title>
<link>http://dtysky.moe/article/Skill-2015_05_19_a</link>
<description>&lt;p&gt;图像处理系列文章基本都是我本科毕设论文同步发布，这个项目以LGPL许可被发布在&lt;a href=&amp;quot;http://fil.dtysky.moe&amp;quot;&gt;F-I-L&lt;/a&gt;。行缓存是局部滤波操作的基础，它是生成窗口的前置条件。行缓存生成器的目的是截取图像的前N行，随后将其作为窗口数据的来源，设计一个行缓存生成器需要考虑到复用性和去耦合，以保证在有需求的情况下，一个行缓存能够被更多的后续模块利用。本节将会介绍如何实现一个行缓存生成器。&lt;br /&gt;
这个IP核的资源在这里：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/FPGA-Imaging-Library/tree/Publish/Generator/RowsGenerator&amp;quot;&gt;RowsGenerator&lt;/a&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;3 算法实现&lt;/h1&gt;
&lt;p&gt;明确了设计和架构，便可以进行算法的实现。本章将会说明如何实现图像处理的算法，以及如何运用它们。  &lt;/p&gt;
&lt;h2&gt;3.8 生成器-行缓存生成器&lt;/h2&gt;
&lt;p&gt;行缓存是局部滤波操作的基础，它是生成窗口的前置条件。行缓存生成器的目的是截取图像的前N行，随后将其作为窗口数据的来源，设计一个行缓存生成器需要考虑到复用性和去耦合，以保证在有需求的情况下，一个行缓存能够被更多的后续模块利用。本节将会介绍如何实现一个行缓存生成器。&lt;/p&gt;
&lt;h3&gt;3.8.1 原理&lt;/h3&gt;
&lt;p&gt;如图3-8-1所示&lt;sup&gt;[3]&lt;/sup&gt;，左侧是第一种窗口生成的方式，行缓存和窗口并行，右侧则是和窗口串行。在FPGA的实现中，无论是哪一种方式，行缓存中的每一行通常都是由一个和图像等宽的Fifo来构造的，而Fifo所消耗的存储器或者LUT资源比较多，所以考虑到实现的便利性、行缓存复用性和去耦合，本库选择了与窗口并行的方式。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;img alt=&amp;quot;图3-8-1 窗口生成形式&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/8/1.png&amp;quot; /&gt;&lt;br /&gt;
图3-8-1 窗口生成形式
&lt;/center&gt;  &lt;/p&gt;
&lt;p&gt;由于用到了Fifo，所以需要调用Xilinx官方的IP核，根据Fifo的数据手册&lt;sup&gt;[14]&lt;/sup&gt;，可知其可以配置为为许多种模式，在这里最需要关心配置是读写时钟、Fifo宽度与Fifo深度。Fifo的读写时钟有两种模式，可以配置为读写同步和读写异步模式，同步模式是即读写共用一个时钟，这样可以达到理论上最大的FMax，同时符合本设计的流水化需求，所以这里选择同步读写模式。同步读写模式的读写时序分别如图3-8-2和3-8-3，可见写入是实时的，而读出则有一个周期的延迟，并且Fifo内部数据的计数值和可读出的数据是同步的，所以只需要在第N行的计数值达到要求后使能第N行的读有效信号，隔一个周期之后使能第N+1行的写有效信号，同时将第N行的输出数据和第N+1行的输入数据连接到一起(第一行数据的输入应当为模块的输入数据)，最后，将每一行的输出拼接起来作为整个模块输出即可。&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;img alt=&amp;quot;图3-8-2 Fifo写时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/8/2.png&amp;quot; /&gt;&lt;br /&gt;
图3-8-2 Fifo写时序
&lt;/center&gt;&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-8-3 Fifo读时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/8/2.png&amp;quot; /&gt;&lt;br /&gt;
图3-8-3 Fifo读时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.8.2 设计&lt;/h3&gt;
&lt;p&gt;根据原理可知，RowsGenerator核(以下简称RG核)需要考虑Fifo宽度与Fifo深度。Fifo宽度根据应用场合(待处理图像的色彩位宽)变换较大，同时考虑到本库所允许的色彩位宽范围为1-12，并且在整个可能的项目中色彩位宽变幻的可能性较高，会因为IP重名而导致综合错误，所以我将其分为了四个阶段，并为每个阶段单独设置了一个Fifo，如表3-8-1，随后使用generate语句，让综合器自动根据当前色彩位宽选择实例化哪一个生成器，实现资源和泛用性的一个平衡。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;色彩位宽&lt;/th&gt;
&lt;th&gt;Fifo宽度&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2, 3, 4&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5, 6, 7, 8&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9, 10, 11, 12&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-8-1 色彩位宽与Fifo宽度
&lt;/center&gt;  &lt;/p&gt;
&lt;p&gt;对于Fifo的深度，则是根据当前图像宽度确定的，同时对于Fifo的IP核，其深度必须为2的幂，考虑到在一个项目中图像宽度几乎不会改变，所以我将Fifo深度的配置权交给了用户，用户可以自己设置这个深度来达到自己的需求。除此之外，由于行缓存确实需要消耗存储器资源，但对于一般的应用消耗的又并不多，无法填满块RAM的一个最小单位18K，使用块RAM可能会造成不必要的浪费，所以默认配置为使用分布式RAM来节约资源，带来的负面影响是会降低FMax，但综合考虑下，这是值得的，如果确实有需要很高速的应用，用户可以自行选择配置为其他模式的实现。综上，一个RG核需要的配置参数、端口和子模块如表3-8-2、表3-8-3和表3-8-4。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rows_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;2 - 15&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;行缓存宽度。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;im_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;1 - 4096&lt;/td&gt;
&lt;td&gt;320&lt;/td&gt;
&lt;td&gt;图像宽度。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;color_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;1 - 12&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;色彩位宽。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;im_width_bits&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;取决于图像宽度&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;图像宽度的位宽。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-8-2 配置参数
&lt;/center&gt;&lt;br /&gt;
&lt;br&gt;
&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;端口&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;td&gt;clk&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;Clock.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rst_n&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;复位，低有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_enable&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据使能，作为第一个fifo的写使能。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_data&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据，必须和in_enable同步输入。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_ready&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据有效，在两种模式下，这个信号都会在out_data可以被读取的时候有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_data&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width * rows_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据，将会和out_ready同步输出。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-8-3 端口
&lt;/center&gt;&lt;br /&gt;
&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&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;tr&gt;
&lt;td&gt;Fifo&lt;/td&gt;
&lt;td&gt;Fifo1xWidthRows&lt;/td&gt;
&lt;td&gt;位宽为1并且深度为N的Fifo(1 &amp;lt; N &amp;lt; 4096)，用于构造色彩位宽为1的行缓存。 你可以自己对fifo进行配置，但是在一个项目中所有的同名的fifo必须拥有同样的配置。 同时你只能够更改写入深度和fifo实现的类型，它的读延迟必须为1！&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fifo&lt;/td&gt;
&lt;td&gt;Fifo4xWidthRows&lt;/td&gt;
&lt;td&gt;位宽为4并且深度为N的Fifo(1 &amp;lt; N &amp;lt; 4096)，用于构造色彩位宽为1的行缓存。 你可以自己对fifo进行配置，但是在一个项目中所有的同名的fifo必须拥有同样的配置。 同时你只能够更改写入深度和fifo实现的类型，它的读延迟必须为1！&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fifo&lt;/td&gt;
&lt;td&gt;Fifo8xWidthRows&lt;/td&gt;
&lt;td&gt;位宽为8并且深度为N的Fifo(1 &amp;lt; N &amp;lt; 4096)，用于构造色彩位宽为1的行缓存。 你可以自己对fifo进行配置，但是在一个项目中所有的同名的fifo必须拥有同样的配置。 同时你只能够更改写入深度和fifo实现的类型，它的读延迟必须为1！&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fifo&lt;/td&gt;
&lt;td&gt;Fifo12xWidthRows&lt;/td&gt;
&lt;td&gt;位宽为12并且深度为N的Fifo(1 &amp;lt; N &amp;lt; 4096)，用于构造色彩位宽为1的行缓存。 你可以自己对fifo进行配置，但是在一个项目中所有的同名的fifo必须拥有同样的配置。 同时你只能够更改写入深度和fifo实现的类型，它的读延迟必须为1！&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-8-4 子模块
&lt;/center&gt;&lt;/p&gt;
&lt;h3&gt;3.8.3 实现&lt;/h3&gt;
&lt;p&gt;根据3.8.2的设计便可以实现一个RG核，但考虑到如果这个RG核采用请求响应模式没有意义，用一般的AXI协议与其交互会使得效率低下且造成资源浪费，同时可能带来比较严重的时序问题，所以AXI版本的行缓存生成不在本节讨论，会用专门的AXI-Stream协议完成。故，此RG核仅有流水线模式。  &lt;/p&gt;
&lt;h4&gt;3.8.3.1 流水线模式&lt;/h4&gt;
&lt;p&gt;保证地址和数据同步流水化输出，在复位时二者皆输出为0，实现如下：&lt;br /&gt;
rst_n为低时，系统复位，输出无效；否则当in_enable使能时，第一个fifo开始填充，之后每一个clk的上升沿都会送入一个数据进行处理，当所有的fifo都被填充到图像宽度的深度时，输出有效，之后每一个周期都将送出一个有效数据，波形如图3-8-4。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-8-4 流水线模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/8/4.png&amp;quot; /&gt;&lt;br /&gt;
图3-8-4 流水线模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.8.3.2 IP核GUI&lt;/h4&gt;
&lt;p&gt;完成功能后对RG核进行了封装，封装如图3-8-5，im_width_bits根据自行im_width自动计算。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-8-5 RG核的GUI&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/8/5.png&amp;quot; /&gt;&lt;br /&gt;
图3-8-5 RG核的GUI
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.8.4 仿真&lt;/h3&gt;
&lt;p&gt;RG核虽然对于所有图像都有意义，但一般用于灰度和二值图像的处理，所以我选取了一张图像的灰度模式与二值模式(来源于局部二值化，在后面的章节将会介绍)作为仿真源，并且考虑到仿真压力，仅测试了宽度为3和5的行缓存，原始图像如图3-8-6。&lt;/p&gt;
&lt;p&gt;&lt;center&gt; 
&lt;img alt=&amp;quot;图3-8-6 原始测试图像&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/8/6.png&amp;quot; /&gt;&lt;br /&gt;
图3-8-6 原始测试图像
&lt;/center&gt;  &lt;/p&gt;
&lt;p&gt;使用HDL功能仿真和软件仿真的结果进行PSNR的计算，仿真结果如图3-8-7所示。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-8-7 仿真结果&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/8/7.png&amp;quot; /&gt;&lt;br /&gt;
图3-8-7 仿真结果之一，行缓存宽度为5，左侧为HDL功能仿真结果，右侧为软件仿真结果
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.8.5 资源和时序&lt;/h3&gt;
&lt;p&gt;由于最终实现和Fifo配置关系很大，这里只对512x512，色彩位宽为8，窗口大小为3，同时使用分布式RAM的状况，根据Vivado生成的报表，主要资源耗费如表3-8-5。&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;Slice LUTs*&lt;/th&gt;
&lt;th&gt;Slice Registers&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;610&lt;/td&gt;
&lt;td&gt;237&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-8-5 主要资源耗费
&lt;/center&gt;
&lt;br&gt;&lt;/p&gt;
&lt;p&gt;同时由于根据时序报告，在使用分布式RAM的情况下，最大的Data Path Delay(数据路径延迟)为3.514ns，即：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;FMax = 284.57MHz  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;即说明，RG核在流水线模式下，理论上在处理1080p全高清图像时可以达到137帧。&lt;br /&gt;
由于数据路径延迟和应用的最终约束设置强相关，所以仅供参考。&lt;/p&gt;
&lt;h3&gt;3.8.6 分析与结论&lt;/h3&gt;
&lt;p&gt;由于RG核与一般的图像处理不同，所以不能采用PSNR来评估，只能对比软件仿真和硬件仿真每一行的文本，完全相同时则为正确(OK)，得到的数据如表3-8-5。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;1-3&lt;/th&gt;
&lt;th&gt;1-5&lt;/th&gt;
&lt;th&gt;2-3&lt;/th&gt;
&lt;th&gt;2-5&lt;/th&gt;
&lt;th&gt;Total&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OK&lt;/td&gt;
&lt;td&gt;OK&lt;/td&gt;
&lt;td&gt;OK&lt;/td&gt;
&lt;td&gt;OK&lt;/td&gt;
&lt;td&gt;OK&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-8-5 文本对比
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;可见RG核有效，同时在使用分布式RAM的情况下也可以达到不错的FMax，设计成功。&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;参考文献&lt;/h1&gt;
&lt;p&gt;[3] Donald G.Bailey.基于FPGA的嵌入式图像处理系统设计[M].原魁,何文浩,肖晗译.北京:电子工业出版社,2013&lt;br /&gt;
[14] Xilinx, Vivado Design Suite, FIFO Generator v12.0, Product Guide, PG057 [EB/OL]. October 1 , 2014  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;感谢&lt;/h1&gt;
&lt;p&gt;仿真图像来源：&lt;br /&gt;
&lt;a href=&amp;quot;http://www.pixiv.net/member_illust.php?mode=medium&amp;amp;illust_id=23103608&amp;quot;&gt;パセリ-絵描き見習い&lt;/a&gt;  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Tuesday, 19 May 2015 16:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.05.19 16:00:article/Skill-2015_05_19_a</guid>
<category>FPGA</category>
<category>FPGA-Imaging-Library</category>
<category>图像处理</category>
<category>行缓存</category>
<category>Fifo</category>
</item>

<item>
<title>【FPGA/图像处理】点操作——色彩反转</title>
<link>http://dtysky.moe/article/Skill-2015_05_17_b</link>
<description>&lt;p&gt;图像处理系列文章基本都是我本科毕设论文同步发布，这个项目以LGPL许可被发布在&lt;a href=&amp;quot;http://fil.dtysky.moe&amp;quot;&gt;F-I-L&lt;/a&gt;。色彩反转可以看做是变换系数为-1时的对比度变换和变换系数为色彩最大值的亮度变换之和，但考虑到在对比度变换时引入符号计算会增加额外的资源和时序消耗，并且一般情况下也不会有负向对比度变换的需求，所以单独将其提出作为一个模块。色彩反转常用于需要反转背景和主题元素的应用，例如解决某些眼障人群对一些色彩搭配不适，又例如在印刷工艺中的负片等，本节将会介绍如何实现一个色彩反转的IP核。&lt;br /&gt;
这个IP核的资源在这里：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/FPGA-Imaging-Library/tree/Publish/Point/ColorReversal&amp;quot;&gt;ColorReversal&lt;/a&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;3 算法实现&lt;/h1&gt;
&lt;p&gt;明确了设计和架构，便可以进行算法的实现。本章将会说明如何实现图像处理的算法，以及如何运用它们。  &lt;/p&gt;
&lt;h2&gt;3.7 点操作——色彩反转&lt;/h2&gt;
&lt;p&gt;色彩反转可以看做是变换系数为-1时的对比度变换和变换系数为色彩最大值的亮度变换之和，但考虑到在对比度变换时引入符号计算会增加额外的资源和时序消耗，并且一般情况下也不会有负向对比度变换的需求，所以单独将其提出作为一个模块。色彩反转常用于需要反转背景和主题元素的应用，例如解决某些眼障人群对一些色彩搭配不适，又例如在印刷工艺中的负片等，本节将会介绍如何实现一个色彩反转的IP核。&lt;/p&gt;
&lt;h3&gt;3.7.1 原理&lt;/h3&gt;
&lt;p&gt;色彩反转的基本原理公式如式3-7-1&lt;sup&gt;[3]&lt;/sup&gt;，由于此变换同样适用于任何色彩通道数量和任何色彩位宽的图像，所以需要针对这两个参数来调整IP核的结构，此公式表示了一个像素中一个通道的色彩是如何被变换的，Q为输出，I为输入，N为色彩位宽，可见实现此运算需要一次减法。  &lt;/p&gt;
&lt;p&gt;$$Q = (2^N - 1) - I\ \ \ \ \ \ \ \ (3-7-1)$$&lt;/p&gt;

&lt;p&gt;但考虑到这种减法的特殊性，即用于减去I的被减数实际上是色彩位宽能够表示的无符号数的最大值，所以可以直接对输入I按位取反来得到输出Q，如式3-7-2，按位取反是简单快速的逻辑运算，理论上比减法的逻辑延时要小，并且实现也更为容易。&lt;/p&gt;
&lt;p&gt;$$Q = \sim I\ \ \ \ \ \ \ \ (3-7-2)$$&lt;/p&gt;

&lt;p&gt;可见，色彩反转需要若干次并行的取反运算，同时要根据色彩通道数量和色彩位宽做相应的适配。  &lt;/p&gt;
&lt;h3&gt;3.7.2 设计&lt;/h3&gt;
&lt;p&gt;根据原理可知，除了第2章的接口标准外，实现一个ColorReversal核(以下简称CR核)也需要根据输入参数做一些适配，但考虑到每一个通道、每一位的运算完全等价，所以不用考虑逻辑复制，直接对输入的每一位取反即可。综上，最终需要的配置参数和端口分别如表3-7-1、表3-7-2所示。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;work_mode&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;0为流水线模式，1为请求响应模式&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;模块的工作模式。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;color_channels&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;色彩通道数量，1为灰度，3为RGB等等。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;color_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;1 - 12&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;色彩位宽。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-7-1 配置参数
&lt;/center&gt;&lt;br /&gt;
&lt;br&gt;
&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;端口&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;td&gt;clk&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;Clock.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rst_n&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;复位，低有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_enable&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据使能，在流水线模式下，它是另一个复位信号，在请求响应模式下，只有在它有效的时候in_data才会被真正地改变。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_data&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据，必须和in_enable同步输入。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_ready&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据有效，在两种模式下，这个信号都会在out_data可以被读取的时候有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_data&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据，将会和out_ready同步输出。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-7-2 端口
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.7.3 实现&lt;/h3&gt;
&lt;p&gt;根据3.7.2的设计便可以实现一个CR核，流水线和请求响应两种模式的实现方式如下：  &lt;/p&gt;
&lt;h4&gt;3.7.3.1 流水线模式&lt;/h4&gt;
&lt;p&gt;保证地址和数据同步流水化输出，在复位时二者皆输出为0，实现如下：&lt;br /&gt;
in_enable或rst_n为低时，out_ready输出为0，即输出无效，系统不工作；否则一个周期后第一个数据被输出，并且每一个clk的上升沿都会送入一个数据in_data进行处理，第一个输出数据有效之后，每一个周期都将送出一个有效数据，波形如图3-7-1。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-7-1 流水线模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/7/1.png&amp;quot; /&gt;&lt;br /&gt;
图3-7-1 流水线模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.7.3.2 请求响应模式&lt;/h4&gt;
&lt;p&gt;基本同3.7.3.1，但输入数据in_data只有在in_enbale的上升沿才会被送入处理，波形如图3-7-2。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-7-2 请求响应模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/7/2.png&amp;quot; /&gt;&lt;br /&gt;
图3-7-2 请求响应模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.7.3.3 IP核GUI&lt;/h4&gt;
&lt;p&gt;完成功能后对CR核进行了封装，封装如图3-7-3。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-7-3 CR核的GUI&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/7/3.png&amp;quot; /&gt;&lt;br /&gt;
图3-7-3 CR核的GUI
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.7.4 仿真&lt;/h3&gt;
&lt;p&gt;CR核对于所有图像输入都有意义，所以我选取了一张图像的RGB模式、灰度模式与二值模式(来源于局部二值化，在后面的章节将会介绍)作为仿真源，对每张图进行流水线和请求响应模式的测试，原始图像如图3-7-4，每一张图像的测试流程如下： &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;流水线模式conf -&amp;gt; 存入*-conf1-pipeline-hdlfun.*内 -&amp;gt; 请求响应模式conf -&amp;gt; 存入*-conf-reqack-hdlfun.*内。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;center&gt; 
&lt;img alt=&amp;quot;图3-7-4 原始测试图像&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/7/4.jpg&amp;quot; /&gt;&lt;br /&gt;
图3-7-4 原始测试图像
&lt;/center&gt;  &lt;/p&gt;
&lt;p&gt;使用HDL功能仿真和软件仿真的结果进行PSNR的计算，仿真结果如图3-7-5所示。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-7-5 仿真结果&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/7/5.jpg&amp;quot; /&gt;&lt;br /&gt;
图3-7-5 仿真结果，左侧为请求响应模式下的HDL功能仿真结果，中间为流水线模式下的HDL功能仿真结果，右侧为软件仿真结果
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.7.5 资源和时序&lt;/h3&gt;
&lt;p&gt;由于两种模式的基本构成大致相同，所以只对第一种模式进行分析，根据Vivado生成的报表，主要资源耗费如表3-7-3。&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;Slice LUTs*&lt;/th&gt;
&lt;th&gt;Slice Registers&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;49&lt;/td&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-7-3 主要资源耗费
&lt;/center&gt;
&lt;br&gt;
根据时序报告，最大的Data Path Delay(数据路径延迟)为2.298ns，即：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;FMax = 435.16MHz  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;即说明，CR核在流水线模式下，理论上在处理1080p全高清图像时可以达到210帧。&lt;br /&gt;
由于数据路径延迟和应用的最终约束设置强相关，所以仅供参考。&lt;/p&gt;
&lt;h3&gt;3.7.6 分析与结论&lt;/h3&gt;
&lt;p&gt;根据仿真结果计算PSNR，得到的数据如表3-7-4。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;1&lt;/th&gt;
&lt;th&gt;2&lt;/th&gt;
&lt;th&gt;3&lt;/th&gt;
&lt;th&gt;Total&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-7-4 PSNR
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;PSNR均值为极大值，可见LT核和软件方法完全等效，同时可以达到不错的FMax，设计成功。&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;参考文献&lt;/h1&gt;
&lt;p&gt;[3] Donald G.Bailey.基于FPGA的嵌入式图像处理系统设计[M].原魁,何文浩,肖晗译.北京:电子工业出版社,2013  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;感谢&lt;/h1&gt;
&lt;p&gt;仿真图像来源：&lt;br /&gt;
&lt;a href=&amp;quot;http://www.pixiv.net/member_illust.php?mode=medium&amp;amp;illust_id=24442630&amp;quot;&gt;パセリ-Fallen&lt;/a&gt;  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Sunday, 17 May 2015 20:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.05.17 20:00:article/Skill-2015_05_17_b</guid>
<category>FPGA</category>
<category>FPGA-Imaging-Library</category>
<category>图像处理</category>
<category>点操作</category>
<category>色彩反转</category>
</item>

<item>
<title>【FPGA/图像处理】点操作——亮度变换</title>
<link>http://dtysky.moe/article/Skill-2015_05_17_a</link>
<description>&lt;p&gt;图像处理系列文章基本都是我本科毕设论文同步发布，这个项目以LGPL许可被发布在&lt;a href=&amp;quot;http://fil.dtysky.moe&amp;quot;&gt;F-I-L&lt;/a&gt;。亮度变换同样是最基础的图像增强运算之一，通过3.3的论述可知，亮度实际上是像素的各个通道色彩分量的一个线性函数，故可以通过更改每个通道的色彩值来进行亮度的变换。所以，亮度变换就是对图像中每一个像素的色彩进行增加或者减少的线性变换，与对比度变相相同，亮度变换的方式也有很多，差异也基本都是变换系数所造成的，系数为常数的变换为线性变换，否则为非线性变换，本节将讨论如何实现线性的亮度变换。&lt;br /&gt;
这个IP核的资源在这里：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/FPGA-Imaging-Library/tree/Publish/Point/LightnessTransform&amp;quot;&gt;LightnessTransform&lt;/a&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;3 算法实现&lt;/h1&gt;
&lt;p&gt;明确了设计和架构，便可以进行算法的实现。本章将会说明如何实现图像处理的算法，以及如何运用它们。  &lt;/p&gt;
&lt;h2&gt;3.6 点操作——亮度换变&lt;/h2&gt;
&lt;p&gt;亮度变换同样是最基础的图像增强运算之一，通过3.3的论述可知，亮度实际上是像素的各个通道色彩分量的一个线性函数，故可以通过更改每个通道的色彩值来进行亮度的变换。所以，亮度变换就是对图像中每一个像素的色彩进行增加或者减少的线性变换，与对比度变相相同，亮度变换的方式也有很多，差异也基本都是变换系数所造成的，系数为常数的变换为线性变换，否则为非线性变换，本节将讨论如何实现线性的亮度变换。&lt;/p&gt;
&lt;h3&gt;3.6.1 原理&lt;/h3&gt;
&lt;p&gt;任何亮度变换都是通过调整变换系数实现的&lt;sup&gt;[3]&lt;/sup&gt;，其变换的原理公式如式3-6-1，其中I为输入，Q为输出，lm_gain为变换系数，为有符号数，可见其本质实际上是映射函数的截距，当变换系数大于0时，亮度增强，否则对比亮度降低。对于线性变换，这个变换系数为常数，即对于所有的输入色彩，所执行的运算都是一致的，这种变换的结果是整张图像所有的像素都被等效变换。  &lt;/p&gt;
&lt;p&gt;$$Q=I + lm\_gain\ \ \ \ \ \ \ \ (3-6-1)$$&lt;/p&gt;

&lt;p&gt;可见，对比度变换需要加法运算，同时由于对比度变换不仅仅适用于灰度图像，还适用于多通道的彩色图像，并且所有通道的变换形式都是一致的。所以需要提供一个配置接口，用以确定输入图像所含的通道数量，并通过通道数量来对基本的单通道变换复制进行最终变换的实现。  &lt;/p&gt;
&lt;h3&gt;3.6.2 设计&lt;/h3&gt;
&lt;p&gt;根据原理可知，除了第2章的接口标准外，实现一个LightnessTransform核(以下简称LT核)还需要有符号加法、输出使能计数器和复位系统，执行加法的次数的数量由输入色彩的通道数量决定，并且每一个通道的运算都是一致的，这个运算有溢出的可能，所以在实现时要考虑对输出作出裁剪，当加法的结果为负数或者超出了当前色彩位宽所能表示的最大值时，比如对于8位色彩为255，则裁剪到0或255。例如，对于9位和9位的有符号加法，要将输出裁剪到允许的输出范围内，则如式3-6-2，其中Res为10位的相加结果，Q为最终输出，Res的最高位为符号位，次高位看作是溢出位，符号位决定是否裁剪到0，溢出位决定是否裁剪到255。  &lt;/p&gt;
&lt;p&gt;$$Q=\begin{cases} 0 &amp;&amp; Res[9] = 1 \\Res[7 : 0] &amp;&amp; Res[9] = 0 \ \&amp;\ Res[8] = 0 \\255 &amp; &amp; Res[9] = 0 \ \&amp;\ Res[8] = 1 \ \end{cases}.\ \ \ \ \ \ \ \ (3-6-2)$$&lt;/p&gt;

&lt;p&gt;综上，最终需要的配置参数和端口分别如表3-6-1、表3-6-2所示。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;work_mode&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;0为流水线模式，1为请求响应模式&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;模块的工作模式。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;color_channels&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;色彩通道数量，1为灰度，3为RGB等等。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;color_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;1 - 12&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;色彩位宽。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-6-1 配置参数
&lt;/center&gt;&lt;br /&gt;
&lt;br&gt;
&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;端口&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;td&gt;clk&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;Clock.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rst_n&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;复位，低有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;lm_gain&lt;/td&gt;
&lt;td&gt;input&lt;/td&gt;
&lt;td&gt;有符号&lt;/td&gt;
&lt;td&gt;color_width : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;亮度变换系数，有符号数，当大于零时，它的值必须是原码，小于零时则必须是补码。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_enable&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据使能，在流水线模式下，它是另一个复位信号，在请求响应模式下，只有在它有效的时候in_data才会被真正地改变。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_data&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据，必须和in_enable同步输入。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_ready&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据有效，在两种模式下，这个信号都会在out_data可以被读取的时候有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_data&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据，将会和out_ready同步输出。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-6-2 端口
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.6.3 实现&lt;/h3&gt;
&lt;p&gt;根据3.6.2的设计便可以实现一个LT核，流水线和请求响应两种模式的实现方式如下：  &lt;/p&gt;
&lt;h4&gt;3.6.3.1 流水线模式&lt;/h4&gt;
&lt;p&gt;保证地址和数据同步流水化输出，在复位时二者皆输出为0，实现如下：&lt;br /&gt;
in_enable或rst_n为低时，out_ready输出为0，即输出无效，系统不工作；否则系统根据lm_gain进行工作，计数器在2个周期后使能输出有效，第一个数据被输出，并且每一个clk的上升沿都会送入一个数据in_data进行处理，第一个输出数据有效之后，每一个周期都将送出一个有效数据，波形如图3-6-1。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-6-1 流水线模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/6/1.png&amp;quot; /&gt;&lt;br /&gt;
图3-6-1 流水线模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.6.3.2 请求响应模式&lt;/h4&gt;
&lt;p&gt;基本同3.6.3.1，但输入数据in_data只有在in_enbale的上升沿才会被送入处理，波形如图3-6-2。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-6-2 请求响应模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/6/2.png&amp;quot; /&gt;&lt;br /&gt;
图3-6-2 请求响应模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.6.3.3 IP核GUI&lt;/h4&gt;
&lt;p&gt;完成功能后对LT核进行了封装，封装如图3-6-3，work_mode被设计为键值对的模式，方便用户理解和选择，其它参数都根据实际状况加上了范围限定。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-6-3 LT核的GUI&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/6/3.png&amp;quot; /&gt;&lt;br /&gt;
图3-6-3 LT核的GUI
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.6.4 仿真&lt;/h3&gt;
&lt;p&gt;LT核对于二值操作没有意义，所以我选取了一张图像的RGB模式和灰度模式作为仿真源，并分别设置了两套参数(理论上可以设置任意套参数)，参数如表3-6-3。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;lm_gain&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-90&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-3-4 仿真参数
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;对每种参数进行流水线和请求响应模式的测试，原始图像如图3-6-4，每一张图像的测试流程如下： &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;流水线模式conf1 -&amp;gt; 存入*-conf1-pipeline-hdlfun.*内 -&amp;gt; 请求响应模式conf1 -&amp;gt; 存入*-conf1-reqack-hdlfun.*内 -&amp;gt; 流水线模式conf2 -&amp;gt; 存入*-conf2-pipeline-hdlfun.*内 -&amp;gt; 请求响应模式conf2 -&amp;gt; 存入*-conf2-reqack-hdlfun.*内。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;center&gt; 
&lt;img alt=&amp;quot;图3-6-4 原始测试图像&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/6/4.jpg&amp;quot; /&gt;&lt;br /&gt;
图3-6-4 原始测试图像
&lt;/center&gt;  &lt;/p&gt;
&lt;p&gt;使用HDL功能仿真和软件仿真的结果进行PSNR的计算，仿真结果如图3-6-5所示。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-6-5 仿真结果&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/6/5.jpg&amp;quot; /&gt;&lt;br /&gt;
图3-6-5 仿真结果，左侧为请求响应模式下的HDL功能仿真结果，中间为流水线模式下的HDL功能仿真结果，右侧为软件仿真结果
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.6.5 资源和时序&lt;/h3&gt;
&lt;p&gt;由于两种模式的基本构成大致相同，所以只对第一种模式进行分析，根据Vivado生成的报表，主要资源耗费如表3-6-4。&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;Slice LUTs*&lt;/th&gt;
&lt;th&gt;Slice Registers&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;67&lt;/td&gt;
&lt;td&gt;57&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-6-4 主要资源耗费
&lt;/center&gt;
&lt;br&gt;
根据时序报告，最大的Data Path Delay(数据路径延迟)为2.533ns，即：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;FMax = 394.78MHz  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;即说明，LT核在流水线模式下，理论上在处理1080p全高清图像时可以达到190帧。&lt;br /&gt;
由于数据路径延迟和应用的最终约束设置强相关，所以仅供参考。&lt;/p&gt;
&lt;h3&gt;3.6.6 分析与结论&lt;/h3&gt;
&lt;p&gt;根据仿真结果计算PSNR，得到的数据如表3-6-5。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;1--90&lt;/th&gt;
&lt;th&gt;1-100&lt;/th&gt;
&lt;th&gt;2--90&lt;/th&gt;
&lt;th&gt;2-100&lt;/th&gt;
&lt;th&gt;Total&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-6-5 PSNR
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;PSNR均值为极大值，可见LT核和软件方法完全等效，同时可以达到不错的FMax，设计成功。&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;参考文献&lt;/h1&gt;
&lt;p&gt;[3] Donald G.Bailey.基于FPGA的嵌入式图像处理系统设计[M].原魁,何文浩,肖晗译.北京:电子工业出版社,2013  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;感谢&lt;/h1&gt;
&lt;p&gt;仿真图像来源：&lt;br /&gt;
&lt;a href=&amp;quot;http://www.pixiv.net/member_illust.php?mode=medium&amp;amp;illust_id=29330221&amp;quot;&gt;LM7-NL&lt;/a&gt;  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Sunday, 17 May 2015 01:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.05.17 01:00:article/Skill-2015_05_17_a</guid>
<category>FPGA</category>
<category>FPGA-Imaging-Library</category>
<category>图像处理</category>
<category>点操作</category>
<category>亮度变换</category>
</item>

<item>
<title>【FPGA/图像处理】点操作——对比度变换</title>
<link>http://dtysky.moe/article/Skill-2015_05_16_b</link>
<description>&lt;p&gt;其实俺也知道每个简单的操作都写这么一大篇很无聊，但论文就是这样的东西，不是么233？&lt;br /&gt;
图像处理系列文章基本都是我本科毕设论文同步发布，这个项目以LGPL许可被发布在&lt;a href=&amp;quot;http://fil.dtysky.moe&amp;quot;&gt;F-I-L&lt;/a&gt;。对比度变换属于图像增强的一种，图像增强，即增强图像中有用的信息，其目的是是改变图像的视觉想过，针对应用刻意强调图像整体或局部特征，是一个失真的过程。对比度变换是最基础的图像增强运算之一，由于人眼不仅仅是根据色彩的绝对值，还会根据某个区域和其周边的一个对比来得到整体的感受，这个“对比”量化后即为对比度。对比度变换的方式有很多，但差异基本都是变换系数所造成的，系数为常数的变换为线性变换，否则为非线性变换，本节将讨论如何实现线性的对比度变换。&lt;br /&gt;
这个IP核的资源在这里：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/FPGA-Imaging-Library/tree/Publish/Point/ContrastTransform&amp;quot;&gt;ContrastTransform&lt;/a&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;3 算法实现&lt;/h1&gt;
&lt;p&gt;明确了设计和架构，便可以进行算法的实现。本章将会说明如何实现图像处理的算法，以及如何运用它们。  &lt;/p&gt;
&lt;h2&gt;3.5 点操作——对比度变换&lt;/h2&gt;
&lt;p&gt;对比度变换属于图像增强的一种，图像增强，即增强图像中有用的信息，其目的是是改变图像的视觉效果，针对应用刻意强调图像整体或局部特征，是一个失真的过程。对比度变换是最基础的图像增强运算之一，由于人眼不仅仅是根据色彩的绝对值，还会根据某个区域和其周边的一个对比来得到整体的感受，这个“对比”量化后即为对比度。对比度变换的方式有很多，但差异基本都是变换系数所造成的，系数为常数的变换为线性变换，否则为非线性变换，本节将讨论如何实现线性的对比度变换。&lt;/p&gt;
&lt;h3&gt;3.5.1 原理&lt;/h3&gt;
&lt;p&gt;任何对比度变换都是通过调整变换系数实现的&lt;sup&gt;[3]&lt;/sup&gt;，对比度变换的原理公式如式3-5-1，其中I为输入，Q为输出，ct_scale为变换系数，可见其本质实际上是映射函数的斜率，当变换系数大于1时，对比度增强，否则对比度降低。对于线性变换，这个变换系数为常数，即对于所有的输入色彩，所执行的运算都是一致的，这种变换的结果是整张图像所有的像素都被等效变换。  &lt;/p&gt;
&lt;p&gt;$$Q=ct\_scale * I\ \ \ \ \ \ \ \ (3-5-1)$$&lt;/p&gt;

&lt;p&gt;可见，对比度变换需要乘法运算，同时由于对比度变换不仅仅适用于灰度图像，还适用于多通道的彩色图像，并且所有通道的变换形式都是一致的。所以需要提供一个配置接口，用以确定输入图像所含的通道数量，并通过通道数量来对基本的单通道变换复制进行最终变换的实现。  &lt;/p&gt;
&lt;h3&gt;3.5.2 设计&lt;/h3&gt;
&lt;p&gt;根据原理可知，除了第2章的接口标准外，实现一个ContrastTransform核(以下简称CT核)还需要若干乘法器、输出使能计数器和复位系统，乘法器的数量由输入色彩的通道数量决定，并且每一个通道的运算都是一致的，这个运算有溢出的可能，所以在实现时要考虑对输出作出裁剪，当乘法的结果超出了当前色彩位宽所能表示的最大值时，比如对于8位色彩为255，则裁剪到255，裁剪过程理论上是一个“大于”的比较过程，但考虑到FPGA在实现“大于”和“小于”运算综合后是加法器实现的，会造成一定的性能影响，所以这里采用“等于”运算来替代。例如，对于8位和8位的无符号乘法，要将输出裁剪为8位，则如式3-5-2，其中Res为16位的相乘结果，Q为最终输出，Res的高八位看作是溢出位，将这高八位组成一个新的无符号数，大于0时则可以判定为溢出。  &lt;/p&gt;
&lt;p&gt;$$Q=\begin{cases} Res[7 : 0] &amp;&amp; Res[15 : 8] = 0 \\255 &amp; &amp; Res[15 : 8] \neq 0 \ \end{cases}.\ \ \ \ \ \ \ \ (3-5-2)$$&lt;/p&gt;

&lt;p&gt;综上，最终需要的配置参数、端口和子模块分别如表3-5-1、表3-5-2与表3-5-3所示。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;work_mode&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;0为流水线模式，1为请求响应模式&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;模块的工作模式。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;color_channels&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;色彩通道数量，1为灰度，3为RGB等等。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;color_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;1 - 12&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;色彩位宽。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mul_delay&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;取决于乘法器的配置。&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;乘法器输出延迟（流水线级数）。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-5-1 配置参数
&lt;/center&gt;&lt;br /&gt;
&lt;br&gt;
&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;端口&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;td&gt;clk&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;Clock.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rst_n&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;复位，低有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ct_scale&lt;/td&gt;
&lt;td&gt;input&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;23 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;对比度变换系数，定点数，12bits.12bis。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_enable&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据使能，在流水线模式下，它是另一个复位信号，在请求响应模式下，只有在它有效的时候in_data才会被真正地改变。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_data&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据，必须和in_enable同步输入。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_ready&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据有效，在两种模式下，这个信号都会在out_data可以被读取的时候有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_data&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据，将会和out_ready同步输出。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-5-2 端口
&lt;/center&gt;&lt;br /&gt;
&lt;br&gt;
&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&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;tr&gt;
&lt;td&gt;Mul&lt;/td&gt;
&lt;td&gt;Multiplier12x24CT&lt;/td&gt;
&lt;td&gt;12位无符号数和24位无符号数的乘法器，被用于定点数的乘法。你可以自己配置这个乘法器，然后更改&amp;quot;mul_delay&amp;quot;，但所有的乘法器必须拥有相同的流水线级数，并且不能更改端口的配置！&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-3-3 子模块
&lt;/center&gt;&lt;/p&gt;
&lt;h3&gt;3.5.3 实现&lt;/h3&gt;
&lt;p&gt;根据3.5.2的设计便可以实现一个CT核，流水线和请求响应两种模式的实现方式如下：  &lt;/p&gt;
&lt;h4&gt;3.5.3.1 流水线模式&lt;/h4&gt;
&lt;p&gt;保证地址和数据同步流水化输出，在复位时二者皆输出为0，实现如下：&lt;br /&gt;
in_enable或rst_n为低时，out_ready输出为0，即输出无效，系统不工作；否则系统根据ct_scale进行工作，计数器在乘法器延迟加1个周期(默认为4)后使能输出有效，第一个数据被输出，并且每一个clk的上升沿都会送入一个数据in_data进行处理，第一个输出数据有效之后，每一个周期都将送出一个有效数据，波形如图3-5-1。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-5-1 流水线模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/5/1.png&amp;quot; /&gt;&lt;br /&gt;
图3-5-1 流水线模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.5.3.2 请求响应模式&lt;/h4&gt;
&lt;p&gt;基本同3.5.3.1，但输入数据in_data只有在in_enbale的上升沿才会被送入处理，波形如图3-5-2。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-5-2 请求响应模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/5/2.png&amp;quot; /&gt;&lt;br /&gt;
图3-5-2 请求响应模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.5.3.3 IP核GUI&lt;/h4&gt;
&lt;p&gt;完成功能后对CT核进行了封装，封装如图3-5-3，work_mode被设计为键值对的模式，方便用户理解和选择，其它参数都根据实际状况加上了范围限定。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-5-3 CT核的GUI&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/5/3.png&amp;quot; /&gt;&lt;br /&gt;
图3-5-3 CT核的GUI
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.5.4 仿真&lt;/h3&gt;
&lt;p&gt;CT核对于二值操作没有意义，所以我选取了一张图像的RGB模式和灰度模式作为仿真源，并分别设置了两套参数(理论上可以设置任意套参数)，参数的选取原则是不可由有限二进制定点数表示，以此来得到最坏情况下的PSNR，参数如表3-5-4。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;ct_scale&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3.3&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-5-4 仿真参数
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;对每种参数进行流水线和请求响应模式的测试，原始图像如图3-5-4，每一张图像的测试流程如下： &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;流水线模式conf1 -&amp;gt; 存入*-conf1-pipeline-hdlfun.*内 -&amp;gt; 请求响应模式conf1 -&amp;gt; 存入*-conf1-reqack-hdlfun.*内 -&amp;gt; 流水线模式conf2 -&amp;gt; 存入*-conf2-pipeline-hdlfun.*内 -&amp;gt; 请求响应模式conf2 -&amp;gt; 存入*-conf2-reqack-hdlfun.*内。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;center&gt; 
&lt;img alt=&amp;quot;图3-5-4 原始测试图像&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/5/4.jpg&amp;quot; /&gt;&lt;br /&gt;
图3-5-4 原始测试图像
&lt;/center&gt;  &lt;/p&gt;
&lt;p&gt;使用HDL功能仿真和软件仿真的结果进行PSNR的计算，仿真结果如图3-5-5所示。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-5-5 仿真结果&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/5/5.jpg&amp;quot; /&gt;&lt;br /&gt;
图3-5-5 仿真结果，左侧为请求响应模式下的HDL功能仿真结果，中间为流水线模式下的HDL功能仿真结果，右侧为软件仿真结果
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.5.5 资源和时序&lt;/h3&gt;
&lt;p&gt;由于两种模式的基本构成大致相同，所以只对第一种模式进行分析，根据Vivado生成的报表，主要资源耗费如表3-5-5。&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;Slice LUTs*&lt;/th&gt;
&lt;th&gt;Slice Registers&lt;/th&gt;
&lt;th&gt;DSP&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;38&lt;/td&gt;
&lt;td&gt;27&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-5-5 主要资源耗费
&lt;/center&gt;
&lt;br&gt;
根据时序报告，最大的Data Path Delay(数据路径延迟)为2.746ns，即：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;FMax = 364.16MHz  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;即说明，CT核在流水线模式下，理论上在处理1080p全高清图像时可以达到175帧。&lt;br /&gt;
由于数据路径延迟和应用的最终约束设置强相关，所以仅供参考。&lt;/p&gt;
&lt;h3&gt;3.5.6 分析与结论&lt;/h3&gt;
&lt;p&gt;根据仿真结果计算PSNR，得到的数据如表3-5-6。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;1-0.2&lt;/th&gt;
&lt;th&gt;1-3.3&lt;/th&gt;
&lt;th&gt;2-0.2&lt;/th&gt;
&lt;th&gt;2-3.3&lt;/th&gt;
&lt;th&gt;Total&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;55.11&lt;/td&gt;
&lt;td&gt;61.09&lt;/td&gt;
&lt;td&gt;55.09&lt;/td&gt;
&lt;td&gt;59.53&lt;/td&gt;
&lt;td&gt;57.70&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-5-6 PSNR
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;PSNR均值为57.70，可见CT核满足处理要求，同时可以达到不错的FMax，设计成功。&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;参考文献&lt;/h1&gt;
&lt;p&gt;[3] Donald G.Bailey.基于FPGA的嵌入式图像处理系统设计[M].原魁,何文浩,肖晗译.北京:电子工业出版社,2013  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;感谢&lt;/h1&gt;
&lt;p&gt;仿真图像来源：&lt;br /&gt;
&lt;a href=&amp;quot;http://www.pixiv.net/member_illust.php?mode=medium&amp;amp;illust_id=31000682&amp;quot;&gt;LM7-sn&lt;/a&gt;  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Saturday, 16 May 2015 23:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.05.16 23:00:article/Skill-2015_05_16_b</guid>
<category>FPGA</category>
<category>FPGA-Imaging-Library</category>
<category>图像处理</category>
<category>点操作</category>
<category>对比度变换</category>
</item>

<item>
<title>【FPGA/图像处理】点操作——阈值化</title>
<link>http://dtysky.moe/article/Skill-2015_05_16_a</link>
<description>&lt;p&gt;图像处理系列文章基本都是我本科毕设论文同步发布，这个项目以LGPL许可被发布在&lt;a href=&amp;quot;http://fil.dtysky.moe&amp;quot;&gt;F-I-L&lt;/a&gt;。阈值化是另一个基本操作，和灰度化一样，它简单地将图像像素分为两类，主要目的是简化后续的计算成本，以及节省存储空间，不过更为彻底。阈值化有二值阈值化，也有多值阈值化，但运用最多的还是二值阈值化，即“二值化”，经过二值化处理后的图像只有两个值——黑色和白色，这样便可以用最小的代价来表示整幅图像的形态特征。阈值化往往被用作某些操作的预处理，比如某些形态学操作(腐蚀，膨胀)就是基于二值图像的。阈值化的阈值可以有许多种来源，由此可以区分为自动阈值化、局部阈值化等等，但本节只讨论最基本的全局阈值化算法。&lt;br /&gt;
这个IP核的资源在这里：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/FPGA-Imaging-Library/tree/Publish/Point/Threshold&amp;quot;&gt;Threshold&lt;/a&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;3 算法实现&lt;/h1&gt;
&lt;p&gt;明确了设计和架构，便可以进行算法的实现。本章将会说明如何实现图像处理的算法，以及如何运用它们。  &lt;/p&gt;
&lt;h2&gt;3.4 点操作——阈值化&lt;/h2&gt;
&lt;p&gt;阈值化是另一个基本操作，和灰度化一样，它简单地将图像像素分为两类，主要目的是简化后续的计算成本，以及节省存储空间，不过更为彻底。阈值化有二值阈值化，也有多值阈值化，但运用最多的还是二值阈值化，即“二值化”，经过二值化处理后的图像只有两个值——黑色和白色，这样便可以用最小的代价来表示整幅图像的形态特征。阈值化往往被用作某些操作的预处理，比如某些形态学操作(腐蚀，膨胀)就是基于二值图像的。阈值化的阈值可以有许多种来源，可以为线性的，也可以为非线性的，由此可以区分为自动阈值化、局部阈值化等等，但本节只讨论最基本的全局阈值化算法。  &lt;/p&gt;
&lt;h3&gt;3.4.1 原理&lt;/h3&gt;
&lt;p&gt;所有的全局阈值化算法都有一个共同的特点，即整张图像都使用同一个阈值，这样做的基本策略是将图像中的每一个像素都与一个固定的阈值进行比较，然后根据比较的结果确定输出。一般的全局阈值化算法原理如式3-4-1，当某个像素的值I大于确定的阈值th时，输出结果Q为1，否则为0。  &lt;/p&gt;
&lt;p&gt;$$Q=\begin{cases} 0 &amp;&amp; I &lt;= th \\1 &amp; &amp; I &gt; th \ \end{cases}.\ \ \ \ \ \ \ \ (3-4-1)$$&lt;/p&gt;

&lt;p&gt;这样做有一个明显的缺点，就是会造成一些“误分类”，即将一些像素分类到我们所不期望的一侧去，这可以通过调整阈值来确定，但这并不总是有效的。由此，便产生了像是了局部阈值化这样的算法来解决这个问题，但这类算法往往要求比较复杂的前置过程，除了这类算法之外，在一些状况下还有一种简单的算法——等高线阈值化&lt;sup&gt;[3]&lt;/sup&gt;。&lt;br /&gt;
等高线阈值化的原理如式3-4-2，它要求两个阈值th1和th2，处于二者之间的像素被分类到1，否则为0。之所以称为等高线阈值化，是因为在像素值变化缓慢的图像中，像素的选择就像地图上的等高线一样。一般在选取同样合理的阈值的状况下，等高线阈值化能够比一般的全局阈值化保留更丰富的边界信息，在一些要求情况下也可以直接将其作为边界检测子来使用。  &lt;/p&gt;
&lt;p&gt;$$Q=\begin{cases} 0 &amp;&amp; I &lt;= th1 \\1 &amp; &amp; th1 &lt; I &lt;=  th2  \\ 0 &amp; &amp; I &gt; th2 \ \end{cases}.\ \ \ \ \ \ \ \ (3-4-2)$$&lt;/p&gt;

&lt;p&gt;可见，阈值化运算并不涉及数值计算，只有简单的比较和分类，所以理论上可以直接采用组合逻辑实现。&lt;/p&gt;
&lt;h3&gt;3.4.2 设计&lt;/h3&gt;
&lt;p&gt;根据原理可知，由于逻辑比较简单清晰，所以只需要考虑第2章的接口标准外即可，并且可以直接使用组合逻辑实现，但考虑到一个单独的模块采用组合逻辑可能会造成与其他模块组合逻辑的串联，这样会增加整体的路径延迟，降低FMax，故本库中即使是一些简单的操作也会至少使用加一级缓冲，采用一级流水线进行实现。综上，实现一个Threshold核(以下简称TH核)需要配置参数、端口分别如表3-4-1和表3-4-2所示。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;work_mode&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;0为流水线模式，1为请求响应模式&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;模块的工作模式。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;color_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;1 - 12&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;色彩位宽。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-4-1 配置参数
&lt;/center&gt;&lt;br /&gt;
&lt;br&gt;
&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;端口&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;td&gt;clk&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;Clock.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rst_n&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;复位，低有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;tr&gt;
&lt;td&gt;th_mode&lt;/td&gt;
&lt;td&gt;input&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;0为基本全局阈值化，1为等高线阈值化&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;操作方法。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;th1&lt;/td&gt;
&lt;td&gt;input&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;阈值1，用于两种模式。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;th2&lt;/td&gt;
&lt;td&gt;input&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;阈值2，只能用于等高线阈值化模式。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_enable&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据使能，在流水线模式下，它是另一个复位信号，在请求响应模式下，只有在它有效的时候in_data才会被真正地改变。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_data&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据，必须和in_enable同步输入。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_ready&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据有效，在两种模式下，这个信号都会在out_data可以被读取的时候有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_data&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据，将会和out_ready同步输出。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-4-2 端口
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.4.3 实现&lt;/h3&gt;
&lt;p&gt;根据3.4.2的设计便可以实现一个TH核，流水线和请求响应两种模式的实现方式如下：  &lt;/p&gt;
&lt;h4&gt;3.4.3.1 流水线模式&lt;/h4&gt;
&lt;p&gt;保证地址和数据同步流水化输出，在复位时二者皆输出为0，实现如下：&lt;br /&gt;
in_enable或rst_n为低时，out_ready输出为0，即输出无效，系统不工作；否则系统根据th_mode、th1和th2的值进行工作，一个周期后输出第一个数据，并且每一个clk的上升沿都会送入一个数据in_data进行处理，第一个输出数据有效之后，每一个周期都将送出一个有效数据，波形如图3-4-1。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-4-1 流水线模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/4/1.png&amp;quot; /&gt;&lt;br /&gt;
图3-4-1 流水线模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.4.3.2 请求响应模式&lt;/h4&gt;
&lt;p&gt;基本同3.4.3.1，但输入数据in_data只有在in_enbale的上升沿才会被送入处理，波形如图3-4-2。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-4-2 请求响应模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/4/2.png&amp;quot; /&gt;&lt;br /&gt;
图3-4-2 请求响应模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.4.3.3 IP核GUI&lt;/h4&gt;
&lt;p&gt;完成功能后对TH核进行了封装，封装如图3-4-3，work_mode被设计为键值对的模式，方便用户理解和选择，其它参数都根据实际状况加上了范围限定。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-4-3 TH核的GUI&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/4/3.png&amp;quot; /&gt;&lt;br /&gt;
图3-4-3 TH核的GUI
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.4.4 仿真&lt;/h3&gt;
&lt;p&gt;TH核只对灰度图像有意义，所以我选取了两张灰度模式的图像，并分别设置了两套参数(理论上可以设置任意套参数)，对一般全局阈值化和等高线阈值化的模式进行配置，参数如表3-4-3。  &lt;/p&gt;
&lt;p&gt;&lt;/center&gt;&lt;br /&gt;
&lt;br&gt;
&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;mode&lt;/th&gt;
&lt;th&gt;th1&lt;/th&gt;
&lt;th&gt;th2&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Base&lt;/td&gt;
&lt;td&gt;128&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Contour&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-4-3 仿真参数
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;其中Base模式为一般全局阈值化，而Contour为等高线阈值化，对每种参数进行流水线和请求响应模式的测试，原始图像如图3-4-4，每一张图像的测试流程如下： &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;流水线模式conf1 -&amp;gt; 存入*-conf1-pipeline-hdlfun.*内 -&amp;gt; 请求响应模式conf1 -&amp;gt; 存入*-conf1-reqack-hdlfun.*内 -&amp;gt; 流水线模式conf2 -&amp;gt; 存入*-conf2-pipeline-hdlfun.*内 -&amp;gt; 请求响应模式conf2 -&amp;gt; 存入*-conf2-reqack-hdlfun.*内。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;center&gt; 
&lt;img alt=&amp;quot;图3-4-4 原始测试图像&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/4/4.jpg&amp;quot; /&gt;&lt;br /&gt;
图3-4-4 原始测试图像
&lt;/center&gt;  &lt;/p&gt;
&lt;p&gt;使用HDL功能仿真和软件仿真的结果进行PSNR的计算，仿真结果如图3-4-5所示。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-4-5 仿真结果&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/4/5.jpg&amp;quot; /&gt;&lt;br /&gt;
图3-4-5 仿真结果，左侧为请求响应模式下的HDL功能仿真结果，中间为流水线模式下的HDL功能仿真结果，右侧为软件仿真结果
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.4.5 资源和时序&lt;/h3&gt;
&lt;p&gt;由于两种模式的基本构成大致相同，所以只对第一种模式进行分析，根据Vivado生成的报表，主要资源耗费如表3-4-4。&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;Slice LUTs*&lt;/th&gt;
&lt;th&gt;Slice Registers&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-4-4 主要资源耗费
&lt;/center&gt;
&lt;br&gt;
根据时序报告，最大的Data Path Delay(数据路径延迟)为2.671ns，即：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;FMax = 374.39MHz  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;即说明，TH核在流水线模式下，理论上在处理1080p全高清图像时可以达到180帧。&lt;br /&gt;
由于数据路径延迟和应用的最终约束设置强相关，所以仅供参考。&lt;/p&gt;
&lt;h3&gt;3.4.6 分析与结论&lt;/h3&gt;
&lt;p&gt;首先分析基本全局阈值化和等高线阈值化的效果，如图3-4-6和3-4-7所示，在给定参数的情况下，等高线阈值化对于边缘的保留的确更为完整，分类更为有效。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt; 
&lt;img alt=&amp;quot;图3-4-6 一般全局阈值化结果&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/4/6.bmp&amp;quot; /&gt;&lt;br /&gt;
图3-4-6 一般全局阈值化结果
&lt;/center&gt;&lt;br /&gt;
&lt;center&gt; 
&lt;img alt=&amp;quot;图3-4-7 等高线阈值化结果&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/4/7.bmp&amp;quot; /&gt;&lt;br /&gt;
图3-4-7 等高线阈值化结果
&lt;/center&gt;  &lt;/p&gt;
&lt;p&gt;根据仿真结果计算PSNR，得到的数据如表3-4-5。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;1-Base-128-0&lt;/th&gt;
&lt;th&gt;1-Contour-50-200&lt;/th&gt;
&lt;th&gt;2-Base-128-0&lt;/th&gt;
&lt;th&gt;2-Contour-50-200&lt;/th&gt;
&lt;th&gt;Total&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-4-5 PSNR
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;PSNR均值为极大值，可见TH核和软件处理完全等效，同时可以达到不错的FMax，设计成功。&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;参考文献&lt;/h1&gt;
&lt;p&gt;[3] Donald G.Bailey.基于FPGA的嵌入式图像处理系统设计[M].原魁,何文浩,肖晗译.北京:电子工业出版社,2013  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;感谢&lt;/h1&gt;
&lt;p&gt;仿真图像来源：&lt;br /&gt;
&lt;a href=&amp;quot;http://www.pixiv.net/member_illust.php?mode=medium&amp;amp;illust_id=10599535&amp;quot;&gt;H2SO4-蝶&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;http://www.pixiv.net/member_illust.php?mode=medium&amp;amp;illust_id=28762438&amp;quot;&gt;パセリ-Favour&lt;/a&gt;  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Saturday, 16 May 2015 01:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.05.16 01:00:article/Skill-2015_05_16_a</guid>
<category>FPGA</category>
<category>FPGA-Imaging-Library</category>
<category>图像处理</category>
<category>点操作</category>
<category>阈值化</category>
</item>

<item>
<title>【FPGA/图像处理】点操作——灰度化</title>
<link>http://dtysky.moe/article/Skill-2015_05_15_a</link>
<description>&lt;p&gt;图像处理系列文章基本都是我本科毕设论文同步发布，这个项目以LGPL许可被发布在&lt;a href=&amp;quot;http://fil.dtysky.moe&amp;quot;&gt;F-I-L&lt;/a&gt;。灰度化是最基本的图像操作之一，它的目的是把一个具有RGB三个灰度通道的图像转换为只具有一个灰度通道的图像，这样做的目的主要是减少后期操作的运算量，因为对于许多应用而言，例如边缘检测、角点识别等，一个灰度通道就已经提供了足够的信息量，甚至在很多情况下，多通道的灰度图会在提高计算复杂度的同时降低运算效果。灰度化属于点操作，一个像素的输出只取决于一个像素的输入，输出像素是输入像素的一个映射，本节将会探讨如何用FPGA实现图像的灰度化。&lt;br /&gt;
这个IP核的资源在这里：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/FPGA-Imaging-Library/tree/Publish/Point/Graying&amp;quot;&gt;Graying&lt;/a&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;3 算法实现&lt;/h1&gt;
&lt;p&gt;明确了设计和架构，便可以进行算法的实现。本章将会说明如何实现图像处理的算法，以及如何运用它们。  &lt;/p&gt;
&lt;h2&gt;3.3 点操作——灰度化&lt;/h2&gt;
&lt;p&gt;灰度化是最基本的图像操作之一，它的目的是把一个具有RGB三个灰度通道的图像转换为只具有一个灰度通道的图像，这样做的目的主要是减少后期操作的运算量，因为对于许多应用而言，例如边缘检测、角点识别等，一个灰度通道就已经提供了足够的信息量，甚至在很多情况下，多通道的灰度图会在提高计算复杂度的同时降低运算效果。灰度化属于点操作，一个像素的输出只取决于一个像素的输入，输出像素是输入像素的一个映射，本节将会探讨如何用FPGA实现图像的灰度化。 &lt;/p&gt;
&lt;h3&gt;3.3.1 原理&lt;/h3&gt;
&lt;p&gt;灰度化的算法有许多种，最直观的如式3-3-1，即将去三个通道的平均值作为灰度化的结果，这个算法虽然符合一般的逻辑规律，但却不符合人类的视觉，业界通用的灰度化算法如式3-3-2，这个算法在ITU-R(ITU Radiocommunication Sector，国际电信联盟无线电通信组)的ITU BT.601建议书&lt;sup&gt;[12]&lt;/sup&gt;中被定义。根据彩色电视系统的传输要求，色彩信号被分为亮度信号Y和色差信号R-Y、G-Y与B-Y，实际传输时只需要传输亮度信号和任意两个色差信号即可。实际上，亮度信号是根据人类的视觉心理原理计算的，它体现了各个基色的亮度总和。  &lt;/p&gt;
&lt;p&gt;$$Y = \frac{Red + Green + Blue}3\ \ \ \ \ \ \ \ (3-3-1)$$&lt;br /&gt;
$$Y = Red * 0.299 + Green* 0.587 + Blue * 0.114\ \ \ \ \ \ \ \ (3-3-2)$$  &lt;/p&gt;
&lt;p&gt;所以一次灰度化运算要执行三次乘法和两次加法，其中每一次乘法都是一个固定系数的小数和一个整数的乘法，加法的位数根据每一个通道的色彩位宽而定，由于本项目中色彩位宽被限定为1-12，考虑到FPGA的特性，在一次灰度化运算中，最多可能需要执行三次定点数乘法和两次12位的无符号加法运算。&lt;/p&gt;
&lt;h3&gt;3.3.2 设计&lt;/h3&gt;
&lt;p&gt;根据原理可知，除了需要考虑第2章的接口标准外，还需要使用三个乘法器和两个加法器，乘法器必须使用厂商提供的专用IP核实现，同时为了最高的FMax，一个周期内实现两次12位的加法是不被允许的，所以我将两次加法进行了拆分，设置了缓冲寄存器。不仅如此，考虑到实际应用场合的复杂，完全设定好的乘法器并不能够满足要求，所以我选择将乘法器的配置权交给用户，设定一个配置参数来让用户在配置好乘法器后修改流水线级数，以匹配乘法器的配置，综上，实现一个Graying核(以下简称GY核)需要配置参数、端口以及子模块分别如表3-3-1、表3-3-2和表3-3-3所示。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;work_mode&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;0为流水线模式，1为请求响应模式&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;模块的工作模式。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;color_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;1 - 12&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;色彩位宽。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mul_delay&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;取决于乘法器的配置。&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;乘法器延迟&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-3-1 配置参数
&lt;/center&gt;&lt;br /&gt;
&lt;br&gt;
&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;端口&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;td&gt;clk&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;Clock.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rst_n&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;复位，低有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_enable&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据使能，在流水线模式下，它是另一个复位信号，在请求响应模式下，只有在它有效的时候in_data才会被真正地改变。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_data&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据，必须和in_enable同步输入。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_ready&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据有效，在两种模式下，这个信号都会在out_data可以被读取的时候有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_data&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据，将会和out_ready同步输出。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-3-2 端口
&lt;/center&gt;&lt;br /&gt;
&lt;br&gt;
&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&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;tr&gt;
&lt;td&gt;MulRed&lt;/td&gt;
&lt;td&gt;MultiplierRedx0d299&lt;/td&gt;
&lt;td&gt;12位无符号数和0.299的定点乘法器，被用于红色通道的计算。你可以自己配置这个乘法器，然后更改&amp;quot;mul_delay&amp;quot;，但所有的乘法器必须拥有相同的流水线级数，并不能更改端口的配置！&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MulGreen&lt;/td&gt;
&lt;td&gt;MultiplierGreenx0d587&lt;/td&gt;
&lt;td&gt;12位无符号数和0.587的定点乘法器，被用于绿色通道的计算。你可以自己配置这个乘法器，然后更改&amp;quot;mul_delay&amp;quot;，但所有的乘法器必须拥有相同的流水线级数，并不能更改端口的配置！&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MulBlue&lt;/td&gt;
&lt;td&gt;MultiplierBluex0d114&lt;/td&gt;
&lt;td&gt;12位无符号数和0.113的定点乘法器，被用于蓝色通道的计算。你可以自己配置这个乘法器，然后更改&amp;quot;mul_delay&amp;quot;，但所有的乘法器必须拥有相同的流水线级数，并不能更改端口的配置！&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-3-3 子模块
&lt;/center&gt;&lt;/p&gt;
&lt;h3&gt;3.3.3 实现&lt;/h3&gt;
&lt;p&gt;根据3.3.2的设计便可以实现一个GY核，此核主要由一个三个执行定参数定点数的乘法器、一个输出使能延时计数器、复位系统和两次12位的无符号数加法构成。乘法器使用厂商提供的乘法器IP核，这里使用的是Vivado中的乘法器IP核，这个IP核可以被配置为许多种模式&lt;sup&gt;[13]&lt;/sup&gt;，对于定参数的乘法，它的实现方式可以被配置为Distributed Memory(分布式存储器),Block Memory(块存储器)以及Dedicated Multiplier(专用乘法器)，前两者相当于建立一个查找表，用查表的方式来计算乘法，其需求的最佳流水线级数比较小，但逻辑延迟比较大，后者相反，这个可以根据用户自身的需求而定，本节默认配置为专用乘法器实现，以得到理论上最大的FMax。同时，为了达到资源和精度之间的平衡，GY核将使用24位的定点数来近似表示每一个参数，例如对于0.299，它的定点数为：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;0.299 ≈ 0.010011001000101101000011 =  0.298999965...  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;可见完全满足要求，同时，为了达到最高的FMax，并考虑到此GY核对舍入方式不敏感，所以这里的舍入没有采用FR核(见3.1)，而是采用向下舍入的方式，直接将定点后的小数位截断，这等同于软件仿真中的int(x)强制类型转换。&lt;br /&gt;
而对于加法，由于要达到最高的FMax，在考察了Xilinx专用加法器的流水线后，发现12位无符号加法的最佳流水线级数为1级，所以直接采用自己设计的一级流水线和加法运算符即可实现，实现两次加法总共需要两级流水线，消耗两个周期。&lt;br /&gt;
综上，流水线和请求响应两种模式的实现方式如下：  &lt;/p&gt;
&lt;h4&gt;3.3.3.1 流水线模式&lt;/h4&gt;
&lt;p&gt;保证地址和数据同步流水化输出，在复位时二者皆输出为0，实现如下：&lt;br /&gt;
in_enable或rst_n为低时，输出使能延迟计数器不工作，out_ready输出为0，即输出无效，系统不工作；否则计数器工作，直到指定延迟周期(默认为5个周期)后，输出有效，并且每一个clk的上升沿都会送入一个数据in_data进行处理，第一个输出数据有效之后，每一个周期都将送出一个有效数据，波形如图3-3-1。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-3-1 流水线模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/3/1.png&amp;quot; /&gt;&lt;br /&gt;
图3-3-1 流水线模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.3.3.2 请求响应模式&lt;/h4&gt;
&lt;p&gt;基本同3.3.3.1，但输入数据in_data只有在in_enbale的上升沿才会被送入处理，波形如图3-3-2。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-3-2 请求响应模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/3/2.png&amp;quot; /&gt;&lt;br /&gt;
图3-3-2 请求响应模式时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.3.3.3 IP核GUI&lt;/h4&gt;
&lt;p&gt;完成功能后对GY核进行了封装，封装如图3-3-3，work_mode被设计为键值对的模式，方便用户理解和选择，其它参数都根据实际状况加上了范围限定。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-3-3 GY核的GUI&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/3/3.png&amp;quot; /&gt;&lt;br /&gt;
图3-3-3 GY核的GUI
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.3.4 仿真&lt;/h3&gt;
&lt;p&gt;GY核只对RGB图像有意义，所以我选取了三张RGB模式的图像，分别对它们进行了流水线和请求响应模式的测试，原始图像如图3-3-4，每一张图像的测试流程如下： &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;流水线模式 -&amp;gt; 存入*-pipeline-hdlfun.*内 -&amp;gt; 请求响应模式 -&amp;gt; 存入*-reqack-hdlfun.*内。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;center&gt; 
&lt;img alt=&amp;quot;图3-3-4 原始测试图像&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/3/4.jpg&amp;quot; /&gt;&lt;br /&gt;
图3-3-4 原始测试图像
&lt;/center&gt;  &lt;/p&gt;
&lt;p&gt;使用HDL功能仿真和软件仿真的结果进行PSNR的计算，仿真结果如图3-3-5所示。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-3-5 仿真结果&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/3/5.jpg&amp;quot; /&gt;&lt;br /&gt;
图3-3-5 仿真结果，左侧为请求响应模式下的HDL功能仿真结果，中间为流水线模式下的HDL功能仿真结果，右侧为软件仿真结果
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.3.5 资源和时序&lt;/h3&gt;
&lt;p&gt;由于两种模式的基本构成大致相同，所以只对第一种模式进行分析，并且由于这里的乘法器配置采用的是专用乘法器，所以资源消耗和其他模式可能差距较大，仅供参考。根据Vivado生成的报表，主要资源耗费如表3-3-4。&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;Slice LUTs*&lt;/th&gt;
&lt;th&gt;Slice Registers&lt;/th&gt;
&lt;th&gt;DSPs&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;28&lt;/td&gt;
&lt;td&gt;26&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-3-3 主要资源耗费
&lt;/center&gt;
&lt;br&gt;
根据时序报告，最大的Data Path Delay(数据路径延迟)为2.265ns，即：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;FMax = 441.50MHz  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;即说明，GY核在流水线模式下，理论上在处理1080p全高清图像时可以达到212帧。&lt;br /&gt;
由于数据路径延迟和应用的最终约束设置强相关，所以仅供参考。&lt;/p&gt;
&lt;h3&gt;3.3.6 分析与结论&lt;/h3&gt;
&lt;p&gt;根据仿真结果计算PSNR，得到的数据如表3-3-4。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;1&lt;/th&gt;
&lt;th&gt;2&lt;/th&gt;
&lt;th&gt;3&lt;/th&gt;
&lt;th&gt;Total&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;46.73&lt;/td&gt;
&lt;td&gt;46.96&lt;/td&gt;
&lt;td&gt;46.83&lt;/td&gt;
&lt;td&gt;46.84&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-3-4 PSNR
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;PSNR均值为46.84，可见GY核完全满足图像处理的要求，在配置灵活性高的情况下可以达到很高的FMax，设计成功。&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;参考文献&lt;/h1&gt;
&lt;p&gt;[12] ITU-R, Recommendation ITU-R BT.601-7, Studio encoding parameters of digital television for standard 4:3 and wide-screen 16:9 aspect ratios [EB/OL]. 03/2011&lt;br /&gt;
[13] Xilinx, Vivado Design Suite, LogiCORE IP Multiplier v12.0, Product Guide, PG108 [EB/OL]. April 2, 2014  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;感谢&lt;/h1&gt;
&lt;p&gt;仿真图像来源：&lt;br /&gt;
&lt;a href=&amp;quot;http://www.pixiv.net/member_illust.php?mode=medium&amp;amp;illust_id=39383610&amp;quot;&gt;041-尾翼&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;http://www.pixiv.net/member_illust.php?mode=medium&amp;amp;illust_id=43935854&amp;quot;&gt;月岡月穂-星を呑む&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;http://www.pixiv.net/member_illust.php?mode=manga&amp;amp;illust_id=47960736&amp;quot;&gt;LM7-xxxまとめ&lt;/a&gt;  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Friday, 15 May 2015 18:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.05.15 18:00:article/Skill-2015_05_15_a</guid>
<category>FPGA</category>
<category>FPGA-Imaging-Library</category>
<category>图像处理</category>
<category>点操作</category>
<category>灰度化</category>
</item>

<item>
<title>【FPGA/图像处理】生成器——帧控制</title>
<link>http://dtysky.moe/article/Skill-2015_05_14_a</link>
<description>&lt;p&gt;图像处理系列文章基本都是我本科毕设论文同步发布，这个项目以LGPL许可被发布在&lt;a href=&amp;quot;http://fil.dtysky.moe&amp;quot;&gt;F-I-L&lt;/a&gt;。帧缓存是FPGA图像处理的一个基本单元，它缓存一张完整的图像，而一张完整的图像是所有图像处理的基础，它为一切操作提供数据源，所以它的泛用性是很高的。一般FPGA中的帧缓存都是用ram来实现的，这些ram可以分为sram和sdram两种，前者控制简单，效率高，后者则控制较为复杂。这一节将会探讨如何使用Xilinx的FPGA中的RAM资源作为帧缓存，并对其进行控制。&lt;br /&gt;
这个IP核的资源在这里：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/FPGA-Imaging-Library/tree/Publish/Generator/FrameController&amp;quot;&gt;FrameController&lt;/a&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;3 算法实现&lt;/h1&gt;
&lt;p&gt;明确了设计和架构，便可以进行算法的实现。本章将会说明如何实现图像处理的算法，以及如何运用它们。  &lt;/p&gt;
&lt;h2&gt;3.2 生成器——帧控制&lt;/h2&gt;
&lt;p&gt;帧缓存是FPGA图像处理的一个基本单元，它缓存一张完整的图像，而一张完整的图像是所有图像处理的基础，它为一切操作提供数据源，所以它的泛用性是很高的。一般FPGA中的帧缓存都是用RAM(Random Access Memory，随机存储器)来实现的，这些RAM可以常用的可以分为SRAM(Static Random Access Memory，静态随机存储器)和SDRAM(Synchronous Dynamic Random Access Memory，同步动态随机存储器)两种，前者控制简单，效率高，后者则控制较为复杂。这一节将会探讨如何设计一个FramController核(以下简称FC核)对以Xilinx的FPGA中的RAM资源为基础的帧缓存进行控制。 &lt;/p&gt;
&lt;h3&gt;3.2.1 原理&lt;/h3&gt;
&lt;p&gt;一般FPGA器件中的片内RAM分为两种，BlockRAM（块存储器，以下称BRAM）和DistributeRAM（分布式存储器，以下称DRAM）&lt;sup&gt;[9]&lt;/sup&gt;，两者本质上都是SRAM。不同的是前者是一些被集中在一些区域的专用存储器，后者是利用器件内的LUT构成的RAM。一般而言，BRAM的资源比较丰富，并且速度比较快，但由于其自身的特性，这些RAM被分为36K和18K两种形式，使用BRAM的单位是18Kb的倍数，所以在很多情况下会造成资源的浪费，另一方面，由于这些RAM的位置是固定的，所以在布局布线上可能会造成更多的延迟。而分布式RAM则正好相反，由于使用的是LUT，所以资源比较少，但比较灵活，不容易造成浪费。&lt;br /&gt;
对于帧缓存，考虑其一般比较大并且读写要求比较高，采用BRAM比较合适，但即使是BRAM，对于一些比较大的图像也是难以胜任的，中低端的FPGA器件的片内BRAM是比较少的，比如对于用于测试的xlg7Z010clg400，片内RAM只有256kb&lt;sup&gt;[10]&lt;/sup&gt;。这时只能够采用SDRAM进行扩展，但考虑到本项目主要论述的是图像处理的算法，所以不加以讨论。&lt;br /&gt;
在Vivado中，RAM的配置被分为许多种&lt;sup&gt;[11]&lt;/sup&gt;，比如Single Port RAM(单口RAM，只有一个读写数据的端口)，Simple Dual Port RAM(简单双口RAM，两个端口，但是只有一个端口可以写入)，True Dual Port RAM(真双口RAM，两个端口都可以写入)等，对于帧缓存，一般采用的是Simple Dual Port RAM，这种配置方式下的RAM读写时序如图3-2-1&lt;sup&gt;[11]&lt;/sup&gt;所示。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-2-1 RAM写时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/2/1.png&amp;quot; /&gt;&lt;br /&gt;
图3-2-1 RAM读写时序
&lt;/center&gt;&lt;br /&gt;
可见，对于写操作，只需要使得地址和数据同步送入RAM相应端口即可，而对于读操作，则需要有一定的延迟，这个延迟是根据配置时选择插入的寄存器来决定的，对于不同的应用插入的级数不同，Vivado中可以最多插入三个寄存器来形成三级流水线，如图3-2-2&lt;sup&gt;[11]&lt;/sup&gt;所示。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-2-2 三级流水线下的读操作&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/2/2.png&amp;quot; /&gt;&lt;br /&gt;
图3-2-2 三级流水线下的读操作
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.2.2 设计&lt;/h3&gt;
&lt;p&gt;根据原理并综合第2章的接口标准，实现一个针对Xilinx器件的BRAM控制器，即这里的FC核需要的配置参数和端口分别如表3-2-1和表3-2-2所示。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;work_mode&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;0为流水线模式，1为请求响应模式&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;模块的工作模式。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;wr_mode&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;0为写，1为读。&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;模块的读写模式。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;data_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;数据位宽。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;im_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;1 - 4096&lt;/td&gt;
&lt;td&gt;320&lt;/td&gt;
&lt;td&gt;图像宽度。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;im_height&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;1 - 4096&lt;/td&gt;
&lt;td&gt;240&lt;/td&gt;
&lt;td&gt;图像高度。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;addr_width&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;取决于图像的宽度和高度。&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;存储帧缓存的RAM的地址位宽。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ram_read_latency&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;0 - 15，取决于RAM。&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;RAM的读延迟，在Xilin器件中，典型为2。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;row_init&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;取决于输入的行偏移。&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;你想要写入的第一行的偏移，取决于应用，比如窗口。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-2-1 配置参数
&lt;/center&gt;&lt;br /&gt;
&lt;br&gt;
&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;端口&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;范围&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;clk&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;Clock.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rst_n&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;复位，低有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_enable&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据使能，在流水线模式下，它是另一个复位信号，在请求响应模式下，只有在它有效的时候in_data才会被真正地改变。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in_data&lt;/td&gt;
&lt;td&gt;输入&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输入数据，必须和in_enable同步输入。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_ready&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据有效，在两种模式下，这个信号都会在out_data可以被读取的时候有效。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out_data&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;color_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出数据，将会和out_ready同步输出。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ram_addr&lt;/td&gt;
&lt;td&gt;output&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;addr_width - 1 : 0&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;输出到RAM的地址。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-2-2 端口
&lt;/center&gt;&lt;/p&gt;
&lt;h3&gt;3.2.3 实现&lt;/h3&gt;
&lt;p&gt;根据配置参数和端口的设计便可以实现一个FC核，此核主要由一个地址输出计数器、一个读使能延时计数器和复位系统构成，由于工作模式有流水线和请求响应两种，读写模式也有两种，所以总共有四种模式，这些模式的实现方式如下：  &lt;/p&gt;
&lt;h4&gt;3.2.3.1 流水线模式写&lt;/h4&gt;
&lt;p&gt;保证地址和数据同步流水化输出，在复位时二者皆输出为0，实现如下：&lt;br /&gt;
in_enable或rst_n为低时，out_ready输出为0，即输出无效，此时地址计数器不工作；否则输出有效，计数器在每个clk的上升沿加1，直到加满根据用户设定的宽和高算出来的地址最大值，开始下一次循环，波形如图3-2-3。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-2-3 流水线模式写入时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/2/3.png&amp;quot; /&gt;&lt;br /&gt;
图3-2-3 流水线模式写入时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.2.3.2 流水线模式读&lt;/h4&gt;
&lt;p&gt;基本同3.2.3.1，但在工作模式时，读使能计数器首先有效，在地址输入后的ram_read_latency个周期后，读使能计数器锁定，并输出第一个有效值，之后每个周期都会输出一个有效值，波形如图3-2-4。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-2-4 流水线模式读出时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/2/4.png&amp;quot; /&gt;&lt;br /&gt;
图3-2-4 流水线模式读出时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.2.3.3 请求响应模式写&lt;/h4&gt;
&lt;p&gt;地址和数据同步输出，一次请求一次响应输出，复位时输出为0，实现如下：&lt;br /&gt;
基本同3.2.3.1，但地址计数器只有在in_enable的上升沿才会加1，波形如图3-2-5。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-2-5 请求响应模式写入时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/2/5.png&amp;quot; /&gt;&lt;br /&gt;
图3-2-5 请求响应模式写入时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.2.3.4 请求响应模式读&lt;/h4&gt;
&lt;p&gt;基本同3.2.3.2，但变为一次请求一次响应输出，地址计数器只有在in_enable的上升沿才会加1，波形如图3-2-6。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-2-6 请求响应模式写入时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/2/6.png&amp;quot; /&gt;&lt;br /&gt;
图3-2-6 请求响应模式写入时序
&lt;/center&gt;  &lt;/p&gt;
&lt;h4&gt;3.2.3.5 IP核GUI&lt;/h4&gt;
&lt;p&gt;完成功能后对FC核进行了封装，封装如图3-2-7，work_mode和wr_mode被设计为键值对的模式，方便用户理解和选择，其它参数都根据实际状况加上了范围限定，addr_width使用expr表达式交由软件自行计算。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-2-7 FC核的GUI&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/2/7.png&amp;quot; /&gt;&lt;br /&gt;
图3-2-7 FC核的GUI
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.2.4 仿真&lt;/h3&gt;
&lt;p&gt;FC核没有软件仿真，并且综合考虑到BRAM在仿真时的配置不便和仿真效率，HDL功能仿真只支持512x512像素和灰度模式下的图像，我选取了三张这样的图像，分别对它们进行了每一种模式进行了测试，每一张图像的测试流程如下： &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;流水线模式写RAM的仿真模型 -&amp;gt; 流水线模式读RAM的仿真模型 -&amp;gt; 存入*-pipeline-hdlfun.*内 -&amp;gt; 请求响应模式写RAM的仿真模型 -&amp;gt; 请求响应模式读RAM的仿真模型 -&amp;gt; 存入*-reqack-hdlfun.*内。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;由于没有软件仿真，所以将原图像作为了软件仿真的结果，用来进行PSNR的计算，仿真结果如图3-2-8所示，保存为bmp格式是为了防止压缩时带来的差异影响到PSNR的计算。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图3-2-8 仿真结果&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/3/2/8.jpg&amp;quot; /&gt;&lt;br /&gt;
图3-2-8 仿真结果，左侧为请求响应模式下的HDL功能仿真结果，中间为流水线模式下的HDL功能仿真结果，右侧为软件仿真结果
&lt;/center&gt;  &lt;/p&gt;
&lt;h3&gt;3.2.5 资源和时序&lt;/h3&gt;
&lt;p&gt;由于四种模式的基本构成大致相同，所以只对第一种模式进行分析，根据Vivado生成的报表，主要资源耗费如表3-2-3。&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;Slice LUTs*&lt;/th&gt;
&lt;th&gt;Slice Registers&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;38&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-2-3 主要资源耗费
&lt;/center&gt;
&lt;br&gt;
根据时序报告，最大的Data Path Delay(数据路径延迟)为1.921ns，即：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;FMax = 520.56MHz  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;由于数据路径延迟和应用的最终约束设置强相关，所以仅供参考。&lt;/p&gt;
&lt;h3&gt;3.2.6 分析与结论&lt;/h3&gt;
&lt;p&gt;根据仿真结果计算PSNR，得到的数据如表3-2-4。  &lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;1&lt;/th&gt;
&lt;th&gt;2&lt;/th&gt;
&lt;th&gt;3&lt;/th&gt;
&lt;th&gt;Total&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;td&gt;1000000.00&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-2-4 PSNR
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;PSNR均值为极大值，可见FC核可以完美完成帧缓存的控制，同时资源利用率很低，可以达到很高的FMax，超过了BRAMFMax的极限，设计成功。&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;参考文献&lt;/h1&gt;
&lt;p&gt;[9] Xilinx, Xilinx 7 Series FPGAs Embedded Memory Advantages, WP377 (v1.1) [EB/OL]. February 17, 2012&lt;br /&gt;
[10] Xilinx,7 Series FPGAs Memory Resources, User Guide, UG473 (v1.11) [EB/OL]. November 12, 2014&lt;br /&gt;
[11] Xilinx, Vivado Design Suite, Block Memory Generator v8.2, LogiCORE IP Product Guide, PG058 [EB/OL]. April 1, 2015 &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;感谢&lt;/h1&gt;
&lt;p&gt;仿真图像来源：&lt;br /&gt;
&lt;a href=&amp;quot;http://www.pixiv.net/member_illust.php?mode=medium&amp;amp;illust_id=43409888&amp;quot;&gt;月岡月穂-ゆらゆら&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;http://www.pixiv.net/member_illust.php?mode=medium&amp;amp;illust_id=27556337&amp;quot;&gt;LM7-oxford eleKtricity&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;http://www.pixiv.net/member_illust.php?mode=medium&amp;amp;illust_id=45293430&amp;quot;&gt;cotta-池&lt;/a&gt;  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Thursday, 14 May 2015 17:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.05.14 17:00:article/Skill-2015_05_14_a</guid>
<category>FPGA</category>
<category>FPGA-Imaging-Library</category>
<category>帧控制</category>
<category>Block Ram</category>
<category>帧缓存</category>
</item>

<item>
<title>【FPGA/图像处理】算术系统</title>
<link>http://dtysky.moe/article/Skill-2015_05_11_b</link>
<description>&lt;p&gt;图像处理系列文章基本都是我本科毕设论文同步发布，这个项目以LGPL许可被发布在&lt;a href=&amp;quot;http://fil.dtysky.moe&amp;quot;&gt;F-I-L&lt;/a&gt;。图像处理中会用到一些基本的数学运算，这些运算构成一个算术系统，对于FPGA而言，如何在资源和运行频率之间保持平衡是一个基本的考量。本节将会说明如何在FPGA中实现符号数、定点数和函数的一些运算。     &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;3 算法实现&lt;/h1&gt;
&lt;p&gt;明确了设计和架构，便可以进行算法的实现。本章将会说明如何实现图像处理的算法，以及如何运用它们。  &lt;/p&gt;
&lt;h2&gt;3.1 算术系统&lt;/h2&gt;
&lt;p&gt;图像处理中会用到一些基本的数学运算，这些运算构成一个算术系统，对于FPGA而言，如何在资源和运行频率之间保持平衡是一个基本的考量。本节将会说明如何在FPGA中实现符号数、定点数和函数的一些运算。  &lt;/p&gt;
&lt;h3&gt;3.1.1 Verilog的符号系统&lt;/h3&gt;
&lt;p&gt;在VerilogHDL2001的标准&lt;sup&gt;[7]&lt;/sup&gt;中，符号系统被加入，在使用时只需要加一个&amp;quot;signed&amp;quot;标志便可以将某一个变量标记成符号数，例如：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;reg&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;signed&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mh&amp;quot;&gt;7&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;num&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这表示定义了一个八位的寄存器型符号数num。&lt;br /&gt;
在Verilog中，符号数是以如表3-1-1的形式存在的，这也是目前DSP中最流行的符号系统。可见，所有的正数以原码形式保存，而负数则以2的补码的形式存在，2的补码的公式定义如式3-1-1。  &lt;/p&gt;
&lt;p&gt;$$X = -2^{N-1} + \sum_{n=0}^{N-2}x_n 2^n\ \ \ \ \ \ \ \ (3-1-1)$$&lt;/p&gt;
&lt;p&gt;其在Verilog中的求得方式如下：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;comp&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;true_sign&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;~&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;true_num&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;其中comp表示补码,true_sign表示原码的符号位，true_num表示原码的数据位，可见，2的补码实质上就是在保留符号位的前提下，对数据位的每一位取反后整体加1。&lt;br /&gt;
&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;Decimal Value&lt;/th&gt;
&lt;th&gt;Signed Representation&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&amp;apos;b011&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3&amp;apos;b010&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3&amp;apos;b001&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;3&amp;apos;b000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;3&amp;apos;b111&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-2&lt;/td&gt;
&lt;td&gt;3&amp;apos;b110&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-3&lt;/td&gt;
&lt;td&gt;3&amp;apos;b101&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-4&lt;/td&gt;
&lt;td&gt;3&amp;apos;b100&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-1-1 3bits的符号数
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;在实际使用中，考虑到与软件系统的兼容性，由于软件系统底层的符号系统一般也是使用2的补码对负数进行处理，所以所有具有符号数输入的模块都要求采用补码形式，对于原码，我提供了一个原码转换补码的模块，模块实现如下：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;module&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;True2Comp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;complement&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;parameter&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;data_channel&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;parameter&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;data_width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;17&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;input&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;data_channel&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;data_width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;output&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;data_channel&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;data_width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;complement&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;genvar&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;generate&lt;/span&gt;
&lt;span class=&amp;quot;cp&amp;quot;&gt;        `define h (i + 1) * data_width - 1&lt;/span&gt;
&lt;span class=&amp;quot;cp&amp;quot;&gt;        `define l i * data_width&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;data_channel&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;begin&lt;/span&gt;
            &lt;span class=&amp;quot;k&amp;quot;&gt;assign&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;complement&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;no&amp;quot;&gt;`h&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;no&amp;quot;&gt;`l&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;no&amp;quot;&gt;`h&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;==&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; 
                &lt;span class=&amp;quot;n&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;no&amp;quot;&gt;`h&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;no&amp;quot;&gt;`l&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;mh&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;mb&amp;quot;&gt;&amp;#39;b1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;~&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;no&amp;quot;&gt;`h&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;no&amp;quot;&gt;`l&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;end&lt;/span&gt;
        &lt;span class=&amp;quot;no&amp;quot;&gt;`undef&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;h&lt;/span&gt;
        &lt;span class=&amp;quot;no&amp;quot;&gt;`undef&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;l&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;endgenerate&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;endmodule&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;此核为T2C核，T2C核的原理是——如果输入是正数，返回原码，否则对每一位进行取反，然后整体加1。T2C核可以完成任何位任何通道的原码到补码的转换。&lt;br /&gt;
Verilog中符号数的计算包含加法和乘法，减法使用相反数的加法代替，除法则需要专用电路实现，在简单应用中往往采用移位或者查找表来实现，这里不做讨论。在图像处理中，符号数的运算会涉及溢出问题，由于可以设置足够的保护位来防止运算本身的溢出，所以唯一需要考虑的溢出就是图像意义下的溢出，比如色彩为数为8的时候，255就是一个溢出上限，而由于一般状况下色彩和坐标都应当正值，所以0一般都作为一个溢出下限，所以这个时候需要一套舍入系统来完成结果的舍入，这一系统将在3.1.3中讨论。  &lt;/p&gt;
&lt;h3&gt;3.1.2 定点数系统&lt;/h3&gt;
&lt;p&gt;在FPGA的图像处理所需要的算术系统中，另一个重要的系统是定点数系统，它的存在可以让FPGA进行小数运算，这对于图像增强或者几何变换等操作是必要的。一般而言，可以把定点数系统看做是整数系统的一个扩展，或者说将整数系统看做定点数系统的一个特例。在定点数系统中，我们人为地在某两个数字之间插入一个小数点的标志，来分割整数部分和小数部分，如表3-1-2所示。它和整数系统唯一不同的地方在于，整数系统的这个小数点永远处于最低位的右侧。同时，定点数所表示的数值与一致，均为式3-1-2的形式，包括符号定点数的计算公式也是与式3-1-1一致的，但n的范围则由原来的&lt;p&gt;([0, \infty])&lt;/p&gt;，扩展到了&lt;p&gt;([-\infty, \infty])&lt;/p&gt;，所以表3-1-2表示的实际上是3.5。 &lt;br /&gt;
$$X = \sum x_n2^n\ \ \ \ \ \ \ \ (3-1-2)$$&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;Integral part&lt;/th&gt;
&lt;th&gt;Point&lt;/th&gt;
&lt;th&gt;Fractional part&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot; frame=&amp;quot;void&amp;quot;&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/td&gt;
&lt;td&gt;.&lt;/td&gt;
&lt;td&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot; frame=&amp;quot;void&amp;quot;&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-1-2 4bits.4bits的定点数
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;定点数的运算和整数运算过程基本一致，不同的是我们需要根据小数运算的规则对结果进行分割，来确立整数位和小数位，比如对于乘法，就需要将原先两个乘数的小数位的位宽相加，作为结果的小数位位宽，如表3-1-3。 
&lt;center&gt;
&lt;table border=&amp;quot;1&amp;quot; cellspacing=&amp;quot;0&amp;quot;&gt;
&lt;tr&gt;
&lt;th&gt;Src1&lt;/th&gt;
&lt;td&gt; &lt;/td&gt;
&lt;td&gt; &lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;.&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt; &lt;/td&gt;
&lt;td&gt; &lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;Src2&lt;/th&gt;
&lt;td&gt; &lt;/td&gt;
&lt;td&gt; &lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;.&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt; &lt;/td&gt;
&lt;td&gt; &lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;Res&lt;/th&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;.&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
表3-1-3 定点数的乘法
&lt;/center&gt;&lt;/p&gt;
&lt;h3&gt;3.1.3 舍入系统&lt;/h3&gt;
&lt;p&gt;由于图像处理所操作的数据基本都是有范围限制的整数，所以一个舍入系统是必要的，这个系统的作用在于对溢出的数据进行合理的截断，以及将小数舍入的整数。在这个项目中，我选择的是就近舍入，即将舍入到最近的数字，这也是软件算术系统中所广泛使用的。比如，对于一个16bits，定点位为8的定点数，其舍入规则如式3-1-3所示。  &lt;/p&gt;
&lt;p&gt;$$Res=\left\{\begin{aligned} 0 &amp;&amp; Src &lt; 0 \\255 &amp; &amp; Src&gt;255\\ x + 1 &amp; &amp; Src = x.(5-9)\ \&amp; \ x&gt;0 \\x - 1 &amp; &amp; Src = x.(0-4)\ \&amp; \ x&gt;0 \\x - 1 &amp; &amp; Src = x.(5-9)\ \&amp; \ x&lt;0 \\x + 1 &amp; &amp; Src = x.(0-4)\ \&amp; \ x&lt;0 \ \end{aligned}\right.\ \ \ \ \ \ \ \ (3-1-3)$$&lt;/p&gt;

&lt;p&gt;对于溢出，一个简单的边界检查便可以达到目的，而对于定点数的小数向整数的舍入，则需要先将定点数转换为原码，而后进行截断，再转换为补码，而后根据原来的补码进行最终的舍入，下面的FR核完成了这个功能：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;module&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;FixedRound&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;clk&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;fixed_num&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;round&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;parameter&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;num_width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;42&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;parameter&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;fixed_pos&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;16&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;input&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;clk&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;input&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;signed&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;num_width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;fixed_num&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;output&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;signed&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;num_width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;fixed_pos&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;round&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kt&amp;quot;&gt;reg&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;signed&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;num_width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;num_true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kt&amp;quot;&gt;reg&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;signed&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;num_width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;fixed_pos&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;num_comp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;tmp_comp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kt&amp;quot;&gt;reg&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;signed&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;num_width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;fixed_pos&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;reg_round&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;assign&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;round&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;reg_round&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;always&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;@(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;posedge&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;clk&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;begin&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;num_orig&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;fixed_num&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;num_width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;==&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;fixed_num&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; 
            &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;fixed_num&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;num_width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;~&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;fixed_num&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;num_width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;2&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)};&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;num_comp&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;num_orig&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;num_width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;==&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;num_orig&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;num_width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;fixed_pos&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; 
            &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;num_orig&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;num_width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;~&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;num_orig&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;num_width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;2&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;fixed_pos&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;tmp_comp&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;num_comp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;c1&amp;quot;&gt;//Why not use num_comp[25] to judge? : if 0&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;case&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;num_orig&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;num_width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;])&lt;/span&gt;
            &lt;span class=&amp;quot;mh&amp;quot;&gt;0&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;reg_round&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;tmp_comp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;fixed_pos&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;==&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;num_comp&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;num_comp&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
            &lt;span class=&amp;quot;mh&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;reg_round&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;tmp_comp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;fixed_pos&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;==&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;num_comp&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;num_comp&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
            &lt;span class=&amp;quot;k&amp;quot;&gt;default&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;cm&amp;quot;&gt;/* default */&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;endcase&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;end&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;endmodule&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;FR核的原理是，定点符号数fixed_num首先被转换为了原码num_true，而后将小数部分进行了整体的截断，之后再转换为了补码num_comp，最后根据num_true的符号位和fixed_num定点位后的第一位一位，即小数点后的第一位的值来确定舍入方式。如果这一位是1，则代表在十进制中，被舍入数的小数点后第一位大于5，否则小于5，之后根据四舍五入法则进行舍入即可。  &lt;/p&gt;
&lt;h3&gt;3.1.4 函数&lt;/h3&gt;
&lt;p&gt;图像处理中会用到一些函数，最为常见的就是三角函数，在一些几何变换中这些函数非常有用，对于一些实现方式而言，函数的计算可以交由软件进行，在FPGA部分则不需要关心传来参数的具体意义，直接计算即可，这种做法的好处是泛用性强，但不够直观，所以当需要一定的直观性的时候，就需要一种方式来让FPGA直接得出这些函数的值。例如，如果想用FPGA求得三角函数的值，最常见的做法就是建立一个LUT(Lookup Table，查找表)，这相当于建立了一个经验公式，来快速地获得限定范围内函数的值，以下代码便定义了一个sin函数的0-359度的查找表，表的键值对由python脚本计算：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;module&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;SinLUT&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;angle&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;value&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;input&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mh&amp;quot;&gt;8&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;angle&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;output&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mh&amp;quot;&gt;17&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;value&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;kt&amp;quot;&gt;reg&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mh&amp;quot;&gt;17&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;reg_value&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;assign&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;value&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;reg_value&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;always&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;@(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;begin&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;case&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;angle&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
            &lt;span class=&amp;quot;mh&amp;quot;&gt;0&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;reg_value&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;18&lt;/span&gt;&lt;span class=&amp;quot;mb&amp;quot;&gt;&amp;#39;b000000000000000000&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
            &lt;span class=&amp;quot;mh&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;reg_value&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;18&lt;/span&gt;&lt;span class=&amp;quot;mb&amp;quot;&gt;&amp;#39;b000000010001110111&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
            &lt;span class=&amp;quot;mh&amp;quot;&gt;2&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;reg_value&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;18&lt;/span&gt;&lt;span class=&amp;quot;mb&amp;quot;&gt;&amp;#39;b000000100011101111&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
            &lt;span class=&amp;quot;mh&amp;quot;&gt;3&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;reg_value&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;18&lt;/span&gt;&lt;span class=&amp;quot;mb&amp;quot;&gt;&amp;#39;b000000110101100101&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
            &lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;
            &lt;span class=&amp;quot;mh&amp;quot;&gt;359&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;reg_value&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;18&lt;/span&gt;&lt;span class=&amp;quot;mb&amp;quot;&gt;&amp;#39;b100000010001110111&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
            &lt;span class=&amp;quot;k&amp;quot;&gt;default&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;reg_value&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;endcase&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;end&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;endmodule&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;hr /&gt;
&lt;h1&gt;参考文献&lt;/h1&gt;
&lt;p&gt;[7] IEEE Standard Verilog Hardware Description Language,&amp;quot; IEEE Std 1364-2001 , vol., no., pp.0_1,856, 2001&lt;br /&gt;
doi: 10.1109/IEEESTD.2006.99495  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Monday, 11 May 2015 19:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.05.11 19:00:article/Skill-2015_05_11_b</guid>
<category>FPGA</category>
<category>FPGA-Imaging-Library</category>
<category>Verilog</category>
<category>符号数</category>
<category>定点数</category>
</item>

<item>
<title>【FPGA/图像处理】设计与架构-仿真与发布</title>
<link>http://dtysky.moe/article/Skill-2015_05_11_a</link>
<description>&lt;p&gt;图像处理系列文章基本都是我本科毕设论文同步发布，这个项目以LGPL许可被发布在&lt;a href=&amp;quot;http://fil.dtysky.moe&amp;quot;&gt;F-I-L&lt;/a&gt;。由于要提供给用户使用，所以必须要提供一套完整的仿真和实测流程，这些仿真程序又有必要遵循相同的架构，以保证开发时的方便和严谨，这考验着系统架构的功底。   &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;2 设计与架构&lt;/h1&gt;
&lt;p&gt;由于所有的图像处理模块从属于同一个系列，并且需要兼容流水线和请求响应两种模式，所以需要一个标准的接口，这个接口用来连接各个模块，接口设计的标准是要使得每个模块之间的耦合最松，同时又不需要每一个处理结果都要开一个单独的帧缓存、以造成资源的浪费。不仅如此，在还需要考虑到模块自身的可定制(重用性)和软件可控性。同时由于要提供给用户使用，必须要提供一套完整的软件和实测流程。&lt;br /&gt;
本章将会说明如何去设计这样的一套接口，如何实现模块的标准化，以及提供给用户的仿真、实测和使用流程。  &lt;/p&gt;
&lt;h2&gt;2.4 目录结构，测试与发布&lt;/h2&gt;
&lt;p&gt;由于这个图像处理库遵循同一套规范，并且面向用户，所以需要一个规范化的设计结构，这个结构被要求提供一套完整的：&lt;br /&gt;
可配置的可选测试样例 -&amp;gt; 软件仿真 -&amp;gt; 功能仿真 -&amp;gt; PSNR计算分析 -&amp;gt; 板上测试。  &lt;/p&gt;
&lt;h3&gt;2.4.1 目录结构&lt;/h3&gt;
&lt;p&gt;由于以上原因，一套完善的目录结构是必要的，这不仅对于用户而言，也是对于开发的便利和严谨性而言，我将目录设计成了如图2-7的形式。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图2-7 目录结构&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/2/7.png&amp;quot; /&gt;&lt;br /&gt;
图2-7 目录结构 
&lt;/center&gt;
每一个目录的作用如下：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;HDL: FPGA工程以及被打包好的IP核被放置在这个文件夹内，它们由Vivado建立。  &lt;/li&gt;
&lt;li&gt;ImegForTest: 一个用于存储图片的文件夹，你可以将你想要进行测试的图像放在这里，只有&amp;quot;jpg&amp;quot;和&amp;quot;bmp&amp;quot;格式的文件被支持，不仅如此，一个名为&amp;quot;conf.json&amp;quot;的文件被用来配置仿真参数。  &lt;/li&gt;
&lt;li&gt;SoftwareSim: 软件仿真的python源文件在这里，它们能够以软件的方法向你展示这个模块的功能。&lt;br /&gt;
仿真结果将会被放置在&amp;quot;SimResCheck&amp;quot;文件夹内。  &lt;/li&gt;
&lt;li&gt;HDLSimDataGen: 这里有一个python的源文件，它是用来创建&amp;quot;dat&amp;quot;文件的，这种文件被作为HDL功能仿真时图像数据的来源。&lt;br /&gt;
dat文件将会被放置在&amp;quot;FunSimForHDL&amp;quot;文件夹内。  &lt;/li&gt;
&lt;li&gt;FunSimForHDL: HDL功能仿真将在这里进行。  &lt;/li&gt;
&lt;li&gt;SimResCheck: 一个名为&amp;quot;covert.py&amp;quot;的python源文件将HDL功能仿真的结果转换为图像，此外，软件仿真的结果也会被放置在这里，另一个名为&amp;quot;compare.py&amp;quot;的源文件用于将所有的软件仿真结果和HDL功能仿真结果进行比对，然后生成一份报告，用于评估IPCore的质量。  &lt;/li&gt;
&lt;li&gt;DocGen: 自己编写的针对HDL的注释-&amp;gt;文档解析器，用于快速生成一个当前项目的文档模板。  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;2.4.2 测试流程&lt;/h3&gt;
&lt;p&gt;测试分为软件仿真、功能仿真、PSNR计算和板上测试。  &lt;/p&gt;
&lt;h4&gt;2.4.2.1 软件测试&lt;/h4&gt;
&lt;p&gt;我根据每一个模块的特性提供了一些用户可选的仿真参数，这些参数通过一个&amp;quot;conf.json&amp;quot;文件被配置，并作用于每一个张测试图像。这些参数和图像随后被用于软件仿真的程序读入，并生成软件仿真的结果，软件仿真程序遵循一个标准，其函数结构如下：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;def&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;name_format&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;root&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;ex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;conf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;):&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;def&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;transform&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;im&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;conf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;):&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;def&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;debug&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;im&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;conf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;):&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;name_format函数接受的参数为所有图像的文件路径、文件名、扩展名，以及用户设定的配置文件，返回一个字符串，这个字符串将作为处理后图像的文件名。&lt;br /&gt;
transform函数接受一个Image对象的指针与用户设定的配置文件，返回一个Image对像的指针，这个对象即为经过这个模块处理后的图像。&lt;br /&gt;
debug函数接受的参数和transform函数一致，但返回的是一个字符串，这个字符串应当包含这个模块处理后的图像的像素数据，用于调试。  &lt;/p&gt;
&lt;h4&gt;2.4.2.2 HDL功能仿真&lt;/h4&gt;
&lt;p&gt;功能仿真用于HDL文件的功能测试，其基本流程是：&lt;br /&gt;
将图像和配置转换为dat文件 -&amp;gt; 搭建Test bench并读入文本进行仿真 -&amp;gt; 输出结果到res文件 —&amp;gt; 转换为图像。  &lt;br /&gt;
将图像转换为dat文件的过程是由python完成的，这个程序同样遵循一个标准：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;def&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;name_format&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;root&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;ex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;conf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;):&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;def&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;conf_format&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;im&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;conf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;):&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;def&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;color_format&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;mode&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;):&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;def&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;create_dat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;im&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;conf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;):&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;name_format函数接受的参数为所有图像的文件路径、文件名、扩展名，以及用户设定的配置文件，返回一个字符串，这个字符串将作为处理后图像的文件名。&lt;br /&gt;
conf_format函数接受一个Image对象的指针与用户设定的配置文件，返回写在目标dat文件起始位置的字符串，这个字符串可以视作与Test bench传递模块参数的接口，它作用于整张图像。&lt;br /&gt;
color_format函数接受一个图像的模式和一个像素的色彩值，返回的是一个格式化后的色彩值，格式化的格式根据模块的需求和图像模式而定，模式通常为RGB、灰度等。&lt;br /&gt;
create_dat函数接受一个Image对象的指针和用户设定的配置文件，它返回的是当前图像被转换后的dat文件所需要写入的所有内容。&lt;br /&gt;
&lt;br&gt;
有了数据的来源，需要考虑的便是Test bench的搭建，Test bench，即测试平台，是HDL验证领域所必须搭建的，这对于模块的功能判断和调试是十分有必要的，我使用SystemVerilogHDL来搭建测试平台，SV灵活强大，抽象能力强，几乎是业界搭建测试平台的标准，对于测试平台，虽然对于不同的模块难以完全标准化，我仍然设立了一套基本的标准：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;interface&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;TBInterface&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;input&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;bit&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;clk&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;input&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;bit&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;rst_n&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;task&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;init_file&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;task&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;init_signal&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;task&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;work_pipeline&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;task&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;work_regack&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;TBInterface是一个接口，用于构造仿真需要的接口，这里使用接口并不是为了在设计的时候模糊接口完整的定义，因为它并不与verilog兼容，这样做的好处仅仅是让结构看起来更清晰，比如在实例化的时候可以这样去做：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;TBInterface&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;#(&lt;/span&gt;&lt;span class=&amp;quot;mh&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;8&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;RGBPipline&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;clk&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;rst_n&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;Test&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;Test1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;RGBPipline&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;clk&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;RGBPipline&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rst_n&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;接口后是四个task(任务)。&lt;br /&gt;
init_file在每一张新的图像输入的时候被调用，一般用于将图像的宽高写入res文件，并且读取用户定义的配置参数，使其作用于整张图像。&lt;br /&gt;
init_signal用于在每张图像被处理前进行一些信号初始化工作，比如rst_n这个复位信号就可以在这个流程中完成模块的复位操作。&lt;br /&gt;
work_pipeline是工作流程，用于指定这个模块在流水线模式下如何被测试，以及将要输出怎样的数据。&lt;br /&gt;
work_regack同上，唯一的区别在于这个task用于请求响应模式下的测试。&lt;br /&gt;
&lt;br&gt;
仿真完成后得到的是一系列的res文件，这些文件中有处理结束后的图像数据，接下来将这些数据利用一个python脚本进行转换，便可以得到HDL功能仿真的结果。  &lt;/p&gt;
&lt;h4&gt;2.4.2.3 PSNR计算分析&lt;/h4&gt;
&lt;p&gt;有了软件仿真和功能仿真的结果，便可以对模块实际运作的质量进行一个评估，这里选用PSNR进行评估，PSNR的计算公式如式2-1。&lt;br /&gt;
$$PSNR = 10\log10(\frac{MAX^2} {MSE})\ \ \ \ \ \ \ \ (2-1)$$  &lt;br /&gt;
&lt;br&gt;
其中，MSE是原图像与处理图像之间的均方误差，MAX是图像在当前位宽下的最大值，这里用软件仿真的结果作为原图像，HDL功能仿真的结果作为处理图像。&lt;br /&gt;
PIL库提供了计算MSE的函数，加上math库中的log函数即可完成计算：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;diffs&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;ImageChops&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;difference&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Image&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;open&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;f_pair&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]),&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;Image&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;open&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;f_pair&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]))&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;stat&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;ImageStat&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Stat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;diffs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;rms&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;sum&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;stat&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rms&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;len&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;stat&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rms&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;psnr&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;20.0&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;math&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;log10&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;255.0&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;rms&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;rms&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1000&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1000&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;PSNR值的单位为dB，理论上，PSNR值越大失真越少，代表图像处理的质量越高，一般情况下PSNR &amp;gt; 30dB即为人眼可以容忍的范围。  &lt;/p&gt;
&lt;h3&gt;2.4.3 板上测试&lt;/h3&gt;
&lt;p&gt;一些板上测试工程会被提供，它们由Vivado的图形化设计界面构建，使用xilinx提供的Zybo开发板，并使用Ov7670摄像头模块作为图像来源，使用VGA作为图像输出，同时建立一个AXI总线的IP核来完成简单的通过软件对模块的配置。  &lt;/p&gt;
&lt;h3&gt;2.4.4 发布&lt;/h3&gt;
&lt;p&gt;项目拥有自己的发布网站，发布网站使用Pelican作为框架，用html、css和js开发，并搭建在VPS上，最终发布于fil.dtysky.moe。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;参考文献&lt;/h1&gt;
&lt;hr /&gt;
&lt;h1&gt;感谢&lt;/h1&gt;
&lt;p&gt;Alexis Metaireau and contributors, Pelican 3.5.0, https://github.com/getpelican  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Monday, 11 May 2015 00:01:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.05.11 00:01:article/Skill-2015_05_11_a</guid>
<category>FPGA</category>
<category>FPGA-Imaging-Library</category>
<category>图像处理</category>
<category>仿真</category>
<category>发布</category>
</item>

<item>
<title>【FPGA/图像处理】设计与架构-接口与IP核设计</title>
<link>http://dtysky.moe/article/Skill-2015_05_10_a</link>
<description>&lt;p&gt;图像处理系列文章基本都是我本科毕设论文同步发布，这个项目以LGPL许可被发布在&lt;a href=&amp;quot;http://fil.dtysky.moe&amp;quot;&gt;F-I-L&lt;/a&gt;。由于这些图像处理模块从属于同一个系列，并且要兼容流水线和请求响应两种模式，有必要遵循同一个标准的接口协议，我设计了一套简单的标准化接口协议，使得图像在处理过程中需要消耗的帧缓存最小化。不仅如此，模块本身的设计也要考虑到强可重用性和软件的可控性。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;2 设计与架构&lt;/h1&gt;
&lt;p&gt;由于所有的图像处理模块从属于同一个系列，并且需要兼容流水线和请求响应两种模式，所以需要一个标准的接口，这个接口用来连接各个模块，接口设计的标准是要使得每个模块之间的耦合最松，同时又不需要每一个处理结果都要开一个单独的帧缓存、以造成资源的浪费。不仅如此，在还需要考虑到模块自身的可定制(重用性)和软件可控性。同时由于要提供给用户使用，必须要提供一套完整的软件和实测流程。&lt;br /&gt;
本章将会说明如何去设计这样的一套接口，如何实现模块的标准化，以及提供给用户的仿真、实测和使用流程。  &lt;/p&gt;
&lt;h2&gt;2.1 分类&lt;/h2&gt;
&lt;p&gt;在设计之前，首先要完成的是分类，由于图像处理种类繁多，所以考察这些不同的种类，并提取共同之处对于设计和架构是十分必要的，针对于FPGA的图像处理操作的分类工作已经在Donald G.Bailey的专著中&lt;sup&gt;[3]&lt;/sup&gt;有非常完整的结论，这里加以提取和重构，已实现的图像处理主要的操作分为如下几类：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Point :对每一个像素进行的操作，它意味着所有这种类型的IP核在一个操作周期内都只能够对一个点进行操作，所以这里将会有甚多的基础操作，像是灰度化、阈值化、对比度变换等等。&lt;/li&gt;
&lt;li&gt;Generator: 这是一个特殊的分类，它包含一些用于生成数据结构或者控制内存的IP核，比如行缓存生成，帧控制等等。&lt;/li&gt;
&lt;li&gt;LocaFilter: 基于窗口的操作，也就是局部滤波器，窗口是一种特殊的数据结构，这种数据结构将图片的一部分分割了出来。这个分类中的IP核经常用于模糊和锐化这些目的，比如均值滤波器，排序滤波器等等。&lt;/li&gt;
&lt;li&gt;Geometry: 如其名所示，这个分类下的IP核被用于几何变换，和其他分类不同，这个分类操作的对象是坐标而不是色彩，比如平移、缩放、旋转等仿射变换。&lt;/li&gt;
&lt;li&gt;Detection: 边缘检测，比如Harris角点检测等等。&lt;/li&gt;
&lt;li&gt;Histogram: 创建直方图，并从中获取一些有用的信息。  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;2.2 接口设计&lt;/h2&gt;
&lt;p&gt;根据这些操作的功能和要求，可以将接口设计分为两个部分——基础端口和扩展端口。  &lt;/p&gt;
&lt;h3&gt;2.2.1 基础端口&lt;/h3&gt;
&lt;p&gt;基础端口，也就是所有模块的接口中，至少包含的一些端口，这些端口被设计为如下形式：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;clk: 时钟信号，用于提供同步时钟。  &lt;/li&gt;
&lt;li&gt;rst_n: 全局复位信号，用于复位和初始化。  &lt;/li&gt;
&lt;li&gt;in_enable: 输入数据使能，用于控制输入数据流。&lt;/li&gt;
&lt;li&gt;in_data: 输入数据流，提供处理的数据源。  &lt;/li&gt;
&lt;li&gt;out_ready: 输出数据有效，作为操作结束的标志。  &lt;/li&gt;
&lt;li&gt;out_data: 输出数据流，送出处理后的数据。  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;这些端口保证了每个模块的基本功能，图示如图2-1。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图2-1 基础接口示意&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/2/1.png&amp;quot; /&gt;&lt;br /&gt;
图2-1 基础接口示意&lt;br /&gt;
&lt;/center&gt;&lt;br /&gt;
模块的运作方式如下：&lt;br /&gt;
首先进行全局的复位，对模块进行初始化，而后输入数据随着输入数据使能信号输入模块，在同步时钟的若干个周期后准备好输出数据，使能输出数据有效信号，通知外部电路取出数据。  &lt;/p&gt;
&lt;h3&gt;2.2.2 扩展端口&lt;/h3&gt;
&lt;p&gt;由于每个模块自身的独特性，基础端口提供的功能往往不足以满足模块的实现，所以这时候需要加入扩展端口来满足需求，扩展端口一般被设计为以下形式：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;x: 不定端口，取决于模块自身的要求，比如对于阈值化模块，这个参数就是阈值。  &lt;/li&gt;
&lt;li&gt;in_count_x: 输入坐标的x分量，通常用于几何变换。  &lt;/li&gt;
&lt;li&gt;in_count_y: 输入坐标的y分量，通常用于几何变换。&lt;/li&gt;
&lt;li&gt;out_count_x: 输出坐标的x分量，通常用于几何变换。&lt;/li&gt;
&lt;li&gt;out_count_y: 输出坐标的y分量，通常用于几何变换。&lt;/li&gt;
&lt;li&gt;frame_addr: 通常用于帧控制，提供某一个输出数据的地址。  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;这些端口和基础端口合并起来，便可以满足每一个模块的需求，如图2-2所示。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图2-2 完整接口示意&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/2/2.png&amp;quot; /&gt;&lt;br /&gt;
图2-2 完整接口示意&lt;br /&gt;
&lt;/center&gt;&lt;br /&gt;
至此，接口的硬件部分便设计完毕。  &lt;/p&gt;
&lt;h3&gt;2.2.3 接口协议&lt;/h3&gt;
&lt;p&gt;接口的硬件部分定义结束后，还必须定义其协议部分。由于每一个模块同时存在流水线模式和请求响应模式，同时为了兼容已有的接口标准，达到最简化的设计目的，我让两种模式遵循了同一套接口标准，不同的仅仅是在两种模式下接口的行为方式。  &lt;/p&gt;
&lt;h4&gt;2.2.3.1 流水线模式&lt;/h4&gt;
&lt;p&gt;流水线模式时，在输入使能in_enable有效的情况下，从第一次输出数据有效标志out_ready有效开始，输出数据out_data便会源源不断地送出，每一个周期都会送出一个有效数据。在这种模式下，从第一个数据有效开始，输出便是连贯的，如图2-3所示，时序图均采用wavedrom绘制。 
&lt;center&gt; 
&lt;img alt=&amp;quot;图2-3 流水线模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/2/3.png&amp;quot; /&gt;&lt;br /&gt;
图2-3 流水线模式时序&lt;br /&gt;
&lt;/center&gt;&lt;br /&gt;
这种模式一般用于需要连续数据流的应用中，由于采用了流水线做缓冲，所以一开始的若干个周期延迟在实际运用中是不需要关心的，也故理论上可以插入任意级流水来达到最高的Fmax(工作频率)。  &lt;/p&gt;
&lt;h4&gt;2.2.3.2 请求响应模式&lt;/h4&gt;
&lt;p&gt;在这个模式下，in_enable和out_ready两个标志信号被当做请求信号req和响应信号ack，输入数据in_data随着每一次in_enable的上升沿被送入模块进行处理，处理完成后out_ready有效来通知外部电路取走数据，直到下次in_enable的上升沿到来为止，输出数据的状态都不会发生改变，如图2-4所示。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图2-4 请求响应模式时序&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/2/4.png&amp;quot; /&gt;&lt;br /&gt;
图2-4 请求响应模式时序&lt;br /&gt;
&lt;/center&gt;&lt;br /&gt;
这种模式一般用于一些特殊的模块，比如直方图操作下的模块，对于这些模块，流水化的操作是没有意义的。此外，这种模式还可以被用于和软件的交互中，因为软件很难做到同步数据流的模式。&lt;br /&gt;
&lt;br&gt;
综上，最终模块的接口工作模式如图2-5。&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图2-5 接口工作模式&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/2/5.png&amp;quot; /&gt;&lt;br /&gt;
图2-5 接口工作模式&lt;br /&gt;
&lt;/center&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2.3 可定制IP核设计&lt;/h2&gt;
&lt;p&gt;明确了每一个模块的接口以及其协议之后，便可以考虑IP核的设计了。IP核(intellectual property core，知识产权核)是指由某一方开发者提供的形式为逻辑单元、芯片设计的可重用模块，使用IP核能够为设计减少开发周期，并且达到比较好的效果。&lt;br /&gt;
对于图像处理操作，一个IP核应当用于良好的可重用性和软件可控性，可重用性本质上就是一个IP提供了若干种工作模式，用户可以根据参数对IP核进行不同的配置，使得IP核在不同配置下被综合成不同的模式。而软件可控性，这里指的是可以通过AXI总线使得SoC部分可以对IP核进行一定的控制，比如在流水线模式下可以提供一些配置参数，在请求响应模式下可以直接进行数据交互。  &lt;/p&gt;
&lt;h3&gt;2.3.1 可重用性设计&lt;/h3&gt;
&lt;p&gt;在VerilogHDL中，可重用性一般是通过parameter语句和generate语句实现的，generate语句在VerilogHDL1995标准&lt;sup&gt;[6]&lt;/sup&gt;里是没有的，但在VerilogHDL2001标准&lt;sup&gt;[7]&lt;/sup&gt;中，向VHDL学习中它加入了这个语句，现在几乎所有的综合工具都支持这个语句。&lt;br /&gt;
parameter语句常用于配置静态参数，来决定模块的工作方式，generate语句则根据parameter语句设定的参数来告诉综合工具哪一部分需要被综合，比如以下代码：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 0 for pipeline, 1 for req-ack&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;parameter&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;work_mode&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;parameter&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;color_width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;8&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;input&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;color_width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;in_data&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;generate&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;work_mode&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;==&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;beign&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;end&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;beign&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;end&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;endgenerate&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;根据color_width来确定输入数据的位宽，根据work_mode来确定要综合的部分。此外，generate语句还可以被用来实现逻辑复制，这为流水线的设计减少了不少工作量。  &lt;/p&gt;
&lt;h3&gt;2.3.2 IP核设计&lt;/h3&gt;
&lt;p&gt;一般情况下，一个单独或者一个系列具有层次的HDL文件便可以被看成一个IP核，这种IP核通用性最强，但从使用效率的角度却不如针对每一个厂商的开发套件专用的封装，本项目使用Vivado作为开发工具，使用的是Vivado的封装工具。&lt;br /&gt;
Vivado的IP封装工具在基本的IP封装上加了一层GUI，用于和用户进行直接的交互，一个设计封装好的IP核如图2-5所示：&lt;br /&gt;
&lt;center&gt;
&lt;img alt=&amp;quot;图2-6 Vivado封装的IP核&amp;quot; src=&amp;quot;//src.dtysky.moe/image/f-i-l/2/6.png&amp;quot; /&gt;&lt;br /&gt;
图2-6 Vivado封装的IP核&lt;br /&gt;
&lt;/center&gt;&lt;br /&gt;
通过这个IP工具，我们可以给模块的参数添加任意形式的约束，由于它支持expr表达式&lt;sup&gt;[8]&lt;/sup&gt;，所以我们甚至可以通过某一个参数的取值来自动确定另一个参数的取值，避免了用户自行计算的这一个步骤，例如，我们想要通过im_width这两个参数来确定im_width_bits(图像宽度的位宽)时，可以利用下面的语句来完成 ；  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;if {[expr log(&lt;span class=&amp;quot;nv&amp;quot;&gt;$im_width&lt;/span&gt;)/log(2)] &amp;gt; [expr int(log(&lt;span class=&amp;quot;nv&amp;quot;&gt;$im_width&lt;/span&gt;)/log(2))] } {
    set &lt;span class=&amp;quot;cp&amp;quot;&gt;${&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;im_width_bits&lt;/span&gt;&lt;span class=&amp;quot;cp&amp;quot;&gt;}&lt;/span&gt; [expr int(log(&lt;span class=&amp;quot;nv&amp;quot;&gt;$im_width&lt;/span&gt;)/log(2)) + 1]
} else {
    set &lt;span class=&amp;quot;cp&amp;quot;&gt;${&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;im_width_bits&lt;/span&gt;&lt;span class=&amp;quot;cp&amp;quot;&gt;}&lt;/span&gt; [expr int(log(&lt;span class=&amp;quot;nv&amp;quot;&gt;$im_width&lt;/span&gt;)/log(2))]
}
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;封装后的IP核有资源文件，一个用于记录IP结构的xml文件和一个控制GUI的tcl文件，以及可能存在的一个服务于expr表达式的gtcl文件构成，这些文件使用相对路径，所以可以很方便地将IP核转移到任何位置。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;参考文献&lt;/h1&gt;
&lt;p&gt;[3] Donald G.Bailey.基于FPGA的嵌入式图像处理系统设计[M].原魁,何文浩,肖晗译.北京:电子工业出版社,2013&lt;br /&gt;
[6] IEEE Standard Hardware Description Language Based on the Verilog(R) Hardware Description Language,&amp;quot; IEEE Std 1364-1995 , vol., no., pp.1,688, Oct. 14 1996&lt;br /&gt;
doi: 10.1109/IEEESTD.2006.99495&lt;br /&gt;
[7] IEEE Standard Verilog Hardware Description Language,&amp;quot; IEEE Std 1364-2001 , vol., no., pp.0_1,856, 2001&lt;br /&gt;
doi: 10.1109/IEEESTD.2006.99495&lt;br /&gt;
[8] Xilinx, Vivado Design Suite, Creating and Packaging Custom IP, UG1118 (v2014.3)[EB/OL]. October 8, 2014  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;感谢&lt;/h1&gt;
&lt;p&gt;WaveDrom user group, wavedrom, http://wavedrom.com&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Sunday, 10 May 2015 20:01:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.05.10 20:01:article/Skill-2015_05_10_a</guid>
<category>FPGA</category>
<category>FPGA-Imaging-Library</category>
<category>图像处理</category>
<category>接口</category>
<category>IP核</category>
</item>

<item>
<title>【FPGA/图像处理】绪论-背景知识</title>
<link>http://dtysky.moe/article/Skill-2015_05_09_a</link>
<description>&lt;p&gt;图像处理系列文章基本都是我本科毕设论文同步发布，这个项目以LGPL许可被发布在&lt;a href=&amp;quot;http://fil.dtysky.moe&amp;quot;&gt;F-I-L&lt;/a&gt;。图像处理技术服务于人类的视觉系统，而视觉是人类感知外界最为重要的知觉之一，所以图像处理对于我们的意义是重大的，历史上的图像处理有多个阶段，到了我们这个时代，则是以数字图像处理为主，其中的算法又可以分为软件和硬件两种实现，硬件实现在速度等方面比起软件实现有许多优势，但其复杂程度和局限性也是比较显然的，所以我们应当做的合理分配和利用二者，做到效率的最大化。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;1. 绪论&lt;/h1&gt;
&lt;h2&gt;1.1 引言&lt;/h2&gt;
&lt;p&gt;图像处理(Image Processing)服务于人类的视觉系统，而视觉作为人类最重要的感觉器官，对我们的意义是十分重大的，所以，视觉数据的记录和处理十分重要，经过漫长的发展，人类对视觉信号的处理方式由原始的间接记录演化成了由电子器件主导的直接记录，通过感光器件，我们可以得到记录光强的若干信息，对这些信息进行处理，也就是对视觉信息进行分析和再构，其重要性不言而喻。&lt;br /&gt;
我们知道，本质上人类所有活动的都依赖于信息的操作，正如当巴贝奇在发散地研究各个领域、想要制作计算机器来尝试一些东西的时候，他真正的研究主题实际上信息，是信息的通信、编码、处理等&lt;sup&gt;[1]&lt;/sup&gt;。这一点在现代社会显得尤为清晰。而图像处理就是对图像信息的操作，它一般处于图像信息输入和输出之间，本质上是对输入信息进行各种各样的滤波，来转换其信息所处的空间，从而达到强调、提取信息等等目的。&lt;br /&gt;
传统的图像处理方法是模拟和光学的，在精确的现代记录方式出现之前，人们用自己的双手和画笔进行着图像处理，那个时代，任何图像信息再被输入之前就已经经过一次处理了，不过那个时候我们往往把这些处理称为“艺术加工”，当然，一幅图像是否具备着相对于自然完全的“真实性”并不重要，因为“真实”本就是一个玄学的概念，最重要的是我们想要获得什么，正如贡布里希在《艺术与错觉》一书中所言，“艺术家同样无法转录他所见到的东西，他所能做的只是把他所见翻译成他的绘画手段的表现形式罢了。”&lt;sup&gt;[2]&lt;/sup&gt;。事实上，图像处理就是有着这样的一个目的——去得到相对于原始图像的、我们想要的东西。&lt;br /&gt;
然而对于这些历史上的处理方式有一个比较重要的问题，就是绝大多数人基本不可能对同一个原图像进行机械而重复的操作，这是绘画这种处理方式本身的门槛所决定的，所以图像处理技术一直没有从艺术领域踏入工业领域，因为实际上它并不具备一般意义上广泛的生产力，但随着技术的飞快进步，尤其是CCD和CMOS这种感光元件以及存储器的出现，让我们获得了记录和复制同一副图像的能力，一旦一件事物被加上了可以复制的属性，它的成本的降低几乎是必然的，所以我们拥有了更多的自由去做一些实验，加上电子技术的进一步发展，数字图像处理技术诞生了，比起传统的处理方式，它大大提高了研发和生产的效率，可以完成的操作是传统方式望尘莫及的。&lt;/p&gt;
&lt;h2&gt;1.2 数字图像处理&lt;/h2&gt;
&lt;p&gt;数字图像处理，即为使用计算机对量化和数字化后的图像数据进行处理，数字图像的定义是：“一个存储着数值的二维数组”，这意味着图像被映射到了一个二维空间内，由若干坐标和坐标信息来表示图像本身的信息，每一个这样的坐标点就被称为“点”，“点”一般作为图像的基本操作单元。
有了被存储在数据单元内的图像数据，便可以对图像进行各种操作，操作的基础是各种数学运算，比如加减乘除卷积等等，利用这些操作可以实现&lt;sup&gt;[3]&lt;/sup&gt;：&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;图像增强：改善图像的主观质量，比如降噪、对比度调整、色彩校正等等。&lt;/li&gt;
&lt;li&gt;图象复原：对已经发生退化的图像进行修正，分析退化的原因进行逆运算，从而将图像恢复到应有的状态。&lt;/li&gt;
&lt;li&gt;图象重建：将图像数据重组，比如缩放、旋转、色彩空间转换等。&lt;/li&gt;
&lt;li&gt;图像分析：使得计算机可以从图像中获取知识，经常表现为某种形式的测量。&lt;/li&gt;
&lt;li&gt;模式识别：基于测量的模式进行物体识别，比如人脸识别等等。  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;计算机视觉便是建立在以上基础操作的一种学科，研究出一系列模型来处理图像，最终给出人们需求的结果。&lt;br /&gt;
传统的机器视觉是基于软件的，理论上任何一种图像处理算法都可以在一个单独的处理器上实现，区别是越复杂的算法，需要消耗的时间越多，这对于追求效率的人类显然是不可接受的，所以越来越多的并行架构出现，比如空间并行和逻辑并行，对于这样的系统，软件的方案就不再是完美的了，软件适合高层的图像处理操作，比如与某些智能操作，而操作级的处理则需要另辟蹊径，FPGA的出现则提供了这样的一种途径。&lt;/p&gt;
&lt;h2&gt;1.3 图像处理的FPGA实现&lt;/h2&gt;
&lt;p&gt;FPGA(Field Programmable Gate Array ,现场可编程逻辑阵列)属于VLSI(Very Large Scale Integration， 超大规模集成电路)的一种&lt;sup&gt;[4]&lt;/sup&gt;，在和其他专用定制的ASIC不同，FPGA属于在设计成本和最终性能之间的一个平衡产物，它是一种包含可反复使用字段的小规模逻辑模块和元件的可编程器件。由于不需要考虑一次性工程成本，所以它的设计成本和上市成本要比传统ASIC低得多，但由于缺少了一些额外的IC后端流程，所以它的性能和功耗一般无法与专用ASIC相提并论，但即便是如此，对于以软件处理为主导的领域，它的性能仍然是一个巨大的优势，图像处理就是这样的一个领域。&lt;br /&gt;
FPGA本质上是一个通用电路，它利用厂商预设的逻辑单元、存储资源和布线资源构成的数字电路来实现一些算法操作，由于开发FPGA本质上是搭建数字电路，FPGA实际上拥有着先天的并行性，这对于图像处理是非常契合的，因为对于大多图像处理操作，每一个像素点，乃至每一个结构元素都是互相独立的，使用FPGA可以对任意处理模块进行复制，从而达到一个周期处理一张图像的理论效果。&lt;br /&gt;
不仅如此，FPGA还十分是和流水化操作，保证每个周期都有一个输出，并且这种输出是连续而不间断的，同时，即便不是流水化操作，FPGA也可以通过自己设计的协议完成请求响应的等工作模式。总而言之，FPGA是十分灵活的。&lt;br /&gt;
许多算法已经被证实过可以使用FPGA进行实现，比如一些点操作、形态学操作等等，同时得到了良好的效果。  &lt;/p&gt;
&lt;h2&gt;1.4 SoC平台-ZYNQ&lt;/h2&gt;
&lt;p&gt;SoC(System on Chip，片上系统)是指一个将计算机或者其他电子系统集成到单一芯片中的集成电路，也就是说，在单一芯片中放置一个CPU，随后提供它的BSP(Board Support Package，板级支持包)，让这个芯片支持软件开发，这种设计常常被用在嵌入式软件领域，众多的微控制器就是其中的一种典型实现。&lt;br /&gt;
这里要探讨的是SoC和FPGA的结合，Xilinx很早就做出过PowerPC硬核和FPGA结合的架构(Virtex-4, 5系列)，但最后由于需求等问题无疾而终，之后又出现了Altera的Nios和Xilinx的Microblaze软核系统，它们利用一部分的逻辑资源构建一个CPU，并提供基本的BSP进行开发，虽然支持面广，但资源和性能仍然不如硬核架构。&lt;br /&gt;
ZYNQ架构是Xilinx最新推出的一种SoC+FPGA的架构，采用7系列的FPGA和ARM硬核的结构，将系统部分称为“PS”端，逻辑部分称为“PL”端，两端通过AXI总线进行交互，并提供了对开发者友好的全套、一体化设计环境，很大程度上解决了性能和资源问题。&lt;br /&gt;
利用ZYNQ平台，我们可以很方便地使用PS部分对PL部分进行配置和数据交互，这为软件算法的硬件加速提供了GPU外的另一种便利的可能，比如可以将两个矩阵送给PL端计算，返回给PS端，由于Xilinx提供了完善的AXI总线模块模板，让这个开发过程变得非常简单。  &lt;/p&gt;
&lt;h2&gt;1.5 本文研究目的和实现内容&lt;/h2&gt;
&lt;p&gt;基于以上原因，我选择实现一个FPGA的图像处理库，这个库将会包含许多基础的图像处理操作，每一个操作都分为流水线和请求响应两个模式，并拥有各自的软件仿真、HDL功能仿真和板上测试，并计算PSNR进行可信度分析。&lt;br /&gt;
每一个操作都会被封装成Xilinx的Vivado设计套件中的IP核，并利用图形化设计界面进行板上测试工程的搭建。&lt;br /&gt;
对于语言的选择，由于VerilogHDL比起VHDL更适合算法描述，而图像处理比起系统架构更多的是算法问题，所以选择使用VerilogHDL进行模块的设计。&lt;br /&gt;
测试平台利用业界普遍用于测试的SystemVerilogHDL进行搭建，功能仿真使用Modelsim软件进行。&lt;br /&gt;
软件仿真则选择简单强大的Python和PIL库实现。&lt;br /&gt;
除此之外，还会简单地展示如何在ZYNQ平台上实现PS和PL端的交互，实现SoC系统和FPGA的高效协作。&lt;br /&gt;
FPGA-Imaging-Library(FPGA的图像处理库)属于自由软件，以LGPL(GNU Lesser General Public License)&lt;sup&gt;[5]&lt;/sup&gt;许可发布。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;参考文献&lt;/h1&gt;
&lt;p&gt;[1] 詹姆斯.格雷克.信息简史[M].高博译.北京:人民邮电出版社,2013:12.&lt;br /&gt;
[2] E.H.贡布里希.艺术与错觉[M]杨成凯,李本正,范景中译.南宁:广西美术出版社,2012:3.&lt;br /&gt;
[3] Donald G.Bailey.基于FPGA的嵌入式图像处理系统设计[M].原魁,何文浩,肖晗译.北京:电子工业出版社,2013.&lt;br /&gt;
[4] U.Meyer-Basese.数字信号处理的FPGA实现(第3版)[M].刘凌译.北京:清华大学出版社,2011:3.&lt;br /&gt;
[5] Free Software Foundation, Inc. GNU LESSER GENERAL PUBLIC LICENSE Version 2.1[EB/OL]. February 1999.  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Saturday, 09 May 2015 15:01:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.05.09 15:01:article/Skill-2015_05_09_a</guid>
<category>FPGA</category>
<category>FPGA-Imaging-Library</category>
<category>图像处理</category>
<category>Image processing</category>
<category>ZYNQ</category>
</item>

<item>
<title>【FPGA】Modelsim的文件结构和使用</title>
<link>http://dtysky.moe/article/Skill-2015_05_08_b</link>
<description>&lt;p&gt;Modelsim是一个HDL仿真软件，其相比其他的软件，最大的优势在于可以在个人电脑上跑出很不错的速度，用起来也颇为顺手，一般分为SE, ISE, ASE几个版本，第一个是原版，后面的是xilinx和altera定制版，SE版本最为强大，支持混合仿真，同时仿真速度不是后两个版本可以比的。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1.工程文件结构&lt;/h2&gt;
&lt;p&gt;对于一般的需求，使用Modelsim主要是通过建立project来完成的，一般情况下这个project都会在被选择在默认的work库中，并且我们选择的应该是不导入文件和不独立创立一个Ini文件，也就是将安装目录下默认的ini文件作为配置文件，这样，建立好一个project后，目录下一般会有两个文件，假设项目名字为EX：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;EX.mpf&lt;br /&gt;
Ex.cr.mti  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;然而真正有用的文件实际上只有EX.mpr一个，也就是说，如果我们想要转移一个仿真工程，原则上只需要复制这一个文件就OK。&lt;br /&gt;
Modelsim的这个工程文件也是明文的，但和Vivado不同，它并不遵从xml语法，它使用的是一套类似标记语言的语法，打开它后，我们将会看到很多行的文件，这些行基本说明了这个工程的所有配置，但有很多是我们不需要关心的，我们需要关心的，实际上只有：&lt;br /&gt;
检索到：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[Project]  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这一行，我们会看到下面有很多类似这样的文本：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;Project_Version = 6
Project_DefaultLib = work
Project_SortMethod = unused
Project_Files_Count = 4
Project_File_0 = ../HDL/ContrastTransform.srcs/sources_1/ip/Multiplier12x24LUT/Multiplier12x24LUT_funcsim.v
......
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这便是我们需要主要关心的主体内容，来一一解析：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Project_Version = 6  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这表明了项目的版本号。  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Project_DefaultLib = work  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这表明了项目默认所在的库。  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Project_SortMethod = unused  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;项目排序，暂时不知道是做什么的，估计是给文件排序？  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Project_Files_Count = 4  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;项目文件总数，如果和实际不符，小了的话少文件，多了的话报错。  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Project_File_0 = ../HDL/ContrastTransform.srcs/sources_1/ip/Multiplier12x24LUT/Multiplier12x24LUT_funcsim.v  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;第零个文件的路径，这里使用的是相对路径，要注意的是，在modelsim里，一般情况下是不能够使用相对路劲的，似乎只有工程目录下的文件才能使用相对路径表示同时有时候也不行，所以如果相对路径行不通就用绝对路径吧。  &lt;/p&gt;
&lt;p&gt;至此，我们需要关心的内容基本就搞定了。  &lt;/p&gt;
&lt;h2&gt;2.使用&lt;/h2&gt;
&lt;p&gt;使用Modelsim的基础部分就不再赘述，已经有无数篇文章写过这种问题，这里主要说几点觉得有用的经验。  &lt;/p&gt;
&lt;h3&gt;1.编译xilinx仿真库到modelsim&lt;/h3&gt;
&lt;p&gt;在Vivado里面，你只需要在它的控制台中输入以下指令：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;compile_simlib&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;directory &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;library_output_directory&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;  &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;simulator &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;agr&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;  
                            &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;simulator_exec_path&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;sim_install_location&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;之后,在modelsima安装目录中打开modelsim.ini文件（去掉只读属性），然后粘贴下列语句：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;x&amp;quot;&gt;&amp;gt;ini&lt;/span&gt;
&lt;span class=&amp;quot;x&amp;quot;&gt;[Library]&lt;/span&gt;
&lt;span class=&amp;quot;x&amp;quot;&gt;std = &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;MODEL_TECH&lt;/span&gt;&lt;span class=&amp;quot;x&amp;quot;&gt;/../std&lt;/span&gt;
&lt;span class=&amp;quot;x&amp;quot;&gt;ieee = &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;MODEL_TECH&lt;/span&gt;&lt;span class=&amp;quot;x&amp;quot;&gt;/../ieee&lt;/span&gt;
&lt;span class=&amp;quot;x&amp;quot;&gt;verilog = &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;MODEL_TECH&lt;/span&gt;&lt;span class=&amp;quot;x&amp;quot;&gt;/../verilog&lt;/span&gt;
&lt;span class=&amp;quot;x&amp;quot;&gt;vital2000 = &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;MODEL_TECH&lt;/span&gt;&lt;span class=&amp;quot;x&amp;quot;&gt;/../vital2000&lt;/span&gt;
&lt;span class=&amp;quot;x&amp;quot;&gt;std_developerskit = &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;MODEL_TECH&lt;/span&gt;&lt;span class=&amp;quot;x&amp;quot;&gt;/../std_developerskit&lt;/span&gt;
&lt;span class=&amp;quot;x&amp;quot;&gt;synopsys = &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;MODEL_TECH&lt;/span&gt;&lt;span class=&amp;quot;x&amp;quot;&gt;/../synopsys&lt;/span&gt;
&lt;span class=&amp;quot;x&amp;quot;&gt;modelsim_lib = &lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;MODEL_TECH&lt;/span&gt;&lt;span class=&amp;quot;x&amp;quot;&gt;/../modelsim_lib&lt;/span&gt;
&lt;span class=&amp;quot;x&amp;quot;&gt;simprim_ver = G:/EDA/Xilinx/simprim_ver（库的路径，以下同）&lt;/span&gt;
&lt;span class=&amp;quot;x&amp;quot;&gt;unisim_ver = G:/EDA/Xilinx/unisim_ver&lt;/span&gt;
&lt;span class=&amp;quot;x&amp;quot;&gt;xilinxcorelib_ver = G:/EDA/Xilinx/xilinxcorelib_ver&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;最后，还原只读属性即可。  &lt;/p&gt;
&lt;h3&gt;2.自动化测试&lt;/h3&gt;
&lt;p&gt;使用tcl脚本和控制台来完成，我的一般做法如下：  &lt;/p&gt;
&lt;p&gt;首先，使用以下指令创建work库：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;vlib work
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;而后，编译文件并执行仿真，一次仿真结束后ctrl+s保存一个wave.do文件，之后新建一个run.do文件，在wave.do文件开头和结尾加上两行内容：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;vsim work.glbl -voptargs=+acc -L unisims_ver work.ContrastTransform_TB
(wave.do)
run -all
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;work.glbl是由于使用了xilinx的仿真库，必须先加载这个才可以进行仿真，后面的则是禁止优化参数，为了调试而准备，这条指令只要结构是vsim xxx，开始仿真的意思。  &lt;/p&gt;
&lt;p&gt;之后再次仿真只需要执行：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;do run.do
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;即可。&lt;/p&gt;
&lt;p&gt;当然，我们也可以建立一个tcl脚本来完成自动化测试，不过看需求了，完。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Friday, 08 May 2015 23:01:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.05.08 23:01:article/Skill-2015_05_08_b</guid>
<category>FPGA</category>
<category>Modelsim</category>
<category>文件结构</category>
<category>目录结构</category>
<category>仿真</category>
</item>

<item>
<title>【FPGA】Vivado的文件结构和配置</title>
<link>http://dtysky.moe/article/Skill-2015_05_08_a</link>
<description>&lt;p&gt;Vivado是Xilinx公司最新的开发套件，ISE不再支持最新的7系列以及后面将要出的系列，然而Vivado本身也不支持7系列以前的硬件，这就表明它本身是一个分水岭，也或许可以说是一次决意的革新，当然，本人对ISE的坑爹早有耳闻所以以前一直用的是A家的Quartus，不过由于某些原因，嘛，总之Vivado用完了还是很顺手的，尤其是其文件结构值得一提，控制台脚本强大，IP封装比较顺滑并且不得不承认或许先封装IP再图形化连接的确可以提高效率（当然这是在用verilog和vhdl的情况下，抽象强大的sv如果被广泛支持就没他啥事了ww），不过将IP的UI层和IP的逻辑层分离值得赞许，但编辑器还是一如既往的狗屎。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1.目录结构&lt;/h2&gt;
&lt;p&gt;当我们新建了一个工程并且完成了文件添加和综合、布局布线以及生成bit流生成，最后导出sdk后，整体的目录主要如下，此处假设工程名称为EX:  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;EX.xpr: 项目管理文件，里面记录了该工程的文件索引。&lt;br /&gt;
Ex.srcs: 项目源文件目录，里面又分文sources_n, sim_n,和constrs_n而sources_n和sim_n内又分为import和new，顾名思义，源文件和仿真文件被分离开，但源文件实际上也是参与仿真的，sources_n下属子目录的子目录中除了自己新建的设计文件，又可以分为ip、bd等,ip目录下是ip核生成的文件，bd则是vivado专用图形设计界面的文件（bd的创建和文件解析将会在后面的教程中说到），sim_n内的文件通常是test bench，constrs_n内是XDC约束文件，用于时序约束和引脚约束等。此外，如果使用了ip，将可能生成ipshare目录，此目录中存放了一些保存被引用ip的一个临时副本。&lt;br /&gt;
EX.runs: 内部有许多名为(name)_synth_n和(name)_imp_n形式的文件夹，里面是综合和布局布线生成的一些临时文件，对于主模块，其(name)为空，里面生成的文件中包含报告文件和bit流。&lt;br /&gt;
EX.hw: 此目录中是存放硬件信息的，包括使用器件和综合后的网表，同时ila的波形调试结果也在此目录中存储。&lt;br /&gt;
EX.sdk: 只有在使用了CPU(无论是软核还是硬核)时才会出现这个目录，这个目录下存放的是SDK的工程，内部包括根据硬件生成的BSP和你自己新建的软件工程。&lt;br /&gt;
EX.cache: 临时缓存文件，无视即可。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这里需要注意的是，vivado使用的是相对路径，这个相对是相对于.xpr文件而言的，实际上这带来了很大的方便，这意味着我们不必关注绝对路径，只要保证文件结构的基本不变，便可以随意使用，不但如此，xilinx的工程师很欣慰的使用了xml明文方式记录文件架构，无论是xpr文件，还是后面教程中要提到的bd文件，还是后面同样提到的ip管理文件，这意味着如果我们需要创建一系列的工程，比如我做的图像处理库，它们都拥有着相似的结构，那么只需要用文本编辑器对xpr等文件进行编辑基本就可以构建相似的工程。  &lt;/p&gt;
&lt;h2&gt;2.文件结构&lt;/h2&gt;
&lt;p&gt;xpr等文件遵从由明文的xml语法，如果你打开任意一个新建好的工程的xpr文件，将会看到类似于以下的内容：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;cp&amp;quot;&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;/span&gt;
......
&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;Version=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;7&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;Minor=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;2&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;Path=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;......&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;DefaultLaunch&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;Dir=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;$PRUNDIR&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;Configuration&amp;gt;&lt;/span&gt;
        &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;Option&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;Name=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;Id&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;Val=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;58d46179d1874f22945d718ae416c55f&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;/&amp;gt;&lt;/span&gt;
        ......
    &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;/Configuration&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;FileSets&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;Version=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;1&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;Minor=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;31&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;FileSet&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;Name=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;sources_1&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;Type=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;DesignSrcs&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;RelSrcDir=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;$PSRCDIR/sources_1&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;Filter&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;Type=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;Srcs&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;File&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;Path=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;$PSRCDIR/sources_1/new/ContrastTransform.v&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;FileInfo&amp;gt;&lt;/span&gt;
            &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;Attr&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;Name=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;UsedIn&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;Val=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;synthesis&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;Attr&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;Name=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;UsedIn&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;Val=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;implementation&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;Attr&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;Name=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;UsedIn&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;Val=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;simulation&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;/FileInfo&amp;gt;&lt;/span&gt;
        &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;/File&amp;gt;&lt;/span&gt;
        &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;Config&amp;gt;&lt;/span&gt;
        &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;Option&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;Name=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;DesignMode&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;Val=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;RTL&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;Option&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;Name=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;TopModule&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;Val=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;ContrastTransform&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;Option&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;Name=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;TopAutoSet&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;Val=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;TRUE&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;/Config&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;/FileSet&amp;gt;&lt;/span&gt;
......
&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;结构一目了然，非常清晰，Project块表示这个项目中所有的元素，Version等属性代表着项目的属性，内部的一些如同Configuration、FileSet等标签则是表明了这个项目的一些配置和文件结构，比如我这个项目拥有一个名为ContrastTransform.v的主要源文件，所以FileSet的sources_1内便有一个File标签内定义着这个文件，随后还给这个文件定义了作用域，比如&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;Name=&amp;quot;UsedIn&amp;quot; Val=&amp;quot;synthesis&amp;quot;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;就表明这个文件被用于综合。&lt;br /&gt;
其他还有一些设置通过名字就能猜得出来，不再多说。&lt;br /&gt;
那么，我们如何善用这种结构呢，很简单。&lt;br /&gt;
比如我有一个AImage的工程，其中有许多文件，这些文件都是以A_X.v, A_Y.sv形式命名的，新建一个工程再添加未免烦躁，这时候我们可以拷贝整个工程，将所有文件的&amp;quot;A_&amp;quot;改成&amp;quot;B_&amp;quot;，并且在xpr文件中将所有的&amp;quot;A_&amp;quot;替换成&amp;quot;B_&amp;quot;即可。&lt;br /&gt;
如果你会脚本语言，那就更简单了，写个小脚本处理一下就OK（python死忠www）。  &lt;/p&gt;
&lt;h2&gt;3.配置&lt;/h2&gt;
&lt;p&gt;文件目录结构以上就说的差不多了，这里再说一些vivado的配置。&lt;br /&gt;
众所周知，对于一个程序员，一个优雅的编辑器是多么的重要，当用惯了还算优雅的quartus的编辑器再来使用vivado的编辑器时，我感觉我吃到了什么不好的东西，这里我想对x家的工程师说一下，工程师何苦为难工程师。&lt;br /&gt;
当然x家工程师还算有良心，提供了编辑器的可配置选项，在vivado的GUI下，点击标题栏的Tools -&amp;gt; Options -&amp;gt; General -&amp;gt; Text Editor:  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;Vivado-editor-conf&amp;quot; src=&amp;quot;/theme/image/2015_05_08_a/1.png&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;如果你是高贵的vim党啊，emacs党啊，还有不怎么高贵的notepad++党啊，他有默认配置，当然我用的sublime他也有，不过不知道是不是环境变量的问题并没有啥用，所以我用了一下策略：&lt;br /&gt;
在这里选择Custom Editor，并且点击后面的...按钮输入打开编辑器时候的指令：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nl&amp;quot;&gt;B&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Poetic_Being&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Sublim_Text_3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sublime_text&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;exe&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;file&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;line&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;B:/Poetic_Being/Sublim_Text_3/sublime_text.exe是我的sublime执行文件路径，替换掉就可以了。  &lt;/p&gt;
&lt;p&gt;其他配置也没什么好说的了，基本都是大家都会的，好，结束。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Friday, 08 May 2015 21:01:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.05.08 21:01:article/Skill-2015_05_08_a</guid>
<category>FPGA</category>
<category>Vivado</category>
<category>文件结构</category>
<category>目录结构</category>
<category>配置</category>
</item>

<item>
<title>【impress.js】告别PPT从我做起</title>
<link>http://dtysky.moe/article/Skill-2015_05_05_a</link>
<description>&lt;p&gt;impress.js是一个牛逼的js库，它为我们开创了演示界面的新纪元，相比其他同样牛逼的类似的js库，它做到了真正的简单易用却效果强大，其提供的接口十分简洁易懂，同时却不失复杂的变化性，结合CSS能够达到你想达到的几乎任何效果，当然有些太复杂的做起来还不如直接用PPT但是，很多时候那些复杂的东西有必要吗？看需求吧www反正我觉得没有必要。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1.获取和示例&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;在这里直接获取源代码，一个js文件，下下来即可&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/bartaz/impress.js&amp;quot;&gt;impress.js&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;官方示例&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&amp;quot;http://bartaz.github.io/impress.js&amp;quot;&gt;Demo by bartaz&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;我的示例&lt;/strong&gt;&lt;br /&gt;
这个是纯粹为支持impress.js的终端做的，不支持的情况下采用抛出信息的处理方式。&lt;br /&gt;
&lt;a href=&amp;quot;http://proj.dtysky.moe/&amp;quot;&gt;Publihed Projects&lt;/a&gt;&lt;br /&gt;
这个为不支持的终端做了一下处理，其实也就是不同的CSS。&lt;br /&gt;
&lt;a href=&amp;quot;http://fil.dtysky.moe/&amp;quot;&gt;FPGA-Imaging-Library&lt;/a&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2.使用&lt;/h2&gt;
&lt;p&gt;首先需要有一定的Html和CSS基础，这个很简单，自行了解即可，因为严格意义上这两个不算编程（基本不涉及逻辑），并且同时，impress.js的使用基本也不涉及逻辑。  &lt;/p&gt;
&lt;h3&gt;1.链接&lt;/h3&gt;
&lt;p&gt;这一点首先单独列出来是为了澄清一些东西，一般情况下，我们会在&lt;strong&gt;head&lt;/strong&gt;块内加入一些metadata，并链接js和CSS文件，但对于impress.js不能这么做，所以不要在header块内引用它，具体在哪里会在下面说明。  &lt;/p&gt;
&lt;h3&gt;2.作用域&lt;/h3&gt;
&lt;p&gt;作用域，也就是impress.js发生作用的区块，根据它的设计，你需要建立一个id为&lt;strong&gt;impress&lt;/strong&gt;的div，并将你需要作为impress.js演示对象的内容放到其中：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;id&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;impress&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;

&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;在这个div中，一般而言我们需要加入一个class为&lt;strong&gt;no-support-message&lt;/strong&gt;的div，这个div中的内容会在终端不支持impress.js的时候显示出来，比如：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;class=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;no-support-message&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    Your browser doesn&amp;#39;t support impress.js.It can&amp;#39;t phones.&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;br&amp;gt;&lt;/span&gt;
    你的浏览器不支持impress.js.请更换，它不支持手机。
&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;当然位置和样式是由你自己决定的，比如在我的第一个实例中我这样定义：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nc&amp;quot;&gt;.no-support-message&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt; 
&lt;span class=&amp;quot;nb&amp;quot;&gt;display&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;none&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; 
&lt;span class=&amp;quot;nb&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;m&amp;quot;&gt;940px&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; 
&lt;span class=&amp;quot;nb&amp;quot;&gt;margin&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;m&amp;quot;&gt;0&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;auto&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;24px&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;auto&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; 
&lt;span class=&amp;quot;nb&amp;quot;&gt;text-align&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;center&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;效果就是当不支持的时候，这段内容就会显示在页面开头的中间部分。&lt;br /&gt;
对于第二个示例，由于我采用的是其他解决方案，所以没有这段内容。  &lt;/p&gt;
&lt;h3&gt;3.设计&lt;/h3&gt;
&lt;p&gt;确定作用域后便可以开始设计，在上面的作用域中加入若干你需要演示的div，每个div都是一个演示页面，它们必须都拥有&lt;strong&gt;step&lt;/strong&gt;这个class，没有这个class的div会当做一般元素处理，也就是不受impress控制。&lt;br /&gt;
第一个例子的首页：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;id&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;first-page&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;class&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;step&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;data-x&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;0&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;data-y&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;0&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;h1&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;id&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;first-title&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;Published Projects&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;h1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;p&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;id&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;first-desc&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;My published projects are here.&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;p&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;id&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;first-link&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;a&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;href&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;http://dtysky.moe&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;target&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;_blank&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;Blog&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;ni&amp;quot;&gt;&amp;amp;nbsp;&amp;amp;nbsp;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;a&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;href&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;https://github.com/dtysky&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;target&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;_blank&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;Github&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;ni&amp;quot;&gt;&amp;amp;nbsp;&amp;amp;nbsp;&lt;/span&gt;dtysky@outlook.com&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;p&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;id&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;page-top&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;Click a page you want to view, you can also press &amp;quot;←&amp;quot;, &amp;quot;→&amp;quot; or whitespace.
    &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;br&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;点击你想进入的页面或者按下 &amp;quot;←&amp;quot;、&amp;quot;→&amp;quot;或空格键.&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;为了显示这些演示页面，必须要为step定义样式，第一个示例中定义的样式如下：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nc&amp;quot;&gt;.no-support-message&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt; 
&lt;span class=&amp;quot;nb&amp;quot;&gt;display&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;none&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; 
&lt;span class=&amp;quot;nb&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;m&amp;quot;&gt;940px&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; 
&lt;span class=&amp;quot;nb&amp;quot;&gt;margin&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;m&amp;quot;&gt;0&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;auto&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;24px&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;auto&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; 
&lt;span class=&amp;quot;nb&amp;quot;&gt;text-align&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;center&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;每一个页面宽度为940px，位置是浏览器页面正中，所有文字居中。&lt;br /&gt;
你应该注意到了，和一般的属性不同，这个首页的div中拥有&lt;strong&gt;data-x&lt;/strong&gt;和&lt;strong&gt;data-y&lt;/strong&gt;两个属性，而这种属性就是impress.js效果的依赖源。&lt;br /&gt;
这些属性定义了每一个页面的位置信息，这个位置是相对于你的浏览器主页面而言的，主页面的中心位置被定义为原点，所有的属性如下：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;data-x: 距离原点的横向偏移量，像素值&lt;br /&gt;
data-y: 距离纵向的纵向偏移量，像素值&lt;br /&gt;
data-rotate-x: 相对当页中心点的x轴旋转偏移量，角度&lt;br /&gt;
data-rotate-y: 相对当页中心点的y轴旋转偏移量，角度&lt;br /&gt;
data-rotate-z: 相对当页中心点的z轴旋转偏移量，角度&lt;br /&gt;
data-scale: 相对于主体页面的比例，比例值  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;有了这些属性，我们便可以开始设计了，具体的设计就是在上面这些块中加入属性值，这里说的已经足够清晰，自己尝试一下就可以了。  &lt;/p&gt;
&lt;h3&gt;4.工作&lt;/h3&gt;
&lt;p&gt;完成了页面设计之后便可以开始开始演示了，在这之前，我们需要链接impress.js并启动它，在你的impress块后加入两行代码即可：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;id&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;impress&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    ......
&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;script&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;type&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;src&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;js/impress.js&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;script&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;script&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;impress&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;().&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;init&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;script&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;5.其他技巧&lt;/h3&gt;
&lt;p&gt;不知道是否只有我有这个需求，就像第一个示例一样，将演示页面分类，一块一块展示，展示完一块可以选择直接返回主页面，这个需求可以直接通过以下方式实现：  &lt;/p&gt;
&lt;p&gt;观察启动后的标题栏，我们可以知道每一个页面的url是这么表示的：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;(主页面url)/#/演示页面id  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;比如对于第一个实例的首页，url是：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;http://proj.dtysky.moe/#/first-page  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;所以可以在某个演示页面中插入一个超链接：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;class&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;step&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    ......
    &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;class&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;return-first&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;a&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;href&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;#/first-page&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;Return / 返回页首&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;class为&lt;strong&gt;return-first&lt;/strong&gt;的块中有一个链接，这个链接指向了首页。  &lt;/p&gt;
&lt;h2&gt;3.示例代码&lt;/h2&gt;
&lt;p&gt;这里提供一个简洁版本的模板，除了主要内容之外一切皆无：&lt;br /&gt;
将三个文件放入一个文件夹即可。  &lt;/p&gt;
&lt;h3&gt;html&lt;/h3&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;cp&amp;quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;html&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;lang&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;en-US&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;head&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;meta&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;charset&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;UTF-8&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;title&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;Test&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;title&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;link&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;rel&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;stylesheet&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;href&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;style.css&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;head&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;body&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;id&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;impress&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;class&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;no-support-message&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
        Your browser doesn&amp;#39;t support impress.js.It can&amp;#39;t phones.&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;br&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
        你的浏览器不支持impress.js.请更换，它不支持手机。
    &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;id&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;first-page&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;class&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;step&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;data-x&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;0&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;data-y&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;0&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;h1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;首页&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;h1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;br&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;点击你想进入的页面或者按下 &amp;quot;←&amp;quot;、&amp;quot;→&amp;quot;或空格键.&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;class&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;step&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;data-x&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;-700&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;data-y&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;-200&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;data-scale&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;0.6&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;data-rotate-z&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;-45&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;h1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;第一块-第一页&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;h1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;class&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;step&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;data-x&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;-1100&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;data-y&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;100&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;data-scale&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;0.4&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;data-rotate-z&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;-135&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;h1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;第一块-第二页&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;h1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;class&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;step&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;data-x&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;-1300&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;data-y&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;-100&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;data-scale&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;0.2&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;data-rotate-z&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;-45&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;h1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;第一块-第三页&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;h1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;class&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;return-first&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;a&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;href&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;#/first-page&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;Return / 返回页首&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;class&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;step&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;data-x&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;800&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;data-y&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;-300&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;data-scale&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;0.6&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;data-rotate-z&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;45&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;h1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;第二块-第一页&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;h1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;class&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;step&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;data-x&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;1150&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;data-y&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;50&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;data-scale&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;0.4&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;data-rotate-z&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;135&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;h1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;第二块-第二页&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;h1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;class&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;step&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;data-x&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;1400&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;data-y&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;-200&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;data-scale&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;0.2&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;data-rotate-z&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;45&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;h1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;第二块-第三页&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;h1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;class&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;return-first&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;a&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;href&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;#/first-page&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;Return / 返回页首&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;script&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;type&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;src&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;impress.js&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;script&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;script&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;impress&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;().&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;init&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;script&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;body&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;html&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;css&lt;/h3&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;h1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;h2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;h3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;h4&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;h5&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;h6&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
&lt;span class=&amp;quot;nb&amp;quot;&gt;font-family&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;Microsoft YaHei&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;important&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;nt&amp;quot;&gt;body&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nb&amp;quot;&gt;margin&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;0px&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;nc&amp;quot;&gt;.return-first&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nb&amp;quot;&gt;margin-top&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;50px&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;nc&amp;quot;&gt;.return-first&lt;/span&gt; &lt;span class=&amp;quot;nt&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nb&amp;quot;&gt;font-size&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;20px&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;nb&amp;quot;&gt;text-decoration&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;none&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;nb&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;#000000&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;nc&amp;quot;&gt;.return-first&lt;/span&gt; &lt;span class=&amp;quot;nt&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;nd&amp;quot;&gt;:hover&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nb&amp;quot;&gt;font-size&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;21px&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;nb&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;#006688&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;nc&amp;quot;&gt;.step&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt; 
    &lt;span class=&amp;quot;nb&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;m&amp;quot;&gt;940px&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; 
    &lt;span class=&amp;quot;nb&amp;quot;&gt;margin&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;m&amp;quot;&gt;0&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;auto&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;24px&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;auto&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; 
    &lt;span class=&amp;quot;nb&amp;quot;&gt;text-align&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;center&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; 
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;nc&amp;quot;&gt;.no-support-message&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt; 
    &lt;span class=&amp;quot;nb&amp;quot;&gt;display&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;none&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; 
    &lt;span class=&amp;quot;nb&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;m&amp;quot;&gt;940px&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; 
    &lt;span class=&amp;quot;nb&amp;quot;&gt;margin&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;m&amp;quot;&gt;0&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;auto&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;24px&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;auto&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; 
    &lt;span class=&amp;quot;nb&amp;quot;&gt;text-align&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;center&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;js&lt;/h3&gt;
&lt;p&gt;自备impress.js&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Tuesday, 05 May 2015 02:01:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.05.05 02:01:article/Skill-2015_05_05_a</guid>
<category>impress.js</category>
<category>PPT</category>
</item>

<item>
<title>【VPS/DNS/Nginx】Blog域名，服务器转移</title>
<link>http://dtysky.moe/article/Skill-2015_04_26_a</link>
<description>&lt;p&gt;由于某些原因我将Blog的域名从dtysky.github.io转移到了dtysky.moe，并且由于另一些原因将服务器转移到了某家的VPS上（正好有Github学生包的Digitalocean的优惠，可以用一年），这二者加上Dnspod提供的解析服务，基本足够满足我以后的某些需求了，比如某些项目的发布和Wiki，比如准备在一个半月内发布的这个&lt;a href=&amp;quot;http://fil.dtysky.moe&amp;quot;&gt;FPGA Imaging Library&lt;/a&gt;。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1.注册域名：&lt;/h2&gt;
&lt;p&gt;首先去&lt;a href=&amp;quot;https://www.godaddy.com/&amp;quot;&gt;Godaddy&lt;/a&gt;注册了dtysky.moe域名，这个没什么特别需要注意的，不过有朋友推荐moe域名（或者说泛ACG域名，比如.lu .wtf啥的，有没人想搞个&amp;quot;foreveralone.wtf.lu.rip&amp;quot;哈哈哈）在&lt;a href=&amp;quot;http://www.hostker.com/&amp;quot;&gt;hostker&lt;/a&gt;买，看了一下的确要便宜不少（差不多一半），如果一定要在Godaddy购买的话，建议去搜一下优惠码，我就优惠了30%...&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2.注册Dnspod并添加网站：&lt;/h2&gt;
&lt;p&gt;由于Godaddy提供的DNS解析可能被墙，所以建议去&lt;a href=&amp;quot;https://www.dnspod.cn&amp;quot;&gt;Dnspod&lt;/a&gt;注册一个账号，随后将你的域名添加进去，之后然后按照&lt;a href=&amp;quot;https://support.dnspod.cn/Kb/showarticle/tsid/42/&amp;quot;&gt;这里的教程&lt;/a&gt;在Godaddy上修改你的域名服务器，生效需要十几分钟到几十个小时。&lt;br /&gt;
改完域名服务器后，在Dnspod的控制台对你的域名添加DNS记录，记录类型啥的知识这里就不说了，总之，至少建立一条记录类型为&lt;strong&gt;A&lt;/strong&gt;的记录，主机记录填写&lt;strong&gt;@&lt;/strong&gt;，也就是根域名，随后将记录值指向你的服务器IP，对于这个IP的选择：  &lt;/p&gt;
&lt;h3&gt;1.继续使用Github-pages：&lt;/h3&gt;
&lt;p&gt;如果你仍然想使用Github的github-pages服务，那么在&lt;a href=&amp;quot;https://help.github.com/articles/tips-for-configuring-an-a-record-with-your-dns-provider/&amp;quot;&gt;这里&lt;/a&gt;找到你需要的IP地址，填上，并且再建立一条类型为&lt;strong&gt;CNAME&lt;/strong&gt;的记录，主机记录填写&lt;strong&gt;www&lt;/strong&gt;（如果你期望将根域名作为你的blog地址）或者其他（将二级域名作为blog地址，比如&lt;strong&gt;blog&lt;/strong&gt;），记录值指向你原来的Github-pages的地址就OK了（比如dtysky.github.io）。&lt;br /&gt;
之后，在你的Github-pages工程根目录下新建一个名为&lt;strong&gt;CNAME&lt;/strong&gt;的文件，把你的域名（比如dtysky.moe）填进去就OK。  &lt;/p&gt;
&lt;h3&gt;2.使用自己的服务器：&lt;/h3&gt;
&lt;p&gt;如果你想使用自己的服务器（搭建服务器可以参考下面），那么就将服务器IP填上，随后再建立一条A记录，主机记录填写&lt;strong&gt;*&lt;/strong&gt;，也就是泛解析，在泛解析下，所有的二级域名都会被解析到指定的记录值，记录值同样填写服务器IP。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3.购买VPS并配置服务器：&lt;/h2&gt;
&lt;p&gt;由于每个人选择的VPS服务商不同，所以如何购买就不赘述了，要注意在选择VPS类型的时候，按照需求即可，对于挂几个静态网站而言，512内存完全够了。。。系统的选择，我这里实例用的是Ubuntu，看需求吧（俺不是xxxer，见谅）。  &lt;/p&gt;
&lt;h3&gt;1.准备：&lt;/h3&gt;
&lt;p&gt;建立了VPS后，应该可以看到服务商给了你一个IP地址，这个IP就是上面那个记录值需要的IP，这个IP同样也要用于管理你的VPS。&lt;br /&gt;
至于VPS的安全协议，随便吧，懒点就SSH，下个PuTTYGen生成一个就成，使用说明自己找吧，挺简单的。&lt;br /&gt;
接下来就是实际的服务器配置了，这里建议首先备好工具&lt;a href=&amp;quot;http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html&amp;quot;&gt;PuTTY&lt;/a&gt;和&lt;a href=&amp;quot;http://winscp.net/eng/docs/lang:chs&amp;quot;&gt;Winscp&lt;/a&gt;，前者是命令行环境，用于调试，后者是FTP环境，用于文件管理。  &lt;/p&gt;
&lt;h3&gt;2.配置&lt;/h3&gt;
&lt;p&gt;首先打开PuTTY，按照你选的安全协议连接到服务器，之后安装nginx，nginx是一个HTTP和反向代理服务器，这里用来做域名管理，安装只需要执行以下命令行代码：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;apt-get update
apt-get install nginx
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;等待安装完成，完成后首先执行以下命令启动以下并检测状态（假定安装使用默认设置）：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;/etc/init.d/nginx start
/etc/init.d/nginx status
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;如果显示&lt;strong&gt;not running&lt;/strong&gt;，一般是80端口被占用，要么在配置文件改端口，要么停止冲突的服务，如何查看端口被某个服务占用请自行谷歌。&lt;br /&gt;
解决冲突问题或者需要更改配置来解决冲突或者没有冲突的，可以进行配置文件管理了，打开Winscp，连接到服务器，打开以下文件夹：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;/etc/nginx  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;为了方便管理，我们新建一个名为&lt;strong&gt;vhosts&lt;/strong&gt;的文件夹，顾名思义就是“虚拟主机”的意思，随后打开nginx.conf文件，在&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;http {
    .....
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;的最后添加这一句：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;include /etc/nginx/vhosts/*.conf;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;然后根据你要管理的域名建立若干配置文件，比如我要管理dtysky.moe和fil.dtysky.moe，我就建立了：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;dtysky.moe.conf&lt;br /&gt;
fil.dtysky.moe.conf   &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;然后根据建立虚拟主机的语法修改每个配置文件就可以了，比如我打开dtysky.moe.conf文件，写入以下内容：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;server&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt; 
    &lt;span class=&amp;quot;n&amp;quot;&gt;listen&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;80&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; 
    &lt;span class=&amp;quot;n&amp;quot;&gt;server_name&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;dtysky&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;moe&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; 
    &lt;span class=&amp;quot;n&amp;quot;&gt;root&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;www&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dtysky&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;moe&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;index&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;index&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;html&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;index&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;htm&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;error_page&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;404&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;m&amp;quot;&gt;404&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;html&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;error_page&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;500&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;502&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;503&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;504&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;m&amp;quot;&gt;50&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;html&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; 
    &lt;span class=&amp;quot;n&amp;quot;&gt;location&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;m&amp;quot;&gt;50&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;html&lt;/span&gt; &lt;span class=&amp;quot;err&amp;quot;&gt;{&lt;/span&gt;
          &lt;span class=&amp;quot;n&amp;quot;&gt;root&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;html&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;err&amp;quot;&gt;}&lt;/span&gt; 
&lt;span class=&amp;quot;nt&amp;quot;&gt;server&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt; 
    &lt;span class=&amp;quot;n&amp;quot;&gt;listen&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;80&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; 
    &lt;span class=&amp;quot;n&amp;quot;&gt;server_name&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;www&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dtysky&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;moe&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; 
    &lt;span class=&amp;quot;n&amp;quot;&gt;location&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;err&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;rewrite&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;^/&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.*&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;$&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;http&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;://&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dtysky&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;moe&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;m&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;permanent&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;err&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;想解决上面端口冲突的朋友可以把这里的端口（listen 80;）改成别的，同时还要对enable-site/default文件进行同样的修改。 &lt;/p&gt;
&lt;p&gt;第一个server内表明将域名dtysk.moe的请求定向到&lt;strong&gt;/www/dtysky.moe&lt;/strong&gt;文件夹下，这个文件夹就是我本地放blog资源的文件夹，你可以按照你的需求更改。&lt;br /&gt;
第二个server下的内容表示将www.dtysky.moe的请求重定向到dtysky.moe，具体请自行学习nginx的语法规则。  &lt;/p&gt;
&lt;p&gt;fil.dtysky.moe.conf基本是同样的写法，如下：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;server { 
    listen 80; 
    server_name fil.dtysky.moe; 
    location / {
        root /www/fil.dtysky.moe; 
        index index.html index.htm;
    }
    error_page 404 /404.html;
    error_page 500 502 503 504 /50x.html; 
    location = /50x.html {
          root html;
    }
}
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;当然这不是唯一的方法，我们同样可以直接把每个虚拟主机的配置都直接写到nginx.conf内，看需求了。  &lt;/p&gt;
&lt;p&gt;配置完成后，用以下命令对nginx进行重启：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;/etc/init.d/nginx reload
/etc/init.d/nginx restart
&lt;/pre&gt;&lt;/div&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Sunday, 26 Apr 2015 20:01:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.04.26 20:01:article/Skill-2015_04_26_a</guid>
<category>VPS</category>
<category>DNS</category>
<category>Nginx</category>
<category>Ubuntu</category>
</item>

<item>
<title>【Vivado/FPGA】包含Fifo或者BRam等xci文件的IP封装注意</title>
<link>http://dtysky.moe/article/Skill-2015_04_12_a</link>
<description>&lt;p&gt;Vivado提供了全新的设计模式，其中一项就是用户自己可以封装IP，然后在其他工程中方便地调用，用户可以自己设计IP界面等等，IP的文件结构我已经很清晰了，不过不会在这里写出，将会在之后的毕业论文中同步写出一个系列。此片博文主要记录了在封装IP中的一个问题，这个问题将会引起严重警告[filemgmt 20-1741]和[Synth 8-2488]。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1.问题描述&lt;/h2&gt;
&lt;p&gt;我封装的某些IP中使用到了官方IP库中的Fifo和BlockRam，在封装时包含了它们的xci文件，由于需求问题，我封装的IP形式如下：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Rows8x512和Rows1x512&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这二者的区别在于它们内部实例化的Fifo的位宽，一个是8位，一个是1位，为了方便起见，我将两个IP内的Fifo都实例化为了“Fifo”这个名字，也就是说两个IP内包含的、依赖于xci文件的子IP拥有着相同的名字。&lt;br /&gt;
随后我在一个工程中同时实例化了这两个IP，综合后出现了以下警告：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[filemgmt 20-1741] File &amp;apos;Fifo.vhd&amp;apos; is used by one or more modules, but with different contents, and may lead to unpredictable results:&lt;br /&gt;
&lt;em&gt; Fifo (b:/Complex_Mind/Graduation_Project/ProjectBuilt/Gobang/Gobang.srcs/sources_1/bd/Gobang/ip/Gobang_Rows8x512_0_0/sources_1/ip/Fifo/synth/Fifo.vhd)&lt;br /&gt;
&lt;/em&gt; Fifo (b:/Complex_Mind/Graduation_Project/ProjectBuilt/Gobang/Gobang.srcs/sources_1/bd/Gobang/ip/Gobang_Rows8x512_1_0/sources_1/ip/Fifo/synth/Fifo.vhd)&lt;br /&gt;
* Fifo (b:/Complex_Mind/Graduation_Project/ProjectBuilt/Gobang/Gobang.srcs/sources_1/bd/Gobang/ip/Gobang_Rows1x512_0_0/ip/Fifo/synth/Fifo.vhd)&lt;br /&gt;
Please reset and regenerate these modules to resolve the differences, or synthesize them independently.  &lt;/p&gt;
&lt;p&gt;[Synth 8-2488] overwriting existing primary unit fifo [&amp;quot;b:/Complex_Mind/Graduation_Project/ProjectBuilt/Gobang/Gobang.srcs/sources_1/bd/Gobang/ip/Gobang_Rows1x512_0_0/ip/Fifo/synth/Fifo.vhd&amp;quot;:59]  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;一开始并没有在意这种问题，但是实际测试的失败让我不得不回来查找问题的所在之处，在查看综合后的原理图之后我发现了以下问题：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/2_zpsfkogyily.png&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/1_zpsr8ayfmyl.png&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;很显然，Rows8x512中位宽为8的Fifo被覆盖了，覆盖成了位宽为1的Fifo，所以in_data直接被削减为了1位，这是由于两个IP钟Fifo子IP的名字是一致的，因为xci只会生成一个总的子IP文件，而不会为每个IP单独生成。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2.解决方案&lt;/h2&gt;
&lt;p&gt;解决方案就是改名，将不同的IP中依赖的子IP起上不同的名字就好，这样虽然改起来麻烦点，但却可以保证IP中只包含子IP的xci文件，这是十分轻量的做法，予以推荐，当然这种做法并不能保证百分百得完美，毕竟工程大了之后重名的可能性也会提高不少，但没有其他办法。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Sunday, 12 Apr 2015 20:01:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.04.12 20:01:article/Skill-2015_04_12_a</guid>
<category>Vivado</category>
<category>FPGA</category>
<category>IPCore</category>
<category>xci</category>
</item>

<item>
<title>【Graphviz/Dot】网络图设计</title>
<link>http://dtysky.moe/article/Skill-2015_03_02_a</link>
<description>&lt;p&gt;诚然，MS的visio是个很强大的网络图工具，不但如此，其他的工具也很多，他们有一个共同点就是图形化界面，这对于一般用户是很友好的，但对于码农，或者说追求更加一点的可控性和效率亦或是懒人而言，还是码代码来的好写，生成排版就交给程序自己去做嘛（当然控制欲极强的人就绕道吧，用这个实现只会更加复杂），所以在基友的推荐下，用了下Graphviz——一个基于Dot语言的流程设计环境，意外感觉不错？   &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1.安装&lt;/h2&gt;
&lt;p&gt;在这里下载Graphviz：&lt;br /&gt;
&lt;a href=&amp;quot;http://www.graphviz.org/Download..php&amp;quot;&gt;http://www.graphviz.org/Download..php&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;根据自己os的类型选择下载完毕后，打开gvedit，我们就可以开始使用了。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2.入门&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1.新建：&lt;/strong&gt;&lt;br /&gt;
点击&lt;strong&gt;New&lt;/strong&gt;按钮，新建一张流程图。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;2.第一张图&lt;/strong&gt;：
首先，在xxx.gv文件中输入代码：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;graph x{
a -- c;
b -- c;
}
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;其次，键盘F5，便可以生成：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/0_zpssyc4dna2.png&amp;quot; /&gt;&lt;br /&gt;
这是一张最简单的无向图，使用&lt;strong&gt;graph&lt;/strong&gt;定义，元素依存关系用双横杠表征。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;3.编码：&lt;/strong&gt;&lt;br /&gt;
如果没有做任何设定，中文无法被正常编码，只会出现一个个方块，这时候我们可以这样解决：  &lt;/p&gt;
&lt;p&gt;首先，将文本保存为utf-8（默认是utf-8）。&lt;br /&gt;
其次，将字体设定为包含中文的自定字体:&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;edge [fontname=&amp;quot;yourfontname&amp;quot;];
node [fontname=&amp;quot;FangSong&amp;quot;];
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;前一个表示边缘的字体，后一个是节点的字体。&lt;/p&gt;
&lt;p&gt;比如我这里是：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;graph x{
    edge [fontname=&amp;quot;FangSong&amp;quot;];
    node [fontname=&amp;quot;FangSong&amp;quot;];
    a [label=&amp;quot;我&amp;quot;];
    b [label=&amp;quot;妹妹&amp;quot;];
    c [label=&amp;quot;女儿&amp;quot;];
    a -- c;
    b -- c;
}
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;结果是：&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/1_zpsjsahx8my.png&amp;quot; /&gt;&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;4.属性：&lt;/strong&gt;&lt;br /&gt;
不错，以上“编码”便是图的一种属性，每个元素都可以拥有各自的属性。  &lt;/p&gt;
&lt;p&gt;属性的定义方式也很简单，其基本形式如下：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;x [xa=&amp;quot;&amp;quot;,xb=&amp;quot;&amp;quot;....]
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;如上面说的一样，label也是一种属性，指的是元素的内容，还有很多其他属性，比如框图样式、连线样式等，不再赘述。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;5.有向图：&lt;/strong&gt;&lt;br /&gt;
有向图用&lt;strong&gt;digraph&lt;/strong&gt;定义，反向和依存关系用横杠加箭头表征，例子如下：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;digraph x{
    edge [fontname=&amp;quot;FangSong&amp;quot;];
    node [fontname=&amp;quot;FangSong&amp;quot;];
    a [label=&amp;quot;我&amp;quot;,color=blue];
    b [label=&amp;quot;妹妹&amp;quot;,color=green];
    c [label=&amp;quot;女儿&amp;quot;,color=red,shape=box];
    a -&amp;gt; b [label=&amp;quot;兄妹&amp;quot;,dir=both];
    a -&amp;gt; c [color=blue,label=&amp;quot;父亲&amp;quot;];
    b -&amp;gt; c [color=green,label=&amp;quot;母亲&amp;quot;];
}
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;结果：&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/2_zpsnx8l8dqj.png&amp;quot; /&gt;&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;6.和sublime协作：&lt;/strong&gt;&lt;br /&gt;
如果不想用他自带的ide，又想达到差不多或者更好的使用体验，可以用别的编辑器，我用sublime，如下操作：&lt;br /&gt;
新建sublime的bulid文件&lt;strong&gt;dot.sublime-bulid&lt;/strong&gt;，写入以下内容：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;shell_cmd&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;yourdot.exepath -Tpng $file -o $file.png&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;file_regex&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;^.+ in (.+) on line ([0-9]+)&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;selector&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;source.dot&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;encoding&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;cp936&amp;quot;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;把yourdot.exepath替换成你graphviz安装目录下bin\dot.exe文件的绝对路径，或者将bin目录加到环境变量，直接写dot就可以。&lt;br /&gt;
之后打开你的dot文件，写入你的语句。&lt;br /&gt;
ctrl+b进行bulid，第一次生成png图像（你可以控制参数生成不同的）。&lt;br /&gt;
用sublime打开Png。&lt;br /&gt;
以后修改的时候直接bulid，之后sublime会自动响应刷新修改后的图像（本来想直接写个插件不用这么麻烦来着，没时间，以后再说ww）。&lt;br /&gt;
整体如下：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/3_zpsy4zfwusv.jpg&amp;quot; /&gt;&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Monday, 02 Mar 2015 20:01:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.03.02 20:01:article/Skill-2015_03_02_a</guid>
<category>Graphviz</category>
<category>Dot</category>
<category>网络图</category>
</item>

<item>
<title>【Python】反射</title>
<link>http://dtysky.moe/article/Skill-2015_02_22_b</link>
<description>&lt;p&gt;以下例子皆基于这个工程：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/Gal2Renpy/tree/master/Gal2Renpy&amp;quot;&gt;https://github.com/dtysky/Gal2Renpy/tree/master/Gal2Renpy&lt;/a&gt;&lt;br /&gt;
反射，指的是程序可以对自身进行访问、修改等等的能力，一个具有反射特性的语言能够为我们提供许多便利-比如轻松地实现扩展，轻松地实现事件等添加等等，我们可以获得任何一个类、函数、实例、模块等自身的信息，通过这些信息进行一些动态的操作，使得某些功能的实现更为简单和清晰。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1.需求&lt;/h2&gt;
&lt;p&gt;在编写游戏剧本解析器的时候我遇到了某些问题，这源于一个需求：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;如何能够采用某种方式，使得用户可以自己扩展一个标签，并且编写自己的py代码去实现这个标签的所有功能？  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这意味着我需要去寻找这样的一种方法：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;一开始搜索所有具有某种相同特性的“类”，之后自动创建他们的实例，在之后去调用这些实例中相同的方法&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;如果能够实现这样的方法，之后只需要提供给用户一个接口（扩展的规范），用户按照这个规范来编写自己的类，放到指定位置，程序就能够自动识别这个类作为自己的一部分，从而便完成了扩展。&lt;br /&gt;
在寻找了一段时间后，我终于找到了方法，这个灵感是在编写sublime的插件时出现的，也就是利用py的反射特性和“万物皆对象”的哲学，来完成这个需求，下面我就会用这个实例来说明。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2.实现&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1.获取当前类名：&lt;/strong&gt;&lt;br /&gt;
定义一个类后，我们可以使用以下方法获取类自身的信息：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;bp&amp;quot;&gt;self&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;__class__&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xxx&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;对于类的名字，我们可以用以下方法获得：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;bp&amp;quot;&gt;self&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;__class__&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;__name__&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这样一来我们就有了判断某个类是否属于某个预定好的类型的一个简单方法——使用正则去检查类的名字，属于某个规范的均为一类，比如我想要设定一个规范，这个规范下的类要执行的是一些要创建某些东西的方法，我可以这样命名：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;class&lt;/span&gt; &lt;span class=&amp;quot;nc&amp;quot;&gt;CreatA&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;pass&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;class&lt;/span&gt; &lt;span class=&amp;quot;nc&amp;quot;&gt;CreatB&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;pass&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;之后使用&lt;strong&gt;CreatX&lt;/strong&gt;去匹配，便可以判断当前类是否是某个规范下的类。&lt;br /&gt;
当然我并没有用这个方法，而是用了比较偷懒的方法（也是为了方便管理嘛）——将所有同样类型的类放在同一个文件夹下，比如&lt;a href=&amp;quot;https://github.com/dtysky/Gal2Renpy/tree/master/Gal2Renpy/SpSyntax&amp;quot;&gt;这个文件夹&lt;/a&gt;内就是用于执行所有标签解析方法（规范&lt;strong&gt;Sp&lt;/strong&gt;）的类。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;2.继承：&lt;/strong&gt;&lt;br /&gt;
光是获得了类名显然还是不够的，我们必须对类名进行某些处理以便于之后的操作，这里我设计了一个父类，在父类预先定义好一些方法，作为某个规范的标准，之后所有从属于这个规范的类均继承自这个父类。&lt;br /&gt;
所有子类使用了驼峰式命名法，并均会被继承自父类的&lt;strong&gt;GetFlag&lt;/strong&gt;方法解析，比如我的&lt;strong&gt;Sp&lt;/strong&gt;规范下的子类：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;def&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;GetFlag&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;bp&amp;quot;&gt;self&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;):&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;s&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;bp&amp;quot;&gt;self&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;__class__&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;__name__&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;replace&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;Sp&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;tmp&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;&amp;#39;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;_s_&lt;/span&gt; &lt;span class=&amp;quot;ow&amp;quot;&gt;in&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;s&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;tmp&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;_s_&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;_s_&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;islower&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;_&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;_s_&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;lower&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;tmp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;此方法会返回解析后的类名，比如&lt;strong&gt;SpAaBb&lt;/strong&gt;会返回&lt;strong&gt;aa_bb&lt;/strong&gt;。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;3.获取：&lt;/strong&gt;&lt;br /&gt;
创建完所有子类后，我们便可以对他们进行实例化，在程序运行时（比如此处是在剧本解析时），只需要根据他们的名字（此处是标签）来选择对应的实例进行操作即可。&lt;br /&gt;
为了实现这个目的，我们首先要获取所有这个规范下的类，我采用的方法如下：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/Gal2Renpy/blob/master/Gal2Renpy/G2R/GetAllClass.py&amp;quot;&gt;GetAllClass&lt;/a&gt;&lt;br /&gt;
这是一个函数，用于获取一个文件夹下所有的类，并且判断这个类是否属于某个标签。&lt;br /&gt;
py可以传递任何东西，包括模块、类、函数，这位我提供了很大的便利。&lt;br /&gt;
这个函数的两个参数中前一个是子类的文件夹路径，后一个是子类的父类。&lt;br /&gt;
首先遍历这个文件夹，将后缀为&lt;strong&gt;.py&lt;/strong&gt;的文件作为模块动态导入，并存入Mds列表：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Mds&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;append&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;__import__&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;n&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;__import()__&lt;/p&gt;

&lt;p&gt;函数用于动态导入模块。&lt;/p&gt;
&lt;p&gt;之后遍历拥有所有模块的队列，从模块中获取类的名字：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;for d in dir(m):
    d=getattr(m,d)
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;先注意&lt;strong&gt;dir()&lt;/strong&gt;函数，它是反射的一部分，用于列举参数中所有的属性、方法等等，比如我们可以执行：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;kn&amp;quot;&gt;import&lt;/span&gt; &lt;span class=&amp;quot;nn&amp;quot;&gt;os&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;dir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;os&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;F_OK&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;O_APPEND&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;O_BINARY&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;O_CREAT&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;O_EXCL&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;O_NOINHERIT&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;O_RANDOM&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;O_RDONLY&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;O_RDWR&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;O_SEQUENTIAL&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;O_SHORT_LIVED&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;O_TEMPORARY&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;O_TEXT&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;O_TRUNC&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;O_WRONLY&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;P_DETACH&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;P_NOWAIT&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;P_NOWAITO&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;P_OVERLAY&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;P_WAIT&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;R_OK&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;SEEK_CUR&amp;#39;&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;...............&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;再注意&lt;strong&gt;getattr()&lt;/strong&gt;函数，它的作用是获取m.d属性：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;getattr&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;os&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;walk&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;function&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;walk&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;at&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x00000000020AD9E8&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;接下来便可以判断这个当前的对象是不是类、以及是不是我们需要的子类了：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;def&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;IsClass&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;):&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;type&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;==&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;type&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ParentClass&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;def&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;IsSubOfTag&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;):&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;issubclass&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ParentClass&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;第一个函数用于判定当前对象的类型，如果是类则返回真，否则返回假。这里使用了&lt;strong&gt;type()&lt;/strong&gt;方法，这个方法用于判定对象的类型：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;type&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;getattr&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;os&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;walk&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;type&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;function&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;第二个函数判定当前已经被判定为类的对象是否为父类ParentClass的子类，使用了&lt;strong&gt;issubclass&lt;/strong&gt;方法。  &lt;/p&gt;
&lt;p&gt;以上都结束后便可以将所有符合要求的子类构成干的列表返回备用了。&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4.实例化：&lt;/strong&gt;&lt;br /&gt;
有了子类构成的列表我们便可以开始利用这些子类了，利用的先决条件是进行实例化，实例化之后可以选择立即使用或者存以待用，比如以下代码是存以待用的一个例子：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;def&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;SpCreat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;SpSyntaxPath&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;SpSyntax&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;):&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;Cls&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;GetAllClass&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;SpSyntaxPath&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;SpSyntax&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;Objs&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{}&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;c&lt;/span&gt; &lt;span class=&amp;quot;ow&amp;quot;&gt;in&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;Cls&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;obj&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;c&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;Objs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;obj&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;GetFlag&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()]&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;obj&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;Objs&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;首先使用上面的&lt;strong&gt;GetAllClass&lt;/strong&gt;方法获取所有&lt;strong&gt;Sp&lt;/strong&gt;规范的子类，然后对每一个子类进行实例化，实例化之后利用每一个子类的&lt;strong&gt;GetFlag&lt;/strong&gt;方法获取其名字，之后将名字和其本身作为键值对存入字典，之后便可以根据标签名来查找这个字典、去调用对应对象的方法了。&lt;br /&gt;
比如，我可以设定一个规范，所有&lt;strong&gt;Sp&lt;/strong&gt;规范下的子类必须有一个&lt;strong&gt;Show&lt;/strong&gt;方法，去返回这个子类职责下执行的结果，那么日后我便可以这样用：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;SpC&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;SpCreat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;SpC&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;NowTag&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Show&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;结束。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Sunday, 22 Feb 2015 15:01:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.02.22 15:01:article/Skill-2015_02_22_b</guid>
<category>Python</category>
<category>反射</category>
<category>参数</category>
<category>可扩展</category>
</item>

<item>
<title>【Css】奇淫巧技-元素分段居中</title>
<link>http://dtysky.moe/article/Skill-2015_02_22_a</link>
<description>&lt;p&gt;让某些元素均匀居中排列是前端布局中很常见的一个需求，笔者在遇到这个问题的时候查找了不少的解决方案，但几乎都无法达到一个通用的效果（相对距离布局），所以进行了些许的研究和尝试，希望大家能够有所借鉴。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1.需求&lt;/h2&gt;
&lt;p&gt;所谓元素均匀居中布局，指的就是让一些文字或者图片在一行中均匀排列，彼此之间的间距相等，例如就是本博客上方的四个分类（Create/Skill...那四个），当然这四个分类实际上并不都居中，不过思想基本是一样的。&lt;br /&gt;
为了实现这种需求，最笨的方法就是固定定位，也就是基于每一块元素固定的坐标，但显然这种做法对于通用性而言并不友好，它并不能够在浏览器尺寸变化是进行跟随。&lt;br /&gt;
另一种方法就是使用fixed进行绝对地相对位置定位，但这样做了之后往往会导致你的所有其他元素都要用同样的方式定位，对于布局又不怎么友好。&lt;br /&gt;
同样的问题也出现在absolute类型的定位上，虽然可以达成目的，但这样做会让其他的元素产生依赖，而且对于每一个这样的元素分别定位的方法显得非常繁杂、违背了简单的设计理念。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2.实现&lt;/h2&gt;
&lt;p&gt;所以我们就必须要有一个简单的方法去实现这个需求，既通用又不繁杂，这个方法如下：  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1.行分割：&lt;/strong&gt;&lt;br /&gt;
对这些元素所处的同一行进行分割，比如我有四个元素，就将这一行分成四个25%的块：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nc&amp;quot;&gt;.one_block&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nb&amp;quot;&gt;display&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;inline&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;block&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;nb&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;25%&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;2.元素居中：&lt;/strong&gt;&lt;br /&gt;
比如对于文字而言，我们可以这么写：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nc&amp;quot;&gt;.center_text&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nb&amp;quot;&gt;text-align&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;center&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这样一来，你的文字就实现了通用而简单的均匀居中。  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3.进阶：&lt;/strong&gt;&lt;br /&gt;
那么如果我不想居中，而是想让两边的文字处于最左和最右侧，并且还能够达到等距均匀分布的效果，怎么办？&lt;br /&gt;
实际上本博客的四个分类就是要实现这样的需求，这时候只需要做一下小小的计算就可以了：&lt;br /&gt;
首先仍然进行行分割，分成四个25%的块。&lt;br /&gt;
然后给最左边和左右边的文字设定样式：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nc&amp;quot;&gt;.text0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nb&amp;quot;&gt;text-align&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;left&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;nc&amp;quot;&gt;.text3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nb&amp;quot;&gt;text-align&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;right&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;然后根据需求计算中间两块文字的定位百分比：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nc&amp;quot;&gt;.text1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nb&amp;quot;&gt;padding-left&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;33%&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;nc&amp;quot;&gt;.text2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nb&amp;quot;&gt;padding-right&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;m&amp;quot;&gt;33%&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;如此便可以达到需求，其他情况类推即可。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Sunday, 22 Feb 2015 12:01:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.02.22 12:01:article/Skill-2015_02_22_a</guid>
<category>Css</category>
<category>Html</category>
<category>前端</category>
<category>居中</category>
</item>

<item>
<title>【Python】正则表达式进阶</title>
<link>http://dtysky.moe/article/Skill-2015_02_20_a</link>
<description>&lt;p&gt;很久之前写过一篇&lt;a href=&amp;quot;http://dtysky.github.io/articles/python-zheng-ze-biao-da-shi-ru-men.html&amp;quot;&gt;正则表达式入门&lt;/a&gt;，仅仅说明了正则的一些基础用法，实质上正则还有许多更加实用的方法，比如查找方法、结果格式化等等，能够帮助我们更加高效地完成字符串的处理，这也是多次尝试之后的经验之谈。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1.规则&lt;/h2&gt;
&lt;p&gt;除了之前提到的一些简单的规则之外，还有一些需要注意的、比较实用的正则规则：&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;1.{n}系：&lt;/strong&gt;&lt;br /&gt;
在正则规则后加上这个系列可以&lt;strong&gt;正好匹配/至少匹配/匹配范围&lt;/strong&gt;指定数量的字符，例子：  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;{n}（正好匹配）：&lt;/strong&gt;  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;re&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;match&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;r&amp;#39;ba{2}b&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;bab&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; 
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;re&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;match&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;r&amp;#39;ba{2}b&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;baab&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;_sre&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;SRE_Match&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;object&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;at&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x000000000276E8B8&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;{n,}（至少匹配）：&lt;/strong&gt;  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;re&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;match&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;r&amp;#39;ba{1,}b&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;baab&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;_sre&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;SRE_Match&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;object&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;at&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x000000000276E988&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;{n,m}（区间匹配）：&lt;/strong&gt;  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;re&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;match&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;r&amp;#39;ba{1,2}b&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;baaab&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; 
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;re&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;match&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;r&amp;#39;ba{1,2}b&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;baab&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;_sre&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;SRE_Match&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;object&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;at&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x000000000276E8B8&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;br&gt;&lt;br /&gt;
&lt;strong&gt;2.(?)系：&lt;/strong&gt;&lt;br /&gt;
这个系列是非捕获匹配，捕获的概念在以前的文章已经写过，不再多说，非捕获匹配的好处在于它在某些时候非常经济（不会消耗被匹配的字符串），同时还可以让编写规则变得简单，甚至在于某些比较拿什么的场合（比如sublime的高亮模板编写）的时候，他是非常有用的。同样通过例子来说明：  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;(?:)（正常非捕获匹配）：&lt;/strong&gt;  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;re&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;match&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;r&amp;#39;b(aa)b&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;baab&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;group&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;aa&amp;#39;&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;re&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;match&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;r&amp;#39;b(?:aa)b&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;baab&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;group&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;

&lt;span class=&amp;quot;n&amp;quot;&gt;Traceback&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;most&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;recent&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;call&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;last&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;):&lt;/span&gt;
  &lt;span class=&amp;quot;n&amp;quot;&gt;File&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;&amp;lt;pyshell#27&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;line&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;ow&amp;quot;&gt;in&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;module&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;group&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;ne&amp;quot;&gt;IndexError&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;no&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;such&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;group&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;(?=)（正向预测）：&lt;/strong&gt;  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;re&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;match&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;r&amp;#39;(b(?=abc))&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;babc&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;group&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;b&amp;#39;&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;re&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;match&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;r&amp;#39;(b(?=abc))&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;bbcd&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;group&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;

&lt;span class=&amp;quot;n&amp;quot;&gt;Traceback&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;most&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;recent&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;call&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;last&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;):&lt;/span&gt;
  &lt;span class=&amp;quot;n&amp;quot;&gt;File&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;&amp;lt;pyshell#33&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;line&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;ow&amp;quot;&gt;in&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;module&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;group&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;ne&amp;quot;&gt;AttributeError&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;NoneType&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;object&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;has&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;no&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;attribute&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;group&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;(?!)（反向预测）：&lt;/strong&gt;  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;re&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;match&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;r&amp;#39;(b(?!abc))&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;babc&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;group&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;

&lt;span class=&amp;quot;n&amp;quot;&gt;Traceback&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;most&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;recent&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;call&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;last&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;):&lt;/span&gt;
  &lt;span class=&amp;quot;n&amp;quot;&gt;File&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;&amp;lt;pyshell#35&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;line&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;ow&amp;quot;&gt;in&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;module&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;group&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;ne&amp;quot;&gt;AttributeError&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;NoneType&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;object&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;has&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;no&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;attribute&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;group&amp;#39;&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;re&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;match&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;r&amp;#39;(b(?!abc))&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;bbcd&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;group&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;b&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;br&gt;
&lt;strong&gt;3.[]系：&lt;/strong&gt;&lt;br /&gt;
[]系列用于范围字符筛选，例子没有必要举，我们只需要明白：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;[xyz]表示匹配xyz中的任何一个  &lt;/li&gt;
&lt;li&gt;[^xyz]表示匹配除了xyz的任何一个  &lt;/li&gt;
&lt;li&gt;[a-z]表示匹配字符a到字符z之间的任何一个  &lt;/li&gt;
&lt;li&gt;[^a-z]表示不匹配a-z之间的  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;2.方法：&lt;/h2&gt;
&lt;p&gt;Python的re库中提供了许多方法，除了之前提过的match还有search、findall等，并且即使是match方法也有一些比较方便的用法。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;1.search方法：&lt;/strong&gt;&lt;br /&gt;
此方法用于匹配一段字符串，有时候我们需要在目标字符串搜索一段字符串，或者找到这段字符串的位置，这时候便可以使用search方法，比如以下的例子：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;re&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;search&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;r&amp;#39;ac&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;qqqacxxxff&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;start&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;end&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
&lt;span class=&amp;quot;mi&amp;quot;&gt;5&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;在&lt;strong&gt;qqqacxxxacffac&lt;/strong&gt;查找&lt;strong&gt;ac&lt;/strong&gt;的位置，找到这段字符串的首位置为3，结束位置为5。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;2.findall方法：&lt;/strong&gt;&lt;br /&gt;
此方法会匹配一个目标字符串中的重复字符串，返回一个数组：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;re&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;findall&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;r&amp;#39;\d+&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;qqq12xxx45ff90&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;12&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;45&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;90&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;br&gt;
&lt;strong&gt;3.match方法：&lt;/strong&gt;&lt;br /&gt;
此方法可以活用，当然这种用法也可以用在search等，例子：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;re&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;match&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;r&amp;#39;\S+(?P&amp;lt;d1st&amp;gt;\d+)\S+(?P&amp;lt;d2nd&amp;gt;\d+)\S+(?P&amp;lt;d3rd&amp;gt;\d+)&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;qqq12xxx45ff90&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;groupdict&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;d1st&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;2&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;d2nd&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;5&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;d3rd&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;0&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;返回的是一个字典。
&lt;br&gt;
此次教程结束www&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Friday, 20 Feb 2015 11:11:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.02.20 11:11:article/Skill-2015_02_20_a</guid>
<category>Python</category>
<category>正则</category>
<category>格式化</category>
</item>

<item>
<title>【Html】Pelican搭建博客进阶-模板和插件</title>
<link>http://dtysky.moe/article/Skill-2015_02_19_a</link>
<description>&lt;p&gt;关于使用Pelican来搭建博客的心得在八个月前就已经有两篇文章加以了说明——那是在我初次搭建这个博客的时候，那时的技术比较青涩嘛难免有纰漏和晦涩之处，但大多内容还是可以看看的：&lt;br /&gt;
&lt;a href=&amp;quot;http://dtysky.github.io/articles/html-pelicanzhu-ti-de-zhi-zuo-zhi-mo-ban.html&amp;quot;&gt;【Html】pelican主题的制作之模板&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;http://dtysky.github.io/articles/python-pelicangitjian-li-bo-ke.html&amp;quot;&gt;【Python】pelican+git建立博客&lt;/a&gt;&lt;br /&gt;
这次我将会对其中的一些内容加以补充和修正，主要是说明模板的构造、变量使用、评论、feed、代码高亮等内容。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1.模板&lt;/h2&gt;
&lt;p&gt;Plican是基于Jinjia2的，而且被大幅简化过，用起来十分舒心，这次需要补充的内容如下。
&lt;br&gt;
&lt;strong&gt;1.变量：&lt;/strong&gt;&lt;br /&gt;
Pelican的变量是在配置文件&lt;strong&gt;pelicanconf.py&lt;/strong&gt;中进行，除了默认的SITENAME之类的变量之外我们也可以自定义变量，比如我可以自定义一个存放图像的文件夹的路径：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;THEMEURL&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;SITEURL&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;/theme&amp;#39;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;IMGURL&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;THEMEURL&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;/image&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这样一来我就可以在模板中自由使用IMGURL这个变量了。&lt;br /&gt;
在模板中使用变量只需要以以下形式即可：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;{{ IMGURL }}
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;br&gt;
&lt;strong&gt;2.主题：&lt;/strong&gt;&lt;br /&gt;
模板主题是由一套HTML模板和CSS样式、JS脚本构成的，对于Pelican而言，和一般静态网页的区别仅仅是HTML文件而已。&lt;br /&gt;
相对于通常的HTML文件，Pelican是将其作为模板进行使用，当然你也可以保存成XML等等格式，无伤大雅，具体的在上面提到的帖子中已经加以了说明。&lt;br /&gt;
这里需要补充的是一些东西的用法。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;继承&lt;/strong&gt;： 我们可以新建一个名为&amp;quot;Base.html&amp;quot;的文件来作为所有模板的基础，在里面将整个网站的基础样式做好高，之后用以下形式的语句定义一些待定义的“块”：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;{% block title %}{% endblock %}
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;定义结束后，便可以在其他的模板中引用这个文件，通过修改这些待定义的“块”的内容来构成新的模板，非常省事省力：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;{% extends &amp;quot;base.html&amp;quot; %}
{% block title %}{{SITENAME}}{% endblock %}
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这代码的意思是此模板继承自base.html，并且将title块的内容替换成SITENAME变量。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;变量&lt;/strong&gt;： 除了上面提到的变量基本使用方法，这里还需要说明，在&lt;strong&gt;{{ }}&lt;/strong&gt;内的语句实际上就是Python语句，比如我们要处理一个bug的时候：&lt;br /&gt;
在默认情况下,.md文件内summary后的内容转换后会被加上&lt;strong&gt;&lt;xmp&gt;&lt;p&gt;&lt;/p&gt;&lt;/xmp&gt;&lt;/strong&gt;标签对，而我们想要去掉这个标签对，就可以使用以下语句：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;%&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;block&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;metadesc&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;%&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}{{&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;article&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;summary&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;replace&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;&amp;lt;p&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;replace&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;&amp;lt;/p&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;}}{&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;%&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;endblock&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;%&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;br&gt;
&lt;strong&gt;逻辑语句&lt;/strong&gt;： 除了变量外，我们也可以在其中使用for循环、if判断这种逻辑语句，只需要以如下的形式：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;%&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;block&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;metakey&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;%&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}{&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;%&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;article&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tags&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;%&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}{&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;%&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;tag&lt;/span&gt; &lt;span class=&amp;quot;ow&amp;quot;&gt;in&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;article&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tags&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;%&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}{{&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;tag&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;e&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;}},{&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;%&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;endfor&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;%&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}{&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;%&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;endif&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;%&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}{&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;%&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;endblock&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;%&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;不看最外部的block，里面的内容表示：如果文章有tags，则遍历tags内的所有tag，将这些tag打印到block内。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2.扩展功能&lt;/h2&gt;
&lt;p&gt;一个博客除了显示文章之外还是需要完善其他的功能的，麻雀虽小五脏俱全才是我们的追求，说起这些功能，尤其是对于码农，往往会有如下需求：&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;1.评论：&lt;/strong&gt;&lt;br /&gt;
评论功能是应当有的，文人墨客在博客上互相探讨先进知识，岂不是一件快意的事情，但为了博客自己搭个后端搞个数据库又比较费力，如果有这个闲情逸致当然是好的，但对于更多人还是先将我们的时间应当运用在刀刃上，那么我们就不得不借助第三方的评论系统了，我选择的，也是Pelican官方推荐的是——Disqus。&lt;br /&gt;
Disqus非常老牌，对于我们而言除了国内这WTF的网络环境导致的偶尔抽风之外非常不错，所以我们完全可以将评论系统放心地委托给他。&lt;br /&gt;
首先，我们要在这里注册一个账号：  &lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://disqus.com/&amp;quot;&gt;https://disqus.com/&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;注册后再次打开以上页面，打开后我们点击&lt;strong&gt;Add Disqus to your site&lt;/strong&gt;，之后弹出如下页面：&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/0_zps9f605422.png&amp;quot; /&gt;&lt;br /&gt;
&lt;strong&gt;site name&lt;/strong&gt;是你的blog地址，&lt;strong&gt;Choose your unique Disqus URL&lt;/strong&gt;下填写你的&lt;strong&gt;shortname&lt;/strong&gt;。&lt;br /&gt;
比如我填写的就是：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;sitename = dtysky.github.io&lt;br /&gt;
shortname = dtysky  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;之后等待一段时间便可以使用了（一般是48小时之内），不过我们先不管他，直接进行模板中评论块的添加：  &lt;/p&gt;
&lt;p&gt;在需要评论的模板（一般是文章模板）的任意位置中添加以下代码：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;id&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;disqus_thread&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;div&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;然后在js代码中添加：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;cm&amp;quot;&gt;/* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */&lt;/span&gt;  
&lt;span class=&amp;quot;kd&amp;quot;&gt;var&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;disqus_shortname&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;your shortname&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;// required: replace example with your forum shortname (you need to register for free)  &lt;/span&gt;
&lt;span class=&amp;quot;c1&amp;quot;&gt;// The following are highly recommended additional parameters. Remove the slashes in front to use.  &lt;/span&gt;
 &lt;span class=&amp;quot;kd&amp;quot;&gt;var&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;disqus_developer&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;  
&lt;span class=&amp;quot;c1&amp;quot;&gt;// var disqus_identifier = &amp;#39;unique_dynamic_id_1234&amp;#39;;  &lt;/span&gt;
&lt;span class=&amp;quot;c1&amp;quot;&gt;// var disqus_url = &amp;#39;http://example.com/permalink-to-page.html&amp;#39;;  &lt;/span&gt;
&lt;span class=&amp;quot;c1&amp;quot;&gt;//var disqus_config = function () { this.language = &amp;quot;zh&amp;quot;; }; // this will set language to italy for the request, and default is en  &lt;/span&gt;
&lt;span class=&amp;quot;cm&amp;quot;&gt;/* * * DON&amp;#39;T EDIT BELOW THIS LINE * * */&lt;/span&gt;  
&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;function&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;  
    &lt;span class=&amp;quot;kd&amp;quot;&gt;var&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;dsq&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;document&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;createElement&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;script&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt; 
    &lt;span class=&amp;quot;nx&amp;quot;&gt;dsq&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;text/javascript&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;dsq&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;async&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;  
    &lt;span class=&amp;quot;nx&amp;quot;&gt;dsq&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;src&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;http://&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;disqus_shortname&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;.disqus.com/embed.js&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;  
    &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;document&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getElementsByTagName&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;head&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;||&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;document&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getElementsByTagName&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;body&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;appendChild&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;dsq&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;  
&lt;span class=&amp;quot;p&amp;quot;&gt;})();&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;便搞定了。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;2.分享：&lt;/strong&gt;&lt;br /&gt;
好不容易写了一篇文章的你是否甘心就被如此埋没？当然有人会甘心的但更多的还是不甘心的人，虽然加了分享功能不一定会让你的文章被更多传阅，但显然它也增加了某种可能性，但自己完成一个全主流网站分享的功能则又会消耗许多时间，对于咱这种非WEB码农的人显然更想要一个简单的方式，所谓互联网时代的有求必有应规律这就生效了——我选择了Bshare：  &lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;http://www.bshare.cn/&amp;quot;&gt;http://www.bshare.cn/&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;Bshare是一个专业分享网站，可以定制你需要的分享网站和形式，之后只需要简短的代码便可以完成分享，整体流程网站上说的很清楚也很容易找到，我就不再赘述了。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;3.Feed：&lt;/strong&gt;&lt;br /&gt;
你是否希望你的博客被人订阅？反正我是希望的www于是我们就来研究一下这个东西。&lt;br /&gt;
Pelican原生支持Feed，同时支持&lt;strong&gt;Atom&lt;/strong&gt;和&lt;strong&gt;RSS&lt;/strong&gt;两种形式，我们只需要在&lt;strong&gt;pelicanconf.py&lt;/strong&gt;定义以下内容即可：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;# Feed generation is usually not desired when developing&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;FEED_ALL_ATOM&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;feeds/all.atom.xml&amp;#39;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;CATEGORY_FEED_ATOM&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;feeds/&lt;/span&gt;&lt;span class=&amp;quot;si&amp;quot;&gt;%s&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;.atom.xml&amp;#39;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;FEED_ALL_RSS&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;feeds/all.rss.xml&amp;#39;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;CATEGORY_FEED_RSS&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;feeds/&lt;/span&gt;&lt;span class=&amp;quot;si&amp;quot;&gt;%s&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;.rss.xml&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这样一来，feed文件便会存在&lt;strong&gt;feeds&lt;/strong&gt;文件夹下，主页和不同分类、作者皆会建立各自的Feed源。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;4.Sitemap：&lt;/strong&gt;&lt;br /&gt;
这东西是给搜索引擎看的，不在赘述，详情请看SEO相关的内容。&lt;br /&gt;
用法也很简单，在：  &lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://github.com/getpelican/pelican-plugins/tree/master/sitemap&amp;quot;&gt;https://github.com/getpelican/pelican-plugins/tree/master/sitemap&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;下上插件，将他扔到PLUGIN文件夹内，在&lt;strong&gt;pelicanconf.py&lt;/strong&gt;内写上：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;PLUGINS&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;sitemap&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,]&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;SITEMAP&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;format&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;xml&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;priorities&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;articles&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;0.5&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
        &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;indexes&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;0.5&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
        &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;pages&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;0.5&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;},&lt;/span&gt;
    &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;changefreqs&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;articles&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;monthly&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
        &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;indexes&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;daily&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
        &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;pages&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;monthly&amp;#39;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;搞定，之后sitemap.xml文件会生成到博客目录下。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;5.代码高亮：&lt;/strong&gt;&lt;br /&gt;
这个对于码农而言的重要性是毋庸置疑的，Pelican原生支持&lt;a href=&amp;quot;http://pygments.org&amp;quot;&gt;Pygments&lt;/a&gt;这个为python编写的代码高亮模块，大家可以去了解一下。&lt;br /&gt;
要使用&lt;strong&gt;Pygments&lt;/strong&gt;首先要先安装它，最简单的方法是pip安装大法，最新版本的python已经集成了pip，具体的使用方法就不赘述了。&lt;br /&gt;
安装完毕后我们就可以使用他了，对于markdown用户而言，只需要在.md文件内需要编写代码的区域这么写：&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/1_zps3b478eef.png&amp;quot; /&gt;
第一行是声明，声明下面的代码是什么语言，如果不声明pygments会猜测。&lt;br /&gt;
所有代码行前必须加上制表符或者几个空格，这个和md的语法规则是一致的。&lt;br /&gt;
当然，到这一步还没有结束，我们仅仅是能够保证代码被解析成pygments规则下的代码块，还需要CSS样式。&lt;br /&gt;
首先，在这里预览你需要的主题：&lt;br /&gt;
&lt;a href=&amp;quot;http://pygments.org/demo/&amp;quot;&gt;http://pygments.org/demo/&lt;/a&gt;&lt;br /&gt;
选中主题后，打开cmd（我是win用户），输入以下代码（前提是先弄好环境变量）：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;pygmentize -f html -a .highlight -S vim &amp;gt; A:\pygments.css
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;其中，&lt;strong&gt;vim&lt;/strong&gt;表示我生成的主题名字是vim，&lt;strong&gt;A:\pygments.css&lt;/strong&gt;表示生成主题文件的路径和名字，其他不要改。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;6.Tag cloud:&lt;/strong&gt;&lt;br /&gt;
所谓的标签云，原理上就是统计不同tag出现的次数，然后根据此处来对次tag加个权重，我们首先在加上这两句代码：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;TAG_CLOUD_STEPS&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;TAG_CLOUD_MAX_ITEMS&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;200&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;他们分别定义了标签云的阶数和最大数量，4阶就代表所有的标签将会按照次数被平均分为四个区域。&lt;br /&gt;
接下来在模板中你想要显示标签的位置（一般是tags模板）加入以下代码：   &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;ul&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;class&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;tag_cloud&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
{% for tag in tag_cloud %}
    &lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;a&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;class&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;tag-{{ tag.1 }}&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;href&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;/tag/{{ tag.0 }}/&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;{{ tag.0 }}&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
{% endfor %}
&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;ul&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;之后在你的CSS里定义tag-0、tag-1这些class的样式即可。   &lt;/p&gt;
&lt;p&gt;之后就是静态网站的常识性问题了（连接css到html），然后就搞定，enjoy it! &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Thursday, 19 Feb 2015 14:20:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.02.19 14:20:article/Skill-2015_02_19_a</guid>
<category>Pelican</category>
<category>Html</category>
<category>Python</category>
<category>Feed</category>
<category>Disqus</category>
<category>Pygments</category>
<category>Share</category>
</item>

<item>
<title>【PCB】Cadence入门之原理图输入-原理图绘制</title>
<link>http://dtysky.moe/article/Skill-2015_02_18_a</link>
<description>&lt;p&gt;&lt;strong&gt;可以参考本人体三维显示器的PCB工程来看这个系列：&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/Led_Array&amp;quot;&gt;https://github.com/dtysky/Led_Array&lt;/a&gt;&lt;br /&gt;
&lt;br&gt;
器件原理图建立完毕后便可以进行PCB原理图的绘制了，所谓原理图，就是将电路中信号的走向说明，从形式上来看就是将IC的各个引脚进行连接，对于原理图，只需要保证连接的正确性即可。 &lt;br /&gt;
当然，这并不是说我们就可以将这些器件随意摆放了，根据器件不同的功能对原理图分页是非常必要的，无论是对于自己未来的review还是对于用户而言，拥有一个良好结构的原理图是会被用户所感谢的，也可以避免被未来的自己破口大骂。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1.原理图建立&lt;/h2&gt;
&lt;p&gt;&lt;br&gt;
&lt;strong&gt;1.新建页:&lt;/strong&gt;&lt;br /&gt;
在第一个教程新建工程的基础上，我们点开&lt;strong&gt;.\xxx.dsn&lt;/strong&gt;会看到两个文件夹，右键&lt;strong&gt;SCHEMATIC1&lt;/strong&gt;便可以在弹出菜单中看到&lt;strong&gt;New Page&lt;/strong&gt;选项，这就是我们新建原理图所需要的选项了，点击它，给你的新原理图进行命名，请起一个有意义的名字。&lt;br /&gt;
有了第一个接下来的也就很简单了，可以根据自己的需求对整个设计进行划分，新建不同的分页，然后起上不同的名字，比如我这个工程就有这些：&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/0_zpsc2040cc3.png&amp;quot; /&gt;&lt;br /&gt;
每一个分页都拥有着自己的功能，这便于在之后的修改中简化查询过程。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;2.导入库:&lt;/strong&gt;&lt;br /&gt;
原理图新建完毕后我们便可以双击任何一张进入该原理图的绘制了，原理图绘制界面和器件原理图基本一致，也是绘制区域+侧边栏的形式。&lt;br /&gt;
可以看到右边侧边栏的右上角有一个右下方带有绿色衬底白色加号的IC标志按钮，
点击它便可以进入器件选择页面：&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/1_zps763b95cd.png&amp;quot; /&gt;&lt;br /&gt;
&lt;strong&gt;Part&lt;/strong&gt;表示器件筛选规则，&lt;strong&gt;Part List&lt;/strong&gt;则是可选择的器件列表，&lt;strong&gt;Libraries&lt;/strong&gt;表示已选择的器件库。&lt;br /&gt;
当然，在选择器件前我们需要有一个器件列表，而器件列表来自于器件库，所以在绘制之前我们需要先将库添加进来，点击:&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/2_zpsd6650caa.png&amp;quot; /&gt;&lt;br /&gt;
左边的那个按钮便可以打开选择窗口，选择你需要的库，导入即可。&lt;br /&gt;
当然很多时候我们并不知道需要的器件在什么库内（比如需要用到一些Cadence自带库内的器件），这时候怎么办呢？&lt;br /&gt;
OrCad非常人性化，这里：&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/3_zps992624ee.png&amp;quot; /&gt;&lt;br /&gt;
点击&lt;strong&gt;Search for Part&lt;/strong&gt;按钮，展开器件查找界面，输入你要查找的器件，比如我输入&lt;strong&gt;C232&lt;/strong&gt;然后点击右边的按钮:&lt;br /&gt;
器件所在的器件库便被找了出来，添加即可ww：&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/4_zps90aa2574.png&amp;quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2.器件放置&lt;/h2&gt;
&lt;p&gt;库导入后便可以开始原理图的绘制了。
&lt;br&gt;
&lt;strong&gt;1.放置器件：&lt;/strong&gt;&lt;br /&gt;
首先要进行器件的放置，器件可以分为无源、有源、电源等，还有一类特殊的器件叫做“接口”（一般用于连接各个分页之间的器件）。&lt;br /&gt;
器件的放置十分简单，双击需要放置的器件（在Part List内选择），然后在原理图绘制区域的任意部位添加即可，要注意到添加器件是连续的，如果不取消或者选择其他器件的话，当前被选器件是一直有效的。
&lt;br&gt;&lt;br /&gt;
&lt;strong&gt;2.放置电源：&lt;/strong&gt;&lt;br /&gt;
器件的放置十分简单，只需要注意放置的位置便可，请按照实际的需求怎么方便怎么来。但有一类特殊的器件叫做电源。&lt;br /&gt;
所谓电源并不是说你选型的电源IC或者用三极管搭的电路，而是一个抽象的器件，用于辅助电源线路的建立，它们的目的是将所有相同的电源网络集合到一起，在明示电源网络的同时，为以后的Layout做好准备，电源的运用同时也与仿真有些关系，不过我没试过。&lt;br /&gt;
电源可以点击右边侧栏的&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/5_zpsa361bd16.png&amp;quot; /&gt;进行选择，按照需求选择即可，摆放和一般器件没有区别。&lt;br /&gt;
不过需要注意，与一般器件不同，可以有多个同样标识的电源出现在同一个原理图内，与它们相连的引脚都处于同一电源网络内：&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/6_zps71068a66.png&amp;quot; /&gt;&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;3.放置接口：&lt;/strong&gt;&lt;br /&gt;
接口是和电源不同的一类特殊器件，并不会出现在Layout的器件列表中，仅仅作为各个分页的信号连接器。&lt;br /&gt;
接口可以点击&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/5_zpsf3b41c4c.png&amp;quot; /&gt;进行添加，样式就按照喜好的来吧，不过最好还是与实际情况相符。&lt;br /&gt;
在两个不同的分页中添加同名的接口，便可以实现不同分页的信号交互：&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/8_zps93626114.png&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/9_zps005f346b.png&amp;quot; /&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3.线路连接&lt;/h2&gt;
&lt;p&gt;器件放置完毕后便可以进行线路的连接了，其实和画电路图是一样的。 &lt;br /&gt;
&lt;strong&gt;在此说明，将鼠标指针悬停到按钮图标上便可以看到该按钮的说明，所以以下我不再截图了，直接打说明。&lt;/strong&gt;&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;1.单线连接：&lt;/strong&gt;&lt;br /&gt;
最最最基础的连接方式就是单线连接，两个引脚一条线，表示着两个引脚被连接到了一起。&lt;br /&gt;
为了实现这种连接，我们只需要点击侧边栏中的&amp;quot;Place wire&amp;quot;按钮（或快捷键w），之后连接两个引脚即可。&lt;br /&gt;
到此还没有结束，我们必须要个连接线定义一个名字，点击&amp;quot;Place Net Alias&amp;quot;（或快捷键N），在弹出窗口写好角度和名字后点OK，然后将指针移动到线上点击左键即可。&lt;br /&gt;
和器件放置一样，这种命名也会自动增量迭代，名称标号不断+1。 &lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;2.总线连接：&lt;/strong&gt;&lt;br /&gt;
总线的概念不在赘述，为了实现总线连接，我们必须要建立总线。&lt;br /&gt;
点击&amp;quot;Place bus&amp;quot;（或者快捷键B)放置总线，之后将总线链接到器件即可，连接方式有两种：&lt;br /&gt;
第一种是与接口的连接，将接口建立为总线模式，直接和总线连接即可，如下图：&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/10_zps70c42a0a.png&amp;quot; /&gt;&lt;br /&gt;
总线和接口的名字不必一致，当为了规范还是建议一致，当然最后还是要根据实际情况考虑。&lt;br /&gt;
第二种是和一般引脚的连接，这就需要另两个按钮——&amp;quot;Place bus entry&amp;quot;（快捷键E）和&amp;quot;Auto connect to bus&amp;quot;了。&lt;br /&gt;
第一个按钮是单个连接到总线，第二个是批量连接到总线，对于第二个按钮，我们需要先命名再连接，第一个则是先连接再命名。&lt;br /&gt;
无论是哪一种方式，总线分支的线名和总线的名字都必须一致，全部连接后的情况如下：  &lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/11_zpsce51f256.png&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;全部完成后，原理图的绘制便结束了，为了将原理图和PCB联系起来，我们还需要生成网表，在此之前还有一些工作需要完成，这将在下一次进行说明。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Wednesday, 18 Feb 2015 14:12:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.02.18 14:12:article/Skill-2015_02_18_a</guid>
<category>PCB</category>
<category>Cadence</category>
<category>OrCad</category>
<category>原理图</category>
</item>

<item>
<title>【PCB】Cadence入门之原理图输入-网表生成</title>
<link>http://dtysky.moe/article/Skill-2015_02_18_b</link>
<description>&lt;p&gt;&lt;strong&gt;可以参考本人体三维显示器的PCB工程来看这个系列：&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/Led_Array&amp;quot;&gt;https://github.com/dtysky/Led_Array&lt;/a&gt;&lt;br /&gt;
&lt;br&gt;
原理图与PCB的中间层过度就是网表了，我们把OrCad的器件和连接信息通过网表传递给Allegro才可以进行Layout，生成网表虽然简单，但前置工作还是有一些的，比如重排、DRC、修正等等。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1.检查&lt;/h2&gt;
&lt;p&gt;器件放置连接完毕后便要生成网表以备Layout使用了，在生成网表前还有一些东西需要说明。
&lt;br&gt;&lt;br /&gt;
&lt;strong&gt;1.重排：&lt;/strong&gt;&lt;br /&gt;
重排，便是对已经布置好的器件进行再一次地规范，这会对已经布置好的器件名称等等进行修改。&lt;br /&gt;
返回工程主界面，选中&lt;strong&gt;.\xxx.dsn&lt;/strong&gt;，点击上面标题栏的&lt;strong&gt;Tools&lt;/strong&gt;，下拉菜单中选择&lt;strong&gt;Annotate&lt;/strong&gt;，全部保持默认即可，具体选项的意思可以自己研究，无非就是作用域、作用形式之类的。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;2.器件属性修改：&lt;/strong&gt;&lt;br /&gt;
这一步的作用是对器件的属性进行完善，所谓器件属性，就是名字、值、标识等等，对其进行修改不仅仅关系到原理图的DRC等等，还关系到Layout时的器件岁对应的种种状况。&lt;br /&gt;
右键器件或者&lt;strong&gt;.\xxx.dsn&lt;/strong&gt;文件夹，可以在弹出菜单中选择&lt;strong&gt;Edit Object Properties&lt;/strong&gt;，如此便可以进入属性编辑界面：&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/0_zps98d91165.png&amp;quot; /&gt;
如果前面一帆风顺，那么此处需要修改的属性也就不多了，仅仅是&lt;strong&gt;Value&lt;/strong&gt;和&lt;strong&gt;PCB Footprint&lt;/strong&gt;，便已足够，其中Value定义了器件的值，如果器件是电阻电容等等便可以定义为0.1uf等等，而PCB Footprint则定义了器件的Layout封装，比如smd_0603、DGG56等等，这些封装是可以从厂商处获得，也可以自己建立，如何建立器件的Layout封装将会在之后的文章中说到。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;3.DRC检查：&lt;/strong&gt;&lt;br /&gt;
最后一步就是DRC检查了，如果这一步完美通过，之后的修改也就没有必要了，当然智者千虑必有一失，也故某些东西还是不能省的。&lt;br /&gt;
所谓DRC，即为&lt;strong&gt;Design Rules Check&lt;/strong&gt;，设计规则检查，也就是检查原理图设计中是否存在错误和漏洞。&lt;br /&gt;
选中&lt;strong&gt;.\xxx.dsn&lt;/strong&gt;后点击标题栏的&lt;strong&gt;Tools&lt;/strong&gt;，下拉菜单中选择&lt;strong&gt;Design Rules Check&lt;/strong&gt;即可打开界面：&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/1_zps78234a20.png&amp;quot; /&gt;&lt;br /&gt;
其中：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Sope：作用域，全部设计还是当前选择的。&lt;/li&gt;
&lt;li&gt;Mode：&lt;/li&gt;
&lt;li&gt;Action：左边——检查设计规则，删除DRC标注，右边——为警告创建标注，保留已经豁免的DRC。&lt;/li&gt;
&lt;li&gt;Design Rules：选择电气规则检查或者物理规则检查。&lt;/li&gt;
&lt;li&gt;其他窗口是一些规则设置，这个可以自己仔细研究，一般而言默认即可。  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;执行完DRC之后，如果没有错误是最好的，如果有的话，就必须要进行修改了。&lt;br /&gt;
DRC错误的表现形式是一个个绿色的小点，双击后便会出现错误的信息，你可以选择直接删除该点，但删除了之后错误依然在根本没有解决问题所以不建议这么做，最好的方法还是修改完毕后再次进行DRC检查。&lt;br /&gt;
一般而言DRC错误都是由于端口类型不匹配，比如in接in端口，总线不匹配，比如线路命名错误。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2.修改和更新&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1.修改：&lt;/strong&gt;&lt;br /&gt;
原理图修改分为两种，即原理图修改和器件修改。&lt;br /&gt;
对于原理图修改，只需要修改完布线和器件等之后再一次进行重排和DRC检查即可，而如果器件本身出错，则会麻烦一些。&lt;br /&gt;
如果器件的原理图出现了错误，我们首先要对器件本身进行修改，双击器件的原理图进行编辑，修改完毕后保存，之后的一步非常重要。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;2.更新：&lt;/strong&gt;&lt;br /&gt;
对器件原理图进行修改之后，我们必须要进行更新操作，否则原本的原理图是不会进行自动更改的，更改方式如下：&lt;br /&gt;
打开&lt;strong&gt;.\xxx.dsn&lt;/strong&gt;文件夹下的&lt;strong&gt;Design cache&lt;/strong&gt;文件夹，右键已经修改完毕的、需要更新的器件，选择&lt;strong&gt;Update cache&lt;/strong&gt;即可。&lt;br /&gt;
如果并不是对器件进行直接的修改，而是要将器件完全替换成另一个器件，或者进行库的更换，那么则需要选择&lt;strong&gt;Replace cache&lt;/strong&gt;，之后按照窗口内的指示进行操作即可。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3.网表生成以及器件BOM导出&lt;/h2&gt;
&lt;p&gt;在若干次的修改后我们终于达到了DRC完全无误的境界，加之属性的完全，我们便可以开始网表的导出了，不但如此，我们还可以生成BOOM来方便采购人员。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;1.网表生成：&lt;/strong&gt;&lt;br /&gt;
回到项目界面，选中&lt;strong&gt;.\xxx.dsn&lt;/strong&gt;，标题栏&lt;strong&gt;Tools&lt;/strong&gt;-&amp;gt;&lt;strong&gt;Creat Netlist&lt;/strong&gt;，打开创建网表界面：&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/2_zps974c5313.png&amp;quot; /&gt;&lt;br /&gt;
此界面下，上面的那几个界面切换按钮是为了对应不同的网表利用工具，对于仅仅为了绘制PCB的我们而言，&lt;strong&gt;PCB Editor&lt;/strong&gt;是需要的，由于只是入门，所以在这一步我们仅仅需要关心那个路径选项，也就是网表文件的保存位置，默认为原理图目录下，为了结构的清晰，我建议将NETLIST放在一个单独的文件夹内，以下是我的目录结构：&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/3_zpse848ab46.png&amp;quot; /&gt;&lt;br /&gt;
点击确认，如果之前都按照标准来的话应该就会创建结束了。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;2.BOM导出：&lt;/strong&gt;&lt;br /&gt;
&lt;strong&gt;BOM&lt;/strong&gt;即为&lt;strong&gt;Bill of Material&lt;/strong&gt;（材料清单），是整个设计中需要用到的实际器件的集合，导出BOM，对于采购人员是十分必要的。&lt;br /&gt;
OrCad导出BOM很简单，仍然是在&lt;strong&gt;Tools&lt;/strong&gt;的下拉菜单中，我们选择&lt;strong&gt;Bill of Material&lt;/strong&gt;即可打开该菜单，有些选项想必大家已经很熟悉了就不再赘述，值得一提的是&lt;strong&gt;Line Item Definition&lt;/strong&gt;这一栏，&lt;strong&gt;Header&lt;/strong&gt;表示生成的电子表和的头栏，而剩下一个则是每一栏对应的值的格式化，然后就是最后一栏的保存路径选择，其他的也没有什么特别好主意的。&lt;br /&gt;
点击OK，生成成功，生成的文件一般是&lt;strong&gt;.BOM&lt;/strong&gt;文件，用Excel打开即可。&lt;br /&gt;
&lt;br&gt;
这样一来，原理图的输入便完全结束了。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Wednesday, 18 Feb 2015 14:12:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.02.18 14:12:article/Skill-2015_02_18_b</guid>
<category>PCB</category>
<category>Cadence</category>
<category>OrCad</category>
<category>原理图</category>
<category>网表</category>
<category>BOM</category>
</item>

<item>
<title>【PCB】Cadence入门之原理图输入-器件</title>
<link>http://dtysky.moe/article/Skill-2015_02_17_a</link>
<description>&lt;p&gt;&lt;strong&gt;可以参考本人体三维显示器的PCB工程来看这个系列：&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/Led_Array&amp;quot;&gt;https://github.com/dtysky/Led_Array&lt;/a&gt;&lt;br /&gt;
&lt;br&gt;
PCB即为电路板，将铜线和铜片埋在在塑料板中达到连接各种器件的目的，简单PCB的设计是硬件工程师应当掌握的技能，尤其是自称为技术宅的少年们，至少对其应当有一定的了解。&lt;br /&gt;
本次教程所用的软件为Cadence套件中的OrCad，Cadence套件非常有名，它的约束和标准能够让新手少走很多弯路，当然其价格...嗯，作为一个学生学习所以暂时就，你懂的，可以用它来学习，自己标准之后再转战PowerPCB等也不错。&lt;br /&gt;
PCB的设计流程基本可以认为是：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;需求分析&lt;/li&gt;
&lt;li&gt;选型&lt;/li&gt;
&lt;li&gt;原理图输入&lt;/li&gt;
&lt;li&gt;布局布线&lt;/li&gt;
&lt;li&gt;Gerber文件输出&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;需求分析以及选型属于具体例子具体分析的，难以抽象集中，所以略过，这次就从原理图输入说起，由于内容较多，这次只说器件原理图的建立。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1.库建立&lt;/h2&gt;
&lt;p&gt;选型结束后便可以开始原理图的输入了，对于第一次打开OrCad界面的新手而言，其实也没什么说的，因为其界面一目了然非常亲民，所以我们便可以直接开始。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;1.建立工程:&lt;/strong&gt;&lt;br /&gt;
首先点击左上角的&lt;strong&gt;File&lt;/strong&gt;，下拉菜单中可以看到&lt;strong&gt;new&lt;/strong&gt;选项，在这个选项下选择&lt;strong&gt;project&lt;/strong&gt;便会弹出一个对话框，我们使用默认选项——schematic（原理图），并输入工程路径，OK后便新建了一个工程:&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/0_zps9ecf90d1.png&amp;quot; /&gt;&lt;br /&gt;
可以看到，新工程下主要有三个文件夹，我们先不管后两个，就看&lt;strong&gt;Design Resources&lt;/strong&gt;里面的文件——&lt;strong&gt;.\xxx.dsn&lt;/strong&gt;（xxx为工程名）和&lt;strong&gt;Library&lt;/strong&gt;文件夹，其中的&lt;strong&gt;Library&lt;/strong&gt;文件夹就是包含本节所要介绍的“库”的文件夹了。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;2.库的添加:&lt;/strong&gt;&lt;br /&gt;
如果已经有了现成的库文件（.olb文件），直接右键&lt;strong&gt;Library&lt;/strong&gt;文件夹，接着选择&lt;strong&gt;add&lt;/strong&gt;再选择olb文件便可完成添加。&lt;br /&gt;
当然，更多的时候我们会发现并没有库文件或者厂家仅仅提供了器件的原理图，亦或是我们想把许多库合并到一起，再或者是厂家提供的原理图太烂不想使用，这个时候就要求我们新建一个库来存放自己所需要的器件原理图。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;3.库的建立:&lt;/strong&gt;&lt;br /&gt;
与第一步一样，新建一个库也需要在&lt;strong&gt;File&lt;/strong&gt;-&amp;gt;&lt;strong&gt;new&lt;/strong&gt;中选择，不过这次选择的是&lt;strong&gt;Library&lt;/strong&gt;，于是我们就有了一个新的Library，它就存在于我们的Library文件夹下面，默认为&lt;strong&gt;library1.olb&lt;/strong&gt;，于是，我们就可以对它进行各种各样的操作了。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;4.将已有的器件原理图合并到新建库中：&lt;/strong&gt;&lt;br /&gt;
由于器件原理图建立内容比较多，独立成章，先说一下这个吧。&lt;br /&gt;
如果我们已经有个一个器件的原理图，那么它一定是存在于某个库之下的，对于非常人性化的OrCad，我们只需要在选中那个原理图使用&amp;quot;ctrl+c&amp;quot;进行复制，然后选中需要合并到的目标库，&amp;quot;ctrl+v&amp;quot;进行粘贴即可，非常顺滑，毫无压力。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2.器件原理图建立&lt;/h2&gt;
&lt;p&gt;器件的原理图来源多种多样，如果你选择的器件是TI这种良心厂商的产品，那么恭喜，大多数器件都可以在自己的介绍页面找到&lt;strong&gt;.bxl&lt;/strong&gt;文件下载，之后配合TI提供的免费版软件&lt;strong&gt;Ultra Librarian&lt;/strong&gt;便可以顺滑地生成器件的原理图以及用于Layout的封装。&lt;br /&gt;
但有时为了成本等等考虑，我们不得不去选用一些WTF的厂商的产品，这时候，器件的原理图就要自己来搞了。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;1.粗暴型：&lt;/strong&gt;&lt;br /&gt;
所谓粗暴型，就是图形化简历，我们只需要右键.olb文件，在弹出菜单中选择&lt;strong&gt;New Part&lt;/strong&gt;即可进行这种建立，这种建立之所以粗暴，在于它的低效和傻瓜。&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/1_zpsa8837f79.png&amp;quot; /&gt; &lt;br /&gt;
首先我们会看到这个界面，这里将会设定新建器件的一些参数。其中需要说明的是：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Part Reference Pefix：器件标识，一般而言IC之类的用&amp;quot;U&amp;quot;，电容是&amp;quot;C&amp;quot;，二极管是&amp;quot;D&amp;quot;，等等。&lt;/li&gt;
&lt;li&gt;PCB Footprint：该器件的PCB封装，这里暂且不管，不过要注意，如果在输出网表的时候此项没有指定，将会产生DRC错误。  &lt;/li&gt;
&lt;li&gt;Multiple-Part Package：该器件是否需要分割，需要分割成几部分，如果器件很大（比如FPGA），那么我们往往需要将器件分割成几个部分，比如配置、IO、供电等等，这时候就会用到这个选项。  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;全部默认，随便去个名字进入该原理图的绘制界面:&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/2_zpsf7f444f4.png&amp;quot; /&gt;&lt;br /&gt;
点击边框便可选中原理图，点击四角拖动可以改变原理图大小，&lt;strong&gt;U?&lt;/strong&gt;是标识，之后在原理图中用到它的时候便会显示&amp;quot;U1&amp;quot;,&amp;quot;U2&amp;quot;...，&lt;strong&gt;&lt;Value&gt;&lt;/strong&gt;是器件的值，比如0.1uf。&lt;br /&gt;
右侧两条便是绘制的主角，最常用的是那个右下方有个小黄点上有个加号的东西，这是引脚的添加按钮，用于添加引脚，点击后出现：&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/3_zps9015ad60.png&amp;quot; /&gt;&lt;br /&gt;
Name表示引脚名，Number表示引脚编号，Shape表示引脚外形，Type是类型，比如三态、双向等等，Width内有单独和总线选项，视情况而定。&lt;br /&gt;
除了引脚添加按钮之外，还有一些画图的按钮，是用来绘制器件原理图外形的，文字按钮用于添加注释，整体完毕后我们可以一个完整的器件原理图，这里我给出一个例子：&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/4_zpsd0b56f6c.png&amp;quot; /&gt;&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;2.省力型：&lt;/strong&gt;&lt;br /&gt;
省力么，就意味着批量，在新建器件原理图时选择&lt;strong&gt;New part form spreadsheet&lt;/strong&gt;即可出现以下窗口：&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/5_zps9116f6a5.png&amp;quot; /&gt;&lt;br /&gt;
顾名思义以及看图变不难理解，这是从电子表格进行器件原理图的建立，&lt;strong&gt;Patr Name&lt;/strong&gt;表示器件名字，&lt;strong&gt;No. of Sections&lt;/strong&gt;则是原理图所包含的分割数量。&lt;br /&gt;
下面的表格中填写引脚说明，按照各自的意思即可，与粗暴型的相差不大。&lt;br /&gt;
注意，虽然这样建立省事，但是这种方式器件外形皆为大方块，不过这种方式一般只用于复杂器件，而对于复杂期间，外观也没什么所谓了。当然也可以在生成完之后再编辑。
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/6_zpsb33e2caa.png&amp;quot; /&gt;&lt;br /&gt;
到此，器件原理图的建立便完成了。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Tuesday, 17 Feb 2015 16:02:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.02.17 16:02:article/Skill-2015_02_17_a</guid>
<category>PCB</category>
<category>Cadence</category>
<category>OrCad</category>
<category>原理图</category>
</item>

<item>
<title>【Python】哈希值</title>
<link>http://dtysky.moe/article/Skill-2014_06_23_a</link>
<description>&lt;p&gt;哈希值是哈希算法生成了一段二进制值，任意的不同数据都拥有不同哈希值，通过这一点可以做很多事情。&lt;br /&gt;
本文将会介绍各种数据类型、以及文件在Python中求取哈希值的方法以及应用举例。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1.求法&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1.hash:&lt;/strong&gt;  &lt;/p&gt;
&lt;p&gt;Python中的&lt;strong&gt;hash&lt;/strong&gt;函数用于求取一个字符串或者数值的哈希值，由于Python中任何数据类型都可以转换为字符串，所以我们利用这个函数来进行简单的哈希值计算，比如：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;hash&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;test&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;如此便可以求得字符串&amp;apos;test&amp;apos;的哈希值，同样，如果是数值的话：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;hash&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;不但如此，我们也可以求取list的哈希值：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;hash&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;str&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;([&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;也可以求得字典的，但是由于字典本身无序（集合也是如此），所以需要加一些小的变动：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;hash&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;str&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;sorted&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;})))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;先将字典排序，而后转为字符串，最后求得哈希值。&lt;br /&gt;
倘若是字典嵌套字典，可以对其中的每一个子字典求哈希值，而后求和：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;#Return hash for a dict which may contain a dict as its value&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;def&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;DHash&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Dict&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;):&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;dh&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;tmp&lt;/span&gt; &lt;span class=&amp;quot;ow&amp;quot;&gt;in&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;Dict&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;isinstance&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Dict&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tmp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;dict&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;):&lt;/span&gt;
            &lt;span class=&amp;quot;n&amp;quot;&gt;dh&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;hash&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tmp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;hash&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;str&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;sorted&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Dict&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tmp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;key&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;lambda&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;])))&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;
            &lt;span class=&amp;quot;n&amp;quot;&gt;dh&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;hash&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tmp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;hash&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;str&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Dict&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tmp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]))&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;dh&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;注意：&lt;/strong&gt;&lt;br /&gt;
使用这个函数之前，先看一下help的说明：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Return a hash value for the object.  Two objects with the same value have the same hash value.  The reverse is not necessarily true, but likely.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;也就是说，同样的哈希值并不一定对应同样的对象，但大多是，也就是说使用它的时候需要考虑条件是否不是非常严格。  &lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;strong&gt;2.hashlib：&lt;/strong&gt;  &lt;/p&gt;
&lt;p&gt;Python有一个原生库hashlib，可以用来求取字符串的md5、sha1等等，与hash函数不同，它是一个不可逆的唯一映射（虽然已经被暴力破解，但在大部分使用情况下我们仍然可以如此认为），至少，比hash函数可靠。  &lt;/p&gt;
&lt;p&gt;若要使用，只需要先加载hashlib库调用函数即可：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;md5obj&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hashlib&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;md5&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;f&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;open&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;path&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;read&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;md5obj&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;update&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;f&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;encode&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;nb&amp;quot;&gt;hash&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;md5obj&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hexdigest&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;但这里有一些细节问题：  &lt;/p&gt;
&lt;p&gt;1.大文件：  &lt;/p&gt;
&lt;p&gt;如果文件过大，则不能直接一次性计算，所得结果会与期望不符，这时候需要将文件进行分段，像这样：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;while&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;f&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;bp&amp;quot;&gt;self&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;fs&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;read&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;8096&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;ow&amp;quot;&gt;not&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;f&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;break&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;md5obj&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;update&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;f&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;encode&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;2.空字符串：  &lt;/p&gt;
&lt;p&gt;在使用是有时会遇到这样的问题：  &lt;/p&gt;
&lt;p&gt;返回值为：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;apos;d41d8cd98f00b204e9800998ecf8427e&amp;apos;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这是因为读取结束后忘记了将文件seek回开头，将会返回一个空字符串的md5值，所以我们必须要记得在文件的md5值计算结束后将文件seek回文件头：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;f&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;seek&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;hr /&gt;
&lt;h2&gt;2.例子&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1.判断文件、对象改动：&lt;/strong&gt;  &lt;/p&gt;
&lt;p&gt;我们可以用哈希值来判断文件或者像是字典这样的对象是否发生了改动，从而进行下一步的操作：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;#Only a file had been changed will process it&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;f&lt;/span&gt; &lt;span class=&amp;quot;ow&amp;quot;&gt;in&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;FileAll&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;Fs&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;open&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;f&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;rb&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;HashFile&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;get&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;f&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;==&lt;/span&gt;&lt;span class=&amp;quot;bp&amp;quot;&gt;None&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;FileNow&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;append&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;f&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;Fs&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;hash&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;==&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;HashFile&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;f&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]:&lt;/span&gt;
            &lt;span class=&amp;quot;k&amp;quot;&gt;pass&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;
            &lt;span class=&amp;quot;n&amp;quot;&gt;FileNow&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;append&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;f&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;Fs&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;close&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这一段实现的是，将现有文件的哈希值和已存的该文件列表上一次的哈希表进行比对，如果哈希值不一样，则表明文件被改动，进行处理，否则忽略。&lt;br /&gt;
当文件的数量巨大的话，如此可以节省很多不必要的开支。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Monday, 23 Jun 2014 17:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2014.06.23 17:00:article/Skill-2014_06_23_a</guid>
<category>Python</category>
<category>hash</category>
<category>哈希值</category>
</item>

<item>
<title>【Python】字典</title>
<link>http://dtysky.moe/article/Skill-2014_06_22_a</link>
<description>&lt;p&gt;Python中的字典是最为强大的数据类型，你几乎可以用它做任何事情（当然他仅仅是一种数据类型，不要要求太高了）。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1.形式：&lt;/h2&gt;
&lt;p&gt;至于在python里字典是如何实现的就不废话了，有兴趣的可以去找字典和哈希表的关系，这里指讲一些实用的东西。&lt;br /&gt;
&lt;strong&gt;首先说明，字典是无序的。&lt;/strong&gt;  &lt;/p&gt;
&lt;p&gt;字典的构造形式如下：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;key1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;value1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;key2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;value2&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;其中keyn为键，valuen为键所对应的值，显而易见，二者是成对的关系。&lt;br /&gt;
&lt;strong&gt;注意&lt;/strong&gt;，在写的时候你并不能由于让他显得好看而这么写：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;key1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;value1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;key2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;value2&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;当然如果有兴趣完全可以尝试一下ww。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2.构造：&lt;/h2&gt;
&lt;p&gt;字典无疑是强大的，他可以让你用任何类型(无论它是数值、字符串，还是函数、对象），进行索引，索引返回的值也可以是任何的类型。&lt;br /&gt;
这也就是说，我们完全可以构造一个以下的字典：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;class&lt;/span&gt; &lt;span class=&amp;quot;nc&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;():&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;def&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;__init__&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;bp&amp;quot;&gt;self&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;):&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;print&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;A&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;def&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;xx&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;():&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;print&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;A&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xx&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;xx&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;A&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这个字典包含了以下信息：&lt;br /&gt;
键1（整型）对应值&amp;apos;1&amp;apos;（字符串）&lt;br /&gt;
键&amp;apos;a&amp;apos;（字符串）对应值&amp;apos;a&amp;apos;（字符串）&lt;br /&gt;
键A（对象）对应值xx（函数）&lt;br /&gt;
键xx（函数）对应值A（对象）  &lt;/p&gt;
&lt;p&gt;当然，我们也可以这么做：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;A&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xx&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt;
    &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:{&lt;/span&gt;
            &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;列表和字典同样没有任何问题。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3.方法：&lt;/h2&gt;
&lt;p&gt;字典常用的方法无非是索引、列举、添加、删除、排序。  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1.索引：&lt;/strong&gt;    &lt;/p&gt;
&lt;p&gt;索引有两种方式，一种是常用的做法：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这句话会返回键&amp;apos;1&amp;apos;对应的值。&lt;br /&gt;
虽然常用，但这个方法会在某些情况下出现问题——当键值不存在的状况下，此方法会报错。&lt;br /&gt;
这里就会用到另一种方法：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;get&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这个方法当键不存在的状况下会返回None。  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.列举：&lt;/strong&gt;&lt;br /&gt;
有三个方法直接列举出字典里的内容（当然用for也可以）：  &lt;/p&gt;
&lt;p&gt;第一个方法为：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;items&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;此方法返回一个列表，列表里有若干元祖，每一个元祖内为一对键和值。  &lt;/p&gt;
&lt;p&gt;第二个方法为：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;keys&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;此方法返回一个列表，列表由键构成。  &lt;/p&gt;
&lt;p&gt;第三个方法为：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;values&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;同上，不过返回的是值而不是键。  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3.添加：&lt;/strong&gt;  &lt;/p&gt;
&lt;p&gt;有时候需要向字典内添加一对键值，方法很简单，如下：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;newkey&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;newvalue&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;如此便添加了一对键值newkey:newvalue。  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4.删除：&lt;/strong&gt;  &lt;/p&gt;
&lt;p&gt;如果想要删除一对键值，如此即可：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;del&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;如果想要一次清空字典，可使用：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;clear&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;5.排序：&lt;/strong&gt;  &lt;/p&gt;
&lt;p&gt;由于字典是无须的，所以可能在一些需要顺序的时候带来麻烦，这样就要求我们去对它排序，一般以下方法即可：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;sorted&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;默认按照键升序排列，字符串按照ascii码处理。&lt;br /&gt;
如果想指定值排序或者降序，可以使用：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;sorted&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;key&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;lambda&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt;&lt;span class=&amp;quot;bp&amp;quot;&gt;True&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;第一个参数是要排序的字典，第二个参数key是排序指定的对象，后面跟了一个lambda表达式，对于字典d[0]为键，d[1]为值，最后一个属性指定升序还是降序，默认False，升序。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4.应用：&lt;/h2&gt;
&lt;p&gt;一个例子：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;#建立空字典&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{}&lt;/span&gt;

&lt;span class=&amp;quot;c1&amp;quot;&gt;#添加键值：&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;ch1&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;color&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;

&lt;span class=&amp;quot;c1&amp;quot;&gt;#更新值:&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;A&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;CH&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;ch1&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;append&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;A&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;

&lt;span class=&amp;quot;c1&amp;quot;&gt;#使用值内对象：&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;print&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;ch1&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;][&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;name&lt;/span&gt;

&lt;span class=&amp;quot;c1&amp;quot;&gt;#删除键值:&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;del&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;ch1&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;

&lt;span class=&amp;quot;c1&amp;quot;&gt;#删除字典:&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;del&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;d&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Sunday, 22 Jun 2014 17:14:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2014.06.22 17:14:article/Skill-2014_06_22_a</guid>
<category>Python</category>
<category>字典</category>
</item>

<item>
<title>【Python】正则表达式入门</title>
<link>http://dtysky.moe/article/Skill-2014_06_12_a</link>
<description>&lt;p&gt;正则表达式用于字符串匹配，其强大之处在于方便和快捷（不说效率），在文本处理的时候非常建议使用。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;基础&lt;/h2&gt;
&lt;p&gt;Python中的正则表达式库为&lt;strong&gt;re&lt;/strong&gt;，使用时加载此库即可：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kn&amp;quot;&gt;import&lt;/span&gt; &lt;span class=&amp;quot;nn&amp;quot;&gt;re&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;1.规则：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;正则表达式的规则很简单，其基本形式如下：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;a.*b\S+&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;其中，a和b为确定字符，表示你希望匹配的具体字符串。&lt;br /&gt;
.和\S为特殊字符，用于匹配并非精确确定的东西，比如此处的.表示任意字符（包括空字符），\S表示&lt;strong&gt;一个&lt;/strong&gt;非空字符。&lt;br /&gt;
*和+是数量词，比如\S+表示匹配1-无穷个非空字符。  &lt;/p&gt;
&lt;p&gt;具体的规则可以在这里查看：&lt;a href=&amp;quot;http://www.jb51.net/shouce/jquery1.82/regexp.html&amp;quot;&gt;&lt;strong&gt;正则表达式规则表&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.基本使用：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在Python中，使用正则表达式匹配可以用re库下的match方法。&lt;br /&gt;
比如我想匹配以下字符串：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;text&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;&amp;#39;&amp;#39;&amp;lt;bg trans1&amp;gt;家，房间&amp;lt;/bg&amp;gt;&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;制定规则时，可以如此制定：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;   :::python
p=r&amp;#39;&lt;span class=&amp;quot;err&amp;quot;&gt;&amp;lt;&lt;/span&gt;\S+\s+S\+&amp;gt;.*&lt;span class=&amp;quot;err&amp;quot;&gt;&amp;lt;&lt;/span&gt;/\S+&amp;gt;&amp;#39;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;也可以使用&lt;strong&gt;ur&amp;apos;xxx&amp;apos;&lt;/strong&gt;的形式，指定为unicode编码。&lt;/p&gt;
&lt;p&gt;含义为：&lt;br /&gt;
&amp;lt;+若干非空字符+若干空字符+若干非空字符+&amp;gt;任意字符+&lt;/+若干非空字符+&gt;  &lt;/p&gt;
&lt;p&gt;设定好规则之后，便可以进行匹配，匹配方法的使用：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;m&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;re&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;match&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;text&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;如果匹配成功，将会返回匹配值，否则返回&lt;strong&gt;None&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3.分组：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;有时候我们希望不仅仅是匹配，并且能够取得我们所期望的一些字符串，这时候可以使用分组功能。&lt;br /&gt;
将规则改写：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;r&amp;#39;&amp;lt;(\S+)\s+(\S+)&amp;gt;(.*)&amp;lt;/(\S+)&amp;gt;&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;以()为标识，里面的表达式被视为一个分组，这时再调用match方法，返回的值便是一个分好的数组，调用group方法便可以得到各个分组的数据。&lt;br /&gt;
执行：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;text&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;&amp;#39;&amp;#39;&amp;lt;bg trans1&amp;gt;家，房间&amp;lt;/bg&amp;gt;&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;r&amp;#39;&amp;lt;(\S+)\s+(\S+)&amp;gt;(.*)&amp;lt;/(\S+)&amp;gt;&amp;#39;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;m&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;re&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;match&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;text&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;可得结果:&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;print&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;m&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;group&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;bg&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;trans1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;家，房间&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;bg&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;print&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;m&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;group&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;bg&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;trans1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;家，房间&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;bg&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;print&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;m&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;group&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;bg&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;print&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;m&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;group&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;trans1&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;print&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;m&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;group&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;err&amp;quot;&gt;家，房间&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;print&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;m&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;group&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;bg&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Thursday, 12 Jun 2014 18:05:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2014.06.12 18:05:article/Skill-2014_06_12_a</guid>
<category>Python</category>
<category>正则表达式</category>
</item>

<item>
<title>【VHDL】优先和非优先语句</title>
<link>http://dtysky.moe/article/Skill-2014_05_12_c</link>
<description>&lt;p&gt;在VHDL中有四种条件语句，它们非别用在进程内和进程外，分为优先和非优先两大类。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;使用&lt;/h2&gt;
&lt;p&gt;进程内的条件语句分为&lt;strong&gt;if-elsif-else&lt;/strong&gt;和&lt;strong&gt;case-when&lt;/strong&gt;两种。  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1.if语句：&lt;/strong&gt;  &lt;/p&gt;
&lt;p&gt;if型是入门者喜欢用的描述方式，它比较符合软件编程的形象，不强行指定所有选项，不强行设定默认值，所以使用不当可能会比较容易产生latch。&lt;br /&gt;
if语句的一个特点就是具有“优先级”，它首先判断第一个条件，如果第一个不合适将会判断第二个，这在综合后的电路中体现为&lt;strong&gt;用时间换取资源&lt;/strong&gt;，比如我们综合以下代码：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;con&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;then&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;con&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:=&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;outs&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;1000&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;elsif&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;con&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;then&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;con&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;con&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;outs&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;0100&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;elsif&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;con&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;then&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;con&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;con&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;outs&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;0010&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;elsif&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;con&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;then&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;con&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;con&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;outs&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;0001&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;end&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;得到结果如下：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/01_zps543dd450.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;这说明if语句综合得到的是一个级联结构。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.case语句：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;case型要求指定默认值，虽然也可以直接填写&lt;strong&gt;null&lt;/strong&gt;跳过但不建议这样去做，否则同样会产生latch。&lt;br /&gt;
case语句没有优先级，所有判断并列发生，&lt;strong&gt;用资源换取时间&lt;/strong&gt;，原则上在资源充分的时候，如无特殊要求建议使用，综合以下代码：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;case&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;con&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;is&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;when&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;con&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:=&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;outs&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;1000&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;when&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;con&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;con&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;outs&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;0100&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;when&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;con&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;con&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;outs&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;0010&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;when&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;con&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;con&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;outs&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;0001&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;when&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;others&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;outs&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;0000&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;end&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;case&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;得到结果如下：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/02_zps5bd20b3b.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;这说明case语句得到的是一个多路选择器。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3.进程外&lt;/strong&gt;&lt;br /&gt;
进程外的条件语句有&lt;strong&gt;with-select&lt;/strong&gt;和&lt;strong&gt;&amp;lt;=-else&lt;/strong&gt;两种类型。&lt;br /&gt;
select语句的每个分支只能够有一个赋值语句，并且只能给一个信号赋值，没有优先级。&lt;br /&gt;
&amp;lt;=-else语句每个分支同样只能给一个信号赋值，有优先级。&lt;br /&gt;
与进程内区别相似，不再细说。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Monday, 12 May 2014 21:05:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2014.05.12 21:05:article/Skill-2014_05_12_c</guid>
<category>FPGA</category>
<category>VHDL</category>
<category>条件语句</category>
</item>

<item>
<title>【VHDL】inout双向端口的使用</title>
<link>http://dtysky.moe/article/Skill-2014_05_12_b</link>
<description>&lt;p&gt;在FPGA设计中常常会遇到和外设的IO口相连的情况，这时候就要求我们必须使用它的双向端口，但这个端口的使用是由一定要求的，尤其是在Altera的器件中尚未发现特例。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;inout端口的使用&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1.结构&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;双向端口是一种特殊的三态资源，尤其是Altera的器件中基本是只存在于IO口附近的，在器件内部一般不会有三态门的存在。&lt;br /&gt;
双向端口可以看作一个选通器，这就决定了它的使用形式。  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.使用方式&lt;/strong&gt;  &lt;/p&gt;
&lt;p&gt;双向端口有三种状态，即“高电平1”、“低电平0”、“高阻态Z”。其中“Z”态用于控制传输方向，操作如下：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;entity&lt;/span&gt; &lt;span class=&amp;quot;nc&amp;quot;&gt;TEST&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;is&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;port&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;clk&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;in&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;std_logic&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;iotest&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;inout&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;std_logic&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;end&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;entity&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;n&amp;quot;&gt;xxxxxxxxxxxxxxxxxxxxxxxxxxxx&lt;/span&gt;

&lt;span class=&amp;quot;n&amp;quot;&gt;outp&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;process&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;clk&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;begin&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;clk&lt;/span&gt;&lt;span class=&amp;quot;na&amp;quot;&gt;&amp;#39;event&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;and&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;clk&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;sc&amp;quot;&gt;&amp;#39;1&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;then&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;iotest&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&amp;quot;sc&amp;quot;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;end&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;end&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;process&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;inp&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;process&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;clk&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;begin&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;clk&lt;/span&gt;&lt;span class=&amp;quot;na&amp;quot;&gt;&amp;#39;event&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;and&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;clk&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;sc&amp;quot;&gt;&amp;#39;1&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;then&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;iotest&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&amp;quot;sc&amp;quot;&gt;&amp;#39;Z&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;insignal&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;iotest&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;end&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;end&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;process&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;以上，线程&lt;strong&gt;outp&lt;/strong&gt;用于输出，&lt;strong&gt;inp&lt;/strong&gt;用于输入。可以看到，在每一次输入前要先将端口置为高阻态。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3.非顶层模块实现io口&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在模块化设计中经常会出现模块端口需要双向的情况，比如在调用一个RAM的控制器时，一般做法是将模块的端口映射到顶层，然后顶层与外部相连。&lt;br /&gt;
这时，将模块和顶层的inout端口直接映射、三态操作放在模块内虽然可以使用（实测，隐患未知），但会产生警告，告诉你三态端口没有被正常使用，这里建议不要这么做，而是换一种更为标准的做法，如下：&lt;br /&gt;
在非顶层模块中，将一个定义为inout类型的端口拆分为三个端口：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;iotest_in&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;in&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;std_logic&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;iotest_out&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;out&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;std_logic&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;iotest_en&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;out&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;std_logic&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;也就是拆分为输入、输出和输出使能三个信号，然后再顶层中进行对应操作即可：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;with&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;iotest_en&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;select&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;iotest&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;iotest_out&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;when&lt;/span&gt; &lt;span class=&amp;quot;sc&amp;quot;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
            &lt;span class=&amp;quot;sc&amp;quot;&gt;&amp;#39;Z&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;when&lt;/span&gt; &lt;span class=&amp;quot;sc&amp;quot;&gt;&amp;#39;0&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
            &lt;span class=&amp;quot;k&amp;quot;&gt;null&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;when&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;others&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;iotest_in&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;iotest&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Monday, 12 May 2014 20:05:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2014.05.12 20:05:article/Skill-2014_05_12_b</guid>
<category>FPGA</category>
<category>VHDL</category>
<category>三态</category>
</item>

<item>
<title>【VHDL】信号和变量</title>
<link>http://dtysky.moe/article/Skill-2014_05_12_a</link>
<description>&lt;p&gt;关于VHDL中信号和变量的区别已经被讨论得很多，但很多描述并不清晰，此处将给出博主个人的调试经验。&lt;/p&gt;
&lt;h2&gt;信号&lt;/h2&gt;
&lt;p&gt;VHDL中用&lt;strong&gt;signal&lt;/strong&gt;来定义一个信号，信号可以用在除了接口外的任何地方，具有全局性，但要注意它有以下几个特性：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1.赋值的单一性：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;信号虽然是全局的，但是仍然不允许在多个进程、或者同时在进程内和进程外存在对同一信号的赋值语句，比如：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;singal&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;std_logic&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;n&amp;quot;&gt;p1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;process&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;clk&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;begin&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;clk&lt;/span&gt;&lt;span class=&amp;quot;na&amp;quot;&gt;&amp;#39;event&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;and&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;clk&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;sc&amp;quot;&gt;&amp;#39;1&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;then&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&amp;quot;sc&amp;quot;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;end&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;end&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;process&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;n&amp;quot;&gt;p1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;process&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;clk&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;begin&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;clk&lt;/span&gt;&lt;span class=&amp;quot;na&amp;quot;&gt;&amp;#39;event&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;and&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;clk&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;sc&amp;quot;&gt;&amp;#39;1&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;then&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&amp;quot;sc&amp;quot;&gt;&amp;#39;0&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;end&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;end&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;process&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这样写是不被允许的，因为FPGA的每个线程是&lt;strong&gt;真正并行&lt;/strong&gt;的，在不同线程内出现对同一信号的赋值隐含着冲突的风险。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.赋值的延时性：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在线程内的信号赋值会有延时，并不能够在赋值结束后就立即改变，而是在&lt;strong&gt;进程结束后&lt;/strong&gt;改变。&lt;br /&gt;
进程结束的时间取决于设计，也就是取决于一个进程内组合逻辑延时+布线延时，如果这个尺度把握不好，很可能造成下一次采集时信号还未变化或者处于亚稳态，进一步导致slack的存在。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;h3&gt;变量&lt;/h3&gt;  &lt;/p&gt;
&lt;p&gt;变量用&lt;strong&gt;variable&lt;/strong&gt;定义，只能够在线程之内定义并使用，是作用于局部的，一般用其来构造&lt;strong&gt;计数器&lt;/strong&gt;，实现一些分布执行的操作。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1.赋值的即时性：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;变量和信号不同，它的赋值是即时的，也就是说，不必等到线程结束，赋值结束后值就立刻改变，并在之后使用到它的地方产生作用，比如：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;process&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;clk&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;variable&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;integer&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;range&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;to&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:=&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
   &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;clk&lt;/span&gt;&lt;span class=&amp;quot;na&amp;quot;&gt;&amp;#39;event&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;and&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;clk&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;sc&amp;quot;&gt;&amp;#39;1&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;then&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;end&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;end&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;process&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这一段代码实际是有问题的，但我们不考虑多次执行的越界问题，就说执行一次后，a的值应当是&lt;strong&gt;a=2&lt;/strong&gt;。&lt;br /&gt;
也就是说，a变量在同一个线程内执行了两次，第一次的赋值立即生效作用于第二次的赋值，这和信号是不同的。
但要注意，变量的&lt;strong&gt;赋值延时&lt;/strong&gt;还是存在的，不过不用理会线程中其他操作的延时而已。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Monday, 12 May 2014 18:05:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2014.05.12 18:05:article/Skill-2014_05_12_a</guid>
<category>FPGA</category>
<category>VHDL</category>
<category>信号</category>
<category>变量</category>
</item>

<item>
<title>【LD3320】语音识别的FPGA控制</title>
<link>http://dtysky.moe/article/Skill-2014_05_10_a</link>
<description>&lt;p&gt;LD3320是一个非特定性的语音识别芯片，并且也有音频录入和输出的功能，这里只说明如何使用它的语音识别功能。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;控制器&lt;/h2&gt;
&lt;p&gt;半年前完成的控制器，虽写法不太规范（也没有做过slack分析），但实测在40M下可以正常使用，下载地址：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/LD3320_FPGA_CONTROLLER&amp;quot;&gt;语音识别控制的VHDL实现&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;使用方法：&lt;/strong&gt;  &lt;/p&gt;
&lt;p&gt;1.在顶层中实例化VOICE模块，需要注意的引脚定义如下：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;inclk0&lt;/strong&gt; 输入时钟&lt;br /&gt;
&lt;strong&gt;clk_voice&lt;/strong&gt; 输出到LD3320的时钟&lt;br /&gt;
&lt;strong&gt;add_en&lt;/strong&gt; 输出到LD3320的数据为地址&lt;br /&gt;
&lt;strong&gt;data_voice&lt;/strong&gt; 输出到或来自LD3320的数据&lt;br /&gt;
&lt;strong&gt;voice_result&lt;/strong&gt; 语音识别结果&lt;br /&gt;
&lt;strong&gt;reco_rqu&lt;/strong&gt; 来自顶层的识别请求&lt;br /&gt;
&lt;strong&gt;reco_fin&lt;/strong&gt; 识别完成后的返回标志&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;2.修改用于存储初始化的内容的&lt;strong&gt;ROM_INIT.mif&lt;/strong&gt;中的内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;高八位是地址，第八位是对应地址的数据，具体对应关系参考&lt;a href=&amp;quot;http://pan.baidu.com/s/1bnzSVUn&amp;quot;&gt;这里&lt;/a&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;3.修改存有待识别的内容的ROM&lt;strong&gt;LIST.mif&lt;/strong&gt;：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;每一个词前先输入该词对应编号，然后输入该词的拼音对应的ASICII码，词中每一个字间用20（空格的ASICII码）隔开，词尾写FF，而后跟上该词拼音加空格的总长度。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;注意点&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;原来使用过程中的一些问题：&lt;/strong&gt;   &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;mic极性:&lt;br /&gt;
mic极性不要接反，否则永远不会识别成功。&lt;br /&gt;
&lt;br&gt;&lt;/li&gt;
&lt;li&gt;寄存器设置优化:&lt;br /&gt;
可以调整地址为&lt;strong&gt;0x35&lt;/strong&gt;（ADC增益），&lt;strong&gt;0xB3&lt;/strong&gt;（语音端点检测），&lt;strong&gt;0xB4&lt;/strong&gt;（语音长度判定），&lt;strong&gt;0xB5&lt;/strong&gt;（检测延迟），&lt;strong&gt;0xB6&lt;/strong&gt;（最大识别时间），&lt;strong&gt;0xB5&lt;/strong&gt;（起始噪声忽略时间）。&lt;br /&gt;
具体可以参考&lt;a href=&amp;quot;http://pan.baidu.com/s/1bnzSVUn&amp;quot;&gt;应用手册&lt;/a&gt;。&lt;/li&gt;
&lt;/ol&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Saturday, 10 May 2014 16:05:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2014.05.10 16:05:article/Skill-2014_05_10_a</guid>
<category>LD3320</category>
<category>FPGA</category>
<category>语音识别</category>
</item>

<item>
<title>【68013】slave-fifo模式下读出时的数据扰动和丢失</title>
<link>http://dtysky.moe/article/Skill-2014_05_09_d</link>
<description>&lt;h2&gt;扰动&lt;/h2&gt;
&lt;p&gt;传输过程中遇到的一个问题。&lt;br /&gt;
68013的输出数据位数设置为16，从上位机发送数据后，68013的FLAG有效，FPGA取数据，当测试数据变化比较大的时候（比如0x00到0xFF）会发生比较严重的疑似扰动现象，于是博主进行了以下测试：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;是否和变化位数有关&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;从8位开始，不断增加位数，在FPGA端用八倍于数据传输频率的示波器查看采样结果。&lt;br /&gt;
测试表明当变动位数大于约12的时候会出现扰动问题，初步认为是串扰的影响，但查询了许多资料之后，发现35M的传输频率在这样的环境之下是不应当有这样的问题的，最后选择了请教老师，老师表明可能是数据建立的问题。  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;是否是数据建立时间的问题&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;将工作频率降到一半，重复以上操作查看采样结果。&lt;br /&gt;
测试表明的确是这个问题，上一个测试中每一个数据的八次采样结果都不同，而这次数据则是在八次采样后保持了稳定，证明数据的建立的确是需要一定的时间的。&lt;br /&gt;
但问题的根本还没有解决——为什么变化位数少的时候不会发生这个问题？苦思之后，猜测是供电的问题。  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;是否是供电的问题&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;由于USB板子是独立的，使用PC外接的的USBUHB供电，所以可能导致供电不足而影响数据变化速度。&lt;br /&gt;
尝试给此板子外接并上一个3.3v的电源，提高其功率。&lt;br /&gt;
测试结果发现变化所需建立时间缩短，但仍然无法达到要求，可能是板子自身的供电用三极管有问题。&lt;br /&gt;
最后的解决方案是将输出改为8位，解决了问题。&lt;br /&gt;
此外，当外接电源之后，数据传输的成功率提升约3倍，更加证明了这个问题的存在性。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;丢数&lt;/h2&gt;
&lt;p&gt;另一个问题，在FPGA读出一个包时有一定概率会丢失一两个数据，具体表现是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;某一两个字节直接消失，同时68013内部数据指针随着消失的数据多移了一位，导致空标志拉高时应有512bytes传输成功，但最后却只传输了511bytes的数据。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这个问题也困扰了蛮久，在论坛上多次询问无果，最后调试发现问题出现在以下的状况：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;传输时有连续三个以上的0x00时，会丢掉一两个0x00。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;具体原因直到现在也没有查明，暂定解决方案是给每一个包位加个校验字节，一个包传完后将校验字节ACK给PC，PC判断传输是否成功。&lt;br /&gt;
此方法不太科学，建议使用CRC等方式提高可靠性(虽然68013文档中说自身已经有三次CRC。。。)&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Friday, 09 May 2014 23:45:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2014.05.09 23:45:article/Skill-2014_05_09_d</guid>
<category>68013</category>
<category>扰动</category>
</item>

<item>
<title>【68013】控制端口EP0使用</title>
<link>http://dtysky.moe/article/Skill-2014_05_09_c</link>
<description>&lt;p&gt;本文章将会说明68013控制端口EP0的使用方法。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;简述&lt;/h2&gt;
&lt;p&gt;控制端口EP0是一个特殊的端口，该端口永远不会被死锁，在每次发送数据时会清空上一次的错误信息，主要用于传输一些控制指令。&lt;br /&gt;
EP0传输的指令有三种，用户自定义的指令是vendor类型。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;PC端&lt;/h2&gt;
&lt;p&gt;前面贴中已经说过应用API实例化USB对象的方法，如果要使用EP0端口，只需要使用实例化的控制端口的&lt;strong&gt;XferData&lt;/strong&gt;端口即可，不过要注意待传输的数据，控制端口传送的数据结构为：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;令牌包+数据段&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;令牌包的作用是定义此次端口要实现的功能，数据段中则是需要传输的数据，令牌包的结构为：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Direction  //指定端口类型，建议使用&lt;strong&gt;CyConst&lt;/strong&gt;中的属性，比如此处想通过EP0向设备发送一个包，则使用&lt;strong&gt;DIR_TO_DEVICE&lt;/strong&gt;属性。&lt;br /&gt;
ReqType    //请求类型，一般选择&lt;strong&gt;CyConst.REQ_VENDOR&lt;/strong&gt;表明为用户定义指令。&lt;br /&gt;
ReqCode    //请求代码，这个是进入了某个类型的请求时需要进入的响应函数的分支，响应函数具体设计见下面。&lt;br /&gt;
Target    //接收方设置。&lt;br /&gt;
Index    //根据请求类型和请求代码按照需求设定。&lt;br /&gt;
Value    //根据请求类型和请求代码按照需求设定。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;数据段则是在令牌包后需要传输的数据，最多64bytes。&lt;br /&gt;
设置完成后，调用&lt;strong&gt;XferData&lt;/strong&gt;传输即可。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;固件部分&lt;/h2&gt;
&lt;p&gt;此处将会说明EP0使用的固件端。&lt;br /&gt;
前面的帖子已言，在&lt;strong&gt;FW.c&lt;/strong&gt;内有一个控制指令响应函数，这个函数中有一个case分支，用户可以增加选项或者直接改写分支对应的响应函数来实现自己想要实现的功能。&lt;br /&gt;
分支的标识即为上位机EP0令牌包中的&lt;strong&gt;ReqCode&lt;/strong&gt;,68013通过此数据选择进入的分支，执行响应的请求，例如我在在此处添加了两个自定义请求：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;case&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0xFF&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;RstAll&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;())&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;break&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;k&amp;quot;&gt;case&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0xFE&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;GetState&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;())&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;break&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;第一个指令请求复位，第二个指令请求获取当前状态。&lt;br /&gt;
在分支中调用的函数由自己定义，原则上放在&lt;strong&gt;FW.c&lt;/strong&gt;和自定义的&lt;strong&gt;slave.c&lt;/strong&gt;内都可以，这里建议放在&lt;strong&gt;slave.c&lt;/strong&gt;内，尽量不要在前一个文件中添加内容。  &lt;/p&gt;
&lt;p&gt;复位函数如下：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;BOOL&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;RstAll&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;void&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;FIFORESET&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x80&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; 
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;FIFORESET&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x02&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;FIFORESET&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x06&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;FIFORESET&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x00&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;                
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;   
    &lt;span class=&amp;quot;n&amp;quot;&gt;EP2FIFOCFG&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x00&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;            
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;EP2FIFOCFG&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x10&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;  &lt;span class=&amp;quot;c1&amp;quot;&gt;//8bits          &lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;EP6FIFOCFG&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x00&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;            
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;EP6FIFOCFG&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x09&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;            
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;EP6AUTOINLENH&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x02&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;EP6AUTOINLENL&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x00&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;INPKTEND&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x84&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;INPKTEND&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x84&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;INPKTEND&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x84&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;INPKTEND&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x84&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;TRUE&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;获取状态的函数也差不多，将请求类型设置为读取，然后在函数中将某些寄存器的值赋给&lt;strong&gt;EP0BUF&lt;/strong&gt;数组中的元素，最后写&lt;strong&gt;EP0BCH/L&lt;/strong&gt;即可。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Friday, 09 May 2014 22:05:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2014.05.09 22:05:article/Skill-2014_05_09_c</guid>
<category>68013</category>
<category>C#</category>
<category>CYUSB.NET</category>
<category>固件</category>
</item>

<item>
<title>【68013】上位机</title>
<link>http://dtysky.moe/article/Skill-2014_05_09_b</link>
<description>&lt;p&gt;本文章将会记录基于官方提供的.NET API的简单使用。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;简述&lt;/h2&gt;
&lt;p&gt;在官方提供的开发包内有两个类库，其中一个是&lt;strong&gt;Cyusb.dll&lt;/strong&gt;，这个类库是.NET环境下的，封装的比较好，使用方便，建议使用。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;对象建立&lt;/h2&gt;
&lt;p&gt;此处将会说明如何使用&lt;strong&gt;CYUSB.NET&lt;/strong&gt;建立USB对象。&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;USBDeviceList&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;MyUsbList&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;USBDeviceList&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;CyConst&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;YYY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这一句用于建立一个Cypress生产的USB设备列表，通过&lt;strong&gt;CyConst.YYY&lt;/strong&gt;来指定列表所从属的设备类型，在这个应用中我选取的是&lt;strong&gt;DEVICES_CYUSB&lt;/strong&gt;，表明作为一般的USB设备。&lt;br /&gt;
建立完成后，可以通过查看&lt;strong&gt;USBDeviceList&lt;/strong&gt;的&lt;strong&gt;Count&lt;/strong&gt;属性来了解已连接设备的个数:&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;MyUsbList&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Count&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;可以加上一句判断，若其值为零，提示用户重新连接设备。&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;CyFX2Device&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;Fw&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;MyUsbList&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;Cypress FX2LP StreamerExample Device&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;CyFX2Device&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这一句建立一个FX2的对象，对象的初始化是通过指定一个正常连接后的设备驱动名称来执行的，比如此处我设定的是&lt;strong&gt;Cypress FX2LP StreamerExample Device&lt;/strong&gt;。&lt;br /&gt;
这个对象通常用于执行固件的下载，比如调用它的&lt;strong&gt;LoadRAM&lt;/strong&gt;方法就是将固件下载到RAM之内，调试之时非常有用。&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;CyUSBDevice&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;MyUsb&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;MyUsbList&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mh&amp;quot;&gt;0x04B4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x00F1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;CyUSBDevice&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这一句建立一个USB设备对象，初始化要求指定设备的VID和PID，VID对于此设备是确定的，PID是驱动所指定的，可以查找相关文档或者在设备管理器中查看。  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;CyUSBEndPoint&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;MyOutPoint&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;MyUsb&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;EndPointOf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mh&amp;quot;&gt;0x02&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;CyUSBEndPoint&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;MyInPoint&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;MyUsb&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;EndPointOf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mh&amp;quot;&gt;0x86&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;CyControlEndPoint&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;MyControlPoint&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;MyUsb&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ControlEndPt&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这几句用于建立该设备下的端口对象。&lt;strong&gt;CyUSBEndPoint&lt;/strong&gt;指定的是传输端口，初始化要求指定端口地址，此处OUT端口指定为EP2，IN端口指定为EP6；&lt;strong&gt;CyControlEndPoint&lt;/strong&gt;制定的是控制端口。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;一些方法和属性&lt;/h2&gt;
&lt;p&gt;此处将会说明类的一些常用方法和属性。&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;bool&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;XferData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ref&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;byte&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[]&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;data&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;ref&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;int&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;len&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这个方法在端口类之内，通过指定&lt;strong&gt;data&lt;/strong&gt;（字节数组）和&lt;strong&gt;len&lt;/strong&gt;（传输长度）来通过端口进行数据发送或采集，如果传输成功则返回True，否则返回Flase。 &lt;br /&gt;
此方法是同步传输方法，使用比较简单，如果需要异步传输，请查看&lt;strong&gt;BeginDataXfer、FinishDataXfer&lt;/strong&gt;方法。&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;TimeOut&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这个属性对端口有效，用于指定一个超时时间，单位是ms，在传输中如果在该时间内未成功，则返回传输失败。  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ReConnect&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;此方法在&lt;strong&gt;CyUSBDevice&lt;/strong&gt;类之内，用于重新连接设备。&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Reset&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;此方法在&lt;strong&gt;CyUSBDevice&lt;/strong&gt;内，用于清空该设备的错误信息（不是硬件复位！）&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;LastError&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;该属性在端口类内，用于查看上一次出现错误的信息。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Friday, 09 May 2014 20:05:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2014.05.09 20:05:article/Skill-2014_05_09_b</guid>
<category>68013</category>
<category>C#</category>
<category>CYUSB.NET</category>
</item>

<item>
<title>【68013】slave-fifo模式的工作方式设置和问题</title>
<link>http://dtysky.moe/article/Skill-2014_05_09_a</link>
<description>&lt;p&gt;在使用过程中出现了一些奇怪的问题，首先就是slave-fifo的工作方式设置中，下面将会进行说明。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;寄存器的设置&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;FIFOPINPOLAR&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这个寄存器是某些引脚极性设置，设置后必须严格按照设定值进行使用这些引脚，否则可能会造成一些奇怪的后果（比如在SLOE极性错误的情况下使能SLRD会导致读出的数据完全错误）。  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;REVCTL&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;此寄存器低二位设置的是OUT和IN端口的SKIP是否有效，只有在有效的情况下写SKIP位才能够发生作用。  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;EPXCFG&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;此类寄存器设置的是各个端口的工作模式，这里需要特别注意的是SIZE、BUF1/0的设置。按理说只要参照官方给出的要求设置便可，用户手册说明如下：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Notice that the packet byte counts differ in the upper PFC bits because the endpoints support different FIFO sizes: The EP2
FIFO can be a maximum of 4096 bytes long, the EP6 FIFO can be a maximum of 2048 bytes long, and the EP4 and EP8
FIFOS can be a maximum of 1024 bytes long.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;也就是说EP2最多配置为4KB，EP6是2KB，EP4/8都是1KB。&lt;/p&gt;
&lt;p&gt;但博主在多次尝试后发现无法配置为&amp;quot;EP2 2x1024 OUT&amp;quot;+&amp;quot;EP6 2x1024 IN&amp;quot;的模式，会强制将其转为两个2x512B的端口，百思不得其解，只得上Cypress官网询问，终于找到了&lt;a href=&amp;quot;http://www.cypress.com/?id=4&amp;amp;rID=31675&amp;quot;&gt;问题所在&lt;/a&gt;：&lt;br /&gt;
由于协议问题，在BULK传输模式下每个BUFFER的大小必须设置为512B。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;EPXFIFOCFG&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;此类寄存器用于配置端口FIFO，需要注意的是&lt;strong&gt;ZEROLENIN&lt;/strong&gt;这一位。当设定为AUTOIN模式时如果这一位有效，将可能造成IN包错乱的情形（本来没有提交被当做提交了），所以慎用。
同时，在正常设置OUT端口的此寄存器前必须要先将其设置为0x00，否则会不生效，具体原因不明。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;FIFORESET&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;复位寄存器，注意每次复位前先对其最高位写1,来屏蔽外部请求，复位结束后清零即可。&lt;br /&gt;
复位结束后需要重新设置&lt;strong&gt;EPXFIFOCFG、EPXAUTOINLENH&lt;/strong&gt;。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Friday, 09 May 2014 18:15:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2014.05.09 18:15:article/Skill-2014_05_09_a</guid>
<category>68013</category>
<category>IC</category>
<category>硬件</category>
<category>固件</category>
</item>

<item>
<title>【68013】slave-fifo模式的固件结构</title>
<link>http://dtysky.moe/article/Skill-2014_05_08_a</link>
<description>&lt;p&gt;在这一篇文章中，博主将对正在使用的slave-fifo模式下68013的固件进行分析，以此来说明68013的固件结构。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;FW.c&lt;/h2&gt;
&lt;p&gt;这个文件中包含Cypress官方默认的一些基本操作（比如电源管理、设备描述符枚举等）。虽然官方不建议自己更改，但在某些情况下我们仍然需要对其进行添加达到一些操作的目的。  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1.整体架构：&lt;/strong&gt;&lt;br /&gt;
文件中共包含两个函数定义： &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;void&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;main&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;void&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;  
&lt;span class=&amp;quot;kt&amp;quot;&gt;void&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;SetupCommand&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;void&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;其中第一个函数是固件的主循环，第二个函数的作用是响应控制端口的指令。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.main函数内部:&lt;/strong&gt;&lt;br /&gt;
此函数中主要通过调用以下函数来实现响应的功能：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;TD_Init&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;用户自己定义的函数，作用是进行68013的初始化，只在固件上电的时候执行一次。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;枚举过程&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;从初始化结束开始到主循环都是枚举过程，用于USB设备的描述符枚举，也就是PC端的设备识别过程。&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;while&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;TRUE&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){...}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这个是固件执行的主循环，当设备枚举成功正常工作时会一直进行此循环中的内容。&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;GotSUD&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;SetupCommand&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;GotSUD&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;FALSE&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;在主循环内，检查是否有从控制端口传来的令牌包，若有，进入响应函数进行处理。&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Sleep&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;){...}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;在主循环内，检查是否有挂起请求，若有，进入挂起模式。&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;TD_Suspend&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()){...}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;在挂起时执行用户定义的挂起操作指令，执行完毕返回后推出挂起。&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;TD_Poll&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;用户自己定义的调度函数，在没有挂起请求或者令牌包的时候不断执行。  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3.SetupCommand内部:&lt;/strong&gt; &lt;br /&gt;
该函数用于实现控制端口传来的控制指令。&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;switch&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;SETUPDAT&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]){&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;case&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;...}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;收到令牌包请求后，读取令牌包数组的第二个元素，来进行对应的操作。&lt;br /&gt;
令牌包的内容由上位机定义，具体内容将在后面的教程中给出。&lt;br /&gt;
在这个函数内已经有了许多官方默认设定的分支，这些分支中执行函数交由用户定义，已经足够完成一些一般的操作，如果还有其他需求也可自己加入其他分支，但注意分支取值有效区间，68013允许的为0xA0-0xAF以外的所有数据。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;slave.c&lt;/h2&gt;
&lt;p&gt;由于使用的是slave-fifo模式，所以这个用户自定义文件名字设定为此，用户的自定义基本操作都在这里。 &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1.整体架构：&lt;/strong&gt;&lt;br /&gt;
此文件主要进行初始化函数、用户调度函数以及各种中断、控制请求的处理函数定义。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.函数详解：&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;void&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;TD_Init&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;void&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;在此函数中对68013进行初始化，可以参考&lt;a href=&amp;quot;http://dtysky.github.io/articles/68013-slave-fifomo-shi-de-diao-shi-xin-de-zhi-chu-shi-hua.html&amp;quot;&gt;这里&lt;/a&gt;。&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;void&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;TD_Poll&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;void&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;在此函数中加入用户需要在主机空闲时执行的操作，具体执行实际参考上面的分析。&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;BOOL&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;TD_Suspend&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;void&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;在此函数中加入在挂起模式时执行的操作。&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;BOOL&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;TD_Resume&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;void&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;在此函数中加入在从挂起中唤醒时执行的操作。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;控制指令响应函数&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这个会在以后的文章中进行分析。&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;void&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;ISR_xxxx&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;void&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;interrupt&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;各种中断函数，用于执行响应的中断，具体中断对应请查看&lt;a href=&amp;quot;http://pan.baidu.com/s/1eqgOY&amp;quot;&gt;EZUSB_TRM&lt;/a&gt;。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;其他&lt;/h2&gt;
&lt;p&gt;剩下的还有一些像是头文件、数据段定义等等，不用自己关心，最好不要修改。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Thursday, 08 May 2014 19:30:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2014.05.08 19:30:article/Skill-2014_05_08_a</guid>
<category>68013</category>
<category>IC</category>
<category>硬件</category>
<category>固件</category>
</item>

<item>
<title>【68013】slave-fifo模式的调试心得之初始化</title>
<link>http://dtysky.moe/article/Skill-2014_05_05_a</link>
<description>&lt;p&gt;68013这个片子用了不少时间，从一开始的懵懂时期凑活能用到现在研究的算是比较深入，期间遇到了一些令人抓狂的问题，现在与大家分享一下。&lt;br /&gt;
由于博主是利用68013来达到PC和FPGA的通信，所以用的是它的slave-fifo模式，其中使用EP2作为批量输出端口，EP6作为批量输入端口，下面就通过一个初始化函数来说明68013的slave-fifo模式的初始化过程。&lt;br /&gt;
这个初始化函数的定义位于用户自定义的c文件内的开头，具体固件的架构将在下一帖内说到。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;slave-fifo模式的初始化&lt;/h2&gt;
&lt;p&gt;在看下面的内容之前，建议先在这里下载官方的&lt;a href=&amp;quot;http://pan.baidu.com/s/1gd3OvKZ&amp;quot;&gt;slave-fifo开发文档&lt;/a&gt;来对照寄存器的位定义，也可以直接参考官网的&lt;a href=&amp;quot;http://pan.baidu.com/s/1eqgOY&amp;quot;&gt;EZUSB_TRM&lt;/a&gt;。 &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;void&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;TD_Init&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;void&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;){&lt;/span&gt; 
    &lt;span class=&amp;quot;n&amp;quot;&gt;CPUCS&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x10&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;   
    &lt;span class=&amp;quot;c1&amp;quot;&gt;//以上定义MCU设置&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;PINFLAGSAB&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x4A&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;          &lt;span class=&amp;quot;c1&amp;quot;&gt;// FLAGB - EP2PF ,FLAGA - EP6EF&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;  
    &lt;span class=&amp;quot;n&amp;quot;&gt;PINFLAGSCD&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x08&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;          &lt;span class=&amp;quot;c1&amp;quot;&gt;// FLAGC - EP2EF&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;  
    &lt;span class=&amp;quot;c1&amp;quot;&gt;//以上定义对外的标志信号，如果设定了PF（可编程标志位，还需同时设定EPXFIFOPFH/L）。  &lt;/span&gt;
    &lt;span class=&amp;quot;c1&amp;quot;&gt;//每一句赋值后面的&amp;quot;SYNCDELAY&amp;quot;是cypress自己在固件里定义的延时同步函数，用来保证寄存器写入正常。&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;PORTACFG&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x80&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;  
    &lt;span class=&amp;quot;c1&amp;quot;&gt;//以上定义端口A的功能，在此模式下一般不需要关心。&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;IFCONFIG&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x03&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;// for async? for sync?&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;  
    &lt;span class=&amp;quot;c1&amp;quot;&gt;//以上定义68013的工作模式。&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;FIFOPINPOLAR&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x3F&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;  
    &lt;span class=&amp;quot;c1&amp;quot;&gt;//以上定义某些引脚的有效极性（高有效或者低有效）。*&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;CPUCS&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x02&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;  
    &lt;span class=&amp;quot;c1&amp;quot;&gt;//以上定义MCU设置，在此模式下默认此值即可**  &lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;REVCTL&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x01&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;  
    &lt;span class=&amp;quot;c1&amp;quot;&gt;//以上定义是否开启每个端口的SKIP功能，这里开启自动输出，关闭自动输入。&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;EP4CFG&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x01&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;                &lt;span class=&amp;quot;c1&amp;quot;&gt;//clear valid bit&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;                     
    &lt;span class=&amp;quot;n&amp;quot;&gt;EP8CFG&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x01&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;                &lt;span class=&amp;quot;c1&amp;quot;&gt;//clear valid bit&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;   
    &lt;span class=&amp;quot;n&amp;quot;&gt;EP2CFG&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0xA0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;                &lt;span class=&amp;quot;c1&amp;quot;&gt;//out 512 bytes, 4x, bulk&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;                    
    &lt;span class=&amp;quot;n&amp;quot;&gt;EP6CFG&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0xE0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;                &lt;span class=&amp;quot;c1&amp;quot;&gt;// in 512 bytes, 4x, bulk&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;              
    &lt;span class=&amp;quot;c1&amp;quot;&gt;//以上定义每一个端口的功能，此处定义为端口2bulk输出，四缓冲，一个缓冲512bytes；端口4bulk输入，四缓冲，一个缓冲512bytes。  &lt;/span&gt;
    &lt;span class=&amp;quot;c1&amp;quot;&gt;//注意，此处的缓冲区个数和大小不可随心设定，而且有些诡异，在之后的贴中会说到。&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;FIFORESET&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x80&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;             &lt;span class=&amp;quot;c1&amp;quot;&gt;// activate NAK-ALL to avoid race conditions&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;                    &lt;span class=&amp;quot;c1&amp;quot;&gt;// see TRM section 15.14&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;FIFORESET&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x02&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;             &lt;span class=&amp;quot;c1&amp;quot;&gt;// reset, FIFO 2&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;                    &lt;span class=&amp;quot;c1&amp;quot;&gt;// &lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;FIFORESET&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x04&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;             &lt;span class=&amp;quot;c1&amp;quot;&gt;// reset, FIFO 4&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;                    &lt;span class=&amp;quot;c1&amp;quot;&gt;// &lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;FIFORESET&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x06&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;             &lt;span class=&amp;quot;c1&amp;quot;&gt;// reset, FIFO 6&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;                    &lt;span class=&amp;quot;c1&amp;quot;&gt;// &lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;FIFORESET&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x08&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;             &lt;span class=&amp;quot;c1&amp;quot;&gt;// reset, FIFO 8&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;                    &lt;span class=&amp;quot;c1&amp;quot;&gt;// &lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;FIFORESET&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x00&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;             &lt;span class=&amp;quot;c1&amp;quot;&gt;// deactivate NAK-ALL&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;   
    &lt;span class=&amp;quot;c1&amp;quot;&gt;//以上为FIFO端口复位操作，首先将FIFORESET的最高位有效，用此来屏蔽其他所有请求，然后依次复位2、4、6、8端口，最后最高位写零，退出复位。&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;EP2FIFOCFG&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x00&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;            
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;EP2FIFOCFG&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x10&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;            &lt;span class=&amp;quot;c1&amp;quot;&gt;// AUTOOUT=1, WORDWIDE=0&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;  
    &lt;span class=&amp;quot;n&amp;quot;&gt;EP6FIFOCFG&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x00&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;            
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;EP6FIFOCFG&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x09&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;            &lt;span class=&amp;quot;c1&amp;quot;&gt;// AUTOIN=1, ZEROLENIN=0, WORDWIDE=1&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;  
    &lt;span class=&amp;quot;c1&amp;quot;&gt;//以上设定上面有效的端口2、6的slave-fifo模式设置，比较需要注意的的是AUTOX和ZEROLENIN，这个在后面的贴中会说到。&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;EP6AUTOINLENH&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x02&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;EP6AUTOINLENL&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x00&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;c1&amp;quot;&gt;//以上设定EP6在自动输入模式下的打包提交阈值。   &lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;EP2FIFOPFH&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x88&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;EP2FIFOPFL&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x00&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;  
    &lt;span class=&amp;quot;c1&amp;quot;&gt;//以上设定EP2PF编程位的有效方式—大于或等于512个字节时有效，这个寄存器的官方说明也有点问题，后面的贴中会说到。&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;INPKTEND&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x84&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;INPKTEND&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x84&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;INPKTEND&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x84&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;INPKTEND&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mh&amp;quot;&gt;0x84&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;SYNCDELAY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;  
    &lt;span class=&amp;quot;c1&amp;quot;&gt;//以上操作是为了清空IN包，以防万一（只有在开启了SKIP功能的情况下才有效）。&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Monday, 05 May 2014 17:32:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2014.05.05 17:32:article/Skill-2014_05_05_a</guid>
<category>68013</category>
<category>IC</category>
<category>硬件</category>
</item>

<item>
<title>【Html】pelican主题的制作之模板</title>
<link>http://dtysky.moe/article/Skill-2014_05_02_a</link>
<description>&lt;h2&gt;概述&lt;/h2&gt;
&lt;p&gt;此次论述的是pelican的theme的构成以及如何创建属于自己的theme。&lt;/br&gt;
第一部分，模板。&lt;/p&gt;
&lt;p&gt;观看此系列文章要求：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;掌握基本的Html语法。  &lt;/li&gt;
&lt;li&gt;了解&lt;a href=&amp;quot;http://www.oschina.net/question/5189_3943&amp;quot;&gt;jinjia2的基本原理&lt;/a&gt;。  &lt;/li&gt;
&lt;li&gt;了解本次创建主题使用的&lt;a href=&amp;quot;http://v3.bootcss.com/css/#type-body-copy&amp;quot;&gt;Bootstrap&lt;/a&gt;提供的一些样式，可以大大提高效率。  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;基本原理&lt;/h2&gt;
&lt;p&gt;首先介绍一下创建pelican主题的基本原理。  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1.pelican主题文件的基本结构&lt;/strong&gt;：&lt;br /&gt;
当在本地利用pelican创建完一个Blog后，建议建立一个文件夹来放置自己的theme，比如博主建立的文件夹名为&lt;strong&gt;MyTheme&lt;/strong&gt;，然后，在该文件夹下建立你的主题文件夹，随便起一个自己喜欢的名字即可，博主的专属主题名为&lt;strong&gt;Sky&lt;/strong&gt;。随后便可以在此文件夹下建立自己的主题文件了，其结构如下：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;├── static&lt;br /&gt;
│　├── css&lt;br /&gt;
│　└── images&lt;br /&gt;
└── templates&lt;br /&gt;
　　├── archives.html    / to display archives &lt;br /&gt;
　　├── article.html     // processed for each article &lt;br /&gt;
　　├── author.html      // processed for each author&lt;br /&gt;
　　├── authors.html     // must list all the authors&lt;br /&gt;
　　├── categories.html  // must list all the categories&lt;br /&gt;
　　├── category.html    // processed for each category&lt;br /&gt;
　　├── index.html       // the index. List all the articles&lt;br /&gt;
　　├── page.html        // processed for each page&lt;br /&gt;
　　├── tag.html         // processed for each tag&lt;br /&gt;
　　└── tags.html        // must list all the tags. Can be a tag cloud.  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;也就是说，在主题目录下会有两个子目录&lt;strong&gt;static&lt;/strong&gt;和&lt;strong&gt;templates&lt;/strong&gt;，其中&lt;strong&gt;static&lt;/strong&gt;存放的是样式、图像等基本静态文件，&lt;strong&gt;templates&lt;/strong&gt;存放的是基于jinjia2的模板文件。&lt;br /&gt;
&lt;strong&gt;本次教程针对模板部分。&lt;/strong&gt;  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.基本模板文件的功能&lt;/strong&gt;：&lt;br /&gt;
下面是每一个pelican要求的基本模板文件列表与其功能。  &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;archives.html：所有文章的集合，在这一页上，你可以按照你喜欢的方式列出所有你已有的文章。&lt;/li&gt;
&lt;li&gt;article.html：文章显示页面，每一篇博文就是用这个模板生成的。  &lt;/li&gt;
&lt;li&gt;author.html：用于显示某个作者所有文章的集合。  &lt;/li&gt;
&lt;li&gt;authors.html：用于列举所有作者的集合。  &lt;/li&gt;
&lt;li&gt;vcategories.html：用于列举所有分类的集合。  &lt;/li&gt;
&lt;li&gt;vcategory.html：用于显示某个分类下所有文章的集合。  &lt;/li&gt;
&lt;li&gt;index.html：这是博客的主页。  &lt;/li&gt;
&lt;li&gt;page.html：尚未清楚作用。  &lt;/li&gt;
&lt;li&gt;tag.html：用于显示所有具有某个标签的文章的集合。  &lt;/li&gt;
&lt;li&gt;tags.html：用于列举所有标签的集合。  &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;3.jinjia2的使用&lt;/strong&gt;：&lt;br /&gt;
jinjia2的使用非常简单，其本质是利用一些Python或自身的语句来做一些重复性的工作，借此达到提高开发效率的目的。&lt;br /&gt;
基本上我们需要的逻辑操作都是以这样的形式出现的：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;{% yourcode %}
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;其中&amp;quot;yourcode&amp;quot;是你想要执行的内容。&lt;br /&gt;
比如：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;{% for tag in tags %}
　　your code
{% end for %}
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这一段代码的意思是：对tags集合中的每一个tag执行一次循环打印，打印的内容就是&amp;quot;your code&amp;quot;这个内容。&lt;br /&gt;
将其做一下小小的改变就可以实现文章列表等我们需要的功能，比如下面一段就是用来列举所有tag的：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;{% for tag in tag_cloud %}
&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;li&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;class=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;tag-{{ tag.1 }}&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;gt;&lt;/span&gt;
　　&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&amp;quot;na&amp;quot;&gt;href=&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;{{ SITEURL }}/{{ tag.0.url }}&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;gt;&lt;/span&gt;
　　　　&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;{{ tag.0 }}&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
　　&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
　&lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
{% endfor %}
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;当然，不仅仅是&lt;strong&gt;for&lt;/strong&gt;，jinjia2同样支持&lt;strong&gt;if&lt;/strong&gt;、&lt;strong&gt;include&lt;/strong&gt;等语句，用到再说。  &lt;/p&gt;
&lt;p&gt;jinjia2有一个非常好的特性，就是支持&lt;strong&gt;继承&lt;/strong&gt; ，这一点使用得当可以进一步提高开发效率。其本质上是建立一个公用的父模板，将许多重复性的东西（例如页面信息，样式表的载入，资源路径等）都做好，然后在子模板中替换父模板中的一些内容即可。&lt;br /&gt;
如果要使用继承，可以在子模板开发加入以下代码： &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;{% extends &amp;quot;base.html&amp;quot; %
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这句代码表示：此模板继承于&lt;strong&gt;base.html&lt;/strong&gt;。  &lt;/p&gt;
&lt;p&gt;由于博客的特殊性（虽然有很多页面，但每一个不同的页面往往会包含相同的元素），博主建议建立一个&lt;strong&gt;base.html&lt;/strong&gt;作为共用的父模板来简化整体的搭建，事实上一般也是这么做的。&lt;/br&gt;
模板继承的精华实际上只有一句代码：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;{% block yourname %}default{% endblock %}
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这表示一个可以在继承其的子模板内替换的块，这个块是用&amp;quot;yourname&amp;quot;进行标识的，可以替换为任意你想要的名字，&amp;quot;default&amp;quot;是缺省值。&lt;/br&gt;
具体使用时只要在子模板中将同样标识的block下&amp;quot;default&amp;quot;的内容替换即可：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;{% block yourname %}aaa{% endblock %}
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;另外，还有一种语句用于打印在python中定义的变量：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;{{ yourvar }}
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这表示将python中定义的变量&amp;quot;youvar&amp;quot;打印在当前位置。  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4.每一个模板内可用变量列表&lt;/strong&gt;：&lt;br /&gt;
pelican制定了一些在某个模板内可用的变量，具体参考&lt;a href=&amp;quot;http://docs.getpelican.com/en/3.1.1/themes.html&amp;quot;&gt;这里&lt;/a&gt;。&lt;br /&gt;
在使用是，只需要用&lt;strong&gt;{{}}&lt;/strong&gt;语句将其在适当的位置打印出来，或者将其作为&lt;strong&gt;{%%}&lt;/strong&gt;语句中的内容即可，可以参加上面列举tag的那个例子。&lt;br /&gt;
注：同样的变量名在不同的模板中可能对应不同的父类，比如在&lt;strong&gt;tag&lt;/strong&gt;中的&lt;strong&gt;articles&lt;/strong&gt;变量指的就是在该&lt;strong&gt;tag&lt;/strong&gt;下的所有&lt;strong&gt;articles&lt;/strong&gt;。  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5.一些小问题&lt;/strong&gt;：&lt;br /&gt;
在写完模板后编译时出现了一个奇怪的问题：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ascii&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;codec&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;can&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;t decode byte 0xe8 in position 0:ordinal not in range(128)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;最终确定此问题是由于编码导致的，但在检查之后确定所有模板文件的编码格式都是UTF-8，而且pelican的源文件又暂时不想动也找不到，所以最终采用了以下方法：&lt;br /&gt;
在你的Python安装路径下的&amp;quot;\Lib\site-packages&amp;quot;中建立一个名为&lt;strong&gt;sitecustomize.py&lt;/strong&gt;的文件，用于加载自定义启动内容，在里面输入：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;# encoding=utf8&lt;/span&gt;
&lt;span class=&amp;quot;kn&amp;quot;&gt;import&lt;/span&gt; &lt;span class=&amp;quot;nn&amp;quot;&gt;sys&lt;/span&gt;
&lt;span class=&amp;quot;nb&amp;quot;&gt;reload&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;sys&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;sys&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;setdefaultencoding&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;utf8&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这样就可以解决前面的问题了。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Friday, 02 May 2014 17:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2014.05.02 17:00:article/Skill-2014_05_02_a</guid>
<category>Html</category>
<category>pelican</category>
<category>软件</category>
</item>

<item>
<title>【Python】pelican+git建立博客</title>
<link>http://dtysky.moe/article/Skill-2014_04_30_a</link>
<description>&lt;h2&gt;概述&lt;/h2&gt;
&lt;p&gt;本博客搭建于github上，github官方推荐的搭建环境是ruby下的jekyll，但由于博主用python有些经验，所以最终还是选择了python下的pelican作为搭建环境。  &lt;/p&gt;
&lt;p&gt;本博客的搭建实在许多教程的指导下完成的，也饶了一些路子，这里就把搭建的过程简单描述一下。  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;观看此文章要求：掌握基本的git操作，已经了解Python的基本操作，但并不需要了解Python的具体编程实现。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;博主在写技术类文章比较喜欢条条框框，因为这种表达形式可以用较为简洁的方式让逻辑显得比较清晰。  &lt;/p&gt;
&lt;h2&gt;搭建步骤&lt;/h2&gt;
&lt;p&gt;按照下列步骤操作前，请先安装python2.7以上的版本以及git.  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1.Git远端&lt;/strong&gt;：&lt;br /&gt;
首先，我们需要在Github上创建一个&lt;strong&gt;repository&lt;/strong&gt;,建议将其名称命名为&lt;strong&gt;username.github.com&lt;/strong&gt;（username为你的用户名），并按照项目指引，在下一个页面选择自动生成一个模板。这些操作结束后，便可以在地址栏输入你的工程名来预览你的博客了。&lt;/br&gt;Git远端准备完毕后，便可以在本地开始博客的搭建了。  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.pip安装：&lt;/strong&gt;&lt;br /&gt;
首先，为了之后的使用方便，建议先安装&lt;strong&gt;pip&lt;/strong&gt;,可以在&lt;a href=&amp;quot;https://pypi.python.org/pypi/pip#downloads&amp;quot;&gt;这里&lt;/a&gt;下载。下载解压后，用管理员身份打开cmd，cd到pip的解压目录，输入指令：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;python setup.py install
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;安装完成后就可以使用pip来简单地安装python的插件了，这里建议在环境变量里加入./python/script的绝对路劲。  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3.安装环境必要插件：&lt;/strong&gt;&lt;br /&gt;
同样以管理员身份打开cmd，输入指令安装pelican：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;pip install pelican
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;等待安装完毕，再输入指令安装&lt;a href=&amp;quot;http://zh.wikipedia.org/wiki/Markdown&amp;quot;&gt;Markdown&lt;/a&gt;：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;pip install Markdown
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;等待安装完毕，至此插件安装完成。  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4.建立Blog目录：&lt;/strong&gt;&lt;br /&gt;
首先在任意路径创立一个文件夹，用于存放你Blog的本地数据，然后cd到这个文件夹的目录，执行指令：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;pelican -quickstart
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这个指令是快速创建博客的指令，按照它的指示一步一步选择即可。&lt;br /&gt;
选择结束后，目录下便会出现如下文件或者文件夹：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;myblog&lt;br /&gt;
├── content              # 存放输入的源文件&lt;br /&gt;
│   └── (pages)          # 存放手工创建的静态页面&lt;br /&gt;
├── output               # 生成的输出文件&lt;br /&gt;
├── develop_server.sh    # 方便开启测试服务器&lt;br /&gt;
├── Makefile             # 方便管理博客的Makefile&lt;br /&gt;
├── pelicanconf.py       # 主配置文件&lt;br /&gt;
└── publishconf.py       # 发布时使用的配置文件  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;其中我们一般最常用的目录是&lt;strong&gt;content&lt;/strong&gt;，它用来存放我们的md文件（也就是每一篇博客文件）。其次，我们主要还会用到&lt;strong&gt;pelicanconf.py&lt;/strong&gt;来配置我们的博客主题等等内容。  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5.pelicanconf.py常用配置：&lt;/strong&gt;&lt;br /&gt;
以下记录了博主用到过的一些自定义配置： &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;AUTHOR&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;u&amp;#39;dtysky&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;#博客作者&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;SITENAME&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;u&amp;#39;一瞬之光，漫寂之暗&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;#博客标题&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;SITESUBTITLE&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;u&amp;#39;自由地看待世界，自由地看待自己。&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;#博客子标题&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;SITEURL&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;http://dtysky.github.io/&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;#主页地址&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;DEFAULT_DATE&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;fs&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;#快速获取本地时间作为文章发表时间&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;DEFAULT_DATE_FORMAT&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;%Y %B &lt;/span&gt;&lt;span class=&amp;quot;si&amp;quot;&gt;%d&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt; %a&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;#使用“年月日时”的时间显示方式&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;TIMEZONE&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;Asia/Shanghai&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;#默认时区，亚洲上海，其他时区可查看国际标准&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;DEFAULT_LANG&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;u&amp;#39;zh&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;#默认语言为中文&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;USE_FOLDER_AS_CATEGORY&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;True&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;#将**content**下的文件夹作为分类，博主目前测试仅可为md文件的所在目录，不支持子分类&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;PLUGIN_PATH&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;D:\Poetic_Being\MyBlog\MyPlugins&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;#插件路径声明&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;PLUGINS&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;assets&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;#插件使用列表&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;THEME&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;gum&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;c1&amp;quot;&gt;#使用主题&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;插件和主题可在这里下载：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/getpelican/pelican-plugins&amp;quot;&gt;插件&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/getpelican/pelican-themes&amp;quot;&gt;主题&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;主题的安装方法：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;pelican-themes -i 主题路径
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;可以使用以下指令检查安装的主题:  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;pelican-themes -l
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;也可以直接给THEME赋一个主题文件夹路径。  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5.博客文件编写：&lt;/strong&gt;&lt;br /&gt;
我们使用的是Markdown语言来编写博客文件，所以需要了解一些基本的语法，不过由于Markdown语言本身就是为了书写而创造的，所以非常简单。&lt;br /&gt;
在这之前，我们先来了解一下pelican中md文件的特殊语句：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Title&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;My&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;super&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;title&lt;/span&gt;  &lt;span class=&amp;quot;err&amp;quot;&gt;#文章标题&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;Date&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2010&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;12&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;03&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;10&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;20&lt;/span&gt; &lt;span class=&amp;quot;err&amp;quot;&gt;#文章时间，如果前面设定过可忽略&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;Tags&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;thats&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;awesome&lt;/span&gt; &lt;span class=&amp;quot;err&amp;quot;&gt;#文章标签&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;Category&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;yeah&lt;/span&gt; &lt;span class=&amp;quot;err&amp;quot;&gt;#文章分类&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;Authors&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;Alexis&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;Metaireau&lt;/span&gt; &lt;span class=&amp;quot;err&amp;quot;&gt;#文章作者，缺省为博客作者&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;Summary&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;Short&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;version&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;index&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;and&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;feeds&lt;/span&gt; &lt;span class=&amp;quot;err&amp;quot;&gt;#文章概括&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;a href=&amp;quot;http://docs.getpelican.com/en/3.1.1/getting_started.html#writing-articles-using-pelican&amp;quot;&gt;更多请看这。&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Markdown的&lt;a href=&amp;quot;http://wowubuntu.com/markdown/#link&amp;quot;&gt;具体语法&lt;/a&gt;可以自己慢慢学习，以下列出常用的几个符号：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;apos;===&amp;apos; #加在大标题下面&lt;br /&gt;
&amp;apos;---&amp;apos; #加在小标题下面&lt;br /&gt;
&amp;apos;* &lt;em&gt;&amp;apos; #强调一&lt;br /&gt;
&amp;apos;*&lt;/em&gt; **&amp;apos; #强调二&lt;br /&gt;
&amp;apos;&amp;gt;&amp;apos; #标注&lt;br /&gt;
&amp;apos;&amp;gt;&amp;gt;&amp;apos; #二级标注&lt;br /&gt;
&amp;apos;[name]+(link)&amp;apos;(去掉&amp;apos;+&amp;apos;) #插入连接&lt;br /&gt;
&amp;apos;![name]+(link)&amp;apos;(去掉&amp;apos;+&amp;apos;) #插入图片&lt;br /&gt;
一些html的标记  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;6.生成Html和上传到github：&lt;/strong&gt;&lt;br /&gt;
首先打开博客目录下的&lt;strong&gt;output&lt;/strong&gt;文件夹，初始化git。&lt;br /&gt;
然后打开Git Bash,输入指令清空所有文件： &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;git -rm -r *  
git add .
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;而后将改动push到Git远端的master分支下（默认分支，必须）。&lt;br /&gt;
接下来，继续打开管理员模式下的cmd，cd到博客目录下执行：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;pelican content
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;等待执行完成，html文件就生成成功了。接下来只要进入&lt;strong&gt;output&lt;/strong&gt;目录，将方才的改动缓存，然后push到远端即可。  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7.更多：&lt;/strong&gt;&lt;br /&gt;
如果要求不高，按照以上操作便可以生成一个简单的博客了，需要更多自定义内容的，可以参考&lt;a href=&amp;quot;http://docs.getpelican.com/en/3.1.1/&amp;quot;&gt;官方的文档&lt;/a&gt;。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Wednesday, 30 Apr 2014 18:06:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2014.04.30 18:06:article/Skill-2014_04_30_a</guid>
<category>Python</category>
<category>pelican</category>
<category>软件</category>
</item>

</channel>
</rss>