<?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/dtysky.rss.xml" rel="self"></atom:link>
<lastBuildDate>Monday, 23 Feb 2026 01:39:47 -00:00</lastBuildDate>

<item>
<title>我的简历</title>
<link>http://dtysky.moe/article/Create-MyResume</link>
<description>&lt;div id=&amp;quot;resume-header&amp;quot;&gt;
    &lt;div class=&amp;quot;resume-title&amp;quot;&gt;
        &lt;h2&gt;瞬光寂暗&lt;/h2&gt;
        &lt;p&gt;邮箱 : dtysky@outlook.com&lt;/p&gt;
        &lt;p&gt;Github : &lt;a href=&amp;quot;https://github.com/dtysky&amp;quot;&gt;dtysky&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;Bilibili : &lt;a href=&amp;quot;https://space.bilibili.com/58698&amp;quot;&gt;瞬光寂暗N&lt;/a&gt;&lt;/p&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;hr /&gt;
&lt;h2&gt;学历和工作&lt;/h2&gt;
&lt;p&gt;2025.03 - now    , 腾讯，互娱，游戏引擎技术组长&lt;br /&gt;
2020.08 - 2025.02，腾讯，微信，图形/大模型&lt;br /&gt;
2019.07 - 2020.07，支付宝，前端技术专家&lt;br /&gt;
2018.03 - 2019.07，支付宝，高级前端开发工程师&lt;br /&gt;
2016.08 - 2018.03，哔哩哔哩，前端工程师&lt;br /&gt;
2015.10 - 2016.07，上海禾赛光电，软件工程师&lt;br /&gt;
2015.07 - 2015.09，华为，逻辑（FPGA）工程师&lt;br /&gt;
2014.10 - 2015.06，Xilinx，研发实习生&lt;br /&gt;
2011.08 - 2015.06，东南大学，测控技术与仪器，工学士  &lt;/p&gt;
&lt;h2&gt;发展方向&lt;/h2&gt;
&lt;p&gt;游戏/WEB/AI。&lt;/p&gt;
&lt;h2&gt;技能&lt;/h2&gt;
&lt;h3&gt;工具&lt;/h3&gt;
&lt;p&gt;工作语言：TypeScript, C#, C++, CSS...需要什么就写什么呗，哪有那么多讲究。&lt;br /&gt;
领域技术：WebGL，WebGPU，FPGA，图形学，游戏开发...需要什么去学呗，哪有这么多讲究。&lt;br /&gt;
业余语言：Rust, Python, Scheme, VHDL, Verilog...更没讲究了，写着玩。&lt;br /&gt;
兴趣爱好：微单，无人机，PS，LR，LRT，Davinci，剪映。&lt;/p&gt;
&lt;h3&gt;专业领域&lt;/h3&gt;
&lt;p&gt;工作：WEB开发, 游戏引擎开发, 图形学&lt;br /&gt;
业余：数字图像处理, FPGA&lt;br /&gt;
爱好：文学创作，专业风光/人文摄影&lt;/p&gt;
&lt;h2&gt;个人介绍&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;生命是积累痛苦的巡礼，但这断然不是虚无与毁灭的故事，在前方一定还存在着救赎和希望。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;非典型码农，INFP，ADHD，宿命论者，混乱善良。自洽，坦然，从容。&lt;br /&gt;
厌恶无聊，追求有趣，言出必遂，尽力而为。但为了避免过早毁灭，也知道节制。&lt;br /&gt;
最欣赏的特质是清醒和勇气，最讨厌的特质是怯懦和贪婪。&lt;br /&gt;
看过不少书，经历过不少事，但仍旧保持着初心。&lt;br /&gt;
从小漂泊过半个中国，没有故乡。&lt;br /&gt;
毕业后换过四个城市五家公司，在B站用爱发电过，在阿里经历过斗争，现在在鹅厂搬砖，工作内容和兴趣一致。&lt;br /&gt;
事业比大多同龄人快，见识过很多光鲜外表下的不堪，看透了许多事，觉得任何交往中最重要的是真诚。&lt;br /&gt;
人格已达「完全之形」，拥有不随环境变化的强硬核心，承受逆境的能力很强。&lt;br /&gt;
对世俗物质本质上没有需求，但仍然在不影响创作的前提下努力工作赚钱，为了获得更多的体验作为输入。&lt;br /&gt;
严格来说我有很多种世俗上更好的选择，不过都放弃了。&lt;br /&gt;
因为从个人审美来看，现在选择的这种生存方式可能会支付更多代价，但却是最美丽动人的。  &lt;/p&gt;
&lt;p&gt;欣赏过一些严肃文学，文艺片，舞台剧，摇滚，独立游戏。&lt;br /&gt;
持续探索适合自己的创作方式，想创作出令自己满意的作品，并产生社会价值，让他人得到慰藉或清醒。&lt;br /&gt;
写过剧本小说，系统学过画画。每周末都会出去拍VLOG，假期会出去旅游。&lt;br /&gt;
目前是专业风光摄影师，设备A7R5/A7R3/AIR3。&lt;/p&gt;
&lt;p&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;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/resume/0.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;主要项目（工作I）&lt;/h2&gt;
&lt;h3&gt;Supercell游戏迁移&lt;/h3&gt;
&lt;p&gt;说明: 将Supercell的自研引擎,以及所有游戏迁移到小游戏平台.&lt;br /&gt;
规模: 超大&lt;br /&gt;
性质: 公司项目&lt;br /&gt;
职位: 技术组长&lt;br /&gt;
进度: 第一个游戏预备上线  &lt;/p&gt;
&lt;h3&gt;XR-FRAME 2.0&lt;/h3&gt;
&lt;p&gt;说明： 在微信小程序中，实现一套官方的混合渲染、高性能、易用、强扩展性、渐进式、遵循小程序开发标准的XR渲染方案。&lt;br /&gt;
规模： 大&lt;br /&gt;
性质： 公司项目&lt;br /&gt;
职位： 主要负责人，整体架构设计、客户端和前端实现。&lt;br /&gt;
进度： 第二版开发完毕，待上线。
使用技能：&lt;br /&gt;
图形学, 客户端开发，前端架构, 游戏引擎架构,WebGPU，Metal，XR, Typescript, C++，OC，JNI......&lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;基于1.0的前端架构，重构了后端实现。  &lt;/li&gt;
&lt;li&gt;负责了WebGPU的工具用渲染后端实现。&lt;/li&gt;
&lt;li&gt;负责了基于Google/Dawn项目的wgpu客户端渲染后端实现。  &lt;/li&gt;
&lt;li&gt;实现了XR系统，让项目可以在VR眼睛上运行。  &lt;/li&gt;
&lt;li&gt;实现过一个纯Metal的渲染后端，后来由于维护成本放弃。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;XR-FRAME&lt;/h3&gt;
&lt;p&gt;说明： 在微信小程序中，实现一套官方的混合渲染、高性能、易用、强扩展性、渐进式、遵循小程序开发标准的XR渲染方案。&lt;br /&gt;
规模： 大&lt;br /&gt;
性质： 公司项目&lt;br /&gt;
职位： 主要负责人，整体架构设计、渲染系统、资源系统、XR系统。&lt;br /&gt;
进度： 第一版已发布，第二版开发中&lt;br /&gt;
使用技能：&lt;br /&gt;
图形学, 前端架构, 游戏引擎架构, XR, Typescript, C++......&lt;br /&gt;
详见官网：&lt;a href=&amp;quot;https://developers.weixin.qq.com/miniprogram/dev/framework/xr-frame/index.html&amp;quot;&gt;微信小程序 XR-FRAME&lt;/a&gt;&lt;br /&gt;
惯例彩蛋：&lt;a href=&amp;quot;https://www.bilibili.com/video/BV12g411J76s&amp;quot;&gt;小程序示例官方AR彩蛋，开启传送门探寻世界真相&lt;/a&gt;&lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;负责了整体架构，在顶层实现了一个小程序的渲染后端，提供了给用户高度的组件定制、资源定制能力。  &lt;/li&gt;
&lt;li&gt;基于跨平台的渲染层（微信内是客户端实现），负责了主体渲染前端和WebGL后端的实现。&lt;/li&gt;
&lt;li&gt;其他重要系统，例如资源系统、AR系统、后处理系统等等的实现。  &lt;/li&gt;
&lt;li&gt;富有个人特色的文档，全面的数据上报监控。  &lt;/li&gt;
&lt;li&gt;和多个团队以及外部服务商对接合作，进行业务落地的推进。  &lt;/li&gt;
&lt;li&gt;第二版将实现3D和2D标签的混合，在GL中统一渲染，便于开发者实现更加酷炫的UI。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;微信小游戏高性能解决方案&lt;/h3&gt;
&lt;p&gt;说明： 在微信小游戏环境内提供逼近原生的性能。&lt;br /&gt;
规模： 超大&lt;br /&gt;
性质： 公司项目&lt;br /&gt;
职位： 主要负责渲染部分。&lt;br /&gt;
进度： 全新版本已发布&lt;br /&gt;
使用技能：&lt;br /&gt;
图形学, 游戏引擎架构, Typescript, C++......&lt;br /&gt;
详见官网：&lt;a href=&amp;quot;https://developers.weixin.qq.com/minigame/dev/guide/&amp;quot;&gt;微信小游戏框架&lt;/a&gt;&lt;br /&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;负责了整个引擎的Web跨端方案实现。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;项目已经支持不少项目，比如&lt;strong&gt;新轩辕传奇&lt;/strong&gt;、&lt;strong&gt;天龙八部荣耀版&lt;/strong&gt;，可在微信小程序内直接搜索游玩。&lt;/p&gt;
&lt;h3&gt;支付宝2020新春五福活动&lt;/h3&gt;
&lt;p&gt;说明： 负责&lt;strong&gt;首页3D展示&lt;/strong&gt;和&lt;strong&gt;福满全球&lt;/strong&gt;：&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/115989449&amp;quot;&gt;亿级前端项目中的3D技术-支付宝2020年新春活动的背后&lt;/a&gt;。&lt;br /&gt;
规模： 大&lt;br /&gt;
性质： 公司项目&lt;br /&gt;
职位： 项目前端负责人。&lt;br /&gt;
进度： 已上线&lt;br /&gt;
使用技能：SEIN.JS&lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;主会场3D模型，5个场景，极致优化，只占20M内存。&lt;/li&gt;
&lt;li&gt;福满全球，几乎所有元素都在3D场景内，所有材质均为定制，极致优化了内存。&lt;/li&gt;
&lt;li&gt;可靠的降级方案。&lt;/li&gt;
&lt;li&gt;覆盖面极广（10亿用户级），极低的闪退率。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;SEIN.JS&lt;/h3&gt;
&lt;p&gt;说明： Web3D游戏引擎。&lt;br /&gt;
规模： 超大&lt;br /&gt;
性质： 公司项目&lt;br /&gt;
职位： 项目负责人，负责内核开发，体系设计，跨BU合作项目管理，部分工具开发&lt;br /&gt;
进度： 已开源，持续迭代中&lt;br /&gt;
使用技能：&lt;br /&gt;
图形学, 游戏引擎架构, Typescript, Unity, Unreal4, C#, Webpack, WebAudio......&lt;br /&gt;
项目源：一系列项目，比如&lt;a href=&amp;quot;https://github.com/hiloteam/Sein.js&amp;quot;&gt;Sein.js&lt;/a&gt;、&lt;a href=&amp;quot;https://github.com/hiloteam/SeinJSUnityToolkit&amp;quot;&gt;SeinJSUnityToolkit&lt;/a&gt;和&lt;a href=&amp;quot;https://github.com/hiloteam/seinjs-gltf-loader&amp;quot;&gt;seinjs-gltf-loader&lt;/a&gt;等等...&lt;br /&gt;
惯例彩蛋：&lt;a href=&amp;quot;https://seinjs.com/cn/song&amp;quot;&gt;荒诞，艺术，存在&lt;/a&gt;&lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;游戏引擎本体&lt;/li&gt;
&lt;li&gt;空间音频系统、相机控制器、GUI等扩展系统(合作)和组件。&lt;/li&gt;
&lt;li&gt;Unity扩展&lt;/li&gt;
&lt;li&gt;CLI&lt;/li&gt;
&lt;li&gt;一系列Webpack扩展Loader以及Plugin&lt;/li&gt;
&lt;li&gt;VSCode扩展&lt;/li&gt;
&lt;li&gt;Inspector&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;项目已经支持内部不少业务。&lt;/p&gt;
&lt;h3&gt;Paradise平台&lt;/h3&gt;
&lt;p&gt;说明： 支付宝内部互动图形探索平台。&lt;br /&gt;
规模： 大&lt;br /&gt;
性质： 公司项目&lt;br /&gt;
职位： 项目前后端负责人&lt;br /&gt;
进度： 完成&lt;br /&gt;
使用技能：&lt;br /&gt;
Typescript，NodeJS，React, Mysql......&lt;br /&gt;
项目源：闭源&lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;前端以及服务端，拥有各种复杂的逻辑，包括但不限于创建作品、自动创建仓库、绑定HOOK自动构建、审核、测试、CLI、Lottie和3D模型预览发布等等。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Bilibili圣诞音游《Jingle Beats》&lt;/h3&gt;
&lt;p&gt;说明： &lt;a href=&amp;quot;https://www.bilibili.com/blackboard/activity-xmas2017.html&amp;quot;&gt;Bilibili圣诞音游《Jingle Beats》&lt;/a&gt;。&lt;br /&gt;
规模： 大&lt;br /&gt;
性质： 公司项目&lt;br /&gt;
职位： 项目前端负责人、开发者、原案&lt;br /&gt;
进度： 完成&lt;br /&gt;
使用技能：&lt;br /&gt;
PIXI.js, Typescript, WebGL, Tween，MUG、Touch Events......&lt;br /&gt;
项目源：&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/32298391&amp;quot;&gt;Bilibili圣诞游戏剖析-用pixi.js实现鬼畜音游&lt;/a&gt;&lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;一个HTML5的偏硬核音游，基于2D渲染引擎PIXI.js。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Bilibili七夕游戏《Double;7》&lt;/h3&gt;
&lt;p&gt;说明： &lt;a href=&amp;quot;http://www.bilibili.com/blackboard/double7.html&amp;quot;&gt;Bilibili七夕游戏《Double;7》&lt;/a&gt;。&lt;br /&gt;
规模： 大&lt;br /&gt;
性质： 公司项目&lt;br /&gt;
职位： 项目前端负责人、开发者、原案、制作进行&lt;br /&gt;
进度： 完成&lt;br /&gt;
使用技能：&lt;br /&gt;
Egret, Typescript, WebGL, Tween......&lt;br /&gt;
项目源：&lt;a href=&amp;quot;https://github.com/dtysky/egret-galgame&amp;quot;&gt;egret-galgame&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/28763290&amp;quot;&gt;Bilibili《七夕之约 - Double;7》技术剖析&lt;/a&gt;&lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;一个HTML5的Galgame，基于游戏引擎Egret，使用了陀螺仪、Websocket等技术，用技术为阿宅带来人文关怀。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;BML2017主视觉&lt;/h3&gt;
&lt;p&gt;说明： &lt;a href=&amp;quot;http://bml.bilibili.com/2017/index.html&amp;quot;&gt;BML2017主视觉&lt;/a&gt;。&lt;br /&gt;
规模： 大&lt;br /&gt;
性质： 公司项目&lt;br /&gt;
职位： 项目前端负责人和开发者&lt;br /&gt;
进度： 完成&lt;br /&gt;
使用技能：  &lt;br /&gt;
Node.js, React.js, Typescript, Less, Webpack......&lt;br /&gt;
项目源：&lt;a href=&amp;quot;http://dtysky.moe/article/Skill-2017_06_12_a&amp;quot;&gt;BML2017主视觉技术剖析&lt;/a&gt;  &lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;一个酷炫的主视觉，用到大量DOM动画和H5视频，PC和移动端双端适配，包含一个复杂的贴吧性质的讨论区。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Bilibili活动管理后台&lt;/h3&gt;
&lt;p&gt;说明： 一个超大型复杂管理后台。&lt;br /&gt;
规模： 超大&lt;br /&gt;
性质： 公司项目&lt;br /&gt;
职位： 核心开发者和架构者之一&lt;br /&gt;
进度： 完成&lt;br /&gt;
使用技能：  &lt;br /&gt;
Node.js, React.js, Redux, Express.js, ES6, Scss, Webpack......&lt;br /&gt;
项目源：闭源。&lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;大型活动管理后台，包括一个复杂CMS，数据源聚合管理，权限系统和发布系统。&lt;br /&gt;
全栈开发，前端技术栈为React + Redux + React-router + Immutable，后端技术栈为Node + Express。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;业余项目&lt;/h2&gt;
&lt;h3&gt;Project 31&lt;/h3&gt;
&lt;p&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;a href=&amp;quot;https://www.bilibili.com/video/BV16qpUepEGL/&amp;quot;&gt;我是「 」，祝我三十一岁生日快乐，我的故事仍在继续&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Project Destiny&lt;/h3&gt;
&lt;p&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;a href=&amp;quot;https://www.bilibili.com/video/BV1zr4y1d7o6/&amp;quot;&gt;三十岁生日，我给自己拍了部微电影&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Project Tomorrow&lt;/h3&gt;
&lt;p&gt;说明： 独立游戏。&lt;br /&gt;
规模： 大&lt;br /&gt;
性质： 梦想&lt;br /&gt;
职位： 负责人，剧本，程序，演出，出钱&lt;br /&gt;
进度： 剧本创作&lt;br /&gt;
使用技能：写作，游戏开发，花钱&lt;br /&gt;
完成内容：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;第二版剧本已完成，MVP版本创作中。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Awaken&lt;/h3&gt;
&lt;p&gt;说明： 基于Hybrid方案和WebDAV的全平台开源阅读软件。&lt;br /&gt;
规模： 中&lt;br /&gt;
性质： 产品输出&lt;br /&gt;
职位： 独立完成&lt;br /&gt;
项目源：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/Awaken&amp;quot;&gt;Awaken&lt;/a&gt;&lt;br /&gt;
使用技能：Hybrid,WebDAV,Android,iOS,Tauri,React&lt;br /&gt;
完成内容：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;功能介绍：&lt;a href=&amp;quot;https://www.bilibili.com/video/BV1uD4y1j79i&amp;quot;&gt;Awaken-开源跨平台多端同步阅读软件&lt;/a&gt;。 &lt;br /&gt;
技术教程：&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/594798266&amp;quot;&gt;Awaken-基于Hybrid方案和WebDAV的全平台开源阅读软件&lt;/a&gt;。 &lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Project Love&lt;/h3&gt;
&lt;p&gt;说明： 29岁前交友企划，以及某种意义上的人生回顾。&lt;br /&gt;
规模： 中&lt;br /&gt;
性质： 记录&lt;br /&gt;
职位： 创作&lt;br /&gt;
进度： 完成&lt;br /&gt;
使用技能：写作，剪辑&lt;br /&gt;
完成内容：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;公众号发文：&lt;a href=&amp;quot;https://mp.weixin.qq.com/s/NNijyysyJwl9rQxNuA9BCA&amp;quot;&gt;Project Love&lt;/a&gt;&lt;br /&gt;
VLOG：&lt;a href=&amp;quot;https://www.bilibili.com/video/BV1dF411N7x6&amp;quot;&gt;某「非刻板印象INFP码农」的日常一天&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Project Self&lt;/h3&gt;
&lt;p&gt;说明： 28岁自我记录企划。&lt;br /&gt;
规模： 中&lt;br /&gt;
性质： 记录&lt;br /&gt;
职位： 需求定制，创作，演讲&lt;br /&gt;
进度： 完成&lt;br /&gt;
使用技能：写作，花钱&lt;br /&gt;
完成内容：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;写真集+文章：&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/507391278&amp;quot;&gt;Project Self&lt;/a&gt;&lt;br /&gt;
个人访谈：&lt;a href=&amp;quot;https://www.bilibili.com/video/BV1kY4y187my&amp;quot;&gt;访谈&lt;/a&gt;，&lt;a href=&amp;quot;https://mp.weixin.qq.com/s/M-x5wLvFEhRXAyZOmUlTUg&amp;quot;&gt;公众号发文&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;WebGPU Renderer &amp;amp; Path Tracer&lt;/h3&gt;
&lt;p&gt;说明： 一个基于WebGPU的渲染引擎，以及基于其实现的路径追踪渲染器，并产出了系列教程。&lt;br /&gt;
规模： 中&lt;br /&gt;
性质： 技术学习&lt;br /&gt;
职位： 独立完成&lt;br /&gt;
进度： 完成&lt;br /&gt;
项目源：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/webgpu-renderer&amp;quot;&gt;webgpu-renderer&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;https://dtysky.github.io/webgpu-renderer/&amp;quot;&gt;Live Demo&lt;/a&gt;&lt;br /&gt;
使用技能：  WebGPU, Typescript, 图形学&lt;br /&gt;
完成内容：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;使用WebGPU实现了一个渲染器，包含绝大部分3D渲染功能。 &lt;br /&gt;
利用渲染器，使用光栅化+计算着色器混合方式，实现了一个路径追踪渲染器，其中包括场景合并、BVH构建、射线求交、蒙特卡洛方法、BRDF、BSDF、降噪等。&lt;br /&gt;
产出了多篇系列教程：&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/407191699&amp;quot;&gt;WebGPU实时光追美少女系列&lt;/a&gt;。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Double;14&lt;/h3&gt;
&lt;p&gt;说明： 庆生用原创游戏。&lt;br /&gt;
规模： 中&lt;br /&gt;
性质： 生日纪念&lt;br /&gt;
职位： 剧本，程序&lt;br /&gt;
进度： 已完成&lt;br /&gt;
使用技能：&lt;br /&gt;
写作，编剧，Unity&lt;br /&gt;
项目源：  闭源 &lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;游戏录屏：  &lt;a href=&amp;quot;https://www.bilibili.com/video/BV1VL4y1v7Xj/&amp;quot;&gt;《Double;14》个人独立游戏&lt;/a&gt;
小说版本：  &lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/401016548&amp;quot;&gt;Double;14&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;gl-matrix-wasm&lt;/h3&gt;
&lt;p&gt;说明： 将&lt;a href=&amp;quot;https://github.com/toji/gl-matrix&amp;quot;&gt;gl-matrix&lt;/a&gt;移植到WebAssembly。&lt;br /&gt;
规模： 中&lt;br /&gt;
性质： 技术输出&lt;br /&gt;
职位： 独立完成&lt;br /&gt;
进度： 完成&lt;br /&gt;
项目源：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/gl-matrix-wasm&amp;quot;&gt;gl-matrix-wasm&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;http://gl-matrix-wasm.dtysky.moe/&amp;quot;&gt;Live Demo&lt;/a&gt;&lt;br /&gt;
使用技能：  Rust，WebAssembly&lt;br /&gt;
完成内容：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;使用Rust + wasm-bindgen + wasm-pack技术栈。&lt;br /&gt;
提供分离、单JS两种形式。&lt;br /&gt;
包含完全的单元测试，以及完整的Benchmark。   &lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;D2-2017前端论坛嘉宾&lt;/h3&gt;
&lt;p&gt;说明： 代表B站，在D2-2017前端论坛进行了分享，中心主题和PPT在这里：&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/32104703&amp;quot;&gt;D2-现代前端-对视觉和交互的探索&lt;/a&gt;。&lt;br /&gt;
规模： 中小&lt;br /&gt;
性质： 分享&lt;br /&gt;
职位： 独立完成&lt;br /&gt;
进度： 完成&lt;br /&gt;
使用技能：  PPT，演讲......  &lt;/p&gt;
&lt;h3&gt;kanata&lt;/h3&gt;
&lt;p&gt;说明： 一个纯前端实现、无依赖的图像处理库，纯TypeScript编写，并在计划WebAssembly的版本。&lt;br /&gt;
规模： 中&lt;br /&gt;
性质： 技术输出&lt;br /&gt;
职位： 独立完成&lt;br /&gt;
进度： 开发中&lt;br /&gt;
项目源：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/kanata&amp;quot;&gt;kanata&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;http://kanata.dtysky.moe/&amp;quot;&gt;Live Demo&lt;/a&gt;&lt;br /&gt;
使用技能：  TypeScript，React.js，数字图像处理......&lt;br /&gt;
完成内容：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;主要包含一个ImageCore和若干点操作、几何变换、直方图操作和局部滤波器。&lt;br /&gt;
本身支持单操作引用，设计上支持流式处理，为敏感的性能做了一些优化，优于市面上很多库。&lt;br /&gt;
目前已然实现了许多操作并搭好了测试框架、写了一部分测试，剩下的操作正在实现中。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;hana-ui&lt;/h3&gt;
&lt;p&gt;说明：和两位队友一起实现的一个React UIKit，作为本项目的负责人，我设计项目框架，完成了项目开发、文档和DEMO的整体结构。除了设计，我也承担了相当的组件开发，在这个过程中我对React、工程化和样式的理解都大有进步。&lt;br /&gt;
规模： 中&lt;br /&gt;
性质： 技术输出&lt;br /&gt;
职位： 独立完成&lt;br /&gt;
进度： 已完成并在内部系统可用、部分外部系统可用，开源准备中。&lt;br /&gt;
项目源：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/hana-group/hana-ui&amp;quot;&gt;hana-ui&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;https://hana-ui.moe/&amp;quot;&gt;Homepage&lt;/a&gt;&lt;br /&gt;
惯例彩蛋：&lt;a href=&amp;quot;https://hana-ui.moe/cn/hana-song&amp;quot;&gt;hana song&lt;/a&gt;&lt;br /&gt;
使用技能：  React.js, ES6, Scss......&lt;/p&gt;
&lt;h3&gt;BlogReworkPro&lt;/h3&gt;
&lt;p&gt;说明： 重构&lt;a href=&amp;quot;http://dtysky.moe/article/Create-2016_03_12_a&amp;quot;&gt;BlogRework&lt;/a&gt;，这是此Blog迎来的第四次重构了，和上一次的间隔比预期要早一些，不过这种事早点没啥坏处。这次重构主要是重写了前端、修了一些后端的BUG，跟进ES6，用Eslint和Flow约束代码规范，上了React最佳实践全家桶并且实现了完美的服务端渲染，加了Memory Cache，样式换成了less，DOM语义化也做了，构建工具也换成了gulp，也就是说，上一次遗留的Feature基本都搞定了。&lt;br /&gt;
规模： 中小&lt;br /&gt;
性质： 新技术学习实践&lt;br /&gt;
职位： 独立完成&lt;br /&gt;
进度： 完成&lt;br /&gt;
使用技能：  &lt;br /&gt;
Python, Node.js, Flask, React.js, Redux, MongoDB, Express.js......&lt;br /&gt;
项目源：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/BlogReworkPro&amp;quot;&gt;BlogRewrokPro&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;http://dtysky.moe/article/Create-2016_10_14_a&amp;quot;&gt;【React/Flask】BlogReworkPro-Rework the BlogRework&lt;/a&gt;&lt;br /&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;&lt;br /&gt;
&lt;a href=&amp;quot;http://dtysky.moe/article/Skill-2016_10_11_a&amp;quot;&gt;【React/Redux】深入理解React服务端渲染&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;http://dtysky.moe/article/Skill-2016_10_13_a&amp;quot;&gt;【Flask/React】此博客服务端的缓存实现&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;http://dtysky.moe/article/Skill-2016_10_14_a&amp;quot;&gt;【Less】实现可选参数以及各种autoprefixer&lt;/a&gt;&lt;br /&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;SEO。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;MoeNotes&lt;/h3&gt;
&lt;p&gt;说明： &lt;strong&gt;正在使用Redux + RXJS重构。&lt;/strong&gt;MoeNotes是一个简单的日记写作软件（当然，我也用其写一些不是特别复杂的小说和文章），不同于印象笔记、Onenote、Leanote等，它所要解决的问题仅仅是“本地展示”，也就是在本地管理你的日志文件，并提供一个类似于Onenote的分类体验。本软件使用Markdown作为笔记编写语言，每一篇日记都会以.md文件的形式保存到本地而不是数据库中，用户可以自行选择如何同步这些文章以及将它们同步到多少地方，完全不依赖于平台，这也是我编写本软件而不使用现成日记软件的一个主要的原因。当然，对文章和分类进行拖动排序也是被支持的。除此之外，本软件还支持“即写即看”，“专注写作”和“专注阅读”三种模式，也可以进行主题的切换和自定义，由于是基于web进行得开发，所以扩展起来也十分方便和简单。&lt;br /&gt;
规模： 中&lt;br /&gt;
性质： 造一个满足自己需求的轮子&lt;br /&gt;
职位： 独立完成&lt;br /&gt;
进度： 完成&lt;br /&gt;
使用技能：  &lt;br /&gt;
Javacript, Node.js, React.js, Electron, HTML, CSS, Grunt, Webpack, Jasmine......&lt;br /&gt;
项目源：&lt;br /&gt;
&lt;a href=&amp;quot;http://moe-notes.dtysky.moe&amp;quot;&gt;MoteNotes&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/MoeNotes&amp;quot;&gt;Github&lt;/a&gt;&lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;本地文件树管理。&lt;/li&gt;
&lt;li&gt;OneNote的分类体验，增强版Markdown支持。&lt;/li&gt;
&lt;li&gt;即使预览、专注写作和专注阅读模式。&lt;/li&gt;
&lt;li&gt;可定制主题。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;BlogRework&lt;/h3&gt;
&lt;p&gt;说明： 重构Blog为SPA。&lt;br /&gt;
规模： 小&lt;br /&gt;
性质： 新技术学习实践&lt;br /&gt;
职位： 独立完成&lt;br /&gt;
进度： 完成&lt;br /&gt;
使用技能：  &lt;br /&gt;
Python, Node.js, Flask, React.js, MongoDB, Express.js, Jade, CSS......&lt;br /&gt;
项目源：&lt;br /&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;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/BlogRework&amp;quot;&gt;BlogRewrok&lt;/a&gt;&lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;扩展Markdown文章解析以及数据库管理。&lt;/li&gt;
&lt;li&gt;后端Web服务器。&lt;/li&gt;
&lt;li&gt;前端界面。&lt;/li&gt;
&lt;li&gt;前端WEB服务器。&lt;/li&gt;
&lt;li&gt;SEO。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h2&gt;主要项目（工作II）&lt;/h2&gt;
&lt;h3&gt;激光雷达上位机&lt;/h3&gt;
&lt;p&gt;说明： 用于展示激光雷达的3D数据，包括实时显示、数据录制、数据回放。&lt;br /&gt;
职位： 负责人&lt;br /&gt;
进度： 基本功能完成&lt;br /&gt;
使用技能：&lt;br /&gt;
JavaScript, C++, Python, Electron, Node.js, React, Three.js, HTML, CSS, Webpack, Grunt
项目源：  闭源 &lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;整体功能分析，实现了UI的设计和所有功能的开发。  &lt;/li&gt;
&lt;li&gt;用Electron作为框架，使用web的方式开发了桌面应用。  &lt;/li&gt;
&lt;li&gt;编写了C++的Node.js扩展，提高了点云坐标系转换效率。   &lt;/li&gt;
&lt;li&gt;用Three.js完成了点云的绘制。  &lt;/li&gt;
&lt;li&gt;实现了数据、显示、控制的隔离，自己实现了事件驱动的设计，保证可维护性。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;无人机甲烷监控数据可视化平台&lt;/h3&gt;
&lt;p&gt;说明： 将后台数据库进行表格形式的显示，兼容WEB、iOS和安卓三个平台。&lt;br /&gt;
职位： 负责人&lt;br /&gt;
进度： 基本完成&lt;br /&gt;
使用技能：&lt;br /&gt;
JavaScript, Python, Flask, MySQL, HTML, CSS, PyJade, Node.js, React native, Object-C&lt;br /&gt;
项目源：  闭源 &lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;开发基于Flask的后端以及使用PyJade模板进行网页显示  &lt;/li&gt;
&lt;li&gt;开发了一版Object-C编写的APP，用于熟练iOS底层。   &lt;/li&gt;
&lt;li&gt;用React native开发了iOS和安卓的APP。  &lt;/li&gt;
&lt;li&gt;设计了美观的UI。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;PM25传感器数据监测平台&lt;/h3&gt;
&lt;p&gt;说明： 接手项目，开发一个数据监测平台，收集不同地方的传感器传回的数据，进行记录和分析&lt;br /&gt;
职位： 负责人之一&lt;br /&gt;
进度： 正在进行&lt;br /&gt;
使用技能：&lt;br /&gt;
JavaScript，Node， React， MongoDB， Grunt， Python， HTML， CSS&lt;br /&gt;
平台：&lt;br /&gt;
OSX（开发），Ubuntu（部署）&lt;br /&gt;
项目源：  闭源 &lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;开发基于Node.js的后端  &lt;/li&gt;
&lt;li&gt;开发基于React.js的前端，用Grunt管理。   &lt;/li&gt;
&lt;li&gt;包含数据库管理，数据下载，数据呈现等功能。  &lt;/li&gt;
&lt;li&gt;做了设计，美化了UI。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;无人机定位&lt;/h3&gt;
&lt;p&gt;说明： 寻找并实现无人机的一些定位方式&lt;br /&gt;
职位： 负责人&lt;br /&gt;
进度： 基本完成&lt;br /&gt;
使用技能：&lt;br /&gt;
Python&lt;br /&gt;
平台：&lt;br /&gt;
Windows（PC），Ubuntu（Ordroid）&lt;br /&gt;
项目源：  闭源 &lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;差分GPS的研究和应用。  &lt;/li&gt;
&lt;li&gt;激光定位的研究和应用。  &lt;/li&gt;
&lt;li&gt;UWB定位的研究和应用。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h2&gt;主要项目（大学）&lt;/h2&gt;
&lt;h3&gt;梦见星空之诗 - Aria der Freiheit und des Seins：&lt;/h3&gt;
&lt;p&gt;说明： 原创游戏。&lt;br /&gt;
规模： 超大&lt;br /&gt;
性质： 梦想&lt;br /&gt;
职位： 企划，监督，设定，剧本，程序&lt;br /&gt;
进度： 长期规划，剧本重构中&lt;br /&gt;
使用技能（已经）：&lt;br /&gt;
哲学（半吊子），设定，写作，编剧，Python，Ren&amp;apos;py&lt;br /&gt;
使用技能（预定）：&lt;br /&gt;
Unity3d，编译器设计，FPGA设计，商学&lt;br /&gt;
项目源：  闭源 &lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;游戏剧本解析器。  &lt;/li&gt;
&lt;li&gt;70万字剧本原稿。  &lt;/li&gt;
&lt;li&gt;游戏系统和界面（暂定）。 &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;体三维显示器：&lt;/h3&gt;
&lt;p&gt;说明： 一个分辨率较高的、由二维LED点阵旋转的三维显示器。&lt;br /&gt;
规模： 大&lt;br /&gt;
性质： 省级SRTP&lt;br /&gt;
职位： 独立完成&lt;br /&gt;
进度： 基本完成&lt;br /&gt;
使用技能：&lt;br /&gt;
PCB，FPGA，C，C#，Autocad&lt;br /&gt;
项目源：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/3D_Displayer_Controller&amp;quot;&gt;控制&lt;/a&gt;，&lt;a href=&amp;quot;https://github.com/dtysky/Led_Array&amp;quot;&gt;PCB&lt;/a&gt;，&lt;a href=&amp;quot;https://github.com/dtysky/3D_Displayer_Machine&amp;quot;&gt;机械&lt;/a&gt;&lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;学习Cadence套件，设计了一个由0603LED，PMOS，NMOS构成的120*114，密度为15.2d、cm^2的LED阵列；同时，完成了由触发器、锁存器、电源部分构成的此LED阵列的控制板，控制板为四层，走线密度较高。  &lt;/li&gt;
&lt;li&gt;学习使用FPGA，利用Altera的CycloneIV系列的器件，使用VHDL语言完成了：&lt;br /&gt;
1).DDR2控制器，完全由自己实现，并自己编写Testbench，利用Modelsim进行了批量数据验证，但缺少PHY的支持。&lt;br /&gt;
2).LED控制器，完全由自己实现，使用ROM进行了板上验证。&lt;br /&gt;
3).学习使用高速USB芯片cy68013，熟悉其slave-fifo模式，完成了它的FPGA控制部分，进行了板上验证。  &lt;/li&gt;
&lt;li&gt;学习使用C#语言，使用cy68013芯片的提供的.  net下的API，完成了其上位机。   &lt;/li&gt;
&lt;li&gt;学习C语言，完成了cy68013的固件部分设计。  &lt;/li&gt;
&lt;li&gt;机械部分的设计，设计了底座、联轴器，以及LED阵列的装载板；使用Autocad设计。  &lt;/li&gt;
&lt;li&gt;基于Matlab的三维切片。  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;游戏剧本解析器：&lt;/h3&gt;
&lt;p&gt;说明： 一个分离剧本和代码的解析器，用于将自己设计的DSL解析成Ren&amp;apos;py的脚本，提供扩展和输入插件。&lt;br /&gt;
规模： 中&lt;br /&gt;
性质： 个人项目&lt;br /&gt;
职位： 独立完成&lt;br /&gt;
进度： 基本完成&lt;br /&gt;
使用技能：  Python，Ren&amp;apos;py&lt;br /&gt;
项目源：  &lt;a href=&amp;quot;https://github.com/dtysky/Gal2Renpy&amp;quot;&gt;Gal2Renpy&lt;/a&gt;&lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;学习使用游戏引擎ren’py和Python，了解了各自特性。  &lt;/li&gt;
&lt;li&gt;DSL的形式继承自xml语言，自己完成了标记语言的解析，以及与ren’py脚本的映射,同时提供给用户自行扩展的接口，具有良好的可扩展性。  &lt;/li&gt;
&lt;li&gt;Sublime的插件，方便输入。  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;FPGA Image Library：&lt;/h3&gt;
&lt;p&gt;说明： 一个开源的FPGA图像处理库，将一些图像处理操作在FPGA上实现，每一个操作都会封装成一个模块，并拥有各自的软件测试与功能仿真，测试图片可以自由选取,并且设计为统一接口，流水化处理。&lt;br /&gt;
规模： 大&lt;br /&gt;
性质： 独立项目&lt;br /&gt;
职位： 独立完成&lt;br /&gt;
进度： &lt;a href=&amp;quot;http://fil.dtysky.moe&amp;quot;&gt;1.0版本发布&lt;/a&gt;，由于工作原因暂时中断= =&lt;br /&gt;
使用技能：  FPGA设计（Verilog, SystemVerilog），Python，图像处理 &lt;br /&gt;
项目源：  &lt;a href=&amp;quot;https://github.com/dtysky/FPGA-Imaging-Library&amp;quot;&gt;FPGA-Imaging-Library&lt;/a&gt; &lt;br /&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;Harris角点检测。  &lt;/li&gt;
&lt;li&gt;平移，缩放，镜像，裁剪，旋转，仿射变换。  &lt;/li&gt;
&lt;li&gt;预计加入直方图操作。  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;我的主页：&lt;/h3&gt;
&lt;p&gt;说明： 一个用于记录自己学习经验的博客。&lt;br /&gt;
规模： 小&lt;br /&gt;
性质： 个人项目&lt;br /&gt;
职位： 独立完成&lt;br /&gt;
进度： 完成&lt;br /&gt;
使用技能 HTML，CSS，JS&lt;br /&gt;
项目源：  不开源
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;学习使用pelican(基于jinjia2)，学习css、html、js，在VPS上搭建。  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;一个面向教学的单周期MIPS CPU：&lt;/h3&gt;
&lt;p&gt;说明： 一个MIPS CPU，单周期32bits，vivado搭建，模块化设计，设立了tcl文件简化预览。&lt;br /&gt;
规模： 小&lt;br /&gt;
性质： 实习项目&lt;br /&gt;
职位： 独立完成&lt;br /&gt;
进度： 完成&lt;br /&gt;
使用技能：FPGA设计, Verilog, Systemverilog, Python &lt;br /&gt;
项目源：  &lt;a href=&amp;quot;https://github.com/dtysky/SIMPLE_MIPS_CPU&amp;quot;&gt;SIMPLE_MIPS_CPU&lt;/a&gt;&lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;完成了ALU, REGFILE, CONTRLO_UNIT, DATAPATH, INST_MEM, DATA_MEM模块的设计与随机测试，并将它们拼接，完成了用于功能仿真的CPU。  &lt;/li&gt;
&lt;li&gt;完成了KEY2INST, SHOW_ON_LED模块的设计与测试，将它们拼接于以上模块中，设计了一台按键编码，用于CPU的板上测试。  &lt;/li&gt;
&lt;li&gt;tcl文件的建立，用户可以在vivado界面写source tcl文件来快速建立工程。  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;其他&lt;/h2&gt;
&lt;p&gt;欲戴皇冠，必承其重。&lt;/p&gt;
&lt;p&gt;一个人不可能被所有人理解，这几年遭受的攻击、嘲讽、中伤即从中来。  &lt;/p&gt;
&lt;p&gt;人要为其言论负责，表达的代价也即为此。&lt;/p&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;作为演员的时候，我们不可忘却愤怒。&lt;br /&gt;
作为观众的时候，我们不可忘却叹息。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我将倾尽所能去体验、去观测、去记录、去表达。&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>Friday, 14 Mar 2025 00:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2025.03.14 00:00:article/Create-MyResume</guid>
<category>简历</category>
</item>

<item>
<title>回顾2022</title>
<link>http://dtysky.moe/article/Life-2023_01_21_a</link>
<description>&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;p&gt;第一部分：&lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://www.bilibili.com/video/BV17R4y1a78z/&amp;quot;&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2023_01_21a/1.jpg&amp;quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;第二部分：&lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://www.bilibili.com/video/BV1wG4y1F7Wz/&amp;quot;&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2023_01_21a/2.jpg&amp;quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;文字版&lt;/h2&gt;
&lt;p&gt;站在一个永恒的视角，我们可以选择入戏去做个演员，也可以选择出离来当个观众。&lt;/p&gt;
&lt;p&gt;但是作为演员的时候，我们不可忘却愤怒；作为观众的时候，我们不可忘却叹息。&lt;/p&gt;
&lt;p&gt;大家好，我是瞬光。自15年大学毕业以来，每年的新年，我都会写一篇文章，来回顾过去一年的种种变化。&lt;/p&gt;
&lt;p&gt;而在这第八年，我决定首次尝试以视频的方式来替代文字，完成这次回顾。&lt;/p&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;p&gt;首先还是以工作开始，自我20年从支付宝离职入职微信以来，负责的基本是&lt;strong&gt;某项目的渲染部分改造&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;但是因为一些机遇的原因吧，很遗憾小组在年初散了，连着我就留下了一小半人。&lt;/p&gt;
&lt;p&gt;说起来这也是工作以来，小组第一次在我跑路之前散，不过我本身向来是一个没什么群体性的人，也没有太大的感觉，毕竟人生无常，无暇悲伤。&lt;/p&gt;
&lt;p&gt;不过小组散了的半年后，这个领域的势头反而起来了，当然还是世事无常，经历过蚂蚁上市前夕离职那件事，我对这些已经没什么感觉了。&lt;/p&gt;
&lt;p&gt;然后呢，个人的性格是无论面对什么境遇，都不会坐以待毙的那种，所以在那之后，我不断做了些尝试，比如：&lt;/p&gt;
&lt;p&gt;最终，我和尚且留下同事很快定了个新方向，和新的TL做了沟通后，完成了一个名为&lt;strong&gt;XR-FRAME&lt;/strong&gt;的项目。&lt;/p&gt;
&lt;p&gt;得益于之前重构渲染部分时的设计独立性，XR-FRAME直接复用了这部分，并且综合了以前的经验，最后效果还不错，依照惯例，我写了非常个人风格的&lt;strong&gt;入门指南&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;以及&lt;strong&gt;官方示例彩蛋&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;项目在十月底完成了上线，后续在不断更新，而我个人也因此在绩效方面获得了五星，至此职业生涯高绩效占比11/14。&lt;/p&gt;
&lt;p&gt;同时我也申报了T11晋升，虽然个人算是比较看淡了，但毕竟是三十岁前最后一次机会了。&lt;/p&gt;
&lt;p&gt;为自己选择的HARD模式人生OL点一个成就还是不错的，虽然现在这形势嘛这次历年最难...尽人事听天命就行吧。&lt;/p&gt;
&lt;h2&gt;技术&lt;/h2&gt;
&lt;p&gt;按照惯例，我每年都会在工作之外，学习一两种新技术扩展边界。今年开始我本来打算打消这个想法，因为精力有限嘛，技术在我这优先级已经下降了，不过还是有所产出。&lt;/p&gt;
&lt;p&gt;今年的技术产出主要是读书软件&lt;strong&gt;Awaken&lt;/strong&gt;，这是由于Kindle要退出中国市场，让我对所有的这种平台失去了信任，同时也找不到现有的很好的满足需求的方案：&lt;/p&gt;
&lt;p&gt;所以我花了两个多月的业余时间，同时也是抱着做一个Hybrid方案项目模板的想法，做了这个应用。&lt;/p&gt;
&lt;p&gt;最后完全满足了我多端同步和Kindle笔记导入的需求，在发布后惯例写了篇技术教程发在了&lt;strong&gt;博客和知乎&lt;/strong&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;吐槽一下苹果的开发体系真是灾难，还有亚马逊的同行们摸鱼真是令人...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;理想&lt;/h2&gt;
&lt;p&gt;既然我决定不再对技术投入过多精力，那么这部分精力总要投入到其他地方，也就是“理想”这部分。&lt;/p&gt;
&lt;p&gt;毕竟马上三十岁了，时不我待啊。&lt;/p&gt;
&lt;p&gt;从去年年底开始，包括正在完成的独立游戏，我脑海中逐渐有了几个项目的雏形，并在不断细化后开始了执行。&lt;/p&gt;
&lt;p&gt;首先是&lt;strong&gt;Project Tomorrow&lt;/strong&gt;，这是一个现实主义的独立游戏，目标是一个线性的像素解谜RPG。我在七月份完成了其第二版剧本，最终控制在了十万字左右，但技术MVP（最小验证版本）版本未达预期，不过整体推进还算及格吧。&lt;/p&gt;
&lt;p&gt;接着是&lt;strong&gt;Project Self&lt;/strong&gt;，这是一个对自己28岁记录的企划，最终完成于四月份，主要是写真集+一次TEG的访谈，记录了自己青年/少年/少女三个侧面的抽象，同时也算走出不敢面对镜头的自卑的开端，当然最终结果没有那么满意，这也成为了我后续自学摄影的起点。&lt;/p&gt;
&lt;p&gt;随后是&lt;strong&gt;Project Love&lt;/strong&gt;，依托于陌上花开发的一个交友贴，对自己过去人生做了简短总结，也是作为未来某个大项目的开端，不过虽然是做好了充分的劝退提示，但这个号称人均985+的平台，最后的一些评论还是有点跌眼镜，不过社会就是如此，也不太有所谓。&lt;/p&gt;
&lt;p&gt;之后是&lt;strong&gt;Project Totem&lt;/strong&gt;，这是在独立游戏陷入美术瓶颈后，想出的一个磨合美术的小项目。主要部分我的博客的改版，左侧换为WebGL绘制银河列车和少年/少女/青年，最后完成了技术改造但美术仍然卡住，认清了最终只能靠自己以后，我决定接下来提升艺术修养，自己做艺术总监管理外包，这也是上面那个读书软件的契机之一。&lt;/p&gt;
&lt;p&gt;最后就是&lt;strong&gt;二十九&lt;/strong&gt;这个项目，作为每年惯例的庆生作品，这次做了一次实验性尝试，第一部分是生日前夜的天桥唱歌实录的视频，歌是我改编自逼哥的某首歌还特意去学了声乐，最后因为天赋嘛，也没办法...第二部分呢则是基于第一部分的半虚构小说，也可以说是我目前完成度最高的私小说之一吧。&lt;/p&gt;
&lt;p&gt;同时这个二十九岁生日，也是第一次我和朋友出去旅游过的，还拍了个VLOG，其中第一次我会心笑了出来——&lt;/p&gt;
&lt;p&gt;整体来讲呢，虽然某些进展不达原先的预期，但也有一些额外的收获，算是还不错吧。&lt;/p&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;p&gt;我的人生最终是为了理想，而这理想的本质无非就是记录和表达，而记录表达又需要体验，在体验中收获观察和反省，积累某种厚度。&lt;/p&gt;
&lt;p&gt;“体验”换个说法，就是“生活”。而相比于前面的工作、技术、理想，今年和以往相比改变最多的，大致就是生活了。&lt;/p&gt;
&lt;p&gt;以前的我总是对生活的不屑或者畏惧，因为我觉得生活很浪费时间，同时在从小漫长的缺乏亲情和打压式教育下，我也不知道什么是生活，也认为自己不配拥有生活。&lt;/p&gt;
&lt;p&gt;这一切在前年开始有个转折，而在去年年初，我给自己立下的最重要的KPI之一，就是“对自己好一点”。&lt;/p&gt;
&lt;p&gt;而通过一系列的项目和经历，我也终于和自己、和生活达成了某种程度的和解，不再委屈自己。&lt;/p&gt;
&lt;p&gt;首先，作为不委屈自己的基础，我彻底放弃了在这个阶段买房。&lt;/p&gt;
&lt;p&gt;虽然终归是完全靠自己攒够了广州城区的首付，但我已经完全跳出了传统的规训，选择活在当下和自己所预期的未来，那么房产自然也就无所谓了，于是我瞬间就有了很多资本去体验生活。&lt;/p&gt;
&lt;p&gt;当然我对物质的欲望本身是挺低的，但搞创作嘛，不能太有钱，也不能太穷，太穷的话会有很多额外的麻烦，所以我也不是很排斥努力工作攒钱。而且我需要工作来帮我维持和世界的链接。&lt;/p&gt;
&lt;p&gt;认真生活的第一步是在年初的时候置办了&lt;strong&gt;全新的行头&lt;/strong&gt;，去COS买了几身比较休闲的新衣服，改变了自己一贯的宅T着装风格。&lt;/p&gt;
&lt;p&gt;然后是虽然有点晚，但终于直面了自己的痘坑，延续五年前的疗程，再做了&lt;strong&gt;三次点阵激光&lt;/strong&gt;，最后一次还录了VLOG，真是太他妈得痛了...只能说尽人事了，剩下的也没办法，倒霉嘛，咋办呢。&lt;/p&gt;
&lt;p&gt;肉体层面解决了痘坑，精神层面呢则是&lt;strong&gt;ADHD&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;在经过了很多了解后，我确认了去年底的双相障碍是误诊，自恋的表象也是错误的，实际上我是个非常典型的ADHD，只是因为我本身高功能加上求知欲强，兴趣满足了ADHD的百倍专注，所以从小掩盖了一些副作用罢了。&lt;/p&gt;
&lt;p&gt;但无法控制的走神分心、三分钟热度、旺盛的精力、轻度阅读障碍、容易细节失误、RSD（拒绝敏感性焦虑）等都完美诠释了这点，但有些我已经靠自己强行维持的强计划性克服了，不过实在不行的时候未来也会考虑嗑药。&lt;/p&gt;
&lt;p&gt;期间还被拉进去了一个ADHD线下群，参加过活动，明确其实自己确实是A中的幸运儿了。&lt;/p&gt;
&lt;p&gt;当然ADHD本质上不是一种病，只是神经特征比较特殊，其创造性和能动性是对我有利的，不服输永不言弃的特质也是很好的。当彻底搞清楚后，我在和人交际时都会说清楚：&lt;/p&gt;
&lt;p&gt;我总是提到自己并非出于自恋，而是容易走神需要拉回注意力，所以敌意也少了许多。此外呢，我还有一些以前没有做出过的努力，就是定下了一个原则。&lt;/p&gt;
&lt;p&gt;我目前对于任何状况，尤其是交际，都坚持尽人事，但顶格事不过三、然后听天命的原则。借此我基本完全消除了RSD带来的副作用，生活轻松了不少。当然确实会失去一些可能性，但相比于触发RSD还是划算得多。&lt;/p&gt;
&lt;p&gt;说到交际，交际方面对比去年有所收敛，因为觉得认识太多人也没啥意义，就是平台上申请我的或者线下活动见见，主要目的也从“认识人”变成了“尝试某种活动”，反正真能聊得来的还是那几个老朋友。&lt;/p&gt;
&lt;p&gt;对了这里要提一嘴交友的前提是互相尊重，陌生人谁都不欠谁的，但似乎很多人并不懂得这个道理。&lt;/p&gt;
&lt;p&gt;交际活动还是挺丰富的，尤其是去做了一些之前出于ADHD不敢，或是觉得太过生活化的尝试。按时间顺序大概过一下吧：&lt;/p&gt;
&lt;p&gt;第一次剧本杀就打了《须臾》&lt;/p&gt;
&lt;p&gt;购置了划船机和赛车游戏外设&lt;/p&gt;
&lt;p&gt;尝试滑冰：&lt;/p&gt;
&lt;p&gt;学会了游泳：&lt;/p&gt;
&lt;p&gt;万青演唱会：&lt;/p&gt;
&lt;p&gt;大学舍友的婚礼，也是我们四个中的第一个走入下一个人生阶段的：&lt;/p&gt;
&lt;p&gt;同时肉体和精神方面和解后，我开始尝试了之前从未接触过的创作媒介——影像。&lt;/p&gt;
&lt;p&gt;我从七月开始，前后购置了大疆MINI2、大疆OM5、Insta360、索尼A7M4等设备，开始&lt;strong&gt;学摄影和旅游，尝试拍摄VLOG和照片&lt;/strong&gt;，感觉慢慢有所上手。&lt;/p&gt;
&lt;p&gt;其中最为满意的应该是我终于掌握了自拍的精髓，拍出了几张满足的&lt;strong&gt;几张自拍&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;其中乱入的这张是倒霉头上长了个奇怪的包，去做手术割了。&lt;/p&gt;
&lt;p&gt;还有一些其他作品：&lt;/p&gt;
&lt;p&gt;和尝试的一些VLOG：&lt;/p&gt;
&lt;p&gt;十一因为疫情的逃命式湘西三日游，非常有戏剧性，尤其悼念一下最终在张家界炸机的MINI2...&lt;/p&gt;
&lt;p&gt;当然输出免不了输入，旅游这些外部尝试算一部分，另一部分则是如常从一些作品中获取的。&lt;/p&gt;
&lt;p&gt;首先是&lt;strong&gt;舞台剧和Live&lt;/strong&gt;，今年我保持了一个月一两部的频率，因为没啥别的花钱欲望买的都是一等座，观感大都挺好，每部完成都会写一个简短总结，收获颇丰：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;电影和纪录片&lt;/strong&gt;也看了一些，审美倾向也有了明确的转向，其中印象最深的应该还是周浩和徐童的作品吧，尤其是《游民三部曲》。&lt;/p&gt;
&lt;p&gt;我终于理解了从小自己的经历应该被归类为哪一类群体，明白了“留守儿童和流民”的一体两面，后续参加的康乐新村公益活动，也让我更加了解了自己这种“异乡人”情节的来源。&lt;/p&gt;
&lt;p&gt;题外话徐童和唐小燕前两天宣布结婚了，只能说都是能完全超越世俗成见的人，活明白了，我自认做不到，只能佩服祝福。&lt;/p&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;p&gt;对了还有参加的一次GGJ，可惜今年过年太早，没空去参加：&lt;/p&gt;
&lt;p&gt;对了提一嘴黑柿子，作为一路看着走过来的INFP同类，感慨进步并祝福。&lt;/p&gt;
&lt;p&gt;综合来说，我对作品已经&lt;strong&gt;不再过多关注所谓的剧情大纲，而是更关注演绎形式&lt;/strong&gt;，这也和我个人的思考有关。&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;p&gt;毕竟已经见识过太多人的贪婪，看清了大多人在亲密关系中对我而言，都是物质或精神上的纯消耗罢了，认定宁缺毋滥，没有了这个消耗精力自然回归了。&lt;/p&gt;
&lt;p&gt;在认清了权责关系，将其运用到一切人和人、人和社会、人和国家的关系后，我彻底拒绝了一切PUA，再也没有人能够从精神上控制我，我也不再在乎任何无聊的指责——&lt;/p&gt;
&lt;p&gt;你臆想的那些偏见，你虚构的那个人只是你个人情绪的一个投射，又不是我，关我屁事，凭什么我要承担你们的期望？&lt;/p&gt;
&lt;p&gt;同时在彻底脱离原生家庭数年后，完全不依靠家庭自己走到现在的我，终于能够以一个旁观的视角认识到了父母的局限性，但理解不意味着原谅，拥有了自己的正当的信念后，再也没有什么能够道德绑架我。&lt;/p&gt;
&lt;p&gt;太多父母自小没给过孩子正经的教育关爱，却编织出一副自己很伟大的幻象。那种传统规训下特有的自私和贪婪，被“父母”这一概念所升华，就自以为拥有了某种无限的权力。&lt;/p&gt;
&lt;p&gt;这在我看来显然是狗屁不通的，所以我只留下了最后的发言：我已经付出了足够的代价走出来拥有了自己的选择，而你们的代价必须要自己承担，别把自己的失败和无聊寄托在别人身上。&lt;/p&gt;
&lt;p&gt;也因此，我彻底明白了比起所谓烟火气带来的所谓岁月静好，我更厌恶失去自由滑向庸俗。从秩序善良彻底转向到混乱善良后，整体来看我比前年更好相处，但比去年更加不好相处了，也算一种螺旋上升吧。&lt;/p&gt;
&lt;p&gt;当然比起年少时那种不知恐惧和伤害为何物的鲁莽，现在的愤怒和叹息完全是我个人的选择，从表象上看起来一致的行为，内在却是完全不同的。&lt;/p&gt;
&lt;p&gt;在理解了所谓“欲戴王冠，必承其重”，“一个人不可能被所有人理解”之后，我选择了承受代价，也因此选择才有了意义。&lt;/p&gt;
&lt;p&gt;“清醒地认识到代价，以勇气去做选择，而不是怯懦地躲在背后以贪婪的目光索取，努力达到在当下的言行一致，和时间尺度的一惯性”——这也是我现在最欣赏的人的特质了。&lt;/p&gt;
&lt;p&gt;但当然为了保持独立性，这种人不招大部分人待见也是必然的，不过这也是代价的一环，人生嘛，就是如此，开看点，你不能什么都要，既要又要。&lt;/p&gt;
&lt;p&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;/p&gt;
&lt;p&gt;题外话，我彻底明白了“实事求是，敬畏自然”的重要性，现在一看到“人定胜天，为了所有人，不惜一切代价”之类的口号，就知道要坏事了，一定会滑向最坏的结果。&lt;/p&gt;
&lt;p&gt;所以可以看到，和其他的否定之否定一样，我最终选择回归了我舍弃的一部分“中二”，同时也将对年轻人的包容作为了最后的乐观之一，千万不要把未来拱手相让。&lt;/p&gt;
&lt;p&gt;但我这种乐观的底线是——年轻人绝对不能过早成熟，或者说自以为成熟。我能认可象牙塔里的理想主义，也能认可历尽千帆的成熟，但绝不能接受半吊子。没有切身经历就不能侃侃而谈，该有勇气的时候就不要迂腐，没有入世，何谈出世。&lt;/p&gt;
&lt;p&gt;以上综合来讲，就是人格达到了“完全之形”，拥有了不随环境变化的强硬核心，这也是坚持后续项目的必要条件。&lt;/p&gt;
&lt;p&gt;那么差不多了，就到这......&lt;/p&gt;
&lt;p&gt;唉，对...还有一点，最后也是最重要的，对“命运”的理解，而所谓命运——&lt;/p&gt;
&lt;p&gt;命运......&lt;/p&gt;
&lt;p&gt;过去，既已确定，则悔恨即为罪恶  &lt;/p&gt;
&lt;p&gt;未来，既已注定，则恐惧即为罪恶  &lt;/p&gt;
&lt;p&gt;转场到少年H&lt;/p&gt;
&lt;h2&gt;少年H的独白&lt;/h2&gt;
&lt;p&gt;（B站投稿考虑分2P）&lt;/p&gt;
&lt;p&gt;（边鼓掌边说）&lt;/p&gt;
&lt;p&gt;过去，既已确定，则悔恨即为罪恶。&lt;br /&gt;
未来，既已注定，则恐惧即为罪恶。  &lt;/p&gt;
&lt;p&gt;那么下一句应该是——&lt;/p&gt;
&lt;p&gt;现在，既已决定，则怠惰即为罪恶。&lt;/p&gt;
&lt;p&gt;话说得还是那么漂亮。&lt;/p&gt;
&lt;p&gt;不过这次你，至少没有再陷入那种怯懦的纠结，或者说，还是终于找回了某些重要的东西？&lt;/p&gt;
&lt;p&gt;值得赞扬，可喜可贺。&lt;/p&gt;
&lt;p&gt;但既然都说到“命运”了，还是交给我这个背后旁观者更合适吧。&lt;/p&gt;
&lt;p&gt;（起身，切换场景？）&lt;/p&gt;
&lt;p&gt;还记得你以前最为坚信的那句话吗，不错，就是那句“我命由我不由天”。&lt;/p&gt;
&lt;p&gt;你蔑视命运，相信“人定胜天”、“我即为世界”、“信念可以超越一切”，以节制和分寸为耻，想以此超越虚无。&lt;/p&gt;
&lt;p&gt;你瞧不起那些按部就班的人，瞧不起那些穷尽算计的人，瞧不起那些怯懦贪婪的人。&lt;/p&gt;
&lt;p&gt;你厌恶失败，你敌视失败，你畏惧失败，你不允许自己失败。&lt;/p&gt;
&lt;p&gt;所以你拼命，追逐，在无数次对自我和周围的伤害后，你终于换取了所谓的高人一等。&lt;/p&gt;
&lt;p&gt;但在命运数次的戏弄后，你终于明白了，那只不过是你不知伤害和恐惧为何物时，所产生一种傲慢罢了。&lt;/p&gt;
&lt;p&gt;后来的你，明白了人不一定能胜天，便和你瞧不起的那些人一样，多了些怯懦和贪婪。&lt;/p&gt;
&lt;p&gt;你想苟活于世，尽可能对这个世界索取，即便是盈余，也要让自己没有遗憾。&lt;/p&gt;
&lt;p&gt;但终于有一天，你还是觉察到了无聊，觉察到了这贪婪和怯懦背后的空虚，于是你又开始怀念。&lt;/p&gt;
&lt;p&gt;怀念那不知伤害与恐惧为何物的自己，怀念那个瞧不起贪婪和怯懦的自己。&lt;/p&gt;
&lt;p&gt;但这时候你又会想：你所瞧不起的，究竟是什么呢？&lt;/p&gt;
&lt;p&gt;是某一个人，是某一群人，还是某种导致这一切现状的，不可说的某种存在呢？&lt;/p&gt;
&lt;p&gt;还记得异域镇魂曲中的那个触及内核的提问吗？&lt;/p&gt;
&lt;p&gt;“究竟是什么，可以改变一个人的本质。”&lt;/p&gt;
&lt;p&gt;这个问题很对你的胃口不是吗？过于容易被崇高的人和事所感染的你，从小一直强制让自己保持怀疑，下意识用理性质疑一切，来避免被情绪利用。&lt;/p&gt;
&lt;p&gt;你讨厌崇拜，也讨厌被崇拜，因为你认为崇拜意味着神化，神化和矮化又是一体两面，都意味着无法看透表象后的真相。&lt;/p&gt;
&lt;p&gt;去崇拜一个人，意味着永远成不了那个人；而被崇拜，也意味着对方永远成不了自己。&lt;/p&gt;
&lt;p&gt;你希望更多人能成为他们崇拜的那个人，哪怕是成为始终保持质疑的自己。希望大家都能够明白自己选择的代价，心甘情愿承担责任。&lt;/p&gt;
&lt;p&gt;但结果又怎么样呢？你发现太多人都并不想为自己负责，只想甩锅让别人为自己负责而已。&lt;/p&gt;
&lt;p&gt;得到了觉得理所应当，没得到就怨天尤人。神化一些，贬低一些，打压一些，整天扣帽子，无视根源互相伤害，祈求有个神清算这个那个。&lt;/p&gt;
&lt;p&gt;这世上那么多悲剧，最终看起来却像是笑话一样，不都因为如此吗？&lt;/p&gt;
&lt;p&gt;所以你瞧不起他们，你觉得这世界就是一个粪坑，恨不得真的有个神来净化这一切，但这种想法和他们又有什么本质区别呢？&lt;/p&gt;
&lt;p&gt;本应导向真理的怀疑，却将你导向了傲慢。认清现实并未让你安宁，却让你更加憎恶。&lt;/p&gt;
&lt;p&gt;好在你从未停止过追问，最终你还是明白了——&lt;/p&gt;
&lt;p&gt;有人在就必然会有问题，而问题的根源就是人本身，所谓的彻底解决问题，本质上就是解决所有人。&lt;/p&gt;
&lt;p&gt;这也就是为何历史上大多的人定胜天，最终都会导致灾难性后果的根本原因。&lt;/p&gt;
&lt;p&gt;那么回到问题：究竟是什么可以改变一个人的本质呢？&lt;/p&gt;
&lt;p&gt;答案自然是：为任何信念所付出的任何选择以及行动，都可以改变一个人本质，同时也决定了一个人的本质。&lt;/p&gt;
&lt;p&gt;一个人在当下是否言行一致，在时间尺度上是否有迹可循，是否保持了某种程度的一惯性，都确立了他一生的基调和底色。&lt;/p&gt;
&lt;p&gt;但显然并不是所有人都拥有等同的选择，有的人拥有比别人多得多的选择。&lt;/p&gt;
&lt;p&gt;现在的你自然是拥有选择的，或者说在过去抓住时运的某些选择后，你拥有了更多的选择。&lt;/p&gt;
&lt;p&gt;但你认为自己和至少那些人，那些你现在瞧不起的，只知道窃取权利不知道承担责任的人不同。&lt;/p&gt;
&lt;p&gt;你认为这种选择的权利，本身就包含着太多的代价，这些直接或间接的代价，都意味着更多的责任。&lt;/p&gt;
&lt;p&gt;于是你终于懂得了“归还”，懂得了他人、社会乃至世界，并不是应该无缘无故理所当然被你索取的。&lt;/p&gt;
&lt;p&gt;同时你也察觉到了，原来自己过去一直做的那些无偿的开源、文章、评论、游戏、公益，本就属于“归还”的一部分。&lt;/p&gt;
&lt;p&gt;只是你的能力太过渺小，所以总是感到痛苦，而现在你终于承认了自己并非无所不能，这也不是什么值得羞耻的事。&lt;/p&gt;
&lt;p&gt;抛开那些规训，抛开那些期望，抛开那些诅咒，抛开那些扭曲的祝福。&lt;/p&gt;
&lt;p&gt;有神性，也有人性，有理智，也有激情；有勇气，也会恐惧，有行动，也会疲惫。这才是真正的你不对吗？&lt;/p&gt;
&lt;p&gt;无论是过去因察觉不到危险的的桀骜不驯，还是现在这明确代价后的选择，都没有那么崇高不是吗？&lt;/p&gt;
&lt;p&gt;我明白的，我们都明白的。&lt;/p&gt;
&lt;p&gt;究其本质，是因为你无法忍受那种枯燥和乏味的人生，就算是现在看起来的精彩纷呈，也过不了多久就会被你厌倦。&lt;/p&gt;
&lt;p&gt;正如你在那篇七年前的小说中所述，你所追求的东西是某种意义，而这东西是无法被贴上价格标签，是无法用金钱购买的。&lt;/p&gt;
&lt;p&gt;意义并不在于包罗万象的怯懦观测，也不在于损人利己的贪婪索取，而在于在面对不可逃避的命运时，以清醒和勇气做出的任一行动的回响。&lt;/p&gt;
&lt;p&gt;那就继续下去，尝试，获得，归还，一次又一次，一次又一次。&lt;/p&gt;
&lt;p&gt;在厌倦了所有的那一天，在肉身灭亡的那一天到来之前，坚持下去。&lt;/p&gt;
&lt;p&gt;如果这就是你的期望，如果这就是你的所求。&lt;/p&gt;
&lt;p&gt;如果你认同这一切的代价，如果你相信这就是唯一的选择。&lt;/p&gt;
&lt;p&gt;毕竟无论是从你，还是我，亦或是此次没有出境的她看来——&lt;/p&gt;
&lt;p&gt;这种生存方式，在我们的审美中，都是最为美丽动人的。&lt;/p&gt;
&lt;p&gt;只是，唯有一点你断然不可忘却：&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>Saturday, 21 Jan 2023 00:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2023.01.21 00:00:article/Life-2023_01_21_a</guid>
<category>新年</category>
<category>回顾</category>
<category>事业</category>
<category>理想</category>
<category>生活</category>
</item>

<item>
<title>Awaken-基于Hybrid方案和WebDAV的全平台开源阅读软件</title>
<link>http://dtysky.moe/article/Create-2022_12_27_a</link>
<description>&lt;p&gt;在本篇文章中，我将从技术选型开始，分享我在开发这个阅读软件过程中的一些经验和心得，也算是对个人技术道路的又一个路标。&lt;/p&gt;
&lt;h2&gt;前言&lt;/h2&gt;
&lt;p&gt;今年六月，亚马逊宣布Kindle将在次年退出中国市场，听到这个新闻的我开始寻找它的替代品，同时由于在思考后彻底失去了对内容平台的信任，我对这个替代品定了以下要求：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;存储不依赖于平台。&lt;/li&gt;
&lt;li&gt;能够跨平台和设备共享，要求支持桌面/安卓/iOS全端。&lt;/li&gt;
&lt;li&gt;允许笔记和进度同步。&lt;/li&gt;
&lt;li&gt;笔记协议开放，能够从Kindle导入。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;带着这些要求寻觅了许久后，我并未找到完全满足的软件。作为一个合格的程序员，我便自然有了自己去实现它的想法，这同时也可以作为我的最终项目《Project Journey》预热。但由于事情太多同时离Kindle完全停止运营为时尚早，我并未立即开始这个项目，而再次启动它的契机，则是九月份的另一个项目。&lt;/p&gt;
&lt;p&gt;在延续创作完去年的独立游戏《Project Tomorrow》的第二版剧本后，我陷入了美术的困境，为了避免一开始就把事情搞砸，我启动了一个另一个比较小的博客改版项目《Project Totem》来磨合和美术的相性，但很遗憾最终失败了（题外话也因此我打消了相当的找个美术对象、或者说合作伙伴对象的想法）。但无论现状如何，项目总要继续推进，于是我最终决定提高自己的艺术修养，让自己继程序、剧本后也成为美术总监把控全局，便于之后找外包。而这个设想前提则离不开读书，所以正好借这个机会完成这个读书软件。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我知道以上思维路径看起来很离谱，但我确实是这么想的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;从九月底开始，我便启动了这个命名为《Awaken》的项目，花了近两个月的业余时间，我终于将其完成。&lt;/p&gt;
&lt;p&gt;项目开源在此：&lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://github.com/dtysky/Awaken&amp;quot;&gt;https://github.com/dtysky/Awaken&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;功能介绍在此：&lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://www.bilibili.com/video/BV1uD4y1j79i/&amp;quot;&gt;Awaken-开源跨平台多端同步阅读软件&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;技术选型&lt;/h2&gt;
&lt;p&gt;经过这么多年的开发，我逐渐明白了技术选型的重要性。看起来轻松的方案最后可能会踩更多的坑，而看起来麻烦的方案可能却是弯路最少的，反而会达到稳定和效率的平衡。技术选型的前提是需求，而由前言中提到的需求可得最影响决策的两个需求是——“同步”和“全平台”。&lt;/p&gt;
&lt;p&gt;“同步”决定了C/S架构，继而需要考虑服务端存储和多端同步方案；而“全平台”则决定了客户端需要一种跨端方案。&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;/p&gt;
&lt;p&gt;综合权衡后我选择了第二种方案，那么接下来的问题只有一个了——如何选择存储方案。这个没什么说的，我选了&lt;strong&gt;WebDAV&lt;/strong&gt;：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;WebDAV （Web-based Distributed Authoring and Versioning） 一种基于 HTTP 1.1协议的通信协议。它扩展了HTTP 1.1，在GET、POST、HEAD等几个HTTP标准方法以外添加了一些新的方法，使应用程序可对Web Server直接读写，并支持写文件锁定(Locking)及解锁(Unlock)，还可以支持文件的版本控制。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;目前国外支持WebDAV的网盘有不少，国内的话就坚果云吧，我也一直用的这个。&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;Flutter，移动端效果应该不错，桌面端残废，开发效率中等吧。&lt;/li&gt;
&lt;li&gt;ReactNative，emmm...不想再踩一次坑，桌面端也差不多残废。&lt;/li&gt;
&lt;li&gt;Hybrid分层方案，在Webview跑的JS前端 + 客户端通过JSB/XHR拦截实现的后端，优点是能充分利用JS生态，代价是效果受到Webview约束，仍然要实现不同客户端的JSB拦截。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;其实不用再多分析大家也能看出来了，综合效果和开发成本，只有第四种方案是可行的：Hybrid本身就是一种业界早已成熟的方案，没有太多坑，不会出现原理上难以解决的工程问题，而且能充分利用JS生态也省去了很多功夫，我是没兴趣为了这么个次要项目重新造一些轮子。&lt;/p&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/create-2022_12_27a/1.png&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;如图，这里我按照一个过时前端老人的习惯，选择了&lt;code&gt;typescript&lt;/code&gt;作为主要开发语言，&lt;code&gt;webpack&lt;/code&gt;作为构建工具，使用&lt;code&gt;dev-server&lt;/code&gt;做为开发服务器，发布时稍微改动做下打包就行。同时为了在开发阶段测试&lt;code&gt;webdav&lt;/code&gt;协议，我写了&lt;code&gt;webdav.server.js&lt;/code&gt;在本地开了个服务。&lt;/p&gt;
&lt;p&gt;代码本体在&lt;code&gt;src&lt;/code&gt;下，&lt;code&gt;interfaces&lt;/code&gt;中包括后端接口协议和书籍同步接口协议的定义，&lt;code&gt;backend&lt;/code&gt;中是不同平台下后端的具体实现，&lt;code&gt;frontend&lt;/code&gt;最后则是具体的前端逻辑。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;platforms&lt;/code&gt;是不同平台下的项目工程。在开发阶段，我用&lt;code&gt;dev-server&lt;/code&gt;开个支持hot-load的本地服务器，以不同平台的Webview打开本地Url来方便调试；在发布阶段，我将产物构建到三个平台工程的指定目录下，再以后续会提到的手段加载。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;test&lt;/code&gt;中是提供测试的一些书籍和Kindle导出的笔记。&lt;/p&gt;
&lt;h2&gt;后端实现&lt;/h2&gt;
&lt;p&gt;客户端的后端部分主要负责通过一致的接口协议，将Native基础能力暴露给Webview前端使用。  &lt;/p&gt;
&lt;p&gt;在代码接口上，我在&lt;code&gt;src/interfaces/IWorker&lt;/code&gt;中定义了接口协议，并在&lt;code&gt;src/backend&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;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;TBaseDir&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;Books&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;Settings&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;None&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;nx&amp;quot;&gt;type&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;TToastType&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;info&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;warning&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;error&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;kr&amp;quot;&gt;interface&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;IFileSystem&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;readFile&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;filePath&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;encoding&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;utf8&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;binary&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;baseDir&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;TBaseDir&lt;/span&gt;&lt;span class=&amp;quot;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;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;nx&amp;quot;&gt;ArrayBuffer&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;writeFile&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;filePath&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;content&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;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;nx&amp;quot;&gt;baseDir&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;TBaseDir&lt;/span&gt;&lt;span class=&amp;quot;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;k&amp;quot;&gt;void&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;removeFile&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;filePath&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;baseDir&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;TBaseDir&lt;/span&gt;&lt;span class=&amp;quot;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;k&amp;quot;&gt;void&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;readDir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;dirPath&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;baseDir&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;TBaseDir&lt;/span&gt;&lt;span class=&amp;quot;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;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;nx&amp;quot;&gt;isDir&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;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;createDir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;dirPath&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;baseDir&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;TBaseDir&lt;/span&gt;&lt;span class=&amp;quot;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;k&amp;quot;&gt;void&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;removeDir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;dirPath&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;baseDir&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;TBaseDir&lt;/span&gt;&lt;span class=&amp;quot;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;k&amp;quot;&gt;void&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;exists&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;filePath&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;baseDir&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;TBaseDir&lt;/span&gt;&lt;span class=&amp;quot;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;kr&amp;quot;&gt;boolean&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;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;IWorker&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;IFileSystem&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;loadSettings&lt;/span&gt;&lt;span class=&amp;quot;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;nx&amp;quot;&gt;ISystemSettings&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;saveSettings&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;settings&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;ISystemSettings&lt;/span&gt;&lt;span class=&amp;quot;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;k&amp;quot;&gt;void&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;selectFolder&lt;/span&gt;&lt;span class=&amp;quot;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;kt&amp;quot;&gt;string&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;selectBook&lt;/span&gt;&lt;span class=&amp;quot;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;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;nx&amp;quot;&gt;selectNote&lt;/span&gt;&lt;span class=&amp;quot;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;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;nx&amp;quot;&gt;showMessage&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;msg&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;kt&amp;quot;&gt;TToastType&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;title?&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;Promise&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;void&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;setBackground&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;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;g&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;b&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;Promise&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;void&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;onAppHide&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;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;o&amp;quot;&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;nx&amp;quot;&gt;getCoverUrl&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;book&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IBook&lt;/span&gt;&lt;span class=&amp;quot;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;kt&amp;quot;&gt;string&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;/pre&gt;&lt;/div&gt;


&lt;p&gt;接口的方法名都很明显了，不做过多解释。接下来要做的就是在各端实现这个&lt;code&gt;IWorker&lt;/code&gt;接口。&lt;/p&gt;
&lt;h3&gt;桌面端&lt;/h3&gt;
&lt;p&gt;在桌面端，基于浏览器的方案有不少，比如最广为人知的&lt;code&gt;Electron&lt;/code&gt;，还有类似的&lt;code&gt;CEF&lt;/code&gt;等，其基本都是打包了一个&lt;code&gt;Chromium&lt;/code&gt;进去，在开发简单、一致性强、兼容性强等优点下，也有安装包大小和内存开销等为人诟病的代价。&lt;/p&gt;
&lt;p&gt;一开始我是准备直接用&lt;code&gt;Electron&lt;/code&gt;的，但由于其只支持桌面，想偷懒的我便不由自主得想：“都2022年了，这么热衷造轮子的前端业界，不会还没有能直接跨所有端的方案吧？”虽然答案仍然确实是没有，但却意外发现了一个框架——&lt;code&gt;Tauri&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Tauri&lt;/code&gt;是基于&lt;code&gt;Rust&lt;/code&gt;和&lt;code&gt;Webview&lt;/code&gt;的混合应用开发框架，其目前支持全桌面平台，并计划支持客户端（当然遥遥无期）。其优势是利用系统原生的Webview（不错桌面系统也有Webview），包体积很小并且内存开销会小一些，但相对代价就是很难利用&lt;code&gt;Node&lt;/code&gt;生态，并且可能存在平台一致性问题，在某些低版本操作系统不支持。&lt;/p&gt;
&lt;p&gt;经过权衡，最终我选择了&lt;code&gt;Tauri&lt;/code&gt;，因为这个应用并不需要什么扩展逻辑，只需要基本的文件系统、提示、桌面选择器等等基本能力，而这些它都有官方支持。接下来，就让我们看看怎么在桌面端实现这个接口。&lt;/p&gt;
&lt;h4&gt;FileSystem&lt;/h4&gt;
&lt;p&gt;首先是接口中的文件系统，看到方法名便可以知道它们本质上就是对本地文件的存取。这个在&lt;code&gt;Tauri&lt;/code&gt;中很简单，其官方提供的库&lt;code&gt;@tauri-apps/api&lt;/code&gt;中就有相关能力，只需要将其引入并在&lt;code&gt;tauri.conf.json&lt;/code&gt;中的&lt;code&gt;tauri.allowlist&lt;/code&gt;中配置好&lt;code&gt;fs&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;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;nx&amp;quot;&gt;from&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;@tauri-apps/api&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;nx&amp;quot;&gt;readFile&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;filePath&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;encoding&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;utf8&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;binary&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;baseDir&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;TBaseDir&lt;/span&gt;&lt;span class=&amp;quot;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;fp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&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;nx&amp;quot;&gt;processPath&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;filePath&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;baseDir&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;encoding&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;utf8&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;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;readTextFile&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;base&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;dir&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;base&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;Promise&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&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;&amp;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;await&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;nx&amp;quot;&gt;readBinaryFile&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;base&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;dir&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;base&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;/pre&gt;&lt;/div&gt;


&lt;p&gt;注意到&lt;code&gt;baseDir&lt;/code&gt;这个参数，他指定了当前操作路径相对的目录，这里我用&lt;code&gt;TBaseDir&lt;/code&gt;指定，&lt;code&gt;Books&lt;/code&gt;表示用户指定的书籍目录，&lt;code&gt;Settings&lt;/code&gt;
则是用户个人配置目录，&lt;code&gt;None&lt;/code&gt;代表传入绝对路径。当然这些目录在不同平台下的表现不一致，在桌面端由于能够允许用户自己指定文件夹，所以&lt;code&gt;Books&lt;/code&gt;是用于配置的，&lt;code&gt;Settings&lt;/code&gt;则是&lt;code&gt;appDir&lt;/code&gt;。&lt;/p&gt;
&lt;h4&gt;其他接口&lt;/h4&gt;
&lt;p&gt;文件系统之外就是其他接口了，其中：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;loadSettings&lt;/code&gt;和&lt;code&gt;saveSettings&lt;/code&gt;只是存取&lt;code&gt;Settings/settings.json&lt;/code&gt;文件。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;selectFolder&lt;/code&gt;、&lt;code&gt;selectBook&lt;/code&gt;、&lt;code&gt;selectNote&lt;/code&gt;和&lt;code&gt;showMessage&lt;/code&gt;都可以用&lt;code&gt;@tauri-apps/api&lt;/code&gt;中的&lt;code&gt;dialog&lt;/code&gt;模块解决，前三个是&lt;code&gt;dialog.open&lt;/code&gt;，最后一个是&lt;code&gt;dialog.message&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;setBackground&lt;/code&gt;和&lt;code&gt;onAppHide&lt;/code&gt;在桌面端是不必要的。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;getCoverUrl&lt;/code&gt;本质上是一种优化，这个会在前端部分说明。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;安卓端&lt;/h3&gt;
&lt;p&gt;不同于桌面端&lt;code&gt;Tauri&lt;/code&gt;帮我们搞定了大部分事情，移动端就要麻烦不少，安卓和iOS要去分别手动实现JS到客户端的绑定，不过好在如开头所说这个选型是比较稳健的，踩了点坑还算顺利。&lt;/p&gt;
&lt;p&gt;安卓的我用的是&lt;code&gt;Kotlin&lt;/code&gt;，写起来没&lt;code&gt;Java&lt;/code&gt;那么啰嗦，它的&lt;code&gt;Webview&lt;/code&gt;用起来是比较简单的，&lt;code&gt;JSBridge&lt;/code&gt;通过&lt;code&gt;addJavascriptInterface&lt;/code&gt;方法配合&lt;code&gt;@JavascriptInterface&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;// 定义JSB&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;AwakenJSB&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;kd&amp;quot;&gt;@JavascriptInterface&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;fun&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;setBackground&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;kt&amp;quot;&gt;Double&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;kt&amp;quot;&gt;Double&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;kt&amp;quot;&gt;Double&lt;/span&gt;&lt;span class=&amp;quot;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;c1&amp;quot;&gt;// 注册JSB&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;webView&lt;/span&gt;&lt;span class=&amp;quot;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;addJavascriptInterface&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;jsb&lt;/span&gt;&lt;span class=&amp;quot;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;Awaken&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;很简单是吧？那么看起来我们只要通过这个JSB实现下文件之类的接口，也没多麻烦的样子？一开始我也是这么想的，但做起来后却发现没这么简单。&lt;/p&gt;
&lt;h4&gt;文件系统&lt;/h4&gt;
&lt;p&gt;JSB有个很大的问题是它只能传输&lt;strong&gt;基本类型&lt;/strong&gt;，也就是数字、字符串之类的，而不能传输二进制数据。但对于这个软件来说，电子书和封面都是二进制数据，如果全部都走JSB的话，只有一招——在一端把二进制数据转&lt;code&gt;base64&lt;/code&gt;，到了另一端再转回来。&lt;/p&gt;
&lt;p&gt;虽然理论上这没啥问题，对于绝大部分书籍而言（1M以内）转换的开销对于客户端或者V8 JIT加持下的JS绰绰有余，但每次都这么转一下对于我而言是难以接受的——即使到现在，我还是有那么一些完美主义倾向。&lt;/p&gt;
&lt;p&gt;那么是否存在一种方式，能够在双端不经转换地传输二进制数据呢？仔细想想，“由前端和其他端互相传输二进制数据”，这不就是&lt;code&gt;XHR&lt;/code&gt;或者说&lt;code&gt;fetch&lt;/code&gt;吗？至此，我的思路便从“如何用JSB传输二进制数据”变成了“如何拦截&lt;code&gt;XHR&lt;/code&gt;”。&lt;/p&gt;
&lt;p&gt;在稍许调研后，我便找到了安卓Webview提供的官方拦截方法：&lt;/p&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;webView&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;webViewClient&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;object&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;WebViewClient&lt;/span&gt;&lt;span class=&amp;quot;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;override&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;fun&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;shouldInterceptRequest&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;WebView?&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;kt&amp;quot;&gt;WebResourceRequest?&lt;/span&gt;&lt;span class=&amp;quot;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;WebResourceResponse&lt;/span&gt;&lt;span class=&amp;quot;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;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;request&lt;/code&gt;最后拿到请求的&lt;code&gt;url&lt;/code&gt;，然后拆分出&lt;code&gt;host&lt;/code&gt;、&lt;code&gt;path&lt;/code&gt;和&lt;code&gt;query&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;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;host&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;s2&amp;quot;&gt;&amp;quot;awaken.api&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;kd&amp;quot;&gt;var&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;method&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;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;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;toString&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;().&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;substring&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;params&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;MutableMap&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;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;String&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;mutableMapOf&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;method&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;to&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;method&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;queryParameterNames&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;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;it&lt;/span&gt;&lt;span class=&amp;quot;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;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;getQueryParameter&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;it&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;toString&lt;/span&gt;&lt;span class=&amp;quot;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;jsb&lt;/span&gt;&lt;span class=&amp;quot;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;callMethod&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;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;requestHeaders&lt;/span&gt;&lt;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;awaken.api&lt;/code&gt;作为此类特殊接口的&lt;code&gt;host&lt;/code&gt;标识，以&lt;code&gt;path&lt;/code&gt;作为请求的方法名，以&lt;code&gt;query&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;fetch&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;http&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;//awaken.api/${method}?${query}`, {&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;method&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;data&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;POST&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;GET&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;body&lt;/span&gt;: &lt;span class=&amp;quot;kt&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;识别到此类请求后，客户端会调用&lt;code&gt;jsb&lt;/code&gt;示例（直接复用了上面提到的JSB类）对应的方法，进行处理：&lt;/p&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;fun&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;callMethod&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;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;params&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Map&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;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;String&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;origHeaders&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Map&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;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;String&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;WebResourceResponse&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;val&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;headers&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;HashMap&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;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;String&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;headers&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;Access-Control-Allow-Origin&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;s2&amp;quot;&gt;&amp;quot;*&amp;quot;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;headers&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;Access-Control-Allow-Methods&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;s2&amp;quot;&gt;&amp;quot;*&amp;quot;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;headers&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;Access-Control-Expose-Headers&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;s2&amp;quot;&gt;&amp;quot;X-Error-Message, Content-Type, WWW-Authenticate&amp;quot;&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;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;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;WebResourceResponse&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;nx&amp;quot;&gt;Charsets&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;UTF_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;toString&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;s2&amp;quot;&gt;&amp;quot;OK&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;headers&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;stream&lt;/span&gt;&lt;span class=&amp;quot;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;error&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Exception&lt;/span&gt;&lt;span class=&amp;quot;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;headers&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-Error-Message&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;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;message&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;toString&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;WebResourceResponse&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;application/json&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;utf-8&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;200&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;Error&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;headers&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;ByteArrayInputStream&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ByteArray&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;这里要注意我在返回的&lt;code&gt;headers&lt;/code&gt;中都写入了允许跨域，这实际上也是为了解决后面章节的问题，而其中的&lt;code&gt;X-Error-Message&lt;/code&gt;这个字段是为了返回错误，至于为什么么...因为&lt;code&gt;iOS&lt;/code&gt;中的拦截无法返回自定义状态信息，安卓也只能跟着搞了。&lt;/p&gt;
&lt;p&gt;至于实际上的文件存取逻辑没什么好说的，查一下API就完了，唯一值得一提的点是我将用户文件都存在了&lt;code&gt;context.getExternalFilesDir(null)!!.toPath()&lt;/code&gt;取得的&lt;strong&gt;扩展外部存储&lt;/strong&gt;中。&lt;/p&gt;
&lt;p&gt;按理说到这了，文件系统应该OK了吧？既满足了需求，又能够避免&lt;code&gt;base64&lt;/code&gt;转换，简直完美！那我只能说太天真了。在实现过程中我很快就遇到了麻烦——我&lt;strong&gt;无法获取到&lt;code&gt;POST&lt;/code&gt;请求的&lt;code&gt;body&lt;/code&gt;&lt;/strong&gt;。并且在深度搜索的最后，也只找到了一个社区给谷歌在19年提的Issue，他们说在考虑支持然后就...没有然后了。&lt;/p&gt;
&lt;p&gt;所以虽然很不甘心，我最终也只能做了个特殊处理——如果是安卓平台并且是&lt;code&gt;writeFile&lt;/code&gt;接口，还是走JSBridge，也就是说在写入的情况下，二进制数据还是要经历 encodeBase64 -&amp;gt; 传到客户端 -&amp;gt; decodeBase64 的过程，具体的逻辑就不多说了也很简单。&lt;/p&gt;
&lt;p&gt;不过最终综合来看，从前端向客户端写入二进制数据的状况只有在添加书籍时，这&lt;strong&gt;在移动端是个非常低频的操作，远低于读取&lt;/strong&gt;，而读取的优化是不受这个影响的，整体仍然很赚。&lt;/p&gt;
&lt;h4&gt;其他接口&lt;/h4&gt;
&lt;p&gt;其他接口的实现就是完全通过JSBridge了，其实也没什么好说的，简单提一下吧。&lt;/p&gt;
&lt;p&gt;首先是&lt;code&gt;showMessage&lt;/code&gt;这个接口，实际上就是利用了客户端的&lt;code&gt;AlertDialog&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;nx&amp;quot;&gt;mAlertDialog&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;AlertDialog&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Builder&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;mContext&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;mAlertDialog&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setPositiveButton&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;OK&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;DialogInterface&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;OnClickListener&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;dialog&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;which&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;dialog&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cancel&lt;/span&gt;&lt;span class=&amp;quot;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;mAlertDialog&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setNegativeButton&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;Close&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;DialogInterface&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;OnClickListener&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;dialog&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;which&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;dialog&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cancel&lt;/span&gt;&lt;span class=&amp;quot;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;mAlertDialog&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setTitle&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;title&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;mAlertDialog&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setMessage&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;message&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;mAlertDialog&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;select&lt;/code&gt;系列就稍微有点麻烦了，实际上是实现了一个通用的&lt;code&gt;selectFiles&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;@JavascriptInterface&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;fun&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;selectFiles&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;title&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;mimeTypes&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;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;Array&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;String&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;arrayOf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;mContext&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;selectFiles&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;mimeTypes&lt;/span&gt;&lt;span class=&amp;quot;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;it&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;mContext&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;mainWebView&lt;/span&gt;&lt;span class=&amp;quot;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;evaluateJavascript&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;Awaken_SelectFilesHandler(${JSONArray(res)})&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;ValueCallback&lt;/span&gt; &lt;span class=&amp;quot;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;


&lt;p&gt;首先注意这里的&lt;code&gt;evaluateJavascript&lt;/code&gt;，这是因为JSBridge都是同步调用，但这里面实际上执行了一个异步操作，所以为了通知前端，我执行了一个全局的JS方法&lt;code&gt;Awaken_SelectFilesHandler&lt;/code&gt;，而对应于前端则会在调用JSB方法前设置这个全局方法的值。&lt;/p&gt;
&lt;p&gt;而这里之所以会调用主&lt;code&gt;activity&lt;/code&gt;的接口然后回调，主要是因为安卓机制上的限制——唤起文件选择菜单实际上是启动另一个&lt;code&gt;activity&lt;/code&gt;，而我们需要重写主&lt;code&gt;activity&lt;/code&gt;的&lt;code&gt;onActivityResult&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;fun&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;selectFiles&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;mimeTypes&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;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;nx&amp;quot;&gt;files&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;nb&amp;quot;&gt;String&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;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;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;selectFilesCallback&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;callback&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;val&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;intent&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Intent&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Intent&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ACTION_OPEN_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;apply&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;addCategory&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Intent&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;CATEGORY_OPENABLE&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;mimeTypes&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;putExtra&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Intent&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;EXTRA_ALLOW_MULTIPLE&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;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;nx&amp;quot;&gt;startActivityForResult&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;intent&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;c1&amp;quot;&gt;// &lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;override&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;fun&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;onActivityResult&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;requestCode&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;nx&amp;quot;&gt;resultCode&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;nx&amp;quot;&gt;resultData&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Intent?&lt;/span&gt;&lt;span class=&amp;quot;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;requestCode&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;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;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;onActivityResult&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;requestCode&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;resultCode&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;resultData&lt;/span&gt;&lt;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;onAppHide&lt;/code&gt;方法可以监听主&lt;code&gt;activity&lt;/code&gt;的&lt;code&gt;onPause&lt;/code&gt;周期，然后用&lt;code&gt;evaluateJavascript&lt;/code&gt;执行一个全局JS方法即可；&lt;code&gt;setBackground&lt;/code&gt;方法在安卓端并不需要，取而代之的是隐藏安卓的虚拟按键，这个本质上就是隐藏&lt;code&gt;ActionBar&lt;/code&gt;并进入全屏模式，代码自己看吧没啥特别要注意的。&lt;/p&gt;
&lt;h3&gt;iOS端&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;iOS&lt;/code&gt;的接口思路和安卓是完全一致的，不过由于平台差异，实现起来还是有些不同。由于实在不想写&lt;code&gt;OC&lt;/code&gt;了，这里我选择的是&lt;code&gt;Swift&lt;/code&gt;顺便带上&lt;code&gt;swiftui&lt;/code&gt;，同时为了JIT的性能用的是&lt;code&gt;WKWebView&lt;/code&gt;。和安卓类似，我同样需要注册&lt;code&gt;JSBridge&lt;/code&gt;和进行&lt;code&gt;XHR&lt;/code&gt;拦截，这里先说JSBridge，&lt;code&gt;XHR&lt;/code&gt;拦截就完全交给文件系统一节吧。&lt;/p&gt;
&lt;p&gt;WKWebView的JSBridge注册也很简单，首先我们要定义一个实现了&lt;code&gt;WKScriptMessageHandler&lt;/code&gt;协议的类&lt;code&gt;AwakenJSB&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;// 实现JSB&lt;/span&gt;
&lt;span class=&amp;quot;kd&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;class&lt;/span&gt; &lt;span class=&amp;quot;nc&amp;quot;&gt;AwakenJSB&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;NSObject&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;WKScriptMessageHandler&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;WKUIDelegate&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;kd&amp;quot;&gt;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;onChangeBg&lt;/span&gt;&lt;span class=&amp;quot;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;escaping&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kc&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;bp&amp;quot;&gt;CGColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&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;())&lt;/span&gt; &lt;span class=&amp;quot;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;// 以下代码在`swiftui`对应实现`UIViewRepresentable`协议的`WebView`类中&lt;/span&gt;
&lt;span class=&amp;quot;c1&amp;quot;&gt;// 实例化JSB&lt;/span&gt;
&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;jsb&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;AwakenJSB&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;onChangeBg&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;onChangeBg&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脚本&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;initJS&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;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&amp;quot;s&amp;quot;&gt;window.Awaken = {&lt;/span&gt;
&lt;span class=&amp;quot;s&amp;quot;&gt;    getPlatform() {&lt;/span&gt;
&lt;span class=&amp;quot;s&amp;quot;&gt;        return &amp;#39;IOS&amp;#39;;&lt;/span&gt;
&lt;span class=&amp;quot;s&amp;quot;&gt;    },&lt;/span&gt;
&lt;span class=&amp;quot;s&amp;quot;&gt;    showMessage(message, type, title) {&lt;/span&gt;
&lt;span class=&amp;quot;s&amp;quot;&gt;        window.webkit.messageHandlers.showMessage.postMessage({title: title, message: message, type: type || &amp;#39;&amp;#39;});&lt;/span&gt;
&lt;span class=&amp;quot;s&amp;quot;&gt;    },&lt;/span&gt;
&lt;span class=&amp;quot;s&amp;quot;&gt;    selectFiles(title, types) {&lt;/span&gt;
&lt;span class=&amp;quot;s&amp;quot;&gt;        window.Awaken.showMessage(&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;iOS&lt;/span&gt;&lt;span class=&amp;quot;err&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;n&amp;quot;&gt;error&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;, &amp;quot;&amp;quot;);&lt;/span&gt;
&lt;span class=&amp;quot;s&amp;quot;&gt;        window.Awaken_SelectFilesHandler([]);&lt;/span&gt;
&lt;span class=&amp;quot;s&amp;quot;&gt;    },&lt;/span&gt;
&lt;span class=&amp;quot;s&amp;quot;&gt;    setBackground(r, g, b) {&lt;/span&gt;
&lt;span class=&amp;quot;s&amp;quot;&gt;        window.webkit.messageHandlers.setBackground.postMessage({r: r, g: g, b: b});&lt;/span&gt;
&lt;span class=&amp;quot;s&amp;quot;&gt;    }&lt;/span&gt;
&lt;span class=&amp;quot;s&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

&lt;span class=&amp;quot;c1&amp;quot;&gt;// 初始化WebView&lt;/span&gt;
&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;config&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;WKWebViewConfiguration&lt;/span&gt;&lt;span class=&amp;quot;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;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;wkWebView&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;WKWebView&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;frame&lt;/span&gt;&lt;span class=&amp;quot;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;zero&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;configuration&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;config&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;config&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;controller&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;addUserScript&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;bp&amp;quot;&gt;WKUserScript&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;source&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;initJS&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;injectionTime&lt;/span&gt;&lt;span class=&amp;quot;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;atDocumentStart&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;forMainFrameOnly&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;c1&amp;quot;&gt;// 注册JSB&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;config&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;controller&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;kc&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;name&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;showMessage&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;config&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;controller&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;kc&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;name&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;setBackground&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;onChangeBg&lt;/code&gt;回调这里暂时无需在意，其他除了流程和安卓大差不差，唯一有显著区别的就是&lt;code&gt;initJS&lt;/code&gt;了，这是一段会在WKWebView加载完&lt;code&gt;html&lt;/code&gt;、执行用户&lt;code&gt;js&lt;/code&gt;前注入的一段&lt;code&gt;js&lt;/code&gt;代码。可以看到其实际上给全局挂载了一些方法，而这些方法在安卓中是直接用&lt;code&gt;@JavascriptInterface&lt;/code&gt;注解实现在JSB类中的。再看每个方法的实现，除了&lt;code&gt;iOS&lt;/code&gt;无法实现文件选择外导致无效的&lt;code&gt;selectFiles&lt;/code&gt;外，都有个调用&lt;code&gt;window.webkit.messageHandlers.xxxx.postMessage&lt;/code&gt;，这是因为WKWebView只支持前端和客户端的&lt;strong&gt;异步通信&lt;/strong&gt;，只能这么搞，好在这几个接口基本都不需要返回值，随便搞搞就行了。在&lt;code&gt;js&lt;/code&gt;中&lt;code&gt;postMessage&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;public&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;func&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;userContentController&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kc&amp;quot;&gt;_&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;userContentController&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;WKUserContentController&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;didReceive&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;message&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;WKScriptMessage&lt;/span&gt;&lt;span class=&amp;quot;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;n&amp;quot;&gt;message&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;s&amp;quot;&gt;&amp;quot;showMessage&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;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;params&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;message&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;body&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;as&lt;/span&gt;&lt;span class=&amp;quot;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;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;showMessage&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;message&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;params&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;message&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;n&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;params&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;type&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;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;n&amp;quot;&gt;params&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;title&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;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;message&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;s&amp;quot;&gt;&amp;quot;setBackground&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;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;params&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;message&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;body&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;as&lt;/span&gt;&lt;span class=&amp;quot;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;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;Double&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;setBackground&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;params&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;r&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;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;n&amp;quot;&gt;params&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;g&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;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;n&amp;quot;&gt;params&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;b&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;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;Swift是我写过的第二啰嗦的语言，苹果的API设计不敢恭维，XCode就是依托答辩。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;文件系统&lt;/h4&gt;
&lt;p&gt;iOS端的&lt;code&gt;XHR&lt;/code&gt;拦截的方式和安卓大同小异，不过这API搞起来虽然蛋疼，但人家却支持了获取&lt;code&gt;request&lt;/code&gt;的&lt;code&gt;body&lt;/code&gt;...要我说你两就不能合计合计整个完全体吗？&lt;/p&gt;
&lt;p&gt;不吐槽了...来看看怎么搞吧，iOS提供了&lt;code&gt;WKURLSchemeHandler&lt;/code&gt;协议来为WKWebView提供&lt;code&gt;XHR&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;kd&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;class&lt;/span&gt; &lt;span class=&amp;quot;nc&amp;quot;&gt;AwakenXHRHandler&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;NSObject&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;WKURLSchemeHandler&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;kd&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;func&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;webView&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kc&amp;quot;&gt;_&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;webView&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;WKWebView&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;start&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;urlSchemeTask&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;WKURLSchemeTask&lt;/span&gt;&lt;span class=&amp;quot;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;kd&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;func&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;webView&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kc&amp;quot;&gt;_&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;webView&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;WKWebView&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;stop&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;urlSchemeTask&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;WKURLSchemeTask&lt;/span&gt;&lt;span class=&amp;quot;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;c1&amp;quot;&gt;// WKWebView的那个`config`，注册拦截器，传入`jsb`只是为了和安卓一样复用JSB类实现逻辑&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;config&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;setURLSchemeHandler&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;AwakenXHRHandler&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;jsb&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;jsb&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;forURLScheme&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;awaken&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;forURLScheme&lt;/code&gt;这个参数，它指定了一个请求的&lt;code&gt;schema&lt;/code&gt;，因为理论上iOS不允许开发者拦截WKWebView的标准协议，类似&lt;code&gt;http&lt;/code&gt;/&lt;code&gt;https&lt;/code&gt;等等，所以这里我必须指定一个自定义的&lt;code&gt;awaken&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;fetch&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;awaken&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;//awaken.api/${method}?${query}`, {&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;method&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;data&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;POST&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;GET&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;body&lt;/span&gt;: &lt;span class=&amp;quot;kt&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;blockquote&gt;
&lt;p&gt;不过在安卓上用自定义&lt;code&gt;schema&lt;/code&gt;请求会报错，也是够麻烦的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;由于WKWebView允许获取拦截到的请求的&lt;code&gt;body&lt;/code&gt;，所以也不用像安卓那样麻烦地去搞什么base64转换了：&lt;/p&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;nv&amp;quot;&gt;request&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;urlSchemeTask&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;request&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;guard&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;requestUrl&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;request&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;url&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;return&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;nv&amp;quot;&gt;method&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;requestUrl&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;path&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;method&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;method&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;&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;method&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;method&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;method&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;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;method&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;startIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;offsetBy&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;nv&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;nb&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nb&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;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;nv&amp;quot;&gt;components&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;URLComponents&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;requestUrl&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;resolvingAgainstBaseURL&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;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;queryItems&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;components&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;?.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;queryItems&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;queryItems&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!=&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;nil&lt;/span&gt;&lt;span class=&amp;quot;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;params&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;queryItems&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;!.&lt;/span&gt;&lt;span class=&amp;quot;bp&amp;quot;&gt;reduce&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;into&lt;/span&gt;&lt;span class=&amp;quot;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;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nb&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;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;n&amp;quot;&gt;item&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;in&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;n&amp;quot;&gt;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;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;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;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;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;body&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;request&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;httpBody&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;取得了&lt;code&gt;method&lt;/code&gt;和&lt;code&gt;params&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;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;paths&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;NSSearchPathForDirectoriesInDomains&lt;/span&gt;&lt;span class=&amp;quot;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;documentDirectory&lt;/span&gt;&lt;span class=&amp;quot;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;userDomainMask&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;n&amp;quot;&gt;mBaseDir&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;URL&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;fileURLWithPath&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;paths&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;bp&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;isDirectory&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;code&gt;response&lt;/code&gt;。一般的教程中都会说返回&lt;code&gt;URLResponse&lt;/code&gt;，但这个无法自定义状态码和&lt;code&gt;headers&lt;/code&gt;，无法满足需求，在调研后我最终找到了&lt;code&gt;HTTPURLResponse&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;nv&amp;quot;&gt;response&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;HTTPURLResponse&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;requestUrl&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;statusCode&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;n&amp;quot;&gt;httpVersion&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;nil&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;headerFields&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;headers&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;和安卓相同，其它接口都是通过JSBridge实现的。&lt;/p&gt;
&lt;p&gt;首先是&lt;code&gt;showMessage&lt;/code&gt;这个接口，得益于iOS的天才API设计和每升一个版本就相当于另一门语言的&lt;code&gt;swift&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;func&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;showMessage&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;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;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&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;String&lt;/span&gt;&lt;span class=&amp;quot;p&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;nb&amp;quot;&gt;String&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;&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;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;alert&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;UIAlertController&lt;/span&gt;&lt;span class=&amp;quot;p&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;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;n&amp;quot;&gt;message&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;message&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;preferredStyle&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;UIAlertController&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Style&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;alert&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;alert&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;addAction&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;bp&amp;quot;&gt;UIAlertAction&lt;/span&gt;&lt;span class=&amp;quot;p&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;s&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;style&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;UIAlertAction&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Style&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;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;handler&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;nil&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt;

    &lt;span class=&amp;quot;bp&amp;quot;&gt;UIApplication&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;shared&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;connectedScenes&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;flatMap&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;$0&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;as&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;UIWindowScene&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)?.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;windows&lt;/span&gt; &lt;span class=&amp;quot;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;bp&amp;quot;&gt;first&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt; &lt;span class=&amp;quot;nv&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;isKeyWindow&lt;/span&gt; &lt;span class=&amp;quot;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;rootViewController&lt;/span&gt;&lt;span class=&amp;quot;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;present&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;alert&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;animated&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;n&amp;quot;&gt;completion&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;nil&lt;/span&gt;&lt;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;onAppHide&lt;/code&gt;，这个也很简单，我们首先要让&lt;code&gt;AwakenJSB&lt;/code&gt;持有WKWebView实例，然后配合&lt;code&gt;NotificationCenter&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;public&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;func&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;setWebview&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;webview&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;WKWebView&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;kc&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;webview&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;webview&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;NotificationCenter&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;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;addObserver&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kc&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;selector&lt;/span&gt;&lt;span class=&amp;quot;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;selector&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;didEnterBackground&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;UIScene&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;didEnterBackgroundNotification&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;object&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;nil&lt;/span&gt;&lt;span class=&amp;quot;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;@objc&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;func&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;didEnterBackground&lt;/span&gt;&lt;span class=&amp;quot;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;webview&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;?.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;evaluateJavaScript&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;window.Awaken_AppHideCB &amp;amp;&amp;amp; window.Awaken_AppHideCB()&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;setBackground&lt;/code&gt;了，这个在安卓中无用，但对于大部分都是刘海异形屏的iPhone，还是很有必要的——我们需要将WKWebView放在&lt;strong&gt;Safe Area&lt;/strong&gt;，顶部和底部保证和WebView的背景色一致，而这一点我最终的做法是利用&lt;code&gt;swiftui&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;struct&lt;/span&gt; &lt;span class=&amp;quot;nc&amp;quot;&gt;ContentView&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&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;n&amp;quot;&gt;State&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;var&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;bgColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;CGColor&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;CGColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;srgbRed&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;green&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;blue&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;alpha&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;nv&amp;quot;&gt;body&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;some&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;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;ZStack&lt;/span&gt;&lt;span class=&amp;quot;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;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;bgColor&lt;/span&gt;&lt;span class=&amp;quot;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;frame&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;minWidth&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;maxWidth&lt;/span&gt;&lt;span class=&amp;quot;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;infinity&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;minHeight&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;maxHeight&lt;/span&gt;&lt;span class=&amp;quot;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;infinity&lt;/span&gt;&lt;span class=&amp;quot;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;background&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;red&lt;/span&gt;&lt;span class=&amp;quot;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;edgesIgnoringSafeArea&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;p&amp;quot;&gt;)&lt;/span&gt;
            &lt;span class=&amp;quot;n&amp;quot;&gt;WebView&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
                &lt;span class=&amp;quot;n&amp;quot;&gt;onChangeBg&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;changeBgColor&lt;/span&gt;
            &lt;span class=&amp;quot;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;func&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;changeBgColor&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;bp&amp;quot;&gt;CGColor&lt;/span&gt;&lt;span class=&amp;quot;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;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;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;/pre&gt;&lt;/div&gt;


&lt;p&gt;在UI的根节点，我在底层放了一个全屏的&lt;code&gt;Color&lt;/code&gt;控件来铺满背景，然后在上面放上自定义的&lt;code&gt;WebView&lt;/code&gt;控件，并利用&lt;code&gt;onChangeBg&lt;/code&gt;这个回调来修改控件的&lt;code&gt;bgColor&lt;/code&gt;属性，最终影响到&lt;code&gt;Color&lt;/code&gt;的颜色。而进一步，我们再将其传入一开始定义给&lt;code&gt;AwakenJSB&lt;/code&gt;传入的那个&lt;code&gt;onChangeBg&lt;/code&gt;回调，最终在JSB类中实现功能&lt;/p&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;func&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;setBackground&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;nb&amp;quot;&gt;Double&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;nb&amp;quot;&gt;Double&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;nb&amp;quot;&gt;Double&lt;/span&gt;&lt;span class=&amp;quot;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;mOnChangeBg&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;bp&amp;quot;&gt;CGColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;srgbRed&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;green&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;n&amp;quot;&gt;blue&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;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;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;h3&gt;webdav跨域&lt;/h3&gt;
&lt;p&gt;至此，三端的后端接口都实现完毕，理论上为前端实现扫平了障碍，但在实际的开发中却还是遇到了因Web方案带来的麻烦，其中最大的一个就是&lt;strong&gt;跨域&lt;/strong&gt;问题。&lt;/p&gt;
&lt;p&gt;众所周知浏览器对于跨域资源是有&lt;code&gt;CORS&lt;/code&gt;来限制的，这本质上是为了内容安全，无可厚非。但对于&lt;code&gt;webdav&lt;/code&gt;这种协议，尤其是用户购买的私有服务，跨域本就不应该是障碍，而实际上很多厂商包括坚果云确实也没做这个限制。一开始我在用本地&lt;code&gt;webdav-server&lt;/code&gt;和&lt;code&gt;webdav-client&lt;/code&gt;这两个开源库调试的时候，也遇到了跨域问题，但在大量搜索后认为是&lt;code&gt;server&lt;/code&gt;的实现不标准，对其进行了魔改后凑合开发，但最终实际环境测试时仍然绕不过这个问题。为此我还专门联系了坚果云的开发人员，而他们的答复也很简单：“我们实现的是标准的WebDAV服务端协议”。&lt;/p&gt;
&lt;p&gt;好了扯了这么久，困扰我的这个问题究竟是什么呢？其实很简单：&lt;strong&gt;对于非简单的跨域请求，浏览器在发送真正的请求前会先发送一个名为&lt;code&gt;preflight&lt;/code&gt;的&lt;code&gt;OPTIONS&lt;/code&gt;请求，来保护不知晓&lt;code&gt;CORS&lt;/code&gt;的老服务器，只有这个请求成功后才允许发出真正的请求&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;听起来很合理对吧？对于大多请求时没毛病，但问题在于&lt;code&gt;webdav&lt;/code&gt;服务是有账号密码验证的，而&lt;code&gt;preflight&lt;/code&gt;请求是不会携带验证信息的，而大多“按照标准实现”的WebDAV服务器在校验前并不会区分你是不是&lt;code&gt;preflight&lt;/code&gt;请求...这TM就死锁了。&lt;/p&gt;
&lt;p&gt;那怎么办呢？没啥办法，既然浏览器的限制绕不过去，就只能借由客户端了，毕竟客户端并没有&lt;code&gt;CORS&lt;/code&gt;。如此一来，就不得不给我们上面提供的&lt;code&gt;XHR&lt;/code&gt;拦截多加个方法了。不过这块处理起来，比上面那些接口要更复杂一些。&lt;/p&gt;
&lt;h4&gt;前端拦截&lt;/h4&gt;
&lt;p&gt;首先因为是用的开源库（我这么懒显然不想去拉一份下来自己改），所以只能看有没有&lt;code&gt;hook&lt;/code&gt;的方案，而这时候选择Hybrid方案的优点就体现出来了——轮子多。我使用了&lt;code&gt;ajax-hook&lt;/code&gt;库来在前端拦截&lt;code&gt;XHR&lt;/code&gt;，将所有&lt;code&gt;webdav&lt;/code&gt;请求都加上了一个&lt;code&gt;prefix&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;DAV_PREFIX&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://AwakenWebDav:&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;proxy&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;onRequest&lt;/span&gt;&lt;span class=&amp;quot;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;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;handler&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;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;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;startsWith&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;DAV_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;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;handler&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;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;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;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;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;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;replace&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;DAV_PREFIX&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;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;code&gt;hook&lt;/code&gt;住&lt;code&gt;XHR&lt;/code&gt;后，接下来就是在不同平台实现请求代理了。&lt;/p&gt;
&lt;h4&gt;桌面后端&lt;/h4&gt;
&lt;p&gt;首先是桌面端，在上面接口的实现中，桌面对文件系统并不依赖与XHR拦截，所以这里要额外想怎么实现。好在&lt;code&gt;Tauri&lt;/code&gt;官方已经给我们准备好了由&lt;code&gt;rust&lt;/code&gt;实现并绑定好的&lt;code&gt;http&lt;/code&gt;模块。&lt;/p&gt;
&lt;p&gt;首先在&lt;code&gt;tauri.conf.json&lt;/code&gt;的&lt;code&gt;tauri.allowlist&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;s2&amp;quot;&gt;&amp;quot;http&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;all&amp;quot;&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;nt&amp;quot;&gt;&amp;quot;request&amp;quot;&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;nt&amp;quot;&gt;&amp;quot;scope&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;https://**&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;http://**&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;/p&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;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;http&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;@tauri-apps/api&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;nx&amp;quot;&gt;http&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fetch&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;method&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;config.method&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;nx&amp;quot;&gt;body&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;config.body&lt;/span&gt; &lt;span class=&amp;quot;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;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;body&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;string&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;http&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;text&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;body&lt;/span&gt;&lt;span class=&amp;quot;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;http&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;bytes&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;body&lt;/span&gt;&lt;span class=&amp;quot;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;headers&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;config.headers&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;responseType&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;sr&amp;quot;&gt;/(png|epub)$/&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;url&lt;/span&gt;&lt;span class=&amp;quot;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;http.ResponseType.Binary&lt;/span&gt; : &lt;span class=&amp;quot;kt&amp;quot;&gt;http.ResponseType.Text&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;nx&amp;quot;&gt;handler&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;config&lt;/span&gt;: &lt;span class=&amp;quot;kt&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;status&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;res.status&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;headers&lt;/span&gt;&lt;span class=&amp;quot;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;response&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;res.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;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;error&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;handler&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;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;/pre&gt;&lt;/div&gt;


&lt;h4&gt;安卓后端&lt;/h4&gt;
&lt;p&gt;移动两端做法基本一致，为前面的&lt;code&gt;XHR&lt;/code&gt;拦截协议新增方法&lt;code&gt;webdav&lt;/code&gt;，然后将真正请求的&lt;code&gt;url&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;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;body&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;platform&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;ANDROID&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;isBase64&lt;/span&gt; &lt;span class=&amp;quot;o&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;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;body&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;string&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;data&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;nx&amp;quot;&gt;isBase64&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;atob&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;body&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;o&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;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;jsb&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setWebdavRequestBody&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;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;method&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;isBase64&lt;/span&gt;&lt;span class=&amp;quot;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;fetch&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;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;API_PREFIX&lt;/span&gt;&lt;span class=&amp;quot;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;webdav&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;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;nb&amp;quot;&gt;encodeURIComponent&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;err&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;method&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;config.method&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;kt&amp;quot;&gt;config.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;headers&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;config.headers&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;errorMessage&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;headers&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;X-Error-Message&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;errorMessage&lt;/span&gt;&lt;span class=&amp;quot;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;err&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;errorMessage&lt;/span&gt;&lt;span class=&amp;quot;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;webdav&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;url&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;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;sr&amp;quot;&gt;/(png|epub)$/&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;url&lt;/span&gt;&lt;span class=&amp;quot;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;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;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;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;text&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;data&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;handler&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;config&lt;/span&gt;: &lt;span class=&amp;quot;kt&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;status&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;res.status&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;statusText&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;res.statusText&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;headers&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;res.headers&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;kt&amp;quot;&gt;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;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;error&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;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;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;handler&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;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;/pre&gt;&lt;/div&gt;


&lt;p&gt;但在最后的处理中双端还是有一些差异：&lt;/p&gt;
&lt;p&gt;首先安卓端由于前面说过的原因，需要将二进制&lt;code&gt;body&lt;/code&gt;转换为&lt;code&gt;base64&lt;/code&gt;，所以在客户端需要实现一个JSB接口&lt;code&gt;setWebdavRequestBody&lt;/code&gt;，在请求前将转好的&lt;code&gt;base64&lt;/code&gt;发给客户端，这个在上面的代码也有所体现。接下来在客户端只需要将请求的&lt;code&gt;url&lt;/code&gt;作为&lt;code&gt;key&lt;/code&gt;，把&lt;code&gt;base64&lt;/code&gt;存到一张&lt;code&gt;Map&lt;/code&gt;中，后续接收到请求取出处理即可，无序赘述。&lt;/p&gt;
&lt;p&gt;其次就是请求代理的本质是将从Web拦截下的请求由客户端发出，再将结果返回Web。而客户端发出请求时，应当&lt;strong&gt;带上原先请求的&lt;code&gt;headers&lt;/code&gt;和&lt;code&gt;body&lt;/code&gt;&lt;/strong&gt;，很遗憾我并未在安卓的官方API找到能满足需求的接口，所以最终我使用了&lt;code&gt;okhttp3&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;fun&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;webdav&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;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;method&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;headers&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Map&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;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;String&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;okhttp3&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;nx&amp;quot;&gt;val&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;request&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;okhttp3&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;Builder&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;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;val&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;cache&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;mWebdavRequestCache&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;+&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;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;cache&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;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;method&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;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;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;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cache&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;second&lt;/span&gt;&lt;span class=&amp;quot;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;method&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;nx&amp;quot;&gt;Base64&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;nx&amp;quot;&gt;cache&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;first&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;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&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;toRequestBody&lt;/span&gt;&lt;span class=&amp;quot;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;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;method&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;nx&amp;quot;&gt;cache&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;first&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;toRequestBody&lt;/span&gt;&lt;span class=&amp;quot;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;mWebdavRequestCache&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;remove&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;+&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;headers&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;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;addHeader&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;it&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;it&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;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;client&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;newCall&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;build&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;execute&lt;/span&gt;&lt;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;吐槽一下&lt;code&gt;kotlin&lt;/code&gt;、&lt;code&gt;gradle&lt;/code&gt;这些工具和依赖他们的库之间的各种依赖冲突真是蛋疼。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;iOS后端&lt;/h4&gt;
&lt;p&gt;iOS这边比起安卓要更简单一些，其原生的&lt;code&gt;URLRequest&lt;/code&gt;就可以满足需求了，在上面的&lt;code&gt;XHR&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;n&amp;quot;&gt;method&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;webdav&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;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;url&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;URL&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;params&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;url&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;o&amp;quot;&gt;!&lt;/span&gt;
    &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;session&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;URLSession&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;shared&lt;/span&gt;
    &lt;span class=&amp;quot;kd&amp;quot;&gt;var&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;req&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;URLRequest&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;req&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;httpMethod&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;request&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;httpMethod&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;req&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;httpBody&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;body&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;req&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;allHTTPHeaderFields&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;request&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;allHTTPHeaderFields&lt;/span&gt;
    &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;task&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;session&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dataTask&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;with&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;req&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;completionHandler&lt;/span&gt;&lt;span class=&amp;quot;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;weak&lt;/span&gt; &lt;span class=&amp;quot;kc&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;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;response&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;k&amp;quot;&gt;in&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;guard&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;strongSelf&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;self&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;return&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;error&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!=&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;nil&lt;/span&gt;&lt;span class=&amp;quot;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;strongSelf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;postFailed&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;urlSchemeTask&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;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;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;data&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!=&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;nil&lt;/span&gt;&lt;span class=&amp;quot;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;nv&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;n&amp;quot;&gt;response&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;as&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;!&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;HTTPURLResponse&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;nv&amp;quot;&gt;headers&lt;/span&gt;&lt;span class=&amp;quot;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;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nb&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;p&amp;quot;&gt;[&lt;/span&gt;
                &lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;Access-Control-Allow-Origin&amp;quot;&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;*&amp;quot;&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;Access-Control-Allow-Methods&amp;quot;&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;*&amp;quot;&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;Access-Control-Expose-Headers&amp;quot;&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;X-Error-Message, Content-Type, WWW-Authenticate&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;strongSelf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;postResponse&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;urlSchemeTask&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;response&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;HTTPURLResponse&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
                &lt;span class=&amp;quot;n&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;requestUrl&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;statusCode&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;statusCode&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
                &lt;span class=&amp;quot;n&amp;quot;&gt;httpVersion&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;nil&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;headerFields&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;headers&lt;/span&gt;
            &lt;span class=&amp;quot;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;strongSelf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;postResponse&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;urlSchemeTask&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;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;strongSelf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;postFinished&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;urlSchemeTask&lt;/span&gt;&lt;span class=&amp;quot;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;task&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;resume&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;h2&gt;前端实现&lt;/h2&gt;
&lt;p&gt;前端逻辑主要是书籍和笔记的管理与同步，堆业务逻辑嘛懂得都懂，无非都是说难也不难，说简单却也坑多有些麻烦。所以这里我不会说太多细节，只会捡重点说一些心得。&lt;/p&gt;
&lt;p&gt;为了加快开发效率（其实是懒），这里我选择了以前用惯了的&lt;code&gt;React&lt;/code&gt;作为前端框架，样式用&lt;code&gt;SCSS&lt;/code&gt;配合&lt;code&gt;css-modules&lt;/code&gt;，并配合17年在B站用爱发电期间和同事一起开源的&lt;a href=&amp;quot;https://hana-ui.moe/&amp;quot;&gt;hana-ui&lt;/a&gt;，效果还行吧。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;已经两年多没写正儿八经的前端代码了，有些手生，第一次用&lt;code&gt;hooks&lt;/code&gt;玩不太转，而且也懒得用什么状态管理库了硬莽，不得不服老啊...嘛...又不是不能用。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;书籍管理&lt;/h3&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;nx&amp;quot;&gt;type&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;TBookType&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;EPUB&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;kr&amp;quot;&gt;interface&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;IBook&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;hash&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;kt&amp;quot;&gt;TBookType&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;author&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;ts&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;removed?&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;cover?&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;hash&lt;/code&gt;，它是电子书本身的&lt;code&gt;md5&lt;/code&gt;，保证了书籍的唯一性，之所以特意算一遍&lt;code&gt;hash&lt;/code&gt;是因为书籍本身可能重名而且也不一定都有&lt;code&gt;ids&lt;/code&gt;元数据。在实际的存储中，我会给每本书籍创建一个以&lt;code&gt;hash&lt;/code&gt;命名的目录，将具体内容存于其中。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;type&lt;/code&gt;是书籍类型，之所以有这个的理由你应该猜到了...不错，一开始我想支持&lt;code&gt;EPUB&lt;/code&gt;/&lt;code&gt;PDF&lt;/code&gt;/&lt;code&gt;MOBI&lt;/code&gt;等多种书籍格式，但最后发现太麻烦不值得花这么多精力就只剩&lt;code&gt;EPUB&lt;/code&gt;了（没事毕竟我们还有针对PDF的&lt;code&gt;OCR&lt;/code&gt;和转换神器&lt;code&gt;calibre&lt;/code&gt;嘛...又不是不能用。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;name&lt;/code&gt;和&lt;code&gt;author&lt;/code&gt;是从电子书提取的书名和作者，没啥好说的。&lt;code&gt;ts&lt;/code&gt;记录的是书籍被修改的（添加/删除）的时间戳，配合&lt;code&gt;removed&lt;/code&gt;在同步时使用，毕竟咱没有中心服务器处理逻辑，为了避免同步错乱没啥办法。&lt;/p&gt;
&lt;p&gt;最后一个&lt;code&gt;cover&lt;/code&gt;是从书籍中提取的封面，方便首页展示。我在添加书籍时，会将封面的二进制数据提取出来存到书籍目录下，之后在每次软件启动拿到书籍信息后，会根据当前平台自动生成一个&lt;code&gt;cover&lt;/code&gt;地址。&lt;/p&gt;
&lt;p&gt;在桌面端，我们需要在&lt;code&gt;tauri.conf.json&lt;/code&gt;中的&lt;code&gt;tauri.protocol&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;s2&amp;quot;&gt;&amp;quot;protocol&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;asset&amp;quot;&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;nt&amp;quot;&gt;&amp;quot;assetScope&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;**&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;/p&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;tauri&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;@tauri-apps/api&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;getCoverUrl&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;book&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IBook&lt;/span&gt;&lt;span class=&amp;quot;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;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;tauri&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;convertFileSrc&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;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;BOOKS_FOLDER&lt;/span&gt;&lt;span class=&amp;quot;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;nx&amp;quot;&gt;book&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&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;nx&amp;quot;&gt;cover&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;png&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;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;nx&amp;quot;&gt;getCoverUrl&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;book&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IBook&lt;/span&gt;&lt;span class=&amp;quot;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;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;err&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;API_PREFIX&lt;/span&gt;&lt;span class=&amp;quot;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;readBinaryFile&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;filePath&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;book&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&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;nx&amp;quot;&gt;cover&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;png&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;base&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Books&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;p&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;p&gt;拉取远端列表 -&amp;gt; 和本地列表对比 -&amp;gt; 拉取远端新增书籍 -&amp;gt; 上传本地新增书籍 -&amp;gt; 同步列表目录&lt;/p&gt;
&lt;p&gt;具体的逻辑就不写了，有兴趣的可以自己去看代码。这里值得特别说明的有几点：&lt;/p&gt;
&lt;p&gt;首先是书籍冲突，其原因很简单，因为本方案没有一个中心的逻辑服务器，逻辑是分布在各个设备单独处理的，同时添加本地书籍并不需要联网，这就可能导致各端分别添加了同样的书籍，然后一并同步到远端的状况，这又会带来两个可能的问题：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;本地尚未上传的书籍已然存在于远端。这个问题从设计层面被解决了：因为书籍的唯一性由&lt;code&gt;hash&lt;/code&gt;决定，一般来讲不会碰撞，所以这里不涉及到状态。&lt;/li&gt;
&lt;li&gt;两个设备同时将本地书籍同步到远端。这个问题是切实存在的，理论上来讲可以通过&lt;code&gt;webdav&lt;/code&gt;协议的&lt;code&gt;lock&lt;/code&gt;方法解决，但考虑到实际会这么操作的可能性很低（喜欢在日常中COS测试工程师的用户不在其中，我也没义务考虑），所以就这么办吧，不处理。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;其次是书籍的删除，同样是由于没有中心服务器的缘由，书籍的删除变得非常麻烦。我不能直接把条目移除覆盖到远端，这样会导致有两个设备都存在统一条目的情况下永远删不掉。所以我退而求其次，即便是删除了也保留条目，然后用书籍协议中的&lt;code&gt;removed&lt;/code&gt;和&lt;code&gt;ts&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;syncToLocalBooks&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IBook&lt;/span&gt;&lt;span class=&amp;quot;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;remoteBooks&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;book&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;localBook&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;localTable&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;book&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&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;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;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;localBook&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;book&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ts&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;localBook&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ts&lt;/span&gt;&lt;span class=&amp;quot;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;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;localBook&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;book&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;span class=&amp;quot;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;syncToLocalBooks&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;book&lt;/span&gt;&lt;span class=&amp;quot;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;syncToRemoteBooks&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IBook&lt;/span&gt;&lt;span class=&amp;quot;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;books&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;book&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;remoteBook&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;remoteTable&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;book&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&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;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;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;remoteBook&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;book&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ts&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;remoteBook&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ts&lt;/span&gt;&lt;span class=&amp;quot;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;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;remoteBook&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;book&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;span class=&amp;quot;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;syncToRemoteBooks&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;book&lt;/span&gt;&lt;span class=&amp;quot;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;EPUB解析阅读&lt;/h3&gt;
&lt;p&gt;在书籍列表点击任一封面后，进入的就是阅读界面了。这里我使用了&lt;code&gt;epub.js&lt;/code&gt;这个开源库，虽然文档一般坑不少，但却是能解决绝大多数的问题，至少不用从头去再造轮子了。&lt;/p&gt;
&lt;p&gt;这一部分吧...虽然算起来是前端最复杂的一部分，也搞了挺久，但其实也没有太多好说的，大部分看&lt;code&gt;epub.js&lt;/code&gt;的文档就OK，值得重点提的几个地方：&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;book&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;ePub&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;nx&amp;quot;&gt;content&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;rendition&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;book&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;renderTo&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;epub-viewer&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;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;100%&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;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;100%&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;stylesheet&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;props.bookStyle&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;allowScriptedContent&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;allowPopups&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;/pre&gt;&lt;/div&gt;


&lt;p&gt;这其中要注意的是&lt;code&gt;allowScriptedContent&lt;/code&gt;和&lt;code&gt;allowPopups&lt;/code&gt;的设置，由于&lt;code&gt;epub.js&lt;/code&gt;使用&lt;code&gt;iframe&lt;/code&gt;的&lt;code&gt;sandbox&lt;/code&gt;模式渲染，所以要启用所有功能必须开启。而&lt;code&gt;stylesheet&lt;/code&gt;会在下面的&lt;strong&gt;主题切换&lt;/strong&gt;一节说到。&lt;/p&gt;
&lt;p&gt;其次是分页，众所周知&lt;strong&gt;页码&lt;/strong&gt;是用来分割传统书籍的内容的，而&lt;strong&gt;EPUB&lt;/strong&gt;这种电子媒介中并没有这个东西。对于Kindle而言，页码其实是一种额外信息，由亚马逊特别处理还原传统书籍照顾读者习惯或者方便引用。而我显然是拿不到这种信息的，所以只能按照业界一般的估计，以&lt;strong&gt;600字&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;pages&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;book&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;locations&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;generate&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;/pre&gt;&lt;/div&gt;


&lt;p&gt;这样看起来似乎OK了，但如果只是这样，你会发现每次进入阅读都会很慢，因为生成页码是个非常耗时的操作。好在&lt;code&gt;epub.js&lt;/code&gt;提供了一个口子，我们只需要生成一次&lt;code&gt;pages&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;nx&amp;quot;&gt;async&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;savePages&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;book&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IBook&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;pages&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;span class=&amp;quot;k&amp;quot;&gt;return&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;bk&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;worker&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;nx&amp;quot;&gt;writeFile&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;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;book&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&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;nx&amp;quot;&gt;pages&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;json&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;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;stringify&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;pages&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;Books&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;c1&amp;quot;&gt;// 加载已存在的分页数据&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;book&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;locations&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;pages&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;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;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;jump&lt;/span&gt; &lt;span class=&amp;quot;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;action&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;EJumpAction&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;cfiOrPageOrIndex?&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;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;nx&amp;quot;&gt;IBookIndex&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;action&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!==&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;EJumpAction&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;span class=&amp;quot;nx&amp;quot;&gt;rendition&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;s1&amp;quot;&gt;&amp;#39;relocated&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;updateProgress&lt;/span&gt;&lt;span class=&amp;quot;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;action&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;EJumpAction&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Pre&lt;/span&gt;&lt;span class=&amp;quot;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;rendition&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;prev&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;nx&amp;quot;&gt;action&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;EJumpAction&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;nx&amp;quot;&gt;rendition&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;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;nx&amp;quot;&gt;action&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;EJumpAction&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;CFI&lt;/span&gt;&lt;span class=&amp;quot;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;// first, jump to chapter&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;rendition&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;display&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cfiOrPageOrIndex&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&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;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;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;c1&amp;quot;&gt;// then, jump to note&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;rendition&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;display&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cfiOrPageOrIndex&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&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;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;nx&amp;quot;&gt;action&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;EJumpAction&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;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;rendition&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;display&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;idToHref&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cfiOrPageOrIndex&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;IBookIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&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;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;nx&amp;quot;&gt;action&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;EJumpAction&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;span class=&amp;quot;nx&amp;quot;&gt;rendition&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;display&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rendition&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;book&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;locations&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cfiFromLocation&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cfiOrPageOrIndex&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&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;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;relocated&lt;/code&gt;方法监听，是为了在进度跳转后，向上一级同步页数。下面就是根据不同状况做的区分处理了：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Pre&lt;/code&gt;和&lt;code&gt;Next&lt;/code&gt;：针对普通切页操作，直接使用&lt;code&gt;prev()&lt;/code&gt;和&lt;code&gt;next()&lt;/code&gt;方法切换页面，这里不能用生成的页码是因为&lt;strong&gt;针对不同设备和画布，一屏显示的内容并不对应一页&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Index&lt;/code&gt;：针对目录索引，&lt;code&gt;idToHref&lt;/code&gt;可以通过&lt;code&gt;book.loaded.navigation&lt;/code&gt;处理得到。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Page&lt;/code&gt;：针对通过进度条修改的页码，可见是转换到&lt;code&gt;CFI&lt;/code&gt;处理。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CFI&lt;/code&gt;：这个比较特别，将在下一节详细论述。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;笔记、书签和进度&lt;/h3&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;interface&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;IBookNote&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;cfi&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;start&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;end&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;page&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;text?&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;annotation?&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;// timestamp&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;modified&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;removed?&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;interface&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;IBookConfig&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;ts&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;lastProgress&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;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;nx&amp;quot;&gt;bookmarks&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IBookNote&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[];&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;notes&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IBookNote&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[];&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;removedTs&lt;/span&gt;&lt;span class=&amp;quot;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;cfi&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;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;IBookConfig&lt;/code&gt;是每本书的配置文件，存于目录的&lt;code&gt;config.json&lt;/code&gt;中：&lt;code&gt;ts&lt;/code&gt;是更新时间戳，&lt;code&gt;progress&lt;/code&gt;是本地进度，&lt;code&gt;lastProgress&lt;/code&gt;是远端最新进度，这三个配合起来可以做进度同步。&lt;code&gt;bookmarks&lt;/code&gt;和&lt;code&gt;notes&lt;/code&gt;分别是书签和笔记列表，&lt;code&gt;removedTs&lt;/code&gt;则是为了解决笔记的删除问题，无奈下特意的一个优化用对象。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;IBookNote&lt;/code&gt;则是笔记或书签的数据结构：&lt;code&gt;cfi&lt;/code&gt;/&lt;code&gt;start&lt;/code&gt;/&lt;code&gt;end&lt;/code&gt;这三个都是&lt;code&gt;CFI&lt;/code&gt;下面会解释，&lt;code&gt;page&lt;/code&gt;是根据&lt;code&gt;cfi&lt;/code&gt;算出的页码，&lt;code&gt;text&lt;/code&gt;和&lt;code&gt;annotation&lt;/code&gt;是笔记专有的标注的文本和用户输入的注解，而&lt;code&gt;modified&lt;/code&gt;/&lt;code&gt;removed&lt;/code&gt;是时间戳，用于记录当前设备删除/修改一条笔记的时间，用于同步。&lt;/p&gt;
&lt;h4&gt;CFI&lt;/h4&gt;
&lt;p&gt;上面多次提到了&lt;code&gt;CFI&lt;/code&gt;，那么这到底是是个什么东西呢？让我们看看官方解释：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This specification, EPUB Canonical Fragment Identifier (epubcfi), defines a standardized method for referencing arbitrary content within an EPUB® Publication through the use of fragment identifiers.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;简单来说，&lt;code&gt;CFI&lt;/code&gt;或者说&lt;code&gt;epubcfi&lt;/code&gt;，就是用于表达对&lt;code&gt;EPUB&lt;/code&gt;电子书中某一段内容的引用。我们可以可以利用它完成对电子书中任一片段的定位索引，其形如&lt;code&gt;epubcfi(/6/12!/4[3Q280-46130e5d9d644673954c13edca4fc20f]/4,/1:325,/1:340)&lt;/code&gt;。具体的定义我不再赘述，有兴趣可以直接看&lt;a href=&amp;quot;https://idpf.org/epub/linking/cfi/&amp;quot;&gt;Specification&lt;/a&gt;，这里只讲我如何利用其完成的笔记功能。&lt;/p&gt;
&lt;p&gt;首先是书签，在上面我们提到过&lt;code&gt;relocated&lt;/code&gt;事件会更新进度，这个事件会返回一个&lt;code&gt;location&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;props&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;onBookmarkInfo&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;location.start.cfi&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;location.end.cfi&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;cfi&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;mergeCFI&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;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;cfi&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;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;cfi&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;kt&amp;quot;&gt;loc&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;modified&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Date.now&lt;/span&gt;&lt;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;location.start&lt;/code&gt;和&lt;code&gt;location.end&lt;/code&gt;分别是当前显示内容的起始和结束&lt;code&gt;CFI&lt;/code&gt;，我自己写了个方法&lt;code&gt;mergeCFI&lt;/code&gt;将它们合并起来备用。而在&lt;code&gt;onBookmarkInfo&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;parser&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;EpubCFI&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;kd&amp;quot;&gt;function&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;checkNoteMark&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;notes&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IBookNote&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;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;end&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;INoteMarkStatus&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;notes&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;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;exist&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;nx&amp;quot;&gt;index&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;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;notes&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;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;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;start&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;s&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;e&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;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;notes&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;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;cse&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;parser&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;compare&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;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;nx&amp;quot;&gt;ces&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;parser&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;compare&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;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;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;ces&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;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;exist&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;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;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;cse&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;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;exist&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;removed&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;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;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;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;exist&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;nx&amp;quot;&gt;index&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;notes.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;/pre&gt;&lt;/div&gt;


&lt;p&gt;这个方法传入一个书签或者笔记，返回其是否存在，以及在当前列表中的位置。为什么要做这个处理呢？因为前面说过——书签和笔记都是&lt;strong&gt;有序&lt;/strong&gt;的，所以我们不能随便插入了事，要先知道插到哪个位置。而有了位置信息，接下来的逻辑也就水到渠成了。&lt;/p&gt;
&lt;p&gt;比起书签，笔记的处理要更麻烦一些，因为其不是一个定点而是片段，同时还需要让用户自己去选中这个片段。UI层面的交互我就不多说了，无非就是堆点逻辑，其中比较重点的是&lt;strong&gt;如何获取选中的文字片段&lt;/strong&gt;。如果你去查文档，它会告诉你在&lt;code&gt;rendition&lt;/code&gt;中有个&lt;code&gt;selected&lt;/code&gt;事件可以解决，但事实上不行，我在阅读了源码后最终只能得到一个Hack的方案——在&lt;code&gt;locationChanged&lt;/code&gt;事件后，获取到当前的&lt;code&gt;content&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;rendition&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;s1&amp;quot;&gt;&amp;#39;locationChanged&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;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;off&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;selected&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;selectNote&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;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;rendition&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getContents&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;c&lt;/span&gt;&lt;span class=&amp;quot;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;on&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;selected&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;selectNote&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;content&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;setContent&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;p&amp;quot;&gt;});&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;而这个事件处理器&lt;code&gt;selectNote&lt;/code&gt;的逻辑也很简单，就是将其传入笔记标注工具组件。在这个组件中，我对每个传入的&lt;code&gt;CFI&lt;/code&gt;判断是否为新，如果是的话使用&lt;code&gt;checkNoteMark&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;range&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Range&lt;/span&gt; &lt;span class=&amp;quot;o&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;span class=&amp;quot;nx&amp;quot;&gt;range&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cfi&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;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;range&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;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;cw&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;s1&amp;quot;&gt;&amp;#39;epub-viewer&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;clientWidth&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;nx&amp;quot;&gt;setX&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;cw&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;/&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;setY&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;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;/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;c1&amp;quot;&gt;// 添加&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;rendition&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;annotations&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;s1&amp;quot;&gt;&amp;#39;highlight&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;cfi&lt;/span&gt;&lt;span class=&amp;quot;p&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;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;s1&amp;quot;&gt;&amp;#39;awaken-highlight&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;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;nx&amp;quot;&gt;rendition&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;annotations&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;remove&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;note&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cfi&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;highlight&amp;#39;&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;awaken-highlight&lt;/code&gt;是主题的一部分，后面会说。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;功能到这差不多完备了，但还有点体验上的细节：&lt;/p&gt;
&lt;p&gt;其一，一般来讲，对于一段已经标注好的笔记，我们往往希望点击它就可以弹出笔记工具栏，而不是需要选中。此时&lt;code&gt;rendition&lt;/code&gt;的&lt;code&gt;markClicked&lt;/code&gt;事件就可以帮助我们。&lt;/p&gt;
&lt;p&gt;其二，如果你看了&lt;code&gt;epub.js&lt;/code&gt;关于文本选择的源码，可以发现其处理非常粗暴：以选择开始为起点，在&lt;code&gt;150ms&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;EVENT_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;bk&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;supportChangeFolder&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;mouseup&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;touchend&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;Contents&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;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;onSelectionChange&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;e&lt;/span&gt;: &lt;span class=&amp;quot;kt&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;kr&amp;quot;&gt;const&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;k&amp;quot;&gt;this&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;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;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;doingSelection&lt;/span&gt;&lt;span class=&amp;quot;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;handler&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;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;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;removeEventListener&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_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;handler&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;selection&lt;/span&gt; &lt;span class=&amp;quot;o&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;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;getSelection&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;nx&amp;quot;&gt;triggerSelectedEvent&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;selection&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;nx&amp;quot;&gt;doingSelection&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;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;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;addEventListener&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_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;handler&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;nx&amp;quot;&gt;doingSelection&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;mouseup&lt;/code&gt;或者&lt;code&gt;touchend&lt;/code&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;private&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_mergeNotes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;localNotes&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IBookNote&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[],&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;remoteNotes&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IBookNote&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[],&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;removedTs&lt;/span&gt;&lt;span class=&amp;quot;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;cfi&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;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;IBookNote&lt;/span&gt;&lt;span class=&amp;quot;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;IBookNote&lt;/span&gt;&lt;span class=&amp;quot;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;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;localIndex&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;remoteIndex&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;pre&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IBookNote&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;less&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IBookNote&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;preRemoved&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IBookNote&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;localIndex&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;localNotes&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;nx&amp;quot;&gt;remoteIndex&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;remoteNotes&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;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;local&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;localNotes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;localIndex&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;remote&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;remoteNotes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;remoteIndex&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;comp&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;nx&amp;quot;&gt;local&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;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;remote&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;1&lt;/span&gt; : &lt;span class=&amp;quot;kt&amp;quot;&gt;parser.compare&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&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;remote&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;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;comp&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;nx&amp;quot;&gt;local&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;modified&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;remote&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;modified&lt;/span&gt;&lt;span class=&amp;quot;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;less&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;remote&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;remoteIndex&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;nx&amp;quot;&gt;less&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;local&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;localIndex&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;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;comp&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;less&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;remote&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;remoteIndex&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;nx&amp;quot;&gt;less&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;local&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;localIndex&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;c1&amp;quot;&gt;// local&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;less&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;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;removedTs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;less&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cfi&lt;/span&gt;&lt;span class=&amp;quot;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;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;less&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;span class=&amp;quot;nx&amp;quot;&gt;removedTs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;less&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cfi&lt;/span&gt;&lt;span class=&amp;quot;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;nx&amp;quot;&gt;preRemoved&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;less&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;c1&amp;quot;&gt;// remote&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;preRemoved&lt;/span&gt;&lt;span class=&amp;quot;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;cfi&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;less&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cfi&lt;/span&gt;&lt;span class=&amp;quot;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;removedTs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;less&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cfi&lt;/span&gt;&lt;span class=&amp;quot;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;o&amp;quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;less&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;modified&lt;/span&gt;&lt;span class=&amp;quot;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;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;c1&amp;quot;&gt;// remote&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;pre&lt;/span&gt;&lt;span class=&amp;quot;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;cfi&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;less&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cfi&lt;/span&gt;&lt;span class=&amp;quot;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;pre&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;modified&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;pre&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;modified&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;less&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;modified&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;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;removedTs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;less&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cfi&lt;/span&gt;&lt;span class=&amp;quot;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;o&amp;quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;less&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;modified&lt;/span&gt;&lt;span class=&amp;quot;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;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;less&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;pre&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;less&lt;/span&gt;&lt;span class=&amp;quot;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;code&gt;note&lt;/code&gt;的&lt;code&gt;removed&lt;/code&gt;属性仅存在于本地条目，而到了远端则变成&lt;code&gt;removedTs&lt;/code&gt;中一部分。这么做首先是由于和书籍同步同样的原因，我必须要在远端存下来某个条目是否被删除了；其次不像书籍一样直接同步&lt;code&gt;removed&lt;/code&gt;字段是由于一个笔记占用开销，同时按照上面这种优化实现逻辑上会出问题。&lt;/p&gt;
&lt;p&gt;如何合并完远端和本地的笔记与书签后，本地存一份然后立即同步到远端，搞定。&lt;/p&gt;
&lt;h4&gt;Webview默认行为&lt;/h4&gt;
&lt;h3&gt;Kindle笔记迁移&lt;/h3&gt;
&lt;p&gt;有了笔记功能，别忘了我最初是为了什么搞这个软件的——从Kindle迁移。所以终于到这一步了，就是如何将Kindle的书迁移过来，笔记也迁移过来。&lt;/p&gt;
&lt;p&gt;Kindle的书籍迁移已经有很成熟的教程了，可以参考：&lt;a href=&amp;quot;https://www.iplaysoft.com/kindle-download-dedrm.html&amp;quot;&gt;一键批量下载 Kindle 全部电子书工具 + 移除 DRM 解密插件 + 格式转换教程 (开源免费)&lt;/a&gt;，最后用&lt;code&gt;calibre&lt;/code&gt;转换的时候选择&lt;code&gt;EPUB&lt;/code&gt;即可。&lt;/p&gt;
&lt;p&gt;接下来就是笔记迁移了，亚马逊提供了几种笔记导出方案，本来想都支持的，后来由于时间不够懒得搞了就只支持&lt;strong&gt;从桌面端软件（PC/Mac）导出&lt;/strong&gt;的笔记，详细教程搜一下就有，最后导出的应当是名为&lt;strong&gt;XXXXX-笔记.html&lt;/strong&gt;这样的文件。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这里吐槽一下亚马逊的同行们，你们连个&lt;code&gt;html&lt;/code&gt;文件的输出都拼不对我真是服了...是因为&lt;code&gt;html&lt;/code&gt;本身容错率太高导致看了下能渲染就没检查了是吧？&lt;/p&gt;
&lt;/blockquote&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;code&gt;EPUB&lt;/code&gt;的&lt;code&gt;CFI&lt;/code&gt;就行，特别简单对吧？哪有这么好的事...在经过查询后，我大概了解了Kindle是怎么计算“位置”的：每&lt;strong&gt;128个字节&lt;/strong&gt;算一个“位置”，这也就解释了为什么你复制中文文本甚至导出的笔记里都会插入这么多空格（我猜的），所以这路子行不通，那可咋整呢？&lt;/p&gt;
&lt;p&gt;想来想去，最后还是只能用原始暴力的方法——文本搜索匹配。我直接把笔记拿出来全文搜索出那个片段总行了吧？正好&lt;code&gt;epub.js&lt;/code&gt;也提供了对章节的&lt;code&gt;find&lt;/code&gt;接口。但在满怀期待试了以后，发现...问题更复杂了：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;脚注：很多书中都会有“xxx[1]yyy”这样的脚注，而由于&lt;code&gt;EPUB&lt;/code&gt;本身是&lt;code&gt;xml&lt;/code&gt;结构的，脚注会被单独渲染为一个结点，但搜索是基于结点的，直接去搜索根本搜不到。&lt;/li&gt;
&lt;li&gt;段落：和脚注差不多的原因，搜索基于&lt;code&gt;xml&lt;/code&gt;结点，跨段落直接跨结点了，搜不了。&lt;/li&gt;
&lt;li&gt;重复：同一段文字会多次出现，对于笔记的“金句”而言不多见，但还是可能在译者评论中出现。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;为了解决这些问题呢，我搞了个挺恶心的算法，具体太长就不贴了有兴趣自己去看吧，大概说下思路：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;用&lt;code&gt;query&lt;/code&gt;的前N个字获取&lt;code&gt;rangeStart&lt;/code&gt;，后N个字获取&lt;code&gt;rangeEnd&lt;/code&gt;，最后合并。&lt;/li&gt;
&lt;li&gt;N看实际情况来取，而&lt;code&gt;textEnd&lt;/code&gt;需要在&lt;code&gt;textStart&lt;/code&gt;（无脚注link）/&lt;code&gt;linkEnd&lt;/code&gt;（有脚注link）的若干结点之内。 &lt;/li&gt;
&lt;li&gt;先判断是否有[\d+]的link，没有的话：&lt;ol&gt;
&lt;li&gt;小于六个字的，直接全文搜索。&lt;/li&gt;
&lt;li&gt;大于六个字的，拆成前六后六两部分，分别搜索后合并。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;有的话&lt;ol&gt;
&lt;li&gt;先查找到第一个link，然后反向搜索link前的文本的前（小于等于六个字），没有文本则直接以link为起点。&lt;/li&gt;
&lt;li&gt;然后找到最后一个link，正向搜索link后的文本的最后（小于等于六个字），没有文本则以link为终点。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&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;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;ITheme&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;color&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;background&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;highlight&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;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;IReadSettings&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;ITheme&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;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;fontSize&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;letterSpace&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;lineSpace&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;fontSize&lt;/code&gt;是文本大小，&lt;code&gt;letterSpace&lt;/code&gt;是字间距，&lt;code&gt;lineSpace&lt;/code&gt;是行间距，这几个单位都是&lt;code&gt;rem&lt;/code&gt;。而&lt;code&gt;theme&lt;/code&gt;就是最重要的主题了，&lt;code&gt;name&lt;/code&gt;是主题名，&lt;code&gt;color&lt;/code&gt;是文本颜色，&lt;code&gt;background&lt;/code&gt;是背景色，&lt;code&gt;highlight&lt;/code&gt;是标注高亮、注解、外部链接颜色。&lt;/p&gt;
&lt;p&gt;如何修改这些字段的UI逻辑没必要赘述，这里比较重要的是如何让这些参数产生效果。还记得前面说过的后端的&lt;code&gt;setBackground&lt;/code&gt;接口，以及初始化电子书时的&lt;code&gt;stylesheet&lt;/code&gt;字段吗？&lt;code&gt;setBackground&lt;/code&gt;配合阅读界面的背景颜色，来让异形屏客户端的非安全区统一颜色，这个没啥好说的。&lt;code&gt;stylesheet&lt;/code&gt;则可以给渲染&lt;code&gt;EPUB&lt;/code&gt;的&lt;code&gt;iframe&lt;/code&gt;添加一个&lt;code&gt;css&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;buildStyleUrl&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;settings&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IReadSettings&lt;/span&gt;&lt;span class=&amp;quot;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;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;style&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;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;color&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;span class=&amp;quot;nx&amp;quot;&gt;settings&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;nx&amp;quot;&gt;font&lt;/span&gt;&lt;span class=&amp;quot;o&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;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;settings&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fontSize&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rem&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;line&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;kt&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;settings&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fontSize&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;settings&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;lineSpace&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rem&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;letter&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;kt&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;settings&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;letterSpace&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;rem&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;touch&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;action&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;none&lt;/span&gt;&lt;span class=&amp;quot;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;webkit&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;touch&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;callout&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;none&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;word&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;break&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;break&lt;/span&gt;&lt;span class=&amp;quot;o&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;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;width&lt;/span&gt;: &lt;span class=&amp;quot;kt&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;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;nx&amp;quot;&gt;color&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;span class=&amp;quot;nx&amp;quot;&gt;settings&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;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;nx&amp;quot;&gt;decoration&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;none&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;border&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;kt&amp;quot;&gt;2px&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;solid&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;settings&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;highlight&lt;/span&gt;&lt;span class=&amp;quot;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;k&amp;quot;&gt;return&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;createObjectURL&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;Blob&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;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/css&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;/p&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;applyReadSettings&lt;/span&gt; &lt;span class=&amp;quot;o&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;rSettings&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IReadSettings&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;background&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;highlight&lt;/span&gt;&lt;span class=&amp;quot;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;rSettings&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;bk&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;worker&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setBackground&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
    &lt;span class=&amp;quot;nb&amp;quot;&gt;parseInt&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;background&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;substring&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;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;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;255&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nb&amp;quot;&gt;parseInt&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;background&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;substring&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;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;16&lt;/span&gt;&lt;span class=&amp;quot;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;255&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; 
    &lt;span class=&amp;quot;nb&amp;quot;&gt;parseInt&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;background&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;substring&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;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;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;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;255&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;sheet&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;s1&amp;quot;&gt;&amp;#39;global-style&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;HTMLStyleElement&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;sheet&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;textContent&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;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;awaken&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;highlight&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;kt&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;highlight&lt;/span&gt;&lt;span class=&amp;quot;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;important&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;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;opacity&lt;/span&gt;: &lt;span class=&amp;quot;kt&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;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;setBookStyle&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;buildStyleUrl&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rSettings&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;setReadSettings&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rSettings&lt;/span&gt;&lt;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;global-style&lt;/code&gt;的修改是为了笔记高亮的样式（&lt;code&gt;epub.js&lt;/code&gt;是用SVG实现的），而&lt;code&gt;setBookStyle&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;rendition&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;themes&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;s1&amp;quot;&gt;&amp;#39;awaken-style&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;props&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;bookStyle&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;rendition&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;themes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;select&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;awaken-style&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;开发完成后就是最终的构建发布了，这个在三端也有不同的做法。当然无论如何，第一步都是构建出生产环境的代码，我将最终代码构建到了&lt;code&gt;dist&lt;/code&gt;目录，以备后续处理。&lt;/p&gt;
&lt;h3&gt;桌面端&lt;/h3&gt;
&lt;p&gt;桌面端的构建很简单，&lt;code&gt;Tauri&lt;/code&gt;都帮我们想好了，我在&lt;code&gt;tauri.conf.json&lt;/code&gt;中指定了资源目录&lt;code&gt;build.distDir&lt;/code&gt;为&lt;code&gt;./assets&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;d&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;platforms/desktop/assets &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; rm -rf &lt;span class=&amp;quot;nv&amp;quot;&gt;$d&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; mkdir &lt;span class=&amp;quot;nv&amp;quot;&gt;$d&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; cp dist/* &lt;span class=&amp;quot;nv&amp;quot;&gt;$d&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;cd&lt;/span&gt; ./platforms/desktop &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; tauri build
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;最终的产物在&lt;code&gt;./platforms/desktop/target/release/bundle&lt;/code&gt;内。&lt;/p&gt;
&lt;h3&gt;安卓端&lt;/h3&gt;
&lt;p&gt;移动端相较于桌面端，需要自己区分开发和生产环境，而且对资源的处理也要更复杂一些。&lt;/p&gt;
&lt;p&gt;在安卓端，首先区分开发和发布环境是通过菜单的&lt;code&gt;Build&lt;/code&gt; -&amp;gt; &lt;code&gt;Select Build Variants&lt;/code&gt;窗口设置的，默认有&lt;code&gt;debug&lt;/code&gt;和&lt;code&gt;release&lt;/code&gt;两种模式，在不同模式切换后会有个全局单例中的变量&lt;code&gt;BuildConfig.DEBUG&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;nx&amp;quot;&gt;BuildConfig&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&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;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;nv&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;platforms/android/app/src/main/assets &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; rm -rf &lt;span class=&amp;quot;nv&amp;quot;&gt;$d&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; mkdir &lt;span class=&amp;quot;nv&amp;quot;&gt;$d&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; cp dist/* &lt;span class=&amp;quot;nv&amp;quot;&gt;$d&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;至于如何返回这些包内静态资源呢？也很简单，在前面我已经在客户端实现了Webview中&lt;code&gt;XHR&lt;/code&gt;的拦截，现在只要在里面添加一些逻辑即可。&lt;/p&gt;
&lt;p&gt;首先，在发布环境下，我需要将Webview加载的&lt;code&gt;url&lt;/code&gt;从调试的&lt;code&gt;dev-server&lt;/code&gt;的地址，换到我们拦截的&lt;code&gt;schema&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;mainWebView&lt;/span&gt;&lt;span class=&amp;quot;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;loadUrl&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;BuildConfig&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&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;p&amp;quot;&gt;{&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;host&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;s2&amp;quot;&gt;&amp;quot;http://awaken.api&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;strong&gt;在之前接口协议之外的&lt;code&gt;method&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;nx&amp;quot;&gt;fun&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;loadAsset&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;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;InputStream&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;mContext&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;open&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;url&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;p&amp;quot;&gt;{&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;index.html&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;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;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;/pre&gt;&lt;/div&gt;


&lt;p&gt;可见在安卓上对包内资源的请求是很简单的。在完成这些后，我们还需要给应用提供一个签名，用&lt;code&gt;Build&lt;/code&gt; -&amp;gt; &lt;code&gt;Generate Signed Build or APK&lt;/code&gt;即可。&lt;/p&gt;
&lt;p&gt;最终构建产物在&lt;code&gt;platforms/android/app/release&lt;/code&gt;中。&lt;/p&gt;
&lt;h3&gt;iOS端&lt;/h3&gt;
&lt;p&gt;iOS的工程配置是完全由XCode管理的，单纯从构建区分来讲并不复杂。&lt;/p&gt;
&lt;p&gt;首先是区分开发和发布环境，这个只需要在工程配置文件的&lt;code&gt;Build Settings&lt;/code&gt; -&amp;gt; &lt;code&gt;swift compiler - Custom Flags&lt;/code&gt; -&amp;gt; &lt;code&gt;Other Swift Flags&lt;/code&gt;中，给&lt;code&gt;Debug&lt;/code&gt;配置加上&lt;code&gt;-DDEBUG&lt;/code&gt;，给&lt;code&gt;Release&lt;/code&gt;配置加上&lt;code&gt;-DRELEASE&lt;/code&gt;，然后创建两个构建的&lt;code&gt;Schema&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;#if&lt;/span&gt; &lt;span class=&amp;quot;cp&amp;quot;&gt;RELEASE&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;
&lt;span class=&amp;quot;cp&amp;quot;&gt;#else&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;
&lt;span class=&amp;quot;cp&amp;quot;&gt;#endif&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;nv&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;platforms/ios/Awaken/assets &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; rm -rf &lt;span class=&amp;quot;nv&amp;quot;&gt;$d&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; mkdir &lt;span class=&amp;quot;nv&amp;quot;&gt;$d&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; cp dist/* &lt;span class=&amp;quot;nv&amp;quot;&gt;$d&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;随后在XCode项目配置的&lt;code&gt;Build Phase&lt;/code&gt; -&amp;gt; &lt;code&gt;Copy Bundle Resource&lt;/code&gt;中，将&lt;code&gt;assets&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;nv&amp;quot;&gt;rp&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;Bundle&lt;/span&gt;&lt;span class=&amp;quot;p&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;path&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;forResource&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;index&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;ofType&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;html&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;inDirectory&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;assets&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;do&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;nv&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;try&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;contentsOfFile&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;rp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;encoding&lt;/span&gt;&lt;span class=&amp;quot;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;utf8&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;wkWebView&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;loadHTMLString&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&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;baseURL&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;URL&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;string&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;awaken://awaken.api&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;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;catch&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;wkWebView&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;load&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;URLRequest&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;URL&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;host&lt;/span&gt;&lt;span class=&amp;quot;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;/pre&gt;&lt;/div&gt;


&lt;p&gt;可见这里比安卓要复杂不少，我先读取了本地Bundle内的&lt;code&gt;index.html&lt;/code&gt;，然后指定了我们实现了&lt;code&gt;XHR&lt;/code&gt;拦截的地址&lt;code&gt;awaken://awaken.api&lt;/code&gt;作为&lt;code&gt;baseURL&lt;/code&gt;。接下来WKWebView就会以这个地址为基础去请求JS、CSS等静态资源了。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;之所以要提前读取&lt;code&gt;html&lt;/code&gt;的内容，是因为通过&lt;code&gt;XHR&lt;/code&gt;拦截返回&lt;code&gt;index.html&lt;/code&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;kd&amp;quot;&gt;private&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;func&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;loadAsset&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;throws&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;FileHandle&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;nv&amp;quot;&gt;fp&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;url&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;&amp;quot;&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;index.html&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;url&lt;/span&gt;
    &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;nameExt&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;fp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;components&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;separatedBy&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;.&amp;quot;&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;nv&amp;quot;&gt;rp&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;Bundle&lt;/span&gt;&lt;span class=&amp;quot;p&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;path&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;forResource&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;nameExt&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;ofType&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;nameExt&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;inDirectory&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;assets&amp;quot;&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;nv&amp;quot;&gt;file&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;FileHandle&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;forReadingAtPath&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;rp&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;file&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;==&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;nil&lt;/span&gt;&lt;span class=&amp;quot;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;throw&lt;/span&gt; &lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;File load error: &lt;/span&gt;&lt;span class=&amp;quot;si&amp;quot;&gt;\(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;si&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;s&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;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;file&lt;/span&gt;&lt;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;p&gt;无论如何，我过去从开源社区已经索取了这么多，那么尽可能做出力所能及的回报也是必要的。无论做点啥总都比躺着伸手祈求强，毕竟——&lt;/p&gt;
&lt;p&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, 27 Dec 2022 23:33:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2022.12.27 23:33:article/Create-2022_12_27_a</guid>
<category>阅读软件</category>
<category>Hybrid</category>
<category>WebDAV</category>
<category>Android</category>
<category>iOS</category>
<category>Tauri</category>
<category>React</category>
</item>

<item>
<title>Project Self</title>
<link>http://dtysky.moe/article/Create-project-self</link>
<description>&lt;p&gt;褐色的枝干攀附于浅黄的墙壁，粉白的花簇拥着吊在其上，油绿的树叶反而成了点缀。风不大，偶有几片花叶被吹落到地上，笔直的水泥路灰里透着青，一直向远处的湛蓝天空延伸。我站在墙下抬头望去，阳光逆着我的视线透过树叶，照在了我带着蓝色美瞳的眼睛上。&lt;/p&gt;
&lt;p&gt;“对对对，就是这个姿势，保持一下！老天赏脸，这光真不错。”身后的摄影师@夏昊（以下简称X）背着他的富士GFX100S，语气轻松：“你今天状态感觉还好吧？”&lt;/p&gt;
&lt;p&gt;X和我同龄，几年前毕业于北影，据说刚毕业时吃过一阵子肉，但前些年电影行业开始不景气就离开了这行当。好在他家境不错，这两年便自由在家学习感兴趣的东西，不时出来给有缘人拍些写真赚点钱。&lt;/p&gt;
&lt;p&gt;“还好，虽然感冒还是有点。”我顶着些许的身体不适，努力保持着这个姿势。保持，意味着静止，而完全的静止，对我而言是不可能的。虽然肉体可以尽量不动，但无法停止的思绪却开始了游离。&lt;/p&gt;
&lt;p&gt;这段时间我似乎一直在感冒，说是感冒，其实就是持续性的头晕加嗓子不舒服。症状一般始于起床，午饭后逐渐缓解，晚上到家又开始。前几天我似乎开始适应了这种不适，但今天特别需要一个良好状态，所以出门前饭后我磕了个对乙酰氨基酚片。和年少时不同，现在的我不再会硬抗肉体的不适来获取某种悲壮的情绪。数次实践下选出的最适合自己的药，让我现在的症状相比早上拍和猫的合照时缓解了许多。&lt;/p&gt;
&lt;p&gt;想到了猫，便想到了早晨，对早晨的回想接替了感冒的主题。而按照惯例，这思绪又进一步跳跃，回到了我和X首次沟通的那天。&lt;/p&gt;
&lt;h2&gt;沟通-起点&lt;/h2&gt;
&lt;p&gt;沟通，对于我而言可以很简单，也可以很困难，这取决于具体的内容。当然，每个人都有自己擅长或不擅长沟通的领域，但像我这么极端区分的，应该是不多。以前我会将这些领域内容区分为很多种类，比如技术、工作、二刺螈、文学、哲学、情感问题等等，但现在想来，我大概仅会将其分为两类：取悦他人，或是取悦自己。&lt;/p&gt;
&lt;p&gt;我擅长取悦自己，即便是往常那对自我的折磨，很多也可以说是一种变相的取悦。但取悦他人，的确是我极不擅长的，所以我一般会尽力避开那些取悦他人的场合，来让自己得到最大的安宁。倘若所有的事情都能被如此干脆得划分，倒也不失为一种有益的秩序。但现实的本质既是混乱，又怎可能事事顺遂？现在摆在眼前的，便是一个特例。&lt;/p&gt;
&lt;p&gt;写真作为一个特例，包含了取悦他人和取悦自己的双重性质。这个取悦的他人倒不是说摄影师，而是源于我拍摄的初心：一个妹子对我社交账号照片的建议。她说“从女方视角，很多男的都很卷，各种花里胡哨，你也应该搞点让大家喜欢的照片。”我觉得蛮有道理，便认真考虑起了这个建议。“考虑”这件事本来并不是什么大事，我每天本就在考虑许多无关紧要的屁事，但前面加上“认真”二字就不同了。&lt;/p&gt;
&lt;p&gt;认真，在我的词典里优先级是极高的。任何事一旦加上了这个前缀，便会直接跃升为一个大工程，我会尽自己的所有可能将其做到最好。写真这事说起来，一开始不过是为了搞几张取悦他人的照片，按理说花个几百块拍套简单的、温柔的、沉稳的艺术照即可。但既然认真了，就需要考虑将其维度延展开。这一认真，一延展，一加工，我便又给自己挖了一个坑。和我之前定下的诸多类似《Project Heart》、《Project Tomorrow》、《Project Fake》等等企划一般，我脑海里突然浮现了一个企划。&lt;/p&gt;
&lt;p&gt;这个企划的名字便是《&lt;strong&gt;Project Self&lt;/strong&gt;》，而其核心便是三个人，或者说是三个人格，亦或说是三个投影比较合适：少年，少女，青年。&lt;/p&gt;
&lt;p&gt;不错，这三个投影，正是在我过去写给自己的私小说中，那数次出现的少年H、少女H和青年H。如此一来，这写真就成了一种记录和表达，那么它和写作也就相去无几。而我的写作，无论目的如何，取悦自己的比例一直远高于取悦他人。考虑到这，写真的初心就变了个味，从取悦他人变成了取悦自己。&lt;/p&gt;
&lt;p&gt;拍写真取悦自己，意味着需要高度的定制，定制又意味着预算和增加和详尽的沟通。对于现在的我，相比于预算这种小事，沟通显得额外艰难。作为一个INFP，我是比较擅长自我剖析和暴露，也逐渐不在乎他人的不理解，但此次却不能如此。如果将其也视为一种创作，那么和往常我一人构思和执笔不同，这一次我是将笔交予了对方。它不但要求摄影师完全理解了我的诉求，还要求对方能够认同，这样才能由衷给出那种表达。&lt;/p&gt;
&lt;p&gt;我写了一篇需求来描述我的意图，将其发给了几个朋友介绍的本地摄影大佬，但都被拒接了。其中有一位给出了“这个只能让一个比较熟悉你的摄影师”来的建议。循着这建议，我找到了这个深圳的北影出身的知乎KOL摄影师朋友。将需求发给他想让他介绍个广州的圈内朋友给我后，他便饶有兴致得以“我就喜欢创意型摄影”自己接下了这个单。&lt;/p&gt;
&lt;p&gt;摄影的报价是我给出的，出于对朋友的信任和避免某些无效的沟通，我咨询了圈内朋友后，给出了圈内的一般最高价格——三四千，对方表示可以：如果结果不满意三千，满意四千，而且排期自由。我觉得如果能达到要求，也算合理，便定了下来，具体时间则是在清明。&lt;/p&gt;
&lt;p&gt;我本来认为做好了这些前置准备，便只需要为了取悦自己而沟通，如此一来效果的达成也就是时间问题。但事后想来，取悦他人还是自己不过是结果论罢了，对于这样的一种非常主观的活动，本质上是双方需求的对抗与中和。对抗和中和，也就是沟通的另一种说法，但在这个侧面而言，沟通的实质其实是“要求”。&lt;/p&gt;
&lt;p&gt;极其不擅于提出“要求”的我，让拍摄饶了一些弯路。&lt;/p&gt;
&lt;h2&gt;第一日&lt;/h2&gt;
&lt;p&gt;拍摄的第一日，正是清明的第一天。由于感冒症状以及昨晚摄影设备和电脑的调试，我大约九点醒来，却挣扎到了十点多才起床。不过打开窗户后，和煦的阳光让我心情好了不少，天公赏脸，今日并未像预报一般是阴天。换了衣服走出卧室后，惯例的低血糖让我有些头晕，便当即决定吃了饭再出门。饭后磕了药，身体状况并未瞬间好了起来，但时间已然不早，我也不想拖延更久，便和X说先带着猫在小区内拍几张日常，之后再正式开始今天的外景。&lt;/p&gt;
&lt;p&gt;在这所有的拍摄之前，X先问了我一句：“看你的需求，到底是想要美化的，还是比较真实的？”&lt;/p&gt;
&lt;p&gt;我不假思索得回到：“我不想做一个伪物，所以当然是真实。”&lt;/p&gt;
&lt;p&gt;于是&lt;strong&gt;真实&lt;/strong&gt;便成了这系列拍摄的主基调，在这个基调下，我们带着滚滚和糖糖下楼了。&lt;/p&gt;
&lt;h3&gt;猫&lt;/h3&gt;
&lt;p&gt;小区坐落在海珠和番禺两个区的交界处，位置可以说是偏僻到鸟不拉屎，但其另一面也确实有足够大的面积、很低的容积率与不错的绿化。我们站在单元门口一眼望去，是一条被错落有致的树木包裹着的林荫小道。此时万里无云，明媚的阳光正透过树叶，向被砖红色和青涩投下粒粒光斑，非常适合取景。我们向前走了几步，X观察了周围，很快就确定了个地点。我走了过去，将猫抱了出来，开始了拍摄。&lt;/p&gt;
&lt;p&gt;“就这是吧？”城市来讲，如果可以列一个我不擅长的事情列表，“拍照”应该是能排在很前列的。所以在拍摄时，我第一想的便是专业的事交给专业的人，尽量遵从对方的指导。&lt;/p&gt;
&lt;p&gt;“再前面一点，对，就这几个光斑下面。”他指着地上的几个光斑，我带着猫走了过去。&lt;/p&gt;
&lt;p&gt;“糖糖！别瞎跑。”生性好动谨慎的糖糖刚下地就想着躲起来，她体型很小又白，匍匐在地，东张西望，见缝就钻。好在虽然我身体仍然不适，但在三年多的相处下早就学会了对付她。我将比较傻不拉几的滚滚放在一边，迅速拎起了糖糖的脖子，将她放回原地。如此重复三四次后，她可能暂时得习得性无助了，便不再反抗。&lt;/p&gt;
&lt;p&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/project-self/1.jpg&amp;quot; /&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;X的声音将我从回想中拉回，我走了过去，看了看他相机中的照片。情绪有点复杂，但还是回了句：“感觉不错，挺有日系清新的那个味。”&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/2.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;情绪复杂，不是在于对方拍摄技巧不好，也并非由于本应出现的那个麻烦：它还尚未出现。问题是在于，这和我本身设想的感觉确实不太一样。在我的设想中，摄影的第一个阶段是“少年”，而我的少年时光，与大众意义、也可能是X所理解的少年感，还是有不小差距的。&lt;/p&gt;
&lt;p&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/project-self/3.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;而X拍摄出的，是包容、阳光和温暖。&lt;/p&gt;
&lt;p&gt;此刻我本应提出一些要求，但虽然时常在文章或是其他场合不掩饰自我的特质，但在这种一对一的、我所不擅于应对的场合中，我将主导权完全交由了对方。“要求”此刻对我而言成为了非常困难的事情，便出于尽可能减少“沟通”的想法，选择了沉默。&lt;/p&gt;
&lt;p&gt;我们就这样相对沉默，顺着笔直的道路向前走着，寻觅着下一个拍摄的场所。走了不一会，我们便停在了一片露营地旁。他指了指不远的一处草丛，说：“这不错，你去坐到这个地方。”&lt;/p&gt;
&lt;p&gt;我便走了过去，很配合得坐了下来，之后又按照X的提示摆好了姿势。这里和方才不太一样，人多了不少，大多还都是妹子。虽然表面上力保那种体面和从容，我的心中还是有些不是滋味。即便是在社会上找到了许多自信的我，还是很不适应那种非常社会化的审视目光，这并非出于怯懦，而是出于一种由羞耻心和厌恶感交织而成的复杂情绪。&lt;/p&gt;
&lt;p&gt;抱有这样情绪的我，望着对面的他，感觉他手中的相机就像是一挺枪，将周围的那种审视浓缩成了对我特攻威力巨大的子弹，要将其射入我的胸口，铭刻我此时的惨状。&lt;/p&gt;
&lt;p&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/project-self/4.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;不一会，我们在这一块换了几个地点，拍了几张照片。整个过程中我们的气氛也逐渐缓和，不再那么沉默，随便聊了聊周边环境，广州的生活气息，甚至还有照片中那个可乐售卖机引发出的一系列关于时代感标语的探讨。然而拍完这一组后，我们的话题还是最终落在了那个麻烦的地方。&lt;/p&gt;
&lt;p&gt;“你是不太放的开的那种性格是吧？”X语气轻快，开玩笑般向我问道。&lt;/p&gt;
&lt;p&gt;“嗯，我是比较严肃。”&lt;/p&gt;
&lt;p&gt;“确实，你这个嘴角一直都是这样”他说着比了个︵的手势：“的，总有种生人勿近的感觉。”&lt;/p&gt;
&lt;p&gt;“哎，那也没啥办法，尤其是在相机前。”我无奈的口吻发自真心：“虽然以前恋爱开心的时候确实也︶过不短时间。”&lt;/p&gt;
&lt;p&gt;“那即便是单身，你也可以把这种状态延续下去吧，从你的一个目的来讲，这样会有更多人喜欢你，阻力也小一些。”&lt;/p&gt;
&lt;p&gt;“可能吧，不过既然你也创作过，应该可以理解︵的时候比较容易出好东西，而︶的时候一般是创作不能的。所以...”&lt;/p&gt;
&lt;p&gt;“OK，我理解了，那就按照这种感觉去拍也好。”虽然他这么说，但我明白他应该是理解了他心目中的那种少年感应该是无法达成了。&lt;/p&gt;
&lt;p&gt;下一组照片决定了在江边拍摄。我们过了马路，靠着石质的栏杆，眼下便是缓缓流淌的珠江。在江的对岸是数座林立的高楼，那便是广州的CBD珠江新城，也是这个城市房价最贵的地方。而此时不知是巧合还是命运，我的旁边，正是一个被放在栏杆墩子上的路灯。路灯虽然很小，但看到了它的我还是会心一笑。我不知道X是否也有这样的想法，但他最后敲定的地点就是这里。&lt;/p&gt;
&lt;p&gt;“好就这个姿势，眼睛看向十点钟方向。”&lt;/p&gt;
&lt;p&gt;我坐到了栏杆上，将双腿交叉，双手自然搭在两侧，按X所说望着十点钟的方向，静静等待着他的拍摄。十点钟的方向，是天空，天很蓝，云很少。群青的天空，CROSSING，群青学院广播社，孤独，这样一来确实有些我认为的少年氛围吧。所以至少在这一刻，我确信我应当展露出了少年的情绪，而当最终拍摄结束时，我看到样片时也终于确定了：我的少年时代，大概确实如此。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/5.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;拍完这组后，X提议这个形态室外差不多了，可以换个内景试试。离这最近的比较适合内景的，自然是广东博物馆。我们便回到了方才的那片露营地，绕着它走向博物馆，而在这个途中，一串泡泡在我们眼前飘过，X便有了想法：“你觉不觉得这个很有意思？”我虽然没有完全会意到，但觉得也算有趣，便和他一起走去，完全没有意识到前方的艰难：密度更高的人群，和他人的配合，更多注视着我的人，还多了小孩和他们的家长。不过最后的结果还挺好，虽然我仍然没能笑出来。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/6.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;h3&gt;过度&lt;/h3&gt;
&lt;p&gt;到博物馆时已经快四点了，我们被管理人员告知今日预约已满，便只好考虑接下来的行程。我原定的规划是去TIT，回工位换衣服后直接进入青年状态，在园区内取一些景物后入夜再到海心桥上拍夜景。但X的想法却有些不同，他建议中间加一个“过渡态”，来体现少年到青年的这种转变。&lt;/p&gt;
&lt;p&gt;我想了一下，感觉这个提议确实有道理。一来我确实带了三身衣服备用，二来如果说白日对应少年，黑夜对应青年，那么傍晚黄昏作为过度，也确实是一个绝妙的象征。于是我换上了第二身衣服，银白的休闲西裤，米黄色的衬衫，蓝灰色的休闲西装外套，加上黑框眼镜，恰好对应了白到黑的一个渐变，也体现了少年/青年特质的部分过度——&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;尚未完全颓废的桀骜不驯，尚未演变成疏离的脆弱，尚未完全伪装起来的自我&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;拍摄始于一个比较文艺的咖啡馆，我们的座位侧面是一个挺帅的、打扮讲究的小哥，后面则是看起来在暧昧的男女。X提议为我在这里拍两张照片，他拿起相机拍我的时候，我感觉其他几个人又审视起了我，这让我不太舒服，效果也就不太好。所以在些许休息后，我们终于走出了这里去了外景，我便松了一口气，恢复了状态。&lt;/p&gt;
&lt;p&gt;第一批正式的照片是在不远处拍的，TIT整个园区的绿化都做得很好，找一个枝叶繁盛的背景并不困难。我按照指导站在了一簇簇树木和花草之前，背对着它们后方的双层Loft：微信的访客中心。此刻已经接近傍晚，透过枝叶的阳光没有了之前的凌厉毒辣，变得温暖且柔和。我在X示意下，大概构想了一下应当摆出的姿态，结果仍然是理所当然的克制和拘谨。而后接着这个情绪，我们找个了比较僻静的角落，顺着台阶一路往上，又拍了几张表现出日常和带着颓废的照片。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/7.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/8.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/project-self/9.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/10.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;拍完后面这张后，X对我说：“是不是有你给的那个广州市人民医院的例子的feel了？”我粗略看了下效果，确实如他所言，相比起来病态的气质少了一些，但多了些肃穆的氛围，总体在我看来拍得很好，却也没有过多的感想。但当事后我再次打开它，我便明确了这是&lt;strong&gt;我今天最喜欢的一张照片&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;些许杂草和青苔丛生在脚下的木板上，像一张破败的地毯。红砖砌成的墙壁充满着岁月的痕迹，横竖两道水泥交叉着嵌在其中，像是半截巨大的十字架。我就站在这十字架的正下方，昂着头，宁静得闭着眼，任由斜射在墙上的阳光从半张脸上掠过。&lt;/p&gt;
&lt;p&gt;如此一来，这个阶段就算是结束了。我提议先去客村那边吃个饭再回来换衣服进入下个阶段，二人便向那边走去。而在快要到目的地时，X却停下来，并把我叫住，我回首一看，他正望着边上这条城中村小道，若有所思。我走了过去，没想到今天最考验我羞耻心的瞬间就如此开始了：在这个最为繁华的时刻，城中村的小道行人和车辆络绎不绝，他让我站在入口的正中间，逆着人流，尽可能摆出姿势停留...我照做了，于是今天&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/project-self/11.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;“我有这么胖，脸有这么方吗？”裸高173，体重62，照镜子认为自己是鹅蛋脸的我，看着这张被像是特稿封面的照片，不禁陷入了沉思。&lt;/p&gt;
&lt;h3&gt;青年&lt;/h3&gt;
&lt;p&gt;晚餐后已是黄昏尾声，我们回到了TIT换了最后一身衣服，进入今天最后的一个形态——青年，也就是现在的我。从最初的白天，到过渡的黄昏，我们终于来到了符合这个阶段的象征——夜晚。不过在去到规划中的拍摄地点之前，我们先拍了张搞事的照片——《某大厂被毕业无助青年》（逃&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/12.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/project-self/13.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;接下来便去到了二沙岛海心桥旁的艺术公园，开始了预定的行程。到岛上时，暮色已经缓缓降临，远方的天空自上方的紫色逐渐向下渐变为橘红，挡在前面那树木错落的枝丫在夜色的浸染下通体黝黑，静静得沉默着。我们拉着箱子过了马路，在路边观望着来来往往的人群，顺便寻觅着第一个拍摄地点。而就在此时，我忽然感受一股袭来的倦意，便毫不讲究得坐在了一根石柱上，将手撑在了行李箱上。&lt;/p&gt;
&lt;p&gt;“就这么来两张吧。”我下意识对着X提议到，于是第一张青年阶段的照片就这么诞生了。事后再看回来，它应当是准确得体现出了我想表达的青年阶段特质&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/project-self/14.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;坐了一会后，我恢复了一些精神，二人便顺着人流继续向前走，不知不觉就来到了海心桥边。海心桥横驾于珠江之上，被其上众多暖白的灯光的映照着，在这夜色中熠熠生辉。在它之下的珠江被微风带起了阵阵涟漪，水面上的倒影随着波纹摇摇晃晃，让身为半个图形程序我不禁在心中感叹：“这渲染果然还是TM得让三次元来啊，这反射，这高光...”&lt;/p&gt;
&lt;p&gt;而就当我这么想的时候，X示意我去往桥边不远处的护栏前方。我走到了那里，背靠护栏，看着他走到了我的左前方，便随着他的视线向右后望去。在立即明白了他的想法后，我微微侧头望向了十点钟的方向，尽可能表现出了第二个特质“伪装”。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/15.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&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/project-self/16.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/project-self/17.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;“你这张很像我的美术老师，二十年后再用吧。”他示意我看着照片。&lt;/p&gt;
&lt;p&gt;“嗯...确实有点显老。”但无论如何，至少这张确实大概表现出了第三个特质“疏离”。&lt;/p&gt;
&lt;p&gt;至此，第一天的拍摄结束，感冒药的药效似乎也完全过了。我脑袋有些昏沉，便拒绝了X邀请我去晚上的一个舞会以“认识圈内朋友”的邀请，打车回到了家后，又让司机将他送到了舞会现场。而在车上是，他提到了付款方式，我表示用大额资金习惯用支付宝，随后我问了句“你现在就要还是？”他表示“先给吧，少点利息。”于是出于信任和与人为善，我便将于先说好的三千先转给了他，最后的一千等全部完成了再给。&lt;/p&gt;
&lt;h2&gt;第二日&lt;/h2&gt;
&lt;p&gt;按照计划，本来应当是第二天就立马去棚里拍第三个形态“少女”，但由于X约定的另一个客户的突发时间安排，时间确实相对比较自由的我便调整了时间，改为了次日的下午。&lt;/p&gt;
&lt;h3&gt;前奏&lt;/h3&gt;
&lt;p&gt;大约下午四点半，我到了摄影棚，发现在场的除了X，还有一对情侣——这正是X的另一个客户，据说是他交了许久的朋友。既然他们还在这里，也就意味着还没拍完。起初我坐在沙发上看了他们一小会，觉得确实还蛮般配，尤其是知道他们异地了不少时间最终成了眷属的情况下。&lt;/p&gt;
&lt;p&gt;但过了不久，我便坐到了化妆台前，由X联系的化妆师给我化妆。&lt;/p&gt;
&lt;h3&gt;化妆&lt;/h3&gt;
&lt;p&gt;由于接下来有个场景还是需要少年/青年两个形态出境，所以我先画了个简单的淡妆，遮了个瑕，然后补了个少年/青年的形态，随后便进入了正式的化妆环节。&lt;/p&gt;
&lt;p&gt;化妆师应该是个三十多的姐姐，看起来已经有多年行业经验了。她一边用娴熟的手法给我上着妆，隔离、打底、遮瑕、修眉、假睫毛、眼线等等，一边还和我搭着话。&lt;/p&gt;
&lt;p&gt;“你这是搞COS嘛？”&lt;/p&gt;
&lt;p&gt;“啊？也不算吧，就是明年就三十了，给自己留个纪念。”&lt;/p&gt;
&lt;p&gt;“三十？看起来一点都不像。”她笑了笑：“感觉就和二十出头一样。”&lt;/p&gt;
&lt;p&gt;“谢谢，很多人都说我显年轻。”我已经不在意对方是不是奉承，说到底这也根本不重要。&lt;/p&gt;
&lt;p&gt;“不过你这想法挺好的，我都三十好几了，那时候也没想着给自己留个纪念。”&lt;/p&gt;
&lt;p&gt;“嗯...我是觉得还是得留下点啥吧。”完全是反射性的回答，我也不知道自己在说什么。&lt;/p&gt;
&lt;p&gt;时间就这样过着，过着，直到最后遮瑕的那一步。&lt;/p&gt;
&lt;p&gt;“小伙子你这皮肤有些差啊，遮瑕估计是没办法完全遮住的。”&lt;/p&gt;
&lt;p&gt;“嗯，最近在做激光试试，你尽力吧。”虽然表现得若无其事，但内心还是有些刺痛吧，但确实也没什么办法，我的基因和出身又不是我能决定的。&lt;/p&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;化妆大概在半小时左右结束，后面化妆师还很靠谱得帮我带上了美瞳和假发。在确定没什么问题后，我按照她和X约定的价格，将三百转了过去，随后换上了第一身衣服——白色LO，并走到了摄影棚的中心，形态“少女”的拍摄正式开始。&lt;/p&gt;
&lt;p&gt;这个棚是一个白棚，和我预想的不太一样。我本来以为回到一个实景棚，里面布置成一个温馨的小房间之类的，有些道具来互动，但这里除了白一无所有。不过既来之则安之，我不擅长提要求，也很讨厌麻烦，便只能对着X和后面到来的他的助理小妹妹，摆起了姿势。&lt;/p&gt;
&lt;p&gt;然后在几张样片后我可悲得发现，即便是穿上了女装，我也没有办法回到几年前穿LO时的那种纯洁可爱的感觉了。&lt;/p&gt;
&lt;p&gt;“这个，我有这么胖吗？”我指着样片，又看了看镜中的自己，感受到了明显的反差。&lt;/p&gt;
&lt;p&gt;“......”X似乎忙了一天，很疲惫，没有做过多回复。&lt;/p&gt;
&lt;p&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/project-self/18.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&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/project-self/19.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;而此刻的我也将这种显胖的原因甩给了“白色”，众所周知，白色的衣服本就容易显胖。于是我连忙换上了那身暗红色的日常LO，走到了镜头前。但在拍了几张后，感觉表情和神态还是不怎么对。于是X提议将镜子放在我的身边，以此来让我注意自己的动作和神情，而在这种优化的操作下，我像是忽然找到了那种日常的自我，出了几张感觉不错的图，虽然仍然没有笑容：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/20.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;日常这身拍完后，便是最后一身GOTH系的LO，设定里是由“白”反转而成的“黑”。在换上了这身衣服后，对着镜子的我感觉才找到了真正的自我，那种不协调的违和感瞬间消失，我不需要去强行伪装出任何表情，只需保持日常的不屑和高冷即可。也正因此，这一身的出片品质应该是今天最高的：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/21.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;h3&gt;晚餐&lt;/h3&gt;
&lt;p&gt;拍摄大约在九点左右结束，此刻的我已经非常疲惫，匆匆卸妆收拾完后便准备回家。但在这之前，我下意识问了一句：“你们吃了吗？”&lt;/p&gt;
&lt;p&gt;“还没有，那你请我们吃饭吧。”X如此回应。&lt;/p&gt;
&lt;p&gt;“OK，我找找吧。”我觉得大家确实也辛苦了一天，即便这一天里只有一半是在为我辛苦，但只要结果好，也无妨。&lt;/p&gt;
&lt;p&gt;于是很快我便打了个车，前往目的地：最近的一家海银海记。&lt;/p&gt;
&lt;p&gt;“那我就按我的习惯点了。”落座后，我按照习惯，将那老几样——三花趾，五花趾，吊龙，胸口朥，千张，粿条等加入购物车下了单。然后大家便闲扯了起来。&lt;/p&gt;
&lt;p&gt;“你是他带的学生，所以是准备考北影嘛？”我起了个话头，问了问对面看起来比我高的小妹妹。&lt;/p&gt;
&lt;p&gt;“嗯，考明年的。”&lt;/p&gt;
&lt;p&gt;“明年...你今年本科毕业嘛？也就是00的？”&lt;/p&gt;
&lt;p&gt;“嗯...我01的。”&lt;/p&gt;
&lt;p&gt;“......”虽然我一直号称自己永远年轻，抵制成长，但当一个真的01年应届生坐在我面前时，还是下意识感受到了自己的老去。&lt;/p&gt;
&lt;p&gt;“挺好，不过现在这环境，还去考电影是真爱啊，你本科是传媒吗？”&lt;/p&gt;
&lt;p&gt;“不是...是工科的。”&lt;/p&gt;
&lt;p&gt;“读研要三年，这不景气也不能一直这样吧，就赌一下三年后的环境。”X接着话茬，阐释着对方的选择并非没有道理。&lt;/p&gt;
&lt;p&gt;“说的也是，反正这年景哪行都不行，互联网也裁员，读研避避风头也好。”&lt;/p&gt;
&lt;p&gt;接着顺着这个，我们便聊起了疫情防控，进而聊到了康米，又转进到了情感问题，接下来又是青春与理想，然后是人的异化...&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;p&gt;众所周知“要求”是双向的，一个人提出，一个人接受或者否决。而倘若在一段重要的关系中一个人的要求总是被否决，那么提出要求的这个人便会渐渐沉默，这关系便会走向终结——按常理而言应该如此。但这世界上却并非所有关系都能被单方终结，在这个奴隶已经不存在的世界...不，90后的很多亲子关系，和主人奴隶之间的关系可谓神似。在许多家庭中，孩子在成长中逐渐都患上了习得性无助，便失去了“要求”的能力，或是即便没有失去，却也觉得“要求”本身是错误和可耻的。而我，自然也是其中的一员。&lt;/p&gt;
&lt;p&gt;我非常不擅长提出要求，取而代之的则是表面的讨好人格，与实际上“沉默-&amp;gt;消耗忍耐度-&amp;gt;结束关系”的链路。即便在毕业后终于离开了家中的六年中，我也只是学会了在职场合理得提出要求，在生活中即便是非常努力去改变，却也只是收获寥寥。这极大限制了我的能力边界，也让我白白遭受了许多无谓的伤害。&lt;/p&gt;
&lt;p&gt;这些伤害可大可小，我可以举出很多很小却可笑的例子。最近一次印象深刻的小事，是在公司咖啡店发生的。往常在我点了热柠檬茶后，店员一般会在上面套上一个隔热的套子，来防止烫手。而不知为何，从某一天后，店员就不会再主动套这个套子了。没有了套子的柠檬茶，握在手上，十分得烫，但在相当的一段时间呢，我的做法都是拿起就走。不错，即便这温度让我感到十分不适，我仍然忍耐着握着它走到了两百米外的办公区。&lt;/p&gt;
&lt;p&gt;就这样过了数次后，某天的我又像往常一样点了个热柠檬茶。而下一瞬间，我并未直接拿起它就走，而是像忽然开窍一般，拿起了旁边的一个套子，自己套上了。看着此时手中的柠檬茶，我并没有往常那样灼热的触感，这让我恍惚了一下，然后忽然意识到了什么，接下来便是盘旋在我脑海中的一句话：&lt;/p&gt;
&lt;p&gt;“他不给，你就不能要？他妈的我凭什么不能要？”&lt;/p&gt;
&lt;p&gt;伴随着这句话，过去二十八年的种种场景便如潮水一般灌入了我的脑海。其中记忆最深刻的，应该是我大概在大二的某个夜晚。那晚我忽然感受到了强烈的牙痛，那是一种钻心的疼痛，让我坐立不安。但我第一时间想得居然不是去医院，而是忍着，是喝凉水，是用纸团顶着它，是用种种手段来抑制疼痛，以此期望它自己过去，不要带来更多的麻烦。即便是后来实在忍不住去了医院，我一开始也没有告知父母，而是想着通过自己的生活费省吃俭用省下这就医的钱。直到被医生说很严重，不得不花几千块钱来做烤瓷牙套时，我才不得不打电话给父母。&lt;/p&gt;
&lt;p&gt;如此正当的一个要求，当时的我却至始至终觉得自己像个贼一般，不配得到这样的治疗，浪费了他们的钱。但时候细想，他们缺这点钱吗？并不缺。我的牙变成这样是我自己造成的吗？并不是。那为什么我会感到这么耻辱？那么我只能认为这是从小的各种经历和教育，让我养成了一种我之前并未意识到的性格：&lt;/p&gt;
&lt;p&gt;厌恶和他人争执，厌恶麻烦别人，厌恶请求他人。不错，我并不是不懂得“要求”，只是不喜欢“请求”罢了。因为请求意味着亏欠，而我不想亏欠任何人。所以我只能用一些其他的策略来让他人认为“应当主动给我”，而不是“我向你要的”。但显然，这种手段只对于亲密关系能产生作用，而且基本都有着严重的副作用。对于商业化的交换，这最终只能导致自己吃亏的同时，还憋着一肚子气。&lt;/p&gt;
&lt;p&gt;所以回归到这次拍摄来看，我这“不愿请求”的习惯，确实让我们的拍摄饶了一些弯路。但好在后续和朋友的沟通下，他们给出了一些分析和建议，说“你既然支付了成本和精力，那应当需要让自己满意”。这让我再次意识到了我去“要求”的正当性和必要性，于是便联系了X，来为了补上第三日的拍摄：室内，以及一个实景棚的剧情编排。&lt;/p&gt;
&lt;h2&gt;准备&lt;/h2&gt;
&lt;p&gt;定下了第三日拍摄日程，便要开始准备了。准备分为两个部分，其一是对家中的收拾，让其变得更加整洁以适应拍摄。&lt;/p&gt;
&lt;p&gt;家中的收拾倒是没什么，也确实该来个大扫除了。我对这种事的启动成本虽然一向比较高，但一旦启动了就会尽可能做好。从卧室飘窗开始，书桌，床上，客厅沙发，电脑桌，餐桌...垃圾收拾得差不多后，我开始拿着吸尘器处理灰尘。从地面，到书架，再到电视柜，以及架在电视柜两旁的音箱...&lt;/p&gt;
&lt;p&gt;“...这是？”我停了下来，望着其中一个音箱上的日历。这是去年公司发的新春礼品的一部分，而其上的日期则一直停留在了2021年的7月份。&lt;/p&gt;
&lt;p&gt;“......”脑中忽然闪过了一些回忆，它们一件件稍纵即逝，却又连绵不绝：“你追求留下痕迹，我追求飞过和体验”，“你追求的是无以轮比的闪耀”，“可是代价呢？”......&lt;/p&gt;
&lt;p&gt;“行了行了，瞎几把想啥，干活。”把日历叠起来扔到垃圾袋后，房间很快便被我顺利打扫干净了。&lt;/p&gt;
&lt;p&gt;收拾房间后的第二件事，则是推演和购置棚拍所需要的道具。吸取上次的经验，以及按照编排的剧本要求，我列了一个需要购置的物品清单。按理说这些东西应该是平平无奇，网上随便就能买到的，但实际过程却非常艰难，也让我学到了一些奇怪的知识。&lt;/p&gt;
&lt;p&gt;首先是在黑色男装形态手中的那把枪，我本以为搞一把没有实际发射功能的、一比一的模型枪应该不难，但一开始搜遍全网，却都只能买到1:2.05的模型。在纳闷之中我查了下原因，才知道现在对枪模的管制已经如此之严格，哪怕是外形一样没有功能都不行，为数不多可以等比例的枪模还必须报备，交由专业的剧组团队保管。我虽然可以理解这政策，但着实也带来了很多麻烦。当然好在经过大量的搜索和碰巧，最后我还是找到了一把金属外壳、等比例的、可以发射软弹的左轮手枪，也算是有个好结果。&lt;/p&gt;
&lt;p&gt;然后是GOTH女装形态手中的《圣经》，我本来只是想着搞个道具，想着这问题应该不大吧即便没有那买本书拿着也不是不行，结果无论是淘宝还是京东一搜索都发现根本没有结果。于是我在纳闷中又去查了下原因...嗯，原来如此，那也没招。最后我只能用家中那本海德格尔的《林中路》来凑合替代一下。&lt;/p&gt;
&lt;p&gt;再者在国内电商网站上和“圣经”有着相似待遇的，还有手持的金属十字架。我已经不想再去查原因是啥了，就直接放弃了这个构思，换了种构图。&lt;/p&gt;
&lt;p&gt;总之虽然遭遇了意想之外的麻烦，但也勉强算是准备好了所需要的道具，它们被我收拾了起来，静待着拍摄日的到来。&lt;/p&gt;
&lt;h2&gt;专访直播&lt;/h2&gt;
&lt;p&gt;在这第三次的拍摄日程前，还插入了一段《腾讯程序员》视频号对我的专访。这个专访缘起于今年二月内部的一个邀请，但由于疫情一次次推迟，直到现在才终于定下了日期。虽然还有少许风险，我还是决定义无反顾去了直播室。这首先是源于我对不可控性的厌恶——那是早已准备好的事情的一再延误，所派生出的那种悬而未决的感觉。再者，则又是一个宿命般的巧合，因为我忽然意识到——这次专访，不正好也可以算作是这《Project Self》的一部分吗？&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;“写真”静态地从外侧记录了我疏远和断绝于这个世界那封闭的“自我”，“访谈”则动态地自内部挖掘了我连接于这个社会那开放的“外延”。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;直播的整体来看算是成功，即便是一开始由于我的小概率事故体质导致除了一些故障（笑），让直播整体延期了半个小时，导致流失了一些观众，但最后数据还是不错：&lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://www.bilibili.com/video/BV1kY4y187my/&amp;quot;&gt;直播访谈录屏&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://mp.weixin.qq.com/s/M-x5wLvFEhRXAyZOmUlTUg&amp;quot;&gt;访谈后续公众号发文&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;访谈结束的第二天，便是最后一日的拍摄了。&lt;/p&gt;
&lt;h2&gt;第三日&lt;/h2&gt;
&lt;p&gt;第三日比原计划晚了一些，但也正碰巧——那是近一周天气最好、没有雨的一天。这次拍摄起前两次有两点区别。其一是我叮嘱了X别拍那种文艺的感觉，早上就搞一些日常的记录，下午就好好拍剧情编排；其二，则是这次多了一个特别的摄影助手。&lt;/p&gt;
&lt;p&gt;这个摄影助手并非和上次一样是X的学生，而是我的一个朋友。这位中大文学在读博士朋友（下称S）曾经鸽过我两次（笑），由于认知上相对接近，比较聊得来，所以认识了快半年还会不时聊聊天。当之前得知我要拍写真时，她觉得这种创作很有趣，就说要来给我当助理，但由于疫情封校遗憾错过。所以得知我还要补第三次，而她正好也能出校，便二话不说做好了计划。&lt;/p&gt;
&lt;h3&gt;日常&lt;/h3&gt;
&lt;p&gt;我们大概在九点半起了床，此时早晨阳光正好，我将滚滚从阳台抱了出来，开始了第一个场景的拍摄。在这个场景中，我的本意是体现出我在做独立游戏的感觉，显示器上的内容也正是如此：一个32寸的屏幕分成了四块，一块是Unity，一块是写逻辑的VS，一块是写剧本VSCode，一块是框架文档。&lt;/p&gt;
&lt;p&gt;然而X似乎并没有回到我的意，而是直接从左前方，也就是显示器的背面进行了拍摄。当然我是可以理解从我侧后方拍的话，位置比较憋屈，所以也没有多说什么。我照计划装模作样敲起了文字，按理说这场景很快就应该过去，但这臭猫不知道犯了什么病，开始折腾。&lt;/p&gt;
&lt;p&gt;“滚滚，你在干啥这么不配合？”我一边按住它的身子，一边将其拖回原位：“你个臭猫，我好吃好喝伺候着，看病花了我四五万，就配合我拍个父慈子孝都不乐意？”&lt;/p&gt;
&lt;p&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/project-self/22.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/project-self/23.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/24.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/25.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;h3&gt;正片&lt;/h3&gt;
&lt;p&gt;拍完早上的日常照后已是接近中午，不一会S便到了小区楼下，我在接她的同时也去楼下趟花店，买了束郁金香来作为今天最后的道具。一切准备就绪，我们就打上了车，前往今天本来定好的目的地——81号摄影棚。&lt;/p&gt;
&lt;p&gt;81号摄影棚是一个广东COS摄影圈内的朋友推荐的，我看了看它家的微博宣传图，觉得里面教堂这个景挺不错，后续虽然也和另一家对比了下，但由于感觉这家的态度比较好，也没想太多就定了下来，其收费是：两小时起订，一小时120，定金150。&lt;/p&gt;
&lt;p&gt;本以为到了地方三个多小时即可结束今天的计划，但实际到了这个坐落于城中村后的棚却让我大跌眼镜：三楼小小的场地，随意凌乱摆放的破旧道具，不伦不类的两侧幕布，以及甩手不过问任何布置的老板。我们在不断调整布景，来来回回忙活了一个多小时后，大家都达成了确实没办法达到我想要的效果的共识，于是当断则断支付了两个小时的费用后及时撤离了。&lt;/p&gt;
&lt;p&gt;撤离后的我们站在路上，已是接近下午三点。我们徘徊在原地，不知如何是好。按照我的规划，找一个真是的教堂，例如“圣心大大教堂”自然是最合适的，但这种地方显然不会让我们进去。&lt;/p&gt;
&lt;p&gt;“要不我先申请入教，然后去拍，拍完了再退教？反正我比他们大对信徒对上帝的理解更深刻。”&lt;/p&gt;
&lt;p&gt;“你可别扯了，人家是要让你信的，谁让你理解了。”&lt;/p&gt;
&lt;p&gt;“哎...”&lt;/p&gt;
&lt;p&gt;犹豫，踟蹰，打趣解决不了实际问题。但我忽然灵光一闪，找到了之前那家觉得态度不好而放弃的棚，在S一个电话得到对方回应后，我们便打了个车，火速去了那里，然后发现这确实是一个质量不错的实景棚。&lt;/p&gt;
&lt;p&gt;“虽然还是不大，但这40平的实景棚看起来已经是广州能找到的最适合的地方了，就今儿拍完吧。”我们做出了决定，拍摄就这样开始了。&lt;/p&gt;
&lt;h4&gt;准备&lt;/h4&gt;
&lt;p&gt;这最后一次的拍摄，重点在于“情节”，情节需要演员出境铺就，而这些演员则是我的多个人格。既然是多个人格一起出镜，那必然就会伴随着技术上的合成，而合成，则需要固定的机位和固定的站位。&lt;/p&gt;
&lt;p&gt;为了达成这个目的，我事先准备好了标签纸和记号笔，同时做好了每个情节、每个角色的序号编排，例如第一个场景的白衣少年是“WB S1”，第三个场景的黑衣少女是“BG S3”等等...写好这些标签后，我就需要把它们贴到地面上，来标记到时候我拍摄应当站在的位置。&lt;/p&gt;
&lt;p&gt;图为S配合我演绎情节中的站位，同时我贴标签的场景：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/26.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;准备结束后，拍摄便正式开始。我总共换了四身衣服，来表现四个人格，而每个人格也基本都有符合自己设定的单人照，以及最后那用于合成的十个场景。至于具体剧情，我就不文字描述了，只能说...我尽力了。&lt;/p&gt;
&lt;h4&gt;单人格&lt;/h4&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/27.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/28.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/29.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;h4&gt;多人格&lt;/h4&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/s1.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/s2-1.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/s2-2.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/s2-3.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/s3.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/s4-1.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/s4-2.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/s5.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/s6-1.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/s6-2.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;h2&gt;结语&lt;/h2&gt;
&lt;p&gt;经过了将近五个小时的漫长拍摄，除了最后的合成，整个企划终于宣告结束。老板人还是不错的，最后收了三个小时的钱，一小时180，加上妆娘的150，加起来共700。为了庆贺，我请二人吃了顿火锅，在整个庆功宴和最后的返程中，我们又闲聊了许多琐事：我个人在理想和现实中游走平衡的焦虑，X事业和生活间的挣扎和平衡，S学业和规划上的压力等等。想来这次聚餐与上次最大的区别，大概就是在于01年的学生妹子换成了94年的在读博士妹子吧，年龄相仿的三人在一起，讨论的话题自然也就现实了许多。也是因此，我再次意识到：无论外在和人设怎样表现出一种青春少年，但人生阶段确实是不同了。&lt;/p&gt;
&lt;p&gt;在回到家中后的第二天，我整理出了S帮我站位的一些花絮发给了她，聊起整个拍摄过程，她直言：“我觉得你的优点挺多的，尤其是细致和认真。”&lt;/p&gt;
&lt;p&gt;面对这个这个评价，我只能无奈一笑：“原来我展现给别人的是这样一种值得褒奖的品质吗？”作为一个标准的INFP，我太清楚自己的拖延和惰性了，但早已根植于我内核的“决定了就一定要做好”的诅咒，却让我不得不花很大精力去反抗我的本能。这一次次细致的规划和设计，无不伴随着极大的焦虑和数次的失眠，客观来讲这对我的身心造成了不小的负担。不但如此，我之所以会这么早就做好尽可能全面的规划，同时事必躬亲不喜欢他人插手，应当还是出于一种深刻的“不信任”——&lt;/p&gt;
&lt;p&gt;似乎从某个时刻起，我就无法由衷得对任何人产生信任了，无论是朋友，还是父母，亦或是其他时刻的我自己。&lt;/p&gt;
&lt;p&gt;自相矛盾，却又强行保持一致。有憧憬光明向其奔跑的一面，也有封闭于漫长黑夜隔绝一切的一面。是一个能很好运用逻辑和理性工作的青年，也是一个拥抱非理性在荒诞和虚无中挣扎的少年。口口声声说着完成最优先，却又无法真的脱离完美主义的陷阱。不断努力尝试扎根于现实的生活，却还是只能悬于空中无法真正得落到地上。&lt;/p&gt;
&lt;p&gt;这样的我未来会成为一个怎样的人呢？会像五年前、十年前的我对现在的我的态度一样吗？我不知道。但我能确定的是，几年、十几年、甚至几十年后的我看到这个项目，一定会产生一个疑问吧：“这个时刻的我，也就是你，究竟是想记录什么，又想表达些什么？”&lt;/p&gt;
&lt;p&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>Saturday, 30 Apr 2022 00:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2022.04.30 00:00:article/Create-project-self</guid>
<category>记录</category>
<category>自我</category>
<category>写真</category>
<category>访谈</category>
</item>

<item>
<title>我为什么写作</title>
<link>http://dtysky.moe/article/Art-我为什么写作</link>
<description>&lt;p&gt;本篇内容是&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/463381291&amp;quot;&gt;《回顾2021》&lt;/a&gt;的最后部分。&lt;/p&gt;
&lt;p&gt;一开始起这个标题的时候，我是觉得配不上。毕竟早有奥威尔出版的同名自传在前，而我写作水平都尚未成熟，更遑论作为一个作家，自然就显得有些不自量力。不过仔细想想，却也并没有更合适的说辞了，那就索性如此吧。&lt;/p&gt;
&lt;p&gt;“写作”这个词可以有很多种阐释。可以说是一门营生的手艺，可以说是一种日常的消遣，也可以说是一种情绪的表达。但无论哪种，作为一种特别的精神活动，写作，尤其是严肃的写作，都是作者和自身的斗争与和解。而在艰难的生活中，人一般不太会想着和自己和解，在轻松的生活中，人一般不会想着和自己斗争。所以有着写作的念头的人，或多或少都有些别扭。&lt;/p&gt;
&lt;p&gt;在十几岁的年纪，少年少女大多容易伤感，这种利于创作的客观环境，比较容易生成写作理想。但随着时光流逝，青春不再，理想情怀被生活的柴米油盐挤到一旁，不顺的被压榨得喘不过气，顺利的想着跃升阶级，除了以写作为生，大都早早抛去了什么当作家的念头。&lt;/p&gt;
&lt;p&gt;理想不能当饭吃，混口饭吃还是很重要的。毕竟“人不吃饭，就会饿死”。而饭吃饱了，又会想着玩乐，也就是精神生活。写作，当然也是一种精神生活，但在疲惫的日子中，这种精神生活也太过自虐了，远不如出去旅旅游、找俊男美女谈谈恋爱、下点昂贵的馆子，最后再发发朋友圈、晒晒微博小红书以换取艳羡来的容易。&lt;/p&gt;
&lt;p&gt;所以在奔三的年纪，不靠写作混饭吃，还一意想着写作的人，着实是有些别扭、甚至是扭曲了。在我的观察看来，这些别扭的人都有些相似，那就是——对痛苦的挖掘和感受能力强，并似乎乐在其中。而作为其中的一份子，我当然也不例外。&lt;/p&gt;
&lt;p&gt;我对痛苦的感受能力很强，以至于有时不得不刻意进入一种“隔绝和抽离”的状态，来让这种“天赋”不影响我自己的生活。另一个拥有同类天赋的朋友曾说过——“写作，就是记录痛苦”。我对这个说法深以为然，并补上了一句“而当痛苦不在的时候，我们就会去寻找痛苦”。但细想后却发觉这并非是根源，毕竟就算是自虐，也没有人天生就喜欢自虐。&lt;/p&gt;
&lt;p&gt;既然不是天生，那必然就是后天养成的，于是我只能从小时候开始寻思。&lt;/p&gt;
&lt;p&gt;对小时的记忆我是比较模糊的，明确的也只有随父母四处奔波，但在这模糊之中却也有些场景相对印象深刻。比如由于ADHD尿床不愿去幼儿园，而被母亲绑在店口的柱子上打；再比如被留守到四川老家，一年见不到两次父母，学英语还被亲戚骂；又比如转学回四川，因为不会方言被欺负，终于在哀求下从四川终于转学回父母身边后，却又因为说方言被欺负；还比如每次考不到全班前三回到家时，都会因为担心被打被骂瑟瑟发抖。&lt;/p&gt;
&lt;p&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;遭受了对作为那时绝对权威的班主任的攻击后，我却并未感受到恐惧。虽然现在看来这不过是一个普通的青春期插曲，却也算是一个开端。从这个开端为始，我就在潜意识中构造了一个“框”，努力将自己框入其中，来维持某种“我所应当”。不过有个框很容易，落实到行为将自己框起来却很难，更何况是易变的少年。所以我尝试借助一些外力，这自然就是“文字”——“用文字影响外界对自己的印象，倒逼自己成为一个那样的人”。&lt;/p&gt;
&lt;p&gt;从这个角度而言，从那一刻起，我的存在便在追逐着我的文字。而文字又并不能凭空产生，它们必然要有一些养料作为来源，而想起来我的养料最初应当是来自于大家熟知三国、西游，以及周树人。于是能力不足以意识到存在和文字之间关联的我，便误以为我应当真正追求的理想是——“成为英雄”，并凭借我自小的固执坚持了下去。&lt;/p&gt;
&lt;p&gt;“成为英雄”是大多男孩子都有过的想法，但在真能挺身而出却寥寥无几，大多也不过成为了自以为厉害的混混。毕竟对权威的恐惧是自小培养的。但不知为何，我从小对这种对权威的知觉却很淡薄，或者说没有概念。虽然不时会被体罚，也确实有过害怕，但在内心中我却从未认为那些被称作“老师”的存在，拥有对我的“管束权”。&lt;/p&gt;
&lt;p&gt;这种思想在闭塞的地方着实异类，但好在成绩不错，老师们也没有太刁难，但在大多还是会管教。但到了高中，却发生了一些转机，我进了以素质教育见长的省重点的重点班，有了一个语文老师兼任的班主任。这个班主任比较开放，对我的个性和想法几乎是完全支持，而单纯的我受到这些鼓励后，自然是有了更加激进的思考。于是在一次语文作文中，我以“刘邦和项羽生活在现代”伪命题，褒扬了项羽的直率和磊落，贬斥了刘邦的阴谋和无耻，并得到了老师切实的夸奖——你很有灵气。现在回想起来，这夸赞应该只是惯用的措辞，但就是这样的一些夸奖，让我认为我确实是有责任要去创作的。&lt;/p&gt;
&lt;p&gt;这个时候，我尚且还没有将“成为英雄”和“写作”关联起来，而是单纯建立起了极强的表达欲。为了支撑起这种表达欲，在写作之外，我尝试读了一些理论著作，比如一些哲学专著。这些当然都不过是浅浅的了解，甚至一部分是为了装逼，但启蒙的种子也切实种下了。从那时起，我就已经开始对“命名权”、“发声权”、“管束权”这些有了懵懂的印象，并尝试写了一些这样的作文，不过都因为言之无物，而收获寥寥。&lt;/p&gt;
&lt;p&gt;再之后，我接触到了二次元，那个年代还完全充满着“真善美”的二次元，出现了一大批亲情、友情、爱情和个人英雄主义的佳作。这些作品也直接将我从现实主义的萌芽，彻底转向到了浪漫主义，并延续了本已漫长的中二期。在这段时间，我也大致定下了一个剧本的初步设定，非常简陋，但满怀着一个少年的真诚。与此同时，我开始寻求“特别”。而后我慢慢得在无意识中，又将这种对“特别”的追求，和“成为英雄”在暗中关联了起来。但毕竟高中还是要应试的，即便是相对的素质教育学校，应试仍然是最后的追求，我不得不应付这些要求，而放弃了一些“自由”的思考。&lt;/p&gt;
&lt;p&gt;在这段时间，写作，应当只是一种青春情感的自然抒发，带着一些少年特有的理想主义，在束缚中作着青涩的表达。到了大学的时候，这种种束缚，也就被彻底解放了。&lt;/p&gt;
&lt;p&gt;大一的我是混沌和浑噩的。来了一个并不喜欢的工科专业，学着并不喜欢的东西，考了并不如意的GPA，思考也似乎停滞了。但我的表达欲终究还是冲破了这种浑噩，在某次契机之后，在一个学姐的鼓励下，我想起了高中那份青涩的游戏设定，下定了决心。而就在下定决心的那一刻，我的大学也彻底改变了。&lt;/p&gt;
&lt;p&gt;我一改之前懒散的作风，开始每天雷打不动得创作剧本。从设定开始、到大纲、到实际的剧情，我花费了大量的心力，甚至现在还能回想起那每天因为在脑中构思剧情，而在路上起的鸡皮疙瘩和由衷的震颤。同时为了增强剧情的理论依据，我还特意从培根读起，沿着霍布斯、休谟、边沁的路径，阅读和抄写原著。&lt;/p&gt;
&lt;p&gt;而另一方面，伴随着文字表达欲一齐被唤起的，还有别的创造欲望。我几乎一人申请了一个省级SRTP项目，意图在什么都不懂的情况下，完成一个涉及到FPGA、软件编程、电路设计、机械的体三维显示器，而且一上来就是“120x120LED阵列”。&lt;/p&gt;
&lt;p&gt;那时的我没有任何恐惧，也没有觉得有什么是我所做不到的，这也应当是我认为离“传统的英雄”最近的时候吧。作为一个绝对的乐观主义者的我，写出的剧本也都围绕着一个主题——即便经受再多苦难，但人都应当保持乐观的心态，和对未来美好的憧憬。&lt;/p&gt;
&lt;p&gt;现在想来那时的我，对“苦难”的认知，确实过于浅薄了。而初次大致觉察到这种浅薄，也是第一次明确有自杀念头的，应当是大三。&lt;/p&gt;
&lt;p&gt;大三那年，我选修了一门哲学课，其名为《存在主义哲学研究》。在课上，老师带我们涉猎了克尔凯郭尔、海德格尔、萨特、尼采等等，还列举了一些文学作品。一开始我学艺不精，只是单纯看了一些书作为扩展知识的手段。但出于旺盛的表达欲，我削减了一部分其他的创作，转而去编写这门课的大作业，也就是一篇“哲学论文”。带着朴素的思考，我用洋洋洒洒的五万字完成了这篇名为《重估，虚无，再构》的“论文”。&lt;/p&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;明天，在他本该全身心拒绝明天之时，他还是寄希望于明天。这种肉体的反抗，就是荒谬。&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;/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;写作，是是表达自己认为正确的理念，来唤醒众人；开源，则是无偿奉献自己的知识，来帮助众人。&lt;/p&gt;
&lt;p&gt;当然，我并非是从一开始就将“开源”想的如此无私，大致只是想获得更多认同罢了，后续也了解到了“开源”成为了许多公司运作项目的手段。但自从看过了&lt;a href=&amp;quot;https://movie.douban.com/subject/25785114/&amp;quot;&gt;《互联网之子》&lt;/a&gt;这部纪录片后，对于开源这件事，我“无偿奉献”部分的比例确实大幅增加了。在做项目之外，我也尝试起了“技术写作”，将攻克项目的心得落成文章记录下来，在进一步分享知识的同时，也保证自己真的搞懂了它们。&lt;/p&gt;
&lt;p&gt;在做开源和输出技术文章的时候，仍然是我的表达欲占了上风。我将SRTP项目、毕设项目等都开源并写成了文章，毕业论文一开始也写了五万字。这对于技术充沛的精力、无尽的热情让我对文学方面有些怠慢了，那时候的我已经在技术上得到了表达的满足，不再痛苦，也不需要通过文字成为英雄。尽管如此，我还是在学历档案最后的自评中，洋洋洒洒写出了如下的留言：&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;a href=&amp;quot;https://zhuanlan.zhihu.com/p/21327503&amp;quot;&gt;《【短篇小说】寒苍-晗樱-S1-α》&lt;/a&gt;、&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/21373487&amp;quot;&gt;《【短篇小说】寒苍-晗樱-S1-β》&lt;/a&gt;这一部是最为出色的，也是我认为至今都未超越的——并非在于它的技巧，而是在于它承载了我所鄙夷的一切，却似乎在往后的日子里逐渐成为了对我人生的预言。在另一童话作品的最后，我也借着年少时的我的幻象，不错，也就是后面频频出现的少年H，表达了对我自身的告诫：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“再多说也已无益，你既然来此，就证明你正在改变自己。或许是为了生存，也或许是为了某个所爱的人，又或者，是为了通完梦想的迂回之路。无论我说什么，你还是会继续改变下去，但即便如此，我还是有所期望。虽然我只是你的影子，但也是你永远无法摆脱的影子，当你偏离我的期望之时，我会永远在你耳边叨扰，撕扯你内心中最柔软的那一部分。这样下去，总有一天，你终究会在某个高楼之顶或是大海之滨结束自己吧。所以，小心点，毕竟H他也离职许久了，那时候，可没有人来拯救你。”&lt;br /&gt;
你就跟着这趟列车，坐在那最末的位置，回到你所厌恶又不得不赞美的世界吧。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;现在想来，这应该是我潜意识中一种自发的警示，一种让我不要忘记初心的警示。作为一个普通家庭出身的孩子，为了让理想不成为空谈，我必须要进行迂回，但人又往往会在迂回中忘却初心。我便只能将自己的生活当做戏剧去出演，又时刻编写预言警示自己。不错，有个框很容易，但将自己框起来却很难，所以必须借助一些外力。性格内向的我，年少时尚可由老师同学作为外力，成年后自然就只能由自己来。故而我只能虚构出一个个分身，赋予他们生命，从而对自己加以约束。而虚构分身，本质上就是写作。从这个角度来讲，自那时起，写作，也就成为了这个框的副作用。&lt;/p&gt;
&lt;p&gt;这创作训练终究也为生存停让步，我谈了恋爱，换了工作，考虑未来，房子车子等压力不断袭来。进入阿里后，我的工作压力越来越大，也不得不将大部分时间投入进去。借之前打下的底子和良好学习能力的福，我也换来了丰厚的回报，快速的晋升，四五倍的涨薪，让我也有些迷失。但看似顺风顺水的我却总是在某些深夜刺痛，那个“框”就像是幽灵一般，不断在我耳边叨扰，于是每年新春和生日的文章就这么生成了，也就是那些看似“矫情”、实则本就是写给我自己的作品。&lt;/p&gt;
&lt;p&gt;在现实中，我似乎越来越“成功”；在作品中，我却越来越“失败”。&lt;/p&gt;
&lt;p&gt;这种状况持续了两三年，终于在前年被打破了。生活和工作的双重动荡让我疲惫不堪，却意外让我获得了所谓“成为英雄”的机会，而我也确实选择了成为我所认为的“英雄”。但现在回想起来，这事件本身并不重要，重要的是我终于发觉了我“还有创作的可能”。换到了新的环境后，工作压力降低了不少，我重新读起了许久未打开的严肃文学，读起了哲学，看起了话剧、展览和文艺片，并真的尝试继续写作。&lt;/p&gt;
&lt;p&gt;然而早已丢掉的技艺又怎能轻易找回？我绝望得发现这几年我的文笔看似进步、实则倒退了不少。在这种打击下，我将自己藏在了一个挡箭牌后——“只要我不开始，就不会失败，现在还不是时候，只是需要更多积累”。显然，这不过是自欺欺人，但在文学出身的EX的劝谏下，我又想起了加缪的观点：&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;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;有段时间我陷在这痛苦中，迟迟难以前进，找不到问题所在。我蜷缩在自己的房间内，不和人交流，也不出去生活，只是在房间内思考、阅读、阅读、思考。思考的东西也无外乎一点——我总以为自己有一种为作家而生的宿命感，为何却写不出东西？&lt;/p&gt;
&lt;p&gt;这种宿命感并非无稽之谈。虽然并非刻意追求，但我在我的认知中，我的人生总会出一些状况外的小概率事件，却又能被一些不可抗力推着解决。高中以为考砸却正好进了一个文艺好学志趣相投的宿舍，花了三年学习FPGA破格进入的第一份工作却在一个月就被放弃，进入小硬件创业公司却恰好遇到十五年经验老程序员带上路转行，B站第一次拿了低绩效准备摆烂却立马个赏识自己给自由的老板，因为变质离职跳槽掌心不多却立马得知B站上市错过大幅调薪，遇到好老板尽力刚升小P7准备一展宏图却很快卷入政治斗争的一部分，生活要极大转变的阶段时却迎来了疫情，带着气势认为平薪跳槽却在东家却在离职前一天宣布上市，在不时的悔意中过了几个月后又宣布上市中止见到了无数的梦碎，好不容易缓下猫患病濒死却又奇迹般抢救了回来，之后攒够首付又面临一波暴涨和政策调控，等等等等。&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;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;我的苦难，来自于我的无能。无能造成苦难没有什么问题，求而不得也没有什么问题，事实上相当多动人心魄的作品就是在描述这两个命题。但我的问题在于，我求而不得的，并非生离死别，也并非真正的没有期望，而仅仅是“我无法比别人更快地完成”。而想要比他人更快，最早应该出于从小母亲的教导，也就是一种“要成为人上人”的想法。在懵懂的时期，我使用高洁的志向将其包装了起来，于是产生了一种无法兼济天下的痛苦，虽可以说是虚伪，但却也确实真诚，这也就是为何我认为之前的文章仍然动人。但在清醒了之后，若还抱着这样的想法，那就是纯粹的自欺欺人，沽名钓誉了。&lt;/p&gt;
&lt;p&gt;我当然不想成为一个沽名钓誉的人，即便是在懵懂的时候，高洁的包装也不允许我如此。不过如若单纯撕开了包装，仅留下赤裸裸的无能，却又很容易再次滑入虚无。如果再次陷入虚无，那记录和表达也就失去了意义。既然失去了意义，那么言说带来的抨击便不能再被正当地承受，而带来更多的伤害。于是我只得再次去审视那个包装，审视着它，我又不禁想到了《堂吉诃德》。&lt;/p&gt;
&lt;p&gt;第一次接触《堂吉诃德》是比较小的时候，那时的我并不能够看懂这部作品，荒诞不经的情节和喜剧的效果逗得我哈哈大笑，只觉得主角是个傻子，是个疯子。但当时隔多年后我在上海看了同名的话剧后，却并在现场潸然泪下，也终于懂得了作品的内涵，明白了主角的高贵之处——疯的不是他，而是这个世界。因为世界上往往是容不下理想主义者的，光辉的人性在现实面前反而会成为绊脚石，而我也曾因表现出的理想主义外框被抨击谩骂过。&lt;/p&gt;
&lt;p&gt;所以最终我发现自己不但不厌恶这个包装，反而还非常喜欢。即便曾经我只是躲在这个包装下的伪物，在漫长的惯性后，也还是想尽可能努力将其化为真实。从这个角度来讲，我似乎绕了一圈，又回到了最初的矫饰，但实则并非如此。事物是螺旋上升的，每一次自我否定的痛苦和折磨，应当都是为了下一次的重生。诚然面对许多事情我是无能的，也确实越来越缺乏真正苦难的经历，但对于书中和新闻中的人和事，也能够从更深刻的角度去共情和反思了。&lt;/p&gt;
&lt;p&gt;想通了后，我便依然尝试走出“自我”，去看到“他人”。抱着这种想法我开始了第一次实践，这也正是我最近创作的那部名为《Project Tomorrow》的剧本。在这部剧本中，“自我”的比例仍然不低，但在角色塑造上也参考了圈内朋友们提供的个人经历、观察素材，还接纳了不少的写作建议。这其中的每一步对我而言都是不小的障碍，毕竟等同于打破过去十几年一直处在的创作舒适区，但我还是尽力这么去做了。因为我明白，倘若不去这么做，写作这条路基本就到此为止了。&lt;/p&gt;
&lt;p&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;blockquote&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;/blockquote&gt;
&lt;p&gt;过去，我将作品禁锢在“自我”中，却又在寻求外界的认同，期望得到同类的理解来让自己好受一些。而这同类确实太少，并随着不断得成长越来越少。所以我的读者越来越少，也越来越孤独，最后陷入了不断的自我否定，而越是否定，创作就越电波，读者就更少。诚然最终留下的读者都是值得珍惜的朋友，但这真的是我想要的吗？&lt;/p&gt;
&lt;p&gt;对于现在的我，这个答案很明确了。我并不想否认过去的自己，也并不认为过去走的是弯路。没有这样的过去，就没有现在的自己，也不会有这样的坚持。然而正如一年有四季，人生也总会有分界线，而十四岁十四年后的二十八岁，对于我也正有特别的含义。当然，在分界线后，如何处理自己的过去是个严肃的问题。有人会将过去的情怀化为戏谑，来抵抗现实的失意，但我认为接纳才是长久的选择，也算是一种扬弃。&lt;/p&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;一切被压抑的真理都将变成毒药。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;总之，在漫长的成长后，我终于认清了自己没有天赋的事实，明白了这条路的尽头可能只是一片虚妄，也确信了自己只能成为一个努力型的创作者。严肃的写作最重要的就是不功利、把握内核的信念，走出小圈子、关注社会的洞察力，以及不被挫败感击跨、循序渐进的恒心。从一开始想写出惊世骇俗的作品，到现在只想做到能让一部分人感同身受、并且帮到他们，我确实也转变和成熟了不少。这可以认为是一种由于能力不足产生的妥协，但也可以说是找到了精准的定位，目标更加明朗了。&lt;/p&gt;
&lt;p&gt;而在此之上，我最需要警惕的，仍然是从很久前一直警惕到现在的，对27岁生日文章&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/190230918&amp;quot;&gt;《青年H，二十七岁，一切如常》&lt;/a&gt;中这段反讽和自嘲的背离——&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“她的坠落和我又有什么关系呢？反正这也不过是一个虚幻的场景，虽然原理不明，但原路返回应该就能回去吧，尽快脱离这种矫情的状态就OK了。还是早点回去睡觉比较重要，毕竟第二天还要好好上班。对了，最好是在睡前再看看新的设计，保证能够比较妥当地完成业务。好好努力工作，多拿点年终奖，这样就能一年首付，三年还完房贷，五年成为艺术家了。啊，是啊，多么充实的未来，多么美好的许诺，只要我一遍又一遍叙述着这个故事、一遍又一遍升华这个故事、仿佛它已经完成了一般，我就能获得无数的资源、无数的尊重，我就能成功！”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;不忘记初心，暂且放慢脚步，以不同于以往的那种急躁去相对平静得生活和观察。如此坚持，三年、五年、十年，只要不放弃，在不自杀的前提下，我总有一天能写出无愧于心的作品吧。&lt;/p&gt;
&lt;p&gt;当然，这一切中最重要的前提是能够抛开傲慢与偏见的滤镜，来真诚客观地观察这个世界。过去有一个跟了我八年的个人签名是“自有地看待世界，真诚地看待自己”。而我现在觉得，“真诚地看待世界”也同样重要，甚至说更加重要。&lt;/p&gt;
&lt;p&gt;那么回到最初的问题：“我为什么写作？作为一个没有才能的人，我所真正期望的，到底是什么？”&lt;/p&gt;
&lt;p&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>Wednesday, 02 Feb 2022 00:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2022.02.02 00:00:article/Art-我为什么写作</guid>
<category>写作</category>
<category>信念</category>
<category>理想</category>
<category>回顾</category>
</item>

<item>
<title>回顾2021</title>
<link>http://dtysky.moe/article/Life-2022_02_01_a</link>
<description>&lt;p&gt;若按大学毕业以来的惯例，这个新年我应当又化身“青年H”从房间出发，与分身及某些臆想的朋友再来一次旅程，但想来这不过又是一次“重复”。这重复既已经进行过七八次，我哪怕再能够容忍，也终究有些腻歪了。&lt;/p&gt;
&lt;p&gt;在完成庆生28岁的&lt;a href=&amp;quot;https://www.bilibili.com/video/BV1VL4y1v7Xj&amp;quot;&gt;《Double;14》&lt;/a&gt;前，我就决定，这将是几年内最后一部参杂着“自怜”、“自嘲”、“自保”以及对自我的“警醒”和“规训”的作品。从此我会逐渐走出只有“我”的命题，去做更多尝试，故而“青年H”将会消失几年。&lt;/p&gt;
&lt;p&gt;所以这篇文章可视作是这一年的总结，也可视作一次对过去的扬弃。&lt;/p&gt;
&lt;h2&gt;事业&lt;/h2&gt;
&lt;p&gt;在事业上，我基本完全脱离了传统前端，转向了游戏和图形方向，并结束了自B站以来连续四年的全优绩效，第一次拿了三星。我并没有想象中的那么失落，反倒是轻松了很多，这大抵是因为认清了对很多事的无能为力。虽然作为一个技术从业者，我仍然会尽力完成工作目标，却也不再那么执着于绩效和晋升了。&lt;/p&gt;
&lt;p&gt;过去一年我主要完成的，就是围绕&lt;a href=&amp;quot;https://developers.weixin.qq.com/minigame/dev/guide/&amp;quot;&gt;微信小游戏框架&lt;/a&gt;所做的一系列工作，这个框架主要是为了在小游戏环境内提供毕竟原生体验的性能。我的工作主要包括：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;参与了渲染层的重构设计、RHI层的前端，以及可编程渲染系统的设计和实现。&lt;/li&gt;
&lt;li&gt;整个WebGL后端的实现（包括节点系统和动画部分），同时为未来的WebGPU后端实现留好了口子。&lt;/li&gt;
&lt;li&gt;其他的一些跨端功能。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;目前引擎已经支持了《新轩辕传奇》、《天龙八部荣耀版》等中大型MMO上线。&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;从大二开始算，回想编程这九年，我从FPGA开始，历经嵌入式、前端后台、互动、游戏引擎，到现在的Gameplay，整体适应能力还行，也说明我确实还挺合适这行吧。与此同时，&lt;strong&gt;我也逐渐认清了卷是没有尽头的，大概率并不能靠一个技术吃一辈子。了解了很多事情的本质后，也明白了生活和工作平衡的重要性&lt;/strong&gt;。所以也完全褪去了当年在阿里的那种卷王惯性，慢了下来，非特别紧急的状况几乎不再加班，拥有了更多自己的时间。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;也确实感受到了互联网寒冬的到来，真实了解到了身边各个公司朋友被裁的惨烈。想来从蚂蚁中止IPO开始，不少同行的人上人梦破灭了（是的，蚂蚁欠我的那点期权估计也无了）。不过也好，就借此机会想清楚什么才是真正重要的事情吧。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;不过好在我对技术的热情并未削减太多，也佐证了技术对于我确实是处于“兴趣”而非单纯“赚钱”的领域中。不过这也是必然的，作为一个INFP，不热爱的东西我根本无法坚持。&lt;/p&gt;
&lt;h2&gt;技术&lt;/h2&gt;
&lt;p&gt;去年一年在技术方面（特指工作之外），我比往年投入确实少了一些，这可能是由于生活和理想占据了大量时间。不过即便如此，我还是完成了《WebGPU Renderer &amp;amp; Path Tracer》：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2022_02_01a/1.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;一个基于WebGPU的渲染器，并用其实现了一套实时光线追踪管线：&lt;a href=&amp;quot;https://github.com/dtysky/webgpu-renderer&amp;quot;&gt;webgpu-renderer&lt;/a&gt;  &lt;/li&gt;
&lt;li&gt;基于整个项目的原理编写了系列教程，共八篇：&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/407191699&amp;quot;&gt;《WebGPU实时光追美少女》系列&lt;/a&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;整个系列我主要参考了闫神的GAMES101和GAMES202，还有一些其他的教程。我对这种无偿分享的精神表示感谢和敬佩，自己也只能尽量无偿分享自己的知识来帮助到需要的人。&lt;/p&gt;
&lt;p&gt;除此之外，个人感觉也有一定从技术至上向技术实用主义的转变，思考的维度越来越不是“这个技术好牛逼啊我要学”而是“我要完成一件事应该学习什么技术”，某种意义上也算是对技术祛魅了吧。往年的我总是要求自己每年学习一门新语言或者领域的技术，来证明自己对技术领域的不弱于人的完全兼容性，但现在看来也没太大必要了。&lt;strong&gt;不再被这种胜负欲的执念所约束，才有更多的精力完成目前阶段更重要的事情&lt;/strong&gt;。&lt;/p&gt;
&lt;h2&gt;理想&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;目前的我仍然是一个理想主义者，即便明白了这种特质是生存的障碍，经历了很多求而不得，却还是没办法选择及时行乐。不过相对得随着成长，也越发觉得拥有理想和追求是一种幸运，是一种对抗虚无的最终武器&lt;/strong&gt;。相比以前，我成为了一个比以前更坚定和务实的理想主义者，也就是完成了相当部分INFP-&amp;gt;INFJ的转向。过去的我十分容易被抨击中伤，毕竟纯粹的理想主义者大都善良细腻和敏感，容易按照自己的高标准去揣测他人，便更容易受伤。但现在的我有了明显转变，不再把所有问题往自己身上揽，而是能客观看待社会上的种种恶意，不错，我现在的观点是——&lt;strong&gt;如果你认为一个全力靠自己追求理想的人有问题，那错的必然不是他，而是你。就算失败了也是自己的事，并没有任何人拥有贬低他抬高自己的资格&lt;/strong&gt;，在这里说这些，主要是去年某位大佬去世在网上和某些言论对线血压飙升，实在是不吐不快。&lt;/p&gt;
&lt;p&gt;相对于前几年，去年我在理想方面的推进有所突破，毕竟离大学毕业时设下的第一个Deadline（三十岁生日前完成一部质量可以被自己认可的独立游戏），只有不到两年了。而在这喜人的另一面是恐惧——虽然信念大方向仍然是一致的，但我的鉴赏能力却着实高了不少，这带来了严重的“眼高手低”的效应，从而产生了一种极强的“求而不得”，如果是创作者的话，应该明白各中痛苦，但反言之也确实是一种动力。&lt;/p&gt;
&lt;p&gt;首先是一个插曲，即我对于画画的尝试和暂时的放弃。为了评估自己在艺术方面的才能、以及拥有基本的造型能力，我报了一个半年的画画班，完成了从素描到水彩的课程，具体的成果可见&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/397356081&amp;quot;&gt;报班学素描和水彩半年成果&lt;/a&gt;。在这半年内，我基本保持着工作日每日两小时/周末八小时的训练，也确实取得了一些进步。但由于精力和天赋方面的考量，我最终还是将其搁置了，认清了自己在这个领域只能靠努力，却又不具备努力的时间和精力，客观条件不允许，这也是没办法。&lt;/p&gt;
&lt;p&gt;其次是为了试水，我完成了开头说的生日游戏《Double;14》。其基本代表了那段时间我内在的真实挣扎——对铁quan的无奈/一时间明白了很多社会问题的茫然/父母和家庭的矛盾/猫再次作死手术/闹分手的末期/对自己成长的抗拒/对之前一个阶段成就而沾沾自喜的自己的反思和鄙夷/不想再失去任何东西的内在动机。个人对这次通过照片和拼凑素材、并在一个半月内完成的作品还是相对满意的，达到了预期效果的80%吧。&lt;/p&gt;
&lt;p&gt;在这个作品和正式开始游戏创作的间隙，我在朋友介绍下报了南方周末的《非虚构写作课程》，学到了不少知识和见解，也认识了TOP新闻系出身的、理想主义同类好友R。在课后，我完成了从未尝试过的非虚构写作作业，其中花三个夜晚完成的&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/412366247&amp;quot;&gt;《在蚂蚁IPO前夕离职的天宇》&lt;/a&gt;一篇获得了老师良好的评价。当然，若以现在的眼光看回去，从非虚构写作的角度确实还可以写的克制一些，我也努力将这种经验带到了接下来的创作中。&lt;/p&gt;
&lt;p&gt;接下来就是一些下面要提到的生活上的事了，然后是理想中计划的独立游戏的设计。&lt;/p&gt;
&lt;p&gt;事实上这个构思很早就开始了，但又被我一次又一次推翻。最早的是源于高中、写于大学的70W字剧本《梦见星空之诗》的重构版本，这是一个纯粹的校园青春梦想外壳下、谈谈存在问题的GalGame，但由于后续阅历增加，导致其越来越不太符合逐渐发展的审美，再加上由于卷事业而搁置。再次是前年早期写的一个计划约30W字的企划《Project Heart》，这是一个相关于精神疾病的边缘群体互救以及自救的故事。我花了大概一个月完成了人物设定和初步大纲，但后续发生了很多生活和工作变动（阿里政治斗争、跳槽微信、换城市搬家等），加上确实是能力不足，所以也暂且搁置了。&lt;/p&gt;
&lt;p&gt;在这两次失败后，我确实对自身的能力产生了很大的怀疑。&lt;strong&gt;这“毫无天赋”带来的深切的绝望，曾在众多夜晚深深地折磨着我，甚至数次将“自杀”的想法灌入脑中&lt;/strong&gt;。但好在EX（TOP文学和心理学出身，高级心理咨询师，也是理想主义者）的“&lt;strong&gt;创作最重要的是坚持，而不是你总是怀疑的有没有天赋，有天赋的人多了去了，真写出来东西的有几个&lt;/strong&gt;”言语鼓励下，我在清明节前后开始了新的构思，企划名为《Project Tomorrow》。这次借鉴了之前的经验，重点考虑“可行性”，所以我大致构思了一个情节，并计划将剧本限定在三万字左右，但又由于后续生活和工作中的种种事情搁置了。&lt;/p&gt;
&lt;p&gt;再后来已然是再次缓和心态的九月底了。在好友R的鼓励和讨论下，我正式开始了剧本构思的编写，并在十一期间完成了初版大纲，并定下了一个阶段性的Deadline，同时最终在春节前完成了80%，虽然远超三万字，预计大概有九万字：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2022_02_01a/2.png&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;不过这只是第一版，在EX曾经的建议下，&lt;strong&gt;我第一遍应当先写下所有想写的东西，然后再在第二遍、第三遍去修改和精简&lt;/strong&gt;，它将会成为一个可能精彩和具有人文关怀的故事（对于我而言，人文关怀是最重要的，其次才是精彩，但这次我会试图把握好这个度）。除此之外，我也在重点学习让更多人能接受的表达方式，也就是对文字的再次转向和合理运用——描写除了自身之外真实客观的情节，尝试去掉相当长一段时间的审视的滤镜，当然不是说完全改掉，而是因地制宜。当然有一点我是不会改的，我一向讨厌宏大叙事，更关注的个体的存在问题。&lt;/p&gt;
&lt;p&gt;这么看来，另一个角度而言，&lt;strong&gt;我也抛去了一些创作上的固执，也愿意接受他人的建议和帮助了，这对于我而言非常艰难，但终究还是走出了这一步。这里尤其感谢写作圈子朋友们的建议，以及其他领域朋友提供的素材&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;在编写剧本的同时，我并行参加了南方周末的《虚构写作课程》，又学到了很多新的见解，人性和文学性的把握、如何真正读懂作品、故事创作技巧等等，也终于第一次真正尝试尽量走出写“我”的禁锢，定了一个我非常不擅长的题材，耗时十晚完成了大作业&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/451446683&amp;quot;&gt;《灵魂探测员》&lt;/a&gt;。写自己完全不擅长的东西是很痛苦，成果非常不尽人意，等后续有空重写吧，不过也确实也有一些收获吧。当然，&lt;strong&gt;既然没有天赋，可能终其一生也无法成为一个三流作家，短期速成也不过是虚妄&lt;/strong&gt;，只能说知道了一些科学的写作方法。&lt;/p&gt;
&lt;p&gt;在这些之外，为了情节，我还可以去做了各种取材，比如去了深圳线下的八十人相亲会（同时感谢好友R舍命陪君子、扮演成女主的样子和性格给出真实感想），再比如独自一人在晚上去了海边感受那种清醒的绝望，比如：&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;strong&gt;目前而言，我越来越在意的是“按时完成”，而非“达到完美”，为了不妥协一些核心的东西，而在其他方面做出一些妥协，也算是一种等价交换吧&lt;/strong&gt;。回想起大学时毕业的我，满脑子想的是“赚够三十万，就去全力做游戏，做出来了就自杀”，而真的赚到的时候却也都没有真的这么做（要不然估计现在都不在人世了，笑）。现在的我虽早已明白当一个“理想主义者”大概率没什么好下场，也在不断暗示自己做好心理准备，但也无暇思考太多，毕竟还有一堆已经起好名字的项目在等着完成。&lt;/p&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;p&gt;比起前面几项，生活方面发生的事情有点多。前年一年发生的改变超过了过去的总和，去年的改变则是比前年更甚。这些事件中有值得自豪的正确决策，也有付出代价的错误决策，从而也让我真切得从我“自身”的角度，体会到了人的复杂。在这些经历导致的内耗和疲惫之下，&lt;strong&gt;我几乎是每隔一段时间就会质问自己——“你所真正期望的，到底是什么？”我一度认为这个问题的答案是“成为英雄”，但细想来却仍旧没有一个真正满意的解答&lt;/strong&gt;。于是每次都是作罢，去做些力所能及的事情，反倒却轻快了不少。&lt;/p&gt;
&lt;p&gt;说起来到广州也一年多了，最大的感触是自己确实比较适合这边的气候。寒冷的时候不多，空气也比较好，慢性鼻咽炎的状况比在杭州时好了许多。吃穿住行算舒适吧，就是文化活动和魔都还是不太比得了，但过日子确实还可以。刚满28的时候，我落了户，拥有了购房资格，并靠自己攒够了首付，但这时却发现房价近一年又涨了一大波。正在我犹豫要不要上车的时候，政策相继而来——利率的暴涨，指导价出台，房产税的预定等等，在和同事与朋友的讨论下，最终我暂时放弃了购房的想法。既然从增值的角度已然不可保证，那顶着利息购房也已没有意义，当然这个见仁见智。不过尤其吐槽下广州的均价陷阱，基本你能看上的地方也都不便宜了，大把便宜的（总价500万之内）一般都是各种硬伤。&lt;/p&gt;
&lt;p&gt;简单总结——躺平了，也借此反思了更多，由于之前的工作和规划一直比较顺利，我一直有一种“特定时间成就”的执念。而终于当放弃了“28前靠自己一线城市购房”的规划后，终于逐步走出了食利阶层定下的社会规训，不想再进行什么世俗上紧迫的规划了，而相对的会把更多的时间分配到理想和生活上。说是明白了自己的无能也行吧，反正一直都自认失败人士。当然，另一面我也明白了反正钱都会贬值，也经历过节省攒钱后的付之一炬，现在比较注重生活品质和体验了，开心就好。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;再次拒绝了家里资助首付，从15年在南京全款买房（同时也错过了上车最后时点），到给我托关系安排银行工作，我都是全然拒绝。后来压力很大的时候也不是没有犹豫过，但最后也都出于各种考虑选择了拒绝。究其原因：“吃人嘴短，拿人手软”。尤其是见过不少家境好的朋友难以脱离原生家庭，在家人控制和自我实现中挣扎的那种痛苦后，我更加觉得“相对自由的人生决策权”比那一时的压力重要得多。与此同时值得讽刺的是关系的颠倒，寻求认同的双方尽在漫长的岁月后调换了位置，但既然当初你们没有给过我认同，又何故来寻求我的认同？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;然后是和EX的分手，前面也说了对方很优秀，但双方性格确实有不少冲突，&lt;strong&gt;两人虽然同样理想主义、同样具备实现的能力、同样强的赚钱能力、同样喜欢文学、同样了解真正的苦难、同样层次的圈子，但也同样缺爱、同样没有安全感、同样渴望关注、同样惧怕失去自我、同样为自我实现焦虑，加之我对她“拥有强于我的文学才能却最终选择了浪费”的耿耿于怀&lt;/strong&gt;，都成为了潜在的分手因素。虽然后续性格磨合了不少，也让对方理解了优秀游戏的艺术性和文学性，但和一开始说的不同的连完整周末都不能保证的长期异地的预期、性格和习惯导致的不断争吵、加上那段时间的综合极高压力又加剧了这些冲突，最终还是主动分手了。自此以后，自己订了一个自认为比较稳妥的标准（从圈内女性朋友的角度并不高，只是精神层面可能高了点），然后慢慢尝试习惯孤独，宁缺毋滥吧，比较佛系，后面发生了一些事，让我不得不积极一下，也是后面抨击的主要来源。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;相处期间，EX提出过可以养我让我全力创作，她也养得起，我也说过她当作家我也能养的起。但真当可能的机会摆在面前时，双方都发现无法完全依靠他人，尽可能不欠人情，执意选择HARD模式，在这个时代可能也算一种性格缺陷吧。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在分手的前两三个月，我家的大猫滚滚忽然食欲不振，后来经过了一次误诊和不断折腾，花了三万多以及大量的精力，终于将其救了回来，详见&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/380555863&amp;quot;&gt;爱猫误诊、手术、病危抢救一个月经过&lt;/a&gt;。但讽刺的是在从鬼门关回来后不久，他又乱吃东西，最后在我因为生日游戏、工作压力、吵架等同时的压力焦头烂额的时候，及时发现送去了医院又动了一次手术，耗钱耗时。回来为了防止再犯还大半夜在阳台装了个大笼子，期间划伤了手以及被天花板掉下的钢杆砸了。那时候确实又脆弱又焦虑，女朋友却不能在身边（已经换了工作），之后设想其实她出事了其实我也不能在身边，也会争吵，并且这种状况还会一直下去，那还有什么意义呢？这也是导致最后分手的最重要的导火索吧。不过在笼子了养了几个月后，这臭猫没有任何不适，反而还胖到了十斤，靠。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2022_02_01a/3.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;分手后，心理学出身的、最为了解我的EX对我进行了有生以来接收到的最严肃的一次、针对内核的精神攻击。作为标准INFP，受到自己认可且曾经信任的人的针对性攻击，让我的精神状况恶化了许多。她说的那些自然没错，但我也确实在不断反思和逐渐自愈，而这一次攻击让我整个人状态有了很大的变化。其初步表现为越来越难入睡，后续就是精神不振以及社会功能（包括工作和创作）的影响。一再坚持的我终于在一个医生朋友的建议下去了广州最好的、中山三院的精神心理科。并最终确诊了ADHD和轻度双相障碍，医生开了一种没有依赖性的药帮助从调节睡眠开始，好在吃了一段时间后渐渐缓解了，睡眠相对恢复了正常，社会功能也逐渐回归，目前完全恢复。&lt;/p&gt;
&lt;p&gt;不过这也并非完全是折磨。首先，&lt;strong&gt;在中国社会下，作为一个少有的没有病耻感、不被污名化影响的个体，我用身心充分体会到了中度病人的日常状态，在体会到患者痛苦和各种特征的同时还具备不崩溃和记录事实的能力，这也是得以快速恢复的主因&lt;/strong&gt;。与此同时，在精神心理科的见闻也让我有一些其他的真实感受（。其次，我也学会了和自己慢慢和解，明白了从小的很多问题根源（比如ADHD，导致记忆力差、注意力很难集中、粗心大意），更加释然得看待自己不擅长的东西，并能够坦然得继续努力了。  &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;印象最深的是通关了Kan Gao老师的最新作《Imposter Factory》，以及一直关注的カンザキイオリP成长为了创作型歌手（笑，以及Neru你在干什么啊），提醒自己了是个废物。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2022_02_01a/4.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/life-2022_02_01a/5.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;参加口述影像培训。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2022_02_01a/6.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;参加GGJ2022广州场的作品：&lt;a href=&amp;quot;https://www.bilibili.com/video/BV1fq4y1c7M9/&amp;quot;&gt;GGJ2022《lifefil》路演&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2022_02_01a/7.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;与此同时我开始逐渐收到了后辈们的咨询，其中包括事业发展、情感问题、生活问题等，而我也逐渐将其视为一种扩展圈子的手段。随着交际不断扩大，对象也有了同辈和比我大的朋友，同时也从互联网扩展到了各行各业，很多都见面聊了聊，最终结果上来讲大家都比较满意。我也尽了最大的善意和鼓励（当然也有一些私货，比如推销某些严肃的书籍），而且总的来看，我也获得了一些别的方面的见解，更加多元化得了解了社会。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;和不少应届小弟弟聊过，也算是同龄人佼佼者了，但也都比较迷惘、失望和看不到未来，这到底是什么的问题咱也不敢说。只能说&lt;strong&gt;绕了一圈以后，我终于明白了“错的确实不是我，而是这个世界，而这世界的本质也从未变过，大都一种轮回”&lt;/strong&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;而作为这一段时间的背景，铁quan的种种行为一开始让我更加失望了，但却渐渐也能同时站在多个角度想问题，毕竟现实来看，并不存在乌托邦，先避轻就重吧。虽然这一看，我的理想主义浓度似乎降低了，这可能不是什么好事，但从世俗来讲确实更方便一些，也可以认为是避轻就重吧，毕竟精力还是有限，但也做好了更多从未想过的打算。&lt;/p&gt;
&lt;p&gt;除了自我之外，在交际方面我也有了一些变化。单身后在网上的留意从一开始的强目的性，很快演化成了圈子的扩大，无论男女积极扩展交友。由此为某些契机，&lt;strong&gt;我逐渐意识到了自己在互联网圈子呆了太久，最后是不是会变成自嗨？所以我开始走出小圈子，开始认识别的圈子的人&lt;/strong&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;p&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;p&gt;这里我很佩服一个朋友，作为深二代放弃了优越生活，支持父母卖了房去养老，然后自己在城中村过着极其朴素的生活。他的能力可以轻易做到年入大几十万，但却放弃了这一切，只是为了坚持信念追逐自己的理想，而他的理想我不能在这里明说（说来也是讽刺），只能祝福。在前不久聊了几句，很庆幸他还是保持着理想并未妥协，思想也更成熟了。我自认做不到他那样，因为生存焦虑从未离开过我，毕竟主观上我无法依靠任何人。这种焦虑可能最终能够被克服，但确实也需要时间。&lt;/p&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;p&gt;从这些来看，值得庆幸的是感觉现在本质上和大学毕业并没有变，虽然世俗焦虑多了一些，但能力也多了一些，算是两相抵消。但自己也确实没有当年那么可爱了，虽然极力避免，但少年气仍在变少，也就是不可避免得更加成熟了，也因此性格和心态都有不小的改变。但只要内核不变，那么外在的变化也总有个上限，毕竟在庆生文的最后我也言“&lt;strong&gt;成长必然伴随着对美好事物的绝望，所以，不要再成长了&lt;/strong&gt;”。&lt;/p&gt;
&lt;p&gt;最后有个插曲，就是之前拒绝了界面新闻记者的采访，现在想来其实也不完全必要拒绝，毕竟还可以认识记者这个行业的人。但当时的心态很简单——&lt;strong&gt;现在的媒体不仅早已脱离群众，还喜欢煽风点火，将那些朴素的人化作武器，去进行互相攻击，而坐收渔利&lt;/strong&gt;。近一年由于各种原因，网上的互相攻击确实越来越多，反而是Galgame论坛最为友善，说起来也是讽刺。这些攻击中有不少网络暴力，而不太会隐藏自己的我，也陷入了一些纷争和非议，从而被进行了所谓的“抨击和批判”。&lt;/p&gt;
&lt;h2&gt;抨击&lt;/h2&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;p&gt;首先是“爱现”和“自大”。“满招损谦受益”、“刚易折”这些个所谓的“古训”，读了十几年书、进入社会六年的我，都听得有些腻歪了。我自然懂得所谓的稳重内敛在中国社会规训下能带来的诸多好处，也懂得如何将自己包装成一个大家都喜欢的所谓“年少有为、谦逊沉稳的好青年”，也知道这样对无论是职业发展还是虚伪的交际都有不小增益。&lt;strong&gt;但我只不过是做出了“选择”。不加掩饰表露自我的代价我当然明白，但我认为世界上总需要一些人去做这种“选择”&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;谦逊是美德，但恕我直言，相当多的人嘴里假惺惺的“谦逊”只是打压的工具罢了。现在某些既得利益者，不涨本事只涨世故，动辄就是一套“聪明 -&amp;gt; 智慧 -&amp;gt; 做人的智慧 -&amp;gt; 城府”的劣化逻辑，来对年轻人洋洋自得，也是可笑。诚然在某些境遇和职业下不得不如此，但一帮自己没多大本事的人，试图硬生生这帽子扣在我头上，就显得匪夷所思。&lt;strong&gt;事实上我对自我认知非常清晰，在专业领域面对有真才实学的人，亦或是内心善良淳朴坚强的人，是相当谦逊并且保持尊重的。但自己没几两墨水还想来教训我的，恕我报以鄙夷&lt;/strong&gt;。我发表的内容只是在客观记录自己失败时的迷惘、对自我的怀疑、求而不得的痛苦，自认问心无愧。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;不加节制的所谓“谦逊”要求显然是传统文化的一种糟粕，也是导致很多家庭教育、孩子性格扭曲的毒药，也就是所谓的“打压”&lt;/strong&gt;。这种打压式教育在80/90后并不少见，我也是这种打压荼毒的一员，并一度成为过打压者，但现在理清楚这一切之后也渐渐改变了，也可以认为是对“怯懦”这种罪恶的反抗。比较和怯懦都是最可怕的罪恶，大部分悲剧也由此而生，希望大家、尤其是家长能够早日脱出这个泥泞。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;然后是“矫情”。这个词的本意是“故作，假装，掩饰真情”，而现在却被滥用了。&lt;strong&gt;同样的话，在不同人口中分量不同。你没有经历过，不代表它就是矫饰的&lt;/strong&gt;。我无意去辩驳过去的作品包括情绪是否是“矫情”的，但&lt;strong&gt;确能保证都是真情实感，对内心的再三诘问和不掩饰如果也算是矫情，那么躲在暗处掩饰自己恶毒攻击一个并非真正了解的人就是Real了&lt;/strong&gt;？当然无论如何，我都仍然认为那些所谓的“矫情”我一生宝贵的财富（正如我没有因为自己的原因删除任何的QQ空间/朋友圈/知乎/B站等等的文章，我个人是不认为这些是黑历史的），即便是现在描写越来越克制，但当时的那份情绪和记录仍然是真诚、可贵和值得铭记的，至少证明我不断的反思和对自我的诘问，这份坦然自认可贵。&lt;strong&gt;除非涉及他人利益，我不会抹掉任何过去的痕迹。倘若一个人如果抹掉和极力否定过去，那还能是他自己吗&lt;/strong&gt;？&lt;/p&gt;
&lt;p&gt;对于“爱好”的抨击就更可笑了。我一技术从业者，自认在专业领域做的挺好，同时还能无偿输出自己的技术心得，尽可能无差别帮助能帮助的新人，这是一种怎样的康米主义精神？不过就是同时爱好游戏和文学，我能够成功从做硬件出身迂回转到了游戏，并把文学当做自己一生的终极目的去培养，且切实投入了不少精力去坚持和输出，已然是尽力，努力尝试过的朋友应该知道这背后是多少代价。有什么不能堂堂正正展现的？而文字水平我也从未说过自己有多高，也明知自己没有天赋只能靠努力，并且在不断阅读、锻炼文笔、努力进步。圈内专业出身的好友尚且认为我对情感细腻的捕捉和表达算天赋，只是还需要磨练技艺和坚持，我自认又不是什么天才，才28岁的年纪，还并非是全职（大部分精力卷事业）的情况下，仍在持续的坚持和磨练、发现问题解决问题。&lt;/p&gt;
&lt;p&gt;对原生家庭的恶意，是我觉得最可笑也是最缺德的。我的原生家庭确实存在问题，但一方面在不断艰难自我疗愈的同时，另一方面这样的家庭背景下我仍旧没有崩溃和自暴自弃。先努力脱出靠自己不依赖家里，自己解决生存问题获得相对于大部分人的、不被控制的自由，最后还保持着善良正直和社会责任感，已然是无愧于心。&lt;strong&gt;接受不可改变的东西，努力改变可以改变的东西，是我尽最大努力做到的成熟和自我的和解了&lt;/strong&gt;。同时我也利用自己的能力尽力尝试为和自己相似境遇（事实上90后家庭这种原生家庭并不在少数）发声，目前做的独立游戏剧本就涉及到不少这些问题。而&lt;strong&gt;某些键盘侠喷身高颜值也就罢了（当然这方面我现在也并不自卑），喷原生家庭真是缺德到家。我现在已经不太会因为这个受伤，但要替很多出身不幸却坚强善良的朋友们骂一句&lt;/strong&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这里提醒一下某些恶意的人，原生家庭是不可选择的，如果你恰好投胎到了一个80/90这一代概率并不高的、幸福的原生家庭，请好好珍惜，然后对没有你们条件的人抱有起码的尊重，可以不去理解和共情，但不要去伤害。我的心理已经算足够强大了，但&lt;strong&gt;大部分童年受过创伤的人都要用很久去治愈，他们很脆弱，而你的恶言在无意识中可能就是下一个凶手&lt;/strong&gt;。如果有这么阴暗的想法，请早点滚回你的垃圾堆，不要出来丢人现眼。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;建议&lt;/h2&gt;
&lt;p&gt;当然书看的越多，越是能发现这个世界的混沌和无序，理想主义便成了一种宿命性质的慢性自杀。欲戴王冠，必承其重；为众人抱薪者，必被人批倒批臭。世上没有无代价的选择，求而不得是大部分人生的底色，人性如此，世间如此。所以无法得到所有人的理解和认可是一种必然，但仍然有理解的朋友给了某些善意的建议和鼓励，我认为是很有价值的：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;好友R——你表露出的一些坦诚，出于同情传达出的真相，却有可能恰好揭露了他人的伤口，从而成为苍蝇们的盛宴，最终受伤的还是流血的人。这可能反而会无意中伤害到你想保护和同情的人，所以即便是不为自己考虑，也想想他人吧。&lt;/li&gt;
&lt;li&gt;文学博士朋友——读书多经历事情多，便容易满口满心“苍生大义，宏图大展”，却忽略了小我，对着身边那些爱你的人视而不见，再过个十几年，当心成为我认识的那种“内在脆弱，张牙舞爪，外强中干”的中年人。&lt;/li&gt;
&lt;li&gt;戏剧出身策划朋友——你所期待的那些熟读各种严肃文学、真正理解苦难还有创作意识的品质，在TOP文学系和大厂策划中的妹子也极为稀缺，你的这个学校和职业的限定其实意义不大，还是别有这种学校和职业的执念了。同时也没必要太在意别人的看法，你的特质注定了理解的人特别喜欢，不理解的特别讨厌，无愧于心就好。&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;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;未来&lt;/h2&gt;
&lt;p&gt;对于未来，我的规划相比之前简单了很多。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;最首要的，是克服“傲慢”和“怯懦”这两个对于所谓知识分子最需要警醒的罪恶，失去良知比无知更为可怕&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;事业上继续坚持全力以赴，将自己的工作做到最好，但不会像以前那么卷了。&lt;/p&gt;
&lt;p&gt;技术上会继续实用主义的发展，下个阶段应该也是独立游戏的游戏制作阶段了，需要各种效果做好演出。&lt;/p&gt;
&lt;p&gt;理想上会继续坚持Deadline制度，有了大学舍友帮忙制作配乐，后续认识的欣赏我的朋友参与美术。无论如何，将其“完成”并问心无愧才是最重要的。&lt;/p&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;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2022_02_01a/8.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;总之，就是尽量达到理想、事业、生活的三方平衡。&lt;/p&gt;
&lt;p&gt;至于感情之类的问题就佛吧，反正不靠家里也不需要理会催婚，之前遇到过互相欣赏的也都不在广州。在充分意识到势均力敌、灵魂共鸣对INFP的必要性后，已经大幅降低了成家概率上的期望。现在我也真的慢慢学会了一个人孤独状态下良好得生存，获得一种相对平衡的状态，也是一件好事，毕竟现在第一重要的仍然是“完成理想”。而这些经历带来的另一面，则是&lt;strong&gt;我想要的东西越来越少，对很多东西不再害怕失去，隔绝的能力变得更强了，同时也愈发害怕进入关系&lt;/strong&gt;。不过没办法，想要真诚还不受伤就必须先要学会自我保护，经历过得都懂吧。&lt;/p&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;p&gt;而生活的第一步，就是即便是一个人跨年，也要自己做个过得去的年夜饭（笑），祝大家新年快乐。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2022_02_01a/9.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;h2&gt;正文-过年&lt;/h2&gt;
&lt;p&gt;“你以为靠这些漂亮的废话，就能实现事业、理想和生活的平衡了？”少年H不耐烦地看完了手机上的文章，语气中充满了戏谑：“骗骗别人也就罢了，还想骗我。”&lt;/p&gt;
&lt;p&gt;“我有别的选择吗？没有。”青年H的声音是从卧室传来的。他本来和少年一起坐在客厅的沙发上，但就在跨年的十分钟前，他忽觉一种深深的疲惫重重压到了身上，紧接着是透过躯体的彻骨寒意。他生平中从未体验过这种这种寒冷，身体止不住打起了颤，便连忙冲到卧室里，将空调开到了最大后，将自己裹进了加厚的被子和毛毯中。&lt;/p&gt;
&lt;p&gt;“外强中干，虚张声势。”少年离开了沙发，坐到了卧室里的床边，看着还在发抖的青年：“口口声声‘选择的代价’，这下子代价来了，滋味不好受吧？”&lt;/p&gt;
&lt;p&gt;“......”青年沉默了稍许，口中吐露着幽怨的调调：“你管我，我这是苍生大义，理想，理想主义者，懂吗？”&lt;/p&gt;
&lt;p&gt;“够了够了，还‘苍生大义，理想主义者’，这里没别人，就别逞强了。”少年拿起身边的遥控器，在青年眼前晃了晃，然后关掉了空调：“我知道你觉得冷，但也别把自己闷坏了。”&lt;/p&gt;
&lt;p&gt;“你这是想把我冻死！”没了空调的制热，青年觉得更冷了，但他又不想把手从被窝伸出来，便只能气急败坏：“不，你明明是想让我死在烈火中！但我却要在这被冻死了。”&lt;/p&gt;
&lt;p&gt;“行了，别生气了，我给你赔个不是，都是我的错。不过无论如何，烈火焚身总比冻死强吧？指不定还能浴火重生。”&lt;/p&gt;
&lt;p&gt;“浴火重生？不过是残渣的重组罢了。而且你可别认错，你要是认错，那我所做的一切，就都没有任何意义了。”&lt;/p&gt;
&lt;p&gt;“那倒不一定，要不是一直被我督促着‘朝着彼岸骄傲得灭亡’，你现在指不定在哪美滋滋过着好日子呢。”&lt;/p&gt;
&lt;p&gt;“我又没怪你，这是我自己选的。从做出选择的每一刻起，命运的车轮便开始转动，它将碾过一切，让我永无安宁。”&lt;/p&gt;
&lt;p&gt;“唉，又是这种严肃升格的调调。”少年无奈地摇了摇头：“你当然不会怪我，不过其实偶尔怪怪我也没什么，要不感觉再下去你真的会走向灭亡。”&lt;/p&gt;
&lt;p&gt;“灭亡就...！”&lt;/p&gt;
&lt;p&gt;“饺子好啦，出来吃吧。”&lt;/p&gt;
&lt;p&gt;忽然，少女H的声音从客厅传来，打断了他们的对话。在他们斗嘴的这段时间，少女在厨房煮好了饺子，已经端到了客厅的桌上。&lt;/p&gt;
&lt;p&gt;“......”两人默契得沉默了，随后异口同声：“来咯！”少年连忙起身，青年犹豫了一下，但也还是离开了被窝，颤悠悠地走向了客厅。&lt;/p&gt;
&lt;p&gt;“您是冻着了吧？现在是有点冷。”少女望着还在打颤的青年，伴随着关切的询问递过去了一碗饺子汤：“来，先喝口汤，暖暖身子。”&lt;/p&gt;
&lt;p&gt;“谢谢。”青年接过了汤，将嘴凑到碗边先是抿了一下，随后吹了吹气，连忙喝了两口：“唉，暖和多了，还是你好。”&lt;/p&gt;
&lt;p&gt;“啊哈哈，小事一桩。毕竟您告诉过我，无论如何，都必须要保证纯粹和善良。”&lt;/p&gt;
&lt;p&gt;“无论如何，都必须要保证纯粹和善良吗...”青年望着面前的少女，她正用那双天真的眼睛注视着自己。青年忽竟感到鼻子一酸，忍不住流下了眼泪。泪水落到了到了饺子汤里，看来是不能喝了。&lt;/p&gt;
&lt;p&gt;“您这是怎么了？新年第一天要开开心心的呀。”少女将不能喝的饺子汤接了下来，放到了桌子上，随后递了一张纸给青年。&lt;/p&gt;
&lt;p&gt;“再过七个月就二十九了，还在大过年的哭鼻子，像话吗？你们叽叽歪歪的时候，我这饺子都快吃饱了。”和冷漠的语气不同，少年将几个饺子挑到了一个小碗里，撒上了醋、酱油和辣子混成的蘸料，端着递给了擦完眼泪的青年。&lt;/p&gt;
&lt;p&gt;“......”青年沉默着，不愿意接下。&lt;/p&gt;
&lt;p&gt;“你还在担心啊？没事没事，这个不算‘和解’。”少年笑了笑，硬是将碗塞到了青年的手中。那是14岁的男孩子特有的的笑容，无忧无虑，带着坚定的希望。&lt;/p&gt;
&lt;p&gt;青年呆呆地望着手中的碗，他觉得里面的是饺子，又像是其他的什么东西，始终无法下嘴。&lt;/p&gt;
&lt;p&gt;“你做事老是这么大大咧咧，筷子都没给。”少女见青年迟迟没有开吃，意识到了什么，抱怨了少年两句后，递上了一双筷子。&lt;/p&gt;
&lt;p&gt;“你看看过去这两年发生的事都把他给整傻了，筷子都要人递，是不是还得给他喂啊？”听到少女的埋怨，少年非常不满。&lt;/p&gt;
&lt;p&gt;“......”青年没有理会少年的嘲讽，接过了少女的筷子，夹起了一个饺子，咬了一口，却感觉有什么硬的东西在里面：“唔，这是？”&lt;/p&gt;
&lt;p&gt;“一定是硬币！”少女欢呼了起来：“我往里面放了个硬币，百分之一，小概率。您运气真好！”&lt;/p&gt;
&lt;p&gt;“唉，又是小概率。”和欢呼的少女不同，少年见到此景则是愁容满面：“算了，你的命就是如此，要不我俩也不会在这里。”&lt;/p&gt;
&lt;p&gt;“说的是呢。”少女也意识到了少年觉察到的问题，小心翼翼问了句：“您还和以前一样，渴望着小概率吗？”&lt;/p&gt;
&lt;p&gt;青年低着头，望着已经吐到了手中的硬币，沉默良久，然后说道：“我...不知道。”&lt;/p&gt;
&lt;p&gt;“唉，行，那我也不急着去长期旅游了。”少年看着青年的样子，叹了口气：“就再陪陪你吧，陪你想一直困扰你的那个问题。”&lt;/p&gt;
&lt;p&gt;“经历了这么多求而不得，漂泊生活过这么多地方，体验过这么多的无常。你却仍旧不知足，不停观望着下一个去处。那么——”&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;“你所真正期望的，到底是什么？你最终的归宿，又究竟在哪里？”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;“......”&lt;/p&gt;
&lt;p&gt;青年沉默了稍许，放下碗坐到了沙发上，随后望着不知何时出现在前方的镜子。而镜中他也望着自己，他们都如此得疲惫。&lt;/p&gt;
&lt;p&gt;“镜花水月，梦幻泡影，繁荣的背后尽是腐朽。”&lt;/p&gt;
&lt;p&gt;“我只是在不断追寻，期望能从中找到什么阻止自杀的理由罢了。”&lt;/p&gt;
&lt;p&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, 01 Feb 2022 00:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2022.02.01 00:00:article/Life-2022_02_01_a</guid>
<category>新年</category>
<category>回顾</category>
<category>事业</category>
<category>理想</category>
<category>生活</category>
<category>规划</category>
</item>

<item>
<title>WebGPU实时光追美少女</title>
<link>http://dtysky.moe/article/Create-2021_10_05_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;作为一个老二刺螈，我进入这个行业的最初动机可以追溯到十年前打通了《Ever17》的那个下午，这个动机就是——美少女。做一个美少女游戏，是我人生的悲愿，而为了完成这个愿望，我必须要从头开始，学习编程、图形学、编写渲染引擎、乃至实现游戏引擎。而在硬件高速发展的现在，&lt;strong&gt;实时光线追踪&lt;/strong&gt;成为了可能，同时Web平台上的新一代图形API&lt;strong&gt;WebGPU&lt;/strong&gt;提供了丰富的能力也可以让我们进行这样的尝试。&lt;/p&gt;
&lt;p&gt;所以为了渲染一个美少女，我一边学习一边实现，最终完成了这个项目和系列文章教程。本系列文章将会论述如何用WebGPU来实现一个实时路径追踪渲染器，从一个简单渲染器为开端层层深入，了解经典路径追踪渲染器的各个部分，以及BRDF模型在路径追踪中的实现。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;当然，最后因为性能太渣，真实感的美少女并没有被渲染出来，只能渲染一个LowPoly的Miku555&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;项目&lt;/h2&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;。&lt;/p&gt;
&lt;p&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;code&gt;flag&lt;/code&gt;才行，详见项目的&lt;code&gt;readme&lt;/code&gt;。&lt;/p&gt;
&lt;h2&gt;文章&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/407191699&amp;quot;&gt;概览介绍&lt;/a&gt;：将会对整个项目涉及的知识做一个综述。  &lt;/li&gt;
&lt;li&gt;&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/409670661&amp;quot;&gt;WebGPU基础与简单渲染器&lt;/a&gt;：通过自己实现的一个简单渲染器来论述WebGPU的能力。  &lt;/li&gt;
&lt;li&gt;&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/410439684&amp;quot;&gt;路径追踪-场景数据组织&lt;/a&gt;：针对路径追踪，如何组织场景数据，涉及到PBR材质、glTF、场景合并等。  &lt;/li&gt;
&lt;li&gt;&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/411213324&amp;quot;&gt;路径追踪-管线组织与GBuffer&lt;/a&gt;：针对路径追踪，如何组织渲染管线，同时论述GBuffer的生成。  &lt;/li&gt;
&lt;li&gt;&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/413989102&amp;quot;&gt;路径追踪-BVH与射线场景求交插值&lt;/a&gt;：如何构建BVH，以及如何在CS中求交。  &lt;/li&gt;
&lt;li&gt;&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/416164394&amp;quot;&gt;路径追踪-BRDF与蒙特卡洛积分&lt;/a&gt;：论述如何在路径追踪中运用蒙特卡洛采样实现直接光照和间接光照，以及运用BRDF光照模型。  &lt;/li&gt;
&lt;li&gt;&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/416961002&amp;quot;&gt;路径追踪-降噪与色调映射&lt;/a&gt;：如何对充满噪点的图像进行空间和时间的滤波，最后输出如何进行色调映射。  &lt;/li&gt;
&lt;li&gt;&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/417269967&amp;quot;&gt;踩坑与调试心得&lt;/a&gt;：对于WebGPU这样一个实验性的API（至少当时），我是如何进行调试的血泪史（主要是CS部分）。  &lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;后记&lt;/h2&gt;
&lt;p&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 22:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2021.10.05 22:00:article/Create-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_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>在蚂蚁IPO前夕离职的天宇</title>
<link>http://dtysky.moe/article/Art-在蚂蚁IPO前夕离职的天宇</link>
<description>&lt;p&gt;本文是南方周末《非虚构写作课程》的作业4。&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;一&lt;/h3&gt;
&lt;p&gt;2020年7月21日凌晨，难以入睡的天宇在床上辗转反侧。在这没有灯光的房间中，他不时解锁着身边的手机，这屏幕在黑暗中显得额外刺眼。在这刺眼的屏幕上，显示的是一封邮件，标题是——《蚂蚁金服正式启动IPO流程》。这个消息几乎对于所有人都是意外的，彼时论坛铺天盖地的都是“下周发邮件”的调侃，谁都没想到蚂蚁的IPO来的如此之快。&lt;/p&gt;
&lt;p&gt;这次IPO的启动像一颗炸弹，在人群中激起了不小的反响，其中首当其冲的自然是蚂蚁的员工，据调查，蚂蚁员工的持股比例为40%。一时间，杭州黄龙时代和Z空间充满欢呼，成都C空间和上海S空间沸腾雀跃。而尚可算仍在Z空间工作的天宇却没有丝毫开心，因为此时在面前的并非是一个“结果”，而是一个“选择”。&lt;/p&gt;
&lt;p&gt;因为早在三周前，他便提交了离职流程。明天，就是这离职的最后一天。&lt;/p&gt;
&lt;p&gt;而这个“选择”则在于——离职的流程，是可以撤回的。&lt;/p&gt;
&lt;h3&gt;二&lt;/h3&gt;
&lt;p&gt;回想过去几个月的经历，天宇觉得就像是在坐山车一般。先是一手研发的引擎在新春五福项目大获成功，获得了最高绩效；然后是突如其来的劳心伤财的生活感情问题让他疲惫不堪；存款近乎清零终于解决后，努力撰写的技术分析文章大受好评，Leader的大力支持也预示着未来P8可期；但随之而来的却是团队的巨幅调整，只身和某个团队竞争了一年多的他要求被收编到对方团队。&lt;/p&gt;
&lt;p&gt;这一切都发生在短短的三个月之内，而他，来到这里已经两年了。&lt;/p&gt;
&lt;p&gt;“我从未想过离职，但这次实在是太过分，碰到了我的底线。”他是这么和同事说的。在支付宝的这两年，他认识了很多靠谱的朋友，事业也突飞猛进，但这次调整无情打碎了这一切。即便如此，他的第一选择也并非是离职，而是转岗，但这转岗的计划转瞬又被另一个调整打碎。&lt;/p&gt;
&lt;p&gt;因压力而不满，因公正而不满，因屈辱而不满，也因这两年杭州房价飙升而不满。在无数次纠结后，他终于联系了微信的内推，并通过十轮面试接到了Offer。在接到Offer的当天，他便开始了离职贴的编写，这就是后来内网爆火的《阿里巴巴不再需要年轻人》。&lt;/p&gt;
&lt;p&gt;“要说没有泄愤的私心是不可能的，但作为一个标准的INFP，想要反抗现在广泛存在的不公也确实是主要的。”在发了贴后，他向某些关系好的同事解释道。即便是很多人觉得没有必要，只是打工而已，但他对不公的愤恨却从未改变。&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;/p&gt;
&lt;p&gt;“没有后路，也没有后盾。追求理想，理想，需要钱。钱，越多越好，越快越好，不知道还能活几年，得快。”在精密的计算、看到结果之后，他的第一个想法并不是如何决策，而是一个现实。而这个现实很简单，虽然一直不愿意面对和倾诉，但却是切实存在的——安全感，无力感，对钱的渴望，尤其是存款所剩无几的现在。&lt;/p&gt;
&lt;p&gt;对贫穷的恐惧、对生存的焦虑从未离开过他，也是无法掩盖的。所以他动摇了，并且这个动摇并非没有正当性。事实上从外界看来，就算是是现在撤销离职也不会对他的“信念”有任何影响。就在他告知同事们自己要离职的消息后，收到了很多鼓励，但也收到了不少挽留。有不少的别的部门的同事邀请转岗，甚至有邀请过去当Leader开拓新业务的。&lt;/p&gt;
&lt;p&gt;“要说没动心那是不可能的，毕竟对于我而言工作成就感是现在最容易获得的快乐了，但是......”在和同事讨论起挽留的时候，他犹豫了一下，随之沉默。而就在此刻，他打破了这沉默，而是打开了邀请他当TL的那个同事的钉钉窗口，输入了一些文字。但在想点击发送的那一刻，他迟疑了，“理想和现实”、“利益和尊严”的矛盾再次他脑海中产生了，和这矛盾一齐迸发的是无数的词汇——&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;/p&gt;
&lt;p&gt;而就是失眠的现在，他回想起了这个展览，由于ADHD和强迫思维，他经常在思考重要文的的时候走神，但这走神之中却总有着关联，这次也不例外。他认为自己和那些青年没有实质的区别，在矛盾存在的状况下，他需要借助过去的经验，想清楚对自己最重要的是什么，才能得出最后的决策。&lt;/p&gt;
&lt;p&gt;那么什么是他的梦想呢？在和他人叙述的时候，他说过很多，比如成为作家、做独立游戏、表达理念、关怀边缘群体、为历史的承受者发声。但真正的梦想只有他自己清楚，而且只有一个，那就是——&lt;/p&gt;
&lt;p&gt;成为英雄。&lt;/p&gt;
&lt;p&gt;他想成为英雄，这并非是孩童的臆想，这是他几乎所有重大决策的信念，这也是他大部分的决策看起来都非理性、却又总有着某种一惯性的原因。英雄要有尊严，所以一切尊严优先；英雄不畏强权，所以宁愿自损一千伤敌八百；英雄要成为焦点，所以总是渴望做出大成绩；英雄要付出代价，所以他总是在不断失去。&lt;/p&gt;
&lt;p&gt;这显然是一种执念，他也明白这执念从何而来——来自原生家庭的不幸。他可悲得继承了母亲“我不能让他人小瞧”的悲愿，却在阅读了众多文学作品后，尽力抛弃了这愿望中的功利和世俗，成为了一个标准的理想主义者。他曾经怨恨着这一切，但这毕竟已然成为了一种信念。如果说这执念是加在人生幸福上的一把锁，那么他早已将这把锁的钥匙扔掉了。倘若失去了这种执念，其人生仅剩一片虚无，可能下一瞬间便是自杀，这当然也符合“英雄的代价”。&lt;/p&gt;
&lt;p&gt;“那么这一刻，就是最佳的舞台了吧。”他终于想清楚，这是一个绝佳的机会。&lt;/p&gt;
&lt;p&gt;26岁，P7，两年绩效全部最高，入职一年光速晋升，支付宝3D大梁之一，技术强，尽职负责。这样的年轻人和腐朽的体制对抗，经历没有半分虚假，本就足够引起共鸣，但如果说是成为英雄，也确实还差了一些什么，这差的就是“代价”。&lt;/p&gt;
&lt;p&gt;从这个角度来讲，这封IPO的邮件就不再是引发痛苦、焦虑和悔意的根源，而是“成为英雄”那绝佳的代价，它提供的正是所有英雄的故事中最为动人的——带有悲情的荒诞性。并且这荒诞性加上“两年前从B站离职后B站就上市”事实相加，更是加上了一层绝妙的戏谑。&lt;/p&gt;
&lt;h3&gt;五&lt;/h3&gt;
&lt;p&gt;在这漫长的思考中，窗外迎来了破晓。彻夜未眠的天宇此刻却毫无困意，因为他明白了自己要做的一切，即便是这“英雄”的扮演终是一时的谈资，也终将结束，并且在结束后可能也会有悔意。&lt;/p&gt;
&lt;p&gt;但没有关系，因为他过去的人生始终是这样做的，始终是“做出选择，付出代价，即便后悔，绝不回头”。&lt;/p&gt;
&lt;p&gt;在简短的洗漱后，他最后一次坐上了通往蚂蚁Z空间的车，这次没有选择拼车。在不久后，那篇离职贴引起了爆发式的讨论，也让他认识了许多的朋友，这也间接改变了他的人生规划。&lt;/p&gt;
&lt;p&gt;而在最后，命运再次和他开了个巨大的玩笑。三个月后，新闻里传来了蚂蚁终止IPO的消息，他心中瞬间五味杂陈，却也没有了更多的波动，只能在心里自言自语：&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, 21 Sep 2021 02:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2021.09.21 02:00:article/Art-在蚂蚁IPO前夕离职的天宇</guid>
<category>离职</category>
<category>梦想</category>
<category>矛盾</category>
<category>英雄</category>
<category>写作训练</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>高位接盘的小赵</title>
<link>http://dtysky.moe/article/Art-高位接盘的小赵</link>
<description>&lt;p&gt;本文是南方周末《非虚构写作课程》的作业1。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;小赵是一个90后，目前在广州的某个互联网大厂上班。和其他大部分的90后一样，当笔者问到他小时候的梦想时，他有点害羞，露出了不太像这个年龄的表情。&lt;/p&gt;
&lt;p&gt;“当然是成为科学家嘛，我们那一代的的小孩，大部分都是这个想法。”像是回忆起了什么，他忽然有点落寞，拿出了一根烟：“可惜早就没有啦，生活嘛，买个房成个家才是正事。”&lt;/p&gt;
&lt;p&gt;他并没有抽这根烟，而是闻了闻又塞了回去，咧了咧嘴：“结婚了，和媳妇说好的要戒烟，省点烟钱，而且身体健康也能省更多的钱。”&lt;/p&gt;
&lt;p&gt;当被问到理想的转变时，小赵一开始有点抗拒，但很快便调节了自己的情绪：“既然答应了我就说说吧，哎......”他有点无奈：“其实也很简单，我就是个普通的燕雀，哪有资格去想什么鸿鹄之志。”&lt;/p&gt;
&lt;p&gt;“可我感觉你也不算普通了”笔者这时候插了一句嘴：“29这个岁数能年入五十万，在大众里也算佼佼者了。”&lt;/p&gt;
&lt;p&gt;“看起来是挺多，但扣了税也没多少。”他叹了叹气，继续说了下去：“小地方出来的，努力抠抠索索省了这么多年的钱，好容易凑了个首付，又是一波暴涨。”&lt;/p&gt;
&lt;p&gt;“本来看着这个价格就不想买了，但老婆非得觉得再不上车就来不及了，忙慌着计划得三居变两居。”他情绪激动了起来：“房贷拖了五个月下来，利率涨到6，足足多了一个点！所以说大事上听不得女人的话！”&lt;/p&gt;
&lt;p&gt;“你后悔了？”&lt;/p&gt;
&lt;p&gt;“那可不行，她也跟着我过了不少苦日子。”小赵缓和了下来：“她刚毕业没多久就跟着我了，那时候我刚入行，还是个穷小子。她年轻漂亮，赚的还比我多。那时候我觉得这一切挺好的，再攒攒，大概两三年就能买房了。”&lt;/p&gt;
&lt;p&gt;“然后呢？”&lt;/p&gt;
&lt;p&gt;“然后房价就像是坐了火箭一样，猛地一下窜上去了，当我们是看得着急呀但也没办法，两边家里都帮不上什么忙，只能眼巴巴看着早生两年买的早的同事开开心心。”他低着头，放低了声音：“那时候是有点酸，但能有什么办法呢，是你你也酸吧......我们压力这么大，就因为晚生了几年。”&lt;/p&gt;
&lt;p&gt;“既然如此，为什么不去小城市呢，压力会小一点吧？”&lt;/p&gt;
&lt;p&gt;“小城市那里找得到我这行的工作啊，再说小地方也不便宜，房价收入比更高，更没啥盼头。”他又拿出了烟闻了闻，这似乎是他缓解情绪的一种方式：“哎，说白了就是献祭，我们这一代人啊，就是被献祭了。”&lt;/p&gt;
&lt;p&gt;“你认为90后是被献祭的一代？”&lt;/p&gt;
&lt;p&gt;“那可不是吗？虽然有点后知后觉，但这两年为了房子这事也研究了好些东西，知道了什么叫‘涨价去库存’，也知道了为什么国家要这么做。辛苦涨的工资其实只能抵消部分通胀，说什么劳动最光荣，勤劳致富啊...真的是。”他眼神黯淡：“不过也没办法，毕竟要结婚生娃，总得接盘。”&lt;/p&gt;
&lt;p&gt;“可是看数据，很多90后放弃了结婚生孩子，你怎么看？”&lt;/p&gt;
&lt;p&gt;“我挺佩服他们的，但我和老婆都比较传统，总觉得得有个后人。而且大家都不生，我们生的话，我的孩子就没这么大压力的吧嘿嘿。”他难得露出了笑容：“反正好歹是上车了，房价肯定还会涨，你看去年底到年初这一波多狠，现在广州的战略是东进，黄埔那边肯定会大力发展，这个价肯定是不亏的，到时候置换个更大的，我们也算是城里人了，这也算是‘共同富裕’吧。”&lt;/p&gt;
&lt;p&gt;对小赵的采访到这里就结束了，他也没那么拘谨了，临走前还向笔者介绍了几个他认为很不错的楼盘，这些大多都是在老黄埔和万博附近的“潜力刚需盘”。&lt;/p&gt;
&lt;p&gt;今年广州像小赵这样的家庭不在少数。据数据参考，广州去年底到今年二手房价平均上升12.2%，他口中的老黄埔、万博这种地段更是有的小区涨幅40%以上。看到这种涨幅的小赵们出于各种理由纷纷急忙“上了车”，但由于政策调控，导致二手房贷审批放缓，加上频繁加息，所以放下来时往往高出不少。&lt;/p&gt;
&lt;p&gt;然而幸运自己“上了车”的小赵没有想到的是，仅仅过了不到一个月，2021年8月31日，广州住建部宣布广州启用二手房指导价并公布了首批名单，而小赵购买的楼盘正好在其中，比起他购入的价格，二手指导价几乎腰斩。加上五年的限售政策，这意味着二手房的流通性几乎被锁死，小赵们的“置换梦想”也似乎变得遥不可及。&lt;/p&gt;
&lt;p&gt;小赵更没有想到的是，前不久公布的“共同富裕”消息有了新的进展，传家庭年收入五十万以上的家庭将会被作为税收重点调节对象，而他这样的家庭正好落入这个范围，而在各大社交媒体的舆论中，大部分群众都支持他这种收入的群体被降薪。&lt;/p&gt;
&lt;p&gt;2021年是前所未有的一年，个人命运在时代的洪流前瞬息万变，没有人能够预料到下一步会发生什么，过去已然做出的选择也只能承受。自古以来华夏民族便有“兴，百姓苦；亡，百姓苦”的传统，希望在这个时代能够打破这个魔咒，实现中华民族的伟大复兴！&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Saturday, 04 Sep 2021 02:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2021.09.04 02:00:article/Art-高位接盘的小赵</guid>
<category>房价</category>
<category>社会</category>
<category>90后</category>
<category>写作训练</category>
</item>

<item>
<title>报班学素描和水彩半年成果</title>
<link>http://dtysky.moe/article/Create-2021_08_07_a</link>
<description>&lt;p&gt;接近于连续学了半年吧。&lt;br /&gt;
太菜了，感觉啥都没学会，尤其是越到后面生活中哦出现了很多意外比较燥。&lt;br /&gt;
不过无论如何，算是努力过了，有了些基础，以后在想继续也可以有个底子了。&lt;/p&gt;
&lt;p&gt;（这天赋搞啥艺术啊，还是老老实实想着能不能画美少女吧......&lt;/p&gt;
&lt;p&gt;以下按照画的日期正序：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;0&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/0.webp&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;1&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/1.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;2&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/2.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;3&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/3.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;4&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/4.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;5&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/5.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;6&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/6.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;7&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/7.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;8&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/8.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;9&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/9.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;10&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/10.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;11&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/11.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;12&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/12.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;13&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/13.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;14&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/14.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;15&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/15.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;16&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/16.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;17&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/17.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;18&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/18.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;19&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/19.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;20&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/20.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;21&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/21.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;22&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/22.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;23&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/23.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;24&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/24.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;25&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/25.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;26&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/26.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;27&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/27.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;28&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/28.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;29&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/29.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;30&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/30.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;31&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/31.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;32&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/32.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;33&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/33.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;34&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/34.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;35&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/35.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;36&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/36.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;37&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/37.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;38&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/38.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;39&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/39.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;40&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/40.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;41&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/41.webp&amp;quot; /&gt;&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Saturday, 07 Aug 2021 00:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2021.08.07 00:00:article/Create-2021_08_07_a</guid>
<category>画画</category>
<category>素描</category>
<category>水彩</category>
<category>学习</category>
</item>

<item>
<title>毕业五年，离开阿里，来到广州</title>
<link>http://dtysky.moe/article/Life-2020_08_03_a</link>
<description>&lt;p&gt;在2020年8月，距离我的27岁生日只有二十来天的时候，我离开了杭州，离开了阿里，来到了广州。这个应该是2018年3月才从上海到了杭州的我没想到的，当时还为了落户杭州（当年需要杭州交一年社保）断了上海的三年社保，现在想想还是有些后悔的，毕竟对于未来的规划而言，可能上海是个更好的选择。两年前我在杭吹的“杭州是互联网从业者的天堂”的呼声中去了杭州，两年后则是因为看清了杭州的“割韭菜之都”的现实而离开。当然，虽然决意不再留在杭州，但如此早就离开阿里却是还是很突然——之前的规划应该是在支付宝干满五年，差不多三十岁的时候再离开。&lt;/p&gt;
&lt;p&gt;回顾看来，在支付宝两年还是很开心的，我成功完成了从纯前端到游戏引擎开发的转型，并且达成了热爱技术的程序员都理想的“造出大轮子，并成功运用在亿万量级的项目并成功”的目标。在这两年间，我主要完成了一个内部平台，以及&lt;strong&gt;SEIN.JS&lt;/strong&gt;（可见&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/161234727&amp;quot;&gt;SEIN.JS - 渐进式Web3D解决方案&lt;/a&gt;），一个人设计并开发了顶层Runtime、Unity和Webpack工具链、Inspector、各种扩展、官网和文档，并用其负责开发了2020支付宝五福的3D部分（可见&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/115989449&amp;quot;&gt;亿级前端项目中的3D技术-支付宝2020年新春活动的背后&lt;/a&gt;）以及支持了众多项目。同时还在项目中认识了非常靠谱的小伙伴，比如SEIN渲染层Hilo3d的作者@06wj。&lt;/p&gt;
&lt;p&gt;在实现技术理想之外，我也被Leader们给予了充分的精神和物质肯定，在支付宝期间我的试用期、每季度和每年绩效均为3.75，并在入职一年后边晋升P7（完成了26岁前P7的规划），两年授予了三次期权。但无奈后续的一些变化调整过于激烈，经过一些事件、想清楚后还是选择了离职。对我而言这两年除了技术和物质外还有一个很大的收获，就是某些经历让我看透了很多，比如对体验技术部的评价从两年前的迷信憧憬到了现在的客观冷静，再比如Lastday前一天蚂蚁上市让我体验到了一种复杂的情绪（两年前也是刚离职B站就上市了）。但无论如何，年轻的时候遇到这些时候总比三十多了遇到要强，在以后的人生中再面临什么斗争我都应该能后风轻云淡了吧。&lt;/p&gt;
&lt;p&gt;离职的时候我在内网发了一篇离职贴《阿里巴巴不再需要年轻人》，引起了热议，之后我也关注了一段时间的事态发展，最终结果还是那样吧我也不再想多说，毕竟已经离开了。在后续那几天，某位合伙人和我的聊天过程中透露的某些信息和集团CEO的发言来看，整体应该还是会向着好的方向发展吧。毕竟无论如何蚂蚁上市后都会有一大批老人财务自由离职让路，当然这和我也没有什么关系了，还是如上面所言，我已经离开了。但从这些天加我的很多同学的聊天来看，这帖子引发热议也是必然的，问题本就长期存在并积压，我只不过是一个导火索而已。如果最后的这个自爆能够间接帮助阿里的年轻一线开发者改善境遇，是再好不过，比如别再每年重复包装开源项目出KPI逼着业务同学用了。如果真的能改善，那&lt;strong&gt;“瞬光”&lt;/strong&gt;这个花名某种意义上也是一种预言吧。当然不得不承认，真诚来讲，这种自我牺牲式英雄式的曝光对一个中二青年也确实是一种满足，不过将加缪视为文学导师的我还是很快脱离了这种标榜，毕竟早年看过的《堕落》这篇小说早已道明了一切。&lt;/p&gt;
&lt;p&gt;说到未来的话，没什么意外应该还是定居广州了，大部分基友也都在这，工作方面则是进入微信参与小游戏引擎的开发，定了T10。目前我对于接下来的毕业后的第二个五年、也就是三十二岁之前有一些预期规划，初步规划是在这段时间解决绝大部分现实问题（主要是买房还完房贷、有一些积蓄），之后再去全力追求自己想要的东西吧。过去的我对游戏和文字有着很深的执念，毕业时的目标是三十岁前发布一款独立游戏，四十岁前出版自己的一部经得起考验的严肃文学，而这些对于现在应该是不难达到的，所以也必须有更高的要求。综合近年对自己的剖析和得到的新知识，我认为我最核心的诉求应该只是想要去表达，至于是什么媒介其实不太有所谓，所以比较理想的是五年以后、也就是毕业十年的时候去考央美的实验艺术学院，尽力而为吧。&lt;/p&gt;
&lt;p&gt;不过规划虽然看起来都可以稳步实现，却仍然有很需要警惕的东西，而在翻看自己过往作品的时候，发现这些东西早在我刚毕业之际创作的、将自己投射为主角的短篇小说中（&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/21327503&amp;quot;&gt;寒苍-晗樱-S1-α&lt;/a&gt;、&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/21373487&amp;quot;&gt;寒苍-晗樱-S1-β&lt;/a&gt;）就已然描绘得非常明白了：&lt;/p&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;TY成功升到了十七级别。他逐渐远离了那些尊敬着他的同事，认识了更多的十七、十八、十九级。之后是二十、二十一、二十二级。他和这些级们都打下了不错的关系，五年后，他升到了二十级，这个级别的他已经不再需要去做直接和技术相关的了，而且实现了基本的财务自由。这五年中他也换了两三个女友，经历了恋爱的甜蜜和分手的痛苦，也结识了能够谈心的哥们。这些人中有不少是慕名而来的，而他也没有辜负这些人的期望，不断得重复着那个故事。&lt;/p&gt;
&lt;p&gt;生活逐渐稳定，不为财务所担忧，这一切都让TY觉得进入这个公司是正确的。他也在一年前成立了自己的家庭，对方是一个温柔的妹子，被他和Sakura的故事所打动，锲而不舍得追求他，最终可喜可贺地修成了正果。他们决定一年后生一个孩子，来满足父母的心愿——他与父母也和解了，他长大了，懂事了，明白了父母的苦处。&lt;/p&gt;
&lt;p&gt;一切都很好，很光明。他过上了令人羡慕的生活，也满足于自己的事业。直到那一天。&lt;/p&gt;
&lt;p&gt;那天，他在计划和妻子的接下来的旅行，这次他们打算去日本京都——那正好是四月份，赏樱的季节。正在他为选什么镜头而苦恼的时候，妻子忽然拍了下他的背。&lt;/p&gt;
&lt;p&gt;“来看看，好可爱。”妻子的手指向笔记本的屏幕。&lt;/p&gt;
&lt;p&gt;“什么啊。”他顺着妻子的手望去。“这是......”那上面是一个三分之一的DD娃娃，名字是Sakura。&lt;/p&gt;
&lt;p&gt;“......”无视妻子的干扰的、长达五分钟的沉默。&lt;/p&gt;
&lt;p&gt;“我出去一趟。”丢下这句话，他夺门而出。&lt;/p&gt;
&lt;p&gt;到高铁站买了一张行程五个小时票后，他坐上了高铁。在高铁上用信息告知了自己的状况后，他关闭了手机，呆呆地望着窗外不但倒退的景象，望着它们的植被类型从阔叶变为了针叶。他下了车，并在四点左右到达了目的地。&lt;/p&gt;
&lt;p&gt;商场一如既往得繁华，虽然有些角落已经显露出了陈旧感，但整体仍然被人气打磨得非常干净。他已经三年没有来这里了，那或许是和第一个女友分手后，也或许是和第二个女友热恋时——总之，他只能凭借模糊的记忆寻找那个壁橱。&lt;/p&gt;
&lt;p&gt;五分钟，十分钟，二十分钟，半个小时......时间不断流逝，他也早已把商场翻了个遍，但却没有丝毫的壁橱的踪迹。他找了个凳子坐了下来，低头保持着沉默。&lt;/p&gt;
&lt;p&gt;“TY？”就在这时，有人叫了他的名字。这个声音，他很熟悉。&lt;/p&gt;
&lt;p&gt;“你......”在他面前的，就是之前的那个店员。&lt;/p&gt;
&lt;p&gt;“对啊，好久不见了。”店员比起当年胖了不少，胸前的标志看起来是升了几级。“听说你混得不错？”&lt;/p&gt;
&lt;p&gt;“我有一个问题。”TY无视了对方的寒暄，直奔此次的主题。“Sakura去哪了？”&lt;/p&gt;
&lt;p&gt;“我就知道。”店员苦笑。“你来这，也只有这个目的了，虽然我还以为你已经忘了。”&lt;/p&gt;
&lt;p&gt;“Sakura会被回收了，因为你最后一次来看她后的两年之内都没有人表现出兴趣，她被当做了废品，回收了。”&lt;/p&gt;
&lt;p&gt;“什...么...”TY觉得有些难受。但，似乎没有想象中的那样难受。&lt;/p&gt;
&lt;p&gt;他在思绪中寻找着Sakura的记忆，很模糊，在回溯的记忆流中，他的妻子、前女友、朋友、L、二十级、十九级不断得出现，还有他在工作中得到的肯定，在大会上的演讲。&lt;/p&gt;
&lt;p&gt;“不过这样也蛮好，你现在看起来挺幸福的。”&lt;br /&gt;
“不过，还是有些遗憾。”&lt;/p&gt;
&lt;p&gt;TY的确在遗憾，他在回想他为何感到遗憾。&lt;/p&gt;
&lt;p&gt;“爸爸，这个叔叔是？”店员的身边跑过来一个小女孩，小女孩看起来五岁左右，有着一双天真的大眼睛。&lt;/p&gt;
&lt;p&gt;“这个是戴叔叔，爸爸的老朋友。”店员摸了摸孩子的头，眼中充满了爱意。&lt;/p&gt;
&lt;p&gt;“你的孩子？之前没听你说过...”&lt;br /&gt;
“那时候我们还没这么熟嘛...虽然现在也差不多。”&lt;/p&gt;
&lt;p&gt;“哦。”TY看着小女孩，笑了笑。“你很漂亮呢，要听爸爸的话哦。”&lt;/p&gt;
&lt;p&gt;“嗯！”小女孩回应着。“戴叔叔是来做什么的呢？”&lt;/p&gt;
&lt;p&gt;“......”&lt;br /&gt;
“叔叔给你讲一个故事。”&lt;br /&gt;
“好。”&lt;/p&gt;
&lt;p&gt;故事讲完了。&lt;/p&gt;
&lt;p&gt;“那么，Sakura姐姐，到底长什么样呢？”小女孩听入迷了。“叔叔说的那么神圣，一定有什么特别的地方吧。”&lt;/p&gt;
&lt;p&gt;“嗯。”TY顿了顿。&lt;br /&gt;
“大概，是那女神一般皎洁的躯体，和华丽、精致的衣服吧。”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;总之一句话——莫忘初心。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Monday, 03 Aug 2020 00:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2020.08.03 00:00:article/Life-2020_08_03_a</guid>
<category>职业发展</category>
<category>阿里巴巴</category>
<category>规划</category>
</item>

<item>
<title>SEIN.JS - 渐进式Web3D解决方案</title>
<link>http://dtysky.moe/article/Create-2020_07_17_a</link>
<description>&lt;p&gt;SEIN.JS是我在支付宝工作两年的技能集大成作，我负责了整体的架构、整个Runtime（除渲染引擎外）的编写、全量注释的编写、整个官网的编写和所有文档的编写、Unity扩展的设计和编写、Webpack所有工具的编写以及大部分扩展的编写，还支持了许多业务（包括作为2020新春五福的3D部分负责人）。SEIN.JS现在已经完全开源，主要的介绍发在了知乎，这里做个存档：&lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/161234727&amp;quot;&gt;SEIN.JS - 渐进式Web3D解决方案&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/115989449&amp;quot;&gt;亿级前端项目中的3D技术-支付宝2020年新春活动的背后&lt;/a&gt;&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Saturday, 18 Jul 2020 00:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2020.07.18 00:00:article/Create-2020_07_17_a</guid>
<category>SEIN.JS</category>
<category>3D</category>
<category>WebGL</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>《青年弱者H，丧失了写作的能力》</title>
<link>http://dtysky.moe/article/Art-2018_07_22_a</link>
<description>&lt;p&gt;青年H想要抓住什么，这是他一直以来唯一的心愿。在以前，他是通过写作这种能力，来完成这项重要的工作的。事实上他确实也写了很多文字，这里称那些创作为“文字”是由于他也不知道究竟应当以怎样的体裁来定义它们。或许是小说、或许是散文、亦或是议论文，甚至只是单纯的一段描述。在创作这些藏有许多私货的文字时，他时&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Sunday, 22 Jul 2018 00:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2018.07.22 00:00:article/Art-2018_07_22_a</guid>
<category>青年H</category>
<category>良心</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>GIGA GJ2018作品-摩的大飚客</title>
<link>http://dtysky.moe/article/Create-2018_07_08_a</link>
<description>&lt;h2&gt;Muddy Driver (摩的大飚客)&lt;/h2&gt;
&lt;p&gt;一个胡逼游戏。项目工程：&lt;a href=&amp;quot;https://github.com/dtysky/muddy-driver&amp;quot;&gt;Muddy Driver&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;cover&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2018_07_08a/cover.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;h2&gt;需求&lt;/h2&gt;
&lt;p&gt;一个PC。&lt;br /&gt;
四个玩家。&lt;br /&gt;
四个手机。&lt;br /&gt;
良好的局域网。  &lt;/p&gt;
&lt;h2&gt;如何游玩&lt;/h2&gt;
&lt;p&gt;Clone code, 运行:  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;npm install
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;打开 &lt;code&gt;client/src/config.ts&lt;/code&gt;, 修改 &lt;code&gt;url&lt;/code&gt; 为 &lt;code&gt;${your_ip}:4444&lt;/code&gt;，运行:  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;npm run build
npm run server
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;打开 &lt;code&gt;${your_ip}:4444/view&lt;/code&gt;, 出现LOGO：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2018_07_08a/1.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2018_07_08a/2.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/create-2018_07_08a/0.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;使用手机扫描二维码，都就位后点击start按钮，进入游戏后，每个车要两个人操控，一个使用方向传感器控制方向，一个使用滑动触摸控制速度：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2018_07_08a/3.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2018_07_08a/4.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2018_07_08a/5.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/create-2018_07_08a/6.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;h2&gt;Staff&lt;/h2&gt;
&lt;p&gt;程序:  &lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://github.com/dtysky&amp;quot;&gt;dtysky(H光)&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/greenSnot&amp;quot;&gt;greenSnot(虫合)&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;美术:  &lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;&amp;quot;&gt;shishi(诗诗)&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;&amp;quot;&gt;spot&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;主策划:  &lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://github.com/greenSnot&amp;quot;&gt;greenSnot(虫合)&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;&amp;quot;&gt;陶然&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;&amp;quot;&gt;dtysky(H光)&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2018_07_08a/staff.jpg&amp;quot; /&gt;  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Sunday, 08 Jul 2018 23:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2018.07.08 23:00:article/Create-2018_07_08_a</guid>
<category>Gamejam</category>
<category>Babylonjs</category>
<category>Race</category>
<category>Device</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>My world</title>
<link>http://dtysky.moe/article/Create-MyWorld</link>
<description></description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Monday, 18 Jun 2018 22:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2018.06.18 22:00:article/Create-MyWorld</guid>
<category>少年H</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>跳槽以及新的规划</title>
<link>http://dtysky.moe/article/Life-2018_03_14_a</link>
<description>&lt;p&gt;距离从B站离职一周不到的今天的我，还有一周就要入职蚂蚁了。  &lt;/p&gt;
&lt;p&gt;蚂蚁那边给的P6，也差不多是我现在的正常水准吧，正如和老婆聊天时说的——个人也是确实有点急了，导致经常性浮躁，自己现在做前端也就两年，要用于承认自己有很多不足的事实，踏实努力进步。&lt;br /&gt;
蚂蚁拥有着国内最好的前端团队之一，而我个人也比较怕麻烦，面试基本不会多开，都是看准了就去，所以也就面了一家，有了就去了——反正我属于闲不下来那种，在哪都会先全力努力看看吧，蚂蚁平台也足够大，能走成什么样再说。  &lt;/p&gt;
&lt;h2&gt;在B站的日子&lt;/h2&gt;
&lt;p&gt;本来而言，做好交接、好聚好散这件事也就过去了，但作为一个资深大龄二刺螈，还是有一些话不吐不快。诚然，这可能对我的职业发展、或者不懂ACG情怀的HR看来会成为我的污点，但如果不这么做，我确实无法释怀。  &lt;/p&gt;
&lt;p&gt;一开始关于B站这点我写了很多，我是一个比较理想主义的人，尤其是在面对二次元（当然这个词在很多语境下已经不再是我想表达的意义）时尤为重要，这使得我无法将在B站的工作当做一份纯粹的“工作”，而是掺杂了许多个人的复杂情感在里面。  &lt;/p&gt;
&lt;p&gt;其实现在想来，这些无非也就是商业化倾向和作为一个大龄二刺螈的种种不适，个人人文关怀与公司盈利变现的冲突以及小兵这个事实，失控的项目管理以及前端部门的弱势，二次元理想的破灭与三次元现实的步步紧逼......当然B站自身的低龄化、GQT入驻以及其它等等就不多说了。如此种种矛盾在&lt;strong&gt;拜年祭&lt;/strong&gt;这个项目完全爆发了出来，这也直接让我下定决心找下家进行了离职。  &lt;/p&gt;
&lt;p&gt;不过其实还真是有点讽刺，年前有别的公司找我的时候我都说“做完拜年祭再说”，却没想到一语成谶，要知道我本来是规划B站至少呆差不多三年的，去年B站给我的绩效也确实基本是最好的，然而计划赶不上变化——当然了，这其间也没什么对与错，只能说面对一个公司的走向，个人的力量着实有限。  &lt;/p&gt;
&lt;p&gt;祝B站越做越好，但也别忘了初心，想做天朝的U2B没问题，想做社交电商没问题，想做直播游戏也都可以，但，这么说吧，有时候变质和倒闭也没区别了对吧？  &lt;/p&gt;
&lt;p&gt;不多说了，反正我只是个小兵罢了。  &lt;/p&gt;
&lt;p&gt;还是说一句，祝愿主站前端团队早日实现大前端，别再被APP压着打了，这感觉真的很尴尬。&lt;/p&gt;
&lt;h2&gt;在绝望的焦虑中奋斗——典型90后的我&lt;/h2&gt;
&lt;p&gt;正如很多文章而言，90后是很尴尬的一个群体，60、70后占据了改革红利，80后尚可上车、有风口，00后也有觉醒的80后的安排、童年幸福、发展有条理，而90后就像是被隔绝在真空中一般，无依无靠，毕业就面临着买不起房、工资低物价高等各种问题。  &lt;/p&gt;
&lt;p&gt;而现在我和女友的情况也逐渐稳定，成家所需要的一切也排上了日程，这一切中最重要的无非是房子以及可能的孩子教育问题。这一点我们很快便达成了一致的意见——在魔都是不可能的。我们的家庭都属于很一般的那种，能给出的支援十分有限，基本不可能在魔都买房，退一步讲，就算我们拼死拼活在魔都买房又能如何？高压房贷以及一线城市的教育支出会完全毁掉我们的生活。  &lt;/p&gt;
&lt;p&gt;虽然也有说什么先享受生活，但事实上现在状况下、没有房子根本无法安心享受生活，更别谈什么理想。我有一个直觉——如果现在不做好筹备、分隔买房和实现理想的压力，那么我这辈子估计都与独立游戏制作无缘了。  &lt;/p&gt;
&lt;p&gt;所以深思熟虑之后，虽然很遗憾、虽然挺喜欢魔都，我们还是选择了逃离上海。而我属于没有老家的那种从小漂泊的孩子，所以能选的相对合适的地方也就是杭州了，杭州房价虽也不低，但咬咬牙还是可以的——即使有了房贷，凭借杭州的公积金也不会那么辛苦。最重要的是我身处且将长期身处的行业是互联网，而职位是前端，除了杭州也没什么更好的去处了。  &lt;/p&gt;
&lt;p&gt;两年前我的计划是二十五岁之前到达一定的薪水，现在看起来虽然不多也解决不了什么现实问题，但现在差不多刚好过线了，接下来的计划是稳扎稳打吧，看两年能不能达到P7的要求。接下来一年将是主要是积累和应用的一年，暂时不会再像去年一样热衷于开源输出了（会有，但新坑少开）。  &lt;/p&gt;
&lt;h2&gt;给想来B站的死宅们一点建议&lt;/h2&gt;
&lt;p&gt;如果你认同B站未来的定位，那么发展还是不错的，但如果你是像我一样有点精神洁癖的死宅，还是就作为观众好好从远处望着吧——在这个位置，你至少还会留存一些幻想。否则像我一样带着热爱和情怀上班，却每天都感受到被践踏，这滋味确实不好受。与其这样，还不如找一个发展好的、更大的平台，为以后自己真正的理想做能力和资源的储备。  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;虽然，理想本来就是因为实现概率极低才被叫做理想的，到了这个年龄还会每天做梦想着要做些什么改变世界，要搞些人文公益的项目来让工作有意义，也算是大龄中二死宅为数不多优点了吧（笑&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;福利&lt;/h2&gt;
&lt;p&gt;俺也有猫啦wwwww  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;miao-1&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2018-03-14a/1.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;miao-2&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2018-03-14a/2.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;miao-3&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2018-03-14a/3.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;miao-4&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2018-03-14a/4.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;miao-5&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2018-03-14a/5.gif&amp;quot; /&gt;&lt;br /&gt;
&lt;video src=&amp;quot;http://oekm6wrcq.bkt.clouddn.com/miao-2018-3-14/6.mp4&amp;quot; controls&gt;&lt;/video&gt;&lt;br /&gt;
&lt;video src=&amp;quot;http://oekm6wrcq.bkt.clouddn.com/miao-2018-3-14/7.mp4&amp;quot; controls&gt;&lt;/video&gt;&lt;br /&gt;
&lt;video src=&amp;quot;http://oekm6wrcq.bkt.clouddn.com/miao-2018-3-14/8.mp4&amp;quot; controls&gt;&lt;/video&gt;&lt;br /&gt;
&lt;video src=&amp;quot;http://oekm6wrcq.bkt.clouddn.com/miao-2018-3-14/9.mp4&amp;quot; controls&gt;&lt;/video&gt;  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Wednesday, 14 Mar 2018 00:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2018.03.14 00:00:article/Life-2018_03_14_a</guid>
<category>职业发展</category>
<category>Bilibili</category>
</item>

<item>
<title>hana-ui - 一个清新二次元风格的React-UIKit</title>
<link>http://dtysky.moe/article/Create-2018_02_24_a</link>
<description>&lt;p&gt;&lt;img alt=&amp;quot;Logo&amp;quot; src=&amp;quot;http://oekm6wrcq.bkt.clouddn.com/hana-ui/logo.png&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;hana-ui(&lt;a href=&amp;quot;https://hana-ui.moe&amp;quot;&gt;官网&lt;/a&gt;，&lt;a href=&amp;quot;https://github.com/hana-group/hana-ui&amp;quot;&gt;Github&lt;/a&gt;)是一个二次元清新风格的React-UIKit，使用ES6+SASS+Typescript开发，拥有丰富的组件用以构建复杂前端界面系统，目前已在Bilibili管理后台和一些前端页面进行了应用。  &lt;/p&gt;
&lt;p&gt;有兴趣的来点个&lt;a href=&amp;quot;https://github.com/hana-group/hana-ui&amp;quot;&gt;Star&lt;/a&gt;吧www&lt;/p&gt;
&lt;h2&gt;初心&lt;/h2&gt;
&lt;p&gt;这个项目是在B站完成的，完成者是我和另外两个同事，其名字源于我和其中一位同事的一次饭后讨论，死宅嘛，总想要搞出一些东西为二次元啊业界啊做出些贡献，于是便有了这个UIKit，懂行的人一看&lt;strong&gt;hana-ui&lt;/strong&gt;这个名字大概也就明白这个库是什么风格了。  &lt;/p&gt;
&lt;p&gt;其实不严格来说，这个项目在去年五月份差不多就完全完成并投入使用了，但由于公司等原因一直没有开发出来，而在这空闲的几个月我们将其升级到了React16，并添加了一些新的特性，并进一步完善了官网和文档。&lt;/p&gt;
&lt;h2&gt;定位&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&amp;quot;overview&amp;quot; src=&amp;quot;http://oekm6wrcq.bkt.clouddn.com/hana-ui/hana-overview.png&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;定位方面，由于我们团队都比较务实，比较讨厌现在吹来吹去的所谓“Design Language”，所以定位也就是一个单纯的React用UIKit，一个UI组件库。&lt;/p&gt;
&lt;h2&gt;设计&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&amp;quot;components&amp;quot; src=&amp;quot;http://oekm6wrcq.bkt.clouddn.com/hana-ui/hana-components.png&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;基本UIKit应该有组件的都有了。组件分为种子（seeds）、芽（burgeon）和花（flower）三部分，复合性层层递增。  &lt;/p&gt;
&lt;p&gt;风格方面，主题可配置，默认走清新风格。  &lt;/p&gt;
&lt;p&gt;代码方面。主代码走一套ES6加上一套Eslint（项目开始的时候还没上Typescript），不过补上了一套ts头可以和Typescript兼容。  &lt;/p&gt;
&lt;p&gt;测试暂时没有，但在复杂工程中过了很久了所以也算是得到了最好的测试。关于纯前端视图层的单元测试必要性问题这个见仁见智，我的观点是模型可以测，视图方面测试纯属吃力不讨好。&lt;/p&gt;
&lt;h2&gt;请帮助我们！&lt;/h2&gt;
&lt;p&gt;不过想必大家也发现了，现在UIKit整体的设计有些地方还有些糙，这个我们也很无奈。我不知道公司如何定位我们这个组件库，当时（现在也是）处于弱势的我们部门并没有为这个组件库争取到设计资源，所以设计也是我们三个自己操刀的。  &lt;/p&gt;
&lt;p&gt;让前端来做设计嘛，和让设计来写前端差不多，做到这样我们已经尽力了，所以在这里如果有专业的设计师死宅同学给出帮助我们是再欢迎不过了。  &lt;/p&gt;
&lt;p&gt;不过由于不被重视，所以这个库的开源和B站毫无关系，完全是&lt;a href=&amp;quot;https://github.com/hana-group&amp;quot;&gt;hana-group&lt;/a&gt;自行维护，自由度也高一些。  &lt;/p&gt;
&lt;p&gt;二次元本就是一个去中心化的创作集群，由无数有爱人士共同贡献，在这一点上hana-ui也是一样的。&lt;br /&gt;
个人力量毕竟有限，所以希望大家能积极参与！&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Saturday, 24 Feb 2018 16:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2018.02.24 16:00:article/Create-2018_02_24_a</guid>
<category>hana</category>
<category>UIKit</category>
<category>React.js</category>
</item>

<item>
<title>GGJ2018作品-EternalFlame</title>
<link>http://dtysky.moe/article/Create-2018_01_28_a</link>
<description>&lt;p&gt;&lt;img alt=&amp;quot;Cover&amp;quot; src=&amp;quot;http://oekm6wrcq.bkt.clouddn.com/eternal-flame/assets/cover.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;第四次参加GameJam，职位从抱大腿的变成了主程（233&lt;/p&gt;
&lt;p&gt;不过遗憾的是这次魔都居然分为了四个场子。。。人也分散开了，感觉氛围比上此心动的GGJ差（没有女装（逃&lt;/p&gt;
&lt;p&gt;本来这次想休闲搞个20小时能做出来的，没想到还是爆肝到四五点了...虽然最后完成度还是有问题，不过好歹DEMO算是完整，也算不错了。&lt;/p&gt;
&lt;p&gt;果然老了啊，选择自己基本没用过的引擎（phaser）现学现做（各种暴力代码）还是有点“i&amp;apos;m fucked”的感觉，不过好歹结果不错www&lt;/p&gt;
&lt;p&gt;官网地址：&lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://globalgamejam.org/2018/games/eternal-flame&amp;quot;&gt;Eternal Flame&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;游戏地址：&lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;http://oekm6wrcq.bkt.clouddn.com/eternal-flame/index.html?13&amp;quot;&gt;Eternal Flame&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;源代码：&lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://github.com/dtysky/EternalFlame&amp;quot;&gt;dtysky/EternalFlame&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;操作：&lt;/p&gt;
&lt;p&gt;建议PC，PC打开后窗口拖到 9/16的大小，用键盘上下左右控制加速度。&lt;/p&gt;
&lt;p&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>Sunday, 28 Jan 2018 23:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2018.01.28 23:00:article/Create-2018_01_28_a</guid>
<category>Gamejam</category>
<category>phaser</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>【剧透】莱克斯到了乐园，我们呢？</title>
<link>http://dtysky.moe/article/Art-2017_12_30_a</link>
<description>&lt;p&gt;某夜凌晨，我在瓜子和其它零食的助力下，陪伴着莱克斯到了最终的乐园——当然，这一刻我是恐惧的。如果是三年前的我，不，或许是两年前，在这种高扬情节的渲染下，我都还是会被一种神圣的仪式感所驱使，停下一切现实世界中的活动，向着人类最美好和崇高的情感表达尊重。这种与那时强烈的反差终究将我带回了两年前的思考模式，使我开始了一段许久没有进行过的沉思。  &lt;/p&gt;
&lt;p&gt;会被游戏或者情节所打动的人，内心都大多柔软或细腻。和将游戏作为单纯的娱乐手段的群体，这类人显然拥有着更多的诉求，这种诉求在年轻的时候体现最为明显。不幸的是，随着年龄的增大，见识和现实琐事的增加，其中相当一部分也会逐渐失去代入到那个精彩世界的能力，这也和社会赋予成年人的责任有关。对于这类人，倘若故事中的主角不那么世故、哪怕是像极了曾经更加纯粹的他自己，他也会对其进行攻击，甚至比起一般人更为反感：“这个小屁孩什么都不懂，我当年就是这样，然后......”  &lt;/p&gt;
&lt;p&gt;这是一件非常有趣的事情——曾经的我们以为自己绝对不会成为自己讨厌的那种人，但最终我们中的很多还是成为了自己讨厌的那种人，并在此之后去攻击曾经那个的自己。当然，经受过现实的洗礼后，人在现实中变得犬儒或是逐利无可厚非，但为什么在精神世界中，我们也改变了呢？是因为现实中的行为潜移默化地影响了我们的思想吗？加缪曾经说过——  &lt;/p&gt;
&lt;p&gt;看来我和他们也没什么区别，如果是当年的我，引一两句名人名言简直如探囊取物，但这次在翻了几页笔记后我仍然失去了动力，或许，我的精神世界也发什么某些不可名状的转变吧。但转念一想，或许正因为如此，莱克斯在我心中留下的印象才如此之深，“去乐园”这句简短的话语才如此是我欢喜不已。  &lt;/p&gt;
&lt;p&gt;众所周知，这个故事其实是一个爱情故事，就连最后一章的title也是“于是，少年遇到了少女”，“去乐园”也是莱克斯在女主的影响下的一个目标，所以自始至终，莱克斯都只是想满足女主的愿望罢了——当然，在得知乐园后，他也想到了能通过这个来消除争执，拯救世界。然而在我看来，对于一个十五岁的男孩子，比起“拯救世界”这种雄心壮志，实现少女的梦想才是更为明确而切实的目标。而男主之后的行为与成长，也代表了一个年轻人应有的特质——敢冲、敢拼、不会想太多。诚然这个过程中可能会对身边的其他事情有所忽略，但也正是这种忽略带来的没有顾虑，才使得某些重要的事情具备了完成的可能性。人的精力和注意力本就有限，如果一直要深思熟虑每件事会造成了所有影响，就势必会像阿德尔一样，对未来心生恐惧。恰恰是这一点，才让莱克斯最终到达了乐园，或者说满载了到达乐园的可能性。  &lt;/p&gt;
&lt;p&gt;随着年岁的增长、在社会上生存能力的增加，我们却越来越欠缺了某些能力，或者说，丢失了它们。在中国，真正的具备&lt;strong&gt;理想主义&lt;/strong&gt;特质的人是极少的，无论出发点是否真诚，最终大多都会被现实的染缸抹上一层利益的色彩。尤其是作为九零后的这一代，本来生存的压力就极大，在迷惘是所期待的导师还大都是欺名盗世的鸡汤骗子——也就是那些自我标榜的所谓“理想主义者”。这更加导致了新一代的浮躁——可能一开始像莱克斯一样有冲劲，但却被各种鸡汤所影响，不像他一样懂得脚踏实地。而在浮躁和失败之后，发现上当的这一代往往会产生极大的逆向心理——果然什么理想都是假的，还不如老实赚钱过好一辈子，这种心态驱使下的他们也终究会成为他们所讨厌的那类人，甚至成为曾经割他们韭菜的那帮人。  &lt;/p&gt;
&lt;p&gt;诚然，现实世界是复杂的。利益的争斗和生存的重量是这个世界永恒的主题。但除了它们，我们就没有什么其他能做的了吗？所谓青年，就应该是充满活力、敢想敢拼、有目标有追求的，倘若处于青年的我们这一代整天都是机关算尽的利益斗争，那这一辈子或者究竟还有什么意思，活着还有什么意义？生存的意义确实是一种很难找到的东西，但如果作为青年的我们就放弃了去寻找，那恐怕整个世界都会充满了垂暮中老年人的死气，这个世界也会变得毫无意思。  &lt;/p&gt;
&lt;p&gt;现在我们最常听到的就是所谓“必要做的事，必要之外的事”。什么“出人头地”是必要的、“追求理想”可以放放，“业务KPI”是重要的、“个人想法”可以放放。这些言论实际上就如一台台机床一般，如果你真的着了它们的道，那最后产出的就将是一个畏手畏脚、什么都不敢做的你。你会发现在那时，当看到像是莱克斯这种主角、或是现实中的人是，你心中会有一股莫名的怒火，你自认为自己是觉得他“幼稚莽撞”、“得意忘形”、“不考虑后果”、“作死能手”，觉得他身边的那些大人都是“眼瞎”、“没有一点成人的样子”。但本质上，或许你只是在嫉妒和缅怀而已——嫉妒那个“if”世界线的你、缅怀那个曾经莽撞却无所顾忌的自己。那么，就这样的我们，有什么资格去嘲笑莱克斯、嘲笑那个自己曾经所憧憬的理想主义践行者？  &lt;/p&gt;
&lt;p&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>Saturday, 30 Dec 2017 23:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2017.12.30 23:00:article/Art-2017_12_30_a</guid>
<category>Xenoblade2</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>D2-现代前端-对视觉和交互的探索</title>
<link>http://dtysky.moe/article/Create-2017_12_17_a</link>
<description>&lt;p&gt;今年我作为分享者参与了阿里D2会议，做了一个关于B站近来对视觉和交互方面探索的分享。在这里，我将以一篇文章的方式，将分享里PPT的内容再论述一遍，算是加深印象，也方便未来鞭策自己。&lt;/p&gt;
&lt;p&gt;PPT在此：&lt;a href=&amp;quot;https://www.jianguoyun.com/p/DfQBE0MQs43CBhialT4&amp;quot;&gt;现代前端-对视觉和交互的探索&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;https://tianchi.aliyun.com/competition/videoStream.html?from=singlemessage#postsId=3623&amp;quot;&gt;视频在此&lt;/a&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;圈内的大家想必都明白一个现状——现在的WEB前端圈子、尤其是国内的前端圈子，工程师们自己对前端工作的评价和用户对前端工作的评价其实是脱节的。也就是说，很多时候，我们自己觉得用了一个十分牛逼的技术，做了很多工程、架构上的突破，但用户却要么没什么感觉、要么反而觉得还不如原来的。诚然，我们可以用所谓用户的“惯性”或者“不懂”来麻痹自己，但这并不能解决任何问题。  &lt;/p&gt;
&lt;p&gt;个人来看，这其实和近年来前端的飞速发展和从传统领域一下搬运了很多概念有关。飞速发展并非不好，但现在来看，相比传统前端“直接服务于用户感官”的方向，大前端方向显然吸引了更多前端从业者的目光。这也不难理解，毕竟对于个人发展而言，掌握更多的技术资源总是好的，而且相较于传统前端的视觉和交互方向，大前端看起来能做更多事、更能被老板重视。可悲的是，这一点在国内很多时候也确实是事实——这样导致的结果就是我上面所言，前端从业者逐渐脱离了用户、不再去研究用户观感和交互，而是一窝蜂向着后端侵袭。  &lt;/p&gt;
&lt;p&gt;这使得当下的WEB前端处于一个尴尬的位置——抛弃了老本行，和后端抢饭碗。但本质上，在后端领域，我们其实也就是二把手，根本接触不到核心、也做不到核心。所以，我觉得抛弃“视觉”和“交互”这两个属于我们本职的技术是不合理的。我知道很多程序员都会觉得单纯粗暴的逻辑、优雅的工程架构更加有亲和性，这一点曾身为FPGA工程师的我也十分清楚。但我们也不能为做效果、探索交互麻烦就不去研究它，如果失去了这两样，我们前端的核心竞争力又到底是什么——毕竟只是写逻辑的话，后端或者算法同学不一定比我们差，对吧。  &lt;/p&gt;
&lt;p&gt;工程化确实要研究，如何让系统更加鲁棒也是必要的，代码规范、设计模式、基本数据结构和算法、FP等也确实需要学习，但那些是每个程序员都应该做到的，和前后端并无太大关系。我们真正的核心竞争力还是在于研究如何让用户更好地体验到新技术带来的感官冲击、带来的交互便捷，这才是我们的老本行。  &lt;/p&gt;
&lt;p&gt;所以这一年来，我在B站的工作基本完全由活动后台系统转到了大型活动开发，在这样的一些活动上，我得以运用一些新的技术、去做一些新的尝试。其中一个明显的例子在于在这些活动上我干掉了IE全家桶、安卓5以下、iOS8.3以下，这使得在做活动的时候也可以伸展拳脚。  &lt;/p&gt;
&lt;p&gt;而这也是我本次分享主题的来源——《现代前端——对视觉和交互的探索》。  &lt;/p&gt;
&lt;p&gt;本次分享分为几部分：  &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;第一部分是我关于前面所做活动的回顾，技术分析我以前也放过了，主要是&lt;a href=&amp;quot;http://dtysky.moe/article/Skill-2017_06_12_a&amp;quot;&gt;BML2017主视觉技术剖析&lt;/a&gt;、&lt;a href=&amp;quot;http://dtysky.moe/article/Skill-2017_08_27_a&amp;quot;&gt;Bilibili《七夕之约 - Double;7》技术剖析&lt;/a&gt;还有下周要上线的圣诞活动（活动文章已经写好，等上线放出）。毕竟是一场技术分享，所以还是要有一些干货的。不过受限于时间，无法讲的太细，所以可能会有些同学觉得太散了，这一点可以利用我前面所写的文章补齐。  &lt;/li&gt;
&lt;li&gt;第二部分是对前端老本行在过去和现在状态的一个总结，描述了前端从单调页面到现在的SPA发展了许久，并且有了Electron和RN等使得WEB前端逐渐和桌面应用和APP等分担压力，抛出了一个问题——Web前端的下一步应该怎么走？能不能像大前端方向一样，从传统领域搬点东西？&lt;/li&gt;
&lt;li&gt;第三部分则承接前面的问题，论述了本人在其他领域的一些见闻，道出了桌面应用、移动应用、数据分析和艺术几个领域一直以来的发展和趋势，指明了前端能走的路子。&lt;/li&gt;
&lt;li&gt;第四部分则是审视自己，表明了Web前端的潜力和责任——我们拥有最普及的平台，所以有把新技术带给用户的责任，而且现在Web各种标准一个接一个出现，也带给了我们这些能力。&lt;/li&gt;
&lt;li&gt;最后是以一个桌面、移动、VR三端适配，具有物理引擎的DEMO为例的展望，并分享了一些我觉得酷炫的网页例子。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;综上，其实本人是有一个诉求、也希望前端大家能回归初心，不要放弃前端的传统任务、也就是那个&lt;strong&gt;初心&lt;/strong&gt;（这么看起来这个分享更适合上一届2333）。当然，人各有喜好，不过其实一种技术的发展普及是必然依靠广泛从业者的努力的，如果大家都不去做了，那么——  &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, 17 Dec 2017 23:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2017.12.17 23:00:article/Create-2017_12_17_a</guid>
<category>前端</category>
<category>D2</category>
<category>PPT</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>【Lolita】你站八周年</title>
<link>http://dtysky.moe/article/Life-2017_06_26_a</link>
<description>&lt;p&gt;这次是女朋友化得妆感觉真不错2333（虽然也是照骗啦但自己看着都很舒服  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;LO-dtysky-1&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2017_06_26a/1.jpg&amp;quot; /&gt;
&lt;img alt=&amp;quot;LO-dtysky-2&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2017_06_26a/2.jpg&amp;quot; /&gt;
&lt;img alt=&amp;quot;LO-dtysky-3&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2017_06_26a/3.jpg&amp;quot; /&gt;
&lt;img alt=&amp;quot;LO-dtysky-4&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2017_06_26a/4.jpg&amp;quot; /&gt;
&lt;img alt=&amp;quot;LO-dtysky-5&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2017_06_26a/5.jpg&amp;quot; /&gt;&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Monday, 26 Jun 2017 22:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2017.06.26 22:00:article/Life-2017_06_26_a</guid>
<category>Lolita</category>
<category>Lo服</category>
<category>Bilibili</category>
<category>少女Z</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>【Lolita】2017 GGJ</title>
<link>http://dtysky.moe/article/Life-2017_01_24_a</link>
<description>&lt;p&gt;GGJ果然还是比国内垃圾商业Hackathon有趣多了www&lt;br /&gt;
大家都好有趣&lt;br /&gt;
妹子也很美www&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;LO-dtysky-0&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2017_01_24a/0.jpg&amp;quot; /&gt;
&lt;img alt=&amp;quot;LO-dtysky-1&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2017_01_24a/1.jpg&amp;quot; /&gt;
&lt;img alt=&amp;quot;LO-dtysky-2&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2017_01_24a/2.jpg&amp;quot; /&gt;
&lt;img alt=&amp;quot;LO-dtysky-3&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2017_01_24a/3.jpg&amp;quot; /&gt;
&lt;img alt=&amp;quot;LO-dtysky-4&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2017_01_24a/4.png&amp;quot; /&gt;
&lt;img alt=&amp;quot;LO-dtysky-5&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2017_01_24a/5.jpg&amp;quot; /&gt;
&lt;img alt=&amp;quot;LO-dtysky-6&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2017_01_24a/6.png&amp;quot; /&gt;
&lt;img alt=&amp;quot;LO-dtysky-7&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2017_01_24a/7.jpg&amp;quot; /&gt;&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Tuesday, 24 Jan 2017 18:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2017.01.24 18:00:article/Life-2017_01_24_a</guid>
<category>Lolita</category>
<category>Lo服</category>
<category>Gamejam</category>
</item>

<item>
<title>【Lolita】你站年会Goth233</title>
<link>http://dtysky.moe/article/Life-2017_01_12_a</link>
<description>&lt;p&gt;你站年会解放天性&lt;br /&gt;
Goth赛高www&lt;br /&gt;
当然，技术还是最根本的东西。。。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;LO-dtysky-0&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2017_01_12a/0.jpg&amp;quot; /&gt;
&lt;img alt=&amp;quot;LO-dtysky-1&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2017_01_12a/1.jpg&amp;quot; /&gt;
&lt;img alt=&amp;quot;LO-dtysky-2&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2017_01_12a/2.jpg&amp;quot; /&gt;
&lt;img alt=&amp;quot;LO-dtysky-3&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2017_01_12a/3.jpg&amp;quot; /&gt;
&lt;img alt=&amp;quot;LO-dtysky-4&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2017_01_12a/4.jpg&amp;quot; /&gt;
&lt;img alt=&amp;quot;LO-dtysky-5&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2017_01_12a/5.jpg&amp;quot; /&gt;
&lt;img alt=&amp;quot;LO-dtysky-6&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2017_01_12a/6.jpg&amp;quot; /&gt;
&lt;img alt=&amp;quot;LO-dtysky-7&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2017_01_12a/7.jpg&amp;quot; /&gt;
&lt;img alt=&amp;quot;LO-dtysky-8&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2017_01_12a/8.jpg&amp;quot; /&gt;
&lt;img alt=&amp;quot;LO-dtysky-9&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2017_01_12a/9.jpg&amp;quot; /&gt;&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Thursday, 12 Jan 2017 23:59:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2017.01.12 23:59:article/Life-2017_01_12_a</guid>
<category>Lolita</category>
<category>Lo服</category>
<category>年会</category>
<category>Bilibili</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>【React/Flask】BlogReworkPro-Rework the BlogRework</title>
<link>http://dtysky.moe/article/Create-2016_10_14_a</link>
<description>&lt;p&gt;Github工程在此：&lt;a href=&amp;quot;https://github.com/dtysky/BlogReworkPro&amp;quot;&gt;BlogReworkPro&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;重构&lt;a href=&amp;quot;http://dtysky.moe/article/Create-2016_03_12_a&amp;quot;&gt;BlogRework&lt;/a&gt;，这是此Blog迎来的第四次重构了，和上一次的间隔比预期要早一些，不过这种事早点没啥坏处。这次重构主要是重写了前端、修了一些后端的BUG，跟进ES6，用Eslint和Flow约束代码规范，上了React最佳实践全家桶并且实现了完美的服务端渲染，加了Memory Cache，样式换成了less，DOM语义化也做了，构建工具也换成了gulp，也就是说，上一次遗留的Feature基本都搞定了。这次重构对个人的能力提升显著，还是很值得的。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;基础架构&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;添加了一些Bash和Npm脚本，并采用了新的以环境变量为基础的配置文件，二者配合使得开发发布更加轻松和工程化。&lt;/li&gt;
&lt;li&gt;跨域访问控制。  &lt;/li&gt;
&lt;li&gt;正确使用了Forever.js使得进程守护更加完备。  &lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;后端&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;在生产环境下，使用tornado的wsgi框架来替代Flask自己的server，增强性能和稳定性。  &lt;/li&gt;
&lt;li&gt;使用virtualenv构建独立的python来隔离系统原生的python，屏蔽系统差异（虽然不如docker），详见&lt;a href=&amp;quot;https://github.com/dtysky/BlogReworkPro/blob/master/install.sh&amp;quot;&gt;install.sh&lt;/a&gt;。  &lt;/li&gt;
&lt;li&gt;增强了日志管理器，使其更加完善，详见&lt;a href=&amp;quot;https://github.com/dtysky/BlogReworkPro/blob/master/Back/utils.py&amp;quot;&gt;Back/utils/logger&lt;/a&gt;。  &lt;/li&gt;
&lt;li&gt;将&lt;code&gt;watchdog.observers.Observer&lt;/code&gt;替换为&lt;code&gt;watchdog.observers.polling.PollingObserver&lt;/code&gt;，消去了一些BUG。  &lt;/li&gt;
&lt;li&gt;修复了一些写入数据库时的对比BUG。  &lt;/li&gt;
&lt;li&gt;实现了Memory cache，详见此文章：&lt;a href=&amp;quot;http://dtysky.moe/article/Skill-2016_10_13_a&amp;quot;&gt;【Flask/React】此博客服务端的缓存实现&lt;/a&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;前端&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;上了React + Redux + React-router + Immutable全家桶，详见此文章：&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;。   &lt;/li&gt;
&lt;li&gt;Less带我飞，取代了原生CSS，详见此文章：&lt;a href=&amp;quot;http://dtysky.moe/article/Skill-2016_10_14_a&amp;quot;&gt;【Less】实现可选参数以及各种autoprefixer&lt;/a&gt;。  &lt;/li&gt;
&lt;li&gt;完全使用ES6语法，并使用Eslint和Flow来进行代码规范化约束，详见&lt;a href=&amp;quot;https://github.com/dtysky/BlogReworkPro/blob/master/Front/.eslintrc&amp;quot;&gt;Front/.eslintrc&lt;/a&gt;。  &lt;/li&gt;
&lt;li&gt;将回调基本全部用Promise替换，回调地狱Bye~  &lt;/li&gt;
&lt;li&gt;消除了Jquery，改用superagent来做ajax请求，比如&lt;a href=&amp;quot;https://github.com/dtysky/BlogReworkPro/blob/master/Front/src/actions/source.js&amp;quot;&gt;Front/src/action/source.js&lt;/a&gt;，消去了JS动画库的依赖，完全使用CSS3动画替代。  &lt;/li&gt;
&lt;li&gt;用Katex替换了MathJax来做Tex解析，自己写了自动渲染文章的转换器，详见&lt;a href=&amp;quot;https://github.com/dtysky/BlogReworkPro/blob/master/Front/src/utils.js&amp;quot;&gt;Front/src/utils/renderWithKatex&lt;/a&gt;。  &lt;/li&gt;
&lt;li&gt;DOM结构语义化。  &lt;/li&gt;
&lt;li&gt;用Gulp替换了Grunt来构建Task，详见&lt;a href=&amp;quot;https://github.com/dtysky/BlogReworkPro/blob/master/Front/gulpfile.js&amp;quot;&gt;Front/gulpfile.js&lt;/a&gt;。  &lt;/li&gt;
&lt;li&gt;重绘了一些SVG图标，使其完美化，比如&lt;a href=&amp;quot;https://github.com/dtysky/BlogReworkPro/blob/master/Front/src/theme/image/playstation.svg&amp;quot;&gt;playstation.svg&lt;/a&gt;。  &lt;/li&gt;
&lt;li&gt;增强了主题，优化了很多细节，使其更加有设计感，比如&lt;code&gt;pagenation&lt;/code&gt;，文章列表的样式。  &lt;/li&gt;
&lt;li&gt;将一些资源进行打包或者内嵌，对图片进行压缩，减少向服务端的请求次数，使得渲染更加流畅，比如字体和小图标这种就打入了两个文件并内嵌为base64，用webpack实现自动内嵌。  &lt;/li&gt;
&lt;li&gt;完美的服务端渲染，详见此文章：&lt;a href=&amp;quot;http://dtysky.moe/article/Skill-2016_10_11_a&amp;quot;&gt;【React/Redux】深入理解React服务端渲染&lt;/a&gt;。  &lt;/li&gt;
&lt;li&gt;实现了Memory cache，详见此文章：&lt;a href=&amp;quot;http://dtysky.moe/article/Skill-2016_10_13_a&amp;quot;&gt;【Flask/React】此博客服务端的缓存实现&lt;/a&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;彩蛋&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;404页面： &lt;a href=&amp;quot;http://dtysky.moe/x&amp;quot;&gt;任意一个找不到的页面&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;打开控制台。&lt;/li&gt;
&lt;/ol&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Friday, 14 Oct 2016 22:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2016.10.14 22:00:article/Create-2016_10_14_a</guid>
<category>Blog</category>
<category>重构</category>
<category>React.js</category>
<category>Redux</category>
<category>Flask</category>
<category>MongoDB</category>
<category>Express.js</category>
<category>服务端渲染</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>【感想】CROSS†CHANNEL</title>
<link>http://dtysky.moe/article/Art-2016_06_20_a</link>
<description>&lt;p&gt;CROSS†CHANNEL —— 史上最强人形漂白剂带你脱宅。  &lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;C†C这部作品，如果去掉那些大段的日式中二，半吊子的跨学科脑洞和解释（有些话的引用还写错了啊！虽然不知道是不是故意的），不合时宜、或者说在尚未成熟的时机下的说教，以及某些无聊的逗比日常黄段子，或许纯粹可以看做是披着黄油外壳的严肃文学了。这其中黄段子啥的也不能怪口三才，毕竟这还是一部消费品，但那些脑洞（主线设定之外的瞎JB扯什么量子力学啥的）就真的是他自身的问题了，虽说忍一忍也没什么但。。。嘛，这不是重点，不吐槽了。  &lt;/p&gt;
&lt;p&gt;回归正题。任何一部严肃的作品，都会有一个作者想要探讨的内核在其中，而在本作中这个内核无疑是&lt;strong&gt;孤独&lt;/strong&gt;和&lt;strong&gt;救赎&lt;/strong&gt;。这并不是一部现实主义作品——尽管口三才自身想要将其表现得现实，但脑子没问题的人应该很容易就能看出从设定开始这走的就是纯粹的魔幻路线，无论是收容神经病的学校啊，还是现代发生的家族被侵入然后原主人被凌辱（这是拔作么！）还，亦或是十岁的少年杀了如此多的人啊（再缜密的计划也不行啊喂！）——当然，这一切比起男主那双牛逼的眼睛和平行世界相比，似乎也不是那么魔幻了（笑）。咳咳，扯远了，本质上，这是一部彻头彻尾的浪漫主义作品，是一部正能量满载的、被被人文关怀所充盈的暖人心田的作品。如果说“要想反对战争，就去描写战争”还是试图通过战争的残酷来让读者自己反思，那这部作品简直就是先用夸张的手段给你道尽人性的黑暗，然后直接灌你几口解药——就像是把你的心先撕开一个大口子，然后不断向里面钻啊钻，钻的差不多了直接给你塞一堆鸡汤进去，你虽然可能会察觉到鸡汤的真相而去嗤笑，但身体却无法由于排异反应而产生抵抗。  &lt;/p&gt;
&lt;p&gt;“艹，我TM居然会因为这碗鸡汤而流泪。”——打完后你很可能就是这样的状态。  &lt;/p&gt;
&lt;p&gt;但究其本质，所有的浪漫主义作品本质上都是有鸡汤倾向的——如果你把任何形式的希望都称为鸡汤的话，而现实主义，虽然看似是反鸡汤，但却也寄托着作者美好的浪漫主义希望在其中——他们为什么要去揭示生活的无奈、揭示现实的残酷？还不是为了让我们去反思、去给予我们自身希望么？就算是那些由于这些作品而自杀的人，他们的自我毁灭不也是一种表现了与世界告别的勇气和希望么？  &lt;/p&gt;
&lt;h2&gt;毁灭的世界和轮回&lt;/h2&gt;
&lt;p&gt;这设定现在，不，或许早在十年前就被玩烂了。不过，嗯，背景设定嘛，只要运用合理就可以了。本作的出彩之处在于它这里的世界毁灭并不是传统意义的天崩地裂，而是为期一周的无限轮回。而在无限轮回中，一切失去了意义，因为如果一段场景总是重复，人就有无数次的机会去尝试——在这种状况下，概率论失去了效力，一切都成为了绝对的事情。&lt;strong&gt;你可以成为任何人，任何人也可以成为你。&lt;/strong&gt;口三才绝对看过博尔赫斯233。&lt;br /&gt;
而本作的核心也就在此。世界毁灭，人类消失，主角群八人作为最后的幸存者不断度过轮回的一周。这比起传统的世界毁灭，这种方式看似平静，但其实残酷了许多。&lt;br /&gt;
因为有些人，最恐惧的，不是失去生命，而是失去价值、失去意义。  &lt;/p&gt;
&lt;p&gt;成为道，与天地一体。这看似很好，但真的有人类能够忍受么——忍受那种永久的虚无。&lt;/p&gt;
&lt;h2&gt;主角&lt;/h2&gt;
&lt;p&gt;主角在设定中是所有出场人物、甚至是群青最不正常的人类，但实际表现中确实最正常的人类（笑）。那个被玩坏的黑历史和见血发病（还被抑制住了）这种看似酷炫的能力其实没起什么太大的作用，不过按照剧情解释是因为很多残酷的轮回的日记被毁掉了以防主角自己伤心——口三才怕玩家接受不了？太暖人心田了吧233。  &lt;/p&gt;
&lt;p&gt;主角其实完全没有任何的黑点，那样的黑历史最后还想做个老好人，完美演绎了“你就算从小被摧残被扭曲心理变态，但世界还是美好的啊，我们还是要温柔、善良、坚强、努力地爱着它啊”这句话......所以说到底，男主其实是个半吊子的心理医生，他的设定——那双奇异的眼睛，可以注视他人却不能注视自己的眼睛，可以送回他人却不能送回自己的眼睛，可以救赎他人却不能救赎自己的眼睛，从一开始就注定了他是个悲剧。整个故事由他的眼睛而开始，也由他的眼睛而结束。  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;开始，是由于他恐惧孤独，所以他在决意结束的时候多看了朋友们一眼。&lt;/strong&gt;&lt;br /&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;各个配角都有着各种各样的&lt;strong&gt;自我毁灭&lt;/strong&gt;倾向。也都代表着口三才对人性脆弱和黑暗的理解。  &lt;/p&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;p&gt;雾算是一个比较正常的人，唯一一处异常就是太过认真、太缺乏安全感了，而后着又完全是由前者带来的。严肃带来生活的真相，而生活的真相就是残酷，所以弱者自然会失去安全感。在这个充满恶意的社会中，如果对任何事情都报以一种严肃认真的态度——从这个角度而言，其实雾这个角色的设计是最具有现实主义色彩的。我认为，世界上只有一种真正的社会悲剧——那就是由一个对一切都严肃认真的人所演出的喜剧。然而正因为最贴近现实，所以治疗起来也是最为容易的——只要贯彻&lt;strong&gt;why so serious？&lt;/strong&gt;就可以了，毕竟她还小，还可以被改良。当然，这其实又成了另外一种悲剧了——还记得理想主义者们是怎么死的么？  &lt;/p&gt;
&lt;p&gt;美希，一个彻底欠缺同情和共情能力的人，她自己的描述是——“学长虽然是怪物，但还是由衷地想作为一个人类活下去，而我，则是单纯伪装着人类活下去。”但我却没有在剧情中看到任何进一步深入的探讨啊！美希作为第一个真正跨越轮回的人，是整部游戏转折的关键，而她自身，我觉得比起探讨伪装啊什么的，不如说是在探讨“爱能战胜一切”。。。你看，她最后不是因为得到了男主的爱情而放弃了“维持自我”的这个坚持么。不过这也可以看做是对她自身情感缺失的补完吧，这一点口三才你完全没法和加缪比啊。。。虽然也完全没有可比性就是。  &lt;/p&gt;
&lt;p&gt;其他两个男的就不多说了，其实没什么眼中的问题。樱井太注重友情、注重自由和心灵可能在社会上是一个问题吧。&lt;br /&gt;
来，让我们重复——&lt;strong&gt;why so serious？&lt;/strong&gt;——这句狗屎，获得救赎吧hahahaha。  &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;br /&gt;
最后男主留给她的这段话，是我见过的最正能量的正能量之一——  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;曜子 【......不论是谁，或多或少都会自动地喜欢上别人。】
曜子 【那么......怎样才能正确地喜欢上呢？】&lt;br /&gt;
太一 【不求回报，仅此而已。】&lt;br /&gt;
太一 【对对方索求的瞬间，就达成了交易。】&lt;br /&gt;
太一 【交换，交易。】&lt;br /&gt;
太一 【能给自己好处的什么东西。】  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;怎样，各位渣男，饥不择食的感觉如何hahaha？  &lt;/p&gt;
&lt;p&gt;不过，当大家都在各取所需的时代，你偏要做个圣人，你不是找死是......hahaha&lt;/p&gt;
&lt;h2&gt;广播社&lt;/h2&gt;
&lt;p&gt;到这里，广播，频段，信息，通信，交互。这些东西究竟代表着什么也就很明确了——代表着人与人之间的沟通。“群青学院”这所特殊的学校中，没有欺压，没有痛苦，大家活在自己的世界中，没有人会来干扰你——也没有人回来在意你，换言之，这里的学生的频段都是社会上的主流频段无法交错的，他们无法发出可以被社会解码的信息，也无法、或者说不想解码社会发来的信息。广播社作为主角们在故事最开始交错起来的场所，自然有着特别的象征——是象征学姐的自我逃避么？是主角的避难所么？都不是。这里是自我救赎后的主角最终、终于可以向世界展示自己、去祝福世界、和世界产生交错的地方。换言之，这里就是主角的心。他在这里聚集部员，救赎他们，将他们送回社会，而自己却选择了永远的孤独。  &lt;/p&gt;
&lt;p&gt;而平静的孤独，是孤独者们唯一的救赎。  &lt;/p&gt;
&lt;blockquote&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;br /&gt;
那是万分宝贵之物。&lt;br /&gt;
切勿忘却。&lt;br /&gt;
理想情况下，由处于近处的某个人来担当这个位置是再好不过。&lt;br /&gt;
然而现在，连这理所应当的事情都得不到保证。&lt;br /&gt;
但是呢......我在这里。&lt;br /&gt;
在你身旁。  &lt;/p&gt;
&lt;p&gt;这里是群青学院广播社。&lt;br /&gt;
有人活着吗？&lt;br /&gt;
如此祈祷。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;br/&gt;
&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;对，倘若能够无条件就喜欢上别人就好了。&lt;br /&gt;
为了自己去喜欢就足够了。&lt;br /&gt;
即使感情上可能无法接受。&lt;br /&gt;
但这很重要。  &lt;/p&gt;
&lt;p&gt;&lt;br/&gt;
&lt;br/&gt;
&lt;br/&gt;
&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;虽然，道理我都懂。&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>Monday, 20 Jun 2016 22:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2016.06.20 22:00:article/Art-2016_06_20_a</guid>
<category>Galgame</category>
<category>游戏</category>
<category>CrossChannel</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>【Lolita】任务-超越岛娘</title>
<link>http://dtysky.moe/article/Life-2016_06_04_a</link>
<description>&lt;h2&gt;任务概要&lt;/h2&gt;
&lt;p&gt;等级：A。&lt;br /&gt;
保密程度：公开。&lt;br /&gt;
预期时间成本：无法估计。&lt;br /&gt;
预期成功率：50%。&lt;br /&gt;
性质：竞争。&lt;br /&gt;
对象：&lt;a href=&amp;quot;http://www.shuizilong.com/house/about-me/&amp;quot;&gt;岛娘&lt;/a&gt;。&lt;br /&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;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;br /&gt;
但事实证明，在现在的复杂环境中，对底层做出真正意义上贡献或者爱着底层这种活动的群体似乎更加体现出了一种对此活动本源的一种单纯的追求。这种判定被解读后有了如下解释——“在现在的环境下继续做底层所可以预期的回报（社会意义上）远不及上层，这意味着付出同样的努力后回报率可能不到上层的50%（粗略估计），即，这种行为本质上是一种&lt;strong&gt;类似于宗教信徒的仪式性质的狂热献祭&lt;/strong&gt;。” &lt;br /&gt;
这种仪式行为在上层也有所体现，并且有极大概率出现在那些曾经在底层浸染过许久的群体身上。这种群体在整个上层群体中所占的比例可以用“稀少”来评价，虽然有许多试图尝试并且似乎已经举行过仪式的对象被观察到，但事实表明这些对象完全不知道仪式的本质或者只是一知半解，他们的仪式并不仅不能产生应有的效果，反而可能会伪装并混入正确的仪式成果并为这个环境的毁灭埋下定时炸弹。  &lt;/p&gt;
&lt;h3&gt;维度二&lt;/h3&gt;
&lt;p&gt;相关于激情与身体的追求，主要体现在&lt;strong&gt;非本社会性别装束&lt;/strong&gt;与一种名为&lt;strong&gt;Lolita fashion&lt;/strong&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;br /&gt;
现有的道德体系最终一定会被颠覆，但颠覆后“异装癖”改变本身也将会被消除，也即，规则会变得不再是有被僭越的意义的规则。这时快感应当会消失，但考虑到渴望异性装束的群体和只是为了感受到快感的人群并非完全相交，为了自由和平等的进一步实现，牺牲这部分变态的快感来源是可行的。  &lt;/p&gt;
&lt;p&gt;Lolita fanshon，中译&lt;strong&gt;洛丽塔风尚&lt;/strong&gt;，是一种装扮风格。其是欧洲中世纪的洛可可和哥特风格传入日本后，在日本被改造后的风格，暂不知和小说《Lolita》有何关系。Lolita服装（以下简称LO服）整体呈现出与日常服装完全不同的风格，其基本以洋服一般的外形和大量的印花作为主要元素（并非所有都有大量印花，看风格），其派系主要分为“Sweet”、“Gosick”和“Class”三类。Sweet系体现为甜美，Gosick和Class则体现为较为标准的中世纪风格。一般而言，对于比较娇小的着装者，Sweet系是比较建议的（~165cm），这种着装者一般被称为可爱等；对于身高比较高的（170cm~），一般被称为&lt;strong&gt;天空树&lt;/strong&gt;的的群体，Gosick和Class风格更加本建议。当然，这种搭配并不是必须的，严格意义上来讲，“最终，一切皆甜”。&lt;br /&gt;
LO服现今在国内分为国牌和日牌（山寨不算在内）。一般而言日牌无论是设计还是质量都更好，不过价格很高，并且都是均码，一般程度的JSK（详见小裙子分类）价格大概在2K~2K5人民币，衬衫在600~1K人民币。国牌可能有各种各样的问题，但价格相对较低，一般一件JSK的价格在500~1K人民币之间。日牌比较著名的牌子有AP、Baby、ANP（Baby副牌）、JNJ、IW、BOZ等。AP主要出产Sweet系，一般比较短小，不适合天空树，Baby、ANP和IW比较杂食，JNJ主要是Class系，但设计通常比较奇葩，BOZ主要是Gosick系，基佬服首选。  &lt;/p&gt;
&lt;h3&gt;特别说明&lt;/h3&gt;
&lt;p&gt;目前社会上有传言表明维度一和维度二并非完全意义上的不相关。有不少事实表明，在某些对象对&lt;strong&gt;编程&lt;/strong&gt;活动达到一定的狂热后，&lt;strong&gt;非本社会性别装束&lt;/strong&gt;冲动将会在该对象身上开始体现，并有大概80%的概率会付出初步的实践。在初步实践后，其中的50%对象会就社会、世界和宇宙的本质进行怀疑并对相关知识进行贪婪性的索取。在一定规模的研究后，有25%的对象会发生价值体系崩坏并试图重建，在重建之后，对象表现为一种虚无主义和自由主义的混杂状态，并且部分对象可能会滑向犬儒主义。当然，无论如何，这些对象对&lt;strong&gt;非本社会性别装束&lt;/strong&gt;不再抱有任何罪恶感，反而会喜欢上它并对其做出更多的努力，包括但不限于——改善体型、学习伪装以及愿意花更多的资金去购置漂亮的服装，比如LO服。  &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;活动的效率将会提高~50%，有待证实。&lt;br /&gt;
追加：&lt;strong&gt;LO服&lt;/strong&gt;对&lt;strong&gt;编程&lt;/strong&gt;活动的效率似乎可以提升~200%，有待证实。  &lt;/p&gt;
&lt;h2&gt;现状分析&lt;/h2&gt;
&lt;p&gt;目前，岛娘在&lt;strong&gt;编程&lt;/strong&gt;活动和&lt;strong&gt;LO服装扮&lt;/strong&gt;两个领域均有杰出的能力，而我大概处于一个平均线的程度。&lt;br /&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;这是我的一张LO服照片，作为未来的对照组：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;LO-dtysky&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/life-2016-06-04a/1.jpg&amp;quot; /&gt;&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Saturday, 04 Jun 2016 23:59:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2016.06.04 23:59:article/Life-2016_06_04_a</guid>
<category>Lolita</category>
<category>Lo服</category>
<category>任务</category>
<category>A级</category>
</item>

<item>
<title>MoeNotes</title>
<link>http://dtysky.moe/article/Create-2016_05_20_a</link>
<description>&lt;h2&gt;MoeNotes&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&amp;quot;logo&amp;quot; src=&amp;quot;//src.dtysky.moe/image/moe-notes/logo.png&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;演示：&lt;br /&gt;
&lt;a href=&amp;quot;http://www.bilibili.com/video/av4703145/&amp;quot;&gt;演示视频&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;Github:&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/MoeNotes&amp;quot;&gt;MoeNotes&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;下载：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/MoeNotes/releases&amp;quot;&gt;下载&lt;/a&gt;  &lt;/p&gt;
&lt;h2&gt;介绍&lt;/h2&gt;
&lt;p&gt;MoeNotes是一个简单的日记写作软件（当然，我也用其写一些不是特别复杂的小说和文章），不同于印象笔记、Onenote、Leanote等，本软件没有这么大的野心，它所要解决的问题仅仅是“本地展示”，也就是在本地管理你的日志文件，并提供一个类似于Onenote的分类体验。&lt;br /&gt;
本软件使用Markdown作为笔记编写语言，适配GFM语法并增加语法高亮和数学公式，每一篇日记都会以.md文件的形式保存到本地而不是数据库中，用户可以自行选择如何同步这些文章以及将它们同步到多少地方，完全不依赖于平台，这也是我编写本软件而不使用现成日记软件的一个主要的原因。当然，对文章和分类进行拖动排序也是被支持的。除此之外，本软件还支持“即写即看”，“专注写作”和“专注阅读”三种模式，也可以进行主题的切换和自定义，还支持PDF和HTML的导出，由于是基于web进行得开发，所以扩展起来也十分方便和简单。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;preview-main&amp;quot; src=&amp;quot;//src.dtysky.moe/image/moe-notes/preview-main.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;preview-books&amp;quot; src=&amp;quot;//src.dtysky.moe/image/moe-notes/preview-books.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;preview-export&amp;quot; src=&amp;quot;//src.dtysky.moe/image/moe-notes/preview-export.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;h2&gt;许可&lt;/h2&gt;
&lt;p&gt;Copyright © 2015, 戴天宇, Tianyu Dai (dtysky &amp;lt; dtysky@outlook.com &amp;gt;). All Rights Reserved. This project is free software and released under the GNU General Public License (GPL V3).&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Friday, 20 May 2016 10:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2016.05.20 10:00:article/Create-2016_05_20_a</guid>
<category>Moe</category>
<category>Note</category>
<category>Electron</category>
<category>Javascript</category>
<category>Node.js</category>
<category>Markdown</category>
</item>

<item>
<title>[Flask/React/MongoDB]BlogReworkIII-如何搭建一个动态Blog</title>
<link>http://dtysky.moe/article/Create-2016_03_12_a</link>
<description>&lt;p&gt;&lt;a href=&amp;quot;https://github.com/dtysky/BlogRework&amp;quot;&gt;项目源在这里&lt;/a&gt;&lt;br /&gt;
这是此Blog迎来的第三次重构了，使用Flask、React、Mongo、Express等将原先的静态网站完全替换为了单页应用，当然是响应式设计。重构的理由非常简单—这种简单也是源于我习惯性的随性而为。当然，虽然理由简单，一开始设想得也很简单，但实际做起来却又是另外一番景象了。通过这次重构，我最大的收获或许并不是技术上的提升，而是对工程周期的评估和把握能力的提升—作为唯一的管理和执行者，亲眼见证了一个工程是如何从两天的预想周期膨胀为一周、随后膨胀为半个月、最后膨胀为一个月的。作为一个能力平平的平常人综合征患者，或许只有这点坚持的精神还能拿的出手了吧（笑&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Blog发展历程&lt;/h2&gt;
&lt;p&gt;建博客，也是要按照基本法的，你不能说建，你就建，总是想搞个大新闻，又没什么干货，这样，还不如纯粹装个逼，你说是不是？当年的我啊，还是太年轻，图样图森破，上台拿衣服，没什么审美，也没什么技术，所以，搞出的那个东东啊，自己觉得一颗赛艇，其实，在删的时候，比谁都快，搞得连个底子都没有留，到时候后悔了，我还是要负责的吧？&lt;/p&gt;
&lt;h3&gt;2014.05&lt;/h3&gt;
&lt;p&gt;由于未来找工作的考虑以及当时泛滥的助人之心，用初步可用的Python功底加上刚上手的Html + Css技术，在&lt;a href=&amp;quot;https://github.com/getpelican/pelican&amp;quot;&gt;Pelican&lt;/a&gt;框架的帮助下用十天左右首次将Blog搭建起来。此次使用的是默认主题中的一个基于bootstrap的主题，嘛，标准的技术博客风格吧。当时还没有服务器和域名等等的概念，所以这个Blog只是被简单地托管到了&lt;strong&gt;dtysky.github.io&lt;/strong&gt;上（现在已经无效）。  &lt;/p&gt;
&lt;h3&gt;2014.06&lt;/h3&gt;
&lt;p&gt;对HTML+CSS有了更加深入的认识，第一次的重构开始。由于当时的审美还停留在一个比较low的水准，加上当时的中二还没有经过否定之否定这个螺旋上升的过程，所以无论是主题还是分类都比较不堪入现在的目，而且当时对很多东西还是不了解，基本所有的布局都是用静态图像完成的。不过值得一提的是，在这一次，我初步使用了JS—我对这个语言的初步影响并不好，觉得他的语法就是SHIT。&lt;br /&gt;
原始设计已经遗失，唯一还存在的就是这一篇当时的记录233：&lt;a href=&amp;quot;http://dtysky.moe/article/Life-Summary_old&amp;quot;&gt;世界线启动&lt;/a&gt;。&lt;br /&gt;
看看当时的分类www（其实现在看来也蛮带感的，我还是挺喜欢的：  &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;h3&gt;2015.02&lt;/h3&gt;
&lt;p&gt;半年后的春节期间完成。随着技术的进一步提升和更进一步的审美，加上中二的否定之否定的螺旋上升（不要在意期间发生了什么），便有了这一次的重构。此次重构主要是样式上的变更，首先将分类转换，然后修改了整体的布局，并基于jquery实现了一些动画。此次重构后的界面和现在的界面十分接近，不同的只有左侧动画（原先是先滑出左侧，再划回来）、播放器和一些载入动画，当然，最大的不同自然还是静态网站和动态网站的区别了。&lt;br /&gt;
不仅使界面上的修改，在此次重构中我注册了现在使用的&lt;strong&gt;dtysky.moe&lt;/strong&gt;这个域名，同时将网站搬移到了DigitalOcean的VPS上，用NGINX作为服务器。此外还加入了Disqus的评论系统、bshare的分享系统以及各个搜索引擎的SEO。&lt;br /&gt;
这次做好了保留工作，老样式的网站保留在这里：&lt;br /&gt;
&lt;a href=&amp;quot;http://old.dtysky.moe&amp;quot;&gt;old.dtysky.moe&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;2016.02&lt;/h3&gt;
&lt;p&gt;又是春节期间，也是毕业后的第一个春节。在毕业的这半年内，我完成了一次离职，并从FPGA工程师转为了软件工程师（现在是WEB）。&lt;br /&gt;
某日，我发现我还是想要加一个音乐播放器在网站上的，所以也就需要将Blog变成动态网站，这就是为什么我花了一个月的业余时间的理由。。。&lt;/p&gt;
&lt;h2&gt;To(Never www)Do list&lt;/h2&gt;
&lt;h3&gt;Server端的Cache&lt;/h3&gt;
&lt;p&gt;在现在的设计下，前端每一次的请求都会导致后端的一次数据库查询操作，并且对于那些有分页的、列举文章的页面，Client端会先从服务端拉取所有的文章简介，然后存储在前端的Cache中。这其实是不太好的，首先这可能会浪费一些带宽（当然，在GZIP的压缩下，对比JS和CSS文件这些数据量都是小CASE），其次比较重要的一点是会造成后端端数据库的查询压力，对于性能不那么强的VPS，这对并发还是有比较大的影响的。考虑到Blog的文章更新还是相对较慢的，所以一个简单Cache的存在还是可以允许的。&lt;br /&gt;
具体的设计，无非就是在某些数据被请求时先去找Cache，如果没有就查询数据库并将其加入Cache，然后在文章有更新时将Cache清空即可。 &lt;/p&gt;
&lt;h3&gt;用Redux和ES7对前端再次重构&lt;/h3&gt;
&lt;p&gt;现在的设计对SEO并不友好（虽然我已经使用了React-router的服务端渲染（由于有AJAX存在，所以单独使用对SEO并没什么用）并且使用了fragment这个meta tag提示搜索引擎这是一个ajax页面（但这毕竟只能对谷歌有效）），所以我需要一个方法来解决React服务端渲染和客户端的数据同步问题，这个可能只能看看REDUX这样的框架了。&lt;br /&gt;
至于ES7么。。。我觉得我暂时无法用ES5写出干净的前端代码，而下一次重构的时候ES7也差不多出来了。。。&lt;/p&gt;
&lt;h2&gt;本次设计&lt;/h2&gt;
&lt;h3&gt;后端&lt;/h3&gt;
&lt;h4&gt;架构&lt;/h4&gt;
&lt;p&gt;用Python实现。&lt;br /&gt;
作为Blog的后端，无非由六个方面需要考虑，其一是对原始文章的解析，其二是数据库管理，其三是文件监控，其四是Sitemap和RSS的生成，其五是WEB服务器，最后一个就是作为辅助的日志管理了，以下是一张基本的设计框架图，然后根据这张图来简单介绍我此次的设计和用到的技术。
//src.dtysky.moe
&lt;img alt=&amp;quot;FlowChart1&amp;quot; src=&amp;quot;http://src.dtysky.moe/image/blog/2016-03-12a/1.svg&amp;quot; /&gt;&lt;/p&gt;
&lt;h4&gt;文章解析&lt;/h4&gt;
&lt;p&gt;手写HTML文章是笨蛋才会去做的事情（虽然我的确手写过一段时间233），现在一般Blog文章的编写都是使用Markdown或者Restructuredtext，冰鞋比起常常用于Documents编写语言的后者，前者由于语法简洁等原因使用范围更广，所以本Blog的文章使用也是Markdown。&lt;br /&gt;
Markdown固然足够满足文章正文的需求，但有一些meta元素，比如文章标题、作者、分类、时间等要素还是需要自己去想办法添加的，我这里借鉴、或者说是延续了Pelican的设计，将所有的meta元素添加到了文章头，和正文用空行隔开，分别对meta和正文进行解析，最后汇总，所以最后编写的文章实际上是这样的：  &lt;/p&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;test&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;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;x&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;test1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;test2&lt;/span&gt;

&lt;span class=&amp;quot;err&amp;quot;&gt;#&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;h1&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;要注意的是这里并没有Category这个标签，这是因为为了延续之前的设计，并且为了简化工作（其实就是懒...），所以直接将该文章所在的目录作为了分类；除此之外，Authors这个标签也不是必须的，如果没有，解析器会使用配置文件中的default_author代替。  &lt;/p&gt;
&lt;h5&gt;Meta解析&lt;/h5&gt;
&lt;p&gt;Meta解析的实现是十分简单的，无非就是将meta的tag和value作为键值对写入一个hashtable中，单考虑到日后的可扩展性，如上面的结构图所示，我将其分为了两个部分：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;MetaParsers：用于将原始的以字符串的形式存在的meta文本解析为一个hashtable或者任何需求的数据形式，所有parsers都继承自&lt;code&gt;MetaDataParser&lt;/code&gt;父类，文章解析程序在初始化的时候会载入每一个子类并创建一个对象，并在解析时调用该对象的&lt;code&gt;parse&lt;/code&gt;方法，对该对象对应的meta元素的值进行解析，并返回被解析过的数据。  &lt;/li&gt;
&lt;li&gt;SlugWrappers：用于生成已经解析过的metadata的url，也就是会在最终的url里出现的、表征该metadata的唯一定位符，这个可以按照喜好自己来，我大部分是默认为不转换（也就是url和原始数据一致），诚然这可能会带来一些SEO问题，但为了防止冲突和便于日后管理，我舍弃了原先Pelican自带的大写转小写、汉字转拼音的做法。这一步会在&lt;code&gt;parse&lt;/code&gt;完成后执行，所有子类对应对象的&lt;code&gt;warp&lt;/code&gt;方法会被调用，将输入的每一个单个的meta元素转换为url。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;通过这两部的解析，原始的meta文本就被转换为了可以供后续流程处理的数据，在我自身的设计中，可能会出现在url中的metadata都会有&lt;code&gt;view&lt;/code&gt;和&lt;code&gt;slug&lt;/code&gt;两个元素，一个用于显示，一个用于定位。&lt;br /&gt;
得益于这种设计，meta都是可扩展的，如果我想添加一个meta，只需要分别从&lt;code&gt;MetaDataParser&lt;/code&gt;和&lt;code&gt;SlugWrapper&lt;/code&gt;继承一个子类，简单重写一下其中的某些成员函数便可以实现。  &lt;/p&gt;
&lt;h5&gt;Markdonw解析&lt;/h5&gt;
&lt;p&gt;这里直接使用了Python的&lt;code&gt;Markdown&lt;/code&gt;库，但另外加入了&lt;code&gt;Pygments&lt;/code&gt;语法高亮插件，有需要的时候也可以加入其他插件（比如表格）。  &lt;/p&gt;
&lt;h4&gt;数据库管理&lt;/h4&gt;
&lt;p&gt;文章解析之后便需要存到地方，这个地方自然就是数据库了（当然静态页面也可以，不过那并不是此Blog的做法），这里选取的是&lt;code&gt;MongoDB&lt;/code&gt;，原因么...最近用得比较多，顺手就这么选了。这个数据库本身没有什么说的，用起来还是很简单，这部分主要难度还是在于如何管理数据库的写入（这可后面要说到的文件监控有很大关系），考虑解耦的原则，这一部分只负责接受数据和指令对数据库进行操作，不负责决策。&lt;br /&gt;
和上面的设计一样，我设计了一个&lt;code&gt;DatabaseWriter&lt;/code&gt;父类，所有的Meta和正文的写入类都继承自这个父类，之后可以重写父类的方法来完成逻辑，在最基本的设计中，每个子类都拥有三个对外的接口函数&lt;code&gt;insert&lt;/code&gt;、&lt;code&gt;update&lt;/code&gt;和&lt;code&gt;delete&lt;/code&gt;，分别用于想数据库中插入一篇文章、更新一篇文章或者删除一张文章，这是一种增量式的设计，保证每次只会对北改动过的文章进行解析和写入。这一步在文章解析后被执行，最终被调用的就是上面提到的那几个方法，接受解析过的完整文本，将数据写入子类对应的表中。&lt;br /&gt;
这里提一句，被写入数据库中的记录是包含文章文件的路径的，这会在后面用到。数据库的设计在此：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/BlogRework/blob/master/Back/Database.md&amp;quot;&gt;Database desigin&lt;/a&gt;  &lt;/p&gt;
&lt;h4&gt;文件监控&lt;/h4&gt;
&lt;p&gt;文件解析和数据库管理都搞定之后，缺少的就是提供输入激励的东西了。这里采用的数据驱动的设计，程序监听文件的状态，在状态发生变化时响应这些变化并作出响应的处理。&lt;br /&gt;
这里选取的是&lt;code&gt;Watch dog&lt;/code&gt;这个系统状态监听库，其中的&lt;code&gt;FileSystemEventHandler&lt;/code&gt;这个模块中提供了良好文件状态事件支持，只需要指定一个监听的文件夹并去重写它预设的&lt;code&gt;on_create&lt;/code&gt;等方法便可以实现逻辑（这里需要注意，在OSX和LINUX下事件触发的状况不同，所以配置文件中才会有&lt;code&gt;is_linux&lt;/code&gt;这个选项），设计如下：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;当文件新建时，触发&lt;code&gt;on_create&lt;/code&gt;事件，解析文章并将其&lt;code&gt;insert&lt;/code&gt;入数据库。&lt;/li&gt;
&lt;li&gt;当文件被删除时，触发&lt;code&gt;on_delete&lt;/code&gt;事件，通过文件名找到数据库中该文章对应的记录，反向找到正文和所有meta，随后调用所有&lt;code&gt;writer&lt;/code&gt;的&lt;code&gt;delete&lt;/code&gt; 方法。&lt;/li&gt;
&lt;li&gt;当文件被修改时，触发&lt;code&gt;on_modified&lt;/code&gt;事件，解析文章并将其&lt;code&gt;update&lt;/code&gt;入数据库。&lt;/li&gt;
&lt;li&gt;当文件被移动时，等价于一次删除和一次新建。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;如此，文件的解析部分基本就完成了。  &lt;/p&gt;
&lt;h4&gt;Sitemap和RSS&lt;/h4&gt;
&lt;p&gt;这两个基本是Blog的标配了，一个用于SEO一个用于用户订阅，其实也没什么难度，就是每次文本解析完毕后读取数据库中所有的文章根据格式生成几个XML文件，稍微研究一下二者的各市标准就行，不过这其中还是稍微有点坑的：&lt;br /&gt;
在XML中插入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;n&amp;quot;&gt;entity_ref&lt;/span&gt; &lt;span class=&amp;quot;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;&amp;lt;&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;&amp;amp;lt;&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;&amp;gt;&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;&amp;amp;gt;&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;&amp;amp;&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;&amp;amp;amp;&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;&amp;#39;&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;&amp;amp;apos;&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;&amp;quot;&amp;#39;&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;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;考虑到这二者实在没有什么扩展性的必要，我就直接写死了，RSS生成的有全站、作者和分类。&lt;/p&gt;
&lt;h4&gt;WEB服务器&lt;/h4&gt;
&lt;p&gt;数据源准备好之后就是想前端给数据的事了，所以需要开一个WEB服务器。这里选取了Flask作为服务器框架，路由规则也比较简单不再赘述。和上面大多数设计一样，每一条路由规则都对应一个继承自&lt;code&gt;WebHandler&lt;/code&gt;的父类，这个父类又继承自Flask中的View类，父类通过重写View类的&lt;code&gt;dispatch_request&lt;/code&gt;方法来响应请求（采用这种方式来做路由而不是装饰器的方式是为了便于后面路由的统一管理）。&lt;br /&gt;
程序初始化时，程序首先会找到&lt;code&gt;WebHandler&lt;/code&gt;的所有子类并注册路由，之后接收到请求时，程序首先会找到该条路由规则对应的&lt;code&gt;handler&lt;/code&gt;对象，然后调用它的&lt;code&gt;dispatch_request&lt;/code&gt;方法，&lt;code&gt;dispatch_request&lt;/code&gt;方法又会调用&lt;code&gt;_handle&lt;/code&gt;方法进行最终响应的实现。&lt;/p&gt;
&lt;h4&gt;日志管理&lt;/h4&gt;
&lt;p&gt;在主体功能之外还有一个重要的功能是被需求的——日志管理。系统需要有全局的&lt;code&gt;logger&lt;/code&gt;来记录关键的信息同步打印并输出到文件，由于这个比较简单我就自己造轮子了：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;logger输出的信息有三个等级—error、info和warnning，分别打印为不同的颜色并输出到不同的文件。&lt;/li&gt;
&lt;li&gt;每天新建一个log文件便于分析。  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;至此，后端的实现基本完成。&lt;/p&gt;
&lt;h3&gt;前端&lt;/h3&gt;
&lt;p&gt;Node.js实现。
前端主要分为两部分—Client和Server，其中Client是大头，负责和用户进行交互，Server负责将Client端需求的数据传送到用户设备。在本设计中Server还有服务端渲染和SEO的职能。&lt;br /&gt;
当然，一时由于经验，而是由于。。。ES5也没规范到哪去，感觉JS这个语言还是有很多缺陷的，ES6似乎好了很多，嘛。。。先凑合着吧，code style的确很烂我承认。&lt;/p&gt;
&lt;h4&gt;工具链&lt;/h4&gt;
&lt;p&gt;前端开发最好还是有一个用于开发测试的工具链，这对于效率的提升是立竿见影的。这里我选择了&lt;code&gt;Grunt&lt;/code&gt;和&lt;code&gt;Webpack&lt;/code&gt;，前者用于自动化事务管理，后者用于打包和测试项目。我的设计如下：&lt;br /&gt;
//src.dtysky.moe
&lt;img alt=&amp;quot;FlowChart2&amp;quot; src=&amp;quot;http://src.dtysky.moe/image/blog/2016-03-12a/2.svg&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;其中&lt;code&gt;debug&lt;/code&gt;、&lt;code&gt;server-build&lt;/code&gt;、&lt;code&gt;client-build&lt;/code&gt;三个事务分别用于测试、打包Server端程序和打包Client端程序，&lt;code&gt;build&lt;/code&gt;事务包含了前两者，简化操作。  &lt;/p&gt;
&lt;h4&gt;Client&lt;/h4&gt;
&lt;p&gt;Client负责的是用户交互，使用了&lt;code&gt;React&lt;/code&gt;和&lt;code&gt;React-router&lt;/code&gt;作为View和前端路由，具体的设计如下：  &lt;/p&gt;
&lt;h5&gt;View&lt;/h5&gt;
&lt;p&gt;基础显示，包括整体视图的布局和一些动画效果，页面属于响应式设计，为PC和移动端分别走了适配，页面组件包括：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Home，显示所有文章的列表。&lt;/li&gt;
&lt;li&gt;Authors， 显示所有作者的列表。&lt;/li&gt;
&lt;li&gt;Tags， 标签云。&lt;/li&gt;
&lt;li&gt;Author，显示某个作者所有文章的列表。&lt;/li&gt;
&lt;li&gt;Tag，显示某个标签所有文章的列表。&lt;/li&gt;
&lt;li&gt;Category，显示某个分类所有文章的列表。&lt;/li&gt;
&lt;li&gt;Article，显示文章。&lt;/li&gt;
&lt;li&gt;Title，标题栏，显示所有分类。&lt;/li&gt;
&lt;li&gt;Left-image，左侧图像滑窗。&lt;/li&gt;
&lt;li&gt;Menu和Menu-phone，前者在PC端的右下角，后者在移动端的上面。&lt;/li&gt;
&lt;li&gt;Footer，移动端的最下方。&lt;/li&gt;
&lt;li&gt;Music-player，音乐播放器。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;动画效果包括：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;元素色彩和基本位移操作，使用CSS中的&lt;code&gt;transition&lt;/code&gt;实现。&lt;/li&gt;
&lt;li&gt;元素旋转和特殊运动，使用&lt;code&gt;animation&lt;/code&gt;和&lt;code&gt;keyframe&lt;/code&gt;实现。&lt;/li&gt;
&lt;li&gt;左侧的图像滑动，使用&lt;code&gt;Velocity-react&lt;/code&gt;库实现。  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;在页面资源方面，将原来的png图标全部封装成了字体和svg文件，找不到线程的就自己手撸...最终素材大小和请求次数的确降低了许多，封装工具在&lt;a href=&amp;quot;https://icomoon.io/app/#/select&amp;quot;&gt;这里&lt;/a&gt;。&lt;/p&gt;
&lt;h5&gt;等待、错误和404&lt;/h5&gt;
&lt;p&gt;这些细节的地方也是需要关注的，具体是什么效果就自己试吧www：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;等待界面，自己的log加上半圆边框不断旋转，“少女祈祷中”是什么梗就不说了，这里其实是想搞一个抽象的、机械齿轮转动的效果。&lt;/li&gt;
&lt;li&gt;一般错误界面，未知错误的界面，一般看不到的，基本就是机械齿轮卡壳的感觉。&lt;/li&gt;
&lt;li&gt;404界面，直接&lt;a href=&amp;quot;/404&amp;quot;&gt;在这&lt;/a&gt;看吧&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h5&gt;Meta修改&lt;/h5&gt;
&lt;p&gt;虽说是单页应用，但每个不同的页面有自己的meta tag还是很重要的，比如&lt;code&gt;title&lt;/code&gt;、&lt;code&gt;keywords&lt;/code&gt;之类的，这里我是用了React-helmet组件对他们进行了修改（当然，本质上应该还是DOM操作就是，图个方便。）&lt;/p&gt;
&lt;h5&gt;分享组件&lt;/h5&gt;
&lt;p&gt;在每篇文章的右上角都会有一个分享的图标，本次重构去掉了对bshare的依赖，自己写了个分享组件，觉得设计的还成，九大社交网站+本页面二维码。  &lt;/p&gt;
&lt;h5&gt;音乐播放器&lt;/h5&gt;
&lt;p&gt;这也是导致本次重构的元凶了，这个播放器基于&lt;a href=&amp;quot;https://github.com/DIYgod/APlayer&amp;quot;&gt;APlayer&lt;/a&gt;，并做了些许的定制性修改。播放源需要自己在配置文件中进行配置，在当前设计中每次修改完音乐列表就必须重新打包发布一遍Client端的代码，不过把它提出来也不是什么难事，有时间吧。。。&lt;br /&gt;
在我自己的设计中，这个播放器还和文章meta中的&lt;code&gt;music&lt;/code&gt;标签关联，当文章中存在这个标签时，播放器会重新载入文章中制定的音乐列表进行播放，否则就会载入配置中默认的列表。  &lt;/p&gt;
&lt;h5&gt;前端路由&lt;/h5&gt;
&lt;p&gt;这个是用&lt;code&gt;React-router&lt;/code&gt;实现的，也是单页应用的精髓，有了它，伪静态的页面便是轻而易举，他会抓取浏览器的历史并完成响应的路由，页面每次只会更新路由指定的区域。&lt;/p&gt;
&lt;h5&gt;缓存&lt;/h5&gt;
&lt;p&gt;为了避免请求过多，这里建立了一个&lt;code&gt;Cache&lt;/code&gt;用于缓存之前向后端请求过的数据，在页面每次加载之前程序会首先从Cache里取数据，如果没有才会请求并将获得的数据加入Cache中。&lt;/p&gt;
&lt;h5&gt;其他&lt;/h5&gt;
&lt;p&gt;Google analysis和Mathjax的支持以及......彩蛋。&lt;br /&gt;
我认为彩蛋是一个网站必须的东西，它可以出现在Meta里，也可以出现在Code的Comments里，而我，则是将其放在了Console...&lt;/p&gt;
&lt;h4&gt;Server&lt;/h4&gt;
&lt;p&gt;Server框架是&lt;code&gt;Express&lt;/code&gt;，也算是从主流了，主要完成：  &lt;/p&gt;
&lt;h5&gt;静态资源&lt;/h5&gt;
&lt;p&gt;用&lt;code&gt;Express&lt;/code&gt;自带的&lt;code&gt;static&lt;/code&gt;中间件实现，此条规则用于访问静态资源。&lt;/p&gt;
&lt;h5&gt;服务端渲染&lt;/h5&gt;
&lt;p&gt;对所有的正常页面请求，使用&lt;code&gt;React&lt;/code&gt;的&lt;code&gt;renderingToString&lt;/code&gt;方法在服务端进行首页渲染，遗憾的是对于本设计这并不能解决SEO问题（前面说到过），所以，就当是提升用户体验吧。&lt;/p&gt;
&lt;h5&gt;针对搜索引擎的页面&lt;/h5&gt;
&lt;p&gt;每一条正常的url都对应着一条特殊的url -&amp;gt; (/xxx -&amp;gt; /jade/xxx)，这个页面是为搜索引擎（或者说就是谷歌）提供的，具体原理是在正常页面的meta中加入一个&lt;code&gt;fragment&lt;/code&gt;的tag，谷歌找到这个tag后就会在原始url后加上&lt;code&gt;?_escaped_fragment_&lt;/code&gt;，我们可以在Server捕捉它并将准备好的另外一个页面提交给谷歌。我所准备的页面是用&lt;code&gt;jade&lt;/code&gt;作为模板的，并用&lt;code&gt;requests&lt;/code&gt;向后端发起请求来获取数据。这些页面去掉了动画和播放器，基本可以看做纯粹由HTML+CSS构成的静态页面了。  &lt;/p&gt;
&lt;h5&gt;日志管理&lt;/h5&gt;
&lt;p&gt;这里同样需要一个Logger，我选取的是&lt;code&gt;Tracer&lt;/code&gt;这个库，将其封装为了一个中间件。  &lt;/p&gt;
&lt;p&gt;到此，前端基本完成。&lt;/p&gt;
&lt;h5&gt;其他&lt;/h5&gt;
&lt;p&gt;NGINX作为反向代理服务器，Forever.js做进程守护。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Saturday, 12 Mar 2016 22:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2016.03.12 22:00:article/Create-2016_03_12_a</guid>
<category>Blog</category>
<category>重构</category>
<category>React.js</category>
<category>Flask</category>
<category>MongoDB</category>
<category>Express.js</category>
<category>Velocity.js</category>
<category>Node.js</category>
<category>Python</category>
<category>Markdown</category>
<category>WatchDog</category>
<category>Jade</category>
<category>Css</category>
<category>SEO</category>
</item>

<item>
<title>新年计划</title>
<link>http://dtysky.moe/article/Life-2016_02_10_a</link>
<description>&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;br /&gt;
我打断了她。&lt;br /&gt;
“怎么会够呢，你可是第一个和我一样的人啊。”&lt;br /&gt;
“&lt;strong&gt;和我一样&lt;/strong&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;hr /&gt;
&lt;p&gt;嘛，这个梦也算是证明了。&lt;br /&gt;
我自己潜意识里认为自己是怎样的人吧。&lt;br /&gt;
当然，现实中是不会有这么个妹子给你“结束”的机会的，弱者，也有那么一点概率能成为强者。&lt;br /&gt;
所以，目标还是要有的，虽然我现在工作还是会占去很多时间，只能说尽力吧。  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;新年目标：&lt;/strong&gt;  &lt;/p&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;女子力： 化妆，减肥，伪声&lt;/li&gt;
&lt;li&gt;梦见星空之诗——剧本： 尽量完成核心内容&lt;/li&gt;
&lt;li&gt;Train to new year——2D卷轴游戏： 在下个春节前完成  &lt;/li&gt;
&lt;li&gt;学习日语： 是时候系统学习了，不过这个应该是优先级最低的一个  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;/blockquote&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>Wednesday, 10 Feb 2016 20:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2016.02.10 20:00:article/Life-2016_02_10_a</guid>
<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>【感想】玉响-毕业写真</title>
<link>http://dtysky.moe/article/Art-2015_09_23_a</link>
<description>&lt;hr /&gt;
&lt;h1&gt;前言&lt;/h1&gt;
&lt;p&gt;挺久没写文字了，无论这是由于现实的忙碌、自我的懒惰亦或是二者相辅相成狼狈为奸导致的悲剧性结果，其能造成的影响也只有一个——那就是表述能力不可避免的下降。当然，就这一点而言我也并不能错怪现实，要怪，也只能怪自身的无能为力而已吧，这是我的锅。但从另一个角度来想，当一个人在面对任何需要耗费他一定的精力才能做好的事情时，他的主观能动性当然是举足轻重的，然而客观事实却是无法忽视的，比如让一个处于焦虑和绝望之中的人去表达终成眷属的男女主所持有的那种暖人心田的幸福时，他所真正表达出来的，恐怕是某种令人感到背脊发凉的东西吧——他往往会把这种幸福以某种“至高的喜剧”的形式表现出来。&lt;br /&gt;
既然如此，我为什么又要重新开始写这些东西？诚然，看完玉响之后我的确深受感动和鼓舞，但这并不构成我写出这篇东西的充分条件。即便是再加上一个像是“由于发烧撞墙并且一个人撑了三天最终还是挂水还得给撑着护士姐姐以友善的微笑”的条件也不能，不过所谓充分条件必要条件毕竟只是隶属于逻辑学的那些毫无情趣的无聊玩意，而且条件多了也不利于用一行三目运算符解决——就算解决了可读性也不会怎么样，所以也不需要太在意，到此为止。&lt;br /&gt;
这样看来我一上都是在说废话，但早在我的某些文章里我就说过，我喜欢说废话——无与伦比的喜欢。我所言的废话都是有其自身的意义在其中的，无论是为了引入语境、还是给自己以缓冲、亦或是纯粹地为了表达而表达，我认为都是美的。一个好的写作者应该会说废话，用于说废话，敢于批判那些不说废话的作家，比如——那些将自身要拯救世界的宏大愿望嵌入到自身作品的每一个角落的作家，我想，他们肯定比不过那些喜剧的作者严肃，有何苦装的这么严肃呢？然而虽然很不愿意承认，我骨子里仍然是一个比较严肃的人，所以自然也就愿意说废话，这是符合科学真理的，是符合大道的，是符合The Will of God的。作为一个严肃的人，有些东西是不得不说出来的，如果不趁着这份矫情表达出来，那么一切都只会随着时间渐渐被淡化，最终什么都说不出来了。更何况时不时写一写文字还能挽救挽救我这已经死的差不多的逻辑能力和整合能力，何乐而不为呢？  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;副题&lt;/h1&gt;
&lt;p&gt;我有一个自认为颇有道理的论断——越是年轻的写作工作者，越喜欢试图去表达一些宏大而缥缈的主题，他们喜欢构建一个个精妙的体系，就像是黑格尔那样，但同时却又没有那样的驾驭能力，设定一个苦大仇深的主角，一个分崩离析的世界，一个个只手遮天的NPC，大家杀啊杀啊杀，最后主角得到了救赎，一切OVER，这也就是比较典型的中二套路。当然，并不是说这样的剧情设定或者模式有什么问题，甚至中二其实也不是核心的问题，最大的问题在于，这些作品往往都带有着明显的二元论色彩，也就是所谓的“非黑即白”，善的就是善的，恶的就是恶的，这也就导致这类作品充满着不可思议的理想主义论调——这潜藏着一种思想，如果世界上的“恶”全都被消灭了，那么正义和爱的光芒必将照耀世界，然而稍微有些常识的人都会看出其中的问题——历史上始于这种思想的那啥不要太多，结果呢？就像是加缪所言“为了艺术而艺术，不过是对现实逃避而已”，这类作品便是这样的一种逃避，作者将一切限定在了自身的理想世界之内，试图去在其中搜索这个现实世界中存在的最优解——他们认为，这些最优解是存在于他们理想世界之中的——也就是那些现实世界的极端边界之中。然而事实并非如此，我们现在都知道，世界并非二元论的，而是一种由若干种互相对立由互相包容的思想共同构成的，所以现实并不是一个理想的系统，而是一个复杂的混沌系统，最优解一般情况下是找不到的，甚至连去找的机会都没有，更多的可能是直觉掌控着一切——这也就是为什么战胜了所谓的“恶”之后世界还是那个鸟样，因为人根本就不是绝对理性的。一个人在极端环境下会做出什么事情可能连自己都不会相信，他可能会杀人、吃人等等，但当他一旦回到了那种不用依靠杀人和吃人就能存活的环境，他又会忏悔、又会悔悟，那么这个时候我们会原谅他么？吃人的他和忏悔的他究竟哪个才是真正的他？他真的分裂了么？&lt;br /&gt;
世界是复杂的，人也是复杂的，二者都不仅仅有横向（空间）上的对比，还有纵向（时间）上的对比。很多作者看到了简单的二元论站不住脚（这不代表不受欢迎，瑕疵必报这种还是很能降压的hhh），于是尝试着更加深刻和复杂的思考。他们在以上的模型基础之上继续发展，然后便出现了划分，一类就是所谓的“洗白”路线，大家都有难言之隐，BOSS都是为了人类好/宇宙好/主角好，总之就是为了你全家，在主角经过重重考验打倒BOSS后，BOSS表明了自己的真意，并露出了欣慰的微笑——“世界就交给你了”。我们说这个逻辑并没有什么问题，最大的问题在于这个洗白往往也太简单了，BOSS被打败后往往三言两语就被主角征服了——这难免也是太小瞧观众的智商了，你作为BOSS的坚持和信念、你的职业操守呢？&lt;br /&gt;
另一个分支则是比较合理，比如素晴啊，DI啊，这类作品虽然也是这种性质，但人家做的极致啊，我就是为了自己爽，我就是为了守护自己想守护的人，世界对我来说怎样都好——对于这一点而言无论是主角和BOSS都是平等的。他们的核心都被表现为——我们的思想没有绝对的对错，虽然你很有道理但是我无法认同，所以就来战，战败了就滚蛋，到了这一点，虽然披着的是各种超现实的外壳，但内核却是相当的现实主义了，当然这是一种比较极端的现实主义（极端肌肉笨蛋的现实主义T_T），比起那些不伦不类的洗白圣母，这类作品更能表现出一种人类对欲望（包括爱、正义、希望、死等等）的极致态度，不错，是极致的“肯定”。像是“时间啊，停下来吧，只因森罗万象之中你最美丽”这种话如果没有相当阅历的人，我觉得是写不出来的，或者说也不过是徒有其名而已——这应当是经历过对中二的“否定之否定”之后才能写出来的东西。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;主题&lt;/h1&gt;
&lt;p&gt;那么玉响属于哪一类呢？都不属于。它属于这样的一类作品——像是一个五六十岁的老人，静静坐在某个湖畔，在黄昏时刻，轻柔的风不时带来了湖中金色的气息，这气息融入了他手中名为世间百态的苦茶之中，他一边喝着茶，一边对着另一个时空中打打杀杀的角色们（这其中也包括现实世界的我们）微笑着，不用言语，而仅仅用目光和表情感染并试图给予我们一些指引。&lt;br /&gt;
以上这种相当矫情的风格并不适合这里的语境，不过也无所谓了，就当是我被感动得不能自己完全不知道自己在写什么吧。这种通过日常情节、将矛盾和冲突控制在一个小圈子之内的作品往往是比较真实的，比起上面那种宏大的模式，这种作品更加具有现实意味，同时也更难驾驭，这也就是我在看完玉响之后就认为这不会是一个二三十岁的作者原作和编剧的作品的原因。结果一查，佐藤顺一——1960年出生，是个有趣的大叔，而且还是水星的...。由“认为导演应该具备演员的功底”这一点可以看出，这个导演的三观还是很正的HHH。  &lt;/p&gt;
&lt;p&gt;玉响这部作品讲得主要是四个女主角成长的故事，前面的剧情就不回顾了，剧场版既然名为毕业写真，那么讲得也就是整部作品接近完结的故事了。到现在为止，剧场版总共出了两集，可以说分别表现了四个女主找到未来前进方向过程中的一些曲折吧。其中女一，也就是啪嗒，她的进路可以说是延续了父亲的道路，父亲对照相的热爱和理解对她的影响无疑是重大的，父亲的死对她的影响也是重大的，而这种影响渗入了她的整个成长过程之中，越成长，她便越能理解父亲在她小时候说的那些话的真意，她也从来没有动摇过这个想法，所以，她的人生实际上是一种延续，那种与世无争的、只想记录一切美好，让这些美好永远存在于这个世界之上，或许这也算是失去了某些真正重要的存在的人才特有的情怀？当然，也可能是我的理解偏差。但无论如何，女主的这个选择都是崇高和幸福的，没有什么能够比在造福他人又能让自己满足的这种事情更让人愉悦了，不是么？&lt;br /&gt;
与女一相似的另一个女主是乃理惠，她也比较像是继承和延续，不过更加具有自我意识、更加要强。受到奶奶的影响，她想成为一个甜品师，她也为之付出了若干努力，达到了很不错的水准，她有着比较明确的目标——虽然这个目标看起来有些不切实际，然而她还是把这个作为信念。但即使如此，她毕竟还只是一个高中生，这种信念是随时都会动摇的，在兄长对她说了要成为世界一流的甜品师需要忍受怎样的孤独去国外XXX的时候，她有些退却了——虽然这种退却是孩子气式的。当然，我们说这里还是有相当的艺术加工的，不过无伤大雅，那种青春的时候，或者说即便是在工作之中，自己原先坚信的一个东西被眼光高出自己的人所否定、同时否定的言语又是那么难以反驳的时候，那种无助、失落、绝望——这些都是来自于愤怒，而一切的愤怒都是来自于无能为力，是每个人都或多或少有过的。面对这种愤怒，多少人选择了坚持，又有多少人选择了妥协和退却呢。的确，我们是不能责怪那些退却者的——它们在选择时所需要付出的代价，比一个高中生要高太多了，但从另一种角度来讲，不也正是因为不知不觉之中我们自己构建起的这些“代价”，或者说叫做“社会责任”，束缚住了我们的双脚么？这其实是一个很有意思的话题——拥有的越多，越保守。&lt;br /&gt;
薰和啪嗒也很像，属于那种想要为别人奉献的那种。一开始，她自以为属于被动型的那种人，只有被拜托才回去想帮忙，而且一旦被拜托就一定会去帮忙，并且往往把别人的事情看得比自己要重要，所以在大家都决定了前进方向的时候，她还在踌躇，在想着自己如果主动想要抓住一些什么——有什么一始而终的兴趣就好了。“如果一开始坚持什么就好了，也不会像现在这样什么都是半吊子”，如果掌握了某一门专业的技术或者爱好，前途也就自然而然明了了——她是这么想的。这一点的确戳到了本人的软肋，我一开始也是如此，广泛地去做东西，试图学会一切、对一切都达到信手拈来的地步，然而我的精力却的确是有限的，并且显然没有十几年来给自己评估的那种智商，结果呢？虽然的确做出来了一些东西，但在每个领域却几乎都是半吊子的程度，虽然有一方面比较精通的，但又不愿意为了这方面而舍弃自己的兴趣和梦想，以至于在前不久还在感慨——“如果一开始我就把所有精力投入到系统的软件学习，现在该是何等的状况啊。”“是不是那时候听了父母的，去事业单位图个清闲搞副业就好了啊，要什么尊严啊...”这形成了我的一个心结，一直堵在某个地方让我很不好受，所以我很期待薰是如何解决这个问题的。那么，她是如何解决的呢？她看到了这个：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;P1&amp;quot; src=&amp;quot;/theme/image/2015-09-23-a/01.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;P2&amp;quot; src=&amp;quot;/theme/image/2015-09-23-a/02.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;P3&amp;quot; src=&amp;quot;/theme/image/2015-09-23-a/03.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;这一幕是绝妙的，仅仅通过婚礼现场和薰自身的呆滞，便表现出了她内心观念的一种转换，加上姐姐夜里的开导，她终于明白了——如果没有为她人奉献的主观意志，根本就不可能想要尽心尽力将承诺做到最好，同时还没有怨言。所以，她根本上还是有着自己自始至终的信念的，那就是——保持善良，为大家的幸福尽一份力，这是她仅存的、属于自己的权利。看到这里，反观我自己，是否能得出一个结论呢？或许我也只不过是想要维持自己那仅存的、属于自己的权利罢了，我知道，除了那不知道什么时候可以实现的梦想，我已经什么都没有了。这里有点矫情了，大家笑笑就好。  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;P4&amp;quot; src=&amp;quot;/theme/image/2015-09-23-a/04.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;总之，这是一部良心番，有机会一定补一补水星www这种日漫里往往有一个让人非常羡慕的、理想化的事实，那就是——当你说你的梦想是做游戏的时候，至少不会有人说这东西不是正经玩意，同时还感到焦虑，尤其这话是从你的父母口中说出的。  &lt;/p&gt;
&lt;p&gt;以当前页面的BGM，也就是剧场版的主题曲的歌词结尾：  &lt;/p&gt;
&lt;div class=&amp;quot;text-center&amp;quot; style=&amp;quot;color:rgb(255,255,255);background-color:rgb(10,200,100);font-size:14px;&amp;quot;&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;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;
AH 我的今后 是为了知晓这份深意
&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;
10年後 20年後 振り返って私 どんな気持ちで今日を思い出すかな&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;
AH 我的时光 开始变得充实
&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;
AH 我的今后 是为了知晓这份深意
&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;
AH 我的今后 是为了知晓这份深意

&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;

&lt;/div&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Tuesday, 22 Sep 2015 01:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.09.22 01:00:article/Art-2015_09_23_a</guid>
<category>影评</category>
<category>动画</category>
<category>玉响</category>
<category>感想</category>
<category>梦想</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>迷惘</title>
<link>http://dtysky.moe/article/Life-2015_08_09_a</link>
<description>&lt;p&gt;挺久没有更新了，一个是毕业后的旅行，一个是入职后比较心烦。也就是大多数人都会碰到的梦想和现实的矛盾，我的梦想是比较清晰的，所以更加痛苦。&lt;br /&gt;
毕业后去了上海、无锡、北京和日本，进行了总共大约二十天的旅行，每个地方都有朋友招待，所以玩得比较开心，之后又去北京参加了一次Hackathon，之前的那些孤独和寂寞什么的也在瞬间就烟消云散，那阵子可以说是近年来最快乐的时段了吧，接下来...自然就是入职。&lt;br /&gt;
其实我一开始对于入职是有些期待的，能够从事一个比较高逼格的职业，做稍微高逼格的东西，同时在入职前部门主管还说此部门加班不严重，你还是可以有时间做你的游戏的，我也就来了，但进来后发现。。。和说好的不一样啊。。。&lt;br /&gt;
只能说我太年轻和理想化了吧，虽然之前在学校也保持着平均每日十来个小时的码代码强度，也就是说，加班对我来说并无所谓，但对于一个只适用于谋生的东西完全消耗精力是我所不能容忍的，所以我尽量减少加班时间，仍然期望一边工作一边做游戏，但是。。。导师对我说的“你要想在这呆下去，就必须把所有精力花在公司，自己的事就别干了”直接否定了我的打算。当谈及我原来的想法时，导师作为一个过来人，抛出了他们的惯用说法——“你们现在就是没有责任感，太自我了，等到要用钱的时候，梦想算什么？”&lt;br /&gt;
他说的，对于现实社会，对于中国社会，在很多层面说，或者在绝大多数的层面上，的确没错，但是，我无法认同。&lt;br /&gt;
如果我继续在这个地方，做几年FPGA的通信协议，搞深搞通，的确看起来日后的前景很不错，但那时，我估计无法交出一个满意的答案——毕竟，无论是什么人，最终都是要面对自己的上帝的。&lt;br /&gt;
基于以上理由，在工作时我虽然尽量把一些想法踢出脑袋，想着尽力全心全意工作，但却仍然只有不到巅峰期一半的效率，以至于导师完全认为我是个无能的新手，我也无法还口，的确，我现在的表现不就是如此么？&lt;br /&gt;
作为我这样的兴趣导向的人，果然还是无法去对完全不感兴趣的东西保证多高的学习效率啊，即使它是我赖以生存的东西。我做错了么？或许吧。但或许我也没错，本来世界就不是非黑即白的东西，想要有所收获就必须有所舍弃。当时犹豫不想舍弃自己的FPGA背景和所谓的“稳定”来到了HW，也成为了这个部门今年唯一的应届生（部门其他人都比我大六岁以上），这一切似乎起步很好，但并不是我想要的。&lt;br /&gt;
我想转行。&lt;br /&gt;
如果现在不转，未来的成本将会越来越大，毕竟FPGA搞通信和游戏完全是风马牛不相及。&lt;br /&gt;
而现在又有这么一个机会，通过朋友内推拿到了上海一个小手游公司的机会，下周去面试。好在有过C#和游戏开发功底，虽然U3D只是大大的半吊子，熟悉ren&amp;apos;py引擎和python也不会拿来用，但还是争取吧，如果面试过了，估计就毫不犹豫了。虽然也知道国内游戏行业是血汗工厂，但这对于我无所谓，我并不怕加班，转行是基于以下考虑的——一是最终我肯定要把游戏做出来，二是我虽然对FPGA有兴趣，但对通信业务的确没兴趣，第三，周围的朋友圈基本都是做软件的，他们的眼界和思想，虽然有些对不起现在的部门的人，但还是想说，高出太多了。。。说到底，我还是想做能够出效果的东西。。。既然工作会占去绝大多数的精力，那么就把这些精力花在刀刃上吧，先全职进去再说。即使到时候游戏行业混不下去，软件功底在，还可以转互联网吧，而且在游戏行业也可以积累一些做游戏需要的资源。&lt;br /&gt;
说到底，现在的痛苦是由以前的选择造成的，不能继续错下去了。虽然这样很对不起公司（前两个月相当于公司亏本），但的确是没有办法了。&lt;br /&gt;
对了，如果面试成功，还要说服父母（听我说现在就想辞职转行，还是游戏行业的小公司，家里都炸了）。。。否则就只有先赚房租再找机会。。。钱果然很重要。。。哈哈哈。。。&lt;br /&gt;
如果没有面试成功的话。。。就需要透支精力继续自学一些知识了，折寿就折寿吧，怎样都好了。&lt;br /&gt;
最后说一句，HW里面很压抑，即使不加班也很压抑，流程很繁重，环境也很沉重，几乎没有自由，所以个性奔放和机灵的少年少女就不要来了，如果来了你们会体会到什么叫做真正的“度日如年”的。&lt;br /&gt;
只希望，或者说必须是——“不要成为自己所讨厌的那种人”。  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;Dream&amp;quot; src=&amp;quot;/theme/image/2015-08-09-a/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, 09 Aug 2015 21:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.08.09 21:00:article/Life-2015_08_09_a</guid>
<category>前途</category>
<category>游戏</category>
<category>梦想</category>
<category>现实</category>
<category>回顾</category>
</item>

<item>
<title>毕设PPT</title>
<link>http://dtysky.moe/article/Create-2015_06_05_a</link>
<description>&lt;p&gt;将鼠标指向框内即可。&lt;br /&gt;
设计的是到一定的步骤按&amp;quot;Return&amp;quot;返回，点下一个大分类。&lt;br /&gt;
没有可以按的时候按空格或者方向键。&lt;/p&gt;
&lt;hr /&gt;
&lt;div style=&amp;quot;text-align: center;margin-top: 50px&amp;quot;&gt;
    &lt;iframe  style=&amp;quot;border-style: dotted;border-color: black&amp;quot; border-style= align=&amp;quot;middle&amp;quot; width=&amp;quot;800px&amp;quot; height=&amp;quot;600px&amp;quot; src=&amp;quot;http://show.dtysky.moe/graduation-ppt/ppt.html&amp;quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Sunday, 07 Jun 2015 16:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.06.07 16:00:article/Create-2015_06_05_a</guid>
<category>FPGA-Imaging-Library</category>
<category>毕业设计</category>
<category>impress.js</category>
</item>

<item>
<title>毕设项目V1.0发布</title>
<link>http://dtysky.moe/article/Create-2015_06_02_a</link>
<description>&lt;p&gt;这部分是致谢，放在这里算了。。。虽说最后论文从六万被砍到一半。。。嘛，没经验废话太多吧。  &lt;/p&gt;
&lt;p&gt;项目主页在这里：&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;p&gt;论文在这里：&lt;br /&gt;
&lt;a href=&amp;quot;http://dtysky.moe/tag/fpga-imaging-library.html&amp;quot;&gt;F-I-L 论文&lt;/a&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;在写完论文的最后一个字时，我感到了莫大的喜悦和恐惧，喜悦的是作为毕业设计的部分终于算是比较完美得被完成，恐惧的是这个开源项目要想存活下去，还有很多东西需要完成，而我不知道自己是否有这个精力和决心。&lt;br /&gt;
四年前，我来到了这个东南大学，一年的懵懂和迷惘之后，感谢黄安杰同学的引导和鼓励，我走了出来，开始利用学校基于的资源和平台认真学习技术。一晃便是三年，三年间，我完成了一个大型SRTP项目，在本院张小国老师、电子学院刘新宁老师和机械学院邓老师的热心指导下，自学了软件、机械、硬件设计方面的众多知识，这也为我的毕业设计打下了良好的基础。&lt;br /&gt;
十二个月前，我参加了本校和Xilinx合作的暑期学校，在一个月的学习和研究后主动成为了Xilinx的实习生，在做了一些工作之后，最终决定在Xilinx完成毕业设计，这要感谢王立辉老师和陆家华经理，给我提供了去做自己想做的东西的机会。&lt;br /&gt;
这个项目最初是以设计一个五子棋系统为导向的，但是很遗憾由于摄像头设备的原因无法完成完美的去噪，而当意识到之时时间已经不足以从头再来了，所以我选择进行转型，世界上有用轮子的人，但也有造轮子的人，而在FPGA的图像处理方面，虽然已经有了一些公司，比如NI的FPGA图像库，但是基本都是闭源的，都需要在支付不菲的费用之后获得授权。所以，出于对开源项目的热情，我选择去实现一个开源的FPGA图像处理库，并竭尽所能让其标准化和规范化，对用户提供友好的测试流程，这也算是对将进入的工程界做一次前置演练吧。在协议方面，我选择了LGPL协议，选择这个协议的原因是——我希望有更多的人可以使用它，但是拒绝将其本质上的商业化，虽然可能也只是我的一厢情愿吧。发布选择了建立博客所使用的Pelican框架，波形图使用了Wavedrom进行绘制，同时感谢Github的Student Developer Pack，提供了一年免费的Digital Ocean的VPS，用于搭建项目主页。&lt;br /&gt;
再次感谢王立辉老师、陆家华经理和和Xilinx中前辈的指导，让我能够最终完成计划中的任务。&lt;br /&gt;
最后，向提供了测试图像的画师表示感谢：  &lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;http://www.pixiv.net/member.php?id=122500&amp;quot;&gt;月岡月穂&lt;/a&gt; &lt;a href=&amp;quot;http://www.pixiv.net/member.php?id=420928&amp;quot;&gt;LM7&lt;/a&gt; &lt;a href=&amp;quot;http://www.pixiv.net/member.php?id=2824699&amp;quot;&gt;cotta&lt;/a&gt; &lt;a href=&amp;quot;http://www.pixiv.net/member.php?id=2823291&amp;quot;&gt;041&lt;/a&gt;  &lt;a href=&amp;quot;http://www.pixiv.net/member.php?id=60263&amp;quot;&gt;H2SO4&lt;/a&gt; &lt;a href=&amp;quot;http://www.pixiv.net/member.php?id=414162&amp;quot;&gt;パセリ&lt;/a&gt; &lt;a href=&amp;quot;http://www.pixiv.net/member.php?id=3952&amp;quot;&gt;おにねこ&lt;/a&gt; &lt;a href=&amp;quot;http://www.pixiv.net/member.php?id=1444045&amp;quot;&gt;色原みたび&lt;/a&gt; &lt;a href=&amp;quot;http://www.pixiv.net/member.php?id=225421&amp;quot;&gt;ぜろきち&lt;/a&gt; &lt;a href=&amp;quot;http://www.pixiv.net/member.php?id=2671042&amp;quot;&gt;方向錯亂&lt;/a&gt; &lt;a href=&amp;quot;http://www.pixiv.net/member.php?id=19365&amp;quot;&gt;あきのん&lt;/a&gt; &lt;a href=&amp;quot;http://mikumikumiku.blogbus.com/tag/%E5%88%9D%E5%BF%83%E7%A4%BE/&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, 02 Jun 2015 22:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.06.02 22:00:article/Create-2015_06_02_a</guid>
<category>FPGA-Imaging-Library</category>
<category>毕业设计</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>Modelsim Fuck you</title>
<link>http://dtysky.moe/article/Life-2015_05_14_a</link>
<description>&lt;p&gt;为什么一个写Modelsim的文章要发在life分类下呢？这一切都源于这张图：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;/theme/image/2015-05-14-a/1.png&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;不错，一般情况下，这张图并没有什么了不起的，但如果加上若干时间的调试和排查才发现这个2B的问题，那就是另一回事了，这个问题是什么呢？&lt;br /&gt;
以下公式：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;n * 64 * 512 + 1(n = 1, 2 ,3 , 4, 5, 6, 7, 8)  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这是在仿真xilinx的一个512x512的blockram控制器的时候遇到的问题。&lt;br /&gt;
以上公式的地址对应的数据必然会出错，奇数缺一个，偶数多个0，所以会造成WLGC的图像错位问题，而且正如图中所示，仿真的波形是木有问题的！！！只有在写入的文件中才会有问题，而且无论是用$fflush还是其他啥不用使能判定都是一样的！！！&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;&amp;quot; src=&amp;quot;/theme/image/2015-05-14-a/2.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;显然不能。&lt;br /&gt;
好的，就这样，PSNR用中间和右边的图来算吧。&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>Thursday, 14 May 2015 00:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.05.14 00:00:article/Life-2015_05_14_a</guid>
<category>Modelsim</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>互联网之子</title>
<link>http://dtysky.moe/article/Art-2015_05_05_a</link>
<description>&lt;p&gt;第一次认识他，是由于某个课程大作业用的web.py这个后端框架
然后无意中看了这个，嘛。。。&lt;br /&gt;
&lt;a href=&amp;quot;http://movie.douban.com/subject/25785114/&amp;quot;&gt;互联网之子&lt;/a&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;我为什么要搭博客？&lt;br /&gt;
一开始是因为那啥吧，某个群里某个大佬说了个搭个技术博客有助于找工作&lt;br /&gt;
当然这只是一个开头的原因，后面更深层次的原因是从花了两个月调某个该死的片子发现了一些该死的问题开始的&lt;br /&gt;
这些该死的问题是如此该死，以至于在官方社区都没有得到应有的解答，最后还是自己试出来的，所以我不想让更多遇到这些问题的人重复我那些焦头烂额SB的经历，所以想通过这么一个方式去帮助别人&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;
我羡慕，并憧憬着Aaron Swartz这种人，却很鲜见地没有在羡慕中包含嫉妒（我一直是一个嫉妒心很强的人，这份嫉妒来自于我对被嫉妒者所展现出来的东西和我认为他真实所有的东西的失衡，当然现在削减了不少&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;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Tuesday, 05 May 2015 14:45:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.05.05 14:45:article/Art-2015_05_05_a</guid>
<category>影评</category>
<category>Aaron Swartz</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>Message board for FPGA-Imaging-Library</title>
<link>http://dtysky.moe/article/Create-2015_05_04_a</link>
<description>&lt;h2&gt;Progress:&lt;/h2&gt;
&lt;p&gt;Building documents...  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;What ?&lt;/h2&gt;
&lt;p&gt;F-I-L is a open source library for image processing on FPGA, it already contents many useful operations, and is updating. All the operations are packaged to IPCores, and of course they follow a same and standardized interface, moreover, each of them can work on piplines-mode and req-ack mode.&lt;br /&gt;
F-I-L是一个FPGA平台的开源的图像处理库，已经拥有了许多常用操作，并在不断更新中。这些操作被以IP核的形式进行了封装，遵循同一种规范化的接口，同时具有流水线和请求响应两种使用模式。
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://fil.dtysky.moe/theme/image/what.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;How ?&lt;/h2&gt;
&lt;p&gt;Certainly, all IPCores have their software simulations, functional simulations and testing on board, the same folder structure and interface can make user do simulations and testing conveniently.&lt;br /&gt;
大部分IP都拥有自己的软件仿真、功能仿真和板上测试，并且具有相同的文件结构和接口，能够让使用者很方便得进行测试和仿真。目前IP形式只支持Xilinx的Vivado套件，不排除未来会向Quartus搬移的可能。 
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://fil.dtysky.moe/theme/image/how.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;More&lt;/h2&gt;
&lt;p&gt;More informations are &lt;a href=&amp;quot;http://fil.dtysky.moe&amp;quot;&gt;here&lt;/a&gt;. &lt;br /&gt;
更多信息请看&lt;a href=&amp;quot;http://fil.dtysky.moe&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>Monday, 04 May 2015 02:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.05.04 02:00:article/Create-2015_05_04_a</guid>
<category>FPGA-Imaging-Library</category>
</item>

<item>
<title>My Resume</title>
<link>http://dtysky.moe/article/Create-MyResumeEn</link>
<description>&lt;div id=&amp;quot;resume-header&amp;quot;&gt;
    &lt;img id=&amp;quot;resume-photo&amp;quot; src=&amp;quot;/theme/image/head.png&amp;quot; alt=&amp;quot;head&amp;quot;&gt;
    &lt;div class=&amp;quot;resume-title&amp;quot;&gt;
        &lt;h2&gt;Tianyu Dai&lt;/h2&gt;
        &lt;p&gt;E-Mail : dtysky@outlook.com&lt;/p&gt;
        &lt;p&gt;Homepage : dtysky.moe&lt;/p&gt;
        &lt;p&gt;Github : dtysky&lt;/p&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;hr /&gt;
&lt;h2&gt;Skills&lt;/h2&gt;
&lt;h4&gt;Platform:&lt;/h4&gt;
&lt;p&gt;FPGA, PCB, Software, Machine   &lt;/p&gt;
&lt;h4&gt;Tools：&lt;/h4&gt;
&lt;p&gt;Expert : VHDL, Verilog, SystemVerilog, Python, Html, Css, Cadence&lt;br /&gt;
Competent : C, C#, JS, Matlab, AutoCad&lt;br /&gt;
Average  : Unity3d, AE, PR, PS  &lt;/p&gt;
&lt;h4&gt;Knowledge background：&lt;/h4&gt;
&lt;p&gt;Machine vision, CPU, Embedded system  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Major Projects&lt;/h2&gt;
&lt;h3&gt;梦见星空之诗 - Aria der Freiheit und des Seins：&lt;/h3&gt;
&lt;p&gt;Discription：An indie game。&lt;br /&gt;
Size: Large &lt;br /&gt;
Role：Planner, Writer, Programmer&lt;br /&gt;
Progress：Refactoring the scenario&lt;br /&gt;
Used skills:  &lt;br /&gt;
Writing, Python, Ren&amp;apos;py&lt;br /&gt;
Source: Closed source &lt;br /&gt;
Completed work：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Game script parser  &lt;/li&gt;
&lt;li&gt;Manuscript for game, 700000 words  &lt;/li&gt;
&lt;li&gt;system and GUI for game  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;3D Displayer：&lt;/h3&gt;
&lt;p&gt;Discription：An 3d displayer based on a high-speed rotation led array &lt;br /&gt;
Size: Large &lt;br /&gt;
Role：Work individually&lt;br /&gt;
Progress：Basically finished&lt;br /&gt;
Used skills:  &lt;br /&gt;
PCB, FPGA, C, C#, Autocad&lt;br /&gt;
Source: &lt;a href=&amp;quot;https://github.com/dtysky/3D_Displayer_Controller&amp;quot;&gt;Controller&lt;/a&gt;，&lt;a href=&amp;quot;https://github.com/dtysky/Led_Array&amp;quot;&gt;PCB&lt;/a&gt;，&lt;a href=&amp;quot;https://github.com/dtysky/3D_Displayer_Machine&amp;quot;&gt;Machine&lt;/a&gt; &lt;br /&gt;
Completed work：   &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;mastered Cadence suite, designed a 120*114 LED array composed by 0603LED, PMOS, NMOS the pixel density is 15.2d/cm^2. At the same time, I finished the controlling circuit that integrates trigger, register, power, and the control of the LED array. It has a 4-layer structure with high density wiring. The manuscript is mainly wrote by python, which improved the mapping efficiency  &lt;/li&gt;
&lt;li&gt;learned to use FPGA; using a series of  Cyclone IV components provided by Altera, with VHDL, I&amp;apos;ve finished:&lt;br /&gt;
1) DDR2 controller, starting from sketch. Coding with Testbench, completed the packaged data testing with Modelsim, also succeed the on-board test&lt;br /&gt;
2) LED controller, starting from sketch. Using ROM completed the on-board test&lt;br /&gt;
3) Learned to utilize the high-speed USB chip cy68013 on slave-fifo mode, finished the FPGA controlling component and tested on board  &lt;/li&gt;
&lt;li&gt;With C#, using the API that is provided by cy68013 chip .net, utilizing object-oriented, multi-tasking ability, etc programmed the host computer  &lt;/li&gt;
&lt;li&gt;With C, designed the firmware for Cy68013  &lt;/li&gt;
&lt;li&gt;Designed the mechanical parts, includes the base, the coupling, and the scaffold for the LED array (AutoCAD)   &lt;/li&gt;
&lt;li&gt;Matlab based 3d images slicing    &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Game script parser：&lt;/h3&gt;
&lt;p&gt;Discription：An parser which can convert a DSL(Domain Specific Language) to  Ren&amp;apos;py scripts, the DSL was designed by myself.&lt;br /&gt;
Size: Normal &lt;br /&gt;
Role：Work individually&lt;br /&gt;
Progress：Basically finished&lt;br /&gt;
Used skills:  &lt;br /&gt;
Python, Ren&amp;apos;py&lt;br /&gt;
Source: &lt;a href=&amp;quot;https://github.com/dtysky/Gal2Renpy&amp;quot;&gt;Gal2Renpy&lt;/a&gt; &lt;br /&gt;
Completed work：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Learning ren’py(a game engine) and python  &lt;/li&gt;
&lt;li&gt;Designing a DSL(Domain Specific Language), completed the source parsing and mapping with ren’py script, then migrating the User Interface, it has good expandability  &lt;/li&gt;
&lt;li&gt;A plug-in for sublime-text, improving the input efficiency  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;FPGA Imaging Library：&lt;/h3&gt;
&lt;p&gt;Discription：An indie game。&lt;br /&gt;
Size: Large &lt;br /&gt;
Role：Work individually&lt;br /&gt;
Progress：Building&lt;br /&gt;
Used skills:  &lt;br /&gt;
FPGA, Verilog, Systemverilog, Python, Image processing&lt;br /&gt;
Source: &lt;a href=&amp;quot;https://github.com/dtysky/FPGA-Imaging-Library&amp;quot;&gt;FPGA-Imaging-Library&lt;/a&gt;   , and will be published here: &lt;a href=&amp;quot;fil.dtysky.moe&amp;quot;&gt;fil.dtysky.moe&lt;/a&gt; &lt;br /&gt;
Completed work：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Graying, Threshold, Enhancement  &lt;/li&gt;
&lt;li&gt;Character output (Paused)  &lt;/li&gt;
&lt;li&gt;Frame controller, Rows generator, Window generator  &lt;/li&gt;
&lt;li&gt;Mean fitter, Rank fitter, Erosion, Dilation, Template matching, Auto threshold  &lt;/li&gt;
&lt;li&gt;Harris  &lt;/li&gt;
&lt;li&gt;Pan, Scale, Mirror, Crop, Roate, Shear, Affine transform  &lt;/li&gt;
&lt;li&gt;More...&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;My Homepage：&lt;/h3&gt;
&lt;p&gt;Discription：A blog for recording my learning experience&lt;br /&gt;
Size: Small &lt;br /&gt;
Role：Work individually&lt;br /&gt;
Progress：Done&lt;br /&gt;
Used skills:  &lt;br /&gt;
Writing, Python, Ren&amp;apos;py&lt;br /&gt;
Source: Closed source &lt;br /&gt;
Completed work：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Learning about pelican(Based on jinjia2), css, html, js, bulit on VPS。  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;A sample MIPS CPU：&lt;/h3&gt;
&lt;p&gt;Discription：A sample single cycle MIPS CPU, designed with verilogHDL, bulit on xilinx vivado, for XUP(Xilinx uinversity project) &lt;br /&gt;
Size: Small &lt;br /&gt;
Role：Work individually&lt;br /&gt;
Progress：Done&lt;br /&gt;
Used skills:  &lt;br /&gt;
FPGA, Verilog, Systemverilog, Python&lt;br /&gt;
Source: &lt;a href=&amp;quot;https://github.com/dtysky/SIMPLE_MIPS_CPU&amp;quot;&gt;SIMPLE_MIPS_CPU&lt;/a&gt; &lt;br /&gt;
Completed work： &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Designed and tested ALU, REGFILE, CONTRLO_UNIT, DATAPATH, INST_MEM, DATA_MEM modules, and connected them for a CPU which is used for functional simulation    &lt;/li&gt;
&lt;li&gt;Designed and tested KEY2INST, SHOW_ON_LED modules, and connected them for a CPU which is used for testing on board      &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h1&gt;Education&lt;/h1&gt;
&lt;p&gt;2011.08 - Now &lt;br /&gt;
Bachelor Southeast University(China), Instrument Science And Engineering College – Control Technology And Instruments  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Record&lt;/h2&gt;
&lt;p&gt;2014, HackShanghai No.3 (OpenTTT, Member)&lt;br /&gt;
2014, 36kr Nanjing hachathon No.4 (Stars, Leader) &lt;br /&gt;
2014.10 - Now, Xilinx R&amp;amp;D Intern&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;Hobby&lt;/h1&gt;
&lt;p&gt;Reading, Music, Writing。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Tuesday, 28 Apr 2015 10:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.04.28 10:00:article/Create-MyResumeEn</guid>
<category>Resume</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>白箱完结感想</title>
<link>http://dtysky.moe/article/Art-2015_03_27_a</link>
<description>&lt;p&gt;白箱终于完结，这也是近年来少有的我追到最后的片子，自从告别废萌和纯粹中二后...毫无疑问这是一部亮点非常多的片子，人物众多但并没有因此而造成人物形象的重叠、存在感的淡化等问题，同时加之是业界来描绘业界自身，让其在内容的充实和真实性上都有着良好的保证，叙事也是张弛有度，除了极少数场景可能都合的有些严重逻辑也没有问题，当然使用良好的都合也可以大幅增加表现力和感染力，这个并不是什么缺点。&lt;br /&gt;
本片对于我而言有两重的意义，第一是这部片子质量的确很不错，第二可能是在于共鸣方面吧，当然只要工作过一段时间的人都会有些感想，不过本人而言更多是在梦想方面的。。。能够坚持下去，并且坚持下去就能化解危机出应有的成果这个的确是太诱人了，也无怪有人说这是“业界的新闻联播”来冷嘲热讽，但这其实有一个问题，就像是动画内某蘑菇的替身所言“照搬现实，一点都不有趣”，要照搬现实的话去看纪录片好了，又何苦那这个来指责一部动画？麦基大叔也在《故事》中提到过这一点，观众并不关心日常无聊烦闷的状态，关心的只是戏剧性、冲击力、有所起伏的能够引起移情和共情，从而进一步引发思考的东西，艺术是一种重现和提纯，他所关注的是表象后面的那些东西，任何一个人物都是一类人的缩影。当然，也有一些反主流的作品逆而行之，不过那是有着那样明确的目的，有着它们自己的定位。大家追求不同，表现不同，仅此而已。    &lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;本片的主要角色当然是开头那五个在高中就做出来了一部DEEMO的少女，她们的定位分别是：&lt;br /&gt;
制作进行，原画，CV，剧本，CG。&lt;br /&gt;
第一主角的职位是制作进行，也是入门门槛最低、最没有传统意义上的“技能”的一个，但却是接触面最广的一个职位，其他职位主要都是往深里挖，而这个职位不同，她是广度上的扩展，所做的是人际脉络的连接，选择这样的一个职位作为主视角是很机智的，因为这样才能从最大的层面描绘这个行业的各个方面。&lt;br /&gt;
很多人可能看不起搞人脉的，但从实际的角度去考虑，其实大家付出的都是差不多的，和人打交道本身就是一种技能，这和其他所谓的“真才实学”也没有什么区别，要付出的都是很多的，而且还要更加提醒吊胆，要知道人比起工具要不可靠多了，我写个代码写错了编译器给我抛回一个error我能确信，并根据他去debug，但人就不一定了，白箱中的人已经算很不错了，因为他们大多数都映射了PA制作人员的期望吧，或许PA的制作人员就是这样的也说不定wwww&lt;br /&gt;
本作的主题也很明显，这是架构在“动画制作”这个背景之上的、一个关于梦想和成长的故事，拥有梦想的少女们在工作后的种种改变、不得不面对的压力和强行的成长，诚然，主角们是幸运的，因为无时不刻都有善良的前辈（我也要金发美女前辈！）来对她们施以援手，但这对于本片无关紧要，和前面所言一致，实际上本片有意无意在回避一些非常现实的问题，或者说即使有这样的问题，也使用一些比较有趣的手法将其本身的痛苦掩盖了过去（白毛的那个算是一个特例），但在掩盖的时候仍然保留了一部分的紧张感，我认为这种艺术加工是很成功的，因为那些部分并没有必要表现出来，角色拥有问题的苦恼、尝试解决问题的努力和问题被解决的方案是一个情节构成的必要部分，其他部分需要根据作品的主题所构造语境做综合考虑，没有必要事无巨细。&lt;br /&gt;
当然，仅仅是拥有一些比较好的故事和主题并不能够让一部作品如此有吸引力，本作在表现上也相当不错，每个人物的塑造、环境的描写和十分用心的作画与演出都是关键，正是PA对于各方面的严格要求，才让我们有机会看到这样的一部作品，制作人、监督、哥特LOLI大人、原画们、演出们、包括CV和音响等等都描绘的相当不错，即使是只有寥寥数言的一些配角也让人映像深刻，能做到这点确实不容易。&lt;br /&gt;
具体就不展开了，本来想把最近大友、今敏先生、汤浅的那些都说一说，但实在比较忙（如果我也有本片那样的一堆助手我也要爆鸡血啊啊啊。。。可惜么，这就是一个无聊的现实&lt;br /&gt;
全家福www，期待第二季：&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/1_zpsvbv2s88d.jpg&amp;quot; /&gt;  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Friday, 27 Mar 2015 00:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.03.27 00:00:article/Art-2015_03_27_a</guid>
<category>白箱</category>
<category>动画</category>
</item>

<item>
<title>体三维显示器-FPGA控制部分</title>
<link>http://dtysky.moe/article/Create-2015_03_22_c</link>
<description>&lt;p&gt;此部分代码在：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/3D_Displayer_Controller/tree/master/VHDL_PLANB&amp;quot;&gt;https://github.com/dtysky/3D_Displayer_Controller/tree/master/VHDL_PLANB&lt;/a&gt;
&lt;br&gt;
体三维显示器为真三维显示的一种，即可以在真是的空间内显示一个三维的图像，图像占据一个真实的三维空间，不需要借助任何辅助视觉器件，我采用的方案是二维LED点阵的旋转，LED点阵为自行设计，最后是FPGA的控制部分。&lt;br /&gt;
名为PLANB，顾名思义，这其实是一个妥协的方案，原本方案是USB（cy68013）+DDR2，本来也实现的差不多了，但一开始忽略的某个问题直接延续到了最后，嘛，usb那块的教程以前写过了:&lt;br /&gt;
&lt;a href=&amp;quot;http://dtysky.github.io/tag/68013.html&amp;quot;&gt;http://dtysky.github.io/tag/68013.html&lt;/a&gt;&lt;br /&gt;
至于DDR2控制器的编写教程和仿真，有时间再说吧。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1.LED阵列控制&lt;/h2&gt;
&lt;p&gt;LED阵列控制需要配合LED阵列的电路设计，也就是buffer到trigger的模型，同时还要考虑到由于电路板不完美造成的噪声等等。
&lt;br&gt;
&lt;strong&gt;1.扫描流程:&lt;/strong&gt;  &lt;br /&gt;
配合LED阵列电路的设计，刷新需要按照以下流程进行：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;扫描&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-03-22c/1.png&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;&lt;br&gt;
&lt;strong&gt;2.状态机：&lt;/strong&gt;&lt;br /&gt;
根据扫描流程和整体方案设计状态机,由于没有什么特别的需求，所以只设计以下几种状态：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;rst：复位&lt;br /&gt;
free：IDLE状态，初始化或者复位后的状态&lt;br /&gt;
work：工作状态  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;初始化时进行复位，复位结束跳转free状态，在free状态时如果接收到&lt;strong&gt;begin&lt;/strong&gt;信号则跳转到work状态，开始工作。  &lt;/p&gt;
&lt;p&gt;&lt;br&gt;
&lt;strong&gt;3.扫描注意事项：&lt;/strong&gt;&lt;br /&gt;
扫描时，由于电路设计不完美所以可能会出现串扰现象，也就是当某一行有效时可能会让其他行同时有效，行和列之间也可能会出现这样的现象，为了避免这样的情况发生，需要采取一些软件上的措施，我采用的方案是在每一次进行列变换前先对前一行进行消隐，由于行有效是在某个信号上升沿的时候送出（trigger的时钟信号），所以在正常显示时下保证此信号为高电平，在切换时或者消隐时在进行一下High-&amp;gt;Low-&amp;gt;High的变换，构造一个上升沿，经测验，该方案有着很大的效果，噪声大大减小。  &lt;/p&gt;
&lt;p&gt;LED控制部分的代码在这里：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/3D_Displayer_Controller/blob/master/VHDL_PLANB/LED/LED.vhd&amp;quot;&gt;https://github.com/dtysky/3D_Displayer_Controller/blob/master/VHDL_PLANB/LED/LED.vhd&lt;/a&gt;  &lt;/p&gt;
&lt;h2&gt;2.图像解码&lt;/h2&gt;
&lt;p&gt;图像解码，也就是对三维切片编码后的每一帧压缩图像进行解码，由上一篇文章：  &lt;br /&gt;
&lt;a href=&amp;quot;http://dtysky.github.io/articles/ti-san-wei-xian-shi-qi-matlabde-san-wei-qie-pian.html&amp;quot;&gt;体三维显示器-Matlab的三维切片&lt;/a&gt;&lt;br /&gt;
可知，每一帧图像存的实际上是发亮点的坐标值，这就需要一个解码单元来这样的数据进行解码，转换成可以LED可以显示的位图。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;1.解码流程：&lt;/strong&gt;
解码基本流程如下：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;解码&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-03-22c/2.png&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.状态机：&lt;/strong&gt;  &lt;/p&gt;
&lt;p&gt;整体流程可以分为以下状态：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;read_next：读下一个坐标点，转入judge状态&lt;br /&gt;
judge：判断当前点坐标是否与上一个坐标的纵坐标相等，如果是转入set状态，否则转入do_write状态&lt;br /&gt;
do_write：将上一行写入fifo，同时跳转到clear状态&lt;br /&gt;
set：将当前行的x置1&lt;br /&gt;
clear：对当前行，当前x坐标前的所有点写0  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;如此，便可以完成解码。&lt;br /&gt;
代码在这里：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/3D_Displayer_Controller/blob/master/VHDL_PLANB/DECODER/DECODER.vhd&amp;quot;&gt;https://github.com/dtysky/3D_Displayer_Controller/blob/master/VHDL_PLANB/DECODER/DECODER.vhd&lt;/a&gt;  &lt;/p&gt;
&lt;h2&gt;3.整体&lt;/h2&gt;
&lt;p&gt;整体，即把解码单元和扫描单元连接起来，这里采用的是双缓存模式，也就是说：  &lt;/p&gt;
&lt;p&gt;实例化fifoA和fifoB两个fifo，当A填充完成后开始扫描，A在提供扫描数据时B填充数据，B扫面时A填充，这样可以保证总是有数据输出，这其实是一个宏观上的流水线：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;整体&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-03-22c/3.png&amp;quot; /&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>Sunday, 22 Mar 2015 19:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.03.22 19:00:article/Create-2015_03_22_c</guid>
<category>体三维</category>
<category>LED阵列</category>
<category>FPGA</category>
<category>VHDL</category>
</item>

<item>
<title>体三维显示器-机械部分</title>
<link>http://dtysky.moe/article/Create-2015_03_22_a</link>
<description>&lt;p&gt;&lt;strong&gt;体三维显示器的机械工程：&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/3D_Displayer_Machine&amp;quot;&gt;https://github.com/dtysky/3D_Displayer_Machine&lt;/a&gt;&lt;br /&gt;
&lt;br&gt;
体三维显示器为真三维显示的一种，即可以在真是的空间内显示一个三维的图像，图像占据一个真实的三维空间，不需要借助任何辅助视觉器件，我采用的方案是二维LED点阵的旋转，LED点阵为自行设计，这里记录了如何设计一个机械结构让LED阵列旋转起来。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1.设计&lt;/h2&gt;
&lt;p&gt;机械部分分为底座、联轴器、电刷以及PCB支架四个部分，同时还涉及电机的选型。
&lt;br&gt;
&lt;strong&gt;1.电机:&lt;/strong&gt;  &lt;br /&gt;
电机主要要考虑的是负载问题，本装置轴向负载大约为2KG，大约10cm的旋转半径，考虑到成本和够用就行的理念，最终选择了一个120W的直流电机作为驱动源。  &lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;2.底座：&lt;/strong&gt;&lt;br /&gt;
底座，也就是放置电机的机械结构，考虑到一般的钢板物理属性（5mm厚度)，如果将电机的底部触地，直接放在底座之上，肯定会导致不稳妥的情况出现，所以这里选择了使用拱形吊桥结构，也就是将电机吊在底座上面，以此稳底座的下盘。后来出于需求，又在底座下面加了一个大钢板，进一步稳定。  &lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;3.支架：&lt;/strong&gt;&lt;br /&gt;
支架的作用是稳固LED阵列，给其提供支撑，基本设计是一个L型的折弯，由于面积比较大，如果直接上的话会有相当的重量，导致负载过大，所以对其实行了挖空工艺，将主要版面的中间部分挖空来减轻重量。&lt;br /&gt;
支架的主要部分是为了配合LED的螺丝孔固定阵列，下面的部分则是为了和联轴器进行连接。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;4.联轴器：&lt;/strong&gt;&lt;br /&gt;
顾名思义，连接电机的轴和PCB支架，这个的设计主要考虑到电机轴的构造。联轴器分为两部分，上面是和支架连接，下面则是和电机的轴连接，和支架连接的部分只需要根据支架底部的孔钻孔即可，和轴连接的部分也需要打几个孔，不过是为了和轴做紧固连接，这个轴属于一侧削平靠摩擦力禁锢的那种，所以可以用逻辑拧紧进行固定。&lt;br /&gt;
联轴器上下两个部分的连接可以使用螺丝进行，没什么特别的。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;5.电刷：&lt;/strong&gt;&lt;br /&gt;
为了解决旋转供电问题，解决这个问题一般有电刷和无限供电两种方式，考虑到风险和电流问题，最终选择了电刷的方案。在联轴器上面放一个集电环，然后通过自己设计的电刷与集电环的连接来供电，集电环是铜质材料，电刷是石墨，实测效果良好。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2.生产&lt;/h2&gt;
&lt;p&gt;大学好的一点就是工培中心，以下皆在工培中心和学校工厂加工。&lt;br /&gt;
&lt;br&gt;
&lt;br&gt;
&lt;strong&gt;1.电机:&lt;/strong&gt;  &lt;br /&gt;
购买所得：  &lt;br /&gt;
&lt;img alt=&amp;quot;电机&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-03-22a/0.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;2.底座：&lt;/strong&gt;&lt;br /&gt;
注意各个部分的尺寸都必须到位！  &lt;br /&gt;
&lt;img alt=&amp;quot;底座-CAD&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-03-22a/11.png&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;底座-实物&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-03-22a/12.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;3.支架：&lt;/strong&gt;&lt;br /&gt;
挖空由于并不要求十分精确，所以采用了很一般的工艺，没有必要上线切割：  &lt;br /&gt;
&lt;img alt=&amp;quot;支架-CAD&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-03-22a/21.png&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;支架-实物&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-03-22a/22.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;4.联轴器：&lt;/strong&gt;&lt;br /&gt;
分成两个部分！CAD图纸和最后成品有区别：  &lt;br /&gt;
&lt;img alt=&amp;quot;联轴器-CAD&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-03-22a/31.png&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;联轴器-实物&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-03-22a/32.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;5.电刷：&lt;/strong&gt;&lt;br /&gt;
可调节式设计：&lt;br /&gt;
&lt;img alt=&amp;quot;电刷-CAD&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-03-22a/41.png&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;电刷-实物&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-03-22a/42.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3.合体&lt;/h2&gt;
&lt;p&gt;机械部分总共花费约800，整体良好，整体如下：&lt;br /&gt;
&lt;img alt=&amp;quot;合体&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-03-22a/5.jpg&amp;quot; /&gt;&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Sunday, 22 Mar 2015 14:10:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.03.22 14:10:article/Create-2015_03_22_a</guid>
<category>体三维</category>
<category>机械</category>
</item>

<item>
<title>体三维显示器-Matlab的三维切片</title>
<link>http://dtysky.moe/article/Create-2015_03_22_b</link>
<description>&lt;p&gt;&lt;br&gt;
体三维显示器为真三维显示的一种，即可以在真是的空间内显示一个三维的图像，图像占据一个真实的三维空间，不需要借助任何辅助视觉器件，我采用的方案是二维LED点阵的旋转，LED点阵为自行设计，这里记录了三维切片方面的东西。&lt;br /&gt;
老实说这只是一个无奈的方案，因为各种各样的原因吧，比如时间和优先级之类的，现在只能切规则几何体，并且要手动输入坐标对。&lt;br /&gt;
同时由于没有使用矩阵运算，所以切片会很慢。。。嘛，暂时就这样了，基本所有切片算法的思路都是这样的。  &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;/p&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;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;zeros&lt;/span&gt;&lt;span class=&amp;quot;p&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;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;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;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;mf&amp;quot;&gt;1.6079&lt;/span&gt;&lt;span class=&amp;quot;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.4078&lt;/span&gt;&lt;span class=&amp;quot;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.4982&lt;/span&gt;&lt;span class=&amp;quot;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.4690&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.0140&lt;/span&gt;&lt;span class=&amp;quot;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.3235&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;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;mf&amp;quot;&gt;0.4690&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.0140&lt;/span&gt;&lt;span class=&amp;quot;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.3235&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.0920&lt;/span&gt;&lt;span class=&amp;quot;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.2363&lt;/span&gt;&lt;span class=&amp;quot;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.3235&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;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;mf&amp;quot;&gt;0.0470&lt;/span&gt;&lt;span class=&amp;quot;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.6582&lt;/span&gt;&lt;span class=&amp;quot;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.4982&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.0920&lt;/span&gt;&lt;span class=&amp;quot;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.2363&lt;/span&gt;&lt;span class=&amp;quot;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.3235&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;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;mf&amp;quot;&gt;0.0470&lt;/span&gt;&lt;span class=&amp;quot;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.6582&lt;/span&gt;&lt;span class=&amp;quot;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.4982&lt;/span&gt;&lt;span class=&amp;quot;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.6079&lt;/span&gt;&lt;span class=&amp;quot;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.4078&lt;/span&gt;&lt;span class=&amp;quot;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.4982&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;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;mf&amp;quot;&gt;1.6079&lt;/span&gt;&lt;span class=&amp;quot;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.4078&lt;/span&gt;&lt;span class=&amp;quot;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.4982&lt;/span&gt;&lt;span class=&amp;quot;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.0920&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.2363&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.3235&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;6&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.0470&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.6582&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.4982&lt;/span&gt;&lt;span class=&amp;quot;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.4690&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.0140&lt;/span&gt;&lt;span class=&amp;quot;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.3235&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;7&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.0920&lt;/span&gt;&lt;span class=&amp;quot;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.2363&lt;/span&gt;&lt;span class=&amp;quot;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.3235&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.6079&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.4078&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.4982&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;8&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.4690&lt;/span&gt;&lt;span class=&amp;quot;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.0140&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.3235&lt;/span&gt;&lt;span class=&amp;quot;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.0470&lt;/span&gt;&lt;span class=&amp;quot;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.6582&lt;/span&gt;&lt;span class=&amp;quot;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.4982&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;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;mf&amp;quot;&gt;1.0920&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.2363&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.3235&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.0470&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.6582&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.4982&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;10&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.6079&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.4078&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.4982&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.0470&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.6582&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.4982&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;11&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.4690&lt;/span&gt;&lt;span class=&amp;quot;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.0140&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.3235&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.6079&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.4078&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.4982&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;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;mf&amp;quot;&gt;1.0920&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.2363&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.3235&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.4690&lt;/span&gt;&lt;span class=&amp;quot;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.0140&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.3235&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;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;n&amp;quot;&gt;tmpx&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;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;tmpy&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;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;tmpz&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;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;n&amp;quot;&gt;tmpx2&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;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;tmpy2&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;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;tmpz2&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;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;xmax&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;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;tmpx&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;tmpx2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]);&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;xmin&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;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;tmpx&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;n&amp;quot;&gt;tmpx2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]);&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;ymax&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;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;tmpx&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;tmpx2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]);&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;ymin&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;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;tmpy&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;n&amp;quot;&gt;tmpy2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]);&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;zmax&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;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;tmpz&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;tmpz2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]);&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;zmin&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;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;tmpz&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;n&amp;quot;&gt;tmpz2&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.构造边的方程组：&lt;/strong&gt;&lt;br /&gt;
由于任意一条空间直线的方程都可以表现为：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;a11*X + a12*y + a13*0 = a14&lt;br /&gt;
a21*x + a22*0 + a23*z = a24   &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这样的形式的方程组，所以对于每一个边，需要算6个系数：  &lt;/p&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;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;zeros&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&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;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;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;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;for&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;i&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;nb&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;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;x1&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;nb&amp;quot;&gt;i&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;x2&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;nb&amp;quot;&gt;i&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;y1&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;nb&amp;quot;&gt;i&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;y2&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;nb&amp;quot;&gt;i&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;z1&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;nb&amp;quot;&gt;i&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;n&amp;quot;&gt;z2&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;nb&amp;quot;&gt;i&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;c&amp;quot;&gt;%x,y&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;nb&amp;quot;&gt;i&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;y2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y1&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;nb&amp;quot;&gt;i&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;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;x2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x1&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;nb&amp;quot;&gt;i&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;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;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;i&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;span class=&amp;quot;n&amp;quot;&gt;x2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x1&lt;/span&gt;&lt;span class=&amp;quot;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;y1&lt;/span&gt;&lt;span class=&amp;quot;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;y2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y1&lt;/span&gt;&lt;span class=&amp;quot;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;x1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;c&amp;quot;&gt;%y,z&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;nb&amp;quot;&gt;i&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;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;nb&amp;quot;&gt;i&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;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;z2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z1&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;nb&amp;quot;&gt;i&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;y2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y1&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;nb&amp;quot;&gt;i&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;z2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z1&lt;/span&gt;&lt;span class=&amp;quot;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;y1&lt;/span&gt;&lt;span class=&amp;quot;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;y2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y1&lt;/span&gt;&lt;span class=&amp;quot;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;z1&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;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;br&gt;
&lt;strong&gt;4.构造切面并切片：&lt;/strong&gt;&lt;br /&gt;
由于本设备是按绕中轴旋转，所以构造的切面应当符合“每旋转i度”构造一次的规则，本次设置为没旋转一度切一次，总共360切片，由于旋转过程中出现的对称现象，所以实际上只需要切一半（180度）即可：  &lt;/p&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;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;ii&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;nb&amp;quot;&gt;i&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;180&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;nb&amp;quot;&gt;tan&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;deg2rad&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&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;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;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;j&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;nb&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;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;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;f1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;strcat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;num2str&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;nb&amp;quot;&gt;j&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;s&amp;quot;&gt;&amp;#39;*x+&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;num2str&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;nb&amp;quot;&gt;j&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;s&amp;quot;&gt;&amp;#39;*y+&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;num2str&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;nb&amp;quot;&gt;j&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;s&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;num2str&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;nb&amp;quot;&gt;j&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;span class=&amp;quot;n&amp;quot;&gt;f2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;strcat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;num2str&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;nb&amp;quot;&gt;j&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;s&amp;quot;&gt;&amp;#39;*x+&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;num2str&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;nb&amp;quot;&gt;j&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;s&amp;quot;&gt;&amp;#39;*y+&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;num2str&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;nb&amp;quot;&gt;j&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;s&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;num2str&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;nb&amp;quot;&gt;j&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;f3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;strcat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;num2str&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;nb&amp;quot;&gt;j&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;1&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;#39;*x+&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;num2str&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;nb&amp;quot;&gt;j&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;s&amp;quot;&gt;&amp;#39;*y+&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;num2str&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;nb&amp;quot;&gt;j&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;3&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;#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;num2str&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;nb&amp;quot;&gt;j&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;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;f4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;strcat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;num2str&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;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;s&amp;quot;&gt;&amp;#39;*x+&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;num2str&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;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;s&amp;quot;&gt;&amp;#39;*y+&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;num2str&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;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;s&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;num2str&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;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;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;n&amp;quot;&gt;solve&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;f1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;f2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;f4&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;#39;x&amp;#39;&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;#39;y&amp;#39;&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;#39;z&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;o&amp;quot;&gt;~&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;isempty&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;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;~&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;isempty&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;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;~&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;isempty&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;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;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xmin&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&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;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xmax&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;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;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ymin&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&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;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ymax&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;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;&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;zmin&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&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;&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;zmax&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;ii&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;p&amp;quot;&gt;];&lt;/span&gt;
                &lt;span class=&amp;quot;n&amp;quot;&gt;ii&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ii&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;end&lt;/span&gt; 
    &lt;span class=&amp;quot;k&amp;quot;&gt;end&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;ii&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;mi&amp;quot;&gt;0&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;ii&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ii&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;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;br&gt;
&lt;strong&gt;5.平面坐标映射：&lt;/strong&gt;&lt;br /&gt;
这个比较容易理解，由于切得的坐标是空间坐标，而在LED显示的是平面坐标，所以要进行一下坐标映射，基本思路就是保留空间矢量的Z分量，将其作为新平面矢量的Y分量，而后取空间点相对于中轴的距离作为新的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;n&amp;quot;&gt;tymax&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;nb&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;zmax&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;&lt;span class=&amp;quot;nb&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;zmin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;res2&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;nb&amp;quot;&gt;i&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;nb&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;res&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;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&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;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;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;nb&amp;quot;&gt;i&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;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;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;nb&amp;quot;&gt;i&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;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;n&amp;quot;&gt;res2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&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;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;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;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&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;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;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&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;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;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&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;tx&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;nb&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;x&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;n&amp;quot;&gt;y&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;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;mi&amp;quot;&gt;0&lt;/span&gt;
            &lt;span class=&amp;quot;n&amp;quot;&gt;res2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&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;tx&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;res2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&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;tx&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;n&amp;quot;&gt;ty&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;res2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&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;zmax&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ty&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;/pre&gt;&lt;/div&gt;


&lt;p&gt;注意，这里同时进行了Y轴坐标系的变换。&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;len&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;nb&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;res2&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;nb&amp;quot;&gt;i&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;nb&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;res2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;res2&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;nb&amp;quot;&gt;i&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;res2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&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;res2&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;nb&amp;quot;&gt;i&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;res2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&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;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;br&gt;
&lt;strong&gt;6.坐标系变换和去归一化：&lt;/strong&gt;&lt;br /&gt;
这个没什么好说的，根据自己实际的情况选取参数，对切片结果进行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;n&amp;quot;&gt;resfin&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;nb&amp;quot;&gt;i&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;nb&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;res2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;resfin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,:)=&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;round&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;res2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&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;mi&amp;quot;&gt;36&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;resfin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&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;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;n&amp;quot;&gt;resfin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&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;0&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;resfin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&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;resfin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&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;59&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;/pre&gt;&lt;/div&gt;


&lt;hr /&gt;
&lt;h2&gt;2.结果&lt;/h2&gt;
&lt;p&gt;以下是上面例子的输出，由于上面所言的边界问题有些瑕疵：&lt;br /&gt;
&lt;img alt=&amp;quot;图解&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-03-22b/1.png&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;完整代码看这里：  &lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://github.com/dtysky/3D_Displayer_Controller/blob/master/VHDL_PLANB/DECODER/CreatMif/test.m&amp;quot;&gt;https://github.com/dtysky/3D_Displayer_Controller/blob/master/VHDL_PLANB/DECODER/CreatMif/test.m&lt;/a&gt;&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Sunday, 22 Mar 2015 13:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.03.22 13:00:article/Create-2015_03_22_b</guid>
<category>体三维</category>
<category>切片</category>
<category>Matlab</category>
</item>

<item>
<title>错误修正</title>
<link>http://dtysky.moe/article/Life-2015_03_06_a</link>
<description>&lt;p&gt;一件事情本身没有对错之分，但是如果和其他事情杂糅到一起，综合，就会产生一些对错的效应，然而这些效应并不是静态的，而是会随着事件的发展而呈现出纷繁复杂的变化，当变化到达了一定的程度，发生了正确向错误的转化时，就应该当机立断进行转向，否则只会越陷越深。这个问题直接暴露出了我性格中最大的弱点，感谢某位的指引，虽然预期很过火但都是实话，也让我醒悟了过来，还有另一位朋友的话说的没错——“Dream when the day is thru, Dream and they might come true, Things never are as bad as they seem, So dream, dream, dream.”&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;现状分析&lt;/h2&gt;
&lt;p&gt;现状是比较残酷的，分为几个方面。&lt;br /&gt;
&lt;strong&gt;1.原创游戏：&lt;/strong&gt;&lt;br /&gt;
不错，这个游戏到现在花费了我两年，已经完成的部分是70W的剧本，1W左右的设定以及60%的素材设计，还有90%程序架构。&lt;br /&gt;
我也一度为我所完成的“壮举”而洋洋自得，的确，我是考虑到了很多将游戏本身做出来的要素，比如更加理性化得设想分工、投资等等，但我却忽略了一个很基本的问题——我并不是天才，一部真正优秀的作品对于一个非天才而言，是不可能一次成功的，这一点首先应该印证在剧本上。&lt;br /&gt;
按照MBTI而言，我算是一个INFP或者ISFP，也就是说我的基本性格是拥有强烈的信念和及其严格的理想，但在行动方面却有所不及，或者说，我本身讨厌耗散这一种行为（这一点是非常讽刺的，因为我平时是以“耗散”作为自己的人生信条的），和耗散相反，我是一个节能主义者，而且并不是为了效率去节能，而是单纯的拒绝行动。&lt;br /&gt;
的确70W字的剧本和期间的努力似乎在表现着我是一个果断的行动派，但这其实是一个对自我的误解，70W的创作过程中其实没有什么太大需要设计的地方，基本只是随性而写，各个角色的行为并不围绕同一个核心主题、或者说很牵强，表现力远远不达标，有的角色甚至对主线没有任何的帮助。&lt;br /&gt;
说实话，对于这一切在70W完稿后的两个月内我就已然觉察到了，但是我的性格和周围的人群对我的态度（不敢反驳）让我有一个美好的幻想——脉络是没有问题的，只需要在原先的基础上改一改就可以了，于是，我将一个本来应该真诚面对的残酷事实美化成了一个可以坦然接受的乌托邦，并自顾自得地沉浸在其中——看呐，这是我的天才，那些无法理解的人就滚他们的吧，那是他们智商不够。&lt;br /&gt;
观众真的是愚蠢的吗？他们真的无法接受真理吗？
诚然，这种人是有的，但我对其比例进行的自我的调整，让它一边在符合我的预期的同时，发生了相对于真实的失调，尤其是在读完朋友推荐的《基础》和《故事》之后，这种感觉越来越强烈。&lt;br /&gt;
很多事情并不是因为观众如何如何，而是因为——现在我是无能的。&lt;br /&gt;
尚且没有做出任何一部“经典”形式的东西的我，怎么可能做得好一部“反经典”的东西呢？&lt;br /&gt;
一部作品的背后绝不是鸟语花香的天堂，而是血汗工厂。&lt;br /&gt;
70W很了不起吗？对于成功者而言那只不过是小小的必经之路而已，一部没有灵魂和血肉的70W，注定了只是一块垫脚石而已，我必须要清醒地认识到这一点。&lt;br /&gt;
所以我必须做出抉择，选择去保留哪些东西，推倒哪些东西，所幸原先的角色和基本故事大部分还是可以保留的，个人情节还是拥有一定的张力，只要进行修改就可以，虽然......&lt;br /&gt;
目前为止原先的剧本被完全舍弃，人物删掉了一个，三个大幅修正，设定保留了大概四分之一，主要角色有两个降格。&lt;br /&gt;
主线剧情重新设计，结构采用“秽翼のユースティア”类型的结构，改单一结局结构为主干分支结构。&lt;br /&gt;
也就是说，完全推倒重来。&lt;br /&gt;
庆幸的是，程序部分没有问题（其实已经被重构过，也就是推倒重来过一边了）。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;2.三维显示器：&lt;/strong&gt;&lt;br /&gt;
这个东西我做的是比较果断，现在最大的问题在于是否还要继续完成（机械部分再次进化，软件部分的调整）。&lt;br /&gt;
按照现在的程度而言，暂时算了。&lt;br /&gt;
现在结题是没什么问题，主要是时间。&lt;br /&gt;
我需要纠正我以为我能够一心多用的天真性格，这个东西优先级往后压，以后再说。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;3.Blog：&lt;/strong&gt;&lt;br /&gt;
本来是准备把以前的经验在短期全部发出来，比如PCB制作教程，显示器构造心得，解析器编成指南之类的，但现在还有更重要的事情去做。&lt;br /&gt;
先暂时放着吧，没有办法。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;4.毕设：&lt;/strong&gt;&lt;br /&gt;
第一优先的东西。&lt;br /&gt;
本来想着比较宏大，现在想想先实现再说。&lt;br /&gt;
棋盘识别（FPGA）+分支预测（软件，先用现成的）。&lt;br /&gt;
一步一步来吧。  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Friday, 06 Mar 2015 18:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.03.06 18:00:article/Life-2015_03_06_a</guid>
<category>日常</category>
<category>创造</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>游戏人物文字设定</title>
<link>http://dtysky.moe/article/Create-2015_02_21_a</link>
<description>&lt;p&gt;这里记录了原创游戏人物的原始设定，方便日后交流，由于是人设，所以暂且不包括表情和姿势（和画师详谈），服装统一为第一服装（大多是校服）。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;我方主要角色&lt;/h1&gt;
&lt;h2&gt;戴寒苍/戴天宇&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
虚无/耗散&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：18岁（现实）&lt;br /&gt;
性格：吐槽别扭受&lt;br /&gt;
服装：校服&lt;br /&gt;
眼色：黑&lt;br /&gt;
发色：银带黑&lt;br /&gt;
发型：待定&lt;br /&gt;
身高：177cm&lt;br /&gt;
肤色：正常色&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式1：&lt;/strong&gt;&lt;br /&gt;
年龄：10岁（“梦”）&lt;br /&gt;
性格：沉着&lt;br /&gt;
服装：特殊—小（类似西服）&lt;br /&gt;
眼色：黑&lt;br /&gt;
发色：银带黑&lt;br /&gt;
发型：待定&lt;br /&gt;
身高：150cm&lt;br /&gt;
肤色：正常色&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Meteor/命月晗樱&lt;/h2&gt;
&lt;p&gt;&lt;img src=&amp;quot;/theme/image/fw/char-soul.jpg&amp;quot; style=&amp;quot;position:absolute;right:0px;z-index:-1;&amp;quot;&gt;&lt;/img&gt;
&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
谎言/真实&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：18岁（现实）&lt;br /&gt;
性格：冰山毒舌&lt;br /&gt;
服装：校服&lt;br /&gt;
眼色：深红&lt;br /&gt;
发色：银&lt;br /&gt;
发型：双马尾&lt;br /&gt;
身高：174cm&lt;br /&gt;
肤色：纯白&lt;br /&gt;
胸围：B-&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式1：&lt;/strong&gt;&lt;br /&gt;
年龄：10岁（“梦”）&lt;br /&gt;
性格：元气温柔&lt;br /&gt;
服装：特殊—小（白色连衣裙）&lt;br /&gt;
眼色：淡红&lt;br /&gt;
发色：银&lt;br /&gt;
发型：双马尾&lt;br /&gt;
身高：140cm&lt;br /&gt;
肤色：纯白&lt;br /&gt;
胸围：A--&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;苓苏.蒲兰特&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
泉&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：17岁（现实）&lt;br /&gt;
性格：天然（前）小恶魔（后）&lt;br /&gt;
服装：校服&lt;br /&gt;
眼色：淡绿&lt;br /&gt;
发色：浅绿&lt;br /&gt;
发型：单侧马尾&lt;br /&gt;
身高：168cm&lt;br /&gt;
肤色：白偏微黄&lt;br /&gt;
胸围：A&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式1：&lt;/strong&gt;&lt;br /&gt;
年龄：17岁（梦界）&lt;br /&gt;
性格：腹黑小恶魔&lt;br /&gt;
服装：哥特风&lt;br /&gt;
眼色：深绿&lt;br /&gt;
发色：深绿&lt;br /&gt;
发型：单侧马尾&lt;br /&gt;
身高：168cm&lt;br /&gt;
肤色：纯白&lt;br /&gt;
胸围：A&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;月雏末&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
幻想&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：18岁（现实）&lt;br /&gt;
性格：软妹弱气（前）正常（后）&lt;br /&gt;
服装：校服&lt;br /&gt;
眼色：浅粉&lt;br /&gt;
发色：粉&lt;br /&gt;
发型：散短发&lt;br /&gt;
身高：166cm&lt;br /&gt;
肤色：白偏微黄&lt;br /&gt;
胸围：F（外观B）&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式1：&lt;/strong&gt;&lt;br /&gt;
年龄：18岁（梦界）&lt;br /&gt;
性格：冷漠强气&lt;br /&gt;
服装：童话骑士风&lt;br /&gt;
眼色：橙&lt;br /&gt;
发色：橙黄&lt;br /&gt;
发型：散短发&lt;br /&gt;
身高：163cm&lt;br /&gt;
肤色：白偏微黄&lt;br /&gt;
胸围：B&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;玖暮&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
理&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：18岁（现实）&lt;br /&gt;
性格：强气傲娇（前后）&lt;br /&gt;
服装：校服&lt;br /&gt;
眼色：淡紫&lt;br /&gt;
发色：紫&lt;br /&gt;
发型：单马尾&lt;br /&gt;
身高：178cm&lt;br /&gt;
肤色：白偏中黄&lt;br /&gt;
胸围：C&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式1：&lt;/strong&gt;&lt;br /&gt;
年龄：18岁（梦界）&lt;br /&gt;
性格：弱气/病娇&lt;br /&gt;
服装：魔女风&lt;br /&gt;
眼色：深紫&lt;br /&gt;
发色：火红&lt;br /&gt;
发型：单马尾&lt;br /&gt;
身高：179cm&lt;br /&gt;
肤色：苍白&lt;br /&gt;
胸围：C&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;恒晓&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
道&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：18岁（现实）&lt;br /&gt;
性格：伪装笨蛋（前）豪爽透彻（后）&lt;br /&gt;
服装：校服&lt;br /&gt;
眼色：黑&lt;br /&gt;
发色：苍蓝&lt;br /&gt;
发型：越帅越好&lt;br /&gt;
身高：181cm&lt;br /&gt;
肤色：一般&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式1：&lt;/strong&gt;&lt;br /&gt;
年龄：18岁（梦界）&lt;br /&gt;
性格：阴暗/冷静&lt;br /&gt;
服装：中国古代神话风&lt;br /&gt;
眼色：深蓝（戴眼镜）&lt;br /&gt;
发色：苍白&lt;br /&gt;
发型：越帅越好&lt;br /&gt;
身高：181cm&lt;br /&gt;
肤色：苍白&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;弗莱士.待暮.斯代达斯特&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
星空（未知）&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：18岁（现实）&lt;br /&gt;
性格：沉着温柔自我厌恶伪装（前）冷静温柔（后）&lt;br /&gt;
服装：校服&lt;br /&gt;
眼色：淡黄&lt;br /&gt;
发色：金&lt;br /&gt;
发型：贵族式正统&lt;br /&gt;
身高：178cm&lt;br /&gt;
肤色：白&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式1：&lt;/strong&gt;&lt;br /&gt;
年龄：18岁（梦界）&lt;br /&gt;
性格：疯狂/冷静&lt;br /&gt;
服装：正装&lt;br /&gt;
眼色：深黄&lt;br /&gt;
发色：金&lt;br /&gt;
发型：贵族式正统&lt;br /&gt;
身高：178cm&lt;br /&gt;
肤色：白&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;无名&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
空&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：22岁（立绘破损一）&lt;br /&gt;
性格：平凡&lt;br /&gt;
服装：无&lt;br /&gt;
完全纯色&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式1：&lt;/strong&gt;&lt;br /&gt;
年龄：22岁（立绘破损二）&lt;br /&gt;
性格：平凡&lt;br /&gt;
服装：&lt;br /&gt;
破损程度1&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式2：&lt;/strong&gt;&lt;br /&gt;
年龄：22岁（立绘破损三）&lt;br /&gt;
性格：神秘&lt;br /&gt;
服装：&lt;br /&gt;
破损程度2&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式3：&lt;/strong&gt;&lt;br /&gt;
年龄：22岁（完整）&lt;br /&gt;
性格：冷静&lt;br /&gt;
服装：校服&lt;br /&gt;
眼色：蓝&lt;br /&gt;
发色：白&lt;br /&gt;
发型：帅&lt;br /&gt;
身高：180cm&lt;br /&gt;
肤色：白&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;散夜&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
实感&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：黑影&lt;br /&gt;
性格：神秘&lt;br /&gt;
服装：无&lt;br /&gt;
无&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式1：&lt;/strong&gt;&lt;br /&gt;
年龄：外观12岁（灵魂）&lt;br /&gt;
性格：情感渴求（前）温柔（后）&lt;br /&gt;
服装：黑色连衣裙&lt;br /&gt;
眼色：黑带紫&lt;br /&gt;
发色：黑&lt;br /&gt;
发型：黑长直&lt;br /&gt;
身高：145cm&lt;br /&gt;
肤色：苍白&lt;br /&gt;
胸围：B&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;其他主要角色&lt;/h1&gt;
&lt;h2&gt;霍普.蒲兰特&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
执念&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：40岁&lt;br /&gt;
性格：完全理智&lt;br /&gt;
服装：正统西服&lt;br /&gt;
眼色：蓝&lt;br /&gt;
发色：白金&lt;br /&gt;
发型：待定&lt;br /&gt;
身高：180cm&lt;br /&gt;
肤色：白&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;莱特.休斯.斯代达斯特&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
奉献&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：外观9岁（灵魂）&lt;br /&gt;
性格：沉着温柔&lt;br /&gt;
服装：休闲西服&lt;br /&gt;
眼色：淡黄&lt;br /&gt;
发色：淡黄&lt;br /&gt;
发型：正统偏帅&lt;br /&gt;
身高：145cm&lt;br /&gt;
肤色：白&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;应龙&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
爱&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：外观18岁（异世界神）&lt;br /&gt;
性格：浩然之气&lt;br /&gt;
服装：神话服装&lt;br /&gt;
眼色：深蓝&lt;br /&gt;
发色：苍白&lt;br /&gt;
发型：越帅越好&lt;br /&gt;
身高：181cm&lt;br /&gt;
肤色：苍白&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;斯诺&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
荣耀&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：40岁&lt;br /&gt;
性格：死板强压&lt;br /&gt;
服装：正统西服&lt;br /&gt;
眼色：深黄&lt;br /&gt;
发色：金&lt;br /&gt;
发型：完全正统&lt;br /&gt;
身高：180cm&lt;br /&gt;
肤色：白&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Ghost&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
复仇&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：外观20岁（复仇集合体）&lt;br /&gt;
性格：仇恨戏谑&lt;br /&gt;
服装：待定&lt;br /&gt;
眼色：绿&lt;br /&gt;
发色：无（小丑面具）&lt;br /&gt;
发型：待定&lt;br /&gt;
身高：175cm&lt;br /&gt;
肤色：中黄&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;戴麟&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
彼方&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：外观30岁&lt;br /&gt;
性格：超越善恶&lt;br /&gt;
服装：休闲&lt;br /&gt;
眼色：黑&lt;br /&gt;
发色：黑&lt;br /&gt;
发型：规矩偏帅&lt;br /&gt;
身高：178cm&lt;br /&gt;
肤色：正常色&lt;br /&gt;
性感胡渣&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;“零”&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
正义&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：？？？岁&lt;br /&gt;
性格：全知，全不知&lt;br /&gt;
服装：无&lt;br /&gt;
无立绘&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;其他角色&lt;/h1&gt;
&lt;h2&gt;花夜月&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
平凡&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：18岁&lt;br /&gt;
性格：略害羞，开朗&lt;br /&gt;
服装：校服&lt;br /&gt;
自由发挥&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;菲克&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
善意之伪&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：外观40岁&lt;br /&gt;
性格：好人&lt;br /&gt;
服装：家居服&lt;br /&gt;
和善中年大叔&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;中年男人&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
救赎&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：40岁&lt;br /&gt;
性格：压抑&lt;br /&gt;
服装：西服&lt;br /&gt;
中年男人&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>Saturday, 21 Feb 2015 14:20:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.02.21 14:20:article/Create-2015_02_21_a</guid>
<category>原创游戏</category>
<category>Galgame</category>
<category>人设</category>
</item>

<item>
<title>论脸</title>
<link>http://dtysky.moe/article/Art-2015_02_21_a</link>
<description>&lt;p&gt;毫无疑问，这是一个看脸的社会，大家都是颜控，虽然没有一张好脸的你不一定会失败，但有一张好脸的你往往会成功，这是完全符合自然规律的，那么，没有一张好脸的我会怎样呢？从我没有一张好脸开始我就在苦苦思索这个问题，历经了近十年的思考和心路变化，我终于明白了一些道理，当然，这些道理也不过是一个脸不好的人十年的道理而已，喷不喷由你www    &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;定义&lt;/h2&gt;
&lt;p&gt;脸，很重要，是的，作为身体的一部分的屁股也是很重要的，那么为什么脸尤为重要呢？&lt;br /&gt;
因为脸不仅仅是作为人的自然身体一部分，还是我们社会身体的一部分。&lt;br /&gt;
社会身体，指的就是社会层面上的身体，虽然这个可能是我造出来的概念不过其实以前已经有过“自然性别”、“社会性别”这样的称谓，所以现在这么写或许也没什么差池。&lt;br /&gt;
社会层面意味着什么呢？它意味着你的脸不仅仅是你的脸，也是大家的脸，或许一张脸并不能够影响你的吃喝拉撒睡，但它却能切实地影响你的人际交往。人际交往作为社会生活的底层，其意义是十分重大的，而脸作为其中的相当一部分，自然也是十分重要的，这也就是为什么我要在这里说脸。&lt;br /&gt;
当然，并不是说说完了脸脸就能好了，虽然脸的主要效用并不是在自然而是在社会，但脸的变化却仍然遵循该死的自然规律，你的脸并不会因为你的自知之明而变的好看，这真是一个悲伤的故事。&lt;br /&gt;
说多了，这里只谈定义，也就是——此文的脸，更多说的是社会身体上的脸，也就是社会意义上的脸。   &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;论述&lt;/h2&gt;
&lt;p&gt;怎么样，看到这是不是一股满满的山寨感扑面而来？因为本来就不是什么正规的东西嘛，论文也不会这么写——我给本文的定时是两小时之内。&lt;br /&gt;
那么我们要从什么地方开始论述呢？是说情人节过去不久，就从恋爱说起吧www&lt;br /&gt;
和在看本文大概五成的同胞一样，本人从没有谈过，嗯，从没有，一丁点也没，虽然看起来是个悲伤的故事但真正懂我的人应该都知道这其实没什么，或许也是必然的，扯远了~&lt;br /&gt;
脸在恋爱中是最为重要的（针对正常恋爱年龄的女性和所有年龄的男性），一张好脸能够让你在众多竞争中出类拔萃，第一时间将你的目标锁定到手（大概），这是众所周知的事情，不需要存有任何的怀疑，如果你还拥有着起码的理性（不错，正视现实吧，本文就是这么残酷www&lt;br /&gt;
很多人说恋爱主要是在于死缠烂打的勇气和锲而不舍的精神以及舍我其谁的自信，我问你，一个脸不好的人在喜欢的人面前何来勇气和精神和自信？这都不过是那些站着说话不腰疼的人的想当然而已。&lt;br /&gt;
这里就可以拿个人经验说事了，脸好自然是符合大家审美的，也就是五官端正皮肤白皙，而脸不好则是五官奇异皮肤扯淡，本人则是前一样占一些后一样占全，眼小鼻子大，从高中开始那无尽的痘痘和现在满脸的坑。这样的面容你让我拿出舍我其谁的自信？简直太搞笑了www当然我现在也不是没有自信，但比起我的能力应有的自信少太多了&lt;br /&gt;
脸不好会大大削弱一个人的自信，这使得即使他有很强的能力也不能够在外人面前充分地表现出来，这会导致两个结果——完全沉默或者物极必反，当然现在有了网络这个平台所以他们也可以成为喷子来发泄，但我坚信有文化的脸不好的都是前一种，比如我www&lt;br /&gt;
沉默意味着什么？意味着存在感稀薄，意味着无法展示自己，意味着失去很多应当属于自己的机会（当然对于脸不好的人，即使是应当属于你的机会也极其容易被脸好的人轻易抢走）。比如某些视频从来都不会拍我，我脸不好还真是对不起了（不错我就是说某些主办人员哈哈哈 &lt;br /&gt;
虽然能哈哈哈过去，当创伤仍然是存在的，虽然我们承受能力强大，但这也意味着我们受到伤害的几率比脸好的人多太多，比如某次我在某餐厅吃饭，餐厅的老板给我递过来一包纸巾，正当我想说谢谢的时候发现纸巾的包包上写着——XXX皮肤医院，XXX专家。当时我的内心真是被感激之情浸润地想直接哭出来（夸张了，我还是很坚强的www），还有一次在买水果的时候也有类似的事情。还有一种情况是人们会去鼓励你，比如母上时不时来一句——不就是痘痘嘛，男孩子有什么大不了的，你很帅啊——之类的话，虽然说的人看起来很满足很安慰，但知道听的人是什么感想么？wwwwwww&lt;br /&gt;
当然啦，对方往往是好意的但是好心往往是会办错事的，因为这个“好心”只是针对于施行者而言的，对于接收方仅仅是被揭起了伤疤而已。&lt;br /&gt;
那么脸不好就不能治么？当然可以，比我鄙人就被坑（www）了1w左右还是那德行，嘛这个是我的问题就不说了，一个客观原因是因为我有大量的事情要去做无法做到作息完全规律，但脸好的人就能做到无论如何都能脸好，当然也有相当一部分每天会把不少时间用在脸上，事实上我花很多时间在脸上的时候也没什么用，所以嘛www&lt;br /&gt;
这个真的没有办法，哎再说就是单纯的怨气发泄了，还是来严肃点吧。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;分析&lt;/h2&gt;
&lt;p&gt;严格来讲，真正由于先天五官问题而称得上“脸不好”的人是不多的，大多都处于平均或者以上水准，都不会引起人的反感，如果后天的习惯得当，绝大多数人都能够拥有标致的五官和白皙的皮肤。&lt;br /&gt;
但事实并非如此，比如笔者本人，那么问题到底出在哪里呢？就以笔者本人的经历来做出一个猜想。  &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;习惯：&lt;br /&gt;
从小的清洁习惯是一个重大的原因，在家境不好、家庭文化程度不高、或者是由于父母忙而寄居在上上辈家中的孩子们往往得不到应有的清洁习惯上的教育，洗脸刷牙草草了事，进食不规范，经常抠鼻屎，经常不洗澡等等，这些习惯直接关系到日后皮肤和五官的发展，尽管有人说他证明了某些习惯和五官的关系不大，但我的观点还是跟随以下感觉吧——你的鼻子大和牙齿天包地啥的和这些没关系？&lt;br /&gt;
一个良好的家庭会教育孩子用活水洗脸，勤洗澡，必须刷牙，青春期有痘痘不要乱动等等，防止一失足成千古恨。&lt;br /&gt;
所以有句话怎么说的来着？&lt;br /&gt;
&lt;strong&gt;——贫穷是原罪。&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;重视程度：&lt;br /&gt;
这也是非常重要的一点，一个家庭是否重视你的皮肤变化，是否愿意给你付出一定的精力去防止你的脸变不好——不错我就是说青春期！&lt;br /&gt;
多少人是因为青春期自然而然就被自然而然了？绝对不是我一个人吧。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;无奈：&lt;br /&gt;
有一定天生的成分在里面，也就是命运这个东西。即使你家里足够重视，为你做了治疗，你仍然有一定概率会不好，因为家人的判断能力和你的判断能力都是有限的，你不知道治疗是否可以生效，也不知道那些面带笑容的家伙是否黑心，于是最后——你反弹了www恭喜&lt;br /&gt;
当然这个或许还好，更坏的是你分明知晓一切，知晓让脸变好的方法，但却因为很多原因不能去那样做，比如这位。  &lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;结论&lt;/h2&gt;
&lt;p&gt;总之我是跪了，就这样吧。&lt;br /&gt;
或许我应该和那个名为阿兰.图灵的男人一样&lt;br /&gt;
——将计算机作为一生唯一的爱人。&lt;br /&gt;
当然我还是不会放弃治疗的嗯。&lt;br /&gt;
另外，如果能遇到对的女孩子我是很愿意的，但是基于以上的原因www&lt;br /&gt;
永别了，我的青春！&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Saturday, 21 Feb 2015 00:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.02.21 00:00:article/Art-2015_02_21_a</guid>
<category>脸</category>
<category>颜控</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>体三维显示器-PCB部分-LED控制板</title>
<link>http://dtysky.moe/article/Create-2015_02_19_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;
体三维显示器为真三维显示的一种，即可以在真是的空间内显示一个三维的图像，图像占据一个真实的三维空间，不需要借助任何辅助视觉器件，我采用的方案是二维LED点阵的旋转，LED点阵为自行设计，这里记录了控制板的设计心得。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1.指标&lt;/h2&gt;
&lt;p&gt;既然LED阵列已经设计完毕，那么其控制电路也就可以开始考虑了。 
这个控制电路应当是怎样的一种形式？但LED打样成功后我思索着这个问题，思考的约束主要来自于如下几个部分： &lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;1.IO数量:&lt;/strong&gt;  &lt;br /&gt;
LED阵列被分成了三块（为了最低的亮度考虑），每块分辨率为120x38，这也就是说按照逐行扫描的方案我需要(120+38)x3个IO去控制整个电路，一开始是想直接用FPGA芯片的IO去搞，甚至花了相当的时间去考虑如何设计一个FPGA核心板（太年轻了），当经历了漫长的选型、设计之后，甚至连原理图都快要完成的并且芯片已经买到手的时候，我发现了一个事实——这是不可能的任务。一个如此多IO脚的FPGA核心板至少十层以上，并且对于我这种菜鸟一次成功率基本为0，而我的经费和时间是有限的。&lt;br /&gt;
经过了基友和老师的劝告，我放弃了执念，选用了现成的开发板——黑金某个板子，IO口二百多个，这也让我不得不去考虑别的方案。&lt;br /&gt;
这个方案是大家所熟悉的，一百LED点阵都会用这种方式——Latch。&lt;br /&gt;
当然，出于某些原因我并没有用Latch，而是用了Trigger。&lt;br /&gt;
利用Trigger锁存数据来达到多路复用的目的，第一个周期将行送出锁存、第二个周期送出列这种形式，当然实际控制会复杂一些（消隐之类的）。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;2.负载：&lt;/strong&gt;&lt;br /&gt;
我选用了Trigger复用IO口的方案，这样一个IO就要对多个Trigger进行驱动，这是不被允许的，因为一般而言这种主控芯片可以负载的电容都是有限的，所以我不得不去考虑其他的方案。 
这时候，就要Buffer出场了，Buffer即为缓冲器，一个Buffer带载Latch的能力一般在4-6个，完全满足我的要求。&lt;br /&gt;
要注意的是，Buffer仅仅起到提高负载的作用，不对逻辑产生任何影响。 &lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;3.特殊引脚：&lt;/strong&gt;&lt;br /&gt;
特殊引脚就是Trigger的时钟引脚，相对于一般的数据引脚，时钟引脚的错误带来的后果更为严重，所谓牵一发而动全身，所以我在布线的时候为其做了特别的保护，加大线宽和线距以及让附近的走线尽量少。  &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;
Buffer选用TI的&lt;strong&gt;SN74alvch162827&lt;/strong&gt;，Trigger选用TI的&lt;strong&gt;SN74alvch16721&lt;/strong&gt;，保证质量。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;2.电源：&lt;/strong&gt;&lt;br /&gt;
除了LED电流之外还要解决这些芯片的供电问题，和LED阵列一样，选择了一个TI的电源模块，按照建议进行了布局。  &lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;3.电路：&lt;/strong&gt;&lt;br /&gt;
电路实际设计的过程比较痛苦，因为走线很多约束不少而面积被LED阵列限制所以双层板肯定不够，最终敲定了四层板。  &lt;br /&gt;
&lt;br&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3.生产&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1.布局布线：&lt;/strong&gt;&lt;br /&gt;
很痛苦，由于布线很紧张所以没有办法自动化，只有手慢慢拉，而且国内的打样厂一般不给打盲孔，所以无法让每一层物尽其用，但还是勉强布完了，如下：  &lt;br /&gt;
&lt;img alt=&amp;quot;layout&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-02-19a/1.png&amp;quot; /&gt;&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;2.打样：&lt;/strong&gt;&lt;br /&gt;
难度比较高，被第一家打样厂拒了之后第二家打样的，花了1K5，比LED阵列还贵。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;3.焊接：&lt;/strong&gt;&lt;br /&gt;
焊接也是蛋疼，找的师傅焊接，花了500。    &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4.总结&lt;/h2&gt;
&lt;p&gt;板子总共花费约2K5，整体良好。&lt;br /&gt;
说实话，作为一个菜鸟的第一块四层板，我感到十分庆幸。&lt;br /&gt;
板子如下：&lt;br /&gt;
&lt;img alt=&amp;quot;pab&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-02-19a/2.jpg&amp;quot; /&gt;&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Thursday, 19 Feb 2015 19:20:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.02.19 19:20:article/Create-2015_02_19_a</guid>
<category>体三维</category>
<category>PCB</category>
<category>LED阵列</category>
<category>Buffer</category>
<category>Trigger</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部分-LED阵列</title>
<link>http://dtysky.moe/article/Create-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;
体三维显示器为真三维显示的一种，即可以在真是的空间内显示一个三维的图像，图像占据一个真实的三维空间，不需要借助任何辅助视觉器件，我采用的方案是二维LED点阵的旋转，LED点阵为自行设计，这里记录了LED阵列的设计心得。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1.指标&lt;/h2&gt;
&lt;p&gt;既然要设计一个LED点阵，要考虑的无非就是颜色、大小、密度、亮度，以及无法避免的成本问题了。
对于这些指标，当然是越高越好，比如全彩、1080p、400ppi、高亮啥的，一开始我也是这么想的，但越往后设计越感受到自己的天真。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;1.密度:&lt;/strong&gt;    &lt;br /&gt;
对于密度而言，通常的led封装到0402已经是封底了，而0402LED的大小能达到的最高密度也就是1/(0.4x0.2)=12.5ppi，而且这个理论密度是根本不可能达到，因为我们必须要为焊盘与布线预留间距（国内PCB打样厂的线宽线距一般要求不低于0.2mm），so...吹了&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;2.大小：&lt;/strong&gt;&lt;br /&gt;
PCB的大小直接受到密度和成本的限制，对于PCB而言成本主要收到层数、大小和孔数的限制，再加上板子最后是要被电机带动旋转的，所以太大将会有风阻和力矩等问题。当然，最后选择的120x114（要知道这个分辨率的LED数量是1w+）实际上还是大了（以我当时的水准驾驭程度和成本的综合考虑而言是相当冒险的）。那么为什么我还要选择这么大的分辨率呢？当然是为了爱，因为当时实验所得这个分辨率下刚好可以显示一个萌妹子的轮廓www。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;3.色彩：&lt;/strong&gt;&lt;br /&gt;
颜色方面也是十分尴尬，能实现全彩自然是好的，但对于一个体三维显示器，彩色要考虑的事情非常多。&lt;br /&gt;
首先需要明白，RGB的LED的色彩是通过不同色彩通道的光混合而成，如果仅仅依靠“亮”与“不亮”来进行调色，三个通道最多出现8种颜色的光，更多的色彩是通过PWM来实现的，即调节一个大周期内每个通道的光的占空比，也就是调节每种颜色的光在一个发光周期中的能量（平均功率），这一点直接回影响控制器件的选择，因为如果想要实现全彩就必须在一个发光周期内至少进行256次PWM输出，加上LED扫描（后面会提到）的所占用的周期，这个要求的速度是比较高的，就算先不考虑控制部分，这一点也直接影响了LED的亮度，不停的变换会让LED的有效亮度减少，由于环境光的影响，LED的亮度将会随着扫描次数（包括色彩调节）大幅下降，由于分辨率已经定的比较大，导致扫描已经占用了不少周期，如果在这种情况下再加上色彩调节，不但会使亮度可能下降到一个不可见的等级，就算可见，色彩在此等亮度下可分辨的程度也是极低的，毫无意义。  &lt;br /&gt;
当然我们说如果换一种扫描方式，加上优秀的供电（LED的接受电流极限一般是几ma到几十ma）和牛X的LED（最高亮度可达几千LM），全彩也不是不可能，但这时候就会遇到其他的问题了——钱，和PCB布局的难度。&lt;br /&gt;
对于一个全彩LED显示电路，6层的PCB是至少的（想要完全解决EMIEMC问题），而EMIEMC问题的解决是需要大量经验和理论的，本人无能，暂时无法解决。同时对于PCB工艺的要求也比较高，再者全彩SMD LED的价格比较高，PCB的价格又是随着层数成几何级上升的，对于一个新手而言失败概率极高，所以对于当时作为一个菜鸟的我，还是妥协了。&lt;br /&gt;
综合考虑，我选择了0603单色翠绿的LED，灰度暂时没有考虑。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;4.亮度：&lt;/strong&gt;&lt;br /&gt;
亮度虽然在上面说过了，不过还是有其他的部分没有提到。&lt;br /&gt;
亮度不仅仅要考虑LED本身，还要考虑供电部分，一开始是准备直接用控制芯片的IO脚来驱动LED，但很快就被否决了。由于显示条件的限制，我并不可能给每一个LED都加上单独的驱动源，所以肯定是以行扫描或者列扫描的方式来驱动，这也就是说每一行LED共阳，每一列LED共阴。&lt;br /&gt;
在这种情况下，驱动电流明显不够不说，每个引脚都以最大功率输出会使得芯片整体负载过高。&lt;br /&gt;
所以我只有进行迂回战术，选用中转的电源来驱动LED电路，这将会在下面说明。
&lt;br&gt;&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.LED：&lt;/strong&gt;&lt;br /&gt;
LED是显示的核心，目标是亮度高、功耗低、便宜，最终我选择了国内某厂的LED，其最大电流是20mA，亮度也算可以。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;2.驱动源：&lt;/strong&gt;&lt;br /&gt;
由于不能够用控制芯片的IO脚驱动，但仍然要用其进行控制，所以我们需要考虑用间接的方法实现IO脚对LED的控制，这样我们就会自然地想到三极管，用PMOS作高端驱动控制阳极，NMOS做低端驱动控制阴极，MOS管的栅极链接到IO上，便可以实现IO对LED的间接控制，这样也解决了LED供电不足的问题。&lt;br /&gt;
当然,MOS管的选取需要考虑到LED的驱动电压和IO的输出电压，否则阈值范围不匹配就完蛋了www&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;3.电源：&lt;/strong&gt;&lt;br /&gt;
电源部分的要求是稳定和足够的功率，在本例中可以预见到的最大瞬时电流输出为3.3V下的120x20=2.4A,我选择的是TI的某个电源模块，最大输出电流5A，宽电压输出，使用简单，十分满足需求。&lt;br /&gt;
这里提一下良心的TI，一百来块的东西就这么随便申请成功还包顺丰www&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;4.电路：&lt;/strong&gt;&lt;br /&gt;
综合下来，LED、电源、MOS管全部选型完毕，接下来就是实际电路的设计了。&lt;br /&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;孔径：过孔的直径，主要约束因素也是电流大小，同样可以通过计算器来计算，另外厂商也有一定的限制，比如内孔0.2mm，外孔0.3mm。  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;设计结束后便可以开始绘制电路板了。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3.生产&lt;/h2&gt;
&lt;p&gt;生产的第一步是电路板绘制，首先是原理图的设计，这点不在赘述，教程可以看本博客的&lt;a href=&amp;quot;http://dtysky.github.io/tag/pcb.html&amp;quot;&gt;PCB系列帖子&lt;/a&gt;，需要注意的是以下几点：&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;1.布局布线：&lt;/strong&gt;&lt;br /&gt;
LED的数量达到五位数的级别，手动布局是完全不可取的做法，面对此等难题只能够借助于自动化的工具，好在Allegro支持脚本，所以我选择了用Python生成了&lt;strong&gt;.scr&lt;/strong&gt;脚本，之后再运行脚本即可，脚本的内容如下：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;add  connect;
pick  201.3  424.3;
pick  201.3  423.7;
done;
add  connect;
pick  203.3  424.3;
pick  203.3  423.7;
done;
add  connect;
pick  205.3  424.3;
pick  205.3  423.7;
done;
add  connect;
pick  207.3  424.3;
pick  207.3  423.7;
......
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这样一来，五位数的约束性“半自动布局”便很快完成了（其实也用了一个来小时），接下来可以用同样的方式去布线，整体的板子很快就可以完成:&lt;br /&gt;
&lt;img alt=&amp;quot;layout&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-02-18a/1.png&amp;quot; /&gt;&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;2.模块化：&lt;/strong&gt;&lt;br /&gt;
分割越细查错越容易，这是不变的真理，面对如此庞大的一个设计，模块化是必须的。&lt;br /&gt;
我将LED阵列作为了一个模块，将所有控制引脚全部引出到排针，降低风险。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;3.个性化：&lt;/strong&gt;&lt;br /&gt;
好不容易做出了个大东西，而且是个人的作品，你是否想要加点个性标志呢？&lt;br /&gt;
对于我而言当然是想的，而且是非常想，所以我花了一些功夫研究如何在PCB上印图案，也获得了成功，具体的方法将会在PCB教程系列写到。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;4.打样：&lt;/strong&gt;&lt;br /&gt;
联系打样厂家，注册账号，上传完整的设计文件（包括各层的Gerber文件、钻孔文件），算清费用，之后等待几天便拿到了产品。我这个板子的打样费总共1K+&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;5.贴片：&lt;/strong&gt;&lt;br /&gt;
这么多的SMD芯片不可能自己焊，我找了一家贴片厂去进行焊接。&lt;br /&gt;
由于只是个人的一个板子，一般的厂家是不给焊的，最后好不容易连蒙带骗游说了一家，为我进行了焊接。&lt;br /&gt;
当然最后板子来回几次后还是有问题，我也懒得继续纠缠了，再继续下去说不定有更多其他的岔子，所以我保守了一下。&lt;br /&gt;
板子贴片费2K6。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4.总结&lt;/h2&gt;
&lt;p&gt;板子总共花费约5K，整体良好，有些许行列的MOS管缺焊。&lt;br /&gt;
说实话，作为一个菜鸟的第一块板子，我感到十分庆幸。&lt;br /&gt;
板子如下：&lt;br /&gt;
&lt;img alt=&amp;quot;pcb&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-02-18a/2.png&amp;quot; /&gt;&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Wednesday, 18 Feb 2015 17:20:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.02.18 17:20:article/Create-2015_02_18_a</guid>
<category>体三维</category>
<category>PCB</category>
<category>LED阵列</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>旅游总结</title>
<link>http://dtysky.moe/article/Life-2015_02_15_a</link>
<description>&lt;p&gt;这次旅行共五天，两天上海两天杭州一天苏州。&lt;br /&gt;
除了苏州母上非得跟团（被坑）其他皆为我带路导游，略累，不过还行。&lt;br /&gt;
具体的感受嘛...  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;上海&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;评价：&lt;/strong&gt;&lt;br /&gt;
四星。&lt;br /&gt;
&lt;strong&gt;地点：&lt;/strong&gt;&lt;br /&gt;
东方明珠，南京东路，外滩，海洋水族馆，人民公园，城隍庙，上海博物馆。&lt;br /&gt;
&lt;strong&gt;详细：&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/microMsg1423551649449_zpsa75713a4.jpg&amp;quot; /&gt;&lt;br /&gt;
然后么，外滩和南京东路早来过了，这次外滩灯光没起来所以一般般，南京东路还是那样人多，整体没啥感觉，他们倒是挺开心。&lt;br /&gt;
晚饭是某巴西烤肉，一人300，我是吃的不错但是父辈似乎有些不适，虽然我的父母已经很开放了w&lt;br /&gt;
第二天一早是海洋水族馆，感觉不错，虽然整体气氛并没有那么...所以说所谓的黄油标准约会场所也是要有妹子才能有气氛啊233。&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/IMG20150211095908_zps553adba5.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/IMG20150211101521_zpse3d7f9ea.jpg&amp;quot; /&gt;&lt;br /&gt;
之后城隍庙没啥说的，就是小吃吃吃，下午上海博物馆倒是意外不错，不要门票内容丰富，学到了不少东西www虽然现在忘得差不多了。&lt;br /&gt;
&lt;strong&gt;总结：&lt;/strong&gt;&lt;br /&gt;
魔都主要还是看新东西吧，上海科技馆没去略可惜不过我机会多，老文化啥的并不多而且我觉得大多老文化也。。。没啥意思。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;杭州&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;评价：&lt;/strong&gt;&lt;br /&gt;
五星。
&lt;strong&gt;地点：&lt;/strong&gt;&lt;br /&gt;
雷峰塔，龙井，断桥残雪，湖心岛，灵隐寺，岳王庙，苏堤。&lt;br /&gt;
&lt;strong&gt;详细：&lt;/strong&gt;&lt;br /&gt;
西湖美的超出意料之外，雷峰塔上看着比东方明珠害怕多了（我恐高），四周都是水，超大一片很赞。&lt;br /&gt;
龙井么，就是一口井...周围人全部以卖茶为生，结果几乎是被比较勉强地买了六两狮峰龙井（2K4/0.5KG），我都觉得略肝疼...&lt;br /&gt;
断桥残雪主要看的是两边，长桥上梁祝的相送（据说）没感受来但真是很美，无法言说，可以感受一下：&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://i1307.photobucket.com/albums/s596/dtysky/IMG20150212152813_zps9c84cb73.jpg&amp;quot; /&gt;&lt;br /&gt;
湖心岛么，就是一个岛，但由于是西湖的岛所以很帅ww&lt;br /&gt;
接下来是灵隐寺，这次旅行觉得最值的地方，这一切都是源于那个摆着百佛雕像的房间，比起一般闭眼沉思装逼无限的佛像，这才是佛真正的状态
进去的一瞬间，差点没忍住哭出来。仅仅是看着，就能感受到他们的无限幸福！虽然我当时听着μ&amp;apos;s的歌，但佛是不会怪罪我的，因为我佛是无限宽容的ww&lt;br /&gt;
岳王庙和苏堤也挺好的，就是没啥突出的地方&lt;br /&gt;
&lt;strong&gt;总结：&lt;/strong&gt;&lt;br /&gt;
杭州是个好地方。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;苏州&lt;/h2&gt;
&lt;p&gt;被过度消费的地方，不提也罢，不建议去。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;旅行最好不要带太多人，和能够插科打挥的哥们更好，会让倦意大减，如果和有文化的哥们一起走旅行根本不烧钱，反而能学到许多东西。  &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>Sunday, 15 Feb 2015 21:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.02.15 21:00:article/Life-2015_02_15_a</guid>
<category>日常</category>
<category>旅游</category>
<category>西湖</category>
</item>

<item>
<title>博客重构</title>
<link>http://dtysky.moe/article/Create-Summary</link>
<description>&lt;h2&gt;概要&lt;/h2&gt;
&lt;p&gt;这次重构博客的主要原因是因为找到了未来的方向，以及对自身价值的进一步认定，博客的分类将会简化，从平时的点滴积累，大部分仍然会是技术和文化类的东西。另外，也将作为原创游戏进度更新的地点。
同时，由于原来博客的界面并不友好且繁杂，所以我对整个页面进行了重新的设计，为移动端和PC端分别做了样式，enjoy it!  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;分类说明&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Create：&lt;/strong&gt;&lt;br /&gt;
原创游戏更新，以及自己的技术作品展示。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;Skill&lt;/strong&gt;&lt;br /&gt;
技术文章，探讨自己的一些技术经验和心得。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;Art&lt;/strong&gt;&lt;br /&gt;
一些对世界的感悟，以及一些文学拙作。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;Life&lt;/strong&gt;&lt;br /&gt;
日常琐事。  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Monday, 09 Feb 2015 16:44:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.02.09 16:44:article/Create-Summary</guid>
<category>格式化</category>
</item>

<item>
<title>两年来的总结</title>
<link>http://dtysky.moe/article/Create-2014_12_19_a</link>
<description>&lt;h2&gt;体三维显示器相关&lt;/h2&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;strong&gt;1. PCB：&lt;/strong&gt;  &lt;/p&gt;
&lt;p&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;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;电路部分，其中转接板-Expand默认为黑金某核心板的转接板。&lt;br /&gt;
LED阵列侧边的引脚预留给光电传感器，防止误差积累，转接板侧边引脚预留给USB开发板和蓝牙设备。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;2. 机械部分：&lt;/strong&gt;  &lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://github.com/dtysky/3D_Displayer_Machine&amp;quot;&gt;https://github.com/dtysky/3D_Displayer_Machine&lt;/a&gt;  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;其中作为整体的那个dwg文件中没有替换后面修改的联轴器。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;3. 控制部分：&lt;/strong&gt;  &lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://github.com/dtysky/3D_Displayer_Controller&amp;quot;&gt;https://github.com/dtysky/3D_Displayer_Controller&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;70%完成度，只需要把DDR2控制器完成或者替换就90%了，做法都有着良好的注释，不过代码写的比较早，不怎么好看，也懒得重构了。&lt;br /&gt;
包含USB和PC通信的上位机、固件、FPGA部分，LED的FPGA控制部分（包括双缓存），DDR2控制器（缺少PHY）。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;4. DDR2控制器：&lt;/strong&gt;  &lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://github.com/dtysky/DDR2_CONTROLLER&amp;quot;&gt;https://github.com/dtysky/DDR2_CONTROLLER&lt;/a&gt;  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;缺少PHY，包括控制器主体、分别用VHDL和Systemverilog完成的Testbench，其中SV的具有随机测试功能，同时包含修改好的1G DDR2模型，直接在Modelsim内混合仿真即可。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;剧本解析器&lt;/h2&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://github.com/dtysky/Gal2Renpy&amp;quot;&gt;https://github.com/dtysky/Gal2Renpy&lt;/a&gt;&lt;br /&gt;
文档坑爹中，等有时间+心情补完。  &lt;/p&gt;
&lt;h2&gt;感言&lt;/h2&gt;
&lt;hr /&gt;
&lt;p&gt;两年前，我是个什么都不会、生活没有方向的菜鸟。&lt;br /&gt;
这时候，学姐出现在了我的面前，她那与我相似的性格吸引了我，我们谈了很多，黄油哲学人生物理耳机无所不谈，最后，我做出了一个选择——将梦想继续下去。&lt;br /&gt;
我翻出了高二时写的设定稿，将其不断重构，使其逐渐完整，然后在某人引导下看培根霍布斯休谟尼采海氏康德等等，都是为了在了解世界的同时，让作品有一些深意，这也导致后来我完成了某选修课的5W字民哲论文和被人吐槽剧本写得像论文，但是，我的确在进步。&lt;br /&gt;
进步是必要的，也是必然的，在以上的前提下，我找到了目标，回归了那个拼搏和努力的我。&lt;br /&gt;
“不懂怎么做，不会做，怕做不完，怎么办？”&lt;br /&gt;
舍友：“先接了再说，东西可以学，机会只有那么点。”&lt;br /&gt;
这里感谢我的舍友给了我这些鼓励和帮助，大一就带领学长学姐拿国奖的他毫无疑问为我提供了强大的技术支援和信心（舍友就是拍视频那个）。&lt;br /&gt;
FPGA不会？学&lt;br /&gt;
PCB不会？学&lt;br /&gt;
CAD不会？学&lt;br /&gt;
C不会？学&lt;br /&gt;
C#不会？学&lt;br /&gt;
Python不会？学&lt;br /&gt;
只要有心学，这些东西都不是问题。&lt;br /&gt;
托他们的福，我也能够在如此低的GPA下找到一个还好的工作，也赢得了每一个面试官的尊重。  &lt;/p&gt;
&lt;p&gt;以上是鸡血正能量，以下是现实  &lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;现实的分界线  &lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;并不是你想做到什么就能做到，可行性分析是重要的。&lt;br /&gt;
综合才能、财力、时间，以及现有科技水准才能得到一个最终的答案。&lt;br /&gt;
这个项目在我看来，最终无疑是&lt;strong&gt;失败&lt;/strong&gt;的。&lt;br /&gt;
不但距离一开始天马行空的“三片DMD全彩投影体三维”等想法相去甚远，甚至连妥协后的计划都没有完成。&lt;br /&gt;
因为我的热情已经被一次又一次的琐事磨灭了。&lt;br /&gt;
企划、采购、联络外包，亲自跑工厂和别的装作自己要批量，唯唯诺诺，为了价格磋商，为了成本计算每一笔小开支，这些东西都是完成这个东西必要的，但却是我不想去做的。&lt;br /&gt;
&lt;strong&gt;我只是想搞技术而已，我不想管什么和商人的交流。&lt;/strong&gt;&lt;br /&gt;
但现实是残酷的，你不得不去做这些事情，或者说这些事情必须有人去做。&lt;br /&gt;
然而我选择了单打独斗。&lt;br /&gt;
我牺牲了所有课余活动，学到了很多，代价是自身能力不足没有完成作品。&lt;br /&gt;
&lt;strong&gt;我不希望这件事再发生在我现在唯一的梦想——那个GAL上。&lt;/strong&gt;  &lt;/p&gt;
&lt;p&gt;我曾也以为我一直能够作为一个中二少年在自己的世界奔跑下去&lt;br /&gt;
但果然还是无法抵挡外面那宽广的世界&lt;br /&gt;
就像那通往银河的列车&lt;br /&gt;
在永恒孤独的铁道，向着路过行者致敬  &lt;/p&gt;
&lt;h2&gt;Thanks&lt;/h2&gt;
&lt;hr /&gt;
&lt;p&gt;感谢学校提供的机会和资金，感谢舍友的技术支援，感谢在米帝读PHD的学姐的引导（嫁给我吧），感谢TI的样片支持，感谢老师们的一些指导，感谢Cypress蛋疼的片子和支持。  &lt;/p&gt;
&lt;p&gt;Over。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Friday, 19 Dec 2014 23:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2014.12.19 23:00:article/Create-2014_12_19_a</guid>
<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>自我的分解和重组</title>
<link>http://dtysky.moe/article/Life-2014_06_06_a</link>
<description>&lt;p&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;/p&gt;
&lt;p&gt;他们，都是我的人格&lt;br /&gt;
都是我的分身&lt;br /&gt;
无论是对是错，正义与否，都是我自己的渴求  &lt;/p&gt;
&lt;p&gt;将自己分解，然后进行重组&lt;br /&gt;
让自己的不同人格在作品这一种媒介上交互&lt;br /&gt;
表露在众人的目光之前&lt;br /&gt;
这，或许就是真正的作家的存在方式  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;不要太高估时间的价值,首先要知道什么才是有价值的。&lt;/p&gt;
&lt;/blockquote&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Friday, 06 Jun 2014 21:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2014.06.06 21:00:article/Life-2014_06_06_a</guid>
<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>梦的叹息</title>
<link>http://dtysky.moe/article/Life-2014_05_12_a</link>
<description>&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;/p&gt;
&lt;p&gt;但是&lt;br /&gt;
人，总是要死的&lt;br /&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;br /&gt;
也应当一直渴求着这份风流。&lt;/p&gt;
&lt;p&gt;你一直在注视着我吧&lt;br /&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>Monday, 12 May 2014 19:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2014.05.12 19:00:article/Life-2014_05_12_a</guid>
<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>重估，虚无，再构</title>
<link>http://dtysky.moe/article/Art-2014_05_08_c</link>
<description>&lt;p&gt;本文写在半年前，一个研讨课的课程论文。&lt;br /&gt;
来日重构，先作存档。
&lt;/br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;object style=&amp;quot;height:1000px;width:100%;&amp;quot; data=&amp;quot;//src.dtysky.moe/blog-site/MySave.pdf&amp;quot; type=&amp;quot;application/pdf&amp;quot; width=&amp;quot;100%&amp;quot; height=&amp;quot;100%&amp;quot;&gt;&lt;/object&gt;&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Thursday, 08 May 2014 23:40:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2014.05.08 23:40:article/Art-2014_05_08_c</guid>
<category>经验论</category>
<category>存在主义</category>
<category>虚无主义</category>
<category>视角主义</category>
<category>尼采</category>
<category>海德格尔</category>
<category>萨特</category>
</item>

<item>
<title>【休谟】休谟哲学体系摘要(上)</title>
<link>http://dtysky.moe/article/Art-2014_05_08_b</link>
<description>&lt;p&gt;本文写在此日两个月前，是第二次读休谟的这一本《人性论》，由于事情较多并且描述较难，所以总结比较简短和草率。  &lt;/p&gt;
&lt;p&gt;百忙之中重读休谟，虽然时间多了几个月。。。
不管这个  &lt;/p&gt;
&lt;p&gt;先说说这本商务印书馆的《人性论》，翻译的实在是。。。&lt;br /&gt;
信算是有了，达雅就，读起来真是蛋疼&lt;br /&gt;
不过还算可以看下去（英文版就算了至少暂时  &lt;/p&gt;
&lt;p&gt;休谟是经验论的极致，也是一个彻底的怀疑主义者  &lt;/p&gt;
&lt;p&gt;可以摆在头的两个观点：  &lt;/p&gt;
&lt;p&gt;1、理性不过是激情的技师和奴仆  &lt;/p&gt;
&lt;p&gt;2、对一切的怀疑这种行为是深思后必然存在的，并且不能通过任何理证式的方法来解决它，只有疏忽和不在意才是唯一的暂时性解药  &lt;/p&gt;
&lt;p&gt;正文：  &lt;/p&gt;
&lt;p&gt;首先在一开始，休谟就生命他索要表述的是“精神科学”&lt;br /&gt;
我们知道科学本来就是一种经验论方法，所以他实际上是用经验论的方法来研究人类的心理，这也就是为什么说他为后面的心理学埋下了伏笔  &lt;/p&gt;
&lt;p&gt;一开始他所引用的一句话：  &lt;/p&gt;
&lt;p&gt;当你能够感觉到你愿意感觉得东西，能够说出你所感觉到的东西的时候，这是非常幸福的时候
——塔西佗。  &lt;/p&gt;
&lt;p&gt;这人我不认识，不过可以大概感受一下休谟的主旨是想表达什么，虽然也可能只是他对自我的鼓励之类的，无所谓。  &lt;/p&gt;
&lt;p&gt;1、观念和印象：  &lt;/p&gt;
&lt;p&gt;休谟将我们心灵中的知觉分为两种——观念和印象。&lt;br /&gt;
印象是最初进入心灵的最猛烈的知觉，观念则是感觉等等在推理和思考中微弱的印象。&lt;br /&gt;
观念总是来自于印象，而观念同时也可以作为印象自身。&lt;br /&gt;
印象往往都是通过感官从外界感知的。  &lt;/p&gt;
&lt;p&gt;观念和印象最大的区别使他们的活泼程度，当某种情绪十分激昂的时候，观念就可以逼近印象，这时候你也就几乎可以完全复现那一刻的印象。  &lt;/p&gt;
&lt;p&gt;举个例子，联系到“诗”的话，也就是那种玄之又玄的“临场感”，甚至可以让你的脑中出现某种幻象。  &lt;/p&gt;
&lt;p&gt;印象是我们知识的基础，我们现在对很多知识的最初印象并不是由我们直接感知的，大都是前人的印象转化为的观念传递的结果。&lt;br /&gt;
由于没有直接感受，所以活泼程度不够，这也就是为什么实践后总是比看书来得记忆深刻的原因。  &lt;/p&gt;
&lt;p&gt;2、记忆和想象：  &lt;/p&gt;
&lt;p&gt;这个没什么说的，就是说想象中的事物至少是记忆中事物的拼合，不可能超出记忆已有的范围，并且一定是符合我们的思维模式的。&lt;br /&gt;
A只能想象出A视角下的东西，不可能超越。&lt;br /&gt;
证明：反例无。  &lt;/p&gt;
&lt;p&gt;3、空间和时间：  &lt;/p&gt;
&lt;p&gt;空间有限性——感官有限。&lt;br /&gt;
时间有限性——心灵有限。  &lt;/p&gt;
&lt;p&gt;思考至少要求有一个最小的刻度去保证对象能够形成一个轮廓，并且需要一个轮廓的定性来让思考可以继续。&lt;br /&gt;
所以从理性的角度，实无穷是不存在的。  &lt;/p&gt;
&lt;p&gt;4、关系：  &lt;/p&gt;
&lt;p&gt;这个休谟分了不少种类，而我认为最重要的应该是“类比”和“因果”这两种。&lt;br /&gt;
类比，就是从一件事物推断到另一个具有何其有相似性质的事物，从而获取和那个相似事物所关联的其他事物的关联。&lt;br /&gt;
因果，就是A导致B这样的关系，它是信念的来源，后面再说。  &lt;/p&gt;
&lt;p&gt;二者的基础都是统计，经验综合。  &lt;/p&gt;
&lt;p&gt;5、因果：  &lt;/p&gt;
&lt;p&gt;A-&amp;gt;B这种因果东西本质上是一种经验综合，是实验的结果。  &lt;/p&gt;
&lt;p&gt;我们并不能够保证：&lt;br /&gt;
A-&amp;gt;B将来一定会发生&lt;br /&gt;
我们只能说：&lt;br /&gt;
A-&amp;gt;B过去总是发生。  &lt;/p&gt;
&lt;p&gt;“A-&amp;gt;B未来一定会发生”不过是我们过去“A-&amp;gt;B总是发生”的一种信念。&lt;br /&gt;
信念可以来自于自身的判断，也可以来自于社会通则。&lt;br /&gt;
而判断本身也是来自于社会通则。&lt;br /&gt;
通则就是习惯本身。  &lt;/p&gt;
&lt;p&gt;总是发生-&amp;gt;因果关系-&amp;gt;信念-&amp;gt;习惯-&amp;gt;信念。  &lt;/p&gt;
&lt;p&gt;当然，这里并不是说休谟否定了那个“因果关系”是事物本质属性的可能，他的意思只是说：
找不到一种方法去证明这种自然的必然性。  &lt;/p&gt;
&lt;p&gt;6、必然性/机会：  &lt;/p&gt;
&lt;p&gt;必然性和机会是两个对立的东西。&lt;br /&gt;
机会会削弱必然性，必然性会降低机会的影响。  &lt;/p&gt;
&lt;p&gt;必然性来自于因果关系。&lt;br /&gt;
机会是对原因的否定。  &lt;/p&gt;
&lt;p&gt;权衡的过程就是机会和必然性博弈的过程，最后哪个影响你的感觉越深，你就会选择哪一种作为判断的主导。  &lt;/p&gt;
&lt;p&gt;“有妹子在场，XXXXXXX，说不定我能打十个”&lt;br /&gt;
——这是机会。&lt;br /&gt;
“我肯定打不过十个啊。”&lt;br /&gt;
——这是必然性。  &lt;/p&gt;
&lt;p&gt;7、信念：  &lt;/p&gt;
&lt;p&gt;信念是实感的来源，信念越深，实感越强，就会越快乐/痛苦。&lt;br /&gt;
当不能理解的时候，你就只能去相信了。&lt;br /&gt;
就算是虚无主义者，也相信世界的本质是虚无。  &lt;/p&gt;
&lt;p&gt;8、原因的效能：  &lt;/p&gt;
&lt;p&gt;原因的效能并不是因果关系对象双方中任意一方的属性，它存于我们的心灵的倾向中。&lt;br /&gt;
你心灵中预设的某个原因的可能越高，他在你判断的时候效能就越高。  &lt;/p&gt;
&lt;p&gt;9、语境：  &lt;/p&gt;
&lt;p&gt;词语的滥用是容易的，但在表述理论的时候永远只会带来负面效应。&lt;br /&gt;
简单来说就是：&lt;br /&gt;
瞎用自己都无法理解的词语玩玩完全可以，但拿出来做学术就是扯淡了。  &lt;/p&gt;
&lt;p&gt;10、通则：  &lt;/p&gt;
&lt;p&gt;通则是习惯的集成，虽然有时没有任何根基道理到总会对我们的判断产生巨大的影响。&lt;br /&gt;
比如复仇的道德必要性啊什么的。  &lt;/p&gt;
&lt;p&gt;11、理性：  &lt;/p&gt;
&lt;p&gt;理性也是人类的一种习惯。&lt;br /&gt;
世界不可能被理性解析完全。&lt;br /&gt;
&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;其实东西很多，简单得这么压缩的面目全非，所以估计我自己看着都蛋疼。  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;如果各位觉得以上枯燥，不如来看看下面我节选的作者的卷末自述：&lt;/strong&gt;  &lt;/p&gt;
&lt;p&gt;我在什么地方？我是什么样的人？我由什么原因获得我的存在，我将来会返回到什么状态？我应该追求谁的恩惠，惧怕谁的愤怒？四周有什么存在物环绕着我？我对谁有任何影响，或者说，谁对我有任何影响？我被所有这类问题迷惑了，开始想象自己处于最可怜的情况中，四周漆黑一团，我完全被剥夺了每一个肢体和每一种官能的运用能力。  &lt;/p&gt;
&lt;p&gt;最幸运的是，理性虽然不能驱散这些疑云，可是自然本身却足以达到那个目的，把我的哲学的忧郁症和昏迷治愈了，或者是通过松散这种心灵倾向，或者是通过某种事物和我的感官的生动印象，消灭了所有这些幻想。我就餐，我玩双六，我谈话，并和我的朋友们谈笑；在经过三四个钟头的娱乐以后，我再返回来看这一类思辨时，就觉得这些思辨那样冷酷、牵强、可笑，因而发现自己无心再继续这类思辨了。  &lt;/p&gt;
&lt;p&gt;XXXXXXX。我准备把我的全部书籍和论文都烧掉，决心不再为了推理和哲学放弃人生的快乐。现在怒气笼罩着我，便发生了这些感想。XXXXXXX。虽然在同时对于那样一种辛苦的勤劳究竟是否合理得不到解答，而且有没有借此到达真理的任何大的希望。我有什么义务非得把时间这样浪费不可呢？这样能够达到怎样的目的？人类的利益还是自己的利益？不能。正如推理或信仰一切的人一定是傻瓜那样，如果我也是个傻瓜，那么我的傻气也一定要是愉快的。  &lt;/p&gt;
&lt;p&gt;XXXXXX。哲学对我的倦怠和松懈提不出任何反对的理由，哲学的胜利的希望只能寄托于愉快的心情的再现，而不能寄托于理性或者信念的力量。  &lt;/p&gt;
&lt;p&gt;一般来说，宗教中的错误是危险的，哲学中的错误则仅仅是可笑而已。  &lt;/p&gt;
&lt;p&gt;一个地道的怀疑主义者，不但会怀疑他的哲学的信念，也会怀疑他的哲学的怀疑；不论由于怀疑或信念，他都从来不会摒弃他可能自然享受到的天真的快乐。  &lt;/p&gt;
&lt;p&gt;我觉得最后这个状态和佛家很像。&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>Thursday, 08 May 2014 22:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2014.05.08 22:00:article/Art-2014_05_08_b</guid>
<category>经验论</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>【霍布斯】霍布斯哲学体系摘要</title>
<link>http://dtysky.moe/article/Art-2014_05_08_a</link>
<description>&lt;p&gt;本文写在此日一年前，是重新开始读哲学的第二个开端，所以有纰漏在所难免，不过大部分仍然是能够正确还原。&lt;br /&gt;
现在理解与一开始有些不同，有时间的话，会加以重审，暂且放在这里做一个记录罢。  &lt;/p&gt;
&lt;p&gt;只仔细研读了“论人类”和“论国家”两部分，宗教方面当时不太感兴趣所以就随便看了下。  &lt;/p&gt;
&lt;p&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;/p&gt;
&lt;p&gt;然后就是对国家的看法，这一部分，虽然的确是能够自洽，但其正确性（合理性）我却持有一些怀疑，就此书的译者的评价而言&lt;br /&gt;
——霍布斯拥有进步的自然观，但却有着反动的政治观。&lt;br /&gt;
这个反动，我就理解为不合理好了，因为他特殊的时代背景所以对无政府的状况的恐惧可以理解，但为此强行正当化某些行为，果然还是无法接受。  &lt;/p&gt;
&lt;p&gt;此处为两部分——赞同和反驳：  &lt;/p&gt;
&lt;p&gt;和平需求法律，法律要求人们服从一个共同的权力，公平和公正来自于法律。&lt;br /&gt;
——这里的法律是广义的，同时包括霍布斯所言的自然法。&lt;br /&gt;
——自然法的约束来源的权力持有者是上帝。&lt;br /&gt;
第一句而言，没有什么可反驳的，因为不成文的法律也是法律。  &lt;/p&gt;
&lt;p&gt;使人们倾向于和平的激情是对死亡的畏惧、对舒适生活所必须事物的欲望，以及通过自己的勤劳获得这一切的希望。&lt;br /&gt;
——倘若对死亡的畏惧不单单指对死亡本身、同时还包括死亡所带来的连协效应，那么我没有什么疑问。  &lt;/p&gt;
&lt;p&gt;自然权利是基本权利，是我们按照我们的意愿选择一切方式保全我们生命的权利。&lt;br /&gt;
——保全我们的生命，包括掠食、剥皮防寒等。&lt;br /&gt;
——这个层面而言，自然状况下对其他同类的捕杀也是自然权利的一部分。&lt;br /&gt;
——自然权利是生物原始的本能，但霍布斯将其称为人性的一部分。&lt;br /&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;/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;/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;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;strong&gt;以下同为相当于一个老师的解答&lt;/strong&gt;  &lt;/p&gt;
&lt;p&gt;霍布斯是个“屌丝自由主义者”，他是最早认识到自由意志（与天定论相对抗）及消极自由（就是他主张的“己所不欲，勿施于人”）的人之一，但最后推出的结论却是要建立起一个有绝对权威的“利维坦”，公民要么选择你死我活的丛林无政府状态，要么选择无条件服从“圣王”，那么你要哪一个呢？  &lt;/p&gt;
&lt;p&gt;不得不说，部分“个人主义自干五”以及“犬儒主义自干五”的主张，其实在五百年前之前就由霍布斯完全地、而且比他们清晰地多地阐述过了，这真是历史的讽刺  &lt;/p&gt;
&lt;p&gt;霍布斯会走向“反动”的结论，主要是霍布斯所处时代所致，也是他个人懦弱、胆小的性格所致。他认识到了”自然法”的重要性，却把“生存”列为人类的第一要务。也就是说，他隐含的意思是，人只有活着自然法才能得以实现，但事实却是，只有自然法得到了实现，人才能活着。他颠倒了这个顺序，紧接着把保障人“生存”的东西说成是“秩序”，是“人造之法”，而“人造之法”又是要靠“绝对的权威”才能得以保障的，因此他的理论也就完成了。也就是说，他虽然认识到了自然法的存在，但在他具体政治构建的主张中，自然法已经完全出局了，谈的全是“人造之法”的事情  &lt;/p&gt;
&lt;p&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>Thursday, 08 May 2014 18:50:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2014.05.08 18:50:article/Art-2014_05_08_a</guid>
<category>机械论</category>
<category>霍布斯</category>
</item>

<item>
<title>艰难的抉择</title>
<link>http://dtysky.moe/article/Life-2014_05_06_a</link>
<description>&lt;p&gt;省创项目延期了半年&lt;br /&gt;
本来以为没有问题的地方陆续出现问题&lt;br /&gt;
由于PC、FPGA、软件、IC固件都是自己搞得，所以一个一个调几乎要崩溃&lt;br /&gt;
当然在时间面前这些或许都不是问题但是。。。&lt;br /&gt;
另一个原因来了，由于自己的英语问题导致欠下了不少债&lt;br /&gt;
两周后的校四级补考，四周后的CET4，两个都直接关系到毕业&lt;br /&gt;
然后是限选考试，然后就是考试周，接下来就是实习&lt;br /&gt;
这是不得不做出的选择  &lt;/p&gt;
&lt;p&gt;但但从根本而言，我还是十分感谢这个项目的&lt;br /&gt;
如果没有它的话，我估计到现在还不明确未来要走的道路吧，因为它，从一年半前的什么都不会到现在能独揽一些东西去做，虽然还有许多不足，但也算是一个比较好的开端了&lt;br /&gt;
嗯，忙完必须完成的这些事，继续努力去做吧&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/xx_zps9af582e7.jpg&amp;quot; /&gt;&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Tuesday, 06 May 2014 16:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2014.05.06 16:00:article/Life-2014_05_06_a</guid>
<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>【培根】培根哲学体系摘要</title>
<link>http://dtysky.moe/article/Art-2014_05_05_a</link>
<description>&lt;p&gt;本文写在此日一年前，是重新开始读哲学的一个开端，所以有纰漏在所难免，不过大部分仍然是能够正确还原。&lt;br /&gt;
有时间的话，会加以重审，暂且放在这里做一个记录罢。  &lt;/p&gt;
&lt;p&gt;一本论说集一本新工具&lt;br /&gt;
整体来说属于理性主义，实用主义&lt;br /&gt;
实践至上&lt;br /&gt;
虽然有些观点和我本身的有些相异，但大体都能够理解并接受吧  &lt;/p&gt;
&lt;p&gt;首先是他对于“真理”的认知：&lt;br /&gt;
1、研究、认识、相信真理是人性的最高之美德&lt;br /&gt;
2、少数由“真理”作为“心”的架构支撑的人，他们才是真正的强大的  &lt;/p&gt;
&lt;p&gt;可见其对真理的存在并不怀疑，并且对其有极高的赞誉&lt;br /&gt;
虽然我通篇都没有找到他对“真理”真正的比较详尽的描述，所以仍然无法对他口中的这个名词得到一个比较正确的解释  &lt;/p&gt;
&lt;p&gt;但在新工具一书之中，虽然基本没有说到真理，但却不止一次，或者说整本书都是为“原理”、“原则”、“法则”、“法式”这一类东西做阐述，不知道这些东西和“真理”是否是一个东西，据我猜测大概应该是如此  &lt;/p&gt;
&lt;p&gt;那么就可以说，研究、认识、相信“原理”、“原则”、“法则”、“法式”是人性的最高之美德。&lt;br /&gt;
这一点从他对“人类的权利”的描述中大概可以看出一些缘由，他将“将人类的权利行使的淋漓尽致的行为”视为最为崇高的行为，而“人类的权利”就是对自然的改造和利用，并且他还指出科学（源于自然哲学）这种学科相较于政治等学科具有绝对的优越性，因为它的获利对象是全体人类，而政治这种东西确具有相当的局限性。&lt;br /&gt;
并不是说他对政治持有鄙夷的态度，因为他对政治也有着很深的研究，以及对其各种手段的研究，但即使如此他还是将科学放在了之前，可见他属于重大局的类型。  &lt;/p&gt;
&lt;p&gt;这样一来，他所说的“真理”应该就是指“自然原理”，而不是过去常说的“人类思想的独有的真理”？  &lt;/p&gt;
&lt;p&gt;二是他对于人性的认知：&lt;br /&gt;
1、人性倾向于懒惰&lt;br /&gt;
2、人性倾向于嫉妒他人&lt;br /&gt;
3、人性倾向于贪婪与急躁&lt;br /&gt;
4、人性倾向于受到公众之中的他人的好恶的影响&lt;br /&gt;
4、“爱”是“愚”之子&lt;br /&gt;
5、“勇气”是“卑贱”与“无识”的产儿&lt;br /&gt;
6、“善”应为人之本，并且其不会过度，其实永存于人性中的，若不发向人类，也会发向其他生物，要“基于自身”去为善，要利他&lt;br /&gt;
7、人的天性可以压服但很少能够完全熄灭&lt;br /&gt;
8、认为某项值得钦羡的事物是垃圾的，往往是没有能力得到它的人&lt;br /&gt;
9、习惯是除了迷信之外凌驾于一切的力量  &lt;/p&gt;
&lt;p&gt;可见他认为人性中根植着“善与恶“双重的存在。但即使是那些他认为”恶“的存在，若有利用价值，他也会就其利用价值进行阐述，所以说真不愧是实用主义。&lt;br /&gt;
在这几条中，我有不解或者质疑的地方：&lt;br /&gt;
1、他在讨论嫉妒的时候，认为”公嫉“=”公愤“，并认为一个国家一旦出现了这种情况，就必然是当权者的错误。这是否表明他这时候完全相信”多数人“的判断？这和他在新工具里所表明的”怀疑“态度并不相同&lt;br /&gt;
2、所谓对”善“的说法，确定发向其他生物的”善“不是为了衬托出对人类的憎恨？如果是的话，这并不是一个所谓的”真正的善“&lt;br /&gt;
3、要”基于自身“去为善，这样是否有意思”强加“的意味？既然是”利他“，不基于他人如何利他？自我满足？&lt;br /&gt;
这里插入一点他对死亡的看法，我十分赞同：&lt;br /&gt;
死亡本身并不足惧，真正恐怖的是死亡带来的一切，也就是”失去“，同时死亡本身还有熄灭他人的妒忌，带来名誉的功效  &lt;/p&gt;
&lt;p&gt;第三，是宗教和政治：&lt;br /&gt;
1、他是一个有神论者&lt;br /&gt;
2、宗教统一可以带来和平，和平是最高的”福祉“&lt;br /&gt;
3、盲从和真假相混不能算作统一 &lt;br /&gt;
4、在上帝和人类之间做出”公断“是愚蠢的&lt;br /&gt;
5、政府不应有偏向性，要给与人民”适当“的自由&lt;br /&gt;
6、人类在肉体方面与禽兽差不多，只因在精神方面与”神“相类，所以才有了人的尊严&lt;br /&gt;
7、全力反迷信本身也是一种迷信&lt;br /&gt;
8、帝王是人，也是神（约束权力、控制意志）&lt;br /&gt;
9、革新应该出于改善而不是作秀&lt;br /&gt;
10、殖民应该是一个利润本国并且带动殖民地原住民富裕的行为&lt;br /&gt;
11、司法者的职责是解释法律而不是立法  &lt;/p&gt;
&lt;p&gt;质疑与不解：&lt;br /&gt;
1、他对宗教十分重视并且认为其是绝对必要的，但却不止一次批驳宗教对于权力的介入，既然相信上帝又为何认为其不应介入实际的权力？&lt;br /&gt;
2、他所谓的”神“，是否与一般理解的”创世“的那个人不同，而是类似于人类的智慧结晶那种，创造来寻求自我优越感的东西？&lt;br /&gt;
3、他是否认为除了上帝之外的一切信仰都是迷信？&lt;br /&gt;
4、他认为殖民地的原住民是否具有和殖民者拥有同样的尊严？  &lt;/p&gt;
&lt;p&gt;第四点，为人处事与道德：&lt;br /&gt;
这点就不细说了，大概就是认为整体应该向善，但在很多情况下可以由于利益去抛弃&lt;br /&gt;
道德来达成一些目的。有些腹黑的意味，但同时却不赞同道德是”工具“。重视友情，持有中庸之道：应该长存的是希望、愉快而不是狂欢。青年人道德优越，老年人重事故与利益。形体或心里残缺遭到轻蔑之人十分勇敢，目的是为了”反优越“。等等。  &lt;/p&gt;
&lt;p&gt;不解：&lt;br /&gt;
1、培根是否有些倾向于功利主义？&lt;br /&gt;
2、他到底是如何理解道德的？  &lt;/p&gt;
&lt;p&gt;第五点，自然与科学：&lt;br /&gt;
1、不应将自然的定则与已明之物加以规定，否则会破坏创造力&lt;br /&gt;
2、纯粹怀疑主义无法得到真的原则与正确的结论&lt;br /&gt;
3、感官作为思考唯一的原材料是重要的，但需要矫正，但多数的感性认识是应当排斥的&lt;br /&gt;
4、逻辑有极高的风险，应为其对原材料依赖过深&lt;br /&gt;
5、要对某体系的学说加以批驳，首先要消除偏见并完全了解那种学说&lt;br /&gt;
6、人的认知局限于自己的感官经验&lt;br /&gt;
7、人类的权力来自人类的知识&lt;br /&gt;
8、较狭窄或属于直接感知的感念拥有”实质性“&lt;br /&gt;
9、一再说明实践的重要性&lt;br /&gt;
10、我们需要一个正当、规则的方法去分析已有的经验，这种方法就是真正的”归纳法“&lt;br /&gt;
11、对自然的普遍原则，不应该深究其因&lt;br /&gt;
12、人的理解力容易将原理复杂化，并添加自我认为真的东西，对真相进行歪曲&lt;br /&gt;
13、文字反制着理解力&lt;br /&gt;
14、实际并不存在/存在却含定义混乱的事物应当被根绝&lt;br /&gt;
15、探查事物对人的的作用比追究其底层原理重要&lt;br /&gt;
16、论证就是哲学本身，最好的论证就是实际的经验&lt;br /&gt;
17、寻因比逐果重要&lt;br /&gt;
18、实用的哲学才有价值&lt;br /&gt;
19、”不可知论“持有者大都是本人无能&lt;br /&gt;
20、在知识问题上”同意“为根据的推测是最坏的&lt;br /&gt;
21、思考不能建筑在意见之上&lt;br /&gt;
22、时间是检验真理的唯一标准&lt;br /&gt;
23、虚荣与无能的带来的擅断使知识严重受制&lt;br /&gt;
24、偶然所成之物的光辉无法与专注所成的光辉相提并论&lt;br /&gt;
25、首先要找到常见事物的原因，才能进一步探究&lt;br /&gt;
26、无法得出应用之果的争辩毫无意义&lt;br /&gt;
27、科学的方法能够划齐人类的智慧，甚少依赖个人的卓越，设定可靠的规则&lt;br /&gt;
28、各种事功本身，作为真理的证物，价值大于人生的安乐&lt;br /&gt;
29、科学只是让人类恢复固有的对自然的权力，如何运用是伦理和宗教等的问题&lt;br /&gt;
30、应该把公认的意见撇在一边，不要好高骛远&lt;br /&gt;
31、一个真正完善的规则：确实、自由、导向行动的。&lt;br /&gt;
32、在动作方面最有用的，在知识方面是最真的。&lt;br /&gt;
33、只要认识到原理人类就能够去创造所有&lt;br /&gt;
34、人类堕落时失去的天真依靠宗教与信仰夺回，对自然的统治权靠技术和科学夺回。  &lt;/p&gt;
&lt;p&gt;不解与质疑：&lt;br /&gt;
1、还是那个问题，培根是不是一个功利主义者？&lt;br /&gt;
2、第8条中的”实质性“指什么？确切性？&lt;br /&gt;
3、11条和25条自相矛盾？&lt;br /&gt;
4、27条有一丝”人类工具化“的味道？还是说我理解错了？&lt;br /&gt;
5、15条，完全否定想象力？&lt;br /&gt;
6、34条中的“天真”说的是？  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;以下是算是一个老师的回复&lt;/strong&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;培根是非常早期的哲学家，他所有的理念只能说是有某种“倾向”，而不能据此说他就是XX主义者。比如功利主义，功利主义的创始人是边泌，在他之前并没有对功利主义的详细论述。倘若培根的著作就已经明确阐述了功利主义的原则，那就没有边泌什么事了&lt;/p&gt;
&lt;p&gt;培根的确是实用主义者。需要注意的是实用主义和功利主义不同，它和“保守主义”一样，是一种一直存在、无需被阐述、被定义（相对地，对它的阐述和定义差别也比较大），大家就能明白的理念。唯一可以确定的就是实用主义是理想主义的反义词。从这一点上说培根和柏拉图的确是完全两条路线&lt;/p&gt;
&lt;p&gt;功利主义和实用主义是两码事。哲学，归根结底，就是一门研究“哪些事物、行为、理念等从本质上对人类是有利的”，“哪些事物、行为、理念等从本质上对人类是根本有害的”的学问。前者即是“善”，后者即是“恶”，哲学还研究如何能够实现“最大善”（至善）。由于对“善”的理解不同，就形成了不同的哲学派别。功利主义所理解的“善”仅仅是“幸福”，它追求的是社会的“幸福最大化”；实用主义和其他派别一样辨析善恶，但它并不把为善、不为恶看作是原则，它认为只要“有利”，偶尔为恶也无妨&lt;/p&gt;
&lt;p&gt;实用主义是一种“形而下”的理念，在它上面可能还有一种指导性的“形而上”的理念。同一种理想的理想主义者，比如自由主义者，互相很容易谈到一起去；功利主义者亦然。但实用主义者却未必。如果实用主义者同时是个人主义者，那么他的“利”就仅仅是自己的“利”，他追求的是自身的利益最大化，当公众善合于他的利益时他便行善，当公众善不合于他的利益的时候他便行恶，其他的实用主义者对他来说都是潜在的竞争对手；如果实用主义者是同时是社群主义者，那么他的“利”就是这整个社群的利益最大化，他不会做本质上对这个社群“不利”（恶）的事情（除非他认为这是“必要之恶”，或者是通往更大利益所必须的牺牲），但完全可能对社群外的人作恶。当实用主义者思考的是“全人类的利益最大化”的时候，他就与哲学家吻合了&lt;/p&gt;
&lt;p&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>Monday, 05 May 2014 17:08:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2014.05.05 17:08:article/Art-2014_05_05_a</guid>
<category>经验论</category>
<category>培根</category>
</item>

<item>
<title>无病呻吟</title>
<link>http://dtysky.moe/article/Life-2014_05_03_a</link>
<description>&lt;p&gt;随便说点啥  &lt;/p&gt;
&lt;p&gt;说这现在项目，仍然问题不断&lt;br /&gt;
烧片子烧片子，时序不稳&lt;br /&gt;
这说明了什么呢？&lt;br /&gt;
&lt;strong&gt;越级和期望的问题&lt;/strong&gt;&lt;br /&gt;
这种技术性的东西，有很多东西不是直接上手就行的，需要一个环境和适当的积累&lt;br /&gt;
可以预料到，如果俺是电子系的，有不少搞这行的老师，也不会落到如此田地&lt;br /&gt;
自己瞎捣鼓真是何必何必&lt;br /&gt;
还有十二天，拼命了  &lt;/p&gt;
&lt;p&gt;另外就是改剧本的问题&lt;br /&gt;
这些天主要在做两个方面的思考&lt;br /&gt;
一个是价值和意义的重审&lt;br /&gt;
一个是内容与形式的关系  &lt;/p&gt;
&lt;p&gt;作为一个没时间看书（当然我说的是有效时间）的现在的我，尝试从某些大爷的原著里去寻找答案是非常不可期待的，所以算是看了一些论文性质的东西  &lt;/p&gt;
&lt;p&gt;第一个问题，我为啥要做这些？梦想？梦想是啥？&lt;br /&gt;
思来想去都想不到答案，最后也只能用“此生存在证明”这个短语来做一个暂时性的结束&lt;br /&gt;
正如某某人曾说的（虽然我赌九成九的概率他不会看我的东西），对这些东西意义的追问必将陷入虚无主义，毕竟个人不是一个文明，对于文明、集体啥的我们还能说它的意义在于延续，虽然我也不知道这个延续有啥意义  &lt;/p&gt;
&lt;p&gt;第二个问题就两句话&lt;br /&gt;
1、雅俗共赏是可贵的&lt;br /&gt;
2、无论是中二恐怖悬疑萌燃无厘头都只是一些形式而已，通过形式来批判内容就是神经病。当然形式和内容的匹配还是有一定规律的，不过并非绝对  &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>Saturday, 03 May 2014 18:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2014.05.03 18:00:article/Life-2014_05_03_a</guid>
<category>日常</category>
</item>

<item>
<title>玄武湖之游</title>
<link>http://dtysky.moe/article/Life-2014_05_02_a</link>
<description>&lt;p&gt;今天总算去了玄武湖公园玩了一下。&lt;br /&gt;
算一算上次出去（公事不算）也是很久之前了，所以这次反而有些不知道干什么。本来想着去别的城市转转，但想想花的时间太长，还有活没有干完呢，所以最后选择就近去玄武湖公园走走算了。  &lt;/p&gt;
&lt;p&gt;先不管路痴属性爆发饶了些路子，总算在一点左右到了公园的内部。于是...就走吧。&lt;br /&gt;
不错，没有其他的计划，反正本来也就是想要走走而已，虽然说只是想将“走”这个行为当成这一次的过程，但想想其实它也就是唯一的一个目的了，当然这个目的还需要加个限定词——在&lt;strong&gt;风景不错&lt;/strong&gt;地方走走，尤其是有&lt;strong&gt;水&lt;/strong&gt;的地方。当然，虽然是这么想的，也总不能一直盯着水面吧，一个再美的东西盯久了也会变得无聊，所以在观赏水的间隙，我也观赏了一些少女。  &lt;/p&gt;
&lt;p&gt;说起少女是很有意思的，由于我的发型和近视加上不愿意戴眼镜，所以在远处只能看到一个模糊的身影，所以远观的时候很多少女都是很不错的，但当走进后...要么就是颜、要么就是听到了少女的发言（虽然我im03隔音不错，但近距离的声音还是可以听到一些的）。反复几次之后，我重新将注目的方向移回了水和天空。  &lt;/p&gt;
&lt;p&gt;然后就是一些小插曲，比如在路上碰到了某两个年纪比较大的中年男人，一个拿着萨克斯，一个拿着谱子和外放在陪某个经典七八十年代的歌曲吧，咱驻足停留了这么一会，感觉挺有意思，然后就走了。另外是途上碰到两个外国哥们，我路过的时候貌似认出了我的塞子指点着说了几句，当然我是不敢去搭话就是（英语等级低）。  &lt;/p&gt;
&lt;p&gt;描述性的东西说完，接下来是&lt;strong&gt;分析&lt;/strong&gt;：  &lt;/p&gt;
&lt;p&gt;这次本来预设的目的是：  &lt;/p&gt;
&lt;blockquote&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;/blockquote&gt;
&lt;p&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;strong&gt;这种方式并不好&lt;/strong&gt;，但还是必须要把这些词进行一下&lt;strong&gt;重定义&lt;/strong&gt;，来保证我说的话是可以理解的：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;生命：既然是出自&lt;strong&gt;第一人称的人&lt;/strong&gt;的口中，那么指的自然就是&lt;strong&gt;人类的生命&lt;/strong&gt;。生命在这里被定义为一种&lt;strong&gt;状态&lt;/strong&gt;，是活着的时候所有小状态的综合。&lt;br /&gt;
本质：这个我就不妄言了，关于它存不存在的问题我解决不了，用它原本的意思就行——事物本身所固有的根本的属性。&lt;br /&gt;
耗费：消耗，消耗能量，消耗激情，消耗每一个我们能够消耗的东西。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这一切加起来也就是说：我们活着所做的一切都是在耗费我们所积攒的的精力、能量、物质、压力等等，没有耗费，就没有生命。&lt;br /&gt;
到此一切都说得通了，我今天出行的目的是：  &lt;/p&gt;
&lt;h2&gt;怕憋坏了，出去耗费耗费，仅此而已。&lt;/h2&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Friday, 02 May 2014 20:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2014.05.02 20:00:article/Life-2014_05_02_a</guid>
<category>生活</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>世界线启动</title>
<link>http://dtysky.moe/article/Life-Summary_old</link>
<description>&lt;h2&gt;初衷&lt;/h2&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;hr /&gt;
&lt;h2&gt;世界线划分&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;经验而谦逊的现实：&lt;/strong&gt;&lt;br /&gt;
工程师的世界线。  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;1.硬件，主要用于记录FPGA和嵌入式的设计思想以及调试心得 &lt;/br&gt; 
2.软件，主要用于记录软件语言设计思想、一些使用过的类库等等。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;雅致而耗散的诗人：&lt;/strong&gt;&lt;br /&gt;
诗人的世界线。  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;1.分析，用于记录博主对于某些作品的评价和感受、亦或是自身对于文学形式与内容的思考研究。此处的鉴赏并不保证具备普遍性，仅为博主自身价值体系内、审美下的见解。&lt;br /&gt;
2.作品，用于记录博主自己的诗、散文、小说等作品。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;矛盾而真诚的世界：&lt;/strong&gt;&lt;br /&gt;
观测者的世界线。  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;1.分析，用于记录博主对一些哲学体系、哲学家的研究和思考，同时也会分享一些认为有意思的哲学文章。&lt;br /&gt;
2.作品，用于记录博主自己的论文等作品。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;纯粹而残缺的歌姬：&lt;/strong&gt;&lt;br /&gt;
读者的世界线。  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;1.音乐，用于记录对一些音乐作品的感受。&lt;br /&gt;
2.绘画，用于记录对一些画作的感受。 &lt;br /&gt;
3.影像，对一些影视作品的感想。&lt;br /&gt;
4.游戏，对一些游戏作品的感想。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;平凡而美丽的生活：&lt;/strong&gt;&lt;br /&gt;
常人的世界线。  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;记录各种各样的生活琐事。&lt;/p&gt;
&lt;/blockquote&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Thursday, 01 May 2014 16:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2014.05.01 16:00:article/Life-Summary_old</guid>
<category>初始化</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>