<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>lzzd 博客</title>
        <link>https://lazy-zed.com/</link>
        <description>Hi，这里是lzzd的博客，欢迎来访🎈🎉</description>
        <lastBuildDate>Tue, 21 Apr 2026 09:09:22 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>zh-CN</language>
        <copyright>All rights reserved 2026, lzzd</copyright>
        <item>
            <title><![CDATA[Agent落地项目小型活动（Skill）]]></title>
            <link>https://lazy-zed.com/article/AI_8</link>
            <guid>https://lazy-zed.com/article/AI_8</guid>
            <pubDate>Thu, 16 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[这篇文章详细复盘了我在游戏项目里落地“小型活动 AI Agent”的全过程：从最初希望 AI 直接完成活动接入，到不断踩坑、修规则、改流程，最后沉淀出一套可复用的 Skill + 项目规则 + 工具链 工作方式。文章会解释 Skill 的本质、为什么 Agent 一开始总会做错、如何把模糊经验变成稳定流程，以及为什么“让 AI 只改代码、输出配表建议”反而是更适合项目现实的方案。

]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-33385e127d7c80e6a931f2bde1386794"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-34985e127d7c8064a818f82579ecb7f6" data-id="34985e127d7c8064a818f82579ecb7f6"><span><div id="34985e127d7c8064a818f82579ecb7f6" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34985e127d7c8064a818f82579ecb7f6" title="一、为什么想做这个东西"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">一、为什么想做这个东西</span></span></h3><div class="notion-text notion-block-34985e127d7c80e5aecacde9547633ed">在游戏研发里，很多工作表面上看是「开发」，实际上是很多固定动作的组合：</div><ul class="notion-list notion-list-disc notion-block-34985e127d7c8041810aed84e36966bd"><li>读策划案</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c8068b34dcffb63a20af7"><li>读反馈表</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c8016b208cdf392d9e4b1"><li>找参考活动</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c8072b713cb8d6a4c059e"><li>定位项目里该改哪些枚举、脚本、界面、配置</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c803092c0d6a77faeaa04"><li>实现代码</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c80419902e8f36faa99b4"><li>做风险检查</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c80d5a5f6d51fb9df13cb"><li>列测试点</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c8030a01efc15ba604919"><li>做 review</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c80ab9cdffc6afdc76263"><li>最后补配置、联调、跑活动</li></ul><div class="notion-text notion-block-34985e127d7c803cb890ff9ded91f200">如果这些动作每次都要从头想，AI 就很容易表现得像「看起来懂很多，但做起来不稳定」。</div><div class="notion-text notion-block-34985e127d7c80458118ea029f4c1c22">真正想解决的问题，不是「让 AI 更聪明」，而是：</div><div class="notion-text notion-block-34985e127d7c8062a04fe66a1d2c6a45"><b>怎么把项目里已经存在的经验、规则、边界条件，变成 AI 可以稳定执行的工作流。</b></div><div class="notion-text notion-block-34985e127d7c80438d05c08f1354f903">选择的切入点，是一个很典型的业务场景：</div><div class="notion-text notion-block-34985e127d7c8096b236e2003d10921b"><b>无新增子活动、以复用为主的小型活动接入。</b></div><div class="notion-text notion-block-34985e127d7c8055b863ef1b910ddad3">这个场景适合做 Agent 落地试点，因为它同时具备三种特征：</div><ol start="1" class="notion-list notion-list-numbered notion-block-34985e127d7c806d9fb6d6bb9c3caf64"><li><b>有明确输入</b>：活动枚举、服务器对象、反馈表、参考活动</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-34985e127d7c8087b661f4dd6a3b2060"><li><b>有固定改动面</b>：枚举、脚本、界面、配置</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-34985e127d7c80f98d50e09709e5d6fe"><li><b>有大量项目经验</b>：哪些能复用，哪些必须问，哪些不能碰</li></ol><div class="notion-text notion-block-34985e127d7c807a8815f5cec629ff7c">换句话说，它天然适合被沉淀成 <code class="notion-inline-code">skill</code>。</div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-34985e127d7c80c3af06ec20bb1babc6" data-id="34985e127d7c80c3af06ec20bb1babc6"><span><div id="34985e127d7c80c3af06ec20bb1babc6" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34985e127d7c80c3af06ec20bb1babc6" title="二、Skill 到底是什么"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">二、Skill 到底是什么</span></span></h3><div class="notion-text notion-block-34985e127d7c8067badcf60009627b69">很多人第一次接触 Cursor 时，会把 <code class="notion-inline-code">skill</code> 理解成「一个提示词模板」。这个理解不完全错，但不够本质。</div><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-34985e127d7c805da328c21aa39421aa" data-id="34985e127d7c805da328c21aa39421aa"><span><div id="34985e127d7c805da328c21aa39421aa" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34985e127d7c805da328c21aa39421aa" title="Skill 的本质"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Skill 的本质</span></span></h4><div class="notion-text notion-block-34985e127d7c804e87b2d206f8d3d144"><code class="notion-inline-code">Skill</code> 本质上不是知识库，也不是代码生成器，而是：</div><div class="notion-text notion-block-34985e127d7c80bda705ccb96fdba978"><b>一份给 Agent 的「项目内工作规程」。</b></div><div class="notion-text notion-block-34985e127d7c8057aad2fe3c449005a5">它的作用不是告诉 AI「这个功能怎么写」，而是告诉 AI：</div><ul class="notion-list notion-list-disc notion-block-34985e127d7c8055a2a5caf7c016725d"><li>这个任务适用于什么范围</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c80b28228e92466f9e71c"><li>先做什么，后做什么</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c807d9e55eba7e2082ee2"><li>什么可以直接做，什么必须先问</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c8083882ac21317277017"><li>哪些文件一定要改，哪些一定不能改</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c8015b953e1346c40a312"><li>哪些错误是常见误判</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c808eb357f9fc5117695c"><li>任务完成后必须输出什么</li></ul><div class="notion-text notion-block-34985e127d7c801185b4d434deb56677">如果把 AI 看成一个新来的开发同学，那 <code class="notion-inline-code">skill</code> 就像是这个项目写给他的：上手说明、操作手册、项目规范、防踩坑清单。</div><blockquote class="notion-quote notion-block-34985e127d7c80749860ff93fc0917fa"><div><b>Skill 不是让 AI 获得知识，而是让 AI 在特定场景下按正确流程做事。</b></div></blockquote><div class="notion-text notion-block-34985e127d7c80bb9e17c9577cb39405">这是后续所有迭代的核心出发点。</div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-34985e127d7c806db3b9c2f866d3f80e" data-id="34985e127d7c806db3b9c2f866d3f80e"><span><div id="34985e127d7c806db3b9c2f866d3f80e" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34985e127d7c806db3b9c2f866d3f80e" title="三、为什么一开始 Agent 总做不对"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">三、为什么一开始 Agent 总做不对</span></span></h3><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-34985e127d7c80099f1cefef5c52c3b1" data-id="34985e127d7c80099f1cefef5c52c3b1"><span><div id="34985e127d7c80099f1cefef5c52c3b1" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34985e127d7c80099f1cefef5c52c3b1" title="1. AI 会「看起来懂」，但不等于「知道项目正确做法」"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">1. AI 会「看起来懂」，但不等于「知道项目正确做法」</span></span></h4><div class="notion-text notion-block-34985e127d7c80a2b246dd1fbf7aa32b">例如让它做一个小型活动，它可能会：</div><ul class="notion-list notion-list-disc notion-block-34985e127d7c80869633e4050ef06630"><li>找到一个看起来像参考活动的脚本目录</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c8095a013d3b79e5dceb4"><li>猜一个参考实现</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c8073b470de28c6de4a6b"><li>开始改脚本</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c80c99768f62cea4df7a7"><li>甚至直接跳过配置表</li></ul><div class="notion-text notion-block-34985e127d7c800b9967cc2ed8b7677e">从语言上看很像在推进任务，但实际上已经偏了。</div><div class="notion-text notion-block-34985e127d7c80268b30ee07a4172a12">原因很简单：<b>AI 擅长补全合理答案，不擅长天然知道你们项目里的隐含规则。</b> 而游戏项目恰恰充满隐含规则，例如：</div><ul class="notion-list notion-list-disc notion-block-34985e127d7c803cad43d078a3fd323d"><li>参考活动应该先从哪张表里确认</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c80859a67f187e641815b"><li>哪个子活动默认进 tab，哪个不进</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c80b3bf30d7c7376a671c"><li>哪些资源必须问，哪些可以复用</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c80e89d01d9dd582ed41a"><li>哪些文件是导表产物，绝对不能直接改</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c80be9ba9fe76fcd56cef"><li>哪种改表方式和项目工具兼容，哪种不兼容</li></ul><div class="notion-text notion-block-34985e127d7c80a8968ffa7925ea5156">如果这些规则没写进 <code class="notion-inline-code">skill</code>，AI 就会开始「合理猜测」。一旦开始猜，稳定性就没了。</div><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-34985e127d7c8004a5dfc51f9858d7a3" data-id="34985e127d7c8004a5dfc51f9858d7a3"><span><div id="34985e127d7c8004a5dfc51f9858d7a3" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34985e127d7c8004a5dfc51f9858d7a3" title="2. AI 容易把「能做」误判成「应该做」"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">2. AI 容易把「能做」误判成「应该做」</span></span></h4><div class="notion-text notion-block-34985e127d7c803792b6d1c478a18af8">它确实能直接改表、转表、继续改生成后的配置文件，但「能做」不等于「项目里应该这样做」。</div><div class="notion-text notion-block-34985e127d7c8070b65ee599c9dd3089">实际验证发现：</div><ul class="notion-list notion-list-disc notion-block-34985e127d7c803fbff4faaa20dd51db"><li>用通用方式去改 <code class="notion-inline-code">xlsx</code>，可能与项目转表工具不兼容</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c80e2ad77feb78b806edc"><li>直接改导表后的 <code class="notion-inline-code">.lua</code> / <code class="notion-inline-code">.json</code>，会破坏项目流程</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c804aa98ffda09c7693ff"><li>就算表改成功了，稳定性和调试成本也很高</li></ul><div class="notion-text notion-block-34985e127d7c800bb318d3613a3b6844">问题不在于 AI 不会改，而在于：<b>它不知道哪里是能力边界，哪里是项目边界。</b></div><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-34985e127d7c80c288f7eecf56df5837" data-id="34985e127d7c80c288f7eecf56df5837"><span><div id="34985e127d7c80c288f7eecf56df5837" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34985e127d7c80c288f7eecf56df5837" title="3. AI 会在复杂链路里逐渐漂移"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">3. AI 会在复杂链路里逐渐漂移</span></span></h4><div class="notion-text notion-block-34985e127d7c8057ba32e6a4a7e92cf3">一开始想让它一条龙做完：读反馈表、找参考活动、改代码、改配置、转表、校验、输出结果。这条链太长，任何一个环节出现偏差，后面就会越走越偏。</div><div class="notion-text notion-block-34985e127d7c80939d1ae4527b2ee7d2">真正让 Agent 稳定的，不是「把所有事情都交给它」，而是：</div><div class="notion-text notion-block-34985e127d7c8008914edc86fcd3d6b6"><b>把它放在最适合自动化的那一段。</b></div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-34985e127d7c808a95f1ea9860fe8931" data-id="34985e127d7c808a95f1ea9860fe8931"><span><div id="34985e127d7c808a95f1ea9860fe8931" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34985e127d7c808a95f1ea9860fe8931" title="四、小型活动 Agent 是怎么一步步迭代出来的"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">四、小型活动 Agent 是怎么一步步迭代出来的</span></span></h3><div class="notion-text notion-block-34985e127d7c80f8a092e139cef4f366">这套 skill 不是一次写成的，而是被真实问题「逼」出来的。整个过程大致经历了四个阶段（下文分阶段展开）。</div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-34985e127d7c805da545ed1617077e0b" data-id="34985e127d7c805da545ed1617077e0b"><span><div id="34985e127d7c805da545ed1617077e0b" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34985e127d7c805da545ed1617077e0b" title="五、第一阶段：先把「要做什么」写清楚"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">五、第一阶段：先把「要做什么」写清楚</span></span></h3><div class="notion-text notion-block-34985e127d7c80a38caed0b124be77b6">最基础的动作，是把场景收敛成明确边界。</div><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-34985e127d7c804f9e34ec4da6385b8b" data-id="34985e127d7c804f9e34ec4da6385b8b"><span><div id="34985e127d7c804f9e34ec4da6385b8b" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34985e127d7c804f9e34ec4da6385b8b" title="适用范围"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">适用范围</span></span></h4><ul class="notion-list notion-list-disc notion-block-34985e127d7c8077ac18d6ff251f8421"><li>纯客户端为主</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c80d280e8c8cebe9e94fe"><li>无新增子活动</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c802bbf59f08db9f018cd"><li>优先复用参考活动</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c80d1bc89e05ea7a9c4da"><li>目标是先跑通</li></ul><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-34985e127d7c805fa1d6e6ac120065ac" data-id="34985e127d7c805fa1d6e6ac120065ac"><span><div id="34985e127d7c805fa1d6e6ac120065ac" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34985e127d7c805fa1d6e6ac120065ac" title="典型输入"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">典型输入</span></span></h4><ul class="notion-list notion-list-disc notion-block-34985e127d7c80e4ae31c513bc5065e8"><li>客户端枚举名</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c80ed98aac02db3adf0a0"><li>服务器对象名</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c80c58f45dedd256cc2e8"><li>复用的子活动列表</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c809d8cedf80c34750952"><li>反馈表问题描述</li></ul><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-34985e127d7c807288cefa0526083266" data-id="34985e127d7c807288cefa0526083266"><span><div id="34985e127d7c807288cefa0526083266" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34985e127d7c807288cefa0526083266" title="典型输出"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">典型输出</span></span></h4><ul class="notion-list notion-list-disc notion-block-34985e127d7c80fe9eb6c0baa6331b58"><li>改哪些代码、脚本</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c80398890f411fc32b428"><li>配表建议</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c800f9d74f3debeb47a54"><li>风险点、测试点、review 建议</li></ul><div class="notion-text notion-block-34985e127d7c8073929ec3397dc83b73">一旦范围没收住，Agent 就会默认把任务扩展成：新玩法设计、服务端对象重建、配置体系重构、UI 全量重做——这些都已经超出「小型活动接入」试点场景。</div><div class="notion-text notion-block-34985e127d7c807a9b24d48c353e4221"><b>第一步不是写规则，而是先把任务边界收紧。</b></div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-34985e127d7c802c86c3e4a640aad877" data-id="34985e127d7c802c86c3e4a640aad877"><span><div id="34985e127d7c802c86c3e4a640aad877" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34985e127d7c802c86c3e4a640aad877" title="六、第二阶段：把「项目经验」写成硬规则"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">六、第二阶段：把「项目经验」写成硬规则</span></span></h3><div class="notion-text notion-block-34985e127d7c80ea8683dc7826176d49">把原本只存在于开发脑子里的经验，改写成 Agent 能执行的规则。例如（均为后续反复验证后加入）：</div><table class="notion-simple-table notion-block-34985e127d7c8054b4a3e2f47321d54d"><tbody><tr class="notion-simple-table-row notion-block-34985e127d7c80629295c95db41a057b"><td class="" style="width:120px"><div class="notion-simple-table-cell"><b>规则</b></div></td><td class="" style="width:120px"><div class="notion-simple-table-cell"><b>解决的问题</b></div></td></tr><tr class="notion-simple-table-row notion-block-34985e127d7c8054a8dbc4621db62c6f"><td class="" style="width:120px"><div class="notion-simple-table-cell">参考活动不能乱猜，必须先从指定配置确认</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">AI 把「名字像」误判成「业务上就是」</div></td></tr><tr class="notion-simple-table-row notion-block-34985e127d7c80d999c1f8e69e471ef9"><td class="" style="width:120px"><div class="notion-simple-table-cell">子活动范围不能自己扩展，以开发者输入的复用列表为准</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">防止从反馈表过度脑补</div></td></tr><tr class="notion-simple-table-row notion-block-34985e127d7c80c4932cd0b484b93224"><td class="" style="width:120px"><div class="notion-simple-table-cell">资源信息优先从反馈表问题描述提取，缺项再问</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">减少无效提问、避免卡死</div></td></tr><tr class="notion-simple-table-row notion-block-34985e127d7c804685b4e7803db2729e"><td class="" style="width:120px"><div class="notion-simple-table-cell">明确哪些能复用、哪些必须确认</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">信息不全时是「继续推进」还是「卡死」</div></td></tr></tbody></table><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-34985e127d7c80478737d42f22cb83b6" data-id="34985e127d7c80478737d42f22cb83b6"><span><div id="34985e127d7c80478737d42f22cb83b6" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34985e127d7c80478737d42f22cb83b6" title="七、第三阶段：从「直接改表」退回到「输出配表建议」"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">七、第三阶段：从「直接改表」退回到「输出配表建议」</span></span></h3><div class="notion-text notion-block-34985e127d7c8084b1ece2dd1a687fb3">这是落地过程中<b>最关键的一次认知变化</b>。</div><div class="notion-text notion-block-34985e127d7c804d9d44c0b6fd8bb7a4">一开始的想法很直观：既然 AI 知道要改哪些表、哪些字段，让它直接改表不就行了？</div><div class="notion-text notion-block-34985e127d7c809ab77bf33a3532aac6">连续跑几轮后，问题基本都集中在配置这一段：</div><ul class="notion-list notion-list-disc notion-block-34985e127d7c80cb9a96c345d62e89b1"><li>改表方式与项目工具链不稳定</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c8008bdaeef916a1ec364"><li>转表链路依赖多</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c805ab3fff05f06938184"><li>导表结果不一致时排查成本高</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c801cad52c28743ed2786"><li>AI 知道「该怎么配」，不等于「改表一定稳」</li></ul><div class="notion-text notion-block-34985e127d7c80d3ac88f0bcd5c8ecdb">真正关心的不是「AI 能不能把表直接填完」，而是：</div><div class="notion-text notion-block-34985e127d7c80e3a458fb28b8a7c734"><b>「这个 AI 工作流，能不能让下一个小型活动明显更快？」</b></div><div class="notion-text notion-block-34985e127d7c80ad94eed06fdee4c655">答案可以是：能。但关键提效点不一定来自「直接改表」，而来自：</div><ul class="notion-list notion-list-disc notion-block-34985e127d7c8072806ff95ad8a46daf"><li>快速找对参考活动</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c809fb407d879a80fb4f8"><li>快速列清代码改动点</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c806ea30ffa737cbd6e3c"><li>快速识别复用关系</li></ul><ul class="notion-list notion-list-disc notion-block-34985e127d7c8024a563ff955a629b60"><li>快速整理完整配表建议</li></ul></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[从零实现 Unity 边缘外发光：Rim Light + Bloom 原理与完整实现]]></title>
            <link>https://lazy-zed.com/article/u3d_14</link>
            <guid>https://lazy-zed.com/article/u3d_14</guid>
            <pubDate>Fri, 30 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[ 本文从菲涅尔现象出发，推导边缘光的数学原理（法线与视线点积），逐步实现锐利内层与柔化外层的双层叠加 Rim Light Shader，再配合 Post-Processing Bloom 让光晕真正向外扩散。总结了坐标系统一、分层叠加等通用 Shader 思路，并延伸出动态颜色、卡通描边、HDR Bloom 联动、URP 迁移四个进阶方向。

]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-19885e127d7c80788290dff7687e854f"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-34485e127d7c806ebdd8dc57bfdefc83" data-id="34485e127d7c806ebdd8dc57bfdefc83"><span><div id="34485e127d7c806ebdd8dc57bfdefc83" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34485e127d7c806ebdd8dc57bfdefc83" title="一、目标是什么"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">一、目标是什么</span></span></h3><div class="notion-text notion-block-34485e127d7c8075b399dc535ceb52a3">做游戏时经常看到一种效果：角色或物体的边缘有一圈发光，像是被光晕包裹（比如网易永劫无间的霸体人物边缘发光效果）。这种效果在技术上叫 <b>Rim Light（边缘光）</b>，也常被称为菲涅尔效果（Fresnel Effect）。</div><div class="notion-text notion-block-34485e127d7c8039b202da95d436df8a">这篇文章记录从零写出这个效果的完整过程，包括原理推导、代码实现、以及用 Post-Processing 让效果更好看的步骤。</div><hr class="notion-hr notion-block-34485e127d7c8020af2af7f2257ba73e"/><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-34485e127d7c8071b97be64525a863a6" data-id="34485e127d7c8071b97be64525a863a6"><span><div id="34485e127d7c8071b97be64525a863a6" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34485e127d7c8071b97be64525a863a6" title="二、原理：为什么边缘会发光"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">二、原理：为什么边缘会发光</span></span></h3><div class="notion-text notion-block-34485e127d7c80c0b542d220f3bb24de">自然界里有一个现象：当你看向一面玻璃的正面时，玻璃很透明；但从很斜的角度看时，玻璃会变得很反光。这就是菲涅尔现象。</div><div class="notion-text notion-block-34485e127d7c80e8a235fb707c0aaead">Rim Light 借用了同样的思路：<b>视线方向和物体表面越垂直（正对着你），亮度越低；越平行（边缘处），亮度越高。</b></div><div class="notion-text notion-block-34485e127d7c80e1bc1fc2f79f02c975">计算方法：</div><div class="notion-text notion-block-34485e127d7c80e0ac7dd21658244a98"><code class="notion-inline-code">rim = 1 - dot(法线方向, 视线方向)</code></div><ul class="notion-list notion-list-disc notion-block-34485e127d7c80f28808d9baf369e77c"><li><code class="notion-inline-code">dot</code> 是点积，结果是两个向量的夹角余弦</li></ul><ul class="notion-list notion-list-disc notion-block-34485e127d7c805a89eee04ccd1a284d"><li>正对摄像机的面：法线和视线方向几乎平行，点积接近 1，<code class="notion-inline-code">1 - 1 = 0</code>，不发光</li></ul><ul class="notion-list notion-list-disc notion-block-34485e127d7c80b0b8b2f7f5c7a35e3d"><li>边缘的面：法线和视线方向接近垂直，点积接近 0，<code class="notion-inline-code">1 - 0 = 1</code>，完全发光</li></ul><div class="notion-text notion-block-34485e127d7c807980f7c39e931eb6df">再用 <code class="notion-inline-code">pow</code> 控制衰减速度：<code class="notion-inline-code">pow(rim, _RimPower)</code> ，RimPower 越大，发光区域越窄越锐利。</div><hr class="notion-hr notion-block-34485e127d7c8012a1e7e64d59dde166"/><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-34485e127d7c80cbbcd9d4bc46663c4d" data-id="34485e127d7c80cbbcd9d4bc46663c4d"><span><div id="34485e127d7c80cbbcd9d4bc46663c4d" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34485e127d7c80cbbcd9d4bc46663c4d" title="三、第一步：搭建 Shader 基础结构"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">三、第一步：搭建 Shader 基础结构</span></span></h3><div class="notion-text notion-block-34485e127d7c801387e0ddb1dd7ab8e2">在 Unity 的 Built-in 渲染管线里，Shader 分三层：</div><ul class="notion-list notion-list-disc notion-block-34485e127d7c8052bb1ddd64edcfa0d2"><li><b>Properties</b>：暴露给 Inspector 的可调参数</li></ul><ul class="notion-list notion-list-disc notion-block-34485e127d7c80efa505cc3a720d71e8"><li><b>SubShader / Pass</b>：实际的渲染逻辑</li></ul><ul class="notion-list notion-list-disc notion-block-34485e127d7c80b7b854cb85eb1baec6"><li><b>CGPROGRAM</b>：顶点着色器和片段着色器的代码区</li></ul><div class="notion-text notion-block-34485e127d7c80b49defed8d4f6028fb">先写好框架，把需要的参数暴露出来：</div><div class="notion-text notion-block-34485e127d7c80478f9af2a62d037a80"><code class="notion-inline-code">Shader &quot;Custom/BuiltInRimLight&quot;
{
    Properties 
    {
        _MainTex (&quot;主纹理&quot;, 2D) = &quot;white&quot; {}
        _RimColor (&quot;边缘光颜色&quot;, Color) = (0.5, 0.5, 1, 1)
        _RimPower (&quot;边缘强度&quot;, Range(0, 8)) = 2.0
    }
    ...
}</code></div><div class="notion-text notion-block-34485e127d7c8035a642daf4f8125cbe"><code class="notion-inline-code">_RimPower</code> 范围设 0~8：太小边缘光会漫到整个模型，太大只有极细一条线。</div><hr class="notion-hr notion-block-34485e127d7c805b9f7ce6fccb26baf3"/><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-34485e127d7c80a8b1cceb705b594c47" data-id="34485e127d7c80a8b1cceb705b594c47"><span><div id="34485e127d7c80a8b1cceb705b594c47" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34485e127d7c80a8b1cceb705b594c47" title="四、第二步：顶点着色器——把数据传到片段着色器"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">四、第二步：顶点着色器——把数据传到片段着色器</span></span></h3><div class="notion-text notion-block-34485e127d7c80db9cb3f465f63b6fc0">顶点着色器的任务是把每个顶点从<b>模型空间</b>变换到正确的坐标系，并把需要的数据打包传给片段着色器（v2f 结构体）。</div><div class="notion-text notion-block-34485e127d7c80638555e6ca6fc6391f">边缘光需要两个关键数据：<b>世界法线</b>和<b>视线方向</b>，必须在同一个坐标系（世界空间）下计算。</div><div class="notion-text notion-block-34485e127d7c80a99590c273250afd06"><code class="notion-inline-code">v2f vert(appdata v) 
{
    v2f o;
    // 顶点位置：模型空间 → 裁剪空间（GPU 最终用这个绘制到屏幕）
    o.pos = UnityObjectToClipPos(v.vertex);
    // 法线：模型空间 → 世界空间（注意不能直接用矩阵乘，要用专用函数）
    o.worldNormal = UnityObjectToWorldNormal(v.normal);
    // 视线方向：从顶点指向摄像机，Unity 内置函数自动计算
    o.viewDir = WorldSpaceViewDir(v.vertex);
    // UV 传递（处理 Tiling/Offset）
    o.uv = TRANSFORM_TEX(v.uv, _MainTex);
    return o;
}</code></div><blockquote class="notion-quote notion-block-34485e127d7c80be8c97d703dfada11c"><div>为什么法线不能直接乘变换矩阵？因为模型做了非等比缩放时，用位置变换矩阵会让法线方向出错。<code class="notion-inline-code">UnityObjectToWorldNormal</code> 内部用了转置逆矩阵，专门解决这个问题。</div></blockquote><hr class="notion-hr notion-block-34485e127d7c809187c3feb52a18049f"/><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-34485e127d7c8077ad4cf12bbfde7602" data-id="34485e127d7c8077ad4cf12bbfde7602"><span><div id="34485e127d7c8077ad4cf12bbfde7602" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34485e127d7c8077ad4cf12bbfde7602" title="五、第三步：片段着色器——计算 Rim 值"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">五、第三步：片段着色器——计算 Rim 值</span></span></h3><div class="notion-text notion-block-34485e127d7c80889286efc961d329a7"><code class="notion-inline-code">fixed4 frag(v2f i) : SV_Target 
{
    fixed4 baseColor = tex2D(_MainTex, i.uv);
    
    float rim = pow(1 - saturate(dot(normalize(i.worldNormal), normalize(i.viewDir))), _RimPower);
    
    return baseColor + rim * _RimColor;
}</code></div><div class="notion-text notion-block-34485e127d7c804392a7d0237db9e0ac"><code class="notion-inline-code">saturate</code> 把点积结果夹在 [0, 1] 之间，防止出现负数导致颜色异常。<code class="notion-inline-code">normalize</code> 确保向量是单位长度，否则点积不代表角度关系。</div><div class="notion-text notion-block-34485e127d7c8038921dd91f9fa1a651">这一步已经能看到边缘光效果了，但边缘比较生硬。</div><hr class="notion-hr notion-block-34485e127d7c801ab58cfb3ed21b46d5"/><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-34485e127d7c80f890f7ffcbad6c84b0" data-id="34485e127d7c80f890f7ffcbad6c84b0"><span><div id="34485e127d7c80f890f7ffcbad6c84b0" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34485e127d7c80f890f7ffcbad6c84b0" title="六、第四步：加入柔化层，模拟外发光扩散"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">六、第四步：加入柔化层，模拟外发光扩散</span></span></h3><div class="notion-text notion-block-34485e127d7c8079aad1e3ebcf3afe71">真实的发光效果不只是一条锐利的线，还有向外扩散的柔光晕。用两层叠加来模拟：</div><div class="notion-text notion-block-34485e127d7c80b9a7d3c75e891ccef9"><code class="notion-inline-code">float rimSharp = pow(rim, _RimPower);          // 锐利内层
float rimSoft  = pow(rim, _RimPower * 0.25);   // 柔化外层，指数更小 → 覆盖范围更宽
return baseColor + rimSharp * _RimColor + rimSoft * _RimColor * 0.2;</code></div><div class="notion-text notion-block-34485e127d7c801697dbd5863e817ed7"><code class="notion-inline-code">_RimPower * 0.25</code> 让指数更小，<code class="notion-inline-code">pow</code> 曲线更平缓，发光范围更宽。<code class="notion-inline-code">* 0.2</code> 让外层强度只有内层的五分之一，形成自然的扩散晕染感。</div><div class="notion-text notion-block-34485e127d7c801d8b11d5653505b353">两层叠加后，效果变成：核心处有一条亮线，外侧有柔和光晕，视觉上像是真的在发光。</div><hr class="notion-hr notion-block-34485e127d7c80509ce3d02c3894e192"/><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-34485e127d7c80829984ebdd4ff92f54" data-id="34485e127d7c80829984ebdd4ff92f54"><span><div id="34485e127d7c80829984ebdd4ff92f54" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34485e127d7c80829984ebdd4ff92f54" title="七、第五步：加 Post-Processing Bloom，让光晕更真实"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">七、第五步：加 Post-Processing Bloom，让光晕更真实</span></span></h3><div class="notion-text notion-block-34485e127d7c80b7b0e7c4dc8d244ccd">Rim Light 本质是在模型材质上加颜色，但颜色再亮也只是&quot;亮色&quot;，不会真的像光一样往外扩散。要做到&quot;往外溢出&quot;的感觉，需要后处理。</div><div class="notion-text notion-block-34485e127d7c8015adebe24faec8feac"><b>配置步骤：</b></div><ol start="1" class="notion-list notion-list-numbered notion-block-34485e127d7c8096977de45d8859cd86"><li>Package Manager 安装 <code class="notion-inline-code">Post Processing</code> 包</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-34485e127d7c808998e2f80b20851d1a"><li>在场景的 Main Camera 上加 <code class="notion-inline-code">Post-process Layer</code> 组件，Layer 选 <code class="notion-inline-code">PostProcessing</code></li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-34485e127d7c8025b667f645bd9e7163"><li>新建一个空物体，Layer 设为 <code class="notion-inline-code">PostProcessing</code>，加 <code class="notion-inline-code">Post-process Volume</code> 组件，勾选 <b>Is Global</b></li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-34485e127d7c8092ab04c18c53790533"><li>在 Volume 里新建 Profile，添加 <b>Bloom</b> 效果</li></ol><div class="notion-text notion-block-34485e127d7c80e9956dd00665fdb42c"><b>Bloom 参数配置：</b></div><table class="notion-simple-table notion-block-34485e127d7c80b2b382dff6f79080fe"><tbody><tr class="notion-simple-table-row notion-block-34485e127d7c80b4b418ef831ca561f8"><td class="" style="width:120px"><div class="notion-simple-table-cell">参数</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">值</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">说明</div></td></tr><tr class="notion-simple-table-row notion-block-34485e127d7c80e19a53f3e2c9233fb3"><td class="" style="width:120px"><div class="notion-simple-table-cell">Intensity</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">6</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">发光强度，值越大扩散越明显</div></td></tr><tr class="notion-simple-table-row notion-block-34485e127d7c805fa4bcd978fa086970"><td class="" style="width:120px"><div class="notion-simple-table-cell">Threshold</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">默认</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">超过此亮度的像素才触发 Bloom</div></td></tr></tbody></table><div class="notion-text notion-block-34485e127d7c80828994e5a47c92e304">Bloom 的原理：把画面中超过阈值的高亮像素做模糊处理，再叠回原画面，产生光溢出的感觉。配合 Rim Light 的高亮边缘，效果会非常自然。</div><hr class="notion-hr notion-block-34485e127d7c806cb6e2cc585f153ce0"/><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-34485e127d7c80959ea7f403ce1c3d23" data-id="34485e127d7c80959ea7f403ce1c3d23"><span><div id="34485e127d7c80959ea7f403ce1c3d23" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34485e127d7c80959ea7f403ce1c3d23" title="八、总结：学到了什么"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">八、总结：学到了什么</span></span></h3><div class="notion-text notion-block-34485e127d7c8077ab88d70d7cb256a7"><b>核心原理：</b> 法线和视线的点积是边缘光的数学基础，本质是判断&quot;这个面有多斜对着摄像机&quot;。这个思路可以扩展到几乎所有方向相关的视觉效果。</div><div class="notion-text notion-block-34485e127d7c807c8a58e28ad25399d5"><b>两个坐标系的重要性：</b> 法线、视线、光线的计算必须在同一个坐标系下进行，不然方向对不上。世界空间是最常用的统一坐标系。</div><div class="notion-text notion-block-34485e127d7c803492efd4948ae43441"><b>分层叠加的思路：</b> 一个效果不够自然时，可以用多层叠加——主层精确，辅助层柔化——而不是去改主层的公式。这个思路在很多 Shader 里都适用。</div><div class="notion-text notion-block-34485e127d7c804e8471ee616e85e1ad"><b>材质 + 后处理的配合：</b> Shader 负责&quot;像素该是什么颜色&quot;，Post-Processing 负责&quot;整个画面该怎么处理&quot;。两者配合才能做出最完整的视觉效果。</div><hr class="notion-hr notion-block-34485e127d7c8042a411d08b4294bb42"/><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-34485e127d7c80f4be6cc4a0bfc8c6bb" data-id="34485e127d7c80f4be6cc4a0bfc8c6bb"><span><div id="34485e127d7c80f4be6cc4a0bfc8c6bb" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34485e127d7c80f4be6cc4a0bfc8c6bb" title="九、发散：还能延伸到哪里"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">九、发散：还能延伸到哪里</span></span></h3><div class="notion-text notion-block-34485e127d7c80428120e4c2ac87df02"><b>方向1 - 让发光颜色动起来</b>
用 <code class="notion-inline-code">_Time</code> 控制颜色随时间变化，或者接入脚本动态修改 <code class="notion-inline-code">_RimColor</code>，做出能量护盾充能、受伤闪光等效果。</div><div class="notion-text notion-block-34485e127d7c808bb535c834501bc61e"><b>方向2 - 基于深度的外描边</b>
Rim Light 是&quot;从材质内部算边缘&quot;，还有另一种思路是&quot;用两个 Pass，第二个 Pass 把模型放大一点，只画背面&quot;，可以做出更均匀的卡通描边效果。</div><div class="notion-text notion-block-34485e127d7c80f7a83dcfe841171173"><b>方向3 - Bloom 阈值联动</b>
配合 HDR 颜色，让 Rim 颜色的亮度超过 1（比如 <code class="notion-inline-code">(2, 2, 4, 1)</code>），Bloom 的 Threshold 卡在 1 附近，只让边缘光触发 Bloom，其他区域不受影响，效果更精准。</div><div class="notion-text notion-block-34485e127d7c802f8d57e2eaad819395"><b>方向4 - URP 迁移</b>
Built-in 管线的 <code class="notion-inline-code">CGPROGRAM</code> 在 URP 里要改成 <code class="notion-inline-code">HLSLPROGRAM</code>，<code class="notion-inline-code">UnityCG.cginc</code> 换成 <code class="notion-inline-code">Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl</code>，思路完全一样，只是 API 名称不同。</div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[UnityShader-迷雾效果]]></title>
            <link>https://lazy-zed.com/article/u3d_6</link>
            <guid>https://lazy-zed.com/article/u3d_6</guid>
            <pubDate>Sat, 14 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[ 本文通过阅读一个开源战争迷雾实现，拆解其核心设计：Color32 双通道三态编码、斜率近似的锥形遮挡算法、CPU-GPU 双缓冲渲染管线，以及帧间 Lerp 平滑过渡。在理解与思考的基础上，用更易读的 byte 数组 + DDA 射线 + 后处理叠加方案实现了自己的版本，并总结了数据分层、遮挡算法权衡、帧间插值等通用思路，最后发散到 Compute Shader 并行、动态障碍物、多单位合并等进阶方向

]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-3b66d17ef2114ea78e135a497d847311"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-34485e127d7c8087b43aeb49f3dda3b4" data-id="34485e127d7c8087b43aeb49f3dda3b4"><span><div id="34485e127d7c8087b43aeb49f3dda3b4" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34485e127d7c8087b43aeb49f3dda3b4" title="一、为什么迷雾效果值得深入研究"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">一、为什么迷雾效果值得深入研究</span></span></h3><div class="notion-text notion-block-34485e127d7c80328294e844eb32d88c">战争迷雾（Fog of War）是策略游戏里最经典的机制之一：地图一开始是黑的，角色走过的地方变灰，当前视野范围内才能看清。</div><div class="notion-text notion-block-34485e127d7c80c4a1bae02ba4b3b6a5">这个效果表面上很简单，实现起来涉及的问题出乎意料地多：视野怎么算？障碍物遮挡怎么处理？探索历史怎么保存？画面怎么平滑过渡？</div><div class="notion-text notion-block-34485e127d7c800bb3d5e18fcfc9a33d">我读了 <a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://github.com/QinZhuo/FogOfWar_ForUnity">FogOfWar_ForUnity</a> 这个开源仓库，把它的设计过了一遍，理解了大部分思路，但也发现一些可以换个方式做的地方——于是决定自己写一个版本。</div><hr class="notion-hr notion-block-34485e127d7c805c8feae3a1c2b7644d"/><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-34485e127d7c80d5a512fefb060fda34" data-id="34485e127d7c80d5a512fefb060fda34"><span><div id="34485e127d7c80d5a512fefb060fda34" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34485e127d7c80d5a512fefb060fda34" title="二、读懂这个仓库：它是怎么做的"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">二、读懂这个仓库：它是怎么做的</span></span></h3><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-34485e127d7c80e982efd2d2ccde8ae5" data-id="34485e127d7c80e982efd2d2ccde8ae5"><span><div id="34485e127d7c80e982efd2d2ccde8ae5" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34485e127d7c80e982efd2d2ccde8ae5" title="数据层：用 Color32 的两个通道编码三种状态"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">数据层：用 Color32 的两个通道编码三种状态</span></span></h4><div class="notion-text notion-block-34485e127d7c80fba3fed0a2e8ce7296">迷雾本质上是一个覆盖全地图的二维数组，每个格子存一个状态：</div><table class="notion-simple-table notion-block-34485e127d7c804da5d3eecf9bc8801c"><tbody><tr class="notion-simple-table-row notion-block-34485e127d7c801fb9d8e2d9c26e937e"><td class="" style="width:120px"><div class="notion-simple-table-cell">状态</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">含义</div></td></tr><tr class="notion-simple-table-row notion-block-34485e127d7c8099882be498c195c992"><td class="" style="width:120px"><div class="notion-simple-table-cell">未探索</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">完全黑暗</div></td></tr><tr class="notion-simple-table-row notion-block-34485e127d7c803f93effb4902ca341c"><td class="" style="width:120px"><div class="notion-simple-table-cell">已探索但不可见</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">半透明灰雾</div></td></tr><tr class="notion-simple-table-row notion-block-34485e127d7c8048a85ef61e21cdff34"><td class="" style="width:120px"><div class="notion-simple-table-cell">当前可见</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">完全透明</div></td></tr></tbody></table><div class="notion-text notion-block-34485e127d7c800ea502e4915e202f3f">这个仓库用 <code class="notion-inline-code">Color32</code> 的两个通道编码这三种状态：</div><ul class="notion-list notion-list-disc notion-block-34485e127d7c802085cbe529a1f83104"><li><code class="notion-inline-code">r == 255</code>：当前帧可见</li></ul><ul class="notion-list notion-list-disc notion-block-34485e127d7c804aa8f7c0156fdc4585"><li><code class="notion-inline-code">b == 255</code>：历史上曾经可见（已探索）</li></ul><div class="notion-text notion-block-34485e127d7c80249e80d45e84ef4886">每帧开始时把 <code class="notion-inline-code">r</code> 清零，保留 <code class="notion-inline-code">b</code>，这样已探索信息永不丢失。<b>这个设计很精妙</b>——用一个数组同时管历史状态和当前帧状态，省了一个数组。</div><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-34485e127d7c804ca3a5c256e39d7b49" data-id="34485e127d7c804ca3a5c256e39d7b49"><span><div id="34485e127d7c804ca3a5c256e39d7b49" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34485e127d7c804ca3a5c256e39d7b49" title="视野层：圆形范围 + 障碍物锥形遮挡"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">视野层：圆形范围 + 障碍物锥形遮挡</span></span></h4><div class="notion-text notion-block-34485e127d7c8045af29dd65c506ac2f">先把圆形范围内所有格标记为可见，然后找出范围内的障碍物，从每个障碍物向后投射&quot;阴影锥&quot;，把被遮挡的格取消可见。</div><div class="notion-text notion-block-34485e127d7c80b4bd69f660cc76740a">遮挡判断没有用 Raycast，而是用<b>斜率近似角度</b>：</div><div class="notion-text notion-block-34485e127d7c80f09c30f63416401791"><code class="notion-inline-code">var k1 = y1 * 1f / x1;  // 障碍物方向斜率
var k2 = y2 * 1f / x2;  // 目标格方向斜率
float z = Mathf.PI / (6 + distance / 1.2f);  // 遮挡角随距离缩小
return Angle(k1, k2) &lt; z;  // 角度差 &lt; 遮挡角 → 被遮挡</code></div><div class="notion-text notion-block-34485e127d7c804fa4caeee350df1373">这样做的好处是避免三角函数开销，性能更好。代价是边缘处有近似误差。</div><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-34485e127d7c80269935e6b00547f91b" data-id="34485e127d7c80269935e6b00547f91b"><span><div id="34485e127d7c80269935e6b00547f91b" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34485e127d7c80269935e6b00547f91b" title="渲染层：CPU → GPU → 模糊 → 帧间插值"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">渲染层：CPU → GPU → 模糊 → 帧间插值</span></span></h4><div class="notion-text notion-block-34485e127d7c808b99c3ee7d0343cfe2">整条渲染管线：</div><ol start="1" class="notion-list notion-list-numbered notion-block-34485e127d7c80dd95c2d19734d3d0f0"><li>CPU 算出每格的 alpha 值（透明/半透明/不透明）</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-34485e127d7c8011a834e557a332dbb7"><li>上传到 Texture2D，再 Blit 做三次 3×3 高斯模糊，让边缘柔和</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-34485e127d7c80749269ea014dbbde75"><li>每帧 <code class="notion-inline-code">lerp(上一帧纹理, 本帧纹理, 0.05)</code>，让迷雾扩散/消散有平滑动画</li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-34485e127d7c802ab5bfe18200344641"><li>把最终纹理贴到一个铺在场景上方的透明 Plane</li></ol><hr class="notion-hr notion-block-34485e127d7c8028afc0d3776123c0a1"/><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-34485e127d7c80fc92d8fa5fa3d7379f" data-id="34485e127d7c80fc92d8fa5fa3d7379f"><span><div id="34485e127d7c80fc92d8fa5fa3d7379f" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34485e127d7c80fc92d8fa5fa3d7379f" title="三、我的思考：哪里可以做得不一样"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">三、我的思考：哪里可以做得不一样</span></span></h3><div class="notion-text notion-block-34485e127d7c80bf9aefdc39095d2f00"><b>问题一：斜率近似的误差</b></div><div class="notion-text notion-block-34485e127d7c8088bec0f47975761010">斜率 <code class="notion-inline-code">k = y/x</code> 在 <code class="notion-inline-code">x = 0</code> 时会产生除零，作者用了 <code class="notion-inline-code">x1 == 0</code> 的特判，但整体逻辑比较难读。用角度（<code class="notion-inline-code">atan2</code>）更直观，现代 GPU 和 CPU 做三角函数的开销其实很小。</div><div class="notion-text notion-block-34485e127d7c80c0b8a2d0946a77641e"><b>问题二：渲染层用 Plane 覆盖</b></div><div class="notion-text notion-block-34485e127d7c803dab22d4a2da8c01e4">用一个 Plane 铺在场景上方，需要精确调整 Plane 的大小和 UV 映射，换场景时容易对不齐。更稳的方式是用后处理（Post-Processing）直接在屏幕空间叠加迷雾纹理，不依赖世界坐标的对齐。</div><div class="notion-text notion-block-34485e127d7c80889a5dd27faed161eb"><b>问题三：视野更新用定时器（0.5s 一次）</b></div><div class="notion-text notion-block-34485e127d7c80899c23d15a5a0cb858">这能省开销，但在角色快速移动时会有明显延迟感。可以用脏标记（dirty flag）：角色移动时标记需要重算，下一帧再更新，既不丢精度又避免每帧都算。</div><hr class="notion-hr notion-block-34485e127d7c8040964debbe271b2b14"/><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-34485e127d7c80448bdef33ebe7fc2b7" data-id="34485e127d7c80448bdef33ebe7fc2b7"><span><div id="34485e127d7c80448bdef33ebe7fc2b7" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34485e127d7c80448bdef33ebe7fc2b7" title="四、自己实现：从零搭一个战争迷雾"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">四、自己实现：从零搭一个战争迷雾</span></span></h3><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-34485e127d7c80dca332e0c63dd70076" data-id="34485e127d7c80dca332e0c63dd70076"><span><div id="34485e127d7c80dca332e0c63dd70076" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34485e127d7c80dca332e0c63dd70076" title="整体设计"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">整体设计</span></span></h4><div class="notion-text notion-block-34485e127d7c8055977ae0bf5dae56ad"><code class="notion-inline-code">[FogMap]        → 地图数据，每格存三态（byte数组）
[FogViewer]     → 挂在角色上，提供位置和视野半径
[FogManager]    → 每帧协调更新，写数据、上传纹理
[FogShader]     → 后处理叠加，根据屏幕 UV 采样迷雾纹理</code></div><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-34485e127d7c804989d3e6318589e2bd" data-id="34485e127d7c804989d3e6318589e2bd"><span><div id="34485e127d7c804989d3e6318589e2bd" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34485e127d7c804989d3e6318589e2bd" title="第一步：地图数据结构"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">第一步：地图数据结构</span></span></h4><div class="notion-text notion-block-34485e127d7c80dd8f00dd3e315a6f0c">用 <code class="notion-inline-code">byte</code> 数组，每格三种状态，比 Color32 更直观：</div><div class="notion-text notion-block-34485e127d7c808ea1c4eb21cb0aa1db"><code class="notion-inline-code">public class FogMap 
{
    public enum FogState : byte { Hidden = 0, Explored = 1, Visible = 2 }
    
    private FogState[] _states;
    private int _width, _height;
    
    public FogMap(int width, int height) 
    {
        _width = width;
        _height = height;
        _states = new FogState[width * height];
    }
    
    public FogState Get(int x, int y) =&gt; _states[y * _width + x];
    public void Set(int x, int y, FogState s) =&gt; _states[y * _width + x] = s;
    
    // 每帧开始：把所有 Visible 降级为 Explored（保留探索历史）
    public void BeginFrame() 
    {
        for (int i = 0; i &lt; _states.Length; i++)
            if (_states[i] == FogState.Visible)
                _states[i] = FogState.Explored;
    }
}</code></div><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-34485e127d7c80bbba08ff28f81b3c7f" data-id="34485e127d7c80bbba08ff28f81b3c7f"><span><div id="34485e127d7c80bbba08ff28f81b3c7f" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34485e127d7c80bbba08ff28f81b3c7f" title="第二步：视野计算——圆形范围 + DDA 射线"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">第二步：视野计算——圆形范围 + DDA 射线</span></span></h4><div class="notion-text notion-block-34485e127d7c800697f0def56f85dda0">DDA（Digital Differential Analyzer）是一种整数格子上的射线追踪算法，不用浮点三角函数，速度快且无歧义：</div><div class="notion-text notion-block-34485e127d7c80ac9b45e071dc398f21"><code class="notion-inline-code">public void ComputeVision(FogMap map, Vector2Int center, int radius)
{
    // 圆形范围内，向每个边缘格发出射线
    for (int angle = 0; angle &lt; 360; angle += 2)  // 每2度一条射线
    {
        float rad = angle * Mathf.Deg2Rad;
        float dx = Mathf.Cos(rad);
        float dy = Mathf.Sin(rad);
        
        float x = center.x, y = center.y;
        
        for (int step = 0; step &lt;= radius; step++)
        {
            int gx = Mathf.RoundToInt(x);
            int gy = Mathf.RoundToInt(y);
            
            if (gx &lt; 0 || gy &lt; 0 || gx &gt;= map.Width || gy &gt;= map.Height) break;
            
            map.Set(gx, gy, FogMap.FogState.Visible);
            
            // 遇到障碍物则停止这条射线
            if (IsObstacle(gx, gy)) break;
            
            x += dx;
            y += dy;
        }
    }
}</code></div><blockquote class="notion-quote notion-block-34485e127d7c807aa39bec48c878e0d4"><div>射线密度（每 2 度一条）根据视野半径调整。半径大时可能漏格，可以改为角度步长 <code class="notion-inline-code">= 1 / radius</code>（每格一条射线）来保证不漏。</div></blockquote><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-34485e127d7c8067925fdd22add0e408" data-id="34485e127d7c8067925fdd22add0e408"><span><div id="34485e127d7c8067925fdd22add0e408" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34485e127d7c8067925fdd22add0e408" title="第三步：把数据上传到纹理"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">第三步：把数据上传到纹理</span></span></h4><div class="notion-text notion-block-34485e127d7c8076a381e396992e9ef6"><code class="notion-inline-code">private Texture2D _fogTexture;
private Color32[] _pixelBuffer;

void UploadToTexture(FogMap map)
{
    for (int i = 0; i &lt; _pixelBuffer.Length; i++)
    {
        byte alpha = map.States[i] switch
        {
            FogMap.FogState.Visible  =&gt; 0,    // 透明，完全可见
            FogMap.FogState.Explored =&gt; 120,  // 半透明，灰雾
            _                        =&gt; 255   // 不透明，全黑
        };
        _pixelBuffer[i] = new Color32(0, 0, 0, alpha);
    }
    _fogTexture.SetPixels32(_pixelBuffer);
    _fogTexture.Apply();
}</code></div><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-34485e127d7c80d68c6ef60f244655a4" data-id="34485e127d7c80d68c6ef60f244655a4"><span><div id="34485e127d7c80d68c6ef60f244655a4" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34485e127d7c80d68c6ef60f244655a4" title="第四步：Shader——后处理叠加迷雾"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">第四步：Shader——后处理叠加迷雾</span></span></h4><div class="notion-text notion-block-34485e127d7c806c8b2de86eb1c2d022">用 Unity 的后处理框架，在屏幕空间叠加，不需要世界坐标对齐：</div><div class="notion-text notion-block-34485e127d7c80beb11af98c889b6f07"><code class="notion-inline-code">Shader &quot;Custom/FogOverlay&quot;
{
    Properties { _FogTex (&quot;迷雾纹理&quot;, 2D) = &quot;black&quot; {} }
    
    SubShader
    {
        Tags { &quot;Queue&quot; = &quot;Overlay&quot; &quot;RenderType&quot; = &quot;Transparent&quot; }
        ZTest Off
        ZWrite Off
        Blend SrcAlpha OneMinusSrcAlpha
        
        Pass
        {
            CGPROGRAM
            #pragma vertex vert_img
            #pragma fragment frag
            #include &quot;UnityCG.cginc&quot;
            
            sampler2D _FogTex;
            
            fixed4 frag(v2f_img i) : SV_Target
            {
                // 把摄像机视口 UV 映射到迷雾纹理 UV
                // 需要传入地图边界和摄像机位置来做偏移
                float2 fogUV = WorldToFogUV(i.uv);
                return tex2D(_FogTex, fogUV);
            }
            ENDCG
        }
    }
}</code></div><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-34485e127d7c80baac83dffe05713e9b" data-id="34485e127d7c80baac83dffe05713e9b"><span><div id="34485e127d7c80baac83dffe05713e9b" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34485e127d7c80baac83dffe05713e9b" title="第五步：帧间平滑过渡"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">第五步：帧间平滑过渡</span></span></h4><div class="notion-text notion-block-34485e127d7c80eaa972d04f40ca82a7">直接改纹理会让迷雾瞬间出现/消失，加一个 Lerp 让过渡更自然：</div><div class="notion-text notion-block-34485e127d7c80beb58bc23c2bf6f91b"><code class="notion-inline-code">private RenderTexture _currentFogRT;
private RenderTexture _targetFogRT;
private Material _lerpMat;  // 只有一行：lerp(_LastTex, _MainTex, _T)

void LerpFog()
{
    _lerpMat.SetTexture(&quot;_LastTex&quot;, _currentFogRT);
    _lerpMat.SetTexture(&quot;_MainTex&quot;, _targetFogRT);
    _lerpMat.SetFloat(&quot;_T&quot;, 0.08f);  // 比原仓库的 0.05 稍快，响应更及时
    
    var temp = RenderTexture.GetTemporary(_currentFogRT.descriptor);
    Graphics.Blit(null, temp, _lerpMat);
    Graphics.Blit(temp, _currentFogRT);
    RenderTexture.ReleaseTemporary(temp);
}</code></div><hr class="notion-hr notion-block-34485e127d7c807995dec706b3d9530b"/><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-34485e127d7c80b2ba1bf0a146f47a58" data-id="34485e127d7c80b2ba1bf0a146f47a58"><span><div id="34485e127d7c80b2ba1bf0a146f47a58" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34485e127d7c80b2ba1bf0a146f47a58" title="五、总结：学到了什么"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">五、总结：学到了什么</span></span></h3><div class="notion-text notion-block-34485e127d7c80fdbe21c91a2f2ca640"><b>数据设计影响一切后续逻辑。</b> 开源仓库用 Color32 双通道编码三态，聪明但难读；用独立 byte 数组更直观，也方便后续扩展（比如加&quot;部分可见&quot;状态）。没有绝对的对错，取决于优先考虑性能还是可维护性。</div><div class="notion-text notion-block-34485e127d7c80a6b78af9b8379e1bb3"><b>视野计算的核心是&quot;遮挡&quot;。</b> 圆形范围简单，障碍物遮挡才是难点。射线方法和锥形阴影各有取舍：射线更精确，锥形更快。理解这个权衡之后，遇到同类问题（光照、声音传播）都能用类似思路处理。</div><div class="notion-text notion-block-34485e127d7c80b7a5b4deda74e1f1c4"><b>渲染和数据要分层。</b> 数据层（byte 数组）负责状态正确，渲染层（纹理 + Shader）负责视觉效果。两层解耦之后，可以独立优化——比如把数据计算挪到 ComputeShader，而不需要改渲染逻辑。</div><div class="notion-text notion-block-34485e127d7c80daa77cdb0b6301f25d"><b>帧间插值是廉价的视觉提升。</b> 一行 <code class="notion-inline-code">lerp</code> 让硬切变成平滑过渡，开销几乎为零，但视觉质量提升明显。这种&quot;用时间换空间感&quot;的思路在游戏开发里到处适用。</div><hr class="notion-hr notion-block-34485e127d7c80f28b85d0d678629ec0"/><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-34485e127d7c8011af05d82ed03688a7" data-id="34485e127d7c8011af05d82ed03688a7"><span><div id="34485e127d7c8011af05d82ed03688a7" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34485e127d7c8011af05d82ed03688a7" title="六、发散：还能延伸到哪里"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">六、发散：还能延伸到哪里</span></span></h3><div class="notion-text notion-block-34485e127d7c8018b951d9b72928b8a9"><b>方向1 - Compute Shader 并行计算</b>
视野的每条射线相互独立，天然适合并行。把视野计算放到 Compute Shader，几千条射线同时算，对大地图的性能提升非常显著。</div><div class="notion-text notion-block-34485e127d7c80a4a2dfc5b20904a38d"><b>方向2 - 动态障碍物</b>
现在的实现假设障碍物是静态的（初始化时用物理检测）。如果要支持动态障碍物（比如可移动的箱子），需要每帧重建障碍物 mask，或者用事件驱动的增量更新。</div><div class="notion-text notion-block-34485e127d7c808e8ff9d8793bde0e93"><b>方向3 - 多单位视野合并</b>
多个单位的视野取并集。可以让每个单位独立维护一张迷雾纹理，GPU 上用 <code class="notion-inline-code">max</code> 操作合并；也可以在 CPU 上用位运算合并 byte 数组。位运算版本：<code class="notion-inline-code">state[i] = max(stateA[i], stateB[i])</code>，一行搞定。</div><div class="notion-text notion-block-34485e127d7c80958723dd412d296e31"><b>方向4 - 视野形状扩展</b>
除了圆形视野，还可以做扇形（带朝向的前方视野，适合潜行游戏）、矩形（摄像机视角，适合监控场景）。只需要修改&quot;哪些格子在视野内&quot;的筛选条件，遮挡逻辑完全不用改。</div><div class="notion-text notion-block-34485e127d7c806a82fffc7d6b068da0"><b>方向5 - 接入 AI</b>
把迷雾数据暴露给 AI 系统：AI 单位只能&quot;看到&quot;当前可见格里的敌人，已探索区域可以记忆上次看到的位置。迷雾不只是视觉效果，还是信息层。</div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ECS-数据导向]]></title>
            <link>https://lazy-zed.com/article/u3d_15</link>
            <guid>https://lazy-zed.com/article/u3d_15</guid>
            <pubDate>Fri, 13 Dec 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[DOTS的ECS

]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-19a85e127d7c801f8805e8fe459d132d"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><div class="notion-callout notion-gray_background_co notion-block-19a85e127d7c815892e2eba9b88b6ba7"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="😀">😀</span></div><div class="notion-callout-text">OOP大部分人都很熟悉，那DOP呢，其实也就是object变成了data，但DOTS（多线程式数据导向型技术堆栈。）的核心思想就是DOP，这里主要不说DOTS，因为我自己也没用过，但其核心组成就是ECS挺有意思，有点想学，但目前还只停留在了解的阶段。</div></div><hr class="notion-hr notion-block-19a85e127d7c80179f1bf0aa46f4c60c"/><h4 class="notion-h notion-h3 notion-h-indent-0 notion-block-19a85e127d7c80b4b4f4c2d4d0b04e22" data-id="19a85e127d7c80b4b4f4c2d4d0b04e22"><span><div id="19a85e127d7c80b4b4f4c2d4d0b04e22" class="notion-header-anchor"></div><a class="notion-hash-link" href="#19a85e127d7c80b4b4f4c2d4d0b04e22" title="ECS（Entity-Component-System）"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">ECS（Entity-Component-System）</span></span></h4><div class="notion-text notion-block-19f85e127d7c80e4aeabe50b73b6ed47">ECS是一种常见的游戏开发架构模式，主要用于管理游戏对象（Entities）、数据（Components）和行为（Systems）。ECS的核心思想是将数据和行为分离，以提高代码的灵活性、可维护性和性能.</div><ul class="notion-list notion-list-disc notion-block-19f85e127d7c80679fa2f9993e7be567"><li><b>Entity（实体）</b>：实体是游戏中的基本对象，通常只是一个唯一的标识符（如整数ID）。实体本身不包含任何数据或行为，它只是一个“容器”，用于关联组件。</li></ul><ul class="notion-list notion-list-disc notion-block-19f85e127d7c8038948cf758fad86e9a"><li><b>Component（组件）</b>：组件是纯粹的数据结构，用于描述实体的某个属性或状态。例如，一个“位置”组件可能包含 <code class="notion-inline-code">x</code> 和 <code class="notion-inline-code">y</code> 坐标，一个“生命值”组件可能包含当前生命值和最大生命值。一个实体可以拥有多个组件。</li></ul><ul class="notion-list notion-list-disc notion-block-19f85e127d7c80979c50ca0c22216598"><li><b>System（系统）</b>：系统是处理逻辑的部分。每个系统负责处理一组具有特定组件的实体。例如，“渲染系统”会处理所有具有“位置”和“渲染”组件的实体，并将它们绘制到屏幕上。</li></ul><hr class="notion-hr notion-block-19a85e127d7c8097b7cfe4ac0cc6cc43"/><h4 class="notion-h notion-h3 notion-h-indent-0 notion-block-19a85e127d7c80d0850ce1e496678891" data-id="19a85e127d7c80d0850ce1e496678891"><span><div id="19a85e127d7c80d0850ce1e496678891" class="notion-header-anchor"></div><a class="notion-hash-link" href="#19a85e127d7c80d0850ce1e496678891" title="ECS的原理"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>ECS的原理</b></span></span></h4><div class="notion-text notion-block-19f85e127d7c801b97e3f103d1e1b0fc">ECS是一种常见的游戏开发架构模式，主要用于管理游戏对象（Entities）、数据（Components）和行为（Systems）。ECS的核心思想是将数据和行为分离，以提高代码的灵活性、可维护性和性能.</div><ol start="1" class="notion-list notion-list-numbered notion-block-19f85e127d7c80198000efdb18ba4398"><li><b>创建实体</b>：首先创建一个实体，通常只是一个唯一的ID。</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-19f85e127d7c80849fe5f908c83ec6ce"><li><b>添加组件</b>：为实体添加所需的组件。例如，为一个玩家实体添加“位置”、“生命值”、“渲染”等组件。</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-19f85e127d7c8069a5f0d2566a5004db"><li><b>系统处理</b>：系统会遍历所有具有特定组件的实体，并执行相应的逻辑。例如，“移动系统”会遍历所有具有“位置”和“速度”组件的实体，并根据速度更新它们的位置。</li></ol><hr class="notion-hr notion-block-19a85e127d7c80febb8ed79d9538bb12"/><h4 class="notion-h notion-h3 notion-h-indent-0 notion-block-19a85e127d7c80248afbfbc47f5ac276" data-id="19a85e127d7c80248afbfbc47f5ac276"><span><div id="19a85e127d7c80248afbfbc47f5ac276" class="notion-header-anchor"></div><a class="notion-hash-link" href="#19a85e127d7c80248afbfbc47f5ac276" title="简单的ECS示例"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">简单的ECS示例</span></span></h4><hr class="notion-hr notion-block-19a85e127d7c8098b111f8aa7e73bafa"/><h4 class="notion-h notion-h3 notion-h-indent-0 notion-block-1ae85e127d7c80e2a73feda23e4c5bd0" data-id="1ae85e127d7c80e2a73feda23e4c5bd0"><span><div id="1ae85e127d7c80e2a73feda23e4c5bd0" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ae85e127d7c80e2a73feda23e4c5bd0" title="核心设计说明："><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>核心设计说明：</b></span></span></h4><ol start="1" class="notion-list notion-list-numbered notion-block-1ae85e127d7c80439eecc205a059ba7e"><li><b>组件 (Component)</b></li></ol><ul class="notion-list notion-list-disc notion-block-1ae85e127d7c80ef8b15c7bae2d34583"><li>使用结构体存储数据（值类型更符合ECS内存友好特性）</li></ul><ul class="notion-list notion-list-disc notion-block-1ae85e127d7c80148bf2e13dce69d5b1"><li>通过 <code class="notion-inline-code">IComponent</code> 接口标记组件类型</li></ul><ol start="1" class="notion-list notion-list-numbered notion-block-1ae85e127d7c80699cd4cb2dc470d8c3"><li><b>实体 (Entity)</b></li></ol><ul class="notion-list notion-list-disc notion-block-1ae85e127d7c80dfadf1f76aa2d7d20b"><li>使用字典存储组件集合</li></ul><ul class="notion-list notion-list-disc notion-block-1ae85e127d7c801c85d6fe3c0bc4bd85"><li>提供组件查询的泛型方法</li></ul><ol start="1" class="notion-list notion-list-numbered notion-block-1ae85e127d7c809293d8c24b6771556a"><li><b>系统 (System)</b></li></ol><ul class="notion-list notion-list-disc notion-block-1ae85e127d7c80dd8744f0e0a07065e2"><li>通过继承抽象类实现具体逻辑</li></ul><ul class="notion-list notion-list-disc notion-block-1ae85e127d7c80e8a84ff6fed5e5dab4"><li>在Update中筛选具有特定组件组合的实体</li></ul><ol start="1" class="notion-list notion-list-numbered notion-block-1ae85e127d7c801a8bd7c55541e18447"><li><b>世界 (World)</b></li></ol><ul class="notion-list notion-list-disc notion-block-1ae85e127d7c8031a207daa62e25c1af"><li>统一管理所有实体和系统</li></ul><ul class="notion-list notion-list-disc notion-block-1ae85e127d7c80949c74ec4b6fce4951"><li>驱动所有系统的更新</li></ul><div class="notion-blank notion-block-19a85e127d7c812e9c3ecd43b03ed7f5"> </div><div class="notion-callout notion-gray_background_co notion-block-19a85e127d7c816081e0d7d5a2b8745b"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="💡">💡</span></div><div class="notion-callout-text">感觉ECS其实比较适合大型复杂的游戏，尤其是有大量的游戏对象的项目，可以用ecs管理其复杂的交互逻辑，同时也适合需要高性能和高灵活性的项目，总之这套开发架构很好用，不过有上手成本，作者本人也还在摸索中~</div></div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[给 NotionNext 博客接入 Dify AI 助手：从原理到避坑全记录]]></title>
            <link>https://lazy-zed.com/article/AI_5</link>
            <guid>https://lazy-zed.com/article/AI_5</guid>
            <pubDate>Wed, 18 Jun 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[本文记录了在 NotionNext 博客中集成 Dify AI 对话助手的完整过程，解释了 Notion 作为配置中心、Vercel 作为部署平台的工作原理，详细说明了 Dify 知识库的配置方式，并重点记录了三个实际踩坑：配置缓存未刷新、BASE_URL 地址混用、以及 siteConfig 读取链在特定渲染时机失效的根因与解决方案码]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-19885e127d7c8090b2f9d59fd470ae2a"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><div class="notion-callout notion-gray_background_co notion-block-19885e127d7c80ccbdb8f04851cc3df5"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="😀">😀</span></div><div class="notion-callout-text"><h3 class="notion-h notion-h2 notion-block-34485e127d7c80d6a0f5c5ba4a3101d4" data-id="34485e127d7c80d6a0f5c5ba4a3101d4"><span><div id="34485e127d7c80d6a0f5c5ba4a3101d4" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34485e127d7c80d6a0f5c5ba4a3101d4" title="背景：先搞清楚架构"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">背景：先搞清楚架构</span></span></h3><div class="notion-text notion-block-34485e127d7c80218428cc6de85e2399">在开始之前，需要理解 NotionNext 博客的运作方式，否则后面的配置步骤会让人摸不着头脑。</div><div class="notion-text notion-block-34485e127d7c805c9b38f0a0a455caf3"><b>Notion 的角色</b></div><div class="notion-text notion-block-34485e127d7c801db9b7c78af9971170">Notion 在这套方案里不只是写文章的地方，它同时承担了三件事：</div><ul class="notion-list notion-list-disc notion-block-34485e127d7c804e8eace2229feea431"><li><b>内容数据库</b>：所有博客文章、页面都存在 Notion 数据库里</li></ul><ul class="notion-list notion-list-disc notion-block-34485e127d7c80af8fe3e3140f36bf0d"><li><b>配置中心</b>：博客的各种开关、参数（比如主题、评论系统、AI 配置）也存在一个 type 为 <code class="notion-inline-code">Config</code> 的 Notion 页面里</li></ul><ul class="notion-list notion-list-disc notion-block-34485e127d7c80ae9cd8c15ca31ca7fc"><li><b>数据源</b>：每次有人访问博客，服务器会通过 Notion 私有 API 拉取最新数据</li></ul><div class="notion-text notion-block-34485e127d7c801bbb73fbb78bc00d99"><b>Vercel 的角色</b></div><div class="notion-text notion-block-34485e127d7c800aad86e4015b5a1a82">Vercel 是部署平台，负责把 GitHub 仓库里的 Next.js 代码编译并运行。它的工作方式：</div><ul class="notion-list notion-list-disc notion-block-34485e127d7c80eb82a0eb8f9e13847f"><li>每次你向 GitHub 推送代码，Vercel 自动重新构建部署</li></ul><ul class="notion-list notion-list-disc notion-block-34485e127d7c80aba796f94fa12d0592"><li>支持环境变量（类似 <code class="notion-inline-code">.env</code> 文件），注入到构建过程中</li></ul><ul class="notion-list notion-list-disc notion-block-34485e127d7c80dba991d2b00305db25"><li>使用 ISR（增量静态再生）：页面第一次被访问时生成静态 HTML，之后定时从 Notion 拉取最新数据刷新缓存</li></ul><div class="notion-text notion-block-34485e127d7c8060a7adc31945592357"><b>为什么配置有两个地方</b></div><div class="notion-text notion-block-34485e127d7c80be9b25c1133c864195">这是很多人困惑的地方。NotionNext 的配置读取优先级是：</div><div class="notion-text notion-block-34485e127d7c80638797f8f148af3bab"><code class="notion-inline-code">Notion 配置页 &gt; Vercel 环境变量 &gt; blog.config.js 默认值</code></div><div class="notion-text notion-block-34485e127d7c803290f4ec463298f522">简单说：Notion 配置页优先级最高，Vercel 环境变量次之。两个地方都可以写，但有些配置在特定时机只能读到其中一个，这是后面踩坑的根源。</div></div></div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-34485e127d7c800986d0e2aa5002a4ee" data-id="34485e127d7c800986d0e2aa5002a4ee"><span><div id="34485e127d7c800986d0e2aa5002a4ee" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34485e127d7c800986d0e2aa5002a4ee" title="为什么选 Dify"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">为什么选 Dify</span></span></h3><div class="notion-text notion-block-34485e127d7c80d18b43e6dab85c2e92">NotionNext 内置了五种 AI 组件：Dify、Coze、Chatbase、WebWhiz、TianliGPT。</div><div class="notion-text notion-block-34485e127d7c80538735eaa13b89f811">选 Dify 的理由：</div><ul class="notion-list notion-list-disc notion-block-34485e127d7c809ba72be6e1aba5f00f"><li>免费版支持<b>知识库 + URL 爬取</b>，可以让 AI 读懂你的博客文章</li></ul><ul class="notion-list notion-list-disc notion-block-34485e127d7c80fdacd7d09e21a93ade"><li>可以选底层模型（接 Claude、GPT、国产模型均可）</li></ul><ul class="notion-list notion-list-disc notion-block-34485e127d7c804691c1d90f5a74fdda"><li>博客代码里已内置 <code class="notion-inline-code">DifyChatbot.js</code> 组件，理论上只需填配置</li></ul><hr class="notion-hr notion-block-34485e127d7c80709cd7ed2889f56c1d"/><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-34485e127d7c80bfba4de62af47447a4" data-id="34485e127d7c80bfba4de62af47447a4"><span><div id="34485e127d7c80bfba4de62af47447a4" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34485e127d7c80bfba4de62af47447a4" title="完整接入步骤"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">完整接入步骤</span></span></h3><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-34485e127d7c80d6a229ea1443156771" data-id="34485e127d7c80d6a229ea1443156771"><span><div id="34485e127d7c80d6a229ea1443156771" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34485e127d7c80d6a229ea1443156771" title="第一步：在 Dify 创建应用"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">第一步：在 Dify 创建应用</span></span></h4><ol start="1" class="notion-list notion-list-numbered notion-block-34485e127d7c805f8143feddacc85be8"><li>访问 <a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://cloud.dify.ai/">cloud.dify.ai</a> 注册并登录</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-34485e127d7c8038861bc30a37fc21e1"><li>左侧点<b>工作室</b> → <b>创建应用</b> → 选 <b>Agent</b></li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-34485e127d7c806dbc3ac33558948f94"><li>填写系统提示词，让 AI 了解自己的定位，例如：</li><ol class="notion-list notion-list-numbered notion-block-34485e127d7c806dbc3ac33558948f94"><div class="notion-text notion-block-34485e127d7c80988e47da39002a181f"><code class="notion-inline-code">你是 [你的博客名] 的 AI 助手，熟悉博客的所有文章内容。
用中文回答用户问题，优先检索知识库中的相关文章。</code></div></ol></ol><ol start="4" class="notion-list notion-list-numbered notion-block-34485e127d7c808b89c1d7fdaeb0ccc9"><li>选择底层模型（免费额度内可用 GPT-3.5 或 Claude Haiku）</li></ol><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-34485e127d7c806994ebc8ba5b97a769" data-id="34485e127d7c806994ebc8ba5b97a769"><span><div id="34485e127d7c806994ebc8ba5b97a769" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34485e127d7c806994ebc8ba5b97a769" title="第二步：创建知识库并爬取博客"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">第二步：创建知识库并爬取博客</span></span></h4><ol start="1" class="notion-list notion-list-numbered notion-block-34485e127d7c803fae72d14b303a5559"><li>左侧点<b>知识库</b> → <b>创建知识库</b></li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-34485e127d7c801c92fadd4388a92790"><li>选择**「从网页同步」**</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-34485e127d7c8098bf15cb3e182a6e5a"><li>填入你的博客地址，开启爬取子页面</li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-34485e127d7c80e493e0fb9e092a3450"><li>等待爬取完成后，回到刚才创建的 Agent，在<b>工具</b>里添加这个知识库</li></ol><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-34485e127d7c80d28e9ff73b453e73c1" data-id="34485e127d7c80d28e9ff73b453e73c1"><span><div id="34485e127d7c80d28e9ff73b453e73c1" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34485e127d7c80d28e9ff73b453e73c1" title="第三步：获取 API Token"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">第三步：获取 API Token</span></span></h4><ol start="1" class="notion-list notion-list-numbered notion-block-34485e127d7c80cdbbc7f3d56032235a"><li>打开你的 Agent 应用</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-34485e127d7c8097b45afaba2f8d5fbf"><li>左侧菜单找**「API 访问」**</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-34485e127d7c80a69beceaf7ea0bb81b"><li>点**「创建密钥」**，生成格式为 <code class="notion-inline-code">app-xxxxxxxxxx</code> 的 Token，保存好</li></ol><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-34485e127d7c80c09a33c14d029abd2f" data-id="34485e127d7c80c09a33c14d029abd2f"><span><div id="34485e127d7c80c09a33c14d029abd2f" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34485e127d7c80c09a33c14d029abd2f" title="第四步：修改代码"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">第四步：修改代码</span></span></h4><div class="notion-text notion-block-34485e127d7c80fea13aced496533956">由于 NotionNext 的配置读取链在某些渲染时机会失效，最可靠的方式是<b>让 DifyChatbot 直接读取环境变量</b>，而不依赖 siteConfig 体系。</div><div class="notion-text notion-block-34485e127d7c80179c11d753d371be5b"><b>修改 </b><code class="notion-inline-code"><b>components/DifyChatbot.js</b></code>（完整替换）：</div><div class="notion-text notion-block-34485e127d7c807da575fd6232d03590"><code class="notion-inline-code">import { useEffect } from &#x27;react&#x27;

const TOKEN = process.env.NEXT_PUBLIC_DIFY_CHATBOT_TOKEN
const BASE_URL = process.env.NEXT_PUBLIC_DIFY_CHATBOT_BASE_URL || &#x27;https://udify.app&#x27;

export default function DifyChatbot() {
  useEffect(() =&gt; {
    if (!TOKEN) return

    window.difyChatbotConfig = { token: TOKEN, baseUrl: BASE_URL }

    const script = document.createElement(&#x27;script&#x27;)
    script.src = `${BASE_URL}/embed.min.js`
    script.id = TOKEN
    script.defer = true
    document.body.appendChild(script)

    return () =&gt; {
      document.getElementById(TOKEN)?.remove()
    }
  }, [])

  return null
}</code></div><div class="notion-text notion-block-34485e127d7c8012b725fa7fe57c84eb"><b>修改 </b><code class="notion-inline-code"><b>components/ExternalPlugins.js</b></code> 一行：</div><div class="notion-text notion-block-34485e127d7c802f91a3c87f995aa108">找到：</div><div class="notion-text notion-block-34485e127d7c803d80dede656db453b3"><code class="notion-inline-code">const DIFY_CHATBOT_ENABLED = siteConfig(&#x27;DIFY_CHATBOT_ENABLED&#x27;, null, NOTION_CONFIG)</code></div><div class="notion-text notion-block-34485e127d7c8000b65dc77c62250a9e">改为：</div><div class="notion-text notion-block-34485e127d7c807e9b14fc51221dfeee"><code class="notion-inline-code">const DIFY_CHATBOT_ENABLED = siteConfig(&#x27;DIFY_CHATBOT_ENABLED&#x27;, null, NOTION_CONFIG) || process.env.NEXT_PUBLIC_DIFY_CHATBOT_ENABLED</code></div><div class="notion-text notion-block-34485e127d7c804cb352c84241645b82">改完后推送到 GitHub：</div><div class="notion-text notion-block-34485e127d7c80eb910cc5d014f69b02"><code class="notion-inline-code">git add components/DifyChatbot.js components/ExternalPlugins.js
git commit -m &quot;feat: 直接读取环境变量启用 Dify 聊天助手&quot;
git push</code></div><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-34485e127d7c809ba35fdb4c387c43d6" data-id="34485e127d7c809ba35fdb4c387c43d6"><span><div id="34485e127d7c809ba35fdb4c387c43d6" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34485e127d7c809ba35fdb4c387c43d6" title="第五步：在 Vercel 设置环境变量"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">第五步：在 Vercel 设置环境变量</span></span></h4><div class="notion-text notion-block-34485e127d7c80fd8829f1df84273686">进入 Vercel 项目 → <b>Settings</b> → <b>Environment Variables</b>，添加三个变量：</div><table class="notion-simple-table notion-block-34485e127d7c80ffb1eef9d96a4d3e7b"><tbody><tr class="notion-simple-table-row notion-block-34485e127d7c805389cdcea004fb3bdd"><td class="" style="width:120px"><div class="notion-simple-table-cell">变量名</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">值</div></td></tr><tr class="notion-simple-table-row notion-block-34485e127d7c80b5a8a8cfda7f1bfbc5"><td class="" style="width:120px"><div class="notion-simple-table-cell"><code class="notion-inline-code">NEXT_PUBLIC_DIFY_CHATBOT_ENABLED</code></div></td><td class="" style="width:120px"><div class="notion-simple-table-cell"><code class="notion-inline-code">true</code></div></td></tr><tr class="notion-simple-table-row notion-block-34485e127d7c8054b186ff6dac6bb64d"><td class="" style="width:120px"><div class="notion-simple-table-cell"><code class="notion-inline-code">NEXT_PUBLIC_DIFY_CHATBOT_TOKEN</code></div></td><td class="" style="width:120px"><div class="notion-simple-table-cell"><code class="notion-inline-code">app-你的Token</code></div></td></tr><tr class="notion-simple-table-row notion-block-34485e127d7c80789695e81aaf46f8f3"><td class="" style="width:120px"><div class="notion-simple-table-cell"><code class="notion-inline-code">NEXT_PUBLIC_DIFY_CHATBOT_BASE_URL</code></div></td><td class="" style="width:120px"><div class="notion-simple-table-cell"><code class="notion-inline-code">https://udify.app</code></div></td></tr></tbody></table><blockquote class="notion-quote notion-block-34485e127d7c805db7ddccb7f8760646"><div>注意：<code class="notion-inline-code">BASE_URL</code> 填 <code class="notion-inline-code">https://udify.app</code>，不是 <code class="notion-inline-code">https://api.dify.ai</code>。前者是加载聊天气泡脚本的地址，后者是 API 接口地址，填错了脚本找不到，气泡不会出现。</div></blockquote><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-34485e127d7c80cbb2b6fb14e597a52e" data-id="34485e127d7c80cbb2b6fb14e597a52e"><span><div id="34485e127d7c80cbb2b6fb14e597a52e" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34485e127d7c80cbb2b6fb14e597a52e" title="第六步：重新部署"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">第六步：重新部署</span></span></h4><div class="notion-text notion-block-34485e127d7c80c2b23cf2c36a56cac7">Vercel <b>Deployments</b> → 最新一条 → 右侧 <code class="notion-inline-code">...</code> → <b>Redeploy</b>。</div><div class="notion-text notion-block-34485e127d7c80d79ffef2fc6bdab87c">部署完成后刷新博客，右下角出现聊天气泡即为成功。</div><hr class="notion-hr notion-block-34485e127d7c80bd8d52cefc7c997848"/><div class="notion-callout notion-gray_background_co notion-block-19885e127d7c80a0a27ce9cd19bf5589"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="💡">💡</span></div><div class="notion-callout-text"><h3 class="notion-h notion-h2 notion-block-34485e127d7c802393d0fd37797f4f73" data-id="34485e127d7c802393d0fd37797f4f73"><span><div id="34485e127d7c802393d0fd37797f4f73" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34485e127d7c802393d0fd37797f4f73" title="踩过的坑"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">踩过的坑</span></span></h3><div class="notion-text notion-block-34485e127d7c8061a72ad96092fb5ba3"><b>坑一：配置改了没用</b></div><div class="notion-text notion-block-34485e127d7c809ab579df16544c85b6">原因：博客有缓存机制，Notion 配置页的改动要等缓存过期或重新部署才生效。改完 Notion 后必须 Redeploy。</div><div class="notion-text notion-block-34485e127d7c809f8933ef049f8a8195"><b>坑二：BASE_URL 填错</b></div><div class="notion-text notion-block-34485e127d7c807b8194ef0ac7e2a338"><code class="notion-inline-code">https://api.dify.ai</code> 是后端 API 地址，不提供 <code class="notion-inline-code">embed.min.js</code> 文件。聊天气泡脚本在 <code class="notion-inline-code">https://udify.app/embed.min.js</code>，两者不能混用。</div><div class="notion-text notion-block-34485e127d7c8055bc86ed36c4edfed2"><b>坑三：siteConfig 读取链在特定场景失效</b></div><div class="notion-text notion-block-34485e127d7c8039a845ca7b9d77fdc7"><code class="notion-inline-code">siteConfig</code> 依赖 React 的 <code class="notion-inline-code">useGlobal()</code> Context，如果组件在某些渲染时机不在 Provider 内，<code class="notion-inline-code">useGlobal()</code> 会抛出异常被静默捕获，整条配置链降级返回 <code class="notion-inline-code">null</code>，导致明明填了配置却不生效。解决方式是直接读 <code class="notion-inline-code">process.env.NEXT_PUBLIC_*</code>，绕过这个依赖。。</div></div></div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Unity-Shader]]></title>
            <link>https://lazy-zed.com/article/u3d_22</link>
            <guid>https://lazy-zed.com/article/u3d_22</guid>
            <pubDate>Sat, 01 Aug 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[对shader的简单理解]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-32c85e127d7c802682f7ca26566fcbe9"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><div class="notion-callout notion-gray_background_co notion-block-32c85e127d7c80639efae98902d1755f"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="😀">😀</span></div><div class="notion-callout-text">个人感觉shader这块，了解原理，有对它导致问题的判断力即可。</div></div><div class="notion-text notion-block-33d85e127d7c802d8c7bc009c6387dfd">ShaderLab</div><div class="notion-text notion-block-33d85e127d7c805b9d70c8fdd38629ed">本质是什么：</div><div class="notion-text notion-block-33d85e127d7c80dda0c9d7f36fa05d51">Shader 是跑在 GPU 上的程序，控制&quot;每个像素最终显示什么颜色&quot;。ShaderLab 是 Unity 封装的一套写 Shader 的语法格式。</div><div class="notion-text notion-block-34485e127d7c8030ab98f9ba55919def">渲染流水线：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-34485e127d7c80a681a9d76f4e0b4815"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Ac16b61ec-44bd-4ace-93b5-da23dd37829e%3Aimage.png?table=block&amp;id=34485e12-7d7c-80a6-81a9-d76f4e0b4815&amp;t=34485e12-7d7c-80a6-81a9-d76f4e0b4815" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-blank notion-block-34485e127d7c809481dccf18b9373b66"> </div><div class="notion-text notion-block-34485e127d7c8053b13fee6e454eddad">CPU→GPU</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-34485e127d7c804da02fed089abf7ef1"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A31024339-523e-4d1e-92e1-ad1873af2825%3Aimage.png?table=block&amp;id=34485e12-7d7c-804d-a02f-ed089abf7ef1&amp;t=34485e12-7d7c-804d-a02f-ed089abf7ef1" alt="notion image" loading="lazy" decoding="async"/></div></figure><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-34485e127d7c8020a4f9eeb9d8b92f5c"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A71759283-7457-4dc6-9fd7-8ce7255947b5%3Aimage.png?table=block&amp;id=34485e12-7d7c-8020-a4f9-eeb9d8b92f5c&amp;t=34485e12-7d7c-8020-a4f9-eeb9d8b92f5c" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-blank notion-block-34485e127d7c8020b757e4dd6e78ea0a"> </div><div class="notion-text notion-block-33d85e127d7c8009b076d6b479f15547">渲染管线流程(gpu流水线)：</div><div class="notion-text notion-block-33d85e127d7c80aaa6a3e42951116a29">CPU 准备网格数据</div><div class="notion-text notion-block-33d85e127d7c80d3a141f033e09487ce">↓
顶点着色器（Vertex Shader）
每个顶点在哪里，做坐标变换
↓
光栅化（Rasterization）
把三角形面片转成一个个像素
↓
片元着色器（Fragment Shader）
每个像素显示什么颜色
↓
输出到屏幕</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-34485e127d7c80b1b292d77c1688600b"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A63760a62-99c3-4734-a4a7-95f911a8f1fe%3Aimage.png?table=block&amp;id=34485e12-7d7c-80b1-b292-d77c1688600b&amp;t=34485e12-7d7c-80b1-b292-d77c1688600b" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-33d85e127d7c8086aaebf59420437a35">需要知道的关键概念：</div><ul class="notion-list notion-list-disc notion-block-33d85e127d7c80c896c7d1b4b51e863a"><li>Pass：一次渲染流程，复杂效果可以多 Pass</li></ul><ul class="notion-list notion-list-disc notion-block-33d85e127d7c80a0890bd17911c20617"><li>Properties：暴露给 Inspector 面板的参数</li></ul><ul class="notion-list notion-list-disc notion-block-33d85e127d7c8071b55fc9f723a9cb4d"><li>URP vs Built-in：现在新项目基本用 URP，两套语法不完全通用</li></ul><ul class="notion-list notion-list-disc notion-block-33d85e127d7c80128552c5c9d01a1094"><li>什么时候找美术/图程：角色描边、溶解、水面等视觉效果找他们，你只需要能看懂 Shader 代码在做什么、会调参数</li></ul><ul class="notion-list notion-list-disc notion-block-34485e127d7c802e8b1bc96b6a5987c8"><li>空间变换：模型空间→世界空间→观察空间→裁剪空间→屏幕空间</li></ul><div class="notion-text notion-block-33d85e127d7c805aad3fca4a42b458de">我们作为程序需要的判断力：</div><div class="notion-text notion-block-33d85e127d7c80d6a471d2bb73f092cf">▎ 这个效果是 Shader 问题还是逻辑问题？这个 Shader 为什么在低端机掉帧（指令数太多 / 采样次数太多）？</div><div class="notion-blank notion-block-32c85e127d7c80859cf3ccab2c77cc3c"> </div></main></div>]]></content:encoded>
        </item>
    </channel>
</rss>