This commit is contained in:
2025-08-02 12:09:34 +08:00
commit e70b01cdca
2785 changed files with 575579 additions and 0 deletions

1
07-Other/.keep Normal file
View File

@@ -0,0 +1 @@
this file is created for keeping the folder after git.

View File

@@ -0,0 +1,3 @@
【StableDiffusion AI绘画工作流进阶教程-第三课:换手】 https://www.bilibili.com/video/BV1Xh4y197nc/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
【StableDiffusion AI绘画工作流进阶教程-第四课:细节大爆炸】 https://www.bilibili.com/video/BV15V4y1174d/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
【拒绝低质量出图3种快速提高AI绘画分辨率的方式十分钟讲完| StableDiffusion WebUI 保姆级攻略·高清修复优化细节无损放大教程】 https://www.bilibili.com/video/BV11m4y12727/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e

View File

@@ -0,0 +1,2 @@
AI生成Logo
https://www.bilibili.com/video/BV1AG411E7Am/?vd_source=d47c0bb42f9c72fd7d74562185cee290

59
07-Other/AI/AI相关.md Normal file
View File

@@ -0,0 +1,59 @@
## B站资源
- stable diffusion webui完全整合版https://www.bilibili.com/video/BV1JG411n7Xe/?spm_id_from=333.788&vd_source=d47c0bb42f9c72fd7d74562185cee290
- AI绘画 【Stable Diffusion】 NovelAI 整合包 解压即用https://www.bilibili.com/video/BV1aN4y1A7j1/?is_story_h5=false&p=1&share_from=ugc&share_medium=android&share_plat=android&share_session_id=1479cd5b-cc57-4d3c-b844-9da42e8bd8c9&share_source=QQ&share_tag=s_i&timestamp=1665242584&unique_k=4ERXawz&vd_source=1ce8b0bdc0c03d8640a4cb25321ec24e
- novelai本地泄露版达到网络版效果的教程https://www.bilibili.com/read/cv18971531?spm_id_from=333.788.b_636f6d6d656e74.17
## 关键词生成工具
https://www.bilibili.com/video/BV1L84y1z7bH/?spm_id_from=333.1007.tianma.1-2-2.click&vd_source=d47c0bb42f9c72fd7d74562185cee290
## AI关键词
正面关键词(测试):extremely detailed CG unity 8k wallpaper,black long hair,cute face,1 adlut girl,happy, green skirt dress, flower pattern in dress,solo,green gown,art of light novel,in field
负面排除关键词(必填):lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry
作者:超级过气歌手 https://www.bilibili.com/read/cv18971531?spm_id_from=333.788.b_636f6d6d656e74.17 出处bilibili
关键词前面一定要加上: masterpiece, best quality, 负关键词加: nsfw, lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry 默认高度设置到768
https://www.bilibili.com/video/BV1X8411W7YV/?spm_id_from=333.1007.tianma.3-3-9.click&vd_source=d47c0bb42f9c72fd7d74562185cee290
### 具体作品关键词
![300](https://picx.zhimg.com/80/v2-ea9e9edb8334be001290c44a2520bd52_720w.webp?source=1940ef5c)
character_concept_design_portrait_a_handsome_young_
![300](https://pic1.zhimg.com/80/v2-c4fba4813ccec87d2662c56e357a3c37_720w.webp?source=1940ef5c)
https://pic1.zhimg.com/80/v2-c4fba4813ccec87d2662c56e357a3c37_720w.webp?source=1940ef5c
## 汉服少女
![image.png](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/20230207102705.png)
https://www.bilibili.com/video/BV1Sd4y1W7my/?spm_id_from=autoNext&vd_source=6154e19c5a98167a5ad1efdbdbdc9427
夸克网盘 链接https://pan.quark.cn/s/b04474b2212f
百度网盘 链接https://pan.baidu.com/s/1gJUIjAr4i7fw8qM7dwFXAg?pwd=ah34
https://space.bilibili.com/331374428
https://www.bilibili.com/video/BV1KG4y1M7Kb/?spm_id_from=333.788.recommend_more_video.6&vd_source=d47c0bb42f9c72fd7d74562185cee290
https://caiyun.139.com/m/i?0l5CM4Am77r8E 提取码kGST
## AI模型
- https://huggingface.co/andite/pastel-mix
- ![](https://huggingface.co/andite/pastel-mix/resolve/main/example-images/grid-0035.png)
- https://civitai.com/
- https://civitai.com/models/4468/counterfeit-v25
- ![](https://imagecache.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/79a71ba8-5efb-4b47-9903-d3ab125f2800/width=400)
- https://civitai.com/models/1411/counterfeit
- ![](https://imagecache.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/fc21332d-80d4-4085-f07c-971ac6d15000/width=400)
- https://civitai.com/models/7178/counterfeitlora300
- ![](https://imagecache.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/19095cb4-cb51-4c0c-69c0-ab8a1eeb1100/width=400)
- https://civitai.com/models/6424
- ![](https://imagecache.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/7727c1c6-8577-475a-55fd-f502b01f5000/width=400)
## AI诱导生成
Controlnet插件诱导生成
![QQ图片20230215150840.jpg](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/QQ%E5%9B%BE%E7%89%8720230215150840.jpg)
![IMG_9365(20230216-081116).JPG](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/IMG_9365(20230216-081116).JPG)
![IMG_9365(20230216-081116).JPG](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/IMG_9366(20230216-082338).JPG)
![IMG_9365(20230216-081116).JPG](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/IMG_9362(20230216-081001).JPG)
![IMG_9365(20230216-081116).JPG](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/IMG_9363.PNG)
![IMG_9365(20230216-081116).JPG](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/IMG_9364(20230216-081059).JPG)
![QQ图片20230216142648.jpg](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/QQ%E5%9B%BE%E7%89%8720230216142648.jpg)

View File

@@ -0,0 +1,4 @@
# 前言
- https://github.com/DanTheMan827/ios-app-signer
- https://dantheman827.github.io/ios-app-signer/
- https://github.com/maciekish/iReSign

View File

@@ -0,0 +1,158 @@
## 安装homebrew
参考:
- https://www.jianshu.com/p/e0471aa6672d
国内镜像安装命令:
```c++
/bin/bash -c "$(curl -fsSL https://gitee.com/ineo6/homebrew-install/raw/master/install.sh)"
```
安装完之后需要替换源:
### 1.必备设置
- 替换 brew.git
```bash
git -C "$(brew --repo)" remote set-url origin https://mirrors.ustc.edu.cn/brew.git
```
- 替换 homebrew-core.git
```bash
git -C "$(brew --repo homebrew/core)" remote set-url origin https://mirrors.ustc.edu.cn/homebrew-core.git
```
### 2.按需设置
- 替换 homebrew-cask.git
```bash
git -C "$(brew --repo homebrew/cask)" remote set-url origin https://mirrors.ustc.edu.cn/homebrew-cask.git
```
- 替换homebrew-bottles
首先要先区分你的mac用哪种终端工具如果是 bash则执行
```bash
echo 'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.ustc.edu.cn/homebrew-bottles' >> ~/.bash_profile
source ~/.bash_profile
```
若是 zsh则执行
```bash
echo 'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.ustc.edu.cn/homebrew-bottles' >> ~/.zshrc
source ~/.zshrc
```
## git
之后就可以输入命令安装git
```bash
brew install git
```
## macOS开发环境
https://ue5wiki.com/wiki/2329190d/
运行生成解决方案.sh时会提示
>ERROR: Invalid SDK MacOSX.sdk, not found in /Library/Developer/CommandLineTools/Platforms/MacOSX.platform/Developer/SDKs
确认安装Xcode之后执行
```bash
sudo ln -s /Applications/Xcode.app/Contents/Developer/Platforms /Library/Developer/CommandLineTools/Platforms
```
## 为 xcode 开启多线程编译
AMD CPU信息查看APP
https://github.com/trulyspinach/SMCAMDProcessor/releases/tag/0.7.1
首先看一下 Mac 的硬件配置:
>sysctl machdep.cpu
找到 `machdep.cpu.core_count` 字段,其中的数值就是 Mac 的核心数。然后可以给 xcode 开启多线程,数量数为核心数 *2如我的是 8 核,就可以开启 16 线程:
> defaults write com.apple.Xcode PBXNumberOfParallelBuildSubtasks 16
# 证书
需要以下文件
- .cer
- .p12
- .mobileprovision
并将其放入
`/Users/你的用户名/Library/Mobiledevice/provisioning Profiles`最后双击导入。p12建议设置一个密码mac里不支持无密码的p12证书。
运行GenerateProjectFiles.command生成解决方案***此时可以检查证书是否有效***。
# 编译流程
- 先编译ShaderCompileWork
- 后编译UE5
***一定要注意虚拟机的CPU以及内存的关系CPU线程数 * 2 < 内存GB 数。所以给的CPU数目一定不能多。*** 即使是黑苹果下如果内存不够多开了比较占用内存的APP会比较容易死机。比如我使用的是7950x 16核心 32线程 与 64GB内存基本就处于一个平衡状态。如果开了XCode的情况打包引擎会出现死机情况。
# 疑难问题解
## UE5.1 Setup.bat 提示unsupported compression method问题
问题为:
>Checking dependencies...
>Updating dependencies: 0% (0/97340)...
> Failed to download 'https://cdn.unrealengine.com/dependencies/UnrealEngine-11447123-52802068b7db445d94de6cd13d574a02/00ba053f58ab8d00cf41519fd27d8059d397a4fb': InvalidDataException: The archive entry was compressed using an unsupported compression method.
此文问题普遍发生在5.0~5.1以及UE4版本解决方法
去 https://github.com/EpicGames/UnrealEngine/releases 的各个版本标签下,下载**Commit.gitdeps.xml**文件,并替换`\Engine\Build`下同名文件即可。
## ExternalBuildToolExecution failed with a nonzero exit code
问题表现为编译到一半提示 `ExternalBuildToolExecution failed with a nonzero exit code`。这主要是因为XCode代码检索会占用非常多的内存使得CPU线程数 * 2 > 内存GB从而导致编译失败。
解决方法:
>在CMD中输入 `defaults write com.apple.dt.XCode IDEIndexDisable 1`禁用XCode代码检索即可。
## ATOMIC_VAR_INIT错误
https://forums.unrealengine.com/t/cannot-build-ue-5-1-1-from-source-on-macos-ventura-13-3-1-xcode-14-3/885545
macOS 13.4 Xcode 14.3编译UE5.1会报错,其原因是:
>So it seems the error is related to the deprecation of `ATOMIC_VAR_INIT` [since C++20 2](https://en.cppreference.com/w/cpp/atomic/ATOMIC_VAR_INIT). I gather [from this table 3](https://en.wikipedia.org/wiki/Xcode#Xcode_11.0_-_14.x_(since_SwiftUI_framework)_2) that Xcode 14.3 includes a clang patch version bump from 14.0.0 to 14.0.3, and a llvm major version bump from 14.0.0 to 15.0.0. With my limited understanding of clang and llvm, I would guess that llvm 15 compiles at C++20, and therefore throws the build error, whereas the build is successful with Xcode 14.2.
解决方法是:
在`Engine/Source/Programs/UnrealBuildTool/Platform/Mac/MacToolChain.cs`中添加判断语句对高版本XCode添加`-Wno-deprecated-pragma`编译变量。
```c++
if (CompilerVersionGreaterOrEqual(14, 0, 0))
{
Arguments.Add("-Wno-deprecated-pragma");
}
```
最后修改结果:
```csharp
protected override void GetCompileArguments_Global(CppCompileEnvironment CompileEnvironment, List<string> Arguments)
{
base.GetCompileArguments_Global(CompileEnvironment, Arguments);
Arguments.Add("-fasm-blocks");
if (CompileEnvironment.bEnableOSX109Support)
{
Arguments.Add("-faligned-new"); // aligned operator new is supported only on macOS 10.14 and above
}
// Pass through architecture and OS info
Arguments.Add("" + FormatArchitectureArg(CompileEnvironment.Architecture));
Arguments.Add($"-isysroot \"{SDKPath}\"");
Arguments.Add("-mmacosx-version-min=" + (CompileEnvironment.bEnableOSX109Support ? "10.9" : Settings.MacOSVersion));
List<string> FrameworksSearchPaths = new List<string>();
foreach (UEBuildFramework Framework in CompileEnvironment.AdditionalFrameworks)
{
FileReference FrameworkPath = new FileReference(Path.GetFullPath(Framework.Name));
if (!FrameworksSearchPaths.Contains(FrameworkPath.Directory.FullName))
{
Arguments.Add($"-F \"{NormalizeCommandLinePath(FrameworkPath.Directory)}\"");
FrameworksSearchPaths.Add(FrameworkPath.Directory.FullName);
}
}
//添加的代码在这里
if (CompilerVersionGreaterOrEqual(14, 0, 0))
{
Arguments.Add("-Wno-deprecated-pragma");
}
}
```
## std::ref问题
添加头文件即可。
```c++
#include <functional>
```
## 清理引擎构建历史&缓存
XCode的构建历史
>~/Library/Developer/Xcode/DerivedData
本人采用删除Engine、LocalBuild文件夹中所有文件与UE5 XCode解决方案文件再使用git恢复。最后再运行Setup.command、GenerateProjectFiles.command。

View File

@@ -0,0 +1,277 @@
# 概念
EFI:
>BIOS是个程序存储在BIOS芯片中而现在的新式电脑用的基本都是UEFI启动早期的过渡电脑用的都是EFI启动。EFI或UEFI的一部分也是存储在一个芯片中。
SSDT&DSDT
>ssdt里信息都是电源管理与显卡相关信息其他设备基本都在dsdt里。
# 相关资料
- [OpenCore-Install-Guid](https://dortania.github.io/OpenCore-Install-Guide/prerequisites.html)
- [OpenCore中文手册](https://oc.skk.moe/1-introduction.html)
- 完美黑苹果系列教程
- [【Windows&macOS】完美双系统系列教程第2集Windows环境下配置OC引导](https://www.bilibili.com/video/BV1Bi4y1S7DN/?spm_id_from=333.788&vd_source=d47c0bb42f9c72fd7d74562185cee290)
- [【Windows&macOS】完美双系统系列教程第3集安装macOS](https://www.bilibili.com/video/BV14a41147Kk/?spm_id_from=pageDriver&vd_source=d47c0bb42f9c72fd7d74562185cee290)
- [【Windows&macOS】完美双系统系列教程第4集登录Apple ID](https://www.bilibili.com/video/BV1gi4y1X7EY/?spm_id_from=333.788&vd_source=d47c0bb42f9c72fd7d74562185cee290)
- USB 定制
- [全新的定制USB教程](https://www.bilibili.com/video/BV1m3411b7JP/?spm_id_from=333.999.0.0&vd_source=d47c0bb42f9c72fd7d74562185cee290)
- [USB定制补充内容该选择哪种USB定制方式](https://www.bilibili.com/video/BV1yv4y1X7Jq/?spm_id_from=333.999.0.0&vd_source=d47c0bb42f9c72fd7d74562185cee290)
- 其他
- [macOS13升级准备OTA也能万无一失](https://www.bilibili.com/video/BV1Be411V7b7/?spm_id_from=333.999.0.0&vd_source=d47c0bb42f9c72fd7d74562185cee290)
- [如何选择有线网卡板载or外置看这个视频可能就够了](https://www.bilibili.com/video/BV1HN411P7Fx/?spm_id_from=333.999.0.0&vd_source=d47c0bb42f9c72fd7d74562185cee290)
- [12-13代intel黑苹果OC引导配置注意事项](https://www.bilibili.com/video/BV17e4y1A7os/?spm_id_from=333.999.0.0&vd_source=d47c0bb42f9c72fd7d74562185cee290)
- [蓝牙自动连接黑苹果和Windows双系统共用一套蓝牙键鼠](https://www.bilibili.com/video/BV1kv4y1w7qH/?spm_id_from=333.999.0.0&vd_source=d47c0bb42f9c72fd7d74562185cee290)
- [免驱显卡编号查询](https://devicehunt.com/view/type/pci/vendor/1002)
# 相关软件
- [SSDTTime](https://github.com/corpnewt/SSDTTime)用于生成ssdt文件。
- [USBToolBox](https://github.com/USBToolBox/tool)用于定制usb。
- [kext](https://github.com/USBToolBox/kext)
- [OCAT](https://github.com/ic005k/OCAuxiliaryTools)OC编辑器。
- [OCC](https://mackie100projects.altervista.org/download-opencore-configurator/)MAC系统中的OC编辑器。
- [DiskGenius](https://www.diskgenius.cn/)查看EFI用。
- [BalenaEtcher-Setup](https://etcher.balena.io/):写入镜像工具。
- [ProperTree](https://github.com/corpnewt/ProperTree):
- [Hackintool](https://github.com/benbaker76/Hackintool)
参考:https://forum.amd-osx.com/threads/asus-x670e-gene-efi-adaptable-to-other-x670-x670e-b650-and-b650e-boards.4160/post-27008
# 完美黑苹果(Win&Mac 双系统)安装流程
如果电脑已经存在EFI分区即你已经先安装了macOS此时安装Win10会破坏之前的EFI引导所以建议先安装Win10之后安装macOS。
1. Win [【Windows&macOS】完美双系统系列教程第2集Windows环境下配置OC引导](https://www.bilibili.com/video/BV1Bi4y1S7DN/?spm_id_from=333.788&vd_source=d47c0bb42f9c72fd7d74562185cee290)
1. 安装Win10系统。
2. Win系统中的所有的硬盘名称改成英文。可选
3. 定制USB。参考[全新的定制USB教程](https://www.bilibili.com/video/BV1m3411b7JP/?spm_id_from=333.999.0.0&vd_source=d47c0bb42f9c72fd7d74562185cee290)
4. 运行SSDTTime.bat生成所需的SSDT文件。
5. OCAT感觉可以直接使用论坛上分享的x670e EFI文件
1. 升级OCAT。
2. 点击数据库图标双击Sample.plist创建案例EFI文件夹。
3. 进行文件整理。
1. 删除EFI/OC/ACPI下的文件并使用这个视频提供的SSDT文件替换。
2. 删除EFI/OC/Drivers下的文件仅保留视频中的3个文件。
4. 配置OCAT的各个选项。
6. 使用balenaEtcher-Setup制作安装盘。
7. 使用DiskGenius删除安装盘中的EFI文件并且使用自己的配置的EFI替换。
2. BIOS [【Windows&macOS】完美双系统系列教程第3集安装macOS](https://www.bilibili.com/video/BV14a41147Kk/?spm_id_from=pageDriver&vd_source=d47c0bb42f9c72fd7d74562185cee290)
3. macOS该过程中不能登录AppleID以防被Ban
1. 安装macOS。
2. 安装OCC。
3. 使用OCC挂载 U盘与本地磁盘的EFI分区并且将内部的EFI文件复制到 本地EFI分区。
4. 拔掉安装U盘重启并且手动选择从安装macOS的硬盘启动。
5. 进行相关的macOS偏好设置。
6. 下载Hackintool完成macOS的USB定制。
7. 打开主板的页面查询自己主板的声卡型号之后到github.com/acidanthera/AppleALC查找声卡型号并且用OCC修改并且测试声卡参数。
4. 解决AppleID登录问题[【Windows&macOS】完美双系统系列教程第4集登录Apple ID](https://www.bilibili.com/video/BV1gi4y1X7EY/?spm_id_from=333.788&vd_source=d47c0bb42f9c72fd7d74562185cee290)
1. 使用OCC打开本地硬盘的EFI并且检查序列号有效性。如果显示返回无法查看就可以了。
2. 检查NVRAM是否正常运行。
1. sudo nvram myvar=test
2. nvram -p | grep -i myvar
# AMD OS X 作业
- https://forum.amd-osx.com/threads/asus-x670e-gene-efi-adaptable-to-other-x670-x670e-b650-and-b650e-boards.4160/
- https://forum.amd-osx.com/threads/asus-x670e-proart-ryzen-9-7950x-radeon-rx-6950-xt-macos-ventura.4530/
很可惜,**第一个无法进入安装界面**。而第二个只需要修改显卡型号就可以进入。
以下为第一个作业的EFI流程笔记为了使此EFI适应**其他板**,必须进行以下更改:
- [x] _通过暂时使用OpenCore.efi_的调试版本并设置_Misc -> Target -> 67_以启用 OpenCore 日志文件,找到您的主板的 MMIO 白名单。引导系统直到出现 OpenCore Picker。这应该足以在 EFI 分区的根文件夹中生成 OpenCore 日志文件。在该日志中搜索“MMIO”您将找到所需的条目请参阅[此 MMIO 白名单指南](https://am5hackintosh.github.io/OpenCore-Install-Guide/extras/devirtualizemmio.html)
- [ ] 创建您自己的USB端口映射该 EFI 文件夹使用 (a) SSDT 和 (b) kext 的组合来创建 USB 端口映射;如果您愿意的话,我还包含了 USBToolBox 的输出;**但您仍然必须为您的特定主板创建 USB 端口映射**;您可以尝试使用此 EFI 文件夹中的 USB 端口映射,但它可能适合您,也可能不适合您
- [ ] 要启用从睡眠状态一键唤醒,`SSDT-USBW.aml`必须使用系统中所有活动 USB (XHC) 控制器的 ACPI 路径进行修改
- [x] **应为您的**主板更新​​ DeviceProperties 部分;现有条目可以在安装 macOS 时删除并在以后创建;这些属性的目的是为 NVMe M.2 和 SATA SSD设置**内置属性,以便它们在桌面上显示为内部驱动器(请参见下面的屏幕截图)**
要使此 EFI 适应您的**CPU、**内存**DIMM、** GPU和**区域**,必须进行以下更改****
- [x] 更改**内核补丁**以反映处理器中物理 CPU 核心的数量;我使用的是 Ryzen 7 7700X它有 8 个核心,因此如果您使用不同的处理器,**请修改前 3 个内核补丁**(请参见下面的屏幕截图)
- [x] 更新**PlatformInfo -> Memory**部分,如下面的屏幕截图所述
- [ ] 还可以在**PlatformInfo -> DataHub**部分中复制或创建**序列号,如下面的屏幕截图所示**
- [x] 如果使用 2、4 或 6 核 CPU请将 ProcessorType 更改为 1537如图所示
- [x] 如果使用具有 8 个或更多核心的 CPU请将 ProcessorType 设置为 3841如图所示
- [x] **在NVRAM**部分中输入您的 CPU 名称如下所示以便它在_“关于本机”中正确显示_
- [ ] 如果使用 RX 550、560、570、580、Vega 56、Vega 64 和 Radeon VI请删除引导参数**agdpmod=pikera (请参见下面的屏幕截图)**
- [x] _在boot-args_下的 NVRAM 部分中,有一个名为**prev-lang:kbd 的**参数;当前设置为`en-US:0`****但您可以将其更改为您所在的区域
步骤记录:
1. 打开OCAT切换升级镜像目前默认的升级镜像站点挂了并升级到0.91版本作业是0.91版本)。
2. 按照网卡修改对应的plist我板载网卡是Inter所以使用**config-Intel-M.2-WiFi-BT.plist**),并将其重命名为**config.plist**。
1. [x] 按照CPU核心数修改**Kernel -> Patch** 中的带有**cpuid_set_info** 与 **algrey - Force cpuid_cores_per_package XX.X+** 项的Replace数据中的**BA0800000000**的 08改成你使用的CPU的核心数。7700x 8核7950x 16核所以改成**BA1600000000**
1. [x] 修改**PlatformInfo -> ProcessorType** 为 3841 超出8核心
2. [x] 修改**NVRAM -> 4D1FDA02-38C7-4A6A-9CC6-4BCCA8B30102 -> revcpuname** CPU名称。
3. [x] 修改**PlatformInfo -> Memory**部分需要查询一下内存序列号因为我是32 x 2所以改成32768 F5-6000J3040G32GX2-TZ5NR
4. [x] 修改语言为中文,**NVRAM -> 7C436110-AB2A-4BBB-A880-FE41995C9F82 -> prev-lang:kbd** 为 **string zh-Hans:252**
5. [ ] 修改系统序列号与UUID**PI -> Generic**。[【Windows&macOS】完美双系统系列教程第2集Windows环境下配置OC引导】 【精准空降到 07:12】](https://www.bilibili.com/video/BV1Bi4y1S7DN/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e&t=432)
6. [x] 修改**DP**中硬盘PCIE、 屏蔽N卡本人的x670e-e装了N卡与A卡使用OpenCore引导时需要确定显示器接在A卡上别问我是怎么知道的实际上x670e-e与x670e gen的STAT NVMEN的控制器分布式一样的所以不用改https://dortania.github.io/OpenCore-Install-Guide/extras/spoof.html#windows-gpu-selection 。
1. SSDT法 https://zhuanlan.zhihu.com/p/436460948
2. NVRAM法 https://imacos.top/2022/01/03/0807/
3. 路径可以在设备管理器 -> 显卡属性 -> 详细信息 -> 位置路径找到。 https://www.reddit.com/r/hackintosh/comments/yetoec/pci_device_paths_without_gfxutil_or_macos_using/
4. 屏蔽N卡 PciRoot(0x0)/Pci(0x1,0x1)/Pci(0x0,0x0) |disable-gpu|Boolean|`True`| https://dortania.github.io/OpenCore-Install-Guide/extras/spoof.html
3. [x] 修改MMIO白名单
4. 可选修改
1. [x] 为了EXPO关闭雷电4接口。(**Bios中操作可能需要禁用**)
2. [ ] USBToolBox 定制
## Debug操作
- debug 3 -> 67MISC - Debug -Target 设置成67之后就可以输出Log到安装U盘根目录。
- 开启Sysreport
- 添加跑码模式找到NVRAM---7C436110-AB2A-4BBB-A880-FE41995C9F82 给boot-args 添加上 -v选项。
![](https://i0.hdslb.com/bfs/article/2a9a049b26a80f2148d1a2fd5618f1d354643e2b.png@1256w_700h_!web-article-pic.avif)
# 显卡型号查询与替换
型号可以在GPU-Z中查询前几位厂商型号后面4位就是显卡型号。
找到之后在 https://devicehunt.com/search/type/pci/vendor/1002/device/any 查询确认即可。
比如GPU-Z显示的DeviceID是1002 73FF - 1DA2 E451。所以我的显卡型号是73FF1002是AMD厂家代号经过上面网站查询显示
>Navi 23 [Radeon RX 6600/6600 XT/6600M]
找到型号后将型号头尾调换后填入OpenCore中即可。
# BIOS问题
参考 https://github.com/dortania/oc-laptop-guide-legacy/blob/master/before-you-start/bios-configuration.md
- Turn off Secure Booter
- Disable it, or you won't be able to access OpenCore to boot macOS or the macOS installation media.
- Turn the TPM offload
- If your computer has a TPM chip, you'll want to turn it off. macOS can't use it anyway.
- Disable VT-D /SVM
- VT-D is Intel's hardware based IO and device offload technology. It's incompatible with macOS and can cause boot issues, and kernel panics. If you have the option, you should turn it off.
- Graphics DVMT-Prealloc
- This is the initial memory used for your Intel GPU. By default it's usually 32MB, but for macOS it should be set to 64MB. If you don't have this option in your BIOS it can be patched later.
- SATA/AHCI/RAID
- You always want your Hard Disks and SSDs to operate in AHCI mode. In any of the other modes, macOS won't see them for installation.
- Disable that dGPU!
- If you can, if not you can disable it with a patch later. You may need to boot the installation media with an extra argument (-x) to use safe mode though. This setting can also be left on if you want to use the dGPU within Windows or other operating systems.
- Enable Legacy USB support
- This is sometimes needed for your keyboard to work in OpenCore. It's always safe to set if it's available.
- Enable XHCI Handoff
- This parameter tells the computer to hand control of the XHCI (USB 3) bus to macOS, if you have it make sure it's enabled.
- Disable Fast Boot
- Fast Boot establishes a cache to boot into Windows more quickly, but that can be problematic when booting into macOS. If you have the parameter, you'll want to turn it off.
- Disable Wake on Lan
- Wake on Lan is one of the leading causes of sleep wakeups, if you have the option in your BIOS to disable it, it's recommended that you do.
- Disable Unsupported Devices
- That fingerprint and some SDCard reader won't work anyway, so turn off the ports if you can and save power! (if possible)
1. SVM虚拟机
2. CSM
3. 快速启动
4. 安全启动
5. 为了EXPO关闭雷电4接口
- If you use USBToolBox to create a USB port map, be wary of the following:
- USBToolBox will create a kext that refers to specific **pcidebug** addresses, which are a combination of `Bus:Device:Function` such as `0:18:0`
- These addresses may change if Thunderbolt is enabled or disabled, so it's necessary to remember whether Thunderbolt was enabled or disabled when USBToolBox was used to generate the port map; the USB kext will work properly only when Thunderbolt is set to the same setting
- On Asus X670E Gene and possibly other Asus AM5 boards, BIOS is already configured properly, but you may want to **enable** AMD EXPO or XMP for full memory speed and **disable** USB4/Thunderbolt
- On other boards, check and set the following:
- EXPO or XMP for memory speed
- XHCI Handoff should be enabled
- Resizable BAR can be left enabled whether or not your GPU supports it
- Above 4G Decoding should be enabled
- If your BIOS has option to disable iGPU, it is not necessary to do so; we can use the supplied SSDT-DISABLE-IGPU.aml to hide iGPU from macOS (it may be necessary to change the PCI path of iGPU inside the SSDT)
# CaseySJ & ovenlite1 EFI
ovenlite1相比CaseySJ
- ACPI
- Add (Remove & Replace)
- SSDT-CREATE-DP68.aml (disable)
- SSDT-EC-AMD.aml
- SSDT-SBUS-MCHC-AMD.aml
- SSDT-SBRG.aml
- SSDT-USBX.aml -> SSDT-EC-USBX.aml
- SSDT-SBUS-MCHC-AMD.aml -> SSDT-SBUS-MCHC.aml
- SSDT-DTPG.aml
- SSDT-TB3HP-ASUS-X670E-GENE.aml (disable)
- SSDT-USB-ASUS-X670E-GENE-GP17.aml
- SSDT-USB-ASUS-X670E-GENE-GPP7.aml
- SSDT-USB-ASUS-X670E-GENE-GPP7-NO-BT.aml (disable)
- SSDT-USBW.aml
- SSDT-AQUANTIA-AQC113C-PCX1.aml (disable)
- Path (Add)
- | | 0 | Replace DP68 with DP10 in AmdTable | 2 | false | 44503638 | 0 | | 416D645461626C65 | 44503130 | | 0 | 0 | 53534454 |
- Booter
- MMIO WhiteList
- Row 5 Address 36507222016 -> 70866960384
- DP (Skip)
- Kernel
- Add(Remove)
- SMCAMDProcessor.kext
- NVMeFix.kext
- USBPorts-XHC1-XHC2-XHC4.kext
- USBPorts-XHC1-XHC2-XHC4-NO-BT.kext
- USBWakeFixup.kext
- AirportItlwm-Monterey.kext
- FeatureUnlock.kext
- IntelBluetoothInjector.kext
- Force
- | com.apple.iokit.IONetworkingFamily | System/Library/Extensions/IONetworkingFamily.kext | | false | Contents/MacOS/IONetworkingFamily | Contents/Info.plist | | 13.99.99 | Any |
- Kext 标识符,以便在添加前检查是否存在,例如:`com.apple.iokit.IONetworkingFamily`。只有在缓存中找不到标识符的驱动程序才会被添加。
- Path
- Enable algrey - _cpuid_set_generic_info - Set microcode=186 - 10.13/10.14/10.15/11.0/12.0/13.0
- Disable CaseySJ - Fix PCI bus enumeration on AM5 - 13.0
- Remove CaseySJ Row 21~26 com.apple.driver.AppleEthernetAquantiaAqtion
- Emulate
- Disable DummyPowerManagement
- Quirks
- Disable CustomSMBIOSGuid
- Scheme
- KernelArch
- x86_64 -> Auto
- MISC
- Boot
- Disable PollAppleHotKeys
- Disable HibernateSkipsPicker
- PickerAttributes 153 -> 17
- PickerVariant blackosx\BsxM1 -> Acidanthera\Syrah
- Timeout 6 -> 10
- Debug(Skip)
- Security
- Disable AllowSetDefault
- Entries
- Add | CustomOS | PciRoot(0x0)/Pci(0x1,0x1)/Pci(0x0,0x0)/NVMe(0x1,11-22-33-44-55-66-77-88)/HD(1,GPT,00000000-0000-0000-0000-000000000000,0x800,0x64000)/\EFI\BOOT\BOOTX64.EFI | | false | Not signed for security reasons | false | Auto | false |
- Tools(Add)
- BootKicker.efi
- ChipTune.efi
- CleanNvram.efi
- ControlMsrE2.efi
- CsrUtil.efi
- GopStop.efi
- KeyTester.efi
- MmapDump.efi
- OpenControl.efi
- ResetSystem.efi
- RtcRw.efi
- TpmInfo.efi
- NVRAM
- Add
- 4D1FDA02-38C7-4A6A-9CC6-4BCCA8B30102 (Remove)
- revcpuname
- revcpu
- 7C436110-AB2A-4BBB-A880-FE41995C9F82 (Add)
- | #INFO (prev-lang:kbd) | String | en:252 (ABC), set 656e3a323532 |
- | ForceDisplayRotationInEFI | Number | 0 |
- boot-args keepsyms=1 revpatch=cpuname e1000=0 debug=0x100 agdpmod=pikera -> keepsyms=1 e1000=0 debug=0x100 agdpmod=pikera
- Delete
- 4D1FDA02-38C7-4A6A-9CC6-4BCCA8B30102 (Remove)
- revcpu
- revcpuname
- opencore-version
- UIScale
- 7C436110-AB2A-4BBB-A880-FE41995C9F82 (Remove)
- csr-active-config
- SystemAudioVolume
- SystemAudioVolumeDB
- UIScale
- LegacySchema
- 7C436110-AB2A-4BBB-A880-FE41995C9F82
- 8BE4DF61-93CA-11D2-AA0D-00E098032B8C
- UEFI
- APPLEInput
- Enable GraphicsInputMirroring
- Audio
- AudioDevice
- Drivers (Remove)
- CrScreenshotDxe.efi
- OpenLinuxBoot.efi
- ext4_x64.efi
- ResetNvramEntry.efi
- ToggleSipEntry.efi
- ReservedMemory
- | 268435456 | HD3000: IGPU memory corruption errata | false | 268435456 | Reserved |
- | 569344 | Fix black screen on wake from hibernation for Lenovo Thinkpad T490 | false | 4096 | RuntimeCode |

View File

@@ -0,0 +1,250 @@
## Electron Quick Start
```
# 克隆这仓库
$ git clone https://github.com/electron/electron-quick-start
# 进入仓库
$ cd electron-quick-start
# 安装依赖库
$ npm install
# 运行应用
$ npm start
```
## 使用IPC进行GUI与原生APP进行通讯
在main.js里添加下面的代码从通道订阅消息
```
var ipc = require('ipc');
ipc.on('close-main-window', function () {
app.quit();
});
```
引入ipc模块后通过通道订阅消息就变得很简单on()方法设置订阅的通道名,定义回调函数。
渲染进程要通过通道发送消息将下面代码加入index.js
```
var ipc = require('ipc');
var closeEl = document.querySelector('.close');
closeEl.addEventListener('click', function () {
ipc.send('close-main-window');
});
```
同样我们引入ipc模块给关闭按钮的元素绑定一个click事件。当点击关闭按钮时通过「close-main-window」通道的send()方法发送消息。
## 全局快捷键
```
var globalShortcut = require('global-shortcut');
app.on('ready', function() {
... // existing code from earlier
globalShortcut.register('ctrl+shift+1', function () {
mainWindow.webContents.send('global-shortcut', 0);
});
globalShortcut.register('ctrl+shift+2', function () {
mainWindow.webContents.send('global-shortcut', 1);
});
});
```
```
ipc.on('global-shortcut', function (arg) {
var event = new MouseEvent('click');
soundButtons[arg].dispatchEvent(event);
});
```
## 保存用户配置
使用nconf模块
```
npm install --save nconf
```
```
var nconf = require('nconf').file({file: getUserHome() + '/sound-machine-config.json'});
function saveSettings(settingKey, settingValue) {
nconf.set(settingKey, settingValue);
nconf.save();
}
function readSettings(settingKey) {
nconf.load();
return nconf.get(settingKey);
}
function getUserHome() {
return process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME'];
}
module.exports = {
saveSettings: saveSettings,
readSettings: readSettings
};
```
## 系统托盘
```
var remote = require('remote');
var Tray = remote.require('tray');
var Menu = remote.require('menu');
var path = require('path');
var trayIcon = null;
if (process.platform === 'darwin') {
trayIcon = new Tray(path.join(__dirname, 'img/tray-iconTemplate.png'));
}
else {
trayIcon = new Tray(path.join(__dirname, 'img/tray-icon-alt.png'));
}
var trayMenuTemplate = [
{
label: 'Sound machine',
enabled: false
},
{
label: 'Settings',
click: function () {
ipc.send('open-settings-window');
}
},
{
label: 'Quit',
click: function () {
ipc.send('close-main-window');
}
}
];
var trayMenu = Menu.buildFromTemplate(trayMenuTemplate);
trayIcon.setContextMenu(trayMenu);
```
## 打包应用
>摘自https://www.jianshu.com/p/f134878af30f
安装electron-package
```
npm install electron-package --save-dev
```
添加scrip命令 ,用于打包electron app。
```
"scripts": {
"start": "electron ."
"build": "electron-packager . hello_electron --platform=darwin --arch=x64 --ignore=node_modules/electron-*",
},
```
**electron-packager命令格式**
```
electron-packager 项目目录 app名称 --platform=平台 --arch=架构 --ignore=要忽略的目录或文件
arch
ia32 x64 armv7l all
plateform
linux win32 darwin mas all
OS X (also known as darwin)
Mac App Store (also known as mas)
```
执行命令npm run build,将得到如下结果
### electron-builder与electron-packager的区别
使用electron-builder打包应用是安装包方式而不想electron-packager打包之后直接是一个可文件夹交给所有的文件暴露出来。由electron-builder打出的包更为轻量并且可以打包出不暴露源码的setup安装程序
## 压缩源码
为避免源代码泄露,可对源码进行压缩。
**安装electron-asar**
```
npm install electron-asar --save-dev
```
**添加scrip命令 ,用于压缩源代码。**
```
"scripts": {
"start": "electron ."
"build": "electron-packager . hello_electron --platform=darwin --arch=x64 --ignore=node_modules/electron-*",
"package":"asar pack hello_electron-darwin-x64/hello_electron.app/Contents/Resources/app hello_electron-darwin-x64/hello_electron.app/Contents/Resources/app.asar"
},
```
**asar 命令格式**
asar pack <dir> <output>
执行npm run package将得到app.asar文件此时可将app文件删除。
## Electron启动node.js服务器
1.直接在index.html中启动外部的node.js服务器
2.将原生的node.js服务器代码使用module.exports = () => {}导出之后在electron的main.js中直接导入
```
app.server = require(__dirname + '/app/app')();
```
## 不迁移项目就可以打包双版本的可行方案
作者并未提供web开发的支持但是提供了非常好的web打包支持。
只要写好逻辑我们可以不用迁移项目就可以打包桌面项目和web项目。
process.env.IS_WEB是暴露的一个全局变量我们可以在渲染进程中获取项目在electron环境下返回false。否则为true。于此我们可以通过设置她的值来达到web dev的效果也可以处理不同环境的不同逻辑一些示例
![image](https://upload-images.jianshu.io/upload_images/3765249-cae045ed68c41067.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![image](https://upload-images.jianshu.io/upload_images/3765249-e8f7f6ac49fdf050.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![image](https://upload-images.jianshu.io/upload_images/3765249-a48751e6cf2d88e3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
## 打开新窗口的“最佳”做法
1.使用webview allowpopups变量控制是否拦截新弹出的窗口
下面的例子是webview允许.open/.showModalDialog/.showModelessDialog的例子
electron的index.html:重点是参数allowpopups
```
<webview id="foo" src="https://newsn.net/test.html" allowpopups style="width:100%; height:360px;"></webview>
```
原文https://newsn.net/say/electron-webview-window-open.html
2.因为是webpack配置入口只有index.html ,所以打开新窗口,一般会再配置一个入口。其实还有一种更佳的做法。
```
>>> 主进程 定义好监听事件
ipc.on('newPage', function(e) {
const modalPath = process.env.NODE_ENV === 'development'
? 'http://localhost:9080/#/newPage'
: `file://${__dirname}/index.html#newPage`
let win = new BrowserWindow({
width: 1024,
height: 724,
webPreferences: {
webSecurity: false
}
})
win.on('close', function() {
win = null
})
win.loadURL(modalPath)
})
>>> router/index.js 定义路由
// import 你的新页面 .vue 文件
{
path: '/newPage',
name: 'newPage',
component: newPage,
}
》》》配置完成 任意进程调用ipc.send('newPage') 完美解决
```
3.
```
document.getElementById("youtube").onclick = function(){
youtubeWindow = new BrowserWindow ({width: 1000, height:800})
youtubeWindow.loadURL("https://youtube.com/");
youtubeWindow.on("close", function(){
youtubeWindow = null;
})
}
document.getElementById("local-list").onclick = function(){
localListWindow = new BrowserWindow ({width: 1000, height:800})
localListWindow.loadURL(`file://${__dirname}/local-list.html`);
localListWindow.on("close", function(){
localListWindow = null;
})
}
```

View File

@@ -0,0 +1,112 @@
## 移植经验
1. 使用npm下载所有在vue-cli项目所使用的模块比如element-ui、NProgress之类的。
2. 将vue-cli项目中src目录下所有文件复制到electron-vue中对应目录。不要直接覆盖main.js因为electron-vue中有一些写法不同了对于这个文件需要自己仔细移植比如 Vue.use(require('vue-electron'))就与vue-cli不同
3. 运行npm run dev解决报错问题。(主要是因为路径失效造成的问题)
## this.$router.push()报错then() undefined
这有可能是因为你使用了动态加载路由也就是在vue-router中使用了vuex,同时夹杂了Electron-vue模板里的东西。在electron-vue模板使用的store/index.js文件中调用了vuex-electron插件而这些插件貌似会与结构起冲突把这些东西删除了就好了。
```
// import { createPersistedState, createSharedMutations } from 'vuex-electron'
// plugins: [
// createPersistedState(),
// createSharedMutations()
// ],
```
我使用的动态路由是修改自
[PanJiaChen/vue-admin-template](https://note.youdao.com/)。该作者也做了个Electron的项目同样是用Electron-vue模板所以相当值得参考。
## 跨域问题
我一开始也很疑惑为啥electron-vue中为啥没有proxytable。后来发现的确没有必要。如果有只可能是因为你的需求有问题。
vue-cli项目是将打包好的js、html等文件一起放到服务端中之后通过浏览器来浏览。服务端和前端文件是放在一起的。所以为了解决前后端分离的问题才使用跨域转发的方式来解决开发模式需要跨而生成模式就不需要。而Electron开发与生成都需要跨毕竟前端和服务端不在一起。所以也就不存在跨域问题也就不需要ProxyTable了。
我的解决方法是这样:
设置一个config.js作为全局设置文件。在里面设置一个变量用于存储本地服务器ip之后在使用axios编写get与post接口的文件将服务器ip变量加到接口地址前就可以了。例如
```
export const requestLogin = params => {return axios.post(`/login`,params).then(res => res.data); };
```
=>
```
//在config.js中base为127.0.0.1:3000
import {base} from '../config';
export const requestLogin = params => {return axios.post(`${base}/login`,params).then(res => res.data); };
```
如果需要导出web版本则可以使用process.env.IS_WEB来判断是否为web模式。
## 静态文件与启动本地服务问题
之前曾想过在electron里直接导入服务端代码来运行服务器但后来发现不行。github上代码都是通过electron(Node.js)的child_process来启动外部服务器的。
这里我使用pm2来启动这样就不需要在程序关闭时关闭服务器了。child_process 老是无法关闭服务器进程,不知道为什么)
```
let exec = require('child_process').exec;
let server = exec("pm2 start ./bin/www", {cwd: process.cwd()+'/server'});
```
静态文件可以放再electron目录下的static文件夹中(之后会打包成asar文件)不过因为本人写的app需要定时更新文件且文件类型较杂所以使用本地服务端来管理静态文件。
于是对于项目中的需要显示图片或者其他文件链接就需要加入本地服务器ip地址就像上一节那样。
## 打包后静态文件失效问题
__dirname是Electron-vue中在webpack中配置的变量对应开发模式与
产品模式时的electron静态文件目录。但如果你使用了一些webpack中没有设置的文件比如字体会出一些问题请参考这个blog
https://blog.csdn.net/aa661020/article/details/79757859
## 关于自定义协议
本人写的app使用了类似QQ的自定义协议来启动自己写的外部程序并传参在electron中为了安全的问题默认把这个功能屏蔽了。electron中应该可以使用protocol来定义协议来解决问题不过我找到了另一种方法那就是直接使用child_process的exec直接用命令行的方式来启动程序。
直接在渲染进程中xxx.vue文件
```
<script>
const exec = require('child_process').exec;
export default {
methods: {
exec(row){
//s为自定义协议的字符串
let s='xxxxxx';
let url="test://"+ s;
//test为程序名注意后面有空格第二个参数中提供程序的运行位置具体的可以参考node.js文档。
exec("test "+url, {cwd: process.cwd()+'/XXXX'});
}
}
}
</script>
```
不过需要注意在中文windows系统没有修改过默认字符集的浏览器里通过自定义协议来启动程序默认传的是gbk的字符集。而不同系统win10win7字符集是不同的。所以就需要对启动程序进行修改。
## 关于多条命令启动
因为本人写的App需要更新数据库mysql数据库所以本人使用mysql的命令进行恢复数据。
奇怪的是child_process的exec竟然不支持多条语句。所以这里我就使用了child_process的spawn。
多条命令之间使用&&间隔具体的请参照cmd指南还有别的间隔符比如| &
```
let cmd='cd /d C:/Program Files/MySQL/MySQL Server 5.7/bin && mysql -uXXXXX -pXXXXX abcde </managersystem_user.sql && mysql -uXXXXX -pXXXXX abcde </abcde_xxxx.sql';
cmd+=" && mysql -uXXXXX -pXXXXX abcde < /abcde_routines.sql";
let child = spawn(cmd, {
shell: true
});
//这里还可以输入新的命令与显示cmd返回内容具体的请参考node.js文档
child.on('exit', ()=> {
});
```
## 关于启动参数
```
//在主进程中
global.sharedObject = {prop1: process.argv}
```
```
//渲染进程中
var remote = require('electron').remote,
arguments = remote.getGlobal('sharedObject').prop1;
console.log(arguments);
```
## 最后推荐一下苏南大叔的blog
里面有关electron的文章很不错
https://newsn.net

View File

@@ -0,0 +1,61 @@
## 修改electron-packager代码
首先确认electron-packager安装位置全局或者局部之后进入`node_modules\electron-packager\`路径。使用VSCode搜索`packageForPlatformAndArch`。代码如下:
```c#
packageForPlatformAndArch (downloadOpts) {
return download.downloadElectronZip(downloadOpts)
.then(zipPath => {
// Create delegated options object with specific platform and arch, for output directory naming
const comboOpts = Object.assign({}, this.opts, {
arch: downloadOpts.arch,
platform: downloadOpts.platform,
electronVersion: downloadOpts.version
})
if (!this.useTempDir) {
return this.createApp(comboOpts, zipPath)
}
if (common.isPlatformMac(comboOpts.platform)) {
/* istanbul ignore else */
if (this.canCreateSymlinks === undefined) {
return this.testSymlink(comboOpts, zipPath)
} else if (!this.canCreateSymlinks) {
return this.skipHostPlatformSansSymlinkSupport(comboOpts)
}
}
return this.checkOverwrite(comboOpts, zipPath)
})
}
```
添加逻辑:
```
let zipFile="electron-v11.2.1-win32-x64.zip";
console.log(__dirname);
let HasFile=fs.existsSync(zipFile);
if(HasFile)
{
const comboOpts = Object.assign({}, this.opts, {
arch: downloadOpts.arch,
platform: downloadOpts.platform,
electronVersion: downloadOpts.version
})
if (!this.useTempDir) {
return this.createApp(comboOpts, zipFile)
}
if (common.isPlatformMac(comboOpts.platform)) {
/* istanbul ignore else */
if (this.canCreateSymlinks === undefined) {
return this.testSymlink(comboOpts, zipFile)
} else if (!this.canCreateSymlinks) {
return this.skipHostPlatformSansSymlinkSupport(comboOpts)
}
}
return this.checkOverwrite(comboOpts, zipFile)
}
```
其中执行路径即为项目根目录直接将zip放在项目目录中即可。

View File

@@ -0,0 +1,18 @@
## H5 PPT
https://revealjs.com/
http://imakewebthings.com/deck.js/
https://github.com/briancavalier/slides?spm=a2c6h.12873639.0.0.2c404409ftv46I
https://github.com/LeaVerou/inspire.js?spm=a2c6h.12873639.0.0.2c404409ftv46I
https://github.com/impress/impress.js/
VUE PPT插件
https://github.com/faveeo/eagle.js
感觉revealjs的功能较多但好像无法直接移植进入VUE。impressJS适合做商业演示inspireJS适合比较简单的演示。
## Node.js生成pptx
https://www.npmjs.com/package/ppt-template
https://www.npmjs.com/package/nodejs-pptx
https://www.npmjs.com/package/pptxgenjs
pptxgenjs用的人最多。

View File

@@ -0,0 +1,11 @@
## word文档生成可以使用
- docx:https://www.npmjs.com/package/docx
## word模板替换
- generate-docx:https://github.com/telemark/generate-docx
- carbone:https://github.com/carboneio/carbone
- docxtemplater:https://www.npmjs.com/package/docxtemplater
- docxtemplater-image-module-free:https://www.npmjs.com/package/docxtemplater-image-module-free
## 库
FileSaver使用案例https://jsfiddle.net/dolanmiu/onadx1gu/

View File

@@ -0,0 +1,168 @@
# 以下摘自官方文档
## Escaping query values
**Caution** These methods of escaping values only works when the
[NO_BACKSLASH_ESCAPES](https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sqlmode_no_backslash_escapes)
SQL mode is disabled (which is the default state for MySQL servers).
In order to avoid SQL Injection attacks, you should always escape any user
provided data before using it inside a SQL query. You can do so using the
`mysql.escape()`, `connection.escape()` or `pool.escape()` methods:
```js
var userId = 'some user provided value';
var sql = 'SELECT * FROM users WHERE id = ' + connection.escape(userId);
connection.query(sql, function (error, results, fields) {
if (error) throw error;
// ...
});
```
Alternatively, you can use `?` characters as placeholders for values you would
like to have escaped like this:
```js
connection.query('SELECT * FROM users WHERE id = ?', [userId], function (error, results, fields) {
if (error) throw error;
// ...
});
```
Multiple placeholders are mapped to values in the same order as passed. For example,
in the following query `foo` equals `a`, `bar` equals `b`, `baz` equals `c`, and
`id` will be `userId`:
```js
connection.query('UPDATE users SET foo = ?, bar = ?, baz = ? WHERE id = ?', ['a', 'b', 'c', userId], function (error, results, fields) {
if (error) throw error;
// ...
});
```
This looks similar to prepared statements in MySQL, however it really just uses
the same `connection.escape()` method internally.
**Caution** This also differs from prepared statements in that all `?` are
replaced, even those contained in comments and strings.
Different value types are escaped differently, here is how:
* Numbers are left untouched
* Booleans are converted to `true` / `false`
* Date objects are converted to `'YYYY-mm-dd HH:ii:ss'` strings
* Buffers are converted to hex strings, e.g. `X'0fa5'`
* Strings are safely escaped
* Arrays are turned into list, e.g. `['a', 'b']` turns into `'a', 'b'`
* Nested arrays are turned into grouped lists (for bulk inserts), e.g. `[['a',
'b'], ['c', 'd']]` turns into `('a', 'b'), ('c', 'd')`
* Objects that have a `toSqlString` method will have `.toSqlString()` called
and the returned value is used as the raw SQL.
* Objects are turned into `key = 'val'` pairs for each enumerable property on
the object. If the property's value is a function, it is skipped; if the
property's value is an object, toString() is called on it and the returned
value is used.
* `undefined` / `null` are converted to `NULL`
* `NaN` / `Infinity` are left as-is. MySQL does not support these, and trying
to insert them as values will trigger MySQL errors until they implement
support.
This escaping allows you to do neat things like this:
```js
var post = {id: 1, title: 'Hello MySQL'};
var query = connection.query('INSERT INTO posts SET ?', post, function (error, results, fields) {
if (error) throw error;
// Neat!
});
console.log(query.sql); // INSERT INTO posts SET `id` = 1, `title` = 'Hello MySQL'
```
And the `toSqlString` method allows you to form complex queries with functions:
```js
var CURRENT_TIMESTAMP = { toSqlString: function() { return 'CURRENT_TIMESTAMP()'; } };
var sql = mysql.format('UPDATE posts SET modified = ? WHERE id = ?', [CURRENT_TIMESTAMP, 42]);
console.log(sql); // UPDATE posts SET modified = CURRENT_TIMESTAMP() WHERE id = 42
```
To generate objects with a `toSqlString` method, the `mysql.raw()` method can
be used. This creates an object that will be left un-touched when using in a `?`
placeholder, useful for using functions as dynamic values:
**Caution** The string provided to `mysql.raw()` will skip all escaping
functions when used, so be careful when passing in unvalidated input.
```js
var CURRENT_TIMESTAMP = mysql.raw('CURRENT_TIMESTAMP()');
var sql = mysql.format('UPDATE posts SET modified = ? WHERE id = ?', [CURRENT_TIMESTAMP, 42]);
console.log(sql); // UPDATE posts SET modified = CURRENT_TIMESTAMP() WHERE id = 42
```
If you feel the need to escape queries by yourself, you can also use the escaping
function directly:
```js
var query = "SELECT * FROM posts WHERE title=" + mysql.escape("Hello MySQL");
console.log(query); // SELECT * FROM posts WHERE title='Hello MySQL'
```
## Escaping query identifiers
If you can't trust an SQL identifier (database / table / column name) because it is
provided by a user, you should escape it with `mysql.escapeId(identifier)`,
`connection.escapeId(identifier)` or `pool.escapeId(identifier)` like this:
```js
var sorter = 'date';
var sql = 'SELECT * FROM posts ORDER BY ' + connection.escapeId(sorter);
connection.query(sql, function (error, results, fields) {
if (error) throw error;
// ...
});
```
It also supports adding qualified identifiers. It will escape both parts.
```js
var sorter = 'date';
var sql = 'SELECT * FROM posts ORDER BY ' + connection.escapeId('posts.' + sorter);
// -> SELECT * FROM posts ORDER BY `posts`.`date`
```
If you do not want to treat `.` as qualified identifiers, you can set the second
argument to `true` in order to keep the string as a literal identifier:
```js
var sorter = 'date.2';
var sql = 'SELECT * FROM posts ORDER BY ' + connection.escapeId(sorter, true);
// -> SELECT * FROM posts ORDER BY `date.2`
```
Alternatively, you can use `??` characters as placeholders for identifiers you would
like to have escaped like this:
```js
var userId = 1;
var columns = ['username', 'email'];
var query = connection.query('SELECT ?? FROM ?? WHERE id = ?', [columns, 'users', userId], function (error, results, fields) {
if (error) throw error;
// ...
});
console.log(query.sql); // SELECT `username`, `email` FROM `users` WHERE id = 1
```
**Please note that this last character sequence is experimental and syntax might change**
When you pass an Object to `.escape()` or `.query()`, `.escapeId()` is used to avoid SQL injection in object keys.
### Preparing Queries
You can use mysql.format to prepare a query with multiple insertion points, utilizing the proper escaping for ids and values. A simple example of this follows:
```js
var sql = "SELECT * FROM ?? WHERE ?? = ?";
var inserts = ['users', 'id', userId];
sql = mysql.format(sql, inserts);
```
Following this you then have a valid, escaped query that you can then send to the database safely. This is useful if you are looking to prepare the query before actually sending it to the database. As mysql.format is exposed from SqlString.format you also have the option (but are not required) to pass in stringifyObject and timezone, allowing you provide a custom means of turning objects into strings, as well as a location-specific/timezone-aware Date.

View File

@@ -0,0 +1,105 @@
## 前言
最近因为某些原因最近决定升级VUE前端以及Electron APP工程中版本其中Node.js版本从9.9.0升级到12.19.1。因为涉及较多东西,所以写此笔记。
推荐:
- 使用nvm为了保证不会耽误旧版本项目可以使用nvm对node.js版本进行管理。你只需要使用nvm use 版本号,即可切换对应版本。
- 使用版本管理,使得每一步升级都可以回滚。
## vue前端
### vue
项目用的是vue-cli生成。vue版本号需要与vue-template-compiler版本号一致。本人值从高版本一个一个往下试错最后用了个可以跑通的版本。因为用的是npm
### node-sass
如果直接使用npm install -s node-sass可能会引发node-gyp编译而造成的错误。推荐先npm uninstall node-sass将node-sass卸载干净之后再安装。这个时候就会直接下载编译好的node-sass而不是尝试编译。
**node-sass与node.js版本也有关系**,需要注意:
NodeJS | Supported node-sass version | Node Module
---|---|---
Node 15 | 5.0+ | 88
Node 14 | 4.14+ | 83
Node 13 | 4.13+, <5.0 |79
Node 12 |4.12+ | 72
Node 11 |4.10+, <5.0 | 67
Node 10 |4.9+ | 64
Node 8 |4.5.3+, <5.0 | 57
Node <8 |<5.0 | <57
### Element-ui
当低于**2.7.0**版本的Element-ui升级时需要注意要添加对**jsx**语法的支持否则会报一下错误
```
error in ./~/.2.11.1@element-ui/packages/form/src/label-wrap.vue
Syntax Error: Unexpected token (23:14)
21 | }
22 | }
> 23 | return (<div class="el-form-item__label-wrap" style={style}>
| ^
24 | { slots }
25 | </div>);
26 | } else {
@ ./~/.2.11.1@element-ui/packages/form/src/label-wrap.vue 4:2-108
```
解决
```npm
npm install babel-plugin-syntax-jsx babel-plugin-transform-vue-jsx babel-helper-vue-jsx-merge-props babel-preset-env --save-dev
```
在项目目录的.babelrc中添加对jsx插件的配置
```json
{
"plugins": ["transform-vue-jsx", ...]
}
```
## Electron
对于Electron-vue项目还是推荐创建一个Electron-vue新项目之后再将代码移植过去比较好
### require/process/module is not define
因为新版本Electron中默认没有集成node
解决修改src/main/index.js,在webPreferences中加入nodeIntegration: true
```
mainWindow = new BrowserWindow({
height: 563,
useContentSize: true,
width: 1000,
webPreferences: {
nodeIntegration: true,
}
})
```
### can't read property 'app' of
是因为electron10后remote模块默认是关闭的,解决修改src/main/index.js,在webPreferences中加入enableRemoteModule: true
### Element-ui的el-table不显示
解决.electron-vue/webpack.renderer.config.js中的
```
let whiteListedModules = ['vue']
```
加入elementui
```
let whiteListedModules = ['vue', 'element-ui']
```
## Node.js后端sqlite3模块
这里会需要使用node-gyp进行编译这个需要安装visual studio2017因为最高版本就支持到2017不支持2019着实蛋疼即使你给2019安装了2017 版本的构建套件win10sdk与c++ ATL库node-gyp都检测到了依然还会说版本不匹配即使指定了版本为2019)。
如果你安装完2017还是不能正常编译就可以手动指定vs版本了执行
```
npm config set msvs_version 2017
```
node-gyp的具体安装步骤可以参考https://github.com/nodejs/node-gyp#on-windows
## 清除注释
### js 双斜杠
```
//(?!.*\..*\.).*\n
```
### js 多行
```
/\*(.|\r\n|\n)*?\*/
```
### HTML
```
<!--(.|[\r\n])*?-->
```

View File

@@ -0,0 +1 @@
1. Apifox

View File

@@ -0,0 +1,54 @@
> 类似的文章还是比较多的,但或多或少有一些问题没有解决,在此我将其整合并分享给大家:
## 测试环境:
- Node.js 9.9.0
- VisualStudio 2015
- "ffi": "gavignus/node-ffi#torycl/forceset-fix",
- "ref": "1.3.5"
- "ref-array": "1.2.0"
- "ref-struct": "1.1.0"
- "ffi-napi": "^2.4.3"
## 编译失败:
当前情况下编译ffi会失败所以有两种解决方法
1. 使用新的ffi-napiapi是一样的同时支持node.js新的napi
2. 使用第三方修改过的ffi在package.json中将ffi后面的版本号改成
> "ffi": "gavignus/node-ffi#torycl/forceset-fix"
## 使用:
```
var ffi = require('ffi');
//第一个形参为dll所在位置dll文件可以不用加.dll第二个为函数信息
var libm = ffi.Library(__dirname + 'dllFile', {
//函数名
'fun': ['int', ['string', 'string']]
});
//调用
var str1="a";
var str2="b";
libm.fun(str1, str2);
```
## 使用c++里的类型
ref、ref-struct、ref-array、ref-union、ref-wchar
在npm查看使用方法在此不做赘述。
## 运行时遇到的错误
1. c++代码是可以用的但是需要把代码写在extern "C"{}里,不过这个我没有亲自试过。
2. dll文件需要放到node.js 执行目录,也就是
```
//即x:\xxxxx\xx
cd /d x:\xxxxx\xx;
node xxx.js;
```
3. dll如果有互相依赖的必须放全。不然只会出现错误126而不会像一般程序那样提示缺少xxx.dll。所以报错了可以用depends看一下dll全了没。
4. dll的需要与node.js的平台相对应比如你的node.js是64位版本的那你的dll也需要使用64位编译。
错误126检查上述1、2、3步。
## 参考:
- wiki
- https://github.com/node-ffi/node-ffi/wiki/Node-FFI-Tutorial<br>
- https://www.jianshu.com/p/914103283ea0
- https://blog.csdn.net/zhulin2609/article/details/51474676

View File

@@ -0,0 +1,45 @@
# 参考
- https://blog.csdn.net/clearlxj/article/details/108264141
>查看源码发现axios返回的内容默认是Stream格式的
如果没有设置responseType的话返回内容将会从Stream转为Buffer再转为String
如果responseType为stream的话不进行转换
如果responseType为arraybuffer的话将Stream转为Buffer
但是如果想把Buffer转为String再转回Buffer的话将会出问题(默认使用utf8进行Buffer的编码和解码)有的文件这样是可以的但是Excel文件这样做的话两次Buffer的值是不同的但是转为base64的话是都可以的。
# 设置responseType为stream
```js
const result = await require('axios')({ url, method, data, responseType: 'stream' });
result.data.pipe(require('fs').createWriteStream(saveFilePath));
```
提供buffer转Stream并保存为文件的方法
```js
function bufferToStream(bufferData, saveFilePath) {
return new Promise((res, rej) => {
const bufferStream = new stream.PassThrough();
bufferStream.end(bufferData);
const ws = fs.createWriteStream(saveFilePath);
bufferStream.pipe(ws).on('finish', () => {
res(saveFilePath);
});
});
}
```
# 设置responseType为arraybuffer
```js
const result = await require('axios')({ url, method, data, responseType: 'arraybuffer' });
const bufferData = result.data;
await bufferToStream(bufferData, saveFilePath);
```
# 不设置responseType设置responseEncoding为base64
```js
const result = await require('axios')({ url, method, data, responseEncoding: 'base64' });
const bufferData = Buffer.from(result.data, 'base64');
await bufferToStream(bufferData, saveFilePath);
```

View File

@@ -0,0 +1,19 @@
## 坐标
1. Item(图元)坐标:属于局部坐标,通常以图元中心为原点(中心对称)非中心对称类比如dialog类一般以左上角为原点正方向x朝右y朝下。
2. setPos的坐标是父类坐标系的坐标一般对于item位于scene中的应用场景。
3. scene(场景)坐标:属于逻辑坐标 logical coordinates与QPainter相同**以场景中心为原点**正方向x朝右y朝下。
4. 图元原点左上角dialog的原点**与场景原点对齐**,导致图元外边框的左上角顶点在场景中的坐标位置为(负数,负数)。
5. View(视图)坐标属于设备坐标device coordinates与窗口相同**默认以左上点为原点** 正方向x朝右y朝下。
6. 默认场景scene的左上角顶点与视图坐标原点对齐。**显示时默认中心对齐**,当场景大小小于视图大小的时候,将中心对齐,此中指的仍然是整个图元的中心,同时,图元原点与场景原点对齐,场景左上角顶点与视图原点对齐,视图左上角顶点不一定是原点???,此时也将出现视图坐标有正值有负值。
- 1translate()将当前视图坐标原点平移从而实现显示图像的平移变换。由于默认场景的左上角顶点与视图坐标原点对齐translate()将坐标原点平移,也就实现了将场景的平移。
- 2rotate()将当前视图围绕视图坐标原点旋转,从而实现显示图像的旋转变换。
- 3size()返回视图大小默认大小100*30由于视图可以是无限大小而且只有在显示后才创建出实际尺寸因此只有在showEvent中调用 size 函数才能正确显示视图大小否则都是返回默认的100*30因为此时视图尚未显示即尚未形成。
https://www.cnblogs.com/cthu/p/5103551.html
## 常用的QGraphicsItem
https://blog.csdn.net/liang19890820/article/details/53065293
## QGraphics
https://blog.csdn.net/liang19890820/article/details/51966791

View File

@@ -0,0 +1,79 @@
## 注意点
隐藏状态栏时第一个界面不能使用Qt设计师界面类创建项目时不勾选**创建界面**选项)。不然以下方法皆会无效。
## 方法1
修改AndroidManifest.xml中activity的主题。即在`<activity>`标签中添加 `android:theme="@android:style/Theme.NoTitleBar.Fullscreen"`
以下为实例代码:
```
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density"
android:name="an.qt.helloQtQuickApp.QtFullscreenActivity"
android:label="-- %%INSERT_APP_NAME%% --"
android:screenOrientation="unspecified"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:launchMode="singleTop">
```
如果无效则需要重写QtActivity的onCreate函数。
大致步骤如下:
1. 在项目目录\android\src\目录下创建与包名相同的目录。例如我们要创建的包为an.qt.helloQtQuickApp那么路径为an\qt\helloQtQuickApp。并创建一个Java文件用于定义我们的Activity。文件名可以随便取,我这里的文件名设为QtFullscreenActivity.java。
2. 修改AndroidManifest.xml文件使用我们刚才新定义的Activity。
` <manifest>` 标签中的package改成我们创建的包名。将`<activity>`标签中android:name改成an.qt.helloQtQuickApp.QtFullscreenActivity,即包名.新创建的Activity类名。
>Java文件代码如下
```
package an.qt.helloQtQuickApp;
import android.content.Context;
import android.content.Intent;
import android.app.PendingIntent;
import android.util.Log;
import android.os.Bundle;
import android.view.WindowManager;
public class QtFullscreenActivity extends org.qtproject.qt5.android.bindings.QtActivity
{
private final static String TAG = "QtFullscreen";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
}
```
详细操作过程可以参看安晓辉的blog
https://blog.csdn.net/foruok/article/details/38265349
## 方法2
大致思路如下使用Qt5.7后在QtAndroid类新增了一个静态函数
```
void QtAndroid::runOnAndroidThread(const QtAndroid::Runnable &runnable)
```
通过它调用以下Java代码
```
Activity activity = (Activity) mContext;
View decorView = activity.getWindow().getDecorView();
decorView.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_IMMERSIVE
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN);
```
这种方法的好处在于可以在运行时控制是否隐藏状态栏而且不用改AndroidManifest.xml文件移植方便。
详细操作可以见作者Blog
https://blog.csdn.net/jun4331247/article/details/80739662
项目Github
https://github.com/WingNan/QtAndroidFullScreen
### 移植步骤
1. 复制文件jfullscreen.h、jfullscreen.cpp至你的项目目录。复制android/src目录及其内部文件至项目同名目录。
2. 在项目上右键点击“添加现有文件",将以上几个文件添加到项目中。
3. 编辑项目文件(*.pro)添加androidextras模块在Qt+=core gui后面加上androidextras
4. 在app的第一个widget中#include "jfullscreen.h",并在构造函数中加入:
```
JFullScreen *pManager = new JFullScreen;
pManager->fullScreenStickyImmersive();
```
注意创建项目时不能勾选“创建界面”选项并且第一个widget不能使用设计师界面类来创建

View File

@@ -0,0 +1,24 @@
# 前言
参考:
- https://doc.qt.io/qt-6/windows-building.html
- 构建选项:https://doc.qt.io/qt-6/configure-options.html
- https://www.cnblogs.com/eslzzyl/p/17270948.html
# 编译步骤
1. 安装Ninja、Perl、Python、CMake并且设置环境变量。
2. 在指定目录下创建`Src``Build``Install`文件夹内。
3. 将源码下载包解压至`Src`中。
4. 进入`Build`文件夹,并且输入`..\Src\qtbase\configure -prefix ..\Install -debug-and-release`
5. 编译`cmake --build . --parallel`
6. `cmake --install .`
之后安装QtCreator进行编译。
PS.感觉不太行,最后还是通过网络安装的方式进行安装。
# Qt6 CMake
https://www.jetbrains.com/help/clion/qt-tutorial.html#cmake-qt
1. 使用QtCreator创建项目之后使用CMake打开CMakeLists.txt。
2. 设置`CMAKE_PREFIX_PATH``6.5.2\msvc2019_64\lib\cmake`,例如:`C:\Qt\6.5.2\msvc2019_64\lib\cmake`
3. 之后依次点Configuration与Generate。

View File

@@ -0,0 +1,53 @@
## 字体文件下载
fontawesome-webfont.ttf另一个是pe-icon-set-weather.ttf
fontawesome-webfont.ttf 下载地址http://fontawesome.dashgame.com/
pe-icon-set-weather.ttf 下载地址https://www.pixeden.com/icon-fonts/the-icons-font-set-weather
在pixeden中还有许多其它的图标字体库下载https://www.pixeden.com/icon-fonts
## Code
```
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QFontDatabase>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//引入图形字体
int fontId = QFontDatabase::addApplicationFont(":/image/pe-icon-set-weather.ttf"); //加入字体并获取字体ID
QString fontName = QFontDatabase::applicationFontFamilies(fontId).at(0); //获取字体名称
QFont iconFont = QFont(fontName);
iconFont.setPixelSize(128); //设置字体大小
ui->lab_e901->setFont(iconFont); //设置Label的字体
ui->lab_e901->setText(QChar(0xe901)); //设置Label的文体
ui->lab_e901->setStyleSheet("color:red;");
QPalette blue_pe;
blue_pe.setColor(QPalette::WindowText,Qt::blue);
ui->lab_e903->setFont(iconFont);
ui->lab_e903->setText(QChar(0xe903));
ui->lab_e903->setPalette(blue_pe);
ui->lab_e905->setFont(iconFont);
ui->lab_e905->setText(QChar(0xe905));
ui->lab_e907->setFont(iconFont);
ui->lab_e907->setText(QChar(0xe907));
}
MainWindow::~MainWindow()
{
delete ui;
}
```
## QFontDatabase
QStringList QFontDatabase::families(QFontDatabase::WritingSystem writingSystem = Any) const
返回可用的字体列表。
QFont QFontDatabase::font(const QString &family, const QString &style, int pointSize) const
返回一个可用的字体对象。

View File

@@ -0,0 +1,104 @@
## 通用选择器
* 作为选择器,作用于所有的 widget。
类型选择器
类名 作为选择器,作用于它自己和它的所有子类。
```
QFrame {
background: gray;
}
```
## 类选择器
. + 类名 或者 . + class 的属性值 作为选择器(使用 setProperty(“class”, “QSSClassName”) 设置),只会作用于它自己,它的子类不受影响,注意和类型选择器的区别。
```
app.setStyleSheet(".QWidget { background: gray; }"
".RedButton { background: magenta; }");
// .RedButton 将作为类选择器
openButton->setProperty("class", "RedButton");
closeButton->setProperty("class", "RedButton");
```
## ID 选择器
'#' + objectName 作为选择器,只作用于用此 objectName 的对象
```
// #openButton 和 #closeButton 作为 ID 选择器
app.setStyleSheet(".QWidget { background: gray; }"
"#openButton, #closeButton { background: magenta; }");
openButton->setObjectName("openButton");
closeButton->setObjectName("closeButton");
```
## 属性选择器
选择器[属性="值"] 作为选择器,这个属性可用通过 object->property(propertyName) 访问的Qt 里称为 Dynamic Properties。
如上面的程序, openButton 和 closeButton 的背景是洋红色的,但是 saveButton 不受影响,也可以使用属性选择器来实现:
```
app.setStyleSheet(".QWidget { background: gray; }"
"QPushButton[level='dangerous'] { background: magenta; }");
openButton->setProperty("level", "dangerous");
```
属性选择器可以包含多个属性,只需要:
```
QPushButton[level='aaa'],QPushButton[level='bbb'] { background: magenta; }
```
如果有多个属性,则会优先选择匹配数多的样式。
## 包含选择器
英语叫做 Descendant Selectordescendant 的表达比较到位。
选择器之间用空格隔开,作用于 Widget 的 子Widget子Widget 的 子Widget……子子孙孙无穷尽也。
```
QFrame {
background: gray;
}
/* 设置 QFrame 中的 QPushButton 的 QSS */
QFrame QPushButton {
border: 2px solid magenta;
border-radius: 10px;
background: white;
padding: 2px 15px;
}
```
## 子元素选择器
选择器之间用 > 隔开,作用于 Widget 的直接 子Widget注意和包含选择器的区别。
```
QFrame {
background: gray;
}
QFrame > QPushButton {
border: 2px solid magenta;
border-radius: 10px;
background: white;
padding: 2px 15px;
}
```
## 伪类选择器
选择器:状态 作为选择器,支持 ! 操作符,表示 非。
```
QPushButton:hover { color: white }
QCheckBox:checked { color: white }
QCheckBox:!checked { color: red }
```
## Subcontrol 选择器
选择器::subcontrol 作为选择 Subcontrol 的选择器。
有些 widget 是由多个部分组合成的,例如 QCheckBox 由 icon(indicator) 和 text 组成,可以使用 选择器::subcontrol 来设置 subcontrol 的样式:
```
QCheckBox::indicator {
width: 20px;
height: 20px;
}
QCheckBox {
spacing: 8px;
}
```

View File

@@ -0,0 +1,102 @@
## 参考项目
https://blog.csdn.net/liang19890820/article/details/50557240
https://www.cnblogs.com/feiyangqingyun/p/3915657.html
QSS模板网站https://qss-stock.devsecstudio.com/
## 技巧
Qt内置图标封装在QStyle中大概七十多个图标可以直接拿来用。
- SP_TitleBarMenuButton,
- SP_TitleBarMinButton,
- SP_TitleBarMaxButton,
- SP_TitleBarCloseButton,
- SP_MessageBoxInformation,
- SP_MessageBoxWarning,
- SP_MessageBoxCritical,
- SP_MessageBoxQuestion,
## 配色
ElementUI配色https://element.eleme.cn/#/zh-CN/component/color
Brand Color `#409EFF`
**辅助色**
- Success#67C23A
- Warning#E6A23C
- Danger#F56C6C
- Info#909399
**基础色**
- 主要文字#303133
- 常规文字#606266
- 次要文字#909399
- 占位文字#C0C4CC
- 一级边框#DCDFE6
- 二级边框#E4E7ED
- 三级边框#EBEEF5
- 四级边框#F2F6FC
## 边框
- 实线 1px
- 虚线 2px
### 圆角
- 无圆角border-radius: 0px
- 小圆角border-radius: 2px
- 大圆角border-radius: 4px
- 圆形圆角border-radius: 30px
### 投影
- 基础投影 box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04)
- 浅色投影 box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1)
## QSS设置函数
feiyangqingyun-QWidgetDemo-master/FlatUI<br>
使用函数的方式来生成QSS代码与设置对应的控件。
```
QString BRUI::setPushButtonQss(QPushButton *btn, int radius, int padding,const QString &normalColor,const QString &normalTextColor,const QString &hoverColor,const QString &hoverTextColor,const QString &pressedColor,const QString &pressedTextColor)
{
QStringList list;
list.append(QString("QPushButton{border-style:none;padding:%1px;border-radius:%2px;color:%3;background:%4;}").arg(padding).arg(radius).arg(normalTextColor).arg(normalColor));
list.append(QString("QPushButton:hover{color:%1;background:%2;}").arg(hoverTextColor).arg(hoverColor));
list.append(QString("QPushButton:pressed{color:%1;background:%2;}").arg(pressedTextColor).arg(pressedColor));
QString qss = list.join("");
if(btn)
btn->setStyleSheet(qss);
return qss;
}
```
## 可以参考的UI工程
QSS生成工具StyleDemo。
给QWidget添加属性来控制样式
```
ui->widgetLeft->setProperty("nav", "left");
ui->widgetBottom->setProperty("form", "bottom");
ui->widgetTop->setProperty("nav", "top");
ui->widgetVideo->setProperty("video", true);
```
Qss
```
QWidget[nav="left"] QAbstractButton:checked,QWidget[nav="left"] QAbstractButton:pressed{
color:#386487;
border-style:solid;
border-width:0px 0px 0px 2px;
padding:4px 4px 4px 2px;
border-color:#00BB9E;
background-color:#EAF7FF;
}
```
### 切换器
ImageSwitch
### 日历
lunarcalendarwidget
### 导航按钮
NavButton
## StyleDemo中的CSS
flatwhite.css
lightblue.css
psblack.css

View File

@@ -0,0 +1,121 @@
## 前言
本人发现有关QML文件之间的通讯的资料不多而且都不太好找所以在这里总结一下方便后续的开发者
## 直接导入qml
目录结构:
```
myapp
|- mycomponents
|- CheckBox.qml
|- DialogBox.qml
|- Slider.qml
|- main
|- application.qml
```
那么可以:
```
import "../mycomponents"
DialogBox {
CheckBox {
// ...
}
Slider {
// ...
}
}
```
但是创建一个命名空间来导入会更好
```
import "../mycomponents" as MyComponents
MyComponents.DialogBox {
// ...
}
```
## qml文件间通讯
3.题回正传直接上代码以StackView管理页面为例
(1)page1.qml跳转到page2.qml传值
```
page1.qml
Rectangle
{
id:rect1
...
MouseArea {
id: maStartQuery
anchors.fill: parent
onClicked:
{
if(!stackView.busy)
stackView.push(Qt.resolvedUrl("qrc:///qml/page2.qml"),
{name:"张三"})//给page2.qml的name传值“张三”,name必须在page2.qml中定义成属性
}
}
...
}
page2.qml定义如下
Rectangle
{
id:rect2
...
property string name:""//要传的值
...
}
```
(2)page2.qml点击"确定"按钮时将结果返回给page1.qml
A.在page1.qml中增加一个函数clickedfunc当点击page2.qml中"确定"按钮时调用;
B.在page2.qml中增加一个属性containerqml用来记录page1.qml
C.在从page1.qml跳转到page2.qml时将rect1传给page2.qml的containerqml属性。
```
page1.qml
Rectangle
{
id:rect1
...
MouseArea {
id: maStartQuery
anchors.fill: parent
onClicked:
{
if(!stackView.busy)
stackView.push(Qt.resolvedUrl("qrc:///qml/page2.qml"),
{name:"张三",containerqml:rect1})
}
}
//当点击page2.qml中"确定"按钮时调用
  function clickedfunc(temp)
{
console.log("改成了:"+temp);
 stackView.pop();//返回到本页
}
...
}
page2.qml
Rectangle
{
id:rect2
...
property variant containerqml: null
property string name:""//要传的值
...
MouseArea {
id: btnOK
anchors.fill: parent
onClicked:
{
containerqml.clickedfunc("李四");//调用page1.qml中的函数实现了传返回值。
}
}
}
```
另一种就是信号与槽

View File

@@ -0,0 +1,163 @@
## color属性
可以使用"blue"、"#RRGGBB"、Qt.rgba()来赋值。具体可以参考QML Basic Type:color
## 手机的横屏模式与竖屏模式
需要修改AndroidManifest.xml中的activity元素的android:screenOrientation属性为"landscape"或"portrait"
## Item组件
1. Item是所有可视元素的基类。
2. 其中一个用处就是可以分组其他可见图元。
3. clip属性如果为true可以裁剪子组件。
4. 通过附加属性Keys来处理按键。详情Keys QML Type
## Text
可以使用HTML修饰过的文本
## Image
1. 如何设置了width与height图片可能会被拉伸此时fillMode属性就可以设置填充模式了。
2. Image默认是阻塞式加载可以通过把asynchronous设为true开启异步模式。可以先显示一个加载图标当status的值为Image.Ready时再显示。
3. source属性是url网络模式会默认开启异步加载此时Image的Progress0。0~1.0、status都会实时更新。
```
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Controls.Styles 1.4
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("App")
BusyIndicator{
id:busy;
running: true;
anchors.centerIn: parent;
z:2
}
Text{
id:stateLabel;
visible:false;
anchors.centerIn: parent;
z:3
}
Image{
id:imageViewer;
asynchronous: true;
cache:false;
anchors.fill: parent;
fillMode: Image.PreserveAspectFit;
onStatusChanged: {
if(imageViewer.status===Image.Loading){
busy.running=true;
stateLabel.visible=false;
}else if (imageViewer.status===Image.Ready){
busy.running=false;
}else if(imageViewer.status===Image.Error){
busy.running=false;
stateLabel.visible=true;
stateLabel.text="error";
}
}
}
Component.onCompleted: {
imageViewer.source="http://img.zcool.cn/community/01d881579dc3620000018c1b430c4b.JPG@3000w_1l_2o_100sh.jpg";
}
}
```
## Object类是所有ECMAScript类的基类它具有以下属性
1. constructor指向创建对象的函数对于Object类它指向object()函数。
2. prototype对该对象原型类型的引用可以在运行时改变原型。
3. hasOwnPropety是否拥有某个属性
4. isPrototypeOf判断对象是否为另一个对象的原型。
5. ptopertyIsEnumerable判断给定的杏树是否可以用for in进行枚举。
6. toString()
7. valueOf(),返回最适合该对象的原始值。
## Qt对象
Qt是QML提供的一个全局宿主对象整合了常用的属性、方法与枚举类型。
Qt.application 应用的全局状态
## Connections
适用对象:
1. 你需要将多个对象连接到同一个QML信号上。
2. 你需要在发出信号的对象的作用域之外来建立连接
3. c++导出对象
```
Connections{
target:area;//目标控件(它的信号触发)
on<Signal>:function or code block;//执行代码
}
```
## 连接信号的方式
```
Rectangle {
id: relay
signal messageReceived(string person, string notice)
//可以连接信号或者函数
Component.onCompleted: {
relay.messageReceived.connect(sendToPost)
relay.messageReceived.connect(sendToTelegraph)
relay.messageReceived.connect(sendToEmail)
relay.messageReceived("Tom", "Happy Birthday")
}
function sendToPost(person, notice) {
console.log("Sending to post: " + person + ", " + notice)
}
function sendToTelegraph(person, notice) {
console.log("Sending to telegraph: " + person + ", " + notice)
}
function sendToEmail(person, notice) {
console.log("Sending to email: " + person + ", " + notice)
}
}
```
## component
### 嵌入式组件:
```
Component {
id: redSquare
Rectangle {
color: "red"
width: 10
height: 10
}
}
```
只能包含一个顶层item与id<br>
### 单文件组件
单文件组件不需要加Component
#### 使用Loader
```
Item {
Component {
id: redSquare
Rectangle { color: "red"; width: 10; height: 10 }
}
Loader { sourceComponent: redSquare }
Loader { sourceComponent: redSquare; x: 10 }
}
```
通过sourceComponent加载组件source则可以通过url加载单文件组件。<br>
在加载完成后(onLoaded)后可以通过Loader的item操作已经加载的组件<br>
如果Loader加载的Item想要处理按键事件那么久必须将Loader对象的focus属性设置为true同时也需要对它加载的Item的accepted属性设置为true以免已经被吃掉的事件再传递给Loader。
### ECMAScript中动态创建对象
```
var newObject = Qt.createQmlObject('import QtQuick 2.0; Rectangle {color: "red"; width: 20; height: 20}',
parentItem,
"dynamicSnippet1");
```
或者
```
var component = Qt.createComponent("Button.qml");
if (component.status == Component.Ready)
component.createObject(parent, {"x": 100, "y": 100});
```

View File

@@ -0,0 +1,113 @@
## 前言
最近在为单位做一个简单的手机App基于Qt技术栈的选择了QtQuick来开发。不得不说QtQucik开发的确舒服很多东西都不用写就可以只用UI定义起来也比较自由。但是本人想通过cookie来作为登陆验证时就发现QtQuick实现起来相当麻烦。主要是没有文档资料只找到一篇qyvlik写的。我也不想直接用WebEngine
## 不带cookie
可以使用XMLHttpRequest比较坑的是官方竟然没有任何案例不过Api都是与js的XmlHttpRequest一样的以下是qyvlik封装的一套分辨操作函数
```
//通过Json对象输出url的query字符串
function urlQuery(jsonObject) {
var query = "";
var i = 0;
for(var iter in jsonObject) {
if(i > 0) {
query += "&";
}
query += iter +"=" + encodeURI(jsonObject[iter]);
i++;
}
// console.log("url query:", query);
return query;
}
//设置头
function setHeader(xhr, headers) {
//"Content-Type":"application/x-www-form-urlencoded"
for(var iter in headers) {
xhr.setRequestHeader(iter, headers[iter]);
}
}
//这里我修改了一下函数的形参从使用的角度来看回调函数一般都会有但是headers不一定要设置所以调换了一下位置
function ajax(method, url, callable,headers,data) {
headers = headers || {};
callable = callable || function(xhr) {
console.log("没有设置callable使用默认log函数")
console.log(xhr.status);
console.log(xhr.responseText);
}
var xhr = new XMLHttpRequest;
xhr.onreadystatechange = function() {
if(xhr.readyState == xhr.DONE) {
callable(xhr);
}
}
xhr.open(method, url);
setHeader(xhr, headers);
if("GET" === method) {
xhr.send();
} else {
xhr.send(data);
}
}
```
为了能够重复利用本人将这些代码都放入一个js文件中之后使用导入的方式重复利用。注意导入的命名控件首字母需要大写
```
import "xmlhttprequest.js" as XmlHttpRequest
```
使用:
```
var jsonObject={user:"admin",password:Qt.md5("123")};
XmlHttpRequest.ajax("GET","http://192.168.3.108:3000/landing"+"?"+XmlHttpRequest.urlQuery(jsonObject),function(xhr){
console.log(xhr.status);
console.log(xhr.responseText);
if(JSON.parse(xhr.responseText).message==="ok") {
stack.push("qrc:/resource/qml/listview.qml",{stack:stack,uifont:uifont});
}else{
message.show("用户名或者密码错误!",2000);
}
});
```
## 带cookie
因为XmlHttpRequest是不能带有cookie的所以只能通过c++导出QNetworkAccessManager、QNetworkRequest、QNetworkReply具体的代码可以参考https://github.com/qyvlik/QmlNetwork。qyvlik封装了一套QML对象我直接拿来用了很可惜他的使用说明写的不太详细有一些操作需要直接看他写的微信案例才能搞定。
```
NetworkAccessManager { id: manager }
NetworkResponse { id: response }
NetworkRequest { id: request }
Buffer { id: buffer }
function initWebWeiXinInfo() {
var url = "http://192.168.3.108:3000/landing";
var data = {
user:"admin",
password:"123"
};
if(buffer.isOpen()) {
buffer.close();
}
buffer.data = JSON.stringify(data);
if(!buffer.open(IODevice.ReadOnly)) {
console.log(buffer.errorString());
}
request.clear();
request.url = url;
// request.setHeader("Cookie", cookie);
request.setHeader("Content-Type", "application/json")
request.ioDevice = buffer;
connectSignalOnce(response.finished,function() {
console.log("data:", buffer.data)
var headers = response.getAllResponseHeaders();
for(var iter in headers) {
console.log(headers[iter]);
}
console.log(response.responseContent);
});
manager.post(request, response);
}
```
## 结语
感觉qyvlik封装的东西比较多需求上本人也就用用Get与Post以及cookie以后有时间会重新封装一个简单版本。

View File

@@ -0,0 +1,118 @@
## 自定义控件样式
请在Qt帮助索引中输入Customizing a Control进行查看<br>
不过实际用下来感觉除非你想自己实现一套效果复杂的UI或是创造一个全新控件比如给UI添加模糊、虚化等ShaderEffect效果。不然不推荐用这个。比如本人就是想把CheckBox的大小改小同时不改变显示样式这个就很难办到。<br>
## 系统自带的几种主题风格
1. Default Style
2. Fusion Style
3. Imagine Style
4. Material Style
5. Universal Style
其中Imagine Style是使用图片定制风格图片需要按照指定的命名来放置具体操作请看文档
http://doc.qt.io/qt-5/qtquickcontrols2-imagine.html
### 在c++中使用QQuickStyle
```
QQuickStyle::setStyle("Material");
```
具体内容请在帮助索引中搜索 QQuickStyle
### 命令行中设置
```
./app -style material
```
### 在Qt的环境变量中设置
```
QT_QUICK_CONTROLS_STYLE=universal ./app
```
### 使用配置文件
```
[Controls]
Style=Material
```
官方的gallery案例用的是这种。
网上有个哥们用的是:
```
if (sty == "mat") {
qputenv("QT_QUICK_CONTROLS_CONF", ":/qtquickcontrols2material.conf");
} else {
qputenv("QT_QUICK_CONTROLS_CONF", ":/qtquickcontrols2universal.conf");
}
```
本人用的是gallery案例中的方式感觉通过设置环境变量来指定对应的conf不太灵活所以上述方式仅供参考。<br>
想要看懂conf文件需要看以下两篇文档
## Qt Quick Controls 2 Configuration File
文档http://doc.qt.io/qt-5/qtquickcontrols2-configuration.html
在默认情况下将文件放置于:/qtquickcontrols2.conf也就是根目录就会生效需要设置QQuickStyle
### Controls Section
Style定义全局控件样式
### XXXX Section
对对应的style进行设置
### Font Configuration
设置字体,有以下几个属性:
1. Family
2. PointSize
3. PixelSize
4. StyleHint
5. Weight
6. Style
### Palette Configuration
Palette我不太清楚是干什么的
## Material Style
文档http://doc.qt.io/qt-5/qtquickcontrols2-material.html#material-theme-attached-prop<br>
你可以单独给某一些控件设置style,以下是对应的属性:
### accent
```
Button {
text: qsTr("Button")
highlighted: true
Material.accent: Material.Orange
}
```
### background
```
Button {
text: qsTr("Button")
highlighted: true
Material.background: Material.Teal
}
```
### elevation
控制阴影的属性
```
Pane {
width: 120
height: 120
Material.elevation: 6
Label {
text: qsTr("I'm a card!")
anchors.centerIn: parent
}
}
```
### foreground
```
Button {
text: qsTr("Button")
Material.foreground: Material.Pink
}
```
### primary
### theme
三个可选项:
1. Material.Light
2. Material.Dark
3. Material.System
```
Pane {
Material.theme: Material.Dark
Button {
text: qsTr("Button")
}
}
```
## 自定义主题
文档http://doc.qt.io/qt-5/qtquickcontrols2-customize.html#creating-a-custom-style
推荐http://www.cnblogs.com/Fuss/archive/2015/03/20/4353698.html<br>
代码没怎么看Control用的是1.0可以作为参考Github上有关这种UI定制的代码还是比较多建议先去知乎搜索 “请问有哪些优质又开源的qml应用”

View File

@@ -0,0 +1,291 @@
## 参考网址
https://blog.csdn.net/wsj18808050/article/details/54234956
Qt获取屏幕物理长度和宽度CM
https://blog.csdn.net/wsj18808050/article/details/54345537
Qt5.8新特新QtLite使用方法以及缩减应用体积的效果
https://blog.csdn.net/wsj18808050/article/details/55808104
QML 中的屏幕适配问题
https://blog.csdn.net/qyvlik/article/details/51241425
QT59HTTP POST GET COOKIE 网络编程
```c++
#include <QNetworkCookie> //单个cookie
#include <QNetworkCookieJar> //储存cookie
```
https://blog.csdn.net/qq_16234613/article/details/53783391
qt 获取部分的cookie信息 如何把获取的cookie转换为QString类型 正则表达式
https://blog.csdn.net/qq_22403265/article/details/51333226
### cookiebrowser使用webview载入cookie
```
m_store = m_webview->page()->profile()->cookieStore();
m_store->loadAllCookies();
```
### QML Settings 小的示例
Setting 可以存储一些变量就想配置文件一样
https://www.cnblogs.com/hbrw/p/6744094.html
### 直接导入qml
Importing QML Document Directories
目录结构:
```
myapp
|- mycomponents
|- CheckBox.qml
|- DialogBox.qml
|- Slider.qml
|- main
|- application.qml
```
那么可以:
```
import "../mycomponents"
DialogBox {
CheckBox {
// ...
}
Slider {
// ...
}
}
```
但是创建一个命名空间来导入会更好
```
import "../mycomponents" as MyComponents
MyComponents.DialogBox {
// ...
}
```
### qml文件间通讯
3.题回正传直接上代码以StackView管理页面为例
(1)page1.qml跳转到page2.qml传值
```
page1.qml
Rectangle
{
id:rect1
...
MouseArea {
id: maStartQuery
anchors.fill: parent
onClicked:
{
if(!stackView.busy)
stackView.push(Qt.resolvedUrl("qrc:///qml/page2.qml"),
{name:"张三"})//给page2.qml的name传值“张三”,name必须在page2.qml中定义成属性
}
}
...
}
page2.qml定义如下
Rectangle
{
id:rect2
...
property string name:""//要传的值
...
}
```
(2)page2.qml点击"确定"按钮时将结果返回给page1.qml
A.在page1.qml中增加一个函数clickedfunc当点击page2.qml中"确定"按钮时调用;
B.在page2.qml中增加一个属性containerqml用来记录page1.qml
C.在从page1.qml跳转到page2.qml时将rect1传给page2.qml的containerqml属性。
```
page1.qml
Rectangle
{
id:rect1
...
MouseArea {
id: maStartQuery
anchors.fill: parent
onClicked:
{
if(!stackView.busy)
stackView.push(Qt.resolvedUrl("qrc:///qml/page2.qml"),
{name:"张三",containerqml:rect1})
}
}
//当点击page2.qml中"确定"按钮时调用
  function clickedfunc(temp)
{
console.log("改成了:"+temp);
 stackView.pop();//返回到本页
}
...
}
page2.qml
Rectangle
{
id:rect2
...
property variant containerqml: null
property string name:""//要传的值
...
MouseArea {
id: btnOK
anchors.fill: parent
onClicked:
{
containerqml.clickedfunc("李四");//调用page1.qml中的函数实现了传返回值。
}
}
}
```
另一种就是信号与槽
### QtQuick 母版页
```
//~ Panel.qml
Item {
property alias headerHeight: headerLoader.height
property alias footerHeight: footerLoader.height
property Component headerComponent: null
readonly property Item headerItem: headerLoader.item
Loader {
id: headerLoader
width: parent.width
height: 40
sourceComponent: headerComponent
Binding {
target: headerLoader.item
property: "anchors.fill"
value: headerLoader
}
}
property Component footerComponent: null
readonly property Item footerItem: footerLoader.item
Loader {
id: footerLoader
width: parent.width
height: 40
anchors.bottom: parent.bottom
Binding {
target: footerLoader.item
property: "anchors.fill"
value: footerLoader
}
}
property Component contentComponent: null
readonly property Item contentItem: contentLoader.item
Loader {
id: contentLoader
width: parent.width
anchors.top: headerLoader.bottom
anchors.bottom: footerLoader.top;
Binding {
target: contentLoader.item
property: "anchors.fill"
value: contentLoader
}
}
}
Panel {
headerComponent: Rectangle {
color: "black"
}
footerComponent: Rectangle {
color: "black"
}
contentComponent: ListView {
delegate: Rectangle { width: parent.width; height: 40; color: "green" }
model: 10
}
}
```
### 全局单例模式
1. 入口文件的 id 和属性
2. 静态 JavaScript 文件
3. qml 单例QML 实现
4. qml 单例c++ 实现
5. 注册上下文属性
### 表单提交不包括cookies
```
function urlQuery(jsonObject) {
var query = "";
var i = 0;
for(var iter in jsonObject) {
if(i > 0) {
query += "&";
}
query += iter +"=" + encodeURI(jsonObject[iter]);
i++;
}
// console.log("url query:", query);
return query;
}
```
```
function setHeader(xhr, headers) {
//"Content-Type":"application/x-www-form-urlencoded"
for(var iter in headers) {
xhr.setRequestHeader(iter, headers[iter]);
}
}
function ajax(method, url, headers, data, callable) {
headers = headers || {};
callable = callable || function(xhr) {
console.log(xhr.responseText);
}
var xhr = new XMLHttpRequest;
xhr.onreadystatechange = function() {
if(xhr.readyState == xhr.DONE) {
callable(xhr);
}
}
xhr.open(method, url);
setHeader(xhr, headers);
if("GET" === method) {
xhr.send();
} else {
xhr.send(data);
}
}
```
如果带有指定名称的头部已经被指定了,这个头部的新值就是:之前指定的值,加上逗号、空白以及这个调用指定的值。
如果 open() 调用指定了认证资格XMLHttpRequest 自动发送一个适当的 Authorization 请求头部。但是,你可以使用 setRequestHeader() 来添加这个头部。类似地,如果 Web 服务器已经保存了和传递给 open() 的 URL 相关联的 cookie适当的 Cookie 或 Cookie2 头部也自动地包含到请求中。可以通过调用 setRequestHeader() 来把这些 cookie 添加到头部。XMLHttpRequest 也可以为 User-Agent 头部提供一个默认值。如果它这么做,你为该头部指定的任何值都会添加到这个默认值后面。
有些请求头部由 XMLHttpRequest 自动设置而不是由这个方法设置,以符合 HTTP 协议。这包括如下和代理相关的头部:
Host
Connection
Keep-Alive
Accept-charset
Accept-Encoding
If-Modified-Since
If-None-Match
If-Range
Range
### XMLHttpRequest设置cookie的问题
https://segmentfault.com/a/1190000004322487
### 如何获取指定objectName的QObject

View File

@@ -0,0 +1,23 @@
## 信号与槽
信号与槽都可以在qml中访问
### Q_INVOKABLE宏
在定义一个类的成员函数时使用Q_INVOKABLE宏来修饰就可以让该方法被元对象系统调用。也就是注册到元对象系统中
例如:
```
Q_INVOKABLE void setAlgorithm(GenerateAlgorithm algorithm);
```
### Q_ENUMS宏
使用Q_ENUMS(枚举名)的方式来注册枚举类型
### Q_PROPERTY宏
Q_PROPERTY宏用来定义可通过元对象系统访问的属性通过它定义的属性可以在QML中访问、修改也可以通过在属性变化时发射特定信号。
### 注册一个QML可用类型
1. 实现c++类
2. 注册QML类型
3. 在QML中导入类型
4. 在QML中创建由c++导出的类型的实例并使用
#### 注册QML类型
1. qmlRegisterSingletonType() 用来注册单例类型
2. qmlRegisterType() 注册非单例类型
3. qmlRegisterTypeNotAvailable() 注册一个类型用来占位
4. qmlRegisterUncreateableType() 注册具有附加属性的附加类型

View File

@@ -0,0 +1,24 @@
## subdirs
新建项目-其他项目-子目录项目。
### 手动填在已有项目
直接在
```
TEMPLATE = subdirs
SUBDIRS += \
AssetEncrypt \
OutlineTool \
TechnicalSupport
```
### 编译顺序
不推荐使用顺序构建命令,因为这会损失多线程构建的性能优势。官方推荐使用编译依赖:
```
# 创建编译依赖以控制编译顺序
TechnicalSupport.depends = AssetEncrypt
TechnicalSupport.depends = OutlineTool
```
## pri
用于管理公共common pri在Pro使用include(****.pri)引入。

103
07-Other/Qt/Qt总览.md Normal file
View File

@@ -0,0 +1,103 @@
## Qt大佬blog
专栏《Qt 实战一二三》
http://blog.csdn.net/column/details/qshare.html
qyvlik
https://blog.csdn.net/qyvlik/
## QT自定义精美换肤界面
http://www.cnblogs.com/feiyangqingyun/p/3915657.html
Qt之自定义界面窗体缩放-跨平台终极版)
http://blog.csdn.net/liang19890820/article/details/50557240
## QTableView Model
QTabelView根据一行记录中的内容自动调整列宽度
http://bbs.csdn.net/topics/390639311
Qt Model/View 学习笔记 (七)
http://www.cppblog.com/yuanyajie/archive/2007/06/19/26641.html
QT在QTableView中使用各种自定义委托
http://www.linuxidc.com/Linux/2012-07/66820.htm
QT中Qtableview视图表格中点击表头进行排序
http://www.cnblogs.com/googly/p/4584264.html
Qt之模型/视图(实时更新数据)
http://blog.sina.com.cn/s/blog_a6fb6cc90101hhse.html
## Qt读取excel文件
### 可以用QSqlDatabase::addDatabase("QODBC"),这个挺不错
```
QStringList referList;
QSqlDatabase db = QSqlDatabase::addDatabase("QODBC");
db.setDatabaseName("DRIVER={Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)};DBQ=" + dir.filePath("test.xlsx"));
if(db.open())
{
QSqlQuery query("select * from [Sheet1$A:A]",db); // Select range, place A1:B5 after $
while (query.next())
{
QString dataStr= query.value(0).toString();
if(dataStr != "0")
{
referList << dataStr;
}
}
}
```
## 移植QT5.6到嵌入式开发板
http://blog.csdn.net/lizuobin2/article/details/52673494
## 判断release与debug
```
#ifdef QT_NO_DEBUG
qDebug() << "release mode";
#else
qDebug() << "debug mode";
#endif
```
## [Qt]新增UAC功能之requireAdministrator
https://blog.csdn.net/luols/article/details/49996369
https://blog.csdn.net/aqtata/article/details/17222691
## 如何把一个Qt项目拆成多个Qt子项目pro、pri使用
https://blog.csdn.net/lee353086/article/details/70808057
Qt Creator管理多个项目
https://blog.csdn.net/csxiaoshui/article/details/44102873
## 在Qt程序退出前时执行函数
https://stackoverflow.com/questions/8165487/how-to-do-cleaning-up-on-exit-in-qt
## Qt自定义委托在QTableView中绘制控件、图片、文字
http://blog.csdn.net/zhi379/article/details/28412189
## 加密你的SQLite
http://foggry.com/blog/2014/05/19/jia-mi-ni-de-sqlite/
## QML分辨率适配
QML 中的屏幕适配问题
http://blog.csdn.net/qyvlik/article/details/51241425
QML怎么适配不同的设备
http://blog.csdn.net/zhx6044/article/details/44180819
## QGraphicsView
QGraphicsView 框架学习(一)、图形元素的编辑
https://blog.csdn.net/wishfly/article/details/77817091?locationNum=3&fps=1
Qt基础——获取QGraphicsScene的缩略图即导出到图片
https://blog.csdn.net/lcl_data/article/details/8731892
Qt绘图之QGraphicsScene QGraphicsView QGraphicsItem详解
https://www.cnblogs.com/cy568searchx/p/3502242.html
Qt 之图形视图框架
https://blog.csdn.net/liang19890820/article/details/51966791
QGraphicsView 框架学习(一)
https://blog.csdn.net/firebolt2002/article/details/46583589
基于Qt QGraphicsView的多点触摸绘图
https://www.cnblogs.com/visonme/p/5435330.html
QGraphicsScene 管理 QGraphicsItem单击/选择/移动/缩放/删除)
https://blog.csdn.net/liang19890820/article/details/53504323

View File

@@ -0,0 +1,70 @@
## 前言
因为Sqlite的源代码中只提供了Sqlite3_key()的接口没有实现。所以Qt中的Sqlite没有密码功能。于是我找了一下资料并且总结一下思路。现成的方法在最后。
## SQLite历代版本与下载
所有ReleaseTag:https://www.sqlite.org/cgi/src/taglist
SQLite不提供明确的下载地址所以地址需要开发者去猜……也就是通过最新的地址以及你所需要的版本号去推
例如:
- sqlite-amalgamation-3240000https://www.sqlite.org/2018/sqlite-amalgamation-3240000.zip
- sqlite-dll-win32-x86-3240000.ziphttps://www.sqlite.org/2018/sqlite-dll-win32-x86-3240000.zip
- sqlite-dll-win64-x64-3240000.ziphttps://www.sqlite.org/2018/sqlite-dll-win64-x64-3240000.zip
- sqlite-amalgamation-3270200.ziphttps://www.sqlite.org/2019/sqlite-amalgamation-3270200.zip
- sqlite-dll-win64-x64-3270200.ziphttps://www.sqlite.org/2019/sqlite-dll-win64-x64-3270200.zip
注意:
- 年份
- 版本号的小数点位在百位上
## 解决思路与大致过程
编写一个QSQLDriver Plugins并且实现Sqlite3_key()。
### 大致步骤
- 在QtCreator中文件-新建文件或者项目-Library-c++Library来创建QSQLDriver Plugins库类型选择Qt Plugin
- 下载Qt源代码并将\qtbase\src\plugins\sqldrivers\sqlite下的qsql_sqlite_p.h与qsql_sqlite.cpp文件复制到新建的插件目录下。
- 修改2个文件中类名与创建插件的类名一致。并且修改open()函数(具体的请看参考资料)。
- 下载sqlite源代码并且实现Sqlite3_key()注意该函数被宏设置为不编译。可以通过在项目中设置来解决也可以在头文件中直接设置宏为1来解决。
- Sqlite3_key()的实现方法可以参考wxsqlite3或CipherSqlite。
## 现成的解决方法
国人编写的插件QtCipherSqlitePlugin。经测试5.14.2 MSVC2017 x64可以使用。使用起来很方便直接用Qt打开sqlitecipher文件夹中的工程直接切成release模式编译即可。
作者推荐的方法是将编译出lib与dll都放入源代码中的不过我个人还是喜欢使用在项目中直接加载Plugin的方式来实现。这样可以方便后续的项目迁移与后续维护。大致代码如下
```
QPluginLoader driverload(qApp->applicationDirPath()+"/plugin/sqldrivers/sqlitecipher.dll");
if(driverload.load())
{
QSqlDriverPlugin *plugin=qobject_cast<QSqlDriverPlugin*>(driverload.instance());
if(plugin)
{
QSqlDriver *driver=plugin->create("SQLITECIPHER");
QSqlDatabase db;
db=QSqlDatabase::addDatabase(driver);
db.setDatabaseName("mydatabase.db");
db.setPassword("123456");
if(db.open())
{
QSqlQuery qry(db);
qry.exec("create table t_trade(order_id varchar(100))");
qry.exec("insert into t_trade(order_id) values('10001')");
qry.exec("insert into t_trade(order_id) values('10002')");
qry.exec("select * from t_trade");
while(qry.next())
{
qDebug()<<qry.value(0).toString();
}
}
}
}
```
PS.debug与release的dll是不通用的所以需要编译两份dll并且载入时进行判断
- QtCipherSqlitePlugin地址
https://github.com/devbean/QtCipherSqlitePlugin
- 编译方法https://github.com/devbean/QtCipherSqlitePlugin/wiki/How-to-compile
- 使用方法https://github.com/devbean/QtCipherSqlitePlugin/wiki/How-to-use
## 参考资料
- https://www.devbean.net/2012/07/qt-sqlite-plugin-with-encryption/
- https://www.cnblogs.com/WushiShengFei/p/9707244.html

View File

@@ -0,0 +1,58 @@
# 前言
参考:
- 2023最新WSL搭建深度学习平台教程:https://zhuanlan.zhihu.com/p/621142457
- WSL2 + Ubuntu + 图形界面安装:https://www.bilibili.com/read/cv11143517/
- https://zhuanlan.zhihu.com/p/150555651
# 安装
更新wsl 1=>2
https://learn.microsoft.com/zh-cn/windows/wsl/install-manual#step-4---download-the-linux-kernel-update-package
文件下载地址https://wslstorestorage.blob.core.windows.net/wslblob/wsl_update_x64.msi
- wsl --set-default-version 2
## 安装Ubuntu
- 在Micrsoft Store中搜索Ubantu22.04并且安装
- 之后运行Ubuntu22.04会报错报错WslRegisterDistribution failed with error: 0x8007019e
- 以管理员模式运行PowerShell并且运行Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
- 重启后运行Ubuntu22.04即可安装
## 桌面安装
https://blog.csdn.net/weixin_44478077/article/details/128723158
1. sudo apt-get intall xrdp
2. sudo apt-get intall xubuntu-desktop
3. sudo apt-get install ubuntu-desktop
4. sudo service dbus restart
5. 执行`sudo vi ~/.bashrc`编辑用户设置vi按i插入
```
export DISPLAY=$(grep -m 1 nameserver /etc/resolv.conf | awk '{print $2}'):0.0
export XDG_SESSION_TYPE=x11
```
保存后执行`source ~/.bashrc`生效。
6. 保证VcXsrv启动状态下输入gnome-session
```bash
echo xfce4-session >~/.xsession
sudo service xrdp restart
Restarting Remote Desktop Protocol server
[20190719-15:20:51] [DEBUG] Testing if xrdp can listen on 0.0.0.0 port 3390.
[20190719-15:20:51] [DEBUG] Closed socket 6 (AF_INET6 :: port 3390)
```
# 进入wsl
# Hyper-V
## 设置分辨率
1.  **sudo vi /etc/default/grub** 找到GRUB_CMDLINE_LINUX_DEFAULT配置, 添加 video=hyperv_fb:[你自己的分辨率值]’。 比如我的是1920x1080. 所以改完后的结果为:GRUB_CMDLINE_LINUX_DEFAULT="quiet splash video=hyperv_fb:1920x1080"
2. 保存并退出vi.
3. 运行:**sudo update-grub**
4. 重启虚拟机:**sudo reboot**
# 安装Qt
# 问题解决
- WslRegisterDistribution failed with error: 0x8037010d
- 管理员模式运行PowerShell执行dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart

View File

@@ -0,0 +1,163 @@
## 前言
最近有个pdf的需求Qt竟然没有显示pdf的api着实令人郁闷。之后我尝试用了poppler但是光配置编译工程就相当麻烦了没有cmake等开源项目编译经验的人完全一脸懵逼。PDFium也是同理手头上没有vpn也无法尝试。感觉Mupdf编译器起来比较简单所以就来用了一下。
本人使用的版本是Mupdf1.12.0+Qt5.9.3+vs2015
## 下载Mupdf库
https://mupdf.com/downloads/
## 编译Mupdf
在mupdf-1.12.0-source\platform\win32目录下就有现成的mupdf.sln。
这里需要注意:这个工程默认使用的是/MT而Qt MSVC默认用的是/MD,所以需要修改编译工程设置。我们这里只需要在编译工程中修改就可以了。
以下是一些有关QMake中设置运行库属性 /md /md /mt /mtd 的相关参考
- http://blog.csdn.net/caoshangpa/article/details/51416077
- http://www.cnblogs.com/codingmylife/archive/2010/05/08/1730832.html
- http://www.voidcn.com/article/p-hhosrsia-hq.html
工程里默认生成的是都是静态库请注意根据测试需要的分别是libmupdf.lib、libresources.lib、libthirdparty.lib这三个库只使用了docs\examples\example.c的代码使用别的函数可能需要再编译别的工程
分别修改解决方案中的libmupdf、libthirdparty、libresources这三个工程将debug下改成/MDdrelease下改成/MD编译即可得到这3个文件。其中libresources只有release版本的所以debug模式下我也引用这个文件。
## 在Qt工程中引入Mupdf静态库
- 引入lib文件
- 新建一个工程在工程的图标上右键——添加库——外部库。平台只勾选windows链接选择静态之后选择对应的库就可以了。本人将debug与release编译的分别放在debug与release文件夹中
- 添加包含目录
- 将Mupdf目录中的include复制到工程目录下本人又新建了一个mupdf将所有文件都放在里面了
- 本人是这么写的,具体可以参考源代码:
- `INCLUDEPATH += $$PWD/mupdf/include/`
之后运行QMake。
## 编写代码进行测试
```c++
#include "widget.h"
#include "ui_widget.h"
#include <QMessageBox>
#include <QDebug>
#include <QImage>
#include <QPixmap>
#include <QLabel>
#include "mupdf/fitz.h"
#include "mupdf/pdf.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
char *input = const_cast< char* >("document.pdf");
float zoom, rotate;
int page_number, page_count;
fz_context *ctx;
fz_document *doc;
fz_pixmap *pix;
fz_matrix ctm;
int x, y;
//第一页为0
page_number=1;
//100%缩放比
zoom=100;
//旋转为0
rotate=0;
//创建上下文
ctx = fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED);
if (!ctx)
{
qDebug()<<stderr<<"cannot create mupdf context";
return;
}
//注册文档控制
fz_try(ctx)
fz_register_document_handlers(ctx);
fz_catch(ctx)
{
qDebug()<<stderr<<"cannot register document handlers:"<< fz_caught_message(ctx);
fz_drop_context(ctx);
return;
}
//打开文档
fz_try(ctx)
doc = fz_open_document(ctx, input);
fz_catch(ctx)
{
qDebug()<<stderr<< "cannot open document:"<< fz_caught_message(ctx);
fz_drop_context(ctx);
return;
}
//取得总的页数
fz_try(ctx)
page_count = fz_count_pages(ctx, doc);
fz_catch(ctx)
{
qDebug()<<stderr<< "cannot count number of pages:"<< fz_caught_message(ctx);
fz_drop_document(ctx, doc);
fz_drop_context(ctx);
return;
}
if (page_number < 0 || page_number >= page_count)
{
qDebug()<<stderr<< "page number out of range: "<< page_number + 1<<"page count:"<<page_count;
fz_drop_document(ctx, doc);
fz_drop_context(ctx);
return;
}
//计算缩放以及旋转
fz_scale(&ctm, zoom / 100, zoom / 100);
fz_pre_rotate(&ctm, rotate);
//渲染pixmap
fz_try(ctx)
pix = fz_new_pixmap_from_page_number(ctx, doc, page_number, &ctm, fz_device_rgb(ctx), 0);
fz_catch(ctx)
{
qDebug()<<stderr<< "cannot render page: %s\n"<< fz_caught_message(ctx);
fz_drop_document(ctx, doc);
fz_drop_context(ctx);
return;
}
//渲染成图片
// unsigned char *samples = fz_pixmap_samples(ctx, pix);
unsigned char *samples = pix->samples;
int width = fz_pixmap_width(ctx, pix);
int height = fz_pixmap_height(ctx, pix);
QImage image(samples, width, height,QImage::Format_RGB888);
QLabel *label=new QLabel;
label->setPixmap(QPixmap::fromImage(image));
ui->layout->addWidget(label);
// if (!image.save("a.png")) {
// return;
// }
//回收内存
fz_drop_pixmap(ctx, pix);
fz_drop_document(ctx, doc);
fz_drop_context(ctx);
}
Widget::~Widget()
{
delete ui;
}
```
## 参考代码 
https://github.com/blueroseslol/QtMupdf
找到一个之前有人封装的库不过经过测试是无法成功编译的不过可以参考一下mupdf库的用法。
https://github.com/xiangxw/mupdf-qt
别的参考:
http://blog.csdn.net/chenyijun/article/details/42582977

View File

@@ -0,0 +1,44 @@
## Shader
- RWStructuredBuffer:可读写可用类型float1234、uint1234
- StructuredBuffer:对应的只读BUffer
## C#脚本
### 声明变量
CS与Buffer
```c#
[SerializeField]
ComputeShader computeShader = default;
ComputeBuffer positionBuffer;
```
### 绑定CS中的变量并且执行
声明Shader中变量id以用于绑定
```c#
static readonly int intpositionsId = Shader.PropertyToID("_Positions"),
resolutionId= Shader.PropertyToID("_Resolution"),
stepId= Shader.PropertyToID("_Step"),
timeId= Shader.PropertyToID("_Time");
```
给变量绑定数值
```c#
computeShader.SetInt(resolutionId, resolution);
computeShader.SetFloat(stepId, step);
computeShader.SetFloat(timeId, Time.time);
computeShader.SetBuffer(0, positionsId, positionBuffer);
```
执行
```c#
int groups = Mathf.CeilToInt(resolution / 8f);
computeShader.Dispatch(0, groups, groups, 1);
```
### 绘制GPU Instance命令
```c#
//需要传入边界盒以及绘制数量
var bounds=new Bounds(Vector3.zero,Vector3.one*(2f+2f/resolution));
Graphics.DrawMeshInstancedProcedural(mesh, 0, material,bounds,positionBuffer.count);
```
### 多kernel
CS可能拥有多个内核函数此时computeShader的SetBuffer()与Dispatch()可以通过第一个形参来设置kernel index。

View File

@@ -0,0 +1,234 @@
## 纯Shader
- Uniform代表该变量在顶点与片元着色器中值都是相同的。
- fixed:低精度数字它们以精度来换取移动设备上的速度。在台式机上fixed只是float的别名。
## 内置库
- UnityShaderVariables.cginc定义了渲染所需的一堆着色器变量例如变换相机和光照数据。这些都在需要时由Unity设置。
- HLSLSupport.cginc进行了设置因此无论代码针对的是哪个平台都可以使用相同的代码进行编写。无需担心使用特定于平台的数据类型等。
- UnityInstancing.cginc专门用于实例化支持这是一种减少绘制调用的特定渲染技术。尽管它不直接包含文件但依赖于UnityShaderVariables。
## 关键字
### PropertyType
- Int
- Float
- Range
- Color
- Vector
- 2D texture
- Cube texture
- 3D texture
### SubShader
针对不同性能的显卡适配不同的Shader。
```
SubShader{
[Tags]
[RenderSetup]
Pass{
}
}
```
### Fallback关键字
Fallback关键词用于处理匹配失败的情况指定一个用于处理这个情况的Pass或者直接跳过。
>FallBack Off
### CGPROGRAM与ENDCG
里面编写这里编写HLSL/CG对于VertexShader与PixelShader则写在SubShader的Pass关键字的{}中。
## 内置变量
### 矩阵
- UNITY_MATRIX_MVP
- UNITY_MATRIX_MV
- UNITY_MATRIX_P
- UNITY_MATRIX_VP
- UNITY_MATRIX_T_MV
- UNITY_MATRIX_IT_MV
- _Object2World
- _World2Object
- unity_WorldToObject
### 摄像机
- WorldSpaceCameraPos
- ProjectionParams
- ScreenParams
- ZBufferParams
- unity_OrthoParams
- unity_CameraProjection
- unity_CameraInvProjection
- unity_CameraWorldClipPlanes[6]
### 灯光
- UNITY_LIGHTMODEL_AMBIENT
- _WorldSpaceLightPos0
## 定义VS与PS名称
```c++
#pragma vertex vert
#pragma fragment frag
```
## UnityCG.cginc
### 常用结构体
- appdata_base float4 vertex : POSITION; float3 normal : NORMAL; float4 texcoord: TEXCOORD0;
- appdata_tan float4 vertex : POSITION; float4 tangent : TANGENT; float3 normal : NORMAL; float4 texcoord : TEXCOORD0;
- appdata_full float4 vertex : POSITION; float4 tangent : TANGENT; float3 normal : NORMAL; float4 texcoord : TEXCOORD0; float4 texcoord1 : TEXCOORD1; float4 texcoord2 : TEXCOORD2; float4 texcoord3 : TEXCOORD3; # if defined(SHADER_API_XBOX360) half4 texcoord4 : TEXCOORD4; half4 texcoord5 : TEXCOORD5; # endif fixed4 color : COLOR;
- appdata_img float4 vertex : POSITION; half2 texcoord : TEXCOORD0;
- v2f_img 裁剪空间中的位置、纹理坐标
### 常用函数
- float4 WorldSpaceViewDir(float4 v)输入一个模型空间中的顶点位置,返回世界空间中从该点到摄像机的观察方向
- float4 UnityWorldSpaceViewDir(float4 v)输入一个世界空间中的顶点位置,返回世界空间中从该点到摄像机的观察方向
- float4 ObjSpaceViewDir(float4 v)输入一个模型空间中的顶点位置,返回模型空间中从该店到摄像机的观察方向
- float4 WorldSpaceLightDir(flaot4 v)仅用于向前渲染。 输入一个模型空间中的顶点位置,返回世界空间中从该点到光源的光照方向。没有被归一化
- float4 ObjectSpaceLightDir(float4 v)仅用于向前渲染中,输入一个模型空间中的顶点位置, 返回模型空间中从该点到光源的光照方向。没有被归一化
- float4 UnityWorldSpaceLightDir(float4 v)仅用于向前渲染中,输入一个世界空间中的顶点位置, 返回世界空间中从该点到光源的光照方向。没有被归一化
- float3 UnityObjectToWorldNormal(float3 norm)把法线方向从模型空间中转换到世界空间中
- float3 UnityObjectToWorldDir(float3 dir)把方向矢量从模型空间中变换到世界空间中
- float3 Unity WorldToObjectDir(float3 dir)把方向矢量从世界空间变换到模型空间中
## 浮点格式
float32位
half16位 -60000~+60000
fixed11位 -2.0~+2.0
## 贴图
`Sampler2D _MainTex`使用类似`float4 _MainTex_ST`作为缩放与位移。
## 管线LightMode
定义在Pass内的Tag{}中。
- Always总是渲染但不计算任何光照。
- ForwardBase用于前向渲染该Pass会计算环境光、平行光、逐顶点SH与LightMap。
- ForwardAdd用于前向渲染该Pass会计算额外的逐像素光源每个Pass对应一个光源。
- Deferred用于延迟渲染该Pass会计算GBuffer。
- ShadowCaster把物体的深度信息渲染ShadowMap或是一张深度纹理中。
- PrepassBase用于遗留的延迟渲染该Pass会渲染法线和高光反射的指数部分。
- PrepassFinal用于遗留的延迟渲染该Pass通过合并纹理、光照和自发光来渲染得到最终的颜色。
- Vertex、VertexLMRGBM、VertexLM用于遗留的顶点光照渲染。
## 不透明物体的渲染顺序
1. 先渲染所有不透明物体,并开启深度测试与深度写入
2. 把半透明物体按它距离摄像机的远近进行排序 ,然后按照从后往前的顺序渲染这些半透明物体,并开启它们的深度测试,但关闭深度写入。
### Unity3d的解决方案
定义了5个渲染队列
- Backgrouond
- Geometry
- AlphaTest
- Transparent
- Overlay
可以在Tags定义队列
```
SubShader{
Tags{"Queue"="AlphaTest"}
Pass{
ZWrite Off
}
}
```
### Blend相关命令
BlendOff
BlendSrcFactorDstFactor
BlendSrcFactorDstFactor,SrcFactorA DstFactorA
BlendOp BlendOperation
### 混合操作
1. Add
2. Sub
3. RevSub
4. Min
5. Max
### 常见的混合类型
1. Blend SrcAlpha OneMinusSrcAlpha 正常
2. Blend OneMinusDstColor One 柔和相加
3. Blend DstColor Zero 正片叠底
4. Blend DstColor SrcColor 两倍相乘
5. BlendOp Min Blend One One 变暗
6. BlendOp Max Blend One One 变亮
7. Blend One One 线性减淡
### 解决半透明乱序问题
1. 使用两个Pass来渲染模型第一个Pass开启深度写入但不输出颜色。第二个Pass进行正常的透明度混合。
2. 双面渲染的透明效果
## 剔除命令
Cull Back | Front | Off
## 内置时间变量
- _Time场景加载开始到现在的时间4个分量为t/20,t,2t,3t
- _SinTime时间的正弦值4个分量为t/8,t/4,t/2,t
- _CosTime时间的余弦值4个分量为t/8,t/4,t/2,t
- untiy_DeltaTimedt为时间的增量4个分量为dt1/dt,SmoothDt,1/SmoothDt
## 预处理命令
### multi_compile
`multi_compile`定义的宏,如`#pragma multi_compile_fog``#pragma multi_compile_fwdbase`等基本上适用于大部分shader与shader自身所带的属性无关。
### shader_feature
`shader_feature`定义的宏多用于针对shader自身的属性。比如shader中有`_NormalMap`这个属性(Property),便可通`#pragma shader_feature _NormalMap`来定义宏用来实现这个shader在material有无`_NormalMap`时可进行不同的处理。
## 优化
减少Draw Call的方式有动态合批与静态合批。Unity中支持两种批处理方式一种是动态批处理一种是静态批处理。对于动态批处理来说有点是一切处理都是Unity自动完成的不需要我们自己做任何操作而且物体是可以移动的但缺点是限制很多可能一不小心就会破坏了这种机制导致Unity无法动态批处理一些使用了相同材质的物体。
而对于静态批处理来说,它的优点是自由度很高,限制很少;但缺点是可能会占用更多的内存,而且经过静态批出里的所有物体都不可以再移动了。
动态批处理的原理是每一帧把可以进行批处理的模型网格进行合并再把合并后模型数据传递给GPU然后使用同一个材质对其渲染。处理实现方便动态批处理的另一个好处是经过批处理的物体仍然可以移动这是由于在处理每帧时Unity都会重新合并一次网格。
### 共享材质
将多张纹理合并到一起,并且制作成材质。
### 动态合批
在使用同一个材质的情况下,满足的条件后就会被动态处理,每帧都会合并一次。条件:
- 能够进行动态批处理的网格顶点属性规模要小于900.例如如果Shader中需要使用顶点位置、法线和纹理坐标这3个顶点属性那么想要让模型能够被动态批处理它的顶点数目不能超过300。需要注意的是这个数字未来有可能会发生变化因此不要依赖这个数据。
- 一般来说所有对象都需要使用同一个缩放尺度。一个例外的情况是如果所有的物体都使用了不同的非统一缩放那么它们也是可以被动态批处理的。但在Unity 5 中,这种对模型缩放的限制已经不存在了。
- 使用光照纹理的物体需要格外小心处理。这些物体需要额外的渲染参数,例如,在光照纹理上的索引、偏移量和缩放信息等。因此,为了让这些物体可以被动态批处理,我们需要保证它们指向光照纹理中的同一个位置。
- 多Pass的Shader会中断批处理。在前向渲染中我们有时需要使用额外的Pass来为模型添加更多的光照效果但这样一来模型就会被动态批处理了。
### 静态合批
在运行开始阶段,把需要进行静态批处理的模型合并到一个新的网格结构中。
## GeometryShader
>ShaderModel必须4.0以上`#program target 4.0`如果低于这个目标u3d会自定提升至该级别。
- maxvertexcount定义输出顶点数如果只是处理三角形只需要设置为3即可。
- triangle为输入类型关键字。
- TriangleStream为输出流类型。
```
[maxvertexcount(3)]
void MyGeometryProgram (
triangle InterpolatorsVertex i[3],
inout TriangleStream<InterpolatorsGeometry> stream
)
```
### Flat线框效果CatLikeCoding中的案例
向三角形添加重心坐标的一种方法是使用网格的顶点颜色存储它们。每个三角形的第一个顶点变为红色,第二个顶点变为绿色,第三个顶点变为蓝色。但是,这将需要具有以此方式分配的顶点颜色的网格,并且无法共享顶点。我们想要一种适用于任何网格的解决方案。幸运的是,我们可以使用我们的几何程序添加所需的坐标。
由于网格不提供重心坐标因此顶点程序不了解它们。所以它们不属于InterpolatorsVertex结构。要使几何程序输出它们我们必须定义一个新结构。首先在MyGeometryProgram上方定义InterpolatorsGeometry。它应包含与InterpolatorsVertex相同的数据因此使用它作为其内容
```hlsl
struct InterpolatorsGeometry {
InterpolatorsVertex data;
CUSTOM_GEOMETRY_INTERPOLATORS
};
```
- MyGeometryProgram的作用为调整按照面法线调整顶点法线在调整barycentricCoordinates值最后塞入inout TriangleStream<InterpolatorsGeometry>中。
- GetAlbedoWithWireframe为线控效果控制最终会在My Lighting.cginc中以宏替换的方式整合至渲染流程中。
```hlsl
float3 GetAlbedoWithWireframe (Interpolators i) {
float3 albedo = GetAlbedo(i);
float3 barys;
barys.xy = i.barycentricCoordinates;
barys.z = 1 - barys.x - barys.y;
float3 deltas = fwidth(barys);
float3 smoothing = deltas * _WireframeSmoothing;
float3 thickness = deltas * _WireframeThickness;
barys = smoothstep(thickness, thickness + smoothing, barys);
float minBary = min(barys.x, min(barys.y, barys.z));
return lerp(_WireframeColor, albedo, minBary);
}
```

View File

@@ -0,0 +1,48 @@
## 公式
```c#
saturate((1.0 + ( (Set_ShadingGrade - (_1st_ShadeColor_Step-_1st_ShadeColor_Feather)) * (0.0 - 1.0) ) / (_1st_ShadeColor_Step - (_1st_ShadeColor_Step-_1st_ShadeColor_Feather)))); // Base and 1st Shade Mask
```
## ShadingGradeMap
```c#
float3 Set_BaseColor = lerp( (_MainTex_var.rgb*_BaseColor.rgb), ((_MainTex_var.rgb*_BaseColor.rgb)*Set_LightColor), _Is_LightColor_Base );
float3 _BaseColor_var = lerp(Set_BaseColor,_Is_LightColor_1st_Shade_var,Set_FinalShadowMask);
float Set_FinalShadowMask = saturate((1.0 + ( (Set_ShadingGrade - (_1st_ShadeColor_Step-_1st_ShadeColor_Feather)) * (0.0 - 1.0) ) / (_1st_ShadeColor_Step - (_1st_ShadeColor_Step-_1st_ShadeColor_Feather))));
float Set_ShadeShadowMask = saturate((1.0 + ( (Set_ShadingGrade - (_2nd_ShadeColor_Step-_2nd_ShadeColor_Feather)) * (0.0 - 1.0) ) / (_2nd_ShadeColor_Step - (_2nd_ShadeColor_Step-_2nd_ShadeColor_Feather))));
float3 Set_FinalBaseColor = lerp( _BaseColor_var,
lerp(_Is_LightColor_1st_Shade_var,
lerp( _2nd_ShadeMap_var.rgb*_2nd_ShadeColor.rgb,
((_2nd_ShadeMap_var.rgb*_2nd_ShadeColor.rgb)*Set_LightColor),
_Is_LightColor_2nd_Shade ),
Set_ShadeShadowMask),
Set_FinalShadowMask);
```
## Feather
```c#
_Set_1st_ShadePosition与 _Set_2st_ShadePosition默认为白色。
float3 Set_BaseColor = lerp( (_BaseColor.rgb*_MainTex_var.rgb), ((_BaseColor.rgb*_MainTex_var.rgb)*Set_LightColor), _Is_LightColor_Base );
float Set_FinalShadowMask = saturate(1.0 + lerp( _HalfLambert_var,
_HalfLambert_var*saturate(_SystemShadowsLevel_var),
_Set_SystemShadowsToBase ) - (_BaseColor_Step-_BaseShade_Feather) *
((1.0 - _Set_1st_ShadePosition_var.rgb).r - 1.0) //对应上面的 (0.0 - 1.0)项
/ (_BaseColor_Step - (_BaseColor_Step-_BaseShade_Feather)));
float3 Set_FinalBaseColor = lerp( Set_BaseColor,
lerp(Set_1st_ShadeColor,
Set_2nd_ShadeColor,
saturate((1.0 + ( (_HalfLambert_var - (_ShadeColor_Step-_1st2nd_Shades_Feather)) * ((1.0 - _Set_2nd_ShadePosition_var.rgb).r - 1.0) ) / (_ShadeColor_Step - (_ShadeColor_Step-_1st2nd_Shades_Feather))))
),
Set_FinalShadowMask); // Final Color
```
## 总结
可以看得出两者都使用了UTS的祖传公式进行插值。但不同点在于
- ShadingGradeMap工作模式的UTS公式插值对象为ShadingGradeMap之后使用1st、2stShadeColor对应的Step与Feather进行计算来获得2个ShadeMask。最后再使用2个Mask对BaseColor、1stShadeColor与2stShadeColor进行插值来获取最终结果。
- Feather工作模式的UTS公式插值对象为HalfLambert之后也使用2份Step与Feather进行计算得到来获得2个ShadeMask。最后再使用2个Mask对BaseColor、1stShadeColor与2stShadeColor进行插值来获取最终结果。还有一个区别在于里面增加了ShadePositionMap对UTS公式的分子项进行控制也就是使用`(1.0 - _Set_2nd_ShadePosition_var.rgb).r - 1.0)`来代替`(0.0 - 1.0)`

View File

@@ -0,0 +1,685 @@
## 各种定义
根据是否开启天使环渲染与`_MAIN_LIGHT_SHADOWS`来定义顶点输入与输出格式。
```c#
struct VertexInput {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 tangent : TANGENT;
float2 texcoord0 : TEXCOORD0;
#ifdef _IS_ANGELRING_OFF
float2 lightmapUV : TEXCOORD1;
#elif _IS_ANGELRING_ON
float2 texcoord1 : TEXCOORD1;
float2 lightmapUV : TEXCOORD2;
#endif
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct VertexOutput {
float4 pos : SV_POSITION;
float2 uv0 : TEXCOORD0;
//v.2.0.4
#ifdef _IS_ANGELRING_OFF
float4 posWorld : TEXCOORD1;
float3 normalDir : TEXCOORD2;
float3 tangentDir : TEXCOORD3;
float3 bitangentDir : TEXCOORD4;
//v.2.0.7
float mirrorFlag : TEXCOORD5;
DECLARE_LIGHTMAP_OR_SH(lightmapUV, vertexSH, 6);
#if defined(_ADDITIONAL_LIGHTS_VERTEX) || (VERSION_LOWER(12, 0))
half4 fogFactorAndVertexLight : TEXCOORD7; // x: fogFactor, yzw: vertex light
#else
half fogFactor : TEXCOORD7;
#endif
# ifndef _MAIN_LIGHT_SHADOWS
float4 positionCS : TEXCOORD8;
int mainLightID : TEXCOORD9;
# else
float4 shadowCoord : TEXCOORD8;
float4 positionCS : TEXCOORD9;
int mainLightID : TEXCOORD10;
# endif
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
//
#elif _IS_ANGELRING_ON
float2 uv1 : TEXCOORD1;
float4 posWorld : TEXCOORD2;
float3 normalDir : TEXCOORD3;
float3 tangentDir : TEXCOORD4;
float3 bitangentDir : TEXCOORD5;
//v.2.0.7
float mirrorFlag : TEXCOORD6;
DECLARE_LIGHTMAP_OR_SH(lightmapUV, vertexSH, 7);
#if defined(_ADDITIONAL_LIGHTS_VERTEX) || (VERSION_LOWER(12, 0))
half4 fogFactorAndVertexLight : TEXCOORD8; // x: fogFactor, yzw: vertex light
#else
half fogFactor : TEXCOORD8; // x: fogFactor, yzw: vertex light
#endif
# ifndef _MAIN_LIGHT_SHADOWS
float4 positionCS : TEXCOORD9;
int mainLightID : TEXCOORD10;
# else
float4 shadowCoord : TEXCOORD9;
float4 positionCS : TEXCOORD10;
int mainLightID : TEXCOORD11;
# endif
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
#else
LIGHTING_COORDS(7,8)
UNITY_FOG_COORDS(9)
#endif
//
};
//灯光数据
struct UtsLight
{
float3 direction;
float3 color;
float distanceAttenuation;
real shadowAttenuation;
int type;
};
```
根据宏定义宏:`_ADDITIONAL_LIGHTS`=>`REQUIRES_WORLD_SPACE_POS_INTERPOLATOR`,`_MAIN_LIGHT_SHADOWS`=>`REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR`。以及一些函数:
```c#
// RaytracedHardShadow
// This is global texture. what to do with SRP Batcher.
#define UNITY_PROJ_COORD(a) a
#define UNITY_SAMPLE_SCREEN_SHADOW(tex, uv) tex2Dproj( tex, UNITY_PROJ_COORD(uv) ).r
#define TEXTURE2D_SAMPLER2D(textureName, samplerName) Texture2D textureName; SamplerState samplerName
TEXTURE2D_SAMPLER2D(_RaytracedHardShadow, sampler_RaytracedHardShadow);
float4 _RaytracedHardShadow_TexelSize;
//function to rotate the UV: RotateUV()
//float2 rotatedUV = RotateUV(i.uv0, (_angular_Verocity*3.141592654), float2(0.5, 0.5), _Time.g);
float2 RotateUV(float2 _uv, float _radian, float2 _piv, float _time)
{
float RotateUV_ang = _radian;
float RotateUV_cos = cos(_time*RotateUV_ang);
float RotateUV_sin = sin(_time*RotateUV_ang);
return (mul(_uv - _piv, float2x2( RotateUV_cos, -RotateUV_sin, RotateUV_sin, RotateUV_cos)) + _piv);
}
//
fixed3 DecodeLightProbe( fixed3 N ){
return ShadeSH9(float4(N,1));
}
inline void InitializeStandardLitSurfaceDataUTS(float2 uv, out SurfaceData outSurfaceData)
{
outSurfaceData = (SurfaceData)0;
// half4 albedoAlpha = SampleAlbedoAlpha(uv, TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap));
half4 albedoAlpha = half4(1.0,1.0,1.0,1.0);
outSurfaceData.alpha = Alpha(albedoAlpha.a, _BaseColor, _Cutoff);
half4 specGloss = SampleMetallicSpecGloss(uv, albedoAlpha.a);
outSurfaceData.albedo = albedoAlpha.rgb * _BaseColor.rgb;
#if _SPECULAR_SETUP
outSurfaceData.metallic = 1.0h;
outSurfaceData.specular = specGloss.rgb;
#else
outSurfaceData.metallic = specGloss.r;
outSurfaceData.specular = half3(0.0h, 0.0h, 0.0h);
#endif
outSurfaceData.smoothness = specGloss.a;
outSurfaceData.normalTS = SampleNormal(uv, TEXTURE2D_ARGS(_BumpMap, sampler_BumpMap), _BumpScale);
outSurfaceData.occlusion = SampleOcclusion(uv);
outSurfaceData.emission = SampleEmission(uv, _EmissionColor.rgb, TEXTURE2D_ARGS(_EmissionMap, sampler_EmissionMap));
}
half3 GlobalIlluminationUTS(BRDFData brdfData, half3 bakedGI, half occlusion, half3 normalWS, half3 viewDirectionWS)
{
half3 reflectVector = reflect(-viewDirectionWS, normalWS);
half fresnelTerm = Pow4(1.0 - saturate(dot(normalWS, viewDirectionWS)));
half3 indirectDiffuse = bakedGI * occlusion;
half3 indirectSpecular = GlossyEnvironmentReflection(reflectVector, brdfData.perceptualRoughness, occlusion);
return EnvironmentBRDF(brdfData, indirectDiffuse, indirectSpecular, fresnelTerm);
}
```
## 顶点着色器
计算
- 顶点法线、切线、次级法线
- 裁剪过的顶点世界坐标
- 使用`ComputeFogFactor`(FOG_LINEAR、FOG_EXP与FOG_EXP2)计算`fogFactorAndVertexLight`或者`fogFactor`。
- shadowCoord
- mainLightID
如果开启天使环渲染则增加一个TexCoord1为天使环UV坐标。
```c#
VertexOutput vert (VertexInput v) {
VertexOutput o = (VertexOutput)0;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_TRANSFER_INSTANCE_ID(v, o);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.uv0 = v.texcoord0;
//v.2.0.4
#ifdef _IS_ANGELRING_OFF
//
#elif _IS_ANGELRING_ON
o.uv1 = v.texcoord1;
#endif
o.normalDir = UnityObjectToWorldNormal(v.normal);
o.tangentDir = normalize( mul( unity_ObjectToWorld, float4( v.tangent.xyz, 0.0 ) ).xyz );
o.bitangentDir = normalize(cross(o.normalDir, o.tangentDir) * v.tangent.w);
o.posWorld = mul(unity_ObjectToWorld, v.vertex);
o.pos = UnityObjectToClipPos( v.vertex );
//v.2.0.7 Detection of the inside the mirror (right or left-handed) o.mirrorFlag = -1 then "inside the mirror".
float3 crossFwd = cross(UNITY_MATRIX_V[0].xyz, UNITY_MATRIX_V[1].xyz);
o.mirrorFlag = dot(crossFwd, UNITY_MATRIX_V[2].xyz) < 0 ? 1 : -1;
//
float3 positionWS = TransformObjectToWorld(v.vertex.xyz);
float4 positionCS = TransformWorldToHClip(positionWS);
half3 vertexLight = VertexLighting(o.posWorld.xyz, o.normalDir);
half fogFactor = ComputeFogFactor(positionCS.z);
OUTPUT_LIGHTMAP_UV(v.lightmapUV, unity_LightmapST, o.lightmapUV);
OUTPUT_SH(o.normalDir.xyz, o.vertexSH);
# if defined(_ADDITIONAL_LIGHTS_VERTEX) || (VERSION_LOWER(12, 0))
o.fogFactorAndVertexLight = half4(fogFactor, vertexLight);
#else
o.fogFactor = fogFactor;
#endif
o.positionCS = positionCS;
#if defined(_MAIN_LIGHT_SHADOWS) && !defined(_RECEIVE_SHADOWS_OFF)
#if SHADOWS_SCREEN
o.shadowCoord = ComputeScreenPos(positionCS);
#else
o.shadowCoord = TransformWorldToShadowCoord(o.posWorld.xyz);
#endif
o.mainLightID = DetermineUTS_MainLightIndex(o.posWorld.xyz, o.shadowCoord, positionCS);
#else
o.mainLightID = DetermineUTS_MainLightIndex(o.posWorld.xyz, 0, positionCS);
#endif
return o;
}
```
## 像素着色器
UTS的着色模式有两种分别封装在`UniversalToonBodyDoubleShadeWithFeather.hlsl`与`UniversalToonBodyShadingGradeMap`中。
```c#
float4 frag(VertexOutput i, fixed facing : VFACE) : SV_TARGET
{
#if defined(_SHADINGGRADEMAP)
return fragShadingGradeMap(i, facing);
#else
return fragDoubleShadeFeather(i, facing);
#endif
}
```
## 透明与裁剪
`ClippingMode`设置为非off后才会开启裁剪选项。拥有以下功能
- 裁剪Mask反转
- 使用BaseMap的Alpha通道作为Mask
- 裁剪强度与透明度强度
这个功能通常用来除了头发之类的透明物体。
## 完整代码
```c#
#if (SHADER_LIBRARY_VERSION_MAJOR ==7 && SHADER_LIBRARY_VERSION_MINOR >= 3) || (SHADER_LIBRARY_VERSION_MAJOR >= 8)
# ifdef _ADDITIONAL_LIGHTS
# ifndef REQUIRES_WORLD_SPACE_POS_INTERPOLATOR
# define REQUIRES_WORLD_SPACE_POS_INTERPOLATOR
# endif
# endif
#else
# ifdef _MAIN_LIGHT_SHADOWS
//# if !defined(_MAIN_LIGHT_SHADOWS_CASCADE)
# ifndef REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR
# define REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR
# endif
//# endif
# endif
# ifdef _ADDITIONAL_LIGHTS
# ifndef REQUIRES_WORLD_SPACE_POS_INTERPOLATOR
# define REQUIRES_WORLD_SPACE_POS_INTERPOLATOR
# endif
# endif
#endif
// RaytracedHardShadow
// This is global texture. what to do with SRP Batcher.
#define UNITY_PROJ_COORD(a) a
#define UNITY_SAMPLE_SCREEN_SHADOW(tex, uv) tex2Dproj( tex, UNITY_PROJ_COORD(uv) ).r
#define TEXTURE2D_SAMPLER2D(textureName, samplerName) Texture2D textureName; SamplerState samplerName
TEXTURE2D_SAMPLER2D(_RaytracedHardShadow, sampler_RaytracedHardShadow);
float4 _RaytracedHardShadow_TexelSize;
//function to rotate the UV: RotateUV()
//float2 rotatedUV = RotateUV(i.uv0, (_angular_Verocity*3.141592654), float2(0.5, 0.5), _Time.g);
float2 RotateUV(float2 _uv, float _radian, float2 _piv, float _time)
{
float RotateUV_ang = _radian;
float RotateUV_cos = cos(_time*RotateUV_ang);
float RotateUV_sin = sin(_time*RotateUV_ang);
return (mul(_uv - _piv, float2x2( RotateUV_cos, -RotateUV_sin, RotateUV_sin, RotateUV_cos)) + _piv);
}
//
fixed3 DecodeLightProbe( fixed3 N ){
return ShadeSH9(float4(N,1));
}
inline void InitializeStandardLitSurfaceDataUTS(float2 uv, out SurfaceData outSurfaceData)
{
outSurfaceData = (SurfaceData)0;
// half4 albedoAlpha = SampleAlbedoAlpha(uv, TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap));
half4 albedoAlpha = half4(1.0,1.0,1.0,1.0);
outSurfaceData.alpha = Alpha(albedoAlpha.a, _BaseColor, _Cutoff);
half4 specGloss = SampleMetallicSpecGloss(uv, albedoAlpha.a);
outSurfaceData.albedo = albedoAlpha.rgb * _BaseColor.rgb;
#if _SPECULAR_SETUP
outSurfaceData.metallic = 1.0h;
outSurfaceData.specular = specGloss.rgb;
#else
outSurfaceData.metallic = specGloss.r;
outSurfaceData.specular = half3(0.0h, 0.0h, 0.0h);
#endif
outSurfaceData.smoothness = specGloss.a;
outSurfaceData.normalTS = SampleNormal(uv, TEXTURE2D_ARGS(_BumpMap, sampler_BumpMap), _BumpScale);
outSurfaceData.occlusion = SampleOcclusion(uv);
outSurfaceData.emission = SampleEmission(uv, _EmissionColor.rgb, TEXTURE2D_ARGS(_EmissionMap, sampler_EmissionMap));
}
half3 GlobalIlluminationUTS(BRDFData brdfData, half3 bakedGI, half occlusion, half3 normalWS, half3 viewDirectionWS)
{
half3 reflectVector = reflect(-viewDirectionWS, normalWS);
half fresnelTerm = Pow4(1.0 - saturate(dot(normalWS, viewDirectionWS)));
half3 indirectDiffuse = bakedGI * occlusion;
half3 indirectSpecular = GlossyEnvironmentReflection(reflectVector, brdfData.perceptualRoughness, occlusion);
return EnvironmentBRDF(brdfData, indirectDiffuse, indirectSpecular, fresnelTerm);
}
struct VertexInput {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 tangent : TANGENT;
float2 texcoord0 : TEXCOORD0;
#ifdef _IS_ANGELRING_OFF
float2 lightmapUV : TEXCOORD1;
#elif _IS_ANGELRING_ON
float2 texcoord1 : TEXCOORD1;
float2 lightmapUV : TEXCOORD2;
#endif
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct VertexOutput {
float4 pos : SV_POSITION;
float2 uv0 : TEXCOORD0;
//v.2.0.4
#ifdef _IS_ANGELRING_OFF
float4 posWorld : TEXCOORD1;
float3 normalDir : TEXCOORD2;
float3 tangentDir : TEXCOORD3;
float3 bitangentDir : TEXCOORD4;
//v.2.0.7
float mirrorFlag : TEXCOORD5;
DECLARE_LIGHTMAP_OR_SH(lightmapUV, vertexSH, 6);
#if defined(_ADDITIONAL_LIGHTS_VERTEX) || (VERSION_LOWER(12, 0))
half4 fogFactorAndVertexLight : TEXCOORD7; // x: fogFactor, yzw: vertex light
#else
half fogFactor : TEXCOORD7;
#endif
# ifndef _MAIN_LIGHT_SHADOWS
float4 positionCS : TEXCOORD8;
int mainLightID : TEXCOORD9;
# else
float4 shadowCoord : TEXCOORD8;
float4 positionCS : TEXCOORD9;
int mainLightID : TEXCOORD10;
# endif
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
//
#elif _IS_ANGELRING_ON
float2 uv1 : TEXCOORD1;
float4 posWorld : TEXCOORD2;
float3 normalDir : TEXCOORD3;
float3 tangentDir : TEXCOORD4;
float3 bitangentDir : TEXCOORD5;
//v.2.0.7
float mirrorFlag : TEXCOORD6;
DECLARE_LIGHTMAP_OR_SH(lightmapUV, vertexSH, 7);
#if defined(_ADDITIONAL_LIGHTS_VERTEX) || (VERSION_LOWER(12, 0))
half4 fogFactorAndVertexLight : TEXCOORD8; // x: fogFactor, yzw: vertex light
#else
half fogFactor : TEXCOORD8; // x: fogFactor, yzw: vertex light
#endif
# ifndef _MAIN_LIGHT_SHADOWS
float4 positionCS : TEXCOORD9;
int mainLightID : TEXCOORD10;
# else
float4 shadowCoord : TEXCOORD9;
float4 positionCS : TEXCOORD10;
int mainLightID : TEXCOORD11;
# endif
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
#else
LIGHTING_COORDS(7,8)
UNITY_FOG_COORDS(9)
#endif
//
};
// Abstraction over Light shading data.
struct UtsLight
{
float3 direction;
float3 color;
float distanceAttenuation;
real shadowAttenuation;
int type;
};
///////////////////////////////////////////////////////////////////////////////
// Light Abstraction //
/////////////////////////////////////////////////////////////////////////////
real MainLightRealtimeShadowUTS(float4 shadowCoord, float4 positionCS)
{
#if !defined(MAIN_LIGHT_CALCULATE_SHADOWS)
return 1.0h;
#endif
ShadowSamplingData shadowSamplingData = GetMainLightShadowSamplingData();
half4 shadowParams = GetMainLightShadowParams();
#if defined(UTS_USE_RAYTRACING_SHADOW)
float w = (positionCS.w == 0) ? 0.00001 : positionCS.w;
float4 screenPos = ComputeScreenPos(positionCS/ w);
return SAMPLE_TEXTURE2D(_RaytracedHardShadow, sampler_RaytracedHardShadow, screenPos);
#endif
return SampleShadowmap(TEXTURE2D_ARGS(_MainLightShadowmapTexture, sampler_MainLightShadowmapTexture), shadowCoord, shadowSamplingData, shadowParams, false);
}
real AdditionalLightRealtimeShadowUTS(int lightIndex, float3 positionWS, float4 positionCS)
{
#if defined(UTS_USE_RAYTRACING_SHADOW)
float w = (positionCS.w == 0) ? 0.00001 : positionCS.w;
float4 screenPos = ComputeScreenPos(positionCS / w);
return SAMPLE_TEXTURE2D(_RaytracedHardShadow, sampler_RaytracedHardShadow, screenPos);
#endif // UTS_USE_RAYTRACING_SHADOW
#if !defined(ADDITIONAL_LIGHT_CALCULATE_SHADOWS)
return 1.0h;
#endif
ShadowSamplingData shadowSamplingData = GetAdditionalLightShadowSamplingData();
#if USE_STRUCTURED_BUFFER_FOR_LIGHT_DATA
lightIndex = _AdditionalShadowsIndices[lightIndex];
// We have to branch here as otherwise we would sample buffer with lightIndex == -1.
// However this should be ok for platforms that store light in SSBO.
UNITY_BRANCH
if (lightIndex < 0)
return 1.0;
float4 shadowCoord = mul(_AdditionalShadowsBuffer[lightIndex].worldToShadowMatrix, float4(positionWS, 1.0));
#else
float4 shadowCoord = mul(_AdditionalLightsWorldToShadow[lightIndex], float4(positionWS, 1.0));
#endif
half4 shadowParams = GetAdditionalLightShadowParams(lightIndex);
return SampleShadowmap(TEXTURE2D_ARGS(_AdditionalLightsShadowmapTexture, sampler_AdditionalLightsShadowmapTexture), shadowCoord, shadowSamplingData, shadowParams, true);
}
UtsLight GetUrpMainUtsLight()
{
UtsLight light;
light.direction = _MainLightPosition.xyz;
// unity_LightData.z is 1 when not culled by the culling mask, otherwise 0.
light.distanceAttenuation = unity_LightData.z;
#if defined(LIGHTMAP_ON) || defined(_MIXED_LIGHTING_SUBTRACTIVE)
// unity_ProbesOcclusion.x is the mixed light probe occlusion data
light.distanceAttenuation *= unity_ProbesOcclusion.x;
#endif
light.shadowAttenuation = 1.0;
light.color = _MainLightColor.rgb;
light.type = _MainLightPosition.w;
return light;
}
UtsLight GetUrpMainUtsLight(float4 shadowCoord, float4 positionCS)
{
UtsLight light = GetUrpMainUtsLight();
light.shadowAttenuation = MainLightRealtimeShadowUTS(shadowCoord, positionCS);
return light;
}
// Fills a light struct given a perObjectLightIndex
UtsLight GetAdditionalPerObjectUtsLight(int perObjectLightIndex, float3 positionWS,float4 positionCS)
{
// Abstraction over Light input constants
#if USE_STRUCTURED_BUFFER_FOR_LIGHT_DATA
float4 lightPositionWS = _AdditionalLightsBuffer[perObjectLightIndex].position;
half3 color = _AdditionalLightsBuffer[perObjectLightIndex].color.rgb;
half4 distanceAndSpotAttenuation = _AdditionalLightsBuffer[perObjectLightIndex].attenuation;
half4 spotDirection = _AdditionalLightsBuffer[perObjectLightIndex].spotDirection;
half4 lightOcclusionProbeInfo = _AdditionalLightsBuffer[perObjectLightIndex].occlusionProbeChannels;
#else
float4 lightPositionWS = _AdditionalLightsPosition[perObjectLightIndex];
half3 color = _AdditionalLightsColor[perObjectLightIndex].rgb;
half4 distanceAndSpotAttenuation = _AdditionalLightsAttenuation[perObjectLightIndex];
half4 spotDirection = _AdditionalLightsSpotDir[perObjectLightIndex];
half4 lightOcclusionProbeInfo = _AdditionalLightsOcclusionProbes[perObjectLightIndex];
#endif
// Directional lights store direction in lightPosition.xyz and have .w set to 0.0.
// This way the following code will work for both directional and punctual lights.
float3 lightVector = lightPositionWS.xyz - positionWS * lightPositionWS.w;
float distanceSqr = max(dot(lightVector, lightVector), HALF_MIN);
half3 lightDirection = half3(lightVector * rsqrt(distanceSqr));
half attenuation = DistanceAttenuation(distanceSqr, distanceAndSpotAttenuation.xy) * AngleAttenuation(spotDirection.xyz, lightDirection, distanceAndSpotAttenuation.zw);
UtsLight light;
light.direction = lightDirection;
light.distanceAttenuation = attenuation;
light.shadowAttenuation = AdditionalLightRealtimeShadowUTS(perObjectLightIndex, positionWS, positionCS);
light.color = color;
light.type = lightPositionWS.w;
// In case we're using light probes, we can sample the attenuation from the `unity_ProbesOcclusion`
#if defined(LIGHTMAP_ON) || defined(_MIXED_LIGHTING_SUBTRACTIVE)
// First find the probe channel from the light.
// Then sample `unity_ProbesOcclusion` for the baked occlusion.
// If the light is not baked, the channel is -1, and we need to apply no occlusion.
// probeChannel is the index in 'unity_ProbesOcclusion' that holds the proper occlusion value.
int probeChannel = lightOcclusionProbeInfo.x;
// lightProbeContribution is set to 0 if we are indeed using a probe, otherwise set to 1.
half lightProbeContribution = lightOcclusionProbeInfo.y;
half probeOcclusionValue = unity_ProbesOcclusion[probeChannel];
light.distanceAttenuation *= max(probeOcclusionValue, lightProbeContribution);
#endif
return light;
}
// Fills a light struct given a loop i index. This will convert the i
// index to a perObjectLightIndex
UtsLight GetAdditionalUtsLight(uint i, float3 positionWS,float4 positionCS)
{
int perObjectLightIndex = GetPerObjectLightIndex(i);
return GetAdditionalPerObjectUtsLight(perObjectLightIndex, positionWS, positionCS);
}
half3 GetLightColor(UtsLight light)
{
return light.color * light.distanceAttenuation;
}
#define INIT_UTSLIGHT(utslight) \
utslight.direction = 0; \
utslight.color = 0; \
utslight.distanceAttenuation = 0; \
utslight.shadowAttenuation = 0; \
utslight.type = 0
int DetermineUTS_MainLightIndex(float3 posW, float4 shadowCoord, float4 positionCS)
{
UtsLight mainLight;
INIT_UTSLIGHT(mainLight);
int mainLightIndex = MAINLIGHT_NOT_FOUND;
UtsLight nextLight = GetUrpMainUtsLight(shadowCoord, positionCS);
if (nextLight.distanceAttenuation > mainLight.distanceAttenuation && nextLight.type == 0)
{
mainLight = nextLight;
mainLightIndex = MAINLIGHT_IS_MAINLIGHT;
}
int lightCount = GetAdditionalLightsCount();
for (int ii = 0; ii < lightCount; ++ii)
{
nextLight = GetAdditionalUtsLight(ii, posW, positionCS);
if (nextLight.distanceAttenuation > mainLight.distanceAttenuation && nextLight.type == 0)
{
mainLight = nextLight;
mainLightIndex = ii;
}
}
return mainLightIndex;
}
UtsLight GetMainUtsLightByID(int index,float3 posW, float4 shadowCoord, float4 positionCS)
{
UtsLight mainLight;
INIT_UTSLIGHT(mainLight);
if (index == MAINLIGHT_NOT_FOUND)
{
return mainLight;
}
if (index == MAINLIGHT_IS_MAINLIGHT)
{
return GetUrpMainUtsLight(shadowCoord, positionCS);
}
return GetAdditionalUtsLight(index, posW, positionCS);
}
VertexOutput vert (VertexInput v) {
VertexOutput o = (VertexOutput)0;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_TRANSFER_INSTANCE_ID(v, o);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.uv0 = v.texcoord0;
//v.2.0.4
#ifdef _IS_ANGELRING_OFF
//
#elif _IS_ANGELRING_ON
o.uv1 = v.texcoord1;
#endif
o.normalDir = UnityObjectToWorldNormal(v.normal);
o.tangentDir = normalize( mul( unity_ObjectToWorld, float4( v.tangent.xyz, 0.0 ) ).xyz );
o.bitangentDir = normalize(cross(o.normalDir, o.tangentDir) * v.tangent.w);
o.posWorld = mul(unity_ObjectToWorld, v.vertex);
o.pos = UnityObjectToClipPos( v.vertex );
//v.2.0.7 Detection of the inside the mirror (right or left-handed) o.mirrorFlag = -1 then "inside the mirror".用于判断是否是渲染镜子反射结果。
//[0]Right unit vector [1] Up unit vector [2] -1 * world space camera Forward unit vector
float3 crossFwd = cross(UNITY_MATRIX_V[0].xyz, UNITY_MATRIX_V[1].xyz);
o.mirrorFlag = dot(crossFwd, UNITY_MATRIX_V[2].xyz) < 0 ? 1 : -1;
//
float3 positionWS = TransformObjectToWorld(v.vertex.xyz);
float4 positionCS = TransformWorldToHClip(positionWS);
half3 vertexLight = VertexLighting(o.posWorld.xyz, o.normalDir);
half fogFactor = ComputeFogFactor(positionCS.z);
OUTPUT_LIGHTMAP_UV(v.lightmapUV, unity_LightmapST, o.lightmapUV);
OUTPUT_SH(o.normalDir.xyz, o.vertexSH);
# if defined(_ADDITIONAL_LIGHTS_VERTEX) || (VERSION_LOWER(12, 0))
o.fogFactorAndVertexLight = half4(fogFactor, vertexLight);
#else
o.fogFactor = fogFactor;
#endif
o.positionCS = positionCS;
#if defined(_MAIN_LIGHT_SHADOWS) && !defined(_RECEIVE_SHADOWS_OFF)
#if SHADOWS_SCREEN
o.shadowCoord = ComputeScreenPos(positionCS);
#else
o.shadowCoord = TransformWorldToShadowCoord(o.posWorld.xyz);
#endif
o.mainLightID = DetermineUTS_MainLightIndex(o.posWorld.xyz, o.shadowCoord, positionCS);
#else
o.mainLightID = DetermineUTS_MainLightIndex(o.posWorld.xyz, 0, positionCS);
#endif
return o;
}
#if defined(_SHADINGGRADEMAP)
#include "UniversalToonBodyShadingGradeMap.hlsl"
#else //#if defined(_SHADINGGRADEMAP)
#include "UniversalToonBodyDoubleShadeWithFeather.hlsl"
#endif //#if defined(_SHADINGGRADEMAP)
float4 frag(VertexOutput i, fixed facing : VFACE) : SV_TARGET
{
#if defined(_SHADINGGRADEMAP)
return fragShadingGradeMap(i, facing);
#else
return fragDoubleShadeFeather(i, facing);
#endif
}
```

View File

@@ -0,0 +1,204 @@
## 描边相关属性
| Property | Function |
| ------------------------- | ------------------------------------------------------------ |
| `OUTLINE MODE` | 指定背面挤出描边的方式。 你可以选择`NML`(正常倒置法)/`POS`(位置缩放法)。/ `POS`位置缩放法。在大多数情况下使用NML但如果是只由硬边组成的网格如立方体POS将防止轮廓被断开。
对简单的形状使用POS对人物和有复杂轮廓的东西使用NML会比较好。 |
| `Outline_Width` | 定义描边宽度 **注意: 这个值依赖于模型被导入Unity时的比例。** which means that you have to be careful if the scale is not 1. |
| `Farthest_Distance` | 轮廓的宽度将根据摄像机和物体之间的距离而改变。摄像机大于这个距离时轮廓宽度将为0。 |
| `Nearest_Distance` | 轮廓的宽度将根据摄像机和物体之间的距离而改变。摄像机小于这个距离时宽度将为`Outline_Width`。 |
| `Outline_Sampler` | 用于美术手动隐藏指定区域的轮廓? |
| `Outline_Color` | 轮廓颜色。 |
| `Is_BlendBaseColor` | 是否与BaseColor颜色融合。 |
| `Is_LightColor_Outline` | 是否受灯光颜色影响。 |
| `Is_OutlineTex` | 是否存在轮廓贴图。 |
| `OutlineTex` | 轮廓贴图,用于控制颜色。 |
| `Offset_Camera_Z` | 在摄像机空间Z坐标轴上偏移。可以用于调整模型尖端与模型交界处处效果。 大多数情况设置为0。 |
| `Is_BakedNormal` | 是否使用烘焙的法线贴图的法线值。 |
| `BakedNormal for Outline` | 指定的烘焙法线贴图。 |
## 调整轮廓强度
>`_OutlineMode`有两种模式NormalDirection与PositionScaling,除了一些简单基础几何体,其他一般使用法线方向偏移。
### Outline Sampler
![](https://github.com/unity3d-jp/UnityChanToonShaderVer2_Project/raw/release/urp/2.3.0/Documentation~/Images_jpg/0906-18_01.jpg)
黑色表示“无线条”,白色表示宽度为 100%。
### BakedNormal
采样烘焙的法线贴图,并将值赋予顶点法线。
### Offset_Camera_Z
>`Offset_Camera_Z`就是单纯的深度偏移,可以用于调整模型尖端与模型交界处处效果。但本人认为这个没有必要。
效果如图:
![](https://github.com/unity3d-jp/UnityChanToonShaderVer2_Project/raw/release/urp/2.3.0/Documentation~/Images_jpg/0205-11_01.jpg)
## 顶点着色器
主要用于偏移顶点坐标、采样法线贴图值并传递给顶点以及传递顶点数据。计算物体与摄像机距离,对设定的最近距离与最远距离插值得到距离因子。之后乘以`_Outline_Width*0.001`
>个人认为这里的比例存在问题,应该使用到摄像机矩阵参数作为比值会比较好。
```c#
float Set_Outline_Width = (_Outline_Width*0.001*smoothstep( _Farthest_Distance, _Nearest_Distance, distance(objPos.rgb,_WorldSpaceCameraPos) )*_Outline_Sampler_var.rgb).r;
//Transparent开启时`_ZOverDrawMode`为1否则为0。
Set_Outline_Width *= (1.0f - _ZOverDrawMode);
```
```c#
#ifdef _OUTLINE_NML
//v.2.0.4.3 baked Normal Texture for Outline
o.pos = UnityObjectToClipPos(lerp(float4(v.vertex.xyz + v.normal*Set_Outline_Width,1), float4(v.vertex.xyz + _BakedNormalDir*Set_Outline_Width,1),_Is_BakedNormal));
#elif _OUTLINE_POS
Set_Outline_Width = Set_Outline_Width*2;
float signVar = dot(normalize(v.vertex),normalize(v.normal))<0 ? -1 : 1;
o.pos = UnityObjectToClipPos(float4(v.vertex.xyz + signVar*normalize(v.vertex)*Set_Outline_Width, 1));
#endif
//v.2.0.7.5
o.pos.z = o.pos.z + _Offset_Z * _ClipCameraPos.z;
return o;
```
## 像素着色器
主要用来设置轮廓颜色,`轮廓颜色=灯光颜色 * BaseMap * BaseColor * (OutlineTex)`。同时提供了裁剪功能可以根据ClippingMask、BaseMap的Alpha通道进行裁剪。
>`_Is_LightColor_Outline`根据`ShaderGUI`的选项进行设置但这里感觉可以增加CaptureMat、World映射Ramp、环境探针以增加更多效果。
```c#
//计算灯光色小于0.05则使用环境色)、灯光亮度
half3 ambientSkyColor = unity_AmbientSky.rgb>0.05 ? unity_AmbientSky.rgb*_Unlit_Intensity : half3(0.05,0.05,0.05)*_Unlit_Intensity;
float3 lightColor = _LightColor0.rgb >0.05 ? _LightColor0.rgb : ambientSkyColor.rgb;
float lightColorIntensity = (0.299*lightColor.r + 0.587*lightColor.g + 0.114*lightColor.b);
lightColor = lightColorIntensity<1 ? lightColor : lightColor/lightColorIntensity;//灯光亮度小于1时对颜色亮度进行缩放。
lightColor = lerp(half3(1.0,1.0,1.0), lightColor, _Is_LightColor_Outline);
//计算BaseMap*BaseColor值、采样OutlineMap值。
float2 Set_UV0 = i.uv0;
float4 _MainTex_var = SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex, TRANSFORM_TEX(Set_UV0, _MainTex));
float3 Set_BaseColor = _BaseColor.rgb*_MainTex_var.rgb;
float3 _Is_BlendBaseColor_var = lerp( _Outline_Color.rgb*lightColor, (_Outline_Color.rgb*Set_BaseColor*Set_BaseColor*lightColor), _Is_BlendBaseColor );
float3 _OutlineTex_var = tex2D(_OutlineTex,TRANSFORM_TEX(Set_UV0, _OutlineTex)).rgb;
#ifdef _IS_OUTLINE_CLIPPING_NO
float3 Set_Outline_Color = lerp(_Is_BlendBaseColor_var, _OutlineTex_var.rgb*_Outline_Color.rgb*lightColor, _Is_OutlineTex );
return float4(Set_Outline_Color,1.0);
#elif _IS_OUTLINE_CLIPPING_YES
//开启裁剪模式的状态下可以根据ClippingMask、BaseMap的Alpha通道进行裁剪。
float4 _ClippingMask_var = SAMPLE_TEXTURE2D(_ClippingMask, sampler_MainTex, TRANSFORM_TEX(Set_UV0, _ClippingMask));
float Set_MainTexAlpha = _MainTex_var.a;
float _IsBaseMapAlphaAsClippingMask_var = lerp( _ClippingMask_var.r, Set_MainTexAlpha, _IsBaseMapAlphaAsClippingMask );
float _Inverse_Clipping_var = lerp( _IsBaseMapAlphaAsClippingMask_var, (1.0 - _IsBaseMapAlphaAsClippingMask_var), _Inverse_Clipping );
float Set_Clipping = saturate((_Inverse_Clipping_var+_Clipping_Level));
clip(Set_Clipping - 0.5);
float4 Set_Outline_Color = lerp( float4(_Is_BlendBaseColor_var,Set_Clipping), float4((_OutlineTex_var.rgb*_Outline_Color.rgb*lightColor),Set_Clipping), _Is_OutlineTex );
return Set_Outline_Color;
#endif
}
```
## 完整代码
```c#
uniform float4 _LightColor0; // this is not set in c# code ?
struct VertexInput {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 tangent : TANGENT;
float2 texcoord0 : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct VertexOutput {
float4 pos : SV_POSITION;
float2 uv0 : TEXCOORD0;
float3 normalDir : TEXCOORD1;
float3 tangentDir : TEXCOORD2;
float3 bitangentDir : TEXCOORD3;
UNITY_VERTEX_OUTPUT_STEREO
};
VertexOutput vert (VertexInput v) {
VertexOutput o = (VertexOutput)0;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.uv0 = v.texcoord0;
float4 objPos = mul ( unity_ObjectToWorld, float4(0,0,0,1) ); //取得物体世界坐标
float2 Set_UV0 = o.uv0;
//TRANSFORM_TEX(v.texcoord,_MainTex);等价于o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
float4 _Outline_Sampler_var = tex2Dlod(_Outline_Sampler,float4(TRANSFORM_TEX(Set_UV0, _Outline_Sampler),0.0,0));//使用_Outline_Sampler的值进行缩放与偏移UV后在顶点着色器中对_Outline_Sampler进行采样。
//v.2.0.4.3 baked Normal Texture for Outline
//计算法线以及法向空间矩阵
o.normalDir = UnityObjectToWorldNormal(v.normal);
o.tangentDir = normalize( mul( unity_ObjectToWorld, float4( v.tangent.xyz, 0.0 ) ).xyz );
o.bitangentDir = normalize(cross(o.normalDir, o.tangentDir) * v.tangent.w);
float3x3 tangentTransform = float3x3( o.tangentDir, o.bitangentDir, o.normalDir);
//UnpackNormal() can't be used, and so as follows. Do not specify a bump for the texture to be used.
//采样_BakedNormal贴图值并解算烘焙法线方向
float4 _BakedNormal_var = (tex2Dlod(_BakedNormal,float4(TRANSFORM_TEX(Set_UV0, _BakedNormal),0.0,0)) * 2 - 1);
float3 _BakedNormalDir = normalize(mul(_BakedNormal_var.rgb, tangentTransform));
//end
float Set_Outline_Width = (_Outline_Width*0.001*smoothstep( _Farthest_Distance, _Nearest_Distance, distance(objPos.rgb,_WorldSpaceCameraPos) )*_Outline_Sampler_var.rgb).r;
Set_Outline_Width *= (1.0f - _ZOverDrawMode);
//v.2.0.7.5
//计算裁剪位置以及按照不同平台来设置_Offset_Z
float4 _ClipCameraPos = mul(UNITY_MATRIX_VP, float4(_WorldSpaceCameraPos.xyz, 1));
//v.2.0.7
#if defined(UNITY_REVERSED_Z)
//v.2.0.4.2 (DX)
_Offset_Z = _Offset_Z * -0.01;
#else
//OpenGL
_Offset_Z = _Offset_Z * 0.01;
#endif
//v2.0.4
//根据OutlineMode对顶线位置进行偏移。
#ifdef _OUTLINE_NML
//v.2.0.4.3 baked Normal Texture for Outline
o.pos = UnityObjectToClipPos(lerp(float4(v.vertex.xyz + v.normal*Set_Outline_Width,1), float4(v.vertex.xyz + _BakedNormalDir*Set_Outline_Width,1),_Is_BakedNormal));
#elif _OUTLINE_POS
Set_Outline_Width = Set_Outline_Width*2;
float signVar = dot(normalize(v.vertex),normalize(v.normal))<0 ? -1 : 1;
o.pos = UnityObjectToClipPos(float4(v.vertex.xyz + signVar*normalize(v.vertex)*Set_Outline_Width, 1));
#endif
//v.2.0.7.5
o.pos.z = o.pos.z + _Offset_Z * _ClipCameraPos.z;
return o;
}
float4 frag(VertexOutput i) : SV_Target{
//v.2.0.5
if (_ZOverDrawMode > 0.99f)
{
return float4(1.0f, 1.0f, 1.0f, 1.0f); // but nothing should be drawn except Z value as colormask is set to 0
}
_Color = _BaseColor;
float4 objPos = mul ( unity_ObjectToWorld, float4(0,0,0,1) );
//v.2.0.7.5
half3 ambientSkyColor = unity_AmbientSky.rgb>0.05 ? unity_AmbientSky.rgb*_Unlit_Intensity : half3(0.05,0.05,0.05)*_Unlit_Intensity;
float3 lightColor = _LightColor0.rgb >0.05 ? _LightColor0.rgb : ambientSkyColor.rgb;
float lightColorIntensity = (0.299*lightColor.r + 0.587*lightColor.g + 0.114*lightColor.b);
lightColor = lightColorIntensity<1 ? lightColor : lightColor/lightColorIntensity;
lightColor = lerp(half3(1.0,1.0,1.0), lightColor, _Is_LightColor_Outline);
float2 Set_UV0 = i.uv0;
float4 _MainTex_var = SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex, TRANSFORM_TEX(Set_UV0, _MainTex));
float3 Set_BaseColor = _BaseColor.rgb*_MainTex_var.rgb;
float3 _Is_BlendBaseColor_var = lerp( _Outline_Color.rgb*lightColor, (_Outline_Color.rgb*Set_BaseColor*Set_BaseColor*lightColor), _Is_BlendBaseColor );
//
float3 _OutlineTex_var = tex2D(_OutlineTex,TRANSFORM_TEX(Set_UV0, _OutlineTex)).rgb;
//v.2.0.7.5
#ifdef _IS_OUTLINE_CLIPPING_NO
float3 Set_Outline_Color = lerp(_Is_BlendBaseColor_var, _OutlineTex_var.rgb*_Outline_Color.rgb*lightColor, _Is_OutlineTex );
return float4(Set_Outline_Color,1.0);
#elif _IS_OUTLINE_CLIPPING_YES
float4 _ClippingMask_var = SAMPLE_TEXTURE2D(_ClippingMask, sampler_MainTex, TRANSFORM_TEX(Set_UV0, _ClippingMask));
float Set_MainTexAlpha = _MainTex_var.a;
float _IsBaseMapAlphaAsClippingMask_var = lerp( _ClippingMask_var.r, Set_MainTexAlpha, _IsBaseMapAlphaAsClippingMask );
float _Inverse_Clipping_var = lerp( _IsBaseMapAlphaAsClippingMask_var, (1.0 - _IsBaseMapAlphaAsClippingMask_var), _Inverse_Clipping );
float Set_Clipping = saturate((_Inverse_Clipping_var+_Clipping_Level));
clip(Set_Clipping - 0.5);
float4 Set_Outline_Color = lerp( float4(_Is_BlendBaseColor_var,Set_Clipping), float4((_OutlineTex_var.rgb*_Outline_Color.rgb*lightColor),Set_Clipping), _Is_OutlineTex );
return Set_Outline_Color;
#endif
}
```

View File

@@ -0,0 +1,793 @@
## 总览
- `DoubleShadeWithFeather`UTS/UniversalToon 的标准工作流程模式。允许 2 种阴影颜色(双阴影颜色)和颜色之间的渐变(羽化)。
- `ShadingGradeMap`:更高级的工作流程模式。除了 DoubleShadeWithFeather 功能之外,此着色器还可以保存称为 ShadingGradeMap 的特殊贴图。
![](https://github.com/unity3d-jp/UnityChanToonShaderVer2_Project/raw/release/urp/2.3.0/Documentation~/Images_jpg/URP_image035.jpg)
### 案例文件
示例文件于`urp-2.2.0-notpackage`标签之后被移除所以可以切换到该commit将示例文件复制出来。
- ToonShader.unity Settings for an illustration-style shader.
- ToonShader_CelLook.unity Settings for a cel-style shader.
- ToonShader_Emissive.unity Settings for a shader with an emissive .
- ToonShader_Firefly.unity Multiple real-time point lights.
- AngelRing\AngelRing.unityAngel ring and ShadingGradeMap sample.
- Baked Normal\Cube_HardEdge.unityBaked Normal reference.
- BoxProjection\BoxProjection.unity Lighting a dark room using Box Projection.
- EmissiveAnimation\EmisssiveAnimation.unityEmissiveAnimation sample.
- LightAndShadows\LightAndShadows.unityComparison between the PBR shader and UTS2.
- MatCapMask\MatCapMask.unityMatcapMask sample.
- Mirror\MirrorTest.unity: Sample scene checking for a mirror object
- NormalMap\NormalMap.unity Tricks for using the normal map with UTS2.
- PointLightTest\PointLightTest.unitySample of cel-style content with point lights.
- Sample\Sample.unity Introduction to the basic UTS2 shaders.
- ShaderBall\ShaderBall.unityUTS2 settings on an example shader ball.
## 贴图
### 基础贴图
- Base Color
- 1st Shade Color
- 2nd Shade Color
除了基础的颜色纹理还可以接受多种自定义选项,例如
- High Color
- Rim Light
- MatCap
- Emissive
### 法线贴图
法线贴图一般在`UTS/UniversalToon`中用于平滑阴影渐变效果。
此外还通过与比例尺一起使用来调整皮肤纹理,以及使用`MatCap`表现头发高光效果。
| `选项` | 函数 | 属性 |
|:-------------------|:-------------------|:-------------------|
| NormalMap Effectiveness | 选择是否在每种颜色上反射法线贴图。如果按钮为Off则该颜色不会反映法线贴图而是由对象本身的几何形状评估。 | |
| `3 Basic Colors` | 当您希望法线贴图反映在颜色中时,请设置为活动。 | _Is_NormalMapToBase |
| `HighColor` | 当您希望法线贴图影响高颜色时,请设置活动。 | _Is_NormalMapToHighColor |
| `RimLight` | 当您希望法线贴图影响 RimLight 时请设置为Active。 | _Is_NormalMapToRimLight |
### 用于添加阴影区域的PositionMap
用于调整投射阴影的顶点位置。`UniversalToonBodyShadingGradeMap`模式下才能使用。
| `选项` | 函数 | 属性 |
|:-------------------|:-------------------|:-------------------|
| `1st Shade Position Map` |使用PositionMap强制修改`1st Shade Color``Position`, 与系统照明无关。指定需要进行投射阴影的区域。| _Set_1st_ShadePosition |
| `2nd Shade Position Map` |使用PositionMap强制修改`2st Shade Color``Position`, 与系统照明无关。指定需要进行投射阴影的区域。 (也会影响到`1st Shade Color``Position Map`). | _Set_2nd_ShadePosition |
为了独立于光照显示第二阴影颜色,请确保填充第一和第二阴影颜色位置图重叠的位置。这样,即使来自其他照明的阴影落在第二个阴影颜色区域,它也会继续显示。
### Shading Grade Map
基于光照来调整阴影亮度,允许在 UV 点级别控制第一和/或第二阴影颜色。
该贴图的精细控制使得“当光线照射到衣服上时隐藏褶皱”这样的效果成为可能。将诸如环境光遮蔽贴图之类的着色贴图应用到着色等级贴图可以使阴影更容易根据光照下降。这对于创建跟随头发刘海或衣服凹面部分的阴影非常有用。
### 边缘光效果(RimLight)
`RimLight``LightDirection_MaskOn``Add_Antipoden_RimLight`效果:
![](https://github.com/unity3d-jp/UnityChanToonShaderVer2_Project/raw/release/urp/2.3.0/Documentation~/Images_jpg/UT2018_UTS2_SuperTips_14.jpg)
RimLight 通常从相机的角度显示在对象边缘周围。在 UTS2 中,您可以相对于主灯的位置调整边缘灯的显示位置。('LightDirection 蒙版')
您还可以将 RimLight 设置为与光源相反的方向。您还可以使用“添加 Antipodean_RimLight”渲染“光反射”。
如果您只想在光源的相反方向上显示边缘光并沿光源方向切割边缘光,请将边缘光的光方向颜色指定为黑色 (0,0,0)。
![](https://github.com/unity3d-jp/UnityChanToonShaderVer2_Project/raw/release/urp/2.3.0/Documentation~/Images_jpg/UT2018_UTS2_SuperTips_15.jpg)
### 天使环
https://github.com/unity3d-jp/UnityChanToonShaderVer2_Project/blob/release/urp/2.3.0/Documentation~/index.md#-making-materials-for-angel-ring
制作天使环材质首先需要将头发模型正面投射到UV上做制作贴图。
## 颜色
高光颜色
通常色(被光照射部分)
1号阴影
2号阴影
## 模板功能
![](https://github.com/unity3d-jp/UnityChanToonShaderVer2_Project/raw/release/urp/2.3.0/Documentation~/Images_jpg/URP_image036.jpg)
可以用来制作睫毛挡住头发的效果。
## 半兰伯特模型
>有一个问题仍然存在。在光照无法到达的区域,模型的外观通常是全黑的,没有任何明暗变化,这会使模型的背光区域看起来就像一个平面一样,失去了模型细节表现。
为此Valve 公司在开发游戏《半条命》时提出了一种技术,由于该技术是在原兰伯特光照模型的基础上进行了一个简单的修改, 因此被称为半兰伯特光照模型。
$$Cdiffuse=(Clight · Mdiffuse) (0.5 (n· I) + 0.5)$$
把n·I 的结果范围从[-1, 1 映射到0, 1 范围内。也就是说对于模型的背光面在原兰伯特光照模型中点积结果将映射到同一个值即0 值处;而在半兰伯特模型中,背光面也可以有明暗变化,不同的点积结果会映射到不同的值上。
## 高光计算
使用半兰伯特模型计算高光Mask
```c#
//半影值计算高光Mask(半兰伯特模型)
float _Specular_var = 0.5*dot(halfDirection,lerp( i.normalDir, normalDirection, _Is_NormalMapToHighColor ))+0.5; // Specular
//Step(a,x) => x>=a ? 1 : 0,计算高光Mask
float _TweakHighColorMask_var = (saturate((_Set_HighColorMask_var.g+_Tweak_HighColorMaskLevel))*lerp( (1.0 - step(_Specular_var,(1.0 - pow(abs(_HighColor_Power),5)))), pow(abs(_Specular_var),exp2(lerp(11,1,_HighColor_Power))), _Is_SpecularToHighColor ));
```
高光颜色=高光贴图 * 高光颜色设定值 * 灯光颜色(可选)
```c#
float4 _HighColor_Tex_var = tex2D(_HighColor_Tex, TRANSFORM_TEX(Set_UV0, _HighColor_Tex));
float3 _HighColor_var = (lerp( (_HighColor_Tex_var.rgb*_HighColor.rgb), ((_HighColor_Tex_var.rgb*_HighColor.rgb)*Set_LightColor), _Is_LightColor_HighColor )*_TweakHighColorMask_var);
//Composition: 3 Basic Colors and HighColor as Set_HighColor
float3 Set_HighColor = (lerp(SATURATE_IF_SDR((Set_FinalBaseColor-_TweakHighColorMask_var)), Set_FinalBaseColor, lerp(_Is_BlendAddToHiColor,1.0,_Is_SpecularToHighColor) )+lerp( _HighColor_var, (_HighColor_var*((1.0 - Set_FinalShadowMask)+(Set_FinalShadowMask*_TweakHighColorOnShadow))), _Is_UseTweakHighColorOnShadow ));
```
## 边缘光
边缘光颜色=边缘光贴图 * 边缘光颜色设定值 * 灯光颜色(可选),其他调整参数有`_Ap_RimLight_Power`、``
```c#
float4 _Set_RimLightMask_var = tex2D(_Set_RimLightMask, TRANSFORM_TEX(Set_UV0, _Set_RimLightMask));
float3 _Is_LightColor_RimLight_var = lerp( _RimLightColor.rgb, (_RimLightColor.rgb*Set_LightColor), _Is_LightColor_RimLight );
//区域计算=1-dot(ViewDir,Normal),之后pow(_RimArea_var,exp2(lerp(3,0,_RimLight_Power)))最后使用UTS祖传公式计算
float _RimArea_var = abs(1.0 - dot(lerp( i.normalDir, normalDirection, _Is_NormalMapToRimLight ),viewDirection));
float _RimLightPower_var = pow(_RimArea_var,exp2(lerp(3,0,_RimLight_Power)));
float _Rimlight_InsideMask_var = saturate(lerp( (0.0 + ( (_RimLightPower_var - _RimLight_InsideMask) * (1.0 - 0.0) ) / (1.0 - _RimLight_InsideMask)), step(_RimLight_InsideMask,_RimLightPower_var), _RimLight_FeatherOff ));
float _VertHalfLambert_var = 0.5*dot(i.normalDir,lightDirection)+0.5;
float3 _LightDirection_MaskOn_var = lerp( (_Is_LightColor_RimLight_var*_Rimlight_InsideMask_var), (_Is_LightColor_RimLight_var*saturate((_Rimlight_InsideMask_var-((1.0 - _VertHalfLambert_var)+_Tweak_LightDirection_MaskLevel)))), _LightDirection_MaskOn );
float _ApRimLightPower_var = pow(_RimArea_var,exp2(lerp(3,0,_Ap_RimLight_Power)));
float3 Set_RimLight = (SATURATE_IF_SDR((_Set_RimLightMask_var.g+_Tweak_RimLightMaskLevel))*lerp( _LightDirection_MaskOn_var, (_LightDirection_MaskOn_var+(lerp( _Ap_RimLightColor.rgb, (_Ap_RimLightColor.rgb*Set_LightColor), _Is_LightColor_Ap_RimLight )*saturate((lerp( (0.0 + ( (_ApRimLightPower_var - _RimLight_InsideMask) * (1.0 - 0.0) ) / (1.0 - _RimLight_InsideMask)), step(_RimLight_InsideMask,_ApRimLightPower_var), _Ap_RimLight_FeatherOff )-(saturate(_VertHalfLambert_var)+_Tweak_LightDirection_MaskLevel))))), _Add_Antipodean_RimLight ));
//Composition: HighColor and RimLight as _RimLight_var
float3 _RimLight_var = lerp( Set_HighColor, (Set_HighColor+Set_RimLight), _RimLight );
```
## Matcap与头发的各向异性高光效果
UTS的老版本模型采用了Matcap给头发圈定高光颜色以及区域范围之后使用法线贴图来实现类似各向异性的效果
```c#
//Matcap
//v.2.0.6 : CameraRolling Stabilizer
//Mirror Script Determination: if sign_Mirror = -1, determine "Inside the mirror".
//v.2.0.7
fixed _sign_Mirror = i.mirrorFlag;
//
float3 _Camera_Right = UNITY_MATRIX_V[0].xyz;
float3 _Camera_Front = UNITY_MATRIX_V[2].xyz;
float3 _Up_Unit = float3(0, 1, 0);
float3 _Right_Axis = cross(_Camera_Front, _Up_Unit);
//Invert if it's "inside the mirror".
if(_sign_Mirror < 0){
_Right_Axis = -1 * _Right_Axis;
_Rotate_MatCapUV = -1 * _Rotate_MatCapUV;
}else{
_Right_Axis = _Right_Axis;
}
float _Camera_Right_Magnitude = sqrt(_Camera_Right.x*_Camera_Right.x + _Camera_Right.y*_Camera_Right.y + _Camera_Right.z*_Camera_Right.z);
float _Right_Axis_Magnitude = sqrt(_Right_Axis.x*_Right_Axis.x + _Right_Axis.y*_Right_Axis.y + _Right_Axis.z*_Right_Axis.z);
float _Camera_Roll_Cos = dot(_Right_Axis, _Camera_Right) / (_Right_Axis_Magnitude * _Camera_Right_Magnitude);
float _Camera_Roll = acos(clamp(_Camera_Roll_Cos, -1, 1));
fixed _Camera_Dir = _Camera_Right.y < 0 ? -1 : 1;
float _Rot_MatCapUV_var_ang = (_Rotate_MatCapUV*3.141592654) - _Camera_Dir*_Camera_Roll*_CameraRolling_Stabilizer;
//v.2.0.7
float2 _Rot_MatCapNmUV_var = RotateUV(Set_UV0, (_Rotate_NormalMapForMatCapUV*3.141592654), float2(0.5, 0.5), 1.0);
//V.2.0.6
float3 _NormalMapForMatCap_var = UnpackNormalScale(tex2D(_NormalMapForMatCap, TRANSFORM_TEX(_Rot_MatCapNmUV_var, _NormalMapForMatCap)), _BumpScaleMatcap);
//v.2.0.5: MatCap with camera skew correction
float3 viewNormal = (mul(UNITY_MATRIX_V, float4(lerp( i.normalDir, mul( _NormalMapForMatCap_var.rgb, tangentTransform ).rgb, _Is_NormalMapForMatCap ),0))).rgb;
float3 NormalBlend_MatcapUV_Detail = viewNormal.rgb * float3(-1,-1,1);
float3 NormalBlend_MatcapUV_Base = (mul( UNITY_MATRIX_V, float4(viewDirection,0) ).rgb*float3(-1,-1,1)) + float3(0,0,1);
float3 noSknewViewNormal = NormalBlend_MatcapUV_Base*dot(NormalBlend_MatcapUV_Base, NormalBlend_MatcapUV_Detail)/NormalBlend_MatcapUV_Base.b - NormalBlend_MatcapUV_Detail;
float2 _ViewNormalAsMatCapUV = (lerp(noSknewViewNormal,viewNormal,_Is_Ortho).rg*0.5)+0.5;
//
//v.2.0.7
float2 _Rot_MatCapUV_var = RotateUV((0.0 + ((_ViewNormalAsMatCapUV - (0.0+_Tweak_MatCapUV)) * (1.0 - 0.0) ) / ((1.0-_Tweak_MatCapUV) - (0.0+_Tweak_MatCapUV))), _Rot_MatCapUV_var_ang, float2(0.5, 0.5), 1.0);
//If it is "inside the mirror", flip the UV left and right.
if(_sign_Mirror < 0){
_Rot_MatCapUV_var.x = 1-_Rot_MatCapUV_var.x;
}else{
_Rot_MatCapUV_var = _Rot_MatCapUV_var;
}
float4 _MatCap_Sampler_var = tex2Dlod(_MatCap_Sampler, float4(TRANSFORM_TEX(_Rot_MatCapUV_var, _MatCap_Sampler), 0.0, _BlurLevelMatcap));
float4 _Set_MatcapMask_var = tex2D(_Set_MatcapMask, TRANSFORM_TEX(Set_UV0, _Set_MatcapMask));
```
## UniversalToonBodyShadingGradeMap
合成公式:
$$1-\frac{( HalfLambert - (Step-Feather))}{Feather}$$
### 初始化数据
初始化表面数据、InputData与Varyings并且填充数据。
### 灯光与环境光数据
```c#
//计算间接照明值使用间接Diffuse乘以SurfaceDiffuse间接Specular乘以SurfaceSpecular最后两者叠加。
half3 envColor = GlobalIlluminationUTS(brdfData, inputData.bakedGI, surfaceData.occlusion, inputData.normalWS, inputData.viewDirectionWS);
envColor *= 1.8f;
//取得主光参数direction、color、distanceAttenuation、shadowAttenuation、type
UtsLight mainLight = GetMainUtsLightByID(i.mainLightID, i.posWorld.xyz, inputData.shadowCoord, i.positionCS);
half3 mainLightColor = GetLightColor(mainLight);
real shadowAttenuation = 1.0;
# ifdef _MAIN_LIGHT_SHADOWS
shadowAttenuation = mainLight.shadowAttenuation;
# endif
```
### 开启裁剪功能
```c#
float4 _ClippingMask_var = SAMPLE_TEXTURE2D(_ClippingMask, sampler_MainTex, TRANSFORM_TEX(Set_UV0, _ClippingMask));
float Set_MainTexAlpha = _MainTex_var.a;
float _IsBaseMapAlphaAsClippingMask_var = lerp( _ClippingMask_var.r, Set_MainTexAlpha, _IsBaseMapAlphaAsClippingMask );
float _Inverse_Clipping_var = lerp( _IsBaseMapAlphaAsClippingMask_var, (1.0 - _IsBaseMapAlphaAsClippingMask_var), _Inverse_Clipping );
float Set_Clipping = saturate((_Inverse_Clipping_var+_Clipping_Level));
clip(Set_Clipping - 0.5);
```
### 计算灯光方向与灯光颜色
```c#
float3 defaultLightDirection = normalize(UNITY_MATRIX_V[2].xyz + UNITY_MATRIX_V[1].xyz);
//v.2.0.5
float3 defaultLightColor = saturate(max(half3(0.05,0.05,0.05)*_Unlit_Intensity,max(ShadeSH9(half4(0.0, 0.0, 0.0, 1.0)),ShadeSH9(half4(0.0, -1.0, 0.0, 1.0)).rgb)*_Unlit_Intensity));
//通过外部传入的_Offset_X_Axis_BLD、_Offset_Y_Axis_BLD、_Offset_Z_Axis_BLD来创建自定义灯光方向
float3 customLightDirection = normalize(mul( unity_ObjectToWorld, float4(((float3(1.0,0.0,0.0)*_Offset_X_Axis_BLD*10)+(float3(0.0,1.0,0.0)*_Offset_Y_Axis_BLD*10)+(float3(0.0,0.0,-1.0)*lerp(-1.0,1.0,_Inverse_Z_Axis_BLD))),0)).xyz);
//HLSL内置函数Any(),用于测试变量是否为非零值
float3 lightDirection = normalize(lerp(defaultLightDirection, mainLight.direction.xyz,any(mainLight.direction.xyz)));
lightDirection = lerp(lightDirection, customLightDirection, _Is_BLD);
//v.2.0.5:
half3 originalLightColor = mainLightColor.rgb;
float3 lightColor = lerp(max(defaultLightColor, originalLightColor), max(defaultLightColor, saturate(originalLightColor)), _Is_Filter_LightColor);
```
### 计算1、2、3 ShadowMask与插值后的BaseColor值之后合并到一起
### 计算高光并且合并到一起
### 计算天使环并且合并到一起
### 计算Matcap并且合成到一起
### 叠加自发光贴图结果
### 灯光循环
### 最终合成
```c#
finalColor = SATURATE_IF_SDR(finalColor) + (envLightColor*envLightIntensity*_GI_Intensity*smoothstep(1,0,envLightIntensity/2)) + emissive;
finalColor += pointLightColor;
```
## 完整代码
```c#
float4 fragShadingGradeMap(VertexOutput i, fixed facing : VFACE) : SV_TARGET
{
//计算世界法线值与摄像机朝向
i.normalDir = normalize(i.normalDir);
float3x3 tangentTransform = float3x3( i.tangentDir, i.bitangentDir, i.normalDir);
float3 viewDirection = normalize(_WorldSpaceCameraPos.xyz - i.posWorld.xyz);
float2 Set_UV0 = i.uv0;
//v.2.0.6
float3 _NormalMap_var = UnpackNormalScale(SAMPLE_TEXTURE2D(_NormalMap, sampler_MainTex, TRANSFORM_TEX(Set_UV0, _NormalMap)), _BumpScale);
float3 normalLocal = _NormalMap_var.rgb;
float3 normalDirection = normalize(mul( normalLocal, tangentTransform )); // Perturbed normals
// todo. not necessary to calc gi factor in shadowcaster pass.
//初始化表面数据、InputData与Varyings并且填充数据。
//input数据位于render-pipelines.universal包含positionWS、normalWS、viewDirectionWS、shadowCoord、fogCoord、vertexLighting、bakedGI,Varyings input用来计算InputData。Vrayings位于LitForwardPass中
//inputData主要用来计算envColorGI、主灯光数据与AdditionalUtsLight数据
SurfaceData surfaceData;
InitializeStandardLitSurfaceDataUTS(i.uv0, surfaceData);
InputData inputData;
Varyings input = (Varyings)0;
// todo. it has to be cared more.
UNITY_SETUP_INSTANCE_ID(input);
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
# ifdef LIGHTMAP_ON
# else
input.vertexSH = i.vertexSH;
# endif
input.uv = i.uv0;
# if defined(_ADDITIONAL_LIGHTS_VERTEX) || (VERSION_LOWER(12, 0))
input.fogFactorAndVertexLight = i.fogFactorAndVertexLight;
# else
input.fogFactor = i.fogFactor;
# endif
# ifdef REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR
input.shadowCoord = i.shadowCoord;
# endif
# ifdef REQUIRES_WORLD_SPACE_POS_INTERPOLATOR
input.positionWS = i.posWorld.xyz;
# endif
# ifdef _NORMALMAP
input.normalWS = half4(i.normalDir, viewDirection.x); // xyz: normal, w: viewDir.x
input.tangentWS = half4(i.tangentDir, viewDirection.y); // xyz: tangent, w: viewDir.y
# if (VERSION_LOWER(7, 5))
input.bitangentWS = half4(i.bitangentDir, viewDirection.z); // xyz: bitangent, w: viewDir.z
#endif //
# else
input.normalWS = half3(i.normalDir);
# if (VERSION_LOWER(12, 0))
input.viewDirWS = half3(viewDirection);
# endif //(VERSION_LOWER(12, 0))
# endif
//位于LitForwardPass中其中SAMPLE_GI()采样LightMap或者SH。
InitializeInputData(input, surfaceData.normalTS, inputData);
//位于Lighting中使用前几个参数初始化最后一个brdfData形参
BRDFData brdfData;
InitializeBRDFData(surfaceData.albedo,
surfaceData.metallic,
surfaceData.specular,
surfaceData.smoothness,
surfaceData.alpha, brdfData);
//计算间接照明值使用间接Diffuse乘以SurfaceDiffuse间接Specular乘以SurfaceSpecular最后两者叠加。
half3 envColor = GlobalIlluminationUTS(brdfData, inputData.bakedGI, surfaceData.occlusion, inputData.normalWS, inputData.viewDirectionWS);
envColor *= 1.8f;
//取得主光参数direction、color、distanceAttenuation、shadowAttenuation、type
UtsLight mainLight = GetMainUtsLightByID(i.mainLightID, i.posWorld.xyz, inputData.shadowCoord, i.positionCS);
half3 mainLightColor = GetLightColor(mainLight);
float4 _MainTex_var = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, TRANSFORM_TEX(Set_UV0, _MainTex));
//v.2.0.4
#ifdef _IS_TRANSCLIPPING_OFF
//
#elif _IS_TRANSCLIPPING_ON
//开启裁剪功能
float4 _ClippingMask_var = SAMPLE_TEXTURE2D(_ClippingMask, sampler_MainTex, TRANSFORM_TEX(Set_UV0, _ClippingMask));
float Set_MainTexAlpha = _MainTex_var.a;
float _IsBaseMapAlphaAsClippingMask_var = lerp( _ClippingMask_var.r, Set_MainTexAlpha, _IsBaseMapAlphaAsClippingMask );
float _Inverse_Clipping_var = lerp( _IsBaseMapAlphaAsClippingMask_var, (1.0 - _IsBaseMapAlphaAsClippingMask_var), _Inverse_Clipping );
float Set_Clipping = saturate((_Inverse_Clipping_var+_Clipping_Level));
clip(Set_Clipping - 0.5);
#endif
real shadowAttenuation = 1.0;
# ifdef _MAIN_LIGHT_SHADOWS
shadowAttenuation = mainLight.shadowAttenuation;
# endif
//v.2.0.4
float3 defaultLightDirection = normalize(UNITY_MATRIX_V[2].xyz + UNITY_MATRIX_V[1].xyz);
//v.2.0.5
float3 defaultLightColor = saturate(max(half3(0.05,0.05,0.05)*_Unlit_Intensity,max(ShadeSH9(half4(0.0, 0.0, 0.0, 1.0)),ShadeSH9(half4(0.0, -1.0, 0.0, 1.0)).rgb)*_Unlit_Intensity));
//通过外部传入的_Offset_X_Axis_BLD、_Offset_Y_Axis_BLD、_Offset_Z_Axis_BLD来创建自定义灯光方向
float3 customLightDirection = normalize(mul( unity_ObjectToWorld, float4(((float3(1.0,0.0,0.0)*_Offset_X_Axis_BLD*10)+(float3(0.0,1.0,0.0)*_Offset_Y_Axis_BLD*10)+(float3(0.0,0.0,-1.0)*lerp(-1.0,1.0,_Inverse_Z_Axis_BLD))),0)).xyz);
//HLSL内置函数Any(),用于测试变量是否为非零值
float3 lightDirection = normalize(lerp(defaultLightDirection, mainLight.direction.xyz,any(mainLight.direction.xyz)));
lightDirection = lerp(lightDirection, customLightDirection, _Is_BLD);
//v.2.0.5:
half3 originalLightColor = mainLightColor.rgb;
float3 lightColor = lerp(max(defaultLightColor, originalLightColor), max(defaultLightColor, saturate(originalLightColor)), _Is_Filter_LightColor);
////// Lighting:
//计算View与主Light的半向量
float3 halfDirection = normalize(viewDirection+lightDirection);
//v.2.0.5
_Color = _BaseColor;
#ifdef _IS_PASS_FWDBASE
//取得设置的LightColor、BaseColor(设置的灯光颜色or主贴图采样结果)、1st_ShadeMap_var最后计算_Is_LightColor_1st_Shade_var与半兰伯特值。
float3 Set_LightColor = lightColor.rgb;
float3 Set_BaseColor = lerp( (_MainTex_var.rgb*_BaseColor.rgb), ((_MainTex_var.rgb*_BaseColor.rgb)*Set_LightColor), _Is_LightColor_Base );
//v.2.0.5
float4 _1st_ShadeMap_var = lerp(SAMPLE_TEXTURE2D(_1st_ShadeMap,sampler_MainTex, TRANSFORM_TEX(Set_UV0, _1st_ShadeMap)),_MainTex_var,_Use_BaseAs1st);
//_1st_ShadeMap_var为确定区域颜色为设置的颜色。_1st_ShadeMap_var.rgb*_1st_ShadeColor.rgb*Set_LightColor可设置为不受LightColor影响。
float3 _Is_LightColor_1st_Shade_var = lerp( (_1st_ShadeMap_var.rgb*_1st_ShadeColor.rgb), ((_1st_ShadeMap_var.rgb*_1st_ShadeColor.rgb)*Set_LightColor), _Is_LightColor_1st_Shade );
float _HalfLambert_var = 0.5*dot(lerp( i.normalDir, normalDirection, _Is_NormalMapToBase ),lightDirection)+0.5; // Half Lambert
//v.2.0.6
float4 _ShadingGradeMap_var = tex2Dlod(_ShadingGradeMap, float4(TRANSFORM_TEX(Set_UV0, _ShadingGradeMap), 0.0, _BlurLevelSGM));
//the value of shadowAttenuation is darker than legacy and it cuases noise in terminaters.
#if !defined (UTS_USE_RAYTRACING_SHADOW)
shadowAttenuation *= 2.0f;
shadowAttenuation = saturate(shadowAttenuation);
#endif
//v.2.0.6
//Minmimum value is same as the Minimum Feather's value with the Minimum Step's value as threshold.
//_Tweak_SystemShadowsLevel控制 Unity 的系统阴影级别。默认为 0级别可调整为 ±0.5。
//使用_Tweak_SystemShadowsLevel来偏移ShadingGradeMap中定义的阴影范围
//最后计算ShadowMask:Set_ShadingGrade=_HalfLambert_var*saturate(_SystemShadowsLevel_var)的值。
float _SystemShadowsLevel_var = (shadowAttenuation *0.5)+0.5+_Tweak_SystemShadowsLevel > 0.001 ? (shadowAttenuation *0.5)+0.5+_Tweak_SystemShadowsLevel : 0.0001;
float _ShadingGradeMapLevel_var = _ShadingGradeMap_var.r < 0.95 ? _ShadingGradeMap_var.r+_Tweak_ShadingGradeMapLevel : 1;
float Set_ShadingGrade = saturate(_ShadingGradeMapLevel_var)*lerp( _HalfLambert_var, (_HalfLambert_var*saturate(_SystemShadowsLevel_var)), _Set_SystemShadowsToBase );
//float Set_ShadingGrade = saturate(_ShadingGradeMapLevel_var)*lerp( _HalfLambert_var, (_HalfLambert_var*saturate(1.0+_Tweak_SystemShadowsLevel)), _Set_SystemShadowsToBase );
//计算1、2、3 ShadowMask与插值后的BaseColor值之后合并到一起。
//lerp( (_2nd_ShadeMap_var.rgb*_2nd_ShadeColor.rgb), ((_2nd_ShadeMap_var.rgb*_2nd_ShadeColor.rgb)*Set_LightColor), _Is_LightColor_2nd_Shade )
//lerp(_Is_LightColor_1st_Shade_var,上一次插值结果Set_ShadeShadowMask)
//lerp(_BaseColor_var,上一次插值结果,Set_FinalShadowMask)
//合成方式为:
float Set_FinalShadowMask = saturate((1.0 + ( (Set_ShadingGrade - (_1st_ShadeColor_Step-_1st_ShadeColor_Feather)) * (0.0 - 1.0) ) / (_1st_ShadeColor_Step - (_1st_ShadeColor_Step-_1st_ShadeColor_Feather)))); // Base and 1st Shade Mask
float3 _BaseColor_var = lerp(Set_BaseColor,_Is_LightColor_1st_Shade_var,Set_FinalShadowMask);
//v.2.0.5
float4 _2nd_ShadeMap_var = lerp(SAMPLE_TEXTURE2D(_2nd_ShadeMap,sampler_MainTex, TRANSFORM_TEX(Set_UV0, _2nd_ShadeMap)),_1st_ShadeMap_var,_Use_1stAs2nd);
float Set_ShadeShadowMask = saturate((1.0 + ( (Set_ShadingGrade - (_2nd_ShadeColor_Step-_2nd_ShadeColor_Feather)) * (0.0 - 1.0) ) / (_2nd_ShadeColor_Step - (_2nd_ShadeColor_Step-_2nd_ShadeColor_Feather)))); // 1st and 2nd Shades Mask
//Composition: 3 Basic Colors as Set_FinalBaseColor
float3 Set_FinalBaseColor = lerp(_BaseColor_var,lerp(_Is_LightColor_1st_Shade_var,lerp( (_2nd_ShadeMap_var.rgb*_2nd_ShadeColor.rgb), ((_2nd_ShadeMap_var.rgb*_2nd_ShadeColor.rgb)*Set_LightColor), _Is_LightColor_2nd_Shade ),Set_ShadeShadowMask),Set_FinalShadowMask);
//采样高光Mask
float4 _Set_HighColorMask_var = tex2D(_Set_HighColorMask, TRANSFORM_TEX(Set_UV0, _Set_HighColorMask));
//半影值计算高光Mask(半兰伯特模型)
float _Specular_var = 0.5*dot(halfDirection,lerp( i.normalDir, normalDirection, _Is_NormalMapToHighColor ))+0.5; // Specular
//Step(a,x) => x>=a ? 1 : 0,计算高光Mask
float _TweakHighColorMask_var = (saturate((_Set_HighColorMask_var.g+_Tweak_HighColorMaskLevel))*lerp( (1.0 - step(_Specular_var,(1.0 - pow(abs(_HighColor_Power),5)))), pow(abs(_Specular_var),exp2(lerp(11,1,_HighColor_Power))), _Is_SpecularToHighColor ));
float4 _HighColor_Tex_var = tex2D(_HighColor_Tex, TRANSFORM_TEX(Set_UV0, _HighColor_Tex));
float3 _HighColor_var = (lerp( (_HighColor_Tex_var.rgb*_HighColor.rgb), ((_HighColor_Tex_var.rgb*_HighColor.rgb)*Set_LightColor), _Is_LightColor_HighColor )*_TweakHighColorMask_var);
//Composition: 3 Basic Colors and HighColor as Set_HighColor
float3 Set_HighColor = (lerp(SATURATE_IF_SDR((Set_FinalBaseColor-_TweakHighColorMask_var)), Set_FinalBaseColor, lerp(_Is_BlendAddToHiColor,1.0,_Is_SpecularToHighColor) )+lerp( _HighColor_var, (_HighColor_var*((1.0 - Set_FinalShadowMask)+(Set_FinalShadowMask*_TweakHighColorOnShadow))), _Is_UseTweakHighColorOnShadow ));
//开始计算边缘光
float4 _Set_RimLightMask_var = tex2D(_Set_RimLightMask, TRANSFORM_TEX(Set_UV0, _Set_RimLightMask));
float3 _Is_LightColor_RimLight_var = lerp( _RimLightColor.rgb, (_RimLightColor.rgb*Set_LightColor), _Is_LightColor_RimLight );
float _RimArea_var = abs(1.0 - dot(lerp( i.normalDir, normalDirection, _Is_NormalMapToRimLight ),viewDirection));
float _RimLightPower_var = pow(_RimArea_var,exp2(lerp(3,0,_RimLight_Power)));
float _Rimlight_InsideMask_var = saturate(lerp( (0.0 + ( (_RimLightPower_var - _RimLight_InsideMask) * (1.0 - 0.0) ) / (1.0 - _RimLight_InsideMask)), step(_RimLight_InsideMask,_RimLightPower_var), _RimLight_FeatherOff ));
float _VertHalfLambert_var = 0.5*dot(i.normalDir,lightDirection)+0.5;
float3 _LightDirection_MaskOn_var = lerp( (_Is_LightColor_RimLight_var*_Rimlight_InsideMask_var), (_Is_LightColor_RimLight_var*saturate((_Rimlight_InsideMask_var-((1.0 - _VertHalfLambert_var)+_Tweak_LightDirection_MaskLevel)))), _LightDirection_MaskOn );
float _ApRimLightPower_var = pow(_RimArea_var,exp2(lerp(3,0,_Ap_RimLight_Power)));
float3 Set_RimLight = (SATURATE_IF_SDR((_Set_RimLightMask_var.g+_Tweak_RimLightMaskLevel))*lerp( _LightDirection_MaskOn_var, (_LightDirection_MaskOn_var+(lerp( _Ap_RimLightColor.rgb, (_Ap_RimLightColor.rgb*Set_LightColor), _Is_LightColor_Ap_RimLight )*saturate((lerp( (0.0 + ( (_ApRimLightPower_var - _RimLight_InsideMask) * (1.0 - 0.0) ) / (1.0 - _RimLight_InsideMask)), step(_RimLight_InsideMask,_ApRimLightPower_var), _Ap_RimLight_FeatherOff )-(saturate(_VertHalfLambert_var)+_Tweak_LightDirection_MaskLevel))))), _Add_Antipodean_RimLight ));
//Composition: HighColor and RimLight as _RimLight_var
float3 _RimLight_var = lerp( Set_HighColor, (Set_HighColor+Set_RimLight), _RimLight );
//Matcap
//v.2.0.6 : CameraRolling Stabilizer
//Mirror Script Determination: if sign_Mirror = -1, determine "Inside the mirror".
//v.2.0.7
fixed _sign_Mirror = i.mirrorFlag;
//
float3 _Camera_Right = UNITY_MATRIX_V[0].xyz;
float3 _Camera_Front = UNITY_MATRIX_V[2].xyz;
float3 _Up_Unit = float3(0, 1, 0);
float3 _Right_Axis = cross(_Camera_Front, _Up_Unit);
//Invert if it's "inside the mirror".
if(_sign_Mirror < 0){
_Right_Axis = -1 * _Right_Axis;
_Rotate_MatCapUV = -1 * _Rotate_MatCapUV;
}else{
_Right_Axis = _Right_Axis;
}
float _Camera_Right_Magnitude = sqrt(_Camera_Right.x*_Camera_Right.x + _Camera_Right.y*_Camera_Right.y + _Camera_Right.z*_Camera_Right.z);
float _Right_Axis_Magnitude = sqrt(_Right_Axis.x*_Right_Axis.x + _Right_Axis.y*_Right_Axis.y + _Right_Axis.z*_Right_Axis.z);
float _Camera_Roll_Cos = dot(_Right_Axis, _Camera_Right) / (_Right_Axis_Magnitude * _Camera_Right_Magnitude);
float _Camera_Roll = acos(clamp(_Camera_Roll_Cos, -1, 1));
fixed _Camera_Dir = _Camera_Right.y < 0 ? -1 : 1;
float _Rot_MatCapUV_var_ang = (_Rotate_MatCapUV*3.141592654) - _Camera_Dir*_Camera_Roll*_CameraRolling_Stabilizer;
//v.2.0.7
float2 _Rot_MatCapNmUV_var = RotateUV(Set_UV0, (_Rotate_NormalMapForMatCapUV*3.141592654), float2(0.5, 0.5), 1.0);
//V.2.0.6
float3 _NormalMapForMatCap_var = UnpackNormalScale(tex2D(_NormalMapForMatCap, TRANSFORM_TEX(_Rot_MatCapNmUV_var, _NormalMapForMatCap)), _BumpScaleMatcap);
//v.2.0.5: MatCap with camera skew correction
float3 viewNormal = (mul(UNITY_MATRIX_V, float4(lerp( i.normalDir, mul( _NormalMapForMatCap_var.rgb, tangentTransform ).rgb, _Is_NormalMapForMatCap ),0))).rgb;
float3 NormalBlend_MatcapUV_Detail = viewNormal.rgb * float3(-1,-1,1);
float3 NormalBlend_MatcapUV_Base = (mul( UNITY_MATRIX_V, float4(viewDirection,0) ).rgb*float3(-1,-1,1)) + float3(0,0,1);
float3 noSknewViewNormal = NormalBlend_MatcapUV_Base*dot(NormalBlend_MatcapUV_Base, NormalBlend_MatcapUV_Detail)/NormalBlend_MatcapUV_Base.b - NormalBlend_MatcapUV_Detail;
float2 _ViewNormalAsMatCapUV = (lerp(noSknewViewNormal,viewNormal,_Is_Ortho).rg*0.5)+0.5;
//
//v.2.0.7
float2 _Rot_MatCapUV_var = RotateUV((0.0 + ((_ViewNormalAsMatCapUV - (0.0+_Tweak_MatCapUV)) * (1.0 - 0.0) ) / ((1.0-_Tweak_MatCapUV) - (0.0+_Tweak_MatCapUV))), _Rot_MatCapUV_var_ang, float2(0.5, 0.5), 1.0);
//If it is "inside the mirror", flip the UV left and right.
if(_sign_Mirror < 0){
_Rot_MatCapUV_var.x = 1-_Rot_MatCapUV_var.x;
}else{
_Rot_MatCapUV_var = _Rot_MatCapUV_var;
}
float4 _MatCap_Sampler_var = tex2Dlod(_MatCap_Sampler, float4(TRANSFORM_TEX(_Rot_MatCapUV_var, _MatCap_Sampler), 0.0, _BlurLevelMatcap));
float4 _Set_MatcapMask_var = tex2D(_Set_MatcapMask, TRANSFORM_TEX(Set_UV0, _Set_MatcapMask));
//
//MatcapMask
float _Tweak_MatcapMaskLevel_var = saturate(lerp(_Set_MatcapMask_var.g, (1.0 - _Set_MatcapMask_var.g), _Inverse_MatcapMask) + _Tweak_MatcapMaskLevel);
float3 _Is_LightColor_MatCap_var = lerp( (_MatCap_Sampler_var.rgb*_MatCapColor.rgb), ((_MatCap_Sampler_var.rgb*_MatCapColor.rgb)*Set_LightColor), _Is_LightColor_MatCap );
//v.2.0.6 : ShadowMask on Matcap in Blend mode : multiply
float3 Set_MatCap = lerp( _Is_LightColor_MatCap_var, (_Is_LightColor_MatCap_var*((1.0 - Set_FinalShadowMask)+(Set_FinalShadowMask*_TweakMatCapOnShadow)) + lerp(Set_HighColor*Set_FinalShadowMask*(1.0-_TweakMatCapOnShadow), float3(0.0, 0.0, 0.0), _Is_BlendAddToMatCap)), _Is_UseTweakMatCapOnShadow );
//
//v.2.0.6
//Composition: RimLight and MatCap as finalColor
//Broke down finalColor composition
float3 matCapColorOnAddMode = _RimLight_var+Set_MatCap*_Tweak_MatcapMaskLevel_var;
float _Tweak_MatcapMaskLevel_var_MultiplyMode = _Tweak_MatcapMaskLevel_var * lerp (1, (1 - (Set_FinalShadowMask)*(1 - _TweakMatCapOnShadow)), _Is_UseTweakMatCapOnShadow);
float3 matCapColorOnMultiplyMode = Set_HighColor*(1-_Tweak_MatcapMaskLevel_var_MultiplyMode) + Set_HighColor*Set_MatCap*_Tweak_MatcapMaskLevel_var_MultiplyMode + lerp(float3(0,0,0),Set_RimLight,_RimLight);
float3 matCapColorFinal = lerp(matCapColorOnMultiplyMode, matCapColorOnAddMode, _Is_BlendAddToMatCap);
//v.2.0.4
#ifdef _IS_ANGELRING_OFF
float3 finalColor = lerp(_RimLight_var, matCapColorFinal, _MatCap);// Final Composition before Emissive
//
#elif _IS_ANGELRING_ON
//计算天使环
float3 finalColor = lerp(_RimLight_var, matCapColorFinal, _MatCap);// Final Composition before AR
//v.2.0.7 AR Camera Rolling Stabilizer
//计算UV将表面法线转换到视角坐标将(-1,1)=》(0,1)之后,按照摄像机的 -(_Camera_Dir*_Camera_Roll)旋转坐标后,采样贴图
float3 _AR_OffsetU_var = lerp(mul(UNITY_MATRIX_V, float4(i.normalDir,0)).xyz,float3(0,0,1),_AR_OffsetU);
float2 AR_VN = _AR_OffsetU_var.xy*0.5 + float2(0.5,0.5);
float2 AR_VN_Rotate = RotateUV(AR_VN, -(_Camera_Dir*_Camera_Roll), float2(0.5,0.5), 1.0);
float2 _AR_OffsetV_var = float2(AR_VN_Rotate.x, lerp(i.uv1.y, AR_VN_Rotate.y, _AR_OffsetV));
float4 _AngelRing_Sampler_var = tex2D(_AngelRing_Sampler,TRANSFORM_TEX(_AR_OffsetV_var, _AngelRing_Sampler));
float3 _Is_LightColor_AR_var = lerp( (_AngelRing_Sampler_var.rgb*_AngelRing_Color.rgb), ((_AngelRing_Sampler_var.rgb*_AngelRing_Color.rgb)*Set_LightColor), _Is_LightColor_AR );
float3 Set_AngelRing = _Is_LightColor_AR_var;
float Set_ARtexAlpha = _AngelRing_Sampler_var.a;
float3 Set_AngelRingWithAlpha = (_Is_LightColor_AR_var*_AngelRing_Sampler_var.a);
//Composition: MatCap and AngelRing as finalColor
finalColor = lerp(finalColor, lerp((finalColor + Set_AngelRing), ((finalColor*(1.0 - Set_ARtexAlpha))+Set_AngelRingWithAlpha), _ARSampler_AlphaOn ), _AngelRing );// Final Composition before Emissive
#endif
//v.2.0.7
#ifdef _EMISSIVE_SIMPLE
float4 _Emissive_Tex_var = tex2D(_Emissive_Tex,TRANSFORM_TEX(Set_UV0, _Emissive_Tex));
float emissiveMask = _Emissive_Tex_var.a;
emissive = _Emissive_Tex_var.rgb * _Emissive_Color.rgb * emissiveMask;
#elif _EMISSIVE_ANIMATION
//v.2.0.7 Calculation View Coord UV for Scroll
float3 viewNormal_Emissive = (mul(UNITY_MATRIX_V, float4(i.normalDir,0))).xyz;
float3 NormalBlend_Emissive_Detail = viewNormal_Emissive * float3(-1,-1,1);
float3 NormalBlend_Emissive_Base = (mul( UNITY_MATRIX_V, float4(viewDirection,0)).xyz*float3(-1,-1,1)) + float3(0,0,1);
float3 noSknewViewNormal_Emissive = NormalBlend_Emissive_Base*dot(NormalBlend_Emissive_Base, NormalBlend_Emissive_Detail)/NormalBlend_Emissive_Base.z - NormalBlend_Emissive_Detail;
float2 _ViewNormalAsEmissiveUV = noSknewViewNormal_Emissive.xy*0.5+0.5;
float2 _ViewCoord_UV = RotateUV(_ViewNormalAsEmissiveUV, -(_Camera_Dir*_Camera_Roll), float2(0.5,0.5), 1.0);
//鏡の中ならUV左右反転.
if(_sign_Mirror < 0){
_ViewCoord_UV.x = 1-_ViewCoord_UV.x;
}else{
_ViewCoord_UV = _ViewCoord_UV;
}
float2 emissive_uv = lerp(i.uv0, _ViewCoord_UV, _Is_ViewCoord_Scroll);
//
float4 _time_var = _Time;
float _base_Speed_var = (_time_var.g*_Base_Speed);
float _Is_PingPong_Base_var = lerp(_base_Speed_var, sin(_base_Speed_var), _Is_PingPong_Base );
float2 scrolledUV = emissive_uv + float2(_Scroll_EmissiveU, _Scroll_EmissiveV)*_Is_PingPong_Base_var;
float rotateVelocity = _Rotate_EmissiveUV*3.141592654;
float2 _rotate_EmissiveUV_var = RotateUV(scrolledUV, rotateVelocity, float2(0.5, 0.5), _Is_PingPong_Base_var);
float4 _Emissive_Tex_var = tex2D(_Emissive_Tex,TRANSFORM_TEX(Set_UV0, _Emissive_Tex));
float emissiveMask = _Emissive_Tex_var.a;
_Emissive_Tex_var = tex2D(_Emissive_Tex,TRANSFORM_TEX(_rotate_EmissiveUV_var, _Emissive_Tex));
float _colorShift_Speed_var = 1.0 - cos(_time_var.g*_ColorShift_Speed);
float viewShift_var = smoothstep( 0.0, 1.0, max(0,dot(normalDirection,viewDirection)));
float4 colorShift_Color = lerp(_Emissive_Color, lerp(_Emissive_Color, _ColorShift, _colorShift_Speed_var), _Is_ColorShift);
float4 viewShift_Color = lerp(_ViewShift, colorShift_Color, viewShift_var);
float4 emissive_Color = lerp(colorShift_Color, viewShift_Color, _Is_ViewShift);
emissive = emissive_Color.rgb * _Emissive_Tex_var.rgb * emissiveMask;
#endif
//
//v.2.0.6: GI_Intensity with Intensity Multiplier Filter
float3 envLightColor = envColor.rgb;
float envLightIntensity = 0.299*envLightColor.r + 0.587*envLightColor.g + 0.114*envLightColor.b <1 ? (0.299*envLightColor.r + 0.587*envLightColor.g + 0.114*envLightColor.b) : 1;
float3 pointLightColor = 0;
#ifdef _ADDITIONAL_LIGHTS
int pixelLightCount = GetAdditionalLightsCount();
// determine main light inorder to apply light culling properly
// when the loop counter start from negative value, MAINLIGHT_IS_MAINLIGHT = -1, some compiler doesn't work well.
// for (int iLight = MAINLIGHT_IS_MAINLIGHT; iLight < pixelLightCount ; ++iLight)
for (int loopCounter = 0; loopCounter < pixelLightCount - MAINLIGHT_IS_MAINLIGHT; ++loopCounter)
{
int iLight = loopCounter + MAINLIGHT_IS_MAINLIGHT;
if (iLight != i.mainLightID)
{
float notDirectional = 1.0f; //_WorldSpaceLightPos0.w of the legacy code.
UtsLight additionalLight = GetUrpMainUtsLight(0,0);
if (iLight != MAINLIGHT_IS_MAINLIGHT)
{
additionalLight = GetAdditionalUtsLight(iLight, inputData.positionWS, i.positionCS);
}
half3 additionalLightColor = GetLightColor(additionalLight);
float3 lightDirection = additionalLight.direction;
//v.2.0.5:
float3 addPassLightColor = (0.5*dot(lerp(i.normalDir, normalDirection, _Is_NormalMapToBase), lightDirection) + 0.5) * additionalLightColor.rgb;
float pureIntencity = max(0.001, (0.299*additionalLightColor.r + 0.587*additionalLightColor.g + 0.114*additionalLightColor.b));
float3 lightColor = max(0, lerp(addPassLightColor, lerp(0, min(addPassLightColor, addPassLightColor / pureIntencity), notDirectional), _Is_Filter_LightColor));
float3 halfDirection = normalize(viewDirection + lightDirection); // has to be recalced here.
//v.2.0.5:
_1st_ShadeColor_Step = saturate(_1st_ShadeColor_Step + _StepOffset);
_2nd_ShadeColor_Step = saturate(_2nd_ShadeColor_Step + _StepOffset);
//
//v.2.0.5: If Added lights is directional, set 0 as _LightIntensity
float _LightIntensity = lerp(0, (0.299*additionalLightColor.r + 0.587*additionalLightColor.g + 0.114*additionalLightColor.b), notDirectional);
//v.2.0.5: Filtering the high intensity zone of PointLights
//计算灯光颜色
float3 Set_LightColor = lerp(lightColor, lerp(lightColor, min(lightColor, additionalLightColor.rgb*_1st_ShadeColor_Step), notDirectional), _Is_Filter_HiCutPointLightColor);
//计算BaseColor与采样_1st_ShadeMap_var与_2nd_ShadeMap_varMask。之后计算Set_FinalShadowMask与Set_ShadeShadowMask用于一次灯光循环的插值。
float3 Set_BaseColor = lerp((_BaseColor.rgb*_MainTex_var.rgb*_LightIntensity), ((_BaseColor.rgb*_MainTex_var.rgb)*Set_LightColor), _Is_LightColor_Base);
//v.2.0.5
float4 _1st_ShadeMap_var = lerp(SAMPLE_TEXTURE2D(_1st_ShadeMap, sampler_MainTex,TRANSFORM_TEX(Set_UV0, _1st_ShadeMap)), _MainTex_var, _Use_BaseAs1st);
float3 Set_1st_ShadeColor = lerp((_1st_ShadeColor.rgb*_1st_ShadeMap_var.rgb*_LightIntensity), ((_1st_ShadeColor.rgb*_1st_ShadeMap_var.rgb)*Set_LightColor), _Is_LightColor_1st_Shade);
//v.2.0.5
float4 _2nd_ShadeMap_var = lerp(SAMPLE_TEXTURE2D(_2nd_ShadeMap, sampler_MainTex,TRANSFORM_TEX(Set_UV0, _2nd_ShadeMap)), _1st_ShadeMap_var, _Use_1stAs2nd);
float3 Set_2nd_ShadeColor = lerp((_2nd_ShadeColor.rgb*_2nd_ShadeMap_var.rgb*_LightIntensity), ((_2nd_ShadeColor.rgb*_2nd_ShadeMap_var.rgb)*Set_LightColor), _Is_LightColor_2nd_Shade);
float _HalfLambert_var = 0.5*dot(lerp(i.normalDir, normalDirection, _Is_NormalMapToBase), lightDirection) + 0.5;
// float4 _Set_2nd_ShadePosition_var = tex2D(_Set_2nd_ShadePosition, TRANSFORM_TEX(Set_UV0, _Set_2nd_ShadePosition));
// float4 _Set_1st_ShadePosition_var = tex2D(_Set_1st_ShadePosition, TRANSFORM_TEX(Set_UV0, _Set_1st_ShadePosition));
// //v.2.0.5:
// float Set_FinalShadowMask = saturate((1.0 + ((lerp(_HalfLambert_var, (_HalfLambert_var*saturate(1.0 + _Tweak_SystemShadowsLevel)), _Set_SystemShadowsToBase) - (_1st_ShadeColor_Step - _1st_ShadeColor_Feather)) * ((1.0 - _Set_1st_ShadePosition_var.rgb).r - 1.0)) / (_1st_ShadeColor_Step - (_1st_ShadeColor_Step - _1st_ShadeColor_Feather))));
//SGM
//v.2.0.6
float4 _ShadingGradeMap_var = tex2Dlod(_ShadingGradeMap, float4(TRANSFORM_TEX(Set_UV0, _ShadingGradeMap), 0.0, _BlurLevelSGM));
//v.2.0.6
//Minmimum value is same as the Minimum Feather's value with the Minimum Step's value as threshold.
//float _SystemShadowsLevel_var = (attenuation*0.5)+0.5+_Tweak_SystemShadowsLevel > 0.001 ? (attenuation*0.5)+0.5+_Tweak_SystemShadowsLevel : 0.0001;
float _ShadingGradeMapLevel_var = _ShadingGradeMap_var.r < 0.95 ? _ShadingGradeMap_var.r + _Tweak_ShadingGradeMapLevel : 1;
//float Set_ShadingGrade = saturate(_ShadingGradeMapLevel_var)*lerp( _HalfLambert_var, (_HalfLambert_var*saturate(_SystemShadowsLevel_var)), _Set_SystemShadowsToBase );
float Set_ShadingGrade = saturate(_ShadingGradeMapLevel_var)*lerp(_HalfLambert_var, (_HalfLambert_var*saturate(1.0 + _Tweak_SystemShadowsLevel)), _Set_SystemShadowsToBase);
//
float Set_FinalShadowMask = saturate((1.0 + ((Set_ShadingGrade - (_1st_ShadeColor_Step - _1st_ShadeColor_Feather)) * (0.0 - 1.0)) / (_1st_ShadeColor_Step - (_1st_ShadeColor_Step - _1st_ShadeColor_Feather))));
float Set_ShadeShadowMask = saturate((1.0 + ((Set_ShadingGrade - (_2nd_ShadeColor_Step - _2nd_ShadeColor_Feather)) * (0.0 - 1.0)) / (_2nd_ShadeColor_Step - (_2nd_ShadeColor_Step - _2nd_ShadeColor_Feather)))); // 1st and 2nd Shades Mask
//SGM
// //Composition: 3 Basic Colors as finalColor
// float3 finalColor =
// lerp(
// Set_BaseColor,
// lerp(
// Set_1st_ShadeColor,
// Set_2nd_ShadeColor,
// saturate(
// (1.0 + ((_HalfLambert_var - (_2nd_ShadeColor_Step - _2nd_Shades_Feather)) * ((1.0 - _Set_2nd_ShadePosition_var.rgb).r - 1.0)) / (_2nd_ShadeColor_Step - (_2nd_ShadeColor_Step - _2nd_Shades_Feather))))
// ),
// Set_FinalShadowMask); // Final Color
//Composition: 3 Basic Colors as finalColor
float3 finalColor =
lerp(
Set_BaseColor,
//_BaseColor_var*(Set_LightColor*1.5),
lerp(
Set_1st_ShadeColor,
Set_2nd_ShadeColor,
Set_ShadeShadowMask
),
Set_FinalShadowMask);
//v.2.0.6: Add HighColor if _Is_Filter_HiCutPointLightColor is False
float4 _Set_HighColorMask_var = tex2D(_Set_HighColorMask, TRANSFORM_TEX(Set_UV0, _Set_HighColorMask));
float _Specular_var = 0.5*dot(halfDirection, lerp(i.normalDir, normalDirection, _Is_NormalMapToHighColor)) + 0.5; // Specular
float _TweakHighColorMask_var = (saturate((_Set_HighColorMask_var.g + _Tweak_HighColorMaskLevel))*lerp((1.0 - step(_Specular_var, (1.0 - pow(abs(_HighColor_Power), 5)))), pow(abs(_Specular_var), exp2(lerp(11, 1, _HighColor_Power))), _Is_SpecularToHighColor));
float4 _HighColor_Tex_var = tex2D(_HighColor_Tex, TRANSFORM_TEX(Set_UV0, _HighColor_Tex));
float3 _HighColor_var = (lerp((_HighColor_Tex_var.rgb*_HighColor.rgb), ((_HighColor_Tex_var.rgb*_HighColor.rgb)*Set_LightColor), _Is_LightColor_HighColor)*_TweakHighColorMask_var);
finalColor = finalColor + lerp(lerp(_HighColor_var, (_HighColor_var*((1.0 - Set_FinalShadowMask) + (Set_FinalShadowMask*_TweakHighColorOnShadow))), _Is_UseTweakHighColorOnShadow), float3(0, 0, 0), _Is_Filter_HiCutPointLightColor);
//
finalColor = SATURATE_IF_SDR(finalColor);
pointLightColor += finalColor;
// pointLightColor += lightColor;
}
}
#endif // _ADDITIONAL_LIGHTS
//
//Final Composition
finalColor = SATURATE_IF_SDR(finalColor) + (envLightColor*envLightIntensity*_GI_Intensity*smoothstep(1,0,envLightIntensity/2)) + emissive;
finalColor += pointLightColor;
#endif
//v.2.0.4
#ifdef _IS_TRANSCLIPPING_OFF
fixed4 finalRGBA = fixed4(finalColor,1);
#elif _IS_TRANSCLIPPING_ON
float Set_Opacity = SATURATE_IF_SDR((_Inverse_Clipping_var+_Tweak_transparency));
fixed4 finalRGBA = fixed4(finalColor,Set_Opacity);
#endif
return finalRGBA;
}
```
```c++
else if (stencilMode == _UTS_StencilMode.StencilMask)
{
material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.AlphaTest - 1;
}
else if (stencilMode == _UTS_StencilMode.StencilOut)
{
material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.AlphaTest;
}
void ApplyStencilMode(Material material)
{
_UTS_StencilMode mode = (_UTS_StencilMode)(material.GetInt(ShaderPropStencilMode));
switch (mode)
{
case _UTS_StencilMode.Off:
// material.SetInt(ShaderPropStencilNo,0);
material.SetInt(ShaderPropStencilComp, (int)_StencilCompFunction.Disabled);
material.SetInt(ShaderPropStencilOpPass, (int)_StencilOperation.Keep);
material.SetInt(ShaderPropStencilOpFail, (int)_StencilOperation.Keep);
break;
case _UTS_StencilMode.StencilMask:
// material.SetInt(ShaderPropStencilNo,0);
material.SetInt(ShaderPropStencilComp, (int)_StencilCompFunction.Always);
material.SetInt(ShaderPropStencilOpPass, (int)_StencilOperation.Replace);
material.SetInt(ShaderPropStencilOpFail, (int)_StencilOperation.Replace);
break;
case _UTS_StencilMode.StencilOut:
// material.SetInt(ShaderPropStencilNo,0);
material.SetInt(ShaderPropStencilComp, (int)_StencilCompFunction.NotEqual);
material.SetInt(ShaderPropStencilOpPass, (int)_StencilOperation.Keep);
material.SetInt(ShaderPropStencilOpFail, (int)_StencilOperation.Keep);
break;
}
}
```
![](https://github.com/unity3d-jp/UnityChanToonShaderVer2_Project/raw/release/urp/2.3.0/Documentation~/Images_jpg/URP_image036.jpg)

View File

@@ -0,0 +1,105 @@
## 资料
- [ ] 游戏诞生之日09 - 美术篇 卡通渲染着色器 UTS2 https://zhuanlan.zhihu.com/p/137288013
- [ ] MMD联动Unity学习笔记 Vol.42 UTS2进阶慎入长篇多图预警https://www.bilibili.com/read/cv3347514
- [ ] 官方文档https://github.com/unity3d-jp/UnityChanToonShaderVer2_Project/blob/release/urp/2.3.0/Documentation~/index.md
- [ ] 官方文档——属性解释https://github.com/unity3d-jp/UnityChanToonShaderVer2_Project/blob/release/urp/2.3.0/Documentation~/Props_en.md
## 总览
没有后处理效果
两种着色方式:
DoubleShadeWithFeatherUTS/UniversalToon 的标准工作流程模式。允许 2 种阴影颜色(双阴影颜色)和颜色之间的渐变(羽化)。
ShadingGradeMap更高级的工作流程模式。除了 DoubleShadeWithFeather 功能之外,此着色器还可以保存称为 ShadingGradeMap 的特殊贴图。
- UniversalToonInput.hlsl定义各种变量与资料实现采样AO贴图`SampleOcclusion()`与初始化Lit表面数据`InitializeStandardLitSurfaceData()`
- UniversalToonHead.hlsl定义了一些宏与函数雾、坐标转换、线性空间与GammaSpace转换相关。
## 光照
## Pass
顺序为:
1. Outline
2. ForwardLit
3. ShadowCaster
4. DepthOnly
模板测试语法:
```c#
Stencil
{
Ref[_StencilNo] //设置渲染的模板缓存值0~255
Comp[_StencilComp] //模板测试的通过条件有除了equal还有Greater、Less、Always、Never等类似ZTest。
Pass[_StencilOpPass] //表示通过模板测试和Z测试注意是都通过的像素怎么处置它的模板值。
Fail[_StencilOpFail] //表示通过了模板测试但没通过Z测试的像素怎么处置它的模板值。
}
```
### Outline
```c#
Tags {"LightMode" = "SRPDefaultUnlit"}:使用这个LightMode标签值在渲染物体时绘制一个额外的Pass。也是URP光照模式的默认值。
Cull [_SRPDefaultUnlitColMode]
ColorMask [_SPRDefaultUnlitColorMask]
Blend SrcAlpha OneMinusSrcAlpha
Stencil
{
Ref[_StencilNo]
Comp[_StencilComp]
Pass[_StencilOpPass]
Fail[_StencilOpFail]
}
```
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "UniversalToonHead.hlsl"
#include "UniversalToonOutline.hlsl"
### ForwardLit
```c#
Tags{"LightMode" = "UniversalForward"}:URP前向渲染
ZWrite[_ZWriteMode]
Cull[_CullMode]
Blend SrcAlpha OneMinusSrcAlpha
Stencil {
Ref[_StencilNo]
Comp[_StencilComp]
Pass[_StencilOpPass]
Fail[_StencilOpFail]
}
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitForwardPass.hlsl"
#include "UniversalToonHead.hlsl"
#include "UniversalToonBody.hlsl"
```
### ShadowCaster
渲染阴影贴图
```c#
Name "ShadowCaster"
Tags{"LightMode" = "ShadowCaster"}
ZWrite On
ZTest LEqual
Cull[_CullMode]
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/ShadowCasterPass.hlsl"
```
### DepthOnly
渲染深度缓存
```c#
Tags{"LightMode" = "DepthOnly"}
ZWrite On
ColorMask 0
Cull[_CullMode]
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/DepthOnlyPass.hlsl"
```

View File

@@ -0,0 +1,71 @@
# Unity通用渲染管线URP系列——自定义渲染管线
## 逻辑分布
- CustomRenderPipelineAsset.cs定义渲染管线Asset最后创建并且返回定义的管线实例。
- CustomRenderPipeline.cs整个渲染管线逻辑。考虑到多视图渲染的关系还需要为摄像机类实现对应的逻辑。
- CameraRenderer.cs摄像机对应的渲染逻辑。
## CustomRenderPipeline
重写`void Render(ScriptableRenderContext context,Camera[] cameras)`,使用自定义的`CameraRenderer`类型的变量renderer,调用`renderer.Render(context,camera)`为每个摄像机(视图)进行渲染。
## CameraRenderer
大部分逻辑都集中在这个类中。
重写`void Render(ScriptableRenderContext context,Camera camera)`
裁剪之前会进行
- `PrepareBuffer()`根据摄像机来设置缓存名称
- `PrepareForSceneWindow()`绘制UI准备
1. [裁剪](#Cull)
2. [设置初始化参数](#Setup)
3. [绘制可见多边形](#DrawVisibleGeometry)
4. [绘制Gizmos]()
5. [提交](#Submit)
全代码
```
```
###
#### Command Buffers
自定义的渲染功能需要设置Command Buffers以存储这个功能所有的渲染命令创建新的Command Buffers需要设置名称案例中使用“Render Camera”。
使用`buffer.BeginSample(bufferName)``buffer.EndSample(bufferName)`给Profiler与帧调试器识别到该渲染功能。
执行`Command Buffers`需要在`context`上调用ExecuteCommandBuffer。这会从缓冲区复制命令但并不会清除它如果要重用它的话就必须在之后明确地执行该操作。因为执行和清除总是一起完成的所以添加同时执行这两种方法的方法很方便。
#### 编辑器相关功能
使用`partial`关键字来建立局部类将Editor相关的代码转移到另一个文件中。
### Cull()
尝试通过Camera获取剪裁变量之后存储裁剪后的结果。
### lighting.Setup(context,cullingResults)
使用`CullingResults`向Shader传递`DirectionalLight`信息,支持多个方向光。
### Setup()
设置初始变量以及做一些初始化操作:
1. `SetupCameraProperties`设置摄像相关属性比如投影矩阵
2. `buffer.ClearRenderTarget`清屏操作。根据清屏标志进行对应的处理。为Depth只会清空深度缓存颜色的话会清空深度与颜色缓存如果项目设置为线性颜色则使用线性颜色进行清屏。
### DrawVisibleGeometry()
根据`SortingSettings``DrawingSettings``FilteringSettings`,之后调用
` context.DrawRenderers(cullingResults,ref drawingSettings,ref filteringSettings)`绘制模型。
`DrawinSettings`的构造函数中,`SortingSettings`起到确定基于正焦还是基于透视的应用排序,还可以用来设置绘制顺序,`criteria = SortingCriteria.CommonOpaque`设置绘制顺序为从近到远。
绘制还需要需要对应着色器的Id案例中使用`static ShaderTagId unlitShaderTagId = new ShaderTagId("SRPDefaultUnlit")`获取。
`FilteringSettings`用于确定哪一些渲染队列是被允许渲染。
以及`context.DrawSkybox(camera)`来绘制天空盒。
#### 绘制顺序
### lighting.Clearup()
渲染完阴影后,清理阴影图集。
### Submit()
提交给渲染队列。

View File

@@ -0,0 +1,71 @@
## BRDF
案例中使用了迪士尼模型。
## 混合模式
`SrcBlend``One``DetBlend``OneMinusSrc`
## Shader GUI
`Shader`{}内添加`CustomEditor“ CustomShaderGUI”`。之后添加脚本:
```c#
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
public class CustomShaderGUI : ShaderGUI {
MaterialEditor editor;
Object[] materials;
MaterialProperty[] properties;
public override void OnGUI (
MaterialEditor materialEditor, MaterialProperty[] properties
) {
base.OnGUI(materialEditor, properties);
editor = materialEditor;
materials = materialEditor.targets;
this.properties = properties;
}
}
```
之后实现
```c#
bool HasProperty (string name) =>
FindProperty(name, properties, false) != null;
void SetProperty (string name, string keyword, bool value) {
if (SetProperty(name, value ? 1f : 0f)) {
SetKeyword(keyword, value);
}
}
bool SetProperty (string name, float value) {
MaterialProperty property = FindProperty(name, properties, false);
if (property != null) {
property.floatValue = value;
return true;
}
return false;
}
void SetKeyword (string keyword, bool enabled) {
if (enabled) {
foreach (Material m in materials) {
m.EnableKeyword(keyword);
}
}
else {
foreach (Material m in materials) {
m.DisableKeyword(keyword);
}
}
}
```
就可以添加若干自定义Shader属性的`Set`函数了。添加按钮逻辑如下:
```c#
bool PresetButton (string name) {
if (GUILayout.Button(name)) {
editor.RegisterPropertyChangeUndo(name);
return true;
}
return false;
}
```

View File

@@ -0,0 +1,185 @@
## 建立自己的管线Shader以及ShaderLibrary
## Shader宏分支控制
### multi_complie
常用的两种做法:
1. 使用multi_complie或shader_feature来定义宏根据不同的宏指令编译出多套ShaderUnity内建shader大体也是这么做的。
2. 有外部传入参数在shader内部if判断选择执行哪部分运算。
因为在shader种使用if、for很影响效率所以第二种方法使用较少用于case较少的时候。
这两个宏一般与`Shader.EnableKeyword("宏名");``Shader.DisableKeyword("宏名");`一起使用。
它会无脑的进行组合编译如果宏指令太多会产生非常多的variant。
`#pragma multi_compile Red Green Blue`
会产生三个variant因为你定义了三个宏
```c#
#pragma multi_compile Red Green Blue
#pragma multi_compile Pink Yellow
```
会产生6个variantRedPinkRedYellowGreenPinkGreenYellowBluePinkBlueYellow),因为他们之间会两两组合。
### shader_feature
该指令的效果和用法基本都与`multi_complie`一样,都是用来添加宏。同时它就是为了multi_compile打包时的爆炸编译的问题。
但如果是需要同时存在两种宏分支的功能就不适合用`shader_feature`了。
## URP ShaderLibrary
### render-pipelines.core
`Packages/com.unity.render-pipelines.core/ShaderLibrary/SpaceTransforms.hlsl`中定义了若干空间转换函数,但因为没有定义宏,所以还需要在用之前定义缺少宏,相关矩阵变量使用`UnityInput.hlsl`进行定义。下面的宏声明可能会有bug最好手动复制错误信息中的变量。估计是字符集的问题
`Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl`包含了若干基础类型定义目前只用于定义real类型。
`Packages/com.unity.render-pipelines.core/ShaderLibrary/UnityInstancing.hlsl`为了实现GPUInstancing所需的库。
`Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonMaterial.hlsl`实现了迪士尼BRDF模型函数。比如`PerceptualSmoothnessToPerceptualRoughness`、`PerceptualRoughnessToRoughness`
```c#
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
#include "UnityInput.hlsl"
#define UNITY_MATRIX_M untiy_ObjectToWorld
#define UNITY_MATRIX_I_M untiy_WorldToObject
#define UNITY_MATRIX_V untiy_MatrixV
#define UNITY_MATRIX_VP unity_MatrixVP
#define UNITY_MATRIX_P glstate_matrix_projection
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/SpaceTransforms.hlsl"
```
### render-pipelines.universal 7.3.1
Unity-Chan使用URP7.3.1
#### Core.hlsl
`Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl`
- 定义了顶点输入、顶点法线输入输入以及初始化函数。
- 定义`UNITY_Z_0_FAR_FROM_CLIPSPACE`宏。
- 返回`UnityInput.hlsl`中定义的`_WorldSpaceCameraPos`,以及`_ScaledScreenParams`。
- 一些常用函数
#### Lighting
`Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl`
实现了灯光相关逻辑(包括阴影)
- 灯光衰减函数
- 灯光数据结构体以及结构体数据填充与计算函数
- BRDF Functions
- Global Illumination主要是球谐结果
- 光照计算LightingLambert、LightingSpecular、LightingPhysicallyBased、VertexLighting
- Fragment FunctionsUniversalFragmentPBR、UniversalFragmentBlinnPhong、LightweightFragmentPBR
#### LitInput
`Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl`
- 定义了PBR所需的`CBUFFER`
- `SampleMetallicSpecGloss()`、`SampleOcclusion()`、`InitializeStandardLitSurfaceData()`
#### LitForwardPass
`Packages/com.unity.render-pipelines.universal/Shaders/LitForwardPass.hlsl`
URP前向管线具体实现文件。像素着色器最终调用`UniversalFragmentPBR()`来计算最后颜色。
## 减少Draw Call的方法
- SRP批处理器:批处理是组合Drawcall的过程可减少CPU和GPU之间的通信时间。最简单的方法是启用SRP batcher。SRP batcher不会减少Draw Call的数量而是使其更精简。它在GPU上缓存了材质属性因此不必在每次绘制调用时都将其发送出去。
- GPU Instancing:使用GPU Instancing可使用少量绘制调用一次绘制或渲染同一网格的多个副本。
### SRP批处理器
遇到错误`SRP Batcher Material property is found in another cbuffer`这个是因为const buffer的名称不正确造成的。
官方文档有如下一句话:
>For a Shader to be compatible with SRP:
All built-in engine properties must be declared in a single CBUFFER named “UnityPerDraw”. For example, unity_ObjectToWorld, or unity_SHAr.
All Material properties must be declared in a single CBUFFER named “UnityPerMaterial”.
翻译成白话来说Shader中所有的内置属性例如unity_ObjectToWorldunity_SHAr等都要在一个名为UnityPerDraw的CBUFFER中声明而所有的Material属性都要在一个名为UnityPerMaterial的CBUFFER中声明。
```c#
cbuffer UntiyPreMaterial
{
float4 _BaseColor;
}
CBUFFER_START(UntiyPreMaterial)
float4 _BaseColor;
CBUFFER_END
```
之后在管线的构造函数中添加设置:
```c#
public CustomRenderPipeline()
{
GraphicsSettings.useScriptableRenderPipelineBatching = true;
}
```
### GPU Instancing
大致步骤:
1. 在Shader文件中添加`#pragma multi_compile_instancing`,这将使Unity生成我们的着色器的两个变体一个具有GPU实例化支持一个不具有GPU实例化支持。材质检查器中还出现了一个切换选项使我们可以选择每种材质要使用的版本。
2. 支持GPU实例化需要更改方法还需要包含`UnityInstancing.hlsl`,作用是重新定义这些宏来访问实例数据数组。但是要进行这项工作需要知道当前正在渲染的对象的索引。索引是通过顶点数据提供的因此需要使其可用。UnityInstancing.hlsl定义了宏来简化此过程但是它假定顶点函数具有struct参数。
3. 声明一个用于传递定点数据的结构体使用GPU实例化时对象索引也可用作顶点属性。我们可以在适当的时候通过简单地将`UNITY_VERTEX_INPUT_INSTANCE_ID`放在属性中来添加它。
4. 在`VertexShader`中添加添加`UNITY_SETUP_INSTANCE_ID(input)`来提取实例的顶点索引数据这足以使GPU实例化进行工作了。
5. 因为SRP批处理程序拥有优先权所以还需要使用`UNITY_INSTANCING_BUFFER_START`替换`CBUFFER_START`以及用`UNITY_INSTANCING_BUFFER_END`替换`CBUFFER_END`,再将内部属性使用`UNITY_DEFINE_INSTANCED_PROP`进行包裹。
6. 为了将顶点索引实例传递至`PixelShader`,还需要再创建一个结构体并添加UNITY_VERTEX_INPUT_INSTANCE_ID。
7. 最后在`PixelShader`中添加`UNITY_SETUP_INSTANCE_ID(input)`来访问实例。使用`UNITY_ACCESS_INSTANCED_PROP`来访问材质中的属性数据。
```cg
UNITY_INSTANCING_BUFFER_START(UnityPerMaterial)
UNITY_DEFINE_INSTANCED_PROP(float4,_BaseColor)
UNITY_INSTANCING_BUFFER_END(UnityPerMaterial)
struct Attributes
{
float3 positionOS : POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct Varyings
{
float4 positionCS: SV_POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
Varyings UnlitPassVertex(Attributes input)
{
Varyings output;
UNITY_SETUP_INSTANCE_ID(input);
UNITY_TRANSFER_INSTANCE_ID(input,output);
float3 positionWS = TransformObjectToWorld(input.positionOS);
output.positionCS=TransformWorldToHClip(positionWS);
return output;
}
float4 UnlitPassFragment(Varyings input) : SV_TARGET
{
UNITY_SETUP_INSTANCE_ID(input);
return UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial,_BaseColor);
}
```
### 动态合批
减少DC的第三种方法称为动态批处理。这是一种古老的技术它将共享相同材质的多个小网格合并为一个较大的网格而该网格被绘制。但如果使用逐对象材质属性per-object material properties会失效。 较大的网格一般按需生成,所以动态合批仅适用于较小的网格。球体还是太大了,但立方体可以使用。
一般来说GPU实例化优于动态批处理。该方法也有一些注意事项例如当涉及不同的比例时不能保证较大网格的法线向量为单位长度。此外绘制顺序也将更改因为它现在是单个网格而不是多个。 还有静态批处理它的工作原理类似但是会提前标记为静态批处理的对象。除了需要更多的内存和存储空间之外它没有任何注意事项。RP不关心这个因此使用起来不用过多担心。
大致步骤:
1. 在`CameraRenderer.DrawVisibleGeometry`中将`enableDynamicBatching`与`enableInstancing`设置为true。
2. 在`CustomRenderPipeline`将`GraphicsSettings.useScriptableRenderPipelineBatching = useSPRBatcher`。
### 给RP添加变量控制
大致步骤:
- `CameraRenderer`中给`DrawVisibleGeometry`与`Render`添加`useDynamicBatching`与`useGPUInstancing`形参,这两变量将用来设置`DrawVisibleGeometry`中的`enableuseDynamicBatching`与`enableInstancing`。
- `CustomRenderPipeline`中添加`useDynamicBatching`与`useGPUInstancing`变量,并且给构造函数添加`useSRPBatcher`、`useDynamicBatching`与`useGPUInstancing`形参,用于修改上述变量以及设置是否启用`SRPBatcher`,并且给`Render`中渲染函数添加上述形参。
- `CustomRenderPipelineAsset`添加`useSRPBatcher`、`useDynamicBatching`与`useGPUInstancing`变量,并修改对应函数的形参。
## 在Shader中添加渲染设置
在`Properties`中
```cg
[Enum(UnityEngine.Rendering.BlendedMode)]
_SrcBlend("Src Blend",Float)=1
[Enum(UnityEngine.Rendering.BlendedMode)]
_DstBlend("Src Blend",Float)=0
[Enum(Off,0,On,1)] _ZWrite ("Z Write",Float)=1
```
在`Pass`中添加
```cg
Blend [_SrcBlend] [_DstBlend]
ZWrite [_ZWrite]
```
## 添加纹理
1. 在`Properties`中添加` _BaseMap("Texture",2D)="white" {}`
2. 因为要支持GPUInstancing的关系代码比较复杂后续步骤见git。

View File

@@ -0,0 +1,103 @@
## Post-FX Stack
为了效率跳过。
`void Draw(RenderTargetIdentifier from,RenderTargetIdentifier to,Pass pass)`
![](https://pic2.zhimg.com/80/v2-9e0c020362ba48d9652de9507acd876d_720w.jpg)
to代表绘制的RT id通过`buffer.SetRenderTarget()`来设置。from为原始渲染结果使用`buffer.SetGlobalTexture()`来向Shader传递贴图资源。
## Bloom
对图像进行双线性采样以生成分辨率不断对半分的Bloom金字塔。
![](https://pic1.zhimg.com/80/v2-263834811ad5f64ae6674ebaae360bc8_720w.jpg)
`_BloomPyramid1`~`_BloomPyramid16`传递id。
![](https://pic1.zhimg.com/80/v2-a2dfea38523980ad31c5e656a181735c_720w.jpg)
创建一个DoBloom方法。首先将摄像机的像素宽度和高度减半然后选择默认的渲染纹理格式。最初我们将从源复制到金字塔中的第一个纹理。追踪那些标识符。
![](https://pic4.zhimg.com/80/v2-8946f3551e373b2a5cc399a360c1e34b_720w.jpg)
## OnBloom
循环整个级别的贴图,并调用`Draw()`绘制之后计算后一级别的贴图的数据准备下一次循环直到循环完成或者贴图分辨为1*1时候。
### Draw
```c#
void Draw (
RenderTargetIdentifier from, RenderTargetIdentifier to, Pass pass
) {
buffer.SetGlobalTexture(fxSourceId, from);
buffer.SetRenderTarget(
to, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store
);
buffer.DrawProcedural(
Matrix4x4.identity, settings.Material, (int)pass,
MeshTopology.Triangles, 3
);
}
```
##
```c#
Pass {
BloomCombine,
BloomHorizontal,
BloomPrefilter,
BloomVertical,
Copy
}
```
Bloom循环的主要内逻辑为
1. copy之前的渲染结果。
2. 进行一次预处理。
3. 取得2个RT首先进行水平高斯模糊之后进行垂直高斯模糊。因为水平模糊已经进行一次采样所以垂直采样的次数可以减半了。
4. 释放中间产生水平高斯的RT。
5. 进行反向叠加产生结果循环。(垂直高斯模糊后的结果)
6. 释放垂直高斯的RT。
## 预处理
Bloom通常在艺术上用于仅使某些东西发光但是我们的效果目前适用于所有对象不管它有多亮。尽管从物理上讲没有意义但是我们可以通过引入亮度阈值来限制影响效果的因素。实际上就是提取亮度高的区域。
c# DoBloom():
```c#
Vector4 threshold;
threshold.x = Mathf.GammaToLinearSpace(bloom.threshold);
threshold.y = threshold.x * bloom.thresholdKnee;
threshold.z = 2f * threshold.y;
threshold.w = 0.25f / (threshold.y + 0.00001f);
threshold.y -= threshold.x;
buffer.SetGlobalVector(bloomThresholdId, threshold);
```
Shader:
```c#
float3 ApplyBloomThreshold (float3 color) {
float brightness = Max3(color.r, color.g, color.b);
float soft = brightness + _BloomThreshold.y;
soft = clamp(soft, 0.0, _BloomThreshold.z);
soft = soft * soft * _BloomThreshold.w;
float contribution = max(soft, brightness - _BloomThreshold.x);
contribution /= max(brightness, 0.00001);
return color * contribution;
}
float4 BloomPrefilterPassFragment (Varyings input) : SV_TARGET {
float3 color = ApplyBloomThreshold(GetSource(input.fxUV).rgb);
return float4(color, 1.0);
}
```
## 解决白色辉光显得块状化问题
使用在Core RP Library的Filtering include文件中定义的SampleTexture2DBicubic函数。
```c#
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Filtering.hlsl"
float4 GetSourceBicubic (float2 fxUV) {
return SampleTexture2DBicubic(
TEXTURE2D_ARGS(_PostFXSource, sampler_linear_clamp), fxUV,
_PostFXSource_TexelSize.zwxy, 1.0, 0.0
);
}
```
之后再合并结果的时候对低分辨的贴图使用该函数进行采样。在案例中被设置为可开启项。
## 强制控制
在`BloomCombinePassFragment`中的给低分辨率结果乘以强度之后在于高分辨率结果叠加。

View File

@@ -0,0 +1,14 @@
## HDR渲染纹理
HDR渲染仅与后处理结合使用才有意义因为我们无法更改最终的帧缓冲区格式。因此当我们在CameraRenderer.Setup中创建自己的中间帧缓冲区时我们将在适当的时候使用默认的HDR格式`buffer.GetTemporarayRT()`中使用`RenderTextureFormat.DefaultHDR`(后处理效果的RT格式),也就是R16G16B16A16_SFloat而不是LDR的常规默认格式。
逐步执行DrawCall时你会注意到场景看起来比最终结果要暗。发生这种情况是因为这些步骤存储在HDR纹理中。由于线性颜色数据按原样显示因此看起来很暗它错误地解释为sRGB。
![](https://pic1.zhimg.com/80/v2-a5c936841f88bc9a44cd4af65cb940c0_720w.jpg)
![](https://pic2.zhimg.com/80/v2-d528a8301bc04fa50aa97d7692b4fc59_720w.jpg)
为什么亮度会变化?
sRGB格式使用非线性传递函数。显示器会为此调整执行所谓的伽马校正。伽玛调节函数通常用c的2.2次方和c原色近似但实际传递函数略有不同。
## 解决因为高亮区域过小而导致的闪烁问题
<video src="https://vdn1.vzuu.com/SD/d8b32ae4-48e8-11eb-9c34-7640c864ad74.mp4?disable_local_cache=1&auth_key=1634485320-0-0-203192af877eba6f1b1ae62e0e6d165b&f=mp4&bu=pico&expiration=1634485320&v=hw"
</video>

View File

@@ -0,0 +1,323 @@
## 级联阴影
阴影贴图的缺点:阴影边缘的锯齿严重。原因是阴影贴图的分辨率低,在对阴影贴图采样时,多个不同的顶点对同一个像素采样,导致生成锯齿。为了解决这种问题,我们使用多张阴影贴图,离相机近的地方使用精细的阴影贴图,离相机远的地方使用粗糙的阴影贴图,这样不仅优化了阴影效果,还保证了渲染效率因此,级联阴影的关键就是生成和使用不同精细度的阴影贴图
阴影贴图的原理:在灯光方向架一台摄像机获取深度图然后再正常渲染自己的场景再在正常渲染场景的时候把fragment转换到光源空间把它和之前渲染的Shadowmap中高度深度作比较看它是否在影子里如果是就返回0不是就返回1
主要的步骤是取得场景中灯光的设置并且传递到Shader中之后在`RenderDirectionalShadows`中取得`GetTemporaryRT`在设置完绘制属性后将渲染结果传递至RT而不是摄像机上通过`RenderDirectionalShadows`绘制各个方向光的阴影。
`RenderDirectionalShadows`主要是通过`cullingResults.ComputeDirectionalShadowMatricesAndCullingPrimitives`来计算视图矩阵、投影矩阵与ShadowSplitData结构该函数第一个参数是可见光指数。接下来的三个参数是两个整数和一个Vector3它们控制阴影级联。稍后我们将处理级联因此现在使用零一和零向量。然后是纹理尺寸我们需要使用平铺尺寸。第六个参数是靠近平面的阴影我们现在将其忽略并将其设置为零。之后操作为
```c#
shadowSettings.splitData = splitData;
//设置矩阵
buffer.SetViewProjectionMatrices(viewMatrix,projectionMatrix);
ExecuteBuffer();
//根据ShadowDrawingSettings绘制阴影
context.DrawShadows(ref shadowSettings);
```
之后开始往`Lit.Shader`添加绘制阴影Pass
```c#
Pass{
Tags {
"LightMode" = "ShadowCaster"
}
ColorMask 0
HLSLPROGRAM
#pragma target 3.5
#pragma shader_feature _CLIPPING
#pragma multi_compile_instancing
#pragma vertex ShadowCasterPassVertex
#pragma fragment ShadowCasterPassFragment
#include "ShadowCasterPass.hlsl"
ENDHLSL
}
```
其中FragmentShader只负责裁剪
```c#
#ifndef CUSTOM_SHADOW_CASTER_PASS_INCLUDED
#define CUSTOM_SHADOW_CASTER_PASS_INCLUDED
#include "../ShaderLibrary/Common.hlsl"
TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);
UNITY_INSTANCING_BUFFER_START(UnityPerMaterial)
UNITY_DEFINE_INSTANCED_PROP(float4, _BaseMap_ST)
UNITY_DEFINE_INSTANCED_PROP(float4, _BaseColor)
UNITY_DEFINE_INSTANCED_PROP(float, _Cutoff)
UNITY_INSTANCING_BUFFER_END(UnityPerMaterial)
struct Attributes {
float3 positionOS : POSITION;
float2 baseUV : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct Varyings {
float4 positionCS : SV_POSITION;
float2 baseUV : VAR_BASE_UV;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
Varyings ShadowCasterPassVertex (Attributes input) {
Varyings output;
UNITY_SETUP_INSTANCE_ID(input);
UNITY_TRANSFER_INSTANCE_ID(input, output);
float3 positionWS = TransformObjectToWorld(input.positionOS);
output.positionCS = TransformWorldToHClip(positionWS);
float4 baseST = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _BaseMap_ST);
output.baseUV = input.baseUV * baseST.xy + baseST.zw;
return output;
}
void ShadowCasterPassFragment (Varyings input) {
UNITY_SETUP_INSTANCE_ID(input);
float4 baseMap = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.baseUV);
float4 baseColor = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _BaseColor);
float4 base = baseMap * baseColor;
#if defined(_CLIPPING)
clip(base.a - UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _Cutoff));
#endif
}
#endif
```
`clip()`是HLSL内置函数当传入数值小于0时丢弃当前像素。`_Cutoff`是一个设定的浮点值默认为0。不是0就是1。在`ShadowCaster`Pass中是为了正确渲染Alpha物体阴影。
`ShadowCaster`Pass是用来渲染阴影贴图灯光空间的深度贴图如果相机空间物体表面深度大于阴影贴图中深度则代表物体处于阴影。取得阴影值在`GetDirectionalShadowAttenuation()=>FilterDirectionalShadow()`里面,采样完阴影后`GetDirectionalShadowAttenuation()`里进行一次插值,`return lerp(1.0,shadow,directional.strength);`,最后在`GetLighting()`取得计算完的阴影值。
在`FilterDirectionalShadow()`中调用了`SAMPLE_TEXTURE2D_SHADOW`宏。`SAMPLE_TEXTURE2D_SHADOW`宏本质是`SampleCmpLevelZero()`函数会对指定的纹理坐标进行采样将采样的结果与传入的参数z当前texel在光源空间的深度进行比较小于等于z视为通过说明此texel没被遮挡否则视为不通过说明此texel位于阴影中
另外注意`SamplerComparisonState`是用来进行深度采样比较的采样器。需要与`SampleCmpLevelZero`一起使用。
### 添加级联效果
由于定向光会影响最大阴影距离范围内的所有物体,因此它们的阴影贴图最终会覆盖较大的区域。由于阴影贴图使用正交投影,因此阴影贴图中的每个纹理像素都具有固定的世界空间大小。如果该尺寸太大,则清晰可见单个阴影纹理,从而导致锯齿状的阴影边缘和小的阴影可能消失。可以通过增加图集大小来缓解这种情况,但仅限于一定程度。
### 添加设置
#### ShadowSettings类
添加`ShadowSettings`类:
```c#
using UnityEngine;
[System.Serializable]
public class ShadowSettings
{
[Min(0f)]
public float maxDistance = 100f;
public enum TextureSize
{
_256=256,_512=512,_1024=1024,_2048=2048,_4096=4096,_8192=8192
}
[System.Serializable]
public struct Directional
{
public TextureSize atlasSize;
}
public Directional directional = new Directional { atlasSize = TextureSize._1024};
}
```
往`CustomRenderPipelineAsset`、`CustomRenderPipeline`、`CameraRenderer` 、`Lighting`依次添加`ShadowSettings`变量以及对应函数中的形参。
#### Shadow类
```c#
public class Shadows
{
const int maxShadowedDirectionalLightCount = 1;
int ShadowedDirectionalLightCount;
const string bufferName = "shadows";
CommandBuffer buffer = new CommandBuffer
{
name = bufferName
};
ScriptableRenderContext context;
CullingResults cullingResults;
ShadowSettings settings;
struct ShadowedDirectionalLight
{
public int visibleLightIndex;
}
private ShadowedDirectionalLight[] ShadowedDirectionalLights =
new ShadowedDirectionalLight[maxShadowedDirectionalLightCount];
public void Setup(
ScriptableRenderContext context,CullingResults cullingResults,
ShadowSettings settings
)
{
ShadowedDirectionalLightCount = 0;
this.context = context;
this.cullingResults = cullingResults;
this.settings = settings;
}
void ExecuteBuffer()
{
context.ExecuteCommandBuffer(buffer);
buffer.Clear();
}
/*
* 存储方向光阴影信息
* 灯光处于阴影有效且处于可见状态时将信息存储在ShadowedDirectionalLights[]中
*/
public void ReserveDirectionalShadows(Light light, int visibleLightIndex)
{
if (ShadowedDirectionalLightCount < maxShadowedDirectionalLightCount &&
light.shadows!=LightShadows.None && light.shadowStrength>0f &&
cullingResults.GetShadowCasterBounds(visibleLightIndex,out Bounds b))
{
ShadowedDirectionalLights[ShadowedDirectionalLightCount++] = new ShadowedDirectionalLight() { visibleLightIndex = visibleLightIndex};
}
}
}
```
- 之后在`Lighting`类中添加`Shadow`类变量shadow并且在`Setup`中添加`shadows.Setup(context,cullingResults,shadowSettings);`。以及在`SetupDirectionalLight`中添加`shadows.ReserveDirectionalShadows(visibleLight.light,index);`。
### 渲染
#### 阴影图集
```c#
TEXTURE2D_SHADOW(_DirectionalShadowAtlas);
#define SHADOW_SAMPLER sampler_linear_clamp_compare
SAMPLER_CMP(SHADOW_SAMPLER);
```
#### 3 级联阴影贴图
现在终于得到阴影,但它们看起来很糟糕。不应被阴影化的表面最终会被形成像素化带的阴影伪影所覆盖。这些是由于阴影贴图的有限分辨率导致的自我阴影化。使用不同的分辨率会更改伪影模式,但不会消除它们。
![](https://pic1.zhimg.com/80/v2-7b1bc2d23fcaaf10112496200536f770_720w.jpg)
#### 球形剔除
Unity通过为其创建一个选择球来确定每个级联覆盖的区域。由于阴影投影是正交的且呈正方形因此它们最终会紧密契合其剔除球但还会覆盖周围的一些空间。这就是为什么可以在剔除区域之外看到一些阴影的原因。同样光的方向与球无关因此所有定向光最终都使用相同的剔除球。
剔除球是ComputeDirectionalShadowMatricesAndCullingPrimitives函数中计算出的`ShadowSplitData`分离出的数据。传递`_CascadeCount`级联数以及`_CascadeCullingSpheres`以及剔除球的位置数据。
创建`ShadowData`结构体以传递级联级别以及阴影硬度。将会在`GetLighting`中通过像素的世界坐标是否在球体内来计算`ShadowData`结构体中的级联级别。
#### 最大距离
此时阴影会在超过最后一个剔除球后消失,为了解决这个问题,会设置一个最大距离,超过最大距离阴影才会消失。具体操作是在`GetShadowData`中比较像素深度以及阴影最大距离值,如果超过则将`strength`设置为0。
#### 给阴影添加衰减与级联渐变
阴影衰减见git
![](https://pic3.zhimg.com/80/v2-3d3ad5abdfc204e2b640ffab7329554a_720w.jpg)
级联衰减公式:
![](https://pic2.zhimg.com/80/v2-01588a58eec8923422b5ea114f2000e5_720w.jpg)
其中f为下面式子的倒数
![](https://pic2.zhimg.com/80/v2-5739eecbfd934de957734ad3d9913ad9_720w.jpg)
在`RenderDirectionalShadows`中计算fade因子后传递给`_ShadowDistanceFade`
#### 清除阴影的摩尔纹
##### 简单的清除方法
1. 最简单的方法是向阴影投射器的深度添加恒定的偏差,这虽然会产生不精确的阴影但可以消除摩尔纹。
2. 另一种方法是应用斜率比例偏差方法是对SetGlobalDepthBias的第二个参数使用非零值。
##### 法线偏差
新建级联数据变量用来传递级级联数据x为剔除球半径倒数y为使用`√2*2f*cullingSphere.w/tileSize`算出来的级联纹理大小,因为最坏的情况是以像素对角方向进行偏移,所以前面有乘以`√2`,存入shader后乘以Normal取得偏移值之后对`surfaceWS`进行偏移。
##### 可配置的偏差
从剔除数据中获取Light以及其shadowBias之后传递给`ShadowedDirectionalLight`。在绘制阴影前将偏移值传递给`buffer.SetGlobalDepthBias(0,light.slopeScaleBiase);`从剔除数据中获取Light以及其normalBias传递到Shader中乘以上一步中的`normalBias`。
##### 解决阴影裁剪问题
当摄像机处于物体中间时,会出现阴影被裁剪的问题。解决方法是在顶点着色器中添加:
```c#
#if UNITY_REVERSED_Z
output.positionCS.z=min(output.positionCS.z,output.positionCS.w*UNITY_NEAR_CLIP_VALUE);
#else
output.positionCS.z=max(output.positionCS.z,output.positionCS.w*UNITY_NEAR_CLIP_VALUE);
#endif
```
原理是当物体z坐标小于近剪裁面时将顶点挤压or贴在近剪裁面上。
对于大三角产生问题的原因不明白。解决方法是取得灯光的`ShadowNearPlane`之后传递给计算剔除球形参中。
##### PCF过滤
到目前为止我们仅对每个片段采样一次阴影贴图且使用了硬阴影。阴影比较采样器使用特殊形式的双线性插值在插值之前执行深度比较。这被称为百分比紧密过滤percentage closer filtering 简称PCF因为其中包含四个纹理像素所以一般指是2×2 PCF过滤器。
- 添加2x2、3x3、5x5,3种过滤枚举、传递shadowAtlasSize到Shader以及对应的Shader宏并且修改`SetKeywords()`。
- 为每种过滤器设置不同的采样次数与宏设置
`DIRECTIONAL_FILTER_SETUP`为SampleShadow_ComputeSamples_Tent_xxx通过Size与positionSTS.xy计算权重以及uv。之后对阴影进行对应次数的采样。
```c#
float FilterDirectionalShadow (float3 positionSTS)
{
#if defined(DIRECTIONAL_FILTER_SETUP)
float weights[DIRECTIONAL_FILTER_SAMPLES];
float2 positions[DIRECTIONAL_FILTER_SAMPLES];
float4 size = _ShadowAtlasSize.yyxx;
DIRECTIONAL_FILTER_SETUP(size, positionSTS.xy, weights, positions);
float shadow = 0;
for (int i = 0; i < DIRECTIONAL_FILTER_SAMPLES; i++) {
shadow += weights[i] * SampleDirectionalShadowAtlas(
float3(positions[i].xy, positionSTS.z)
);
}
return shadow;
#else
return SampleDirectionalShadowAtlas(positionSTS);
#endif
}
```
在`Shadows.cs`中修改`SetCascadeData()`。增大滤镜大小可使阴影更平滑但也会导致粉刺再次出现。我们需要增加法向偏置以匹配滤波器尺寸。可以通过将纹理像素大小乘以1加上SetCascadeData中的过滤器模式来自动执行此操作。
```c#
void SetCascadeData(int index, Vector4 cullingSphere, float tileSize)
{
float texelSize = 2f * cullingSphere.w / tileSize;
float filterSize = texelSize * ((float)settings.directional.filter + 1f);
cullingSphere.w -= filterSize;
cullingSphere.w *= cullingSphere.w;
cascadeCullingSpheres[index] = cullingSphere;
cascadeData[index] = new Vector4(
1f / cullingSphere.w,
filterSize*1.4142136f);
}
```
##### 级联过渡
在`GetShadowData()`中根据距离计算fade如果不是最后一个球就把fade赋值给cascadeBlend。最后一个球的`·`strength=strength*fade`
```c#
for (i=0;i<_CascadeCount;i++)
{
float4 sphere=_CascadeCullingSpheres[i];
float distanceSqr=DistanceSquared(surfaceWS.position,sphere.xyz);
if(distanceSqr<sphere.w)
{
if(i== _CascadeCount-1)
{
data.strength*=FadedShadowStrength(distanceSqr,_CascadeData[i].x,_ShadowDistanceFade.z);
}
break;;
}
}
```
##### 过渡抖动
提高级联过渡效果。在`LitPassFragment`中给像素计算抖动值
```c#
surface.dither=InterleavedGradientNoise(input.positionCS.xy,0);
```
当使用抖动混合时,如果我们不在上一个级联中,则当混合值小于抖动值时,跳到下一个级联。
```c#
#if defined(_CASCADE_BLEND_DITHER)
else if(data.cascadeBlend < surfaceWS.dither)
{
i+=1;
}
#endif
```
##### 其他功能效果
- 透明度
- 阴影模式
- 裁切阴影
- 抖动阴影
- 无阴影
- 不受光阴影投射器
- 接受阴影

View File

@@ -0,0 +1,204 @@
## ToonShader
书中案例使用了2个Pass来实现效果。
### OutlinePass
模型外面边
```
Pass {
NAME "OUTLINE"
Cull Front
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
float _Outline;
fixed4 _OutlineColor;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f {
float4 pos : SV_POSITION;
};
v2f vert (a2v v) {
v2f o;
float4 pos = mul(UNITY_MATRIX_MV, v.vertex);
//IT_MV rotates normals from object to eye space
float3 normal = mul((float3x3)UNITY_MATRIX_IT_MV, v.normal);
normal.z = -0.5;
pos = pos + float4(normalize(normal), 0) * _Outline;
o.pos = mul(UNITY_MATRIX_P, pos);
return o;
}
float4 frag(v2f i) : SV_Target {
return float4(_OutlineColor.rgb, 1);
}
ENDCG
}
```
### ToonShader
颜色计算:
`Ramp颜色=tex2D(_Ramp, float2(dot(worldNormal, worldLightDir))).rgb`
`diffuse=贴图颜色*指定颜色*灯光颜色*(Ramp贴图)`
```
fixed4 c = tex2D (_MainTex, i.uv);
fixed3 albedo = c.rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
fixed diff = ;
diff = (diff * 0.5 + 0.5) * atten;
fixed3 diffuse = _LightColor0.rgb * albedo * tex2D(_Ramp, float2(diff, diff)).rgb;
```
高光计算:
使用fwidth与smooth主要是用于抗锯齿。https://blog.csdn.net/candycat1992/article/details/44673819
```
fixed spec = dot(worldNormal, worldHalfDir);
fixed w = fwidth(spec) * 2.0;
fixed3 specular = _Specular.rgb * lerp(0, 1, smoothstep(-w, w, spec + _SpecularScale - 1)) * step(0.0001, _SpecularScale);
```
Pass代码
```
Pass {
Tags { "LightMode"="ForwardBase" }
Cull Back
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
#include "UnityShaderVariables.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _Ramp;
fixed4 _Specular;
fixed _SpecularScale;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
float4 tangent : TANGENT;
};
struct v2f {
float4 pos : POSITION;
float2 uv : TEXCOORD0;
float3 worldNormal : TEXCOORD1;
float3 worldPos : TEXCOORD2;
SHADOW_COORDS(3)
};
v2f vert (a2v v) {
v2f o;
o.pos = mul( UNITY_MATRIX_MVP, v.vertex);
o.uv = TRANSFORM_TEX (v.texcoord, _MainTex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(_Object2World, v.vertex).xyz;
TRANSFER_SHADOW(o);
return o;
}
float4 frag(v2f i) : SV_Target {
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
fixed3 worldHalfDir = normalize(worldLightDir + worldViewDir);
fixed4 c = tex2D (_MainTex, i.uv);
fixed3 albedo = c.rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
fixed diff = dot(worldNormal, worldLightDir);
diff = (diff * 0.5 + 0.5) * atten;
fixed3 diffuse = _LightColor0.rgb * albedo * tex2D(_Ramp, float2(diff, diff)).rgb;
fixed spec = dot(worldNormal, worldHalfDir);
fixed w = fwidth(spec) * 2.0;
fixed3 specular = _Specular.rgb * lerp(0, 1, smoothstep(-w, w, spec + _SpecularScale - 1)) * step(0.0001, _SpecularScale);
return fixed4(ambient + diffuse + specular, 1.0);
}
ENDCG
}
```
### 素描风格
通过`hatchFactor=max(0,dot(worldLightDir,worldNormal))*7`获取因子。之后计算各个图层的混合因子:
```
if (hatchFactor > 6.0) {
// Pure white, do nothing
} else if (hatchFactor > 5.0) {
o.hatchWeights0.x = hatchFactor - 5.0;
} else if (hatchFactor > 4.0) {
o.hatchWeights0.x = hatchFactor - 4.0;
o.hatchWeights0.y = 1.0 - o.hatchWeights0.x;
} else if (hatchFactor > 3.0) {
o.hatchWeights0.y = hatchFactor - 3.0;
o.hatchWeights0.z = 1.0 - o.hatchWeights0.y;
} else if (hatchFactor > 2.0) {
o.hatchWeights0.z = hatchFactor - 2.0;
o.hatchWeights1.x = 1.0 - o.hatchWeights0.z;
} else if (hatchFactor > 1.0) {
o.hatchWeights1.x = hatchFactor - 1.0;
o.hatchWeights1.y = 1.0 - o.hatchWeights1.x;
} else {
o.hatchWeights1.y = hatchFactor;
o.hatchWeights1.z = 1.0 - o.hatchWeights1.y;
}
```
之后在片元着色器中将因子与6张素描贴图的采样结果相乘以及计算白色区域光照最后相加
```
fixed4 hatchTex0 = tex2D(_Hatch0, i.uv) * i.hatchWeights0.x;
fixed4 hatchTex1 = tex2D(_Hatch1, i.uv) * i.hatchWeights0.y;
fixed4 hatchTex2 = tex2D(_Hatch2, i.uv) * i.hatchWeights0.z;
fixed4 hatchTex3 = tex2D(_Hatch3, i.uv) * i.hatchWeights1.x;
fixed4 hatchTex4 = tex2D(_Hatch4, i.uv) * i.hatchWeights1.y;
fixed4 hatchTex5 = tex2D(_Hatch5, i.uv) * i.hatchWeights1.z;
fixed4 whiteColor = fixed4(1, 1, 1, 1) * (1 - i.hatchWeights0.x - i.hatchWeights0.y - i.hatchWeights0.z -
i.hatchWeights1.x - i.hatchWeights1.y - i.hatchWeights1.z);
fixed4 hatchColor = hatchTex0 + hatchTex1 + hatchTex2 + hatchTex3 + hatchTex4 + hatchTex5 + whiteColor;
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
return fixed4(hatchColor.rgb * _Color.rgb * atten, 1.0);
```

View File

@@ -0,0 +1,313 @@
## BrightnessSaturationAndContrast
### 给摄像机添加脚本
添加2个Meta让其可以在编辑器模式下运行并且只能绑定Camera组件。
```
[ExecuteInEditMode]
[RequireComponent(typeof(Camera))]
```
实现基础类
```
using UnityEngine;
[ExecuteInEditMode]
[RequireComponent(typeof(Camera))]
public class PostEffectsBase : MonoBehaviour
{
protected void CheckResources()
{
bool isSupported = CheckSupport();
if (isSupported == false)
{
NotSupported();
}
}
protected bool CheckSupport()
{
if (SystemInfo.supportsImageEffects == false || SystemInfo.supportsRenderTextures == false)
{
Debug.LogWarning("Not Supported!");
return false;
}
return true;
}
protected void NotSupported()
{
enabled = false;
}
protected Material CheckShaderAndCreateMaterial(Shader shader, Material material)
{
if (shader == null)
return null;
if (shader.isSupported && material && material.shader == shader)
return material;
if (!shader.isSupported)
return null;
else
{
material = new Material(shader);
material.hideFlags = HideFlags.DontSave;
if (material)
return material;
else
return null;
}
}
protected void Start()
{
CheckResources();
}
}
```
之后根据需求在子类中添加变量:
```
using UnityEngine;
public class BrightnessSaturationAndContrast : PostEffectsBase
{
[Range(0.0f, 3.0f)]
public float brightness = 1.0f;
[Range(0.0f, 3.0f)]
public float saturation = 1.0f;
[Range(0.0f, 3.0f)]
public float contrast = 1.0f;
public Shader briSatConShader;
private Material briSatConMaterial;
public Material material
{
get
{
briSatConMaterial = CheckShaderAndCreateMaterial(briSatConShader, briSatConMaterial);
return briSatConMaterial;
}
}
private void OnRenderImage(RenderTexture src, RenderTexture dest)
{
if (material != null)
{
material.SetFloat("_Brightness", brightness);
material.SetFloat("_Saturation", saturation);
material.SetFloat("_Contrast", contrast);
Graphics.Blit(src, dest, material);
}
else
{
Graphics.Blit(src, dest);
}
}
}
```
最后在OnRenderImage中调用Graphics.Blit()进行渲染。
### 添加后处理Shader
```
Shader "PostProcess/BrightnessSaturationAndContrast" {
Properties {
_MainTex ("Base", 2D) = "white" {}
_Brightness("Brightness",Float)=1
_Saturation("Saturation",Float)=1
_Contrast("Constrast",Float)=1
}
SubShader {
Pass{
ZTest Always Cull Off ZWrite Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
half _Brightness;
half _Saturation;
half _Contrast;
struct v2f{
float4 pos : SV_POSITION;
half2 uv : TEXCOORD0;
};
v2f vert(appdata_img v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv=v.texcoord;
return o;
}
fixed4 frag(v2f i) : SV_Target{
fixed4 renderTex=tex2D(_MainTex,i.uv);
fixed3 finalColor=renderTex.rgb * _Brightness;
fixed luminance=0.2125*renderTex.r+0.7154*renderTex.g+0.0721*renderTex.b;
fixed3 luminanceColor=fixed3(luminance,luminance,luminance);
finalColor =lerp(luminanceColor,finalColor,_Saturation);
fixed3 avgColor=fixed3(0.5,0.5,0.5);
finalColor=lerp(avgColor,finalColor,_Contrast);
return fixed4(finalColor,renderTex.a);
}
ENDCG
}
}
Fallback Off
}
```
## 高斯模糊
与之前不同这里利用RenderTexture.GetTemporary函数分配了一块与屏幕图像大小相同的缓冲区。这是因为高斯模糊需要调用两个Pass我们需要使用一块中间缓存来存储第一个Pass执行完毕后得到的模糊结果。`RenderTexture buffer = RenderTexture.GetTemporary(rtW, rtH, 0);`我们首先调用`Graphics.Blit(src, buffer, material, 0),`使用Shader中的第一个pass对src进行处理并将结果存储在了buffer中。然后在调用`Graphics.Blit(src, buffer, material, 1)`,使用Shader中的第二个Pass对buffer进行处理返回最终的屏幕图像。最后我们还需要调用`RenderTexture.ReleaseTemporary`来释放之前分配的缓存。
```
private void OnRenderImage(RenderTexture src, RenderTexture dest)
{
if (material != null)
{
int rtW = src.width;
int rtH = src.height;
RenderTexture buffer = RenderTexture.GetTemporary(rtW, rtH, 0);
Graphics.Blit(src, buffer, material, 0);
Graphics.Blit(buffer, dest, material, 1);
RenderTexture.Release(buffer);
}
else
{
Graphics.Blit(src, dest);
}
}
```
实现降采样:
```
private void OnRenderImage(RenderTexture src, RenderTexture dest)
{
if (material != null)
{
int rtW = src.width / downSample;
int rtH = src.height / downSample;
RenderTexture buffer = RenderTexture.GetTemporary(rtW, rtH, 0);
buffer.filterMode=FilterMode.Bilinear;
Graphics.Blit(src, buffer, material, 0);
Graphics.Blit(buffer, dest, material, 1);
RenderTexture.Release(buffer);
}
else
{
Graphics.Blit(src, dest);
}
}
```
### CGINCLUDE
使用CGINCLUDE与ENDCG关键字将通用部分引用给其他Pass
```
Shader "Unity Shaders Book/Chapter 12/Gaussian Blur" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_BlurSize ("Blur Size", Float) = 1.0
}
SubShader {
CGINCLUDE
#include "UnityCG.cginc"
sampler2D _MainTex;
half4 _MainTex_TexelSize;
float _BlurSize;
struct v2f {
float4 pos : SV_POSITION;
half2 uv[5]: TEXCOORD0;
};
v2f vertBlurVertical(appdata_img v) {
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
half2 uv = v.texcoord;
o.uv[0] = uv;
o.uv[1] = uv + float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
o.uv[2] = uv - float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
o.uv[3] = uv + float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
o.uv[4] = uv - float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
return o;
}
v2f vertBlurHorizontal(appdata_img v) {
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
half2 uv = v.texcoord;
o.uv[0] = uv;
o.uv[1] = uv + float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
o.uv[2] = uv - float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
o.uv[3] = uv + float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;
o.uv[4] = uv - float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;
return o;
}
fixed4 fragBlur(v2f i) : SV_Target {
float weight[3] = {0.4026, 0.2442, 0.0545};
fixed3 sum = tex2D(_MainTex, i.uv[0]).rgb * weight[0];
for (int it = 1; it < 3; it++) {
sum += tex2D(_MainTex, i.uv[it*2-1]).rgb * weight[it];
sum += tex2D(_MainTex, i.uv[it*2]).rgb * weight[it];
}
return fixed4(sum, 1.0);
}
ENDCG
ZTest Always Cull Off ZWrite Off
Pass {
NAME "GAUSSIAN_BLUR_VERTICAL"
CGPROGRAM
#pragma vertex vertBlurVertical
#pragma fragment fragBlur
ENDCG
}
Pass {
NAME "GAUSSIAN_BLUR_HORIZONTAL"
CGPROGRAM
#pragma vertex vertBlurHorizontal
#pragma fragment fragBlur
ENDCG
}
}
FallBack "Diffuse"
}
```

View File

@@ -0,0 +1,103 @@
## EPIC商城
https://epicgames.hyperwallet.com/hw2web/landing.xhtml?faces-redirect=true&refreshme=true
lou jiajie
Lou JiaJie
owner
INDIVIDUAL
米粒儿
Reimu
## 向日葵
blueroseslol
orkj694780U9t7r1
ljj199221
## 光猫
useradmin
nhg9h
telecomadmin60087438
使用老爸的账号可以查看账号密码。
## Github Token
- 图床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
- Picgo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
## youtube视频下载
https://qdownloader.net/youtube-video-downloader
https://www.y2mate.com/youtube/
# DNS
微软DNS
- 4.2.2.1
- 4.2.2.2
## PS4 DNS
- 119.29.29.29
- 114.114.114.114
香港DNS
- 202.181.202.140
- 202.181.224.2
全网通
- 首选119.29.29.29
- 备选182.254.116.116
## Switch DNS
- 主要DNS112.106.53.22
- 备用DNS112.106.53.34
- 主要DNS203.112.2.4
- 备用DNS203.112.2.5
- 主要DNS168.126.63.1
- 备用DNS168.126.63.2
# 百度网盘账号:
- blueroseslol
- ljj88483649
- 18767165526
- ljj199221
## 使用SSR给PS4作为代理
https://mikublog.com/shadowsocks/609
-----------------------------------------------------------------------------------------------------------------------
## 达瓦的UDN账号
blakeguy@163.com
Zhuzw6270668
## Steam 118错误解决
如果没有行政部门背锅,那么就是电信、网通、移动利用垄断地位进行了违法活动。
现在发现的症状基本是DNS污染。
解决方法是把DNS服务器换成208.67.222.222或208.67.220.220 o(* ̄︶ ̄*)o
## 搬瓦工
- 378100977@qq.com
- ljj199221
被墙检测https://wivwiv.com/post/ssr-v2ray-trojan/
## SpeedTest
https://www.speedtest.net/
## ip测速
https://tools.ipip.net/newping.php
## 检查Ip是否被墙
https://bandwagonhoster.com/653.html
## 中心阿里云账号
子账户zjsjcpzljczx@1042965787832287
密码:123456&*()LlL
123456&*()LlL
登录地址http://signin.aliyun.com/1042965787832287/login.htm
### 远程桌面
116.62.69.44
administrator
zaq1@XSW2

View File

@@ -0,0 +1,16 @@
bd0dc-c80ce
09120-97ed1
46bae-53203
0a1b4-dde0d
cae50-36821
8c24c-31c93
6931b-b582f
90e3e-33c1e
58bce-5b314
94535-5da38
6963d-6c7b7
e5a82-23d6f
837af-f7b5c
3f421-2383d
465e8-03051
1d2fe-f3fdf

View File

@@ -0,0 +1,5 @@
## 参考
https://zhuanlan.zhihu.com/p/413730301
## 步骤
win10以管理员模式打开PowerShell输入`CheckNetIsolation.exe loopbackexempt -a -p=S-1-15-2-1609473798-1231923017-684268153-4268514328-882773646-2760585773-1760938157`之后运行。

BIN
07-Other/技术演示.pptx (Stored with Git LFS) Normal file

Binary file not shown.

114
07-Other/生活/婚礼.md Normal file
View File

@@ -0,0 +1,114 @@
老徐:
- [ ] 婚礼布置:**2~2.5W**。老妈去谈。
- [ ] 需要一起面谈。
- [ ] **几个伴娘&伴郎**。 暂定2个。 500~800 * 4
- [ ] 电子请柬。
- [ ] 婚礼服装:
- [ ] 主婚纱、秀禾、敬酒服。
- [ ] 去杭州婚纱城看看。
- 稀区两件4000不含鞋。
- [ ] 伴娘、伴郎服装。
- [ ] 订做西装。费用。
- [ ] 化妆师 5000
- [ ] 确定价位5000左右。
- 3800 4800 5800 6800
- [ ] 摄影&摄像 10000
- [ ] 双摄像 + 双摄影 = 5200 +4800
- [ ] 摄影价位。单机,
- [ ] 摄像价位。单机,
- [ ] 制作?
- [x] 司仪:小徐 5000
- [ ] 伴手礼
- [ ] 婚车: 1300 * 5 = 6500
- 4 ~ 5 辆奥迪
## 伴手礼
- 朱总1
## 喜糖
- 技术组15
- 演员9
- 运营+导播+音频14 ~ 15
# 婚庆信息
一、新郎信息
新郎姓名:楼嘉杰
联系方式18767165526
新郎早上出发地址杭州市西湖区古荡嘉绿南苑4幢4单元502
新家地址杭州市上城区映霞璟庭2-1101
二、新娘信息
新娘姓名:舒菁
联系方式13666680899
新娘早上化妆地址:温德姆酒店
三、 双方家长信息(姓名+联系方式)
新郎父亲: 楼13957130794
新郎母亲: 姚13516809400
新娘父亲: 舒
新娘母亲13396591017
四、伴团信息(姓名+联系方式)
共几对2
主伴郎陈飞宇17681850992
主伴娘13547453671
五、其他信息
外景地址:白塔公园
晚宴酒店地址:温德姆酒店(凤起路)
宴会厅名+楼层3楼宴会厅
酒店婚宴负责人(姓名+联系方式):
婚车队负责人(姓名+联系方式):
婚车装饰方式(婚车自带或者花店装饰)
婚车装饰地址:
烟酒负责人(姓名+联系方式):
# TODO
1. [ ] 婚车路线 & ~~婚车队长确认胸花、手捧花、新娘花。~~
1. [ ] 婚车座位安排。
2. [ ] 事先确认超时费用。
2. [x] 通知2位伴郎来过的时间。伴郎协作给司机烟。
1. [ ] 伴郎帮忙拿一下花。
2. [x] 拉进群。
3. [ ] 确认摄影摄像服务时间。
4. [x] 新郎家做装饰。
5. [ ] 婚车出发以及快到在群里通知。
6. [x] 向酒店确认化妆间是否可以全天使用。
7. [ ] 拍摄外景拍摄道具、外套、如果下雨还需要带上雨伞。
8. [x] ***向徐老师确认婚宴才彩排的事宜。劲量早***
9. [x] 酒宴排位以及敬酒顺序。
10. [ ] 物料确认
1. [ ] 家中茶叶
2. [ ] 矿泉水
11. [x] 提前和物业沟通停车位事宜。
12. [x] 堵门游戏红包
13. [x] 葡萄汁&桑葚汁。
14. [x] ***5元红包 120个***。
15. [x] 新娘准备行李箱。
## 司仪TODO
1. 父亲领新娘走上台,新郎去接新娘。
2. 携手走上舞台
3. 童男 * 2 送戒指。相互佩戴,拥抱、佩戴。
5. 敬酒。
6. ~~捧花取消~~
7. 拍大合影 2张开灯/关灯。
8. 15个水果娃娃。
9. 包装纸包。
10. [x] 确定购买的盲盒礼品,买的品类。
11. [x] ***5元 120个红包***,保证人手一个。
12. [x] 需要从LED屏幕多接一根线接司仪电脑。
13. [x] 问一下婚庆公司的音响。是酒店还是外调的。几对,怎么摆。 建议***2对音响***,问一下多少钱。
14. [x] 确定酒店是LED屏还是投影仪。
15. [x] 家长是否上台,上台讲话代表决定。
16. 到酒店后,带着伴郎去找徐老师。
17. 大人4点钟到酒店。
# 喜糖
- [x] 1102
- [x] 1105
- [x] 1108
- [x] 1301

View File

@@ -0,0 +1,34 @@
# 待办事项
- [ ] 家里取东西
- [ ] 电脑
- [ ] 音箱
- [ ] PS4
- [ ] Switch
- [ ] 牙线
- [ ] 剃须刀
- [ ] 身份证以及其他证件
- [ ] 银行卡
- [ ] 饼干?
- [ ] EPIC以及流浪地球纪念品
- [ ] 婚纱照整理以及手机照片整理并且上传到NAS上。
- [ ] 智能家居
- [ ] 部署HomeBridge 连接扫地机器人
- [ ] ~~Aqara GetWay~~
- [ ] NAS
- [ ] 音乐
- [ ] Subsonic
- [ ] ***Navidrome***
- [ ] Jellyfin
- [ ] Emby
- [ ] AudioStation
- [ ] Plex
- [ ] 手机照片备份
- [ ] Obsidian CloudReve Webdav同步
- [ ] 自动签到框架 https://www.bilibili.com/video/BV1we4115725/?spm_id_from=333.999.0.0&vd_source=d47c0bb42f9c72fd7d74562185cee290
# 每周需要做的事情
1. 清理卫生间。
2. 清理扫地机器人。
3. 清理洗碗机。
4. 全屋打扫。
1.

View File

@@ -0,0 +1 @@
https://git.unlock-music.dev/um/cli

View File

@@ -0,0 +1,25 @@
# 12月
| 支出项目 | 日期 | 数额 | 支出账户 |
| ----------------- | ----- | -------------- | ---- |
| 转账给舒筋 | 11.24 | 10000 | 微信 |
| 转账给舒菁 | 11.28 | 5078.00 | 微信 |
| 舒菁taobao购物代付 | 11.29 | 242.75 | 支付宝 |
| 水费 | 12.02 | 14.5 | 支付宝 |
| 电费 | 12.02 | 77.47 | 支付宝 |
| 舒菁红包 | 12.03 | 200 | 微信 |
| TaoBao购买鞋架 | 12.07 | 133 | 支付宝 |
| 购买光纤相关包括1月买的光模块 | 12.10 | 238 | 支付宝 |
| 转账给 | 12.12 | 10000 | 微信 |
| 山姆购物(公司购物) | 12.13 | ~~121~~ | 支付宝 |
| 山姆购物(面包、厕纸、冲牙器) | 12.15 | 717.3 | 支付宝 |
| 看病(武警医院看支气管炎) | 12.20 | 107 | 支付宝 |
| 看病(浙二发热门诊看支气管炎) | 12.21 | 260 | 支付宝 |
| 看病(市一看鼻炎) | 12.27 | 334 | 支付宝 |
| 舒菁红包 | 12.27 | 200 | 微信 |
| 舒菁taobao购物代付 | 12.27 | 215.05 | 支付宝 |
| 山姆购物(面包、鸡蛋、华夫饼) | 12.29 | 110.6 | 支付宝 |
| 转账给舒菁 | 12.29 | 1755 | 微信 |
| 山姆购物 | 12.31 | ~~581.37~~ 148 | 支付宝 |
支付宝45025 + 3846.86
微信22518.35 + 13032
JD购物3862.05 + 2471.5

View File

@@ -0,0 +1,117 @@
---
tags:
---
# 1月
| 支出项目 | 日期 | 数额 | 支出账户 |
| -------- | ---- | ------ | ---- |
| 水费 | 1.1 | 37.7 | 支付宝 |
| 电费 | 1.1 | 452.46 | 支付宝 |
| 燃气费 | 1.1 | 136.4 | 支付宝 |
| 公交卡 | 1.7 | 100 | 支付宝 |
| TaoBao购物 | 1.8 | 39.55 | 支付宝 |
| JD购物睡衣 | 1.9 | 154.84 | 微信 |
| JD购物睡衣 | 1.9 | 159.95 | |
| 推拿续卡 | 1.11 | 3000 | 支付宝 |
| 山姆购物 | 1.11 | 300 | 支付宝 |
| 咪咪内外驱与疫苗 | 1.18 | 294 | 支付宝 |
| 舒菁公粮 | 1.14 | 10000 | 微信 |
| 过年礼物 | 1.25 | 2000 | 微信 |
| | | | |
支付宝支出5231.73 收入11.46
微信支出13055 收入2749
JD购物332.89
# 2月
| 支出项目 | 日期 | 数额 | 支出账户 |
| ----------- | -------- | ----- | -------- |
| 水费 | 2.2 | 43.5 | 支付宝 |
| 电费 | 2.2 | 434.7 | 支付宝 |
| 社保费 | 2.3 | 962.4 | 支付宝 |
| 看病 | 2.3 | 312 | 支付宝 |
| 山姆购物 | 2.16 | 192.4 | 支付宝 |
| 米家灯带COB | 2.16 | 145.5 | 支付宝 |
| 转账给舒菁 | 2.6 | 1000 | 微信 |
| 麦当劳 | 2.6~2.26 | 348.3 | 微信 |
| 各种咖啡 | | 125 | 微信 |
| 舒菁公粮 | 2.20 | 5000 | 银行卡 |
| 电信费 | .11 | 99.53 | 银行卡 |
| | | | |
支付宝3045支出 1000.12收入
微信1633.27支出 1127.85收入
JD购物327
# 3月
| 支出项目 | 日期 | 数额 | 支出账户 |
| ------------ | ---- | ------- | -------- |
| 水费 | 3.2 | 40.6 | 支付宝 |
| 电费 | 3.2 | 365.3 | 支付宝 |
| 社保费 | 3.3 | 1419.54 | 支付宝 |
| 山姆购物 | 3.3 | 109.8 | 支付宝 |
| 乐刻健身季卡 | 3.11 | 414 | 支付宝 |
| 山姆购物 | 3.18 | 119.7 | 支付宝 |
| 山姆购物 | 3.24 | 233.6 | 支付宝 |
| 宽带费 | | 128 | 支付宝 |
| | | | |
支付宝3649.61支出 0.15收入
微信771.25支出 765.3收入
JD购物1133.19
# 4月
| 支出项目 | 日期 | 数额 | 支出账户 |
| -------------------- | ---- | ------- | -------- |
| 水费 | 4.2 | 46.4 | 支付宝 |
| 电费 | 4.2 | 225.71 | 支付宝 |
| 宽带费 | 4.1 | 128 | 银行卡 |
| 社保费 | 4.2 | 1419.54 | 支付宝 |
| 无限机兵 | 4.9 | 187 | 支付宝 |
| 个人所得税 | 4.11 | 7858.5 | 支付宝 |
| 西湖边和李双喝茶 | 4.13 | 298 | 支付宝 |
| 地铁充值 | 4.14 | 100 | 支付宝 |
| 舒菁亲密付 | 4.25 | 129 | 支付宝 |
| B站大会员 | 4.26 | 148 | 支付宝 |
| 地铁充值 | 4.28 | 100 | 支付宝 |
| 西湖边和李双吃外婆家 | 4.14 | 213.44 | 微信 |
| 黑神话悟空 | 4.27 | 60 | 微信 |
| | | | |
支付宝11336.52支出
微信601.16支出 92.52收入
JD购物920.84
# 5月
| 支出项目 | 日期 | 数额 | 支出账户 |
| ------------------ | ---- | ------ | -------- |
| 水费 | 5.4 | 43.5 | 支付宝 |
| 电费 | 5.1 | 152.79 | 支付宝 |
| 燃气费 | 5.1 | 241.8 | 支付宝 |
| 宽带费 | 5.1 | 128.73 | 银行卡 |
| 舒菁亲密付 | 5.1 | 293 | 支付宝 |
| 优衣库 | 5.5 | 192 | 支付宝 |
| 舒菁公粮 | 5.8 | 4000 | 支付宝 |
| 购买UE资产 | 5.17 | 216.19 | 支付宝 |
| 和舒菁在厚牛家吃饭 | 5.18 | 259 | 支付宝 |
| 舒菁亲密付 | 5.24 | 125 | 支付宝 |
| 战双 | 5.25 | 856 | 支付宝 |
| 舒菁亲密付 | 5.30 | 203 | 支付宝 |
| SeeSeeCrepes | 5.11 | 96 | 微信 |
| 520 | 5.20 | 520 | 微信 |
| JD购买制氧机 | 5.30 | 2853 | 银行卡 |
| JD购买水果 | 5.31 | 126 | 银行卡 |
| | | | |
支付宝7977.28支出 111收入
微信1039支持 50收入
JD购物2864.62
# 6月
| 支出项目 | 日期 | 数额 | 支出账户 |
| ---- | --- | ------ | ----- |
| 水费 | 6.2 | 43.5 | 支付宝 |
| 电费 | 6.2 | 147.41 | 支付宝 |
| 舒菁公粮 | 6.5 | 7500 | 招商银行卡 |
| 宽带费 | 6.1 | 128.73 | 银行卡 |
| | | | |
# 7月

View File

@@ -0,0 +1,14 @@
# 前言
- 知乎
- https://www.zhihu.com/people/XMedia2015
# 相关软件
>有什么关于 Apple TV 的用后体验及软件意见分享? - 殇小辛的回答 - 知乎
- https://www.zhihu.com/question/572608645/answer/3167083336
>在大陆使用 Apple TV 有什么建议?
- https://www.zhihu.com/question/59459394/answer/2933247325
1. infuse
2. Fileball
3. B站miao/cheers
4. 看电视台节目的AppAPTV、iSTB Lite、iSTB、iPlay TV、TV+
5. 阿里云盘播放器iiVA和Alplayer

View File

@@ -0,0 +1,41 @@
# 前言
- Aqara M2刷固件M2需要拆机M1S 4.0固件之后也不支持Telnet开启
- 利用Aqara Gateway集成将Aqara网关(M1s,P3,E1)接入HA:https://post.smzdm.com/p/agqnrkpw/
- AqaraGateway:https://github.com/niceboygithub/AqaraGateway
- Aqara M2 网关开启Telnet集成到Home Assistant:https://sspai.com/post/86223
- M2 2022 POE版本开启Telnet: https://github.com/niceboygithub/AqaraCameraHubfw/blob/main/modified/M2PoE/telnet.md
- [[#M2 2022 Telnet]]
- ***万物皆可HA教你把各种智能设备接入Home Assistant 图文教程***:https://www.bilibili.com/opus/770959033731383298
# Aqara HomeKit反接到HA
https://post.smzdm.com/p/awz3kgrg/
1. Aqara接入一次Homekit之后在IOS家庭App中选中网关点击网关之后选择从"家庭"中移除桥接设备。在家庭APP中可以打开配对模式 31141937533
2. 重新启动一下Aqara网关。
3. 之后就可以HA中找到Aqara网关的HomeKit设备连接提示了。
| https://bbs.hassbian.com/thread-24044-1-1.html |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| _本帖最后由 longtor 于 2024-5-22 05:38 编辑_ <br> <br><br>Aqara  m2网关反向接入homeassistant 一个网关同时接入 Aqara、苹果家庭、HA <br><br>总体思路先接Aqara APP、苹果家庭APPhomekit再删除homekit然后接入ha最后从ha接回homekitha自带的hassbridge接入到苹果家庭<br><br>1、Aqara APP添加网关M2<br>2、苹果家庭APP<br>1加入网关M2。<br>2删除家庭设置 - 家居中枢与桥接 - 删除 Aqara Hub M2<br><br>3、homeassistant<br>1集成 里面,自动弹出新集成。配置。<br>2填写网关上面homekit的ID码提交。<br>3在 新增设备选项中填写 区域,或取消,接入成功。<br>4Homekit Bridge生成二维码。<br><br>4、苹果家庭 APP扫二维码新增 网桥homekit 设备 出现。 |
| |
# M2 2022 Telnet
Aqara M2网关接HA教程
如果你正在寻找如何将Aqara M2 POE网关接入Home Assistant那么你来对地方了这里有一个详细的教程专门针对M2 POE版本型号ZHWG19LM。通过以下步骤你可以轻松地将你的网关接入Home Assistant。
**准备工作** 🛠️
首先你需要准备一个U盘并格式化。注意格式化必须为FAT32分区必须为MBR。如果后续网关降级失败可以从这两点入手。
下载降级文件 📥
从GitHub的AqaraCameraHubfw项目中下载两个降级文件并将它们放入U盘。
**正式步骤** 📋
断电并插入U盘拔掉M2的电源线断电然后将U盘插上。
按住按钮并通电按住前面的小按钮同时插上电源线通电。当小灯变成淡紫色时松开按钮。等待一会儿M2会完成降级并重启灯会变成正常的颜色。成功后M2就降级为了一个很老的版本并开启了telnet。
**连接telnet**
使用telnet连上M2账号为root没有密码直接回车。然后执行三条命令来进行更新用的是AqaraCameraHubfw中修改过的固件。看了下基本是跟官方同步的版本。后续不要用aqara home进行更新需要更新的话就连上telnet使用这个命令进行更新。执行更新命令的时候可能需要网关所在的网络有科学上网环境。如果执行第三个命令的时候后输出找不到文件或目录那就是第二个命令没有成功下载到文件。我们将/tmp/curl -s -k -L -o....中的-s删掉不让他静默执行就可以看到报错信息输出可通过这个查找问题原因。执行完这三个命令后输入reboot对网关进行重启然后再次连上telnet执行那三个命令然后reboot重启非常重要一定要这样执行两次。这时候我们进aqara home查看版本信息会看到已经是最新的版本了。
**关闭自动更新** 🚫
接下来关闭自动更新固件的指令,防止网关自动更新。
**接入Home Assistant** 🏠
这时候M2网关已经完成了固件的刷入并开启了telnet然后我们在hacs中的Repository中输入aqara gateway的项目地址并安装。安装完成重启ha然后我们就能把网关接入ha了。

View File

@@ -0,0 +1,3 @@
- 这是一个简单的连接器,用于通过 MQTT 公开 HDMI-CEC 适配器 https://hub.docker.com/r/tobiasha/hdmi-cec
- Raspberry Pi 运行 hdmi-cec-rest 的 Docker 映像 https://github.com/blakeblackshear/rpi-hdmi-cec-rest
- https://github.com/hsbiti/docker-hdmi-cec-rest

View File

@@ -0,0 +1,62 @@
# 前言
- 和风天气heweather HomeAssistant插件使用说明 https://www.bilibili.com/opus/687775477026259015
- HomeAssistant 和风天气插件的自动化分享 https://www.bilibili.com/read/cv18078640/
# 和风天气 Token
afdc8b37975043888ad47943ff25b980
# HA
在configuration.yaml中添加
```YAML
template:
- trigger:
- platform: time_pattern
hours: "*"
action:
- service: weather.get_forecasts
data:
type: hourly
target:
entity_id: weather.he_feng_tian_qi
response_variable: hourly
sensor:
- name: "小时天气预警"
unique_id: weather_forecast_hourly
state: >
{% if hourly['weather.he_feng_tian_qi'].forecast[0].condition in ('sunny','cloudy','partlycloudy','windy') %}
off
{% else %}
on
{% endif %}
attributes:
states: >
{% if hourly['weather.he_feng_tian_qi'].forecast[0].condition in ('sunny','cloudy','partlycloudy','windy') %}
未来一小时,天气{{ hourly['weather.he_feng_tian_qi'].forecast[0].text }},没有降雨
{% else %}
接下来一小时会有{{ hourly['weather.he_feng_tian_qi'].forecast[0].text }},降水概率为 {{ hourly['weather.he_feng_tian_qi'].forecast[0].precipitation_probability }}%
{% endif %}
```
## 自动化
对应的自动化:
```yaml
alias: 一小时天气预警
description: ""
triggers:
- entity_id:
- sensor.xiao_shi_tian_qi_yu_jing
trigger: state
actions:
- device_id: 384bea520a0ee825ecb63fb71d19b2a1
domain: mobile_app
type: notify
message: "注意注意! 接下来的天气为{{state_attr('sensor.xiao_shi_tian_qi_yu_jing','states')}} "
title: 天气预警
- action: notify.send_message
metadata: {}
data:
message: "[\"注意注意! {{state_attr('sensor.xiao_shi_tian_qi_yu_jing','states')}}\"]"
target:
entity_id: notify.xiaomi_cn_799945688_l06a_play_text_a_5_1
initial_state: true
```

View File

@@ -0,0 +1,11 @@
# 安装
Docker模式下安装
https://blog.csdn.net/weixin_44379605/article/details/127784744
```c++
docker run -d -it -p 1880:1880 -v node_red_data:/data --name mynodered nodered/node-red
```
# 创建令牌
点击用户->"安全"选项卡->创建长期令牌
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJjMWZlYjYzYmZjMzk0MDU3YjE1MTFhN2IyZTQzODYwZCIsImlhdCI6MTczODY2MjgzMiwiZXhwIjoyMDU0MDIyODMyfQ.P8nUFoxJFPst_CpJHvShag7sMk6yvG841Kv0hL_tPdU

View File

@@ -0,0 +1 @@
https://zhuanlan.zhihu.com/p/352298928

View File

@@ -0,0 +1,271 @@
科沃斯IDECJ3FLME
# fix HA不出现实体的问题
- https://bbs.hassbian.com/thread-27784-1-1.html
- https://bbs.hassbian.com/thread-28408-1-1.html
1. 进入容器的CMD
2. cd /usr/local/lib/python3.13/site-packages/deebot_client/hardware/deebot
3. ln -svfT lr4qcs.py 3sp2in.py
删除 rm -rf 3sp2in.py
- https://mitmproxy.org/
- 【mitmproxy抓包工具 从安装到简单使用】 https://www.bilibili.com/video/BV1UC4y1t7EL/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- IOS使用MitmProxy
- https://www.jianshu.com/p/498073a8971f
- https://hachibye.medium.com/%E4%BB%A5mitmproxy%E7%8D%B2%E5%8F%96%E6%89%8B%E6%A9%9F%E6%95%B8%E6%93%9A%E5%B0%81%E5%8C%85-9870f6507b63
https://github.com/DeebotUniverse/client.py/issues/492
```
<redacted>
```
# In HA my X5 Pro did'nt accept cleaning commands (rcp not support error)
# Command Json
Start
https://api-ngiot.dc-cn.cn.ecouser.net/api/iot/endpoint/control?si=6btMpMAS6PhZrX3D&ct=q&eid=9b7f3e00-3bea-4354-9ba8-b0dc9ceb6e0e&et=3sp2in&er=96uOLWSM&apn=clean_V2&fmt=j
{"body":{"data":{"act":"start","content":{"type":"auto"}}},"header":{"channel":"iOS","reqid":"RIzVHS","ts":"1741248430546","ver":"0.0.50","m":"request","pri":1,"tzm":480,"tzc":"Asia\/Shanghai"}}
Pause
https://api-ngiot.dc-cn.cn.ecouser.net/api/iot/endpoint/control?si=EKptJpQmzpbr4rKF&ct=q&eid=9b7f3e00-3bea-4354-9ba8-b0dc9ceb6e0e&et=3sp2in&er=96uOLWSM&apn=clean_V2&fmt=j
{"body":{"data":{"act":"pause"}},"header":{"channel":"iOS","reqid":"yepkRW","ts":"1741248436662","ver":"0.0.50","m":"request","pri":1,"tzm":480,"tzc":"Asia\/Shanghai"}}
Resume
https://api-ngiot.dc-cn.cn.ecouser.net/api/iot/endpoint/control?si=6hYStayiak272hKX&ct=q&eid=9b7f3e00-3bea-4354-9ba8-b0dc9ceb6e0e&et=3sp2in&er=96uOLWSM&apn=clean_V2&fmt=j
{"body":{"data":{"act":"resume"}},"header":{"channel":"iOS","reqid":"nYtIgx","ts":"1741248438194","ver":"0.0.50","m":"request","pri":1,"tzm":480,"tzc":"Asia\/Shanghai"}}
Stop
https://api-ngiot.dc-cn.cn.ecouser.net/api/iot/endpoint/control?si=kSFXbynehrfkNWEK&ct=q&eid=9b7f3e00-3bea-4354-9ba8-b0dc9ceb6e0e&et=3sp2in&er=96uOLWSM&apn=clean_V2&fmt=j
{"body":{"data":{"act":"stop"}},"header":{"channel":"iOS","reqid":"EiLUjr","ts":"1741248516782","ver":"0.0.50","m":"request","pri":1,"tzm":480,"tzc":"Asia\/Shanghai"}}
AI Clean Start
https://api-ngiot.dc-cn.cn.ecouser.net/api/iot/endpoint/control?si=trfmKfcdAKwWaRim&ct=q&eid=9b7f3e00-3bea-4354-9ba8-b0dc9ceb6e0e&et=3sp2in&er=96uOLWSM&apn=clean_V2&fmt=j
{"body":{"data":{"act":"start","content":{"type":"entrust"}}},"header":{"channel":"iOS","reqid":"pSKuSb","ts":"1741249424264","ver":"0.0.50","m":"request","pri":1,"tzm":480,"tzc":"Asia\/Shanghai"}}
Charge
https://api-ngiot.dc-cn.cn.ecouser.net/api/iot/endpoint/control?si=JC4G5mYJnMHaA6mR&ct=q&eid=9b7f3e00-3bea-4354-9ba8-b0dc9ceb6e0e&et=3sp2in&er=96uOLWSM&apn=charge&fmt=j
{"body":{"data":{"act":"go"}},"header":{"channel":"iOS","reqid":"GDJHxj","ts":"1741249495751","ver":"0.0.50","m":"request","pri":1,"tzm":480,"tzc":"Asia\/Shanghai"}}
This is a single room vacuum only:
https://api-ngiot.dc-cn.cn.ecouser.net/api/iot/endpoint/control?si=NM7kXnJYG2mszmwY&ct=q&eid=9b7f3e00-3bea-4354-9ba8-b0dc9ceb6e0e&et=3sp2in&er=96uOLWSM&apn=clean_V2&fmt=j
{"body":{"data":{"act":"start","content":{"type":"freeClean","value":"1,0"}}},"header":{"channel":"iOS","reqid":"MMTIad","ts":"1741249568936","ver":"0.0.50","m":"request","pri":1,"tzm":480,"tzc":"Asia\/Shanghai"}}
# Add Deebot X5 Pro Omni 提交
相关的Git提交
https://github.com/DeebotUniverse/client.py/pull/659/commits/da52c671ce544c21b6753b380148f93f8dadc6e5#diff-a5514fb587b325f41e547af3e94ae3470a2366bef25dd8b5ae5842fc25328bc0
- lr4qcs.py:p1jij8.py
- p1jij8.py
## p1jij8.py
```c++
"""Deebot T20 Omni Capabilities."""
from __future__ import annotations
from deebot_client.capabilities import (
Capabilities,
CapabilityClean,
CapabilityCleanAction,
CapabilityCustomCommand,
CapabilityEvent,
CapabilityExecute,
CapabilityLifeSpan,
CapabilityMap,
CapabilitySet,
CapabilitySetEnable,
CapabilitySettings,
CapabilitySetTypes,
CapabilityStats,
CapabilityWater,
DeviceType,
)
from deebot_client.commands.json.advanced_mode import GetAdvancedMode, SetAdvancedMode
from deebot_client.commands.json.battery import GetBattery
from deebot_client.commands.json.carpet import (
GetCarpetAutoFanBoost,
SetCarpetAutoFanBoost,
)
from deebot_client.commands.json.charge import Charge
from deebot_client.commands.json.charge_state import GetChargeState
from deebot_client.commands.json.clean import Clean, CleanArea, GetCleanInfo
from deebot_client.commands.json.clean_count import GetCleanCount, SetCleanCount
from deebot_client.commands.json.clean_logs import GetCleanLogs
from deebot_client.commands.json.clean_preference import (
GetCleanPreference,
SetCleanPreference,
)
from deebot_client.commands.json.continuous_cleaning import (
GetContinuousCleaning,
SetContinuousCleaning,
)
from deebot_client.commands.json.custom import CustomCommand
from deebot_client.commands.json.error import GetError
from deebot_client.commands.json.fan_speed import GetFanSpeed, SetFanSpeed
from deebot_client.commands.json.life_span import GetLifeSpan, ResetLifeSpan
from deebot_client.commands.json.map import (
GetCachedMapInfo,
GetMajorMap,
GetMapTrace,
GetMinorMap,
)
from deebot_client.commands.json.multimap_state import (
GetMultimapState,
SetMultimapState,
)
from deebot_client.commands.json.network import GetNetInfo
from deebot_client.commands.json.play_sound import PlaySound
from deebot_client.commands.json.pos import GetPos
from deebot_client.commands.json.relocation import SetRelocationState
from deebot_client.commands.json.stats import GetStats, GetTotalStats
from deebot_client.commands.json.true_detect import GetTrueDetect, SetTrueDetect
from deebot_client.commands.json.volume import GetVolume, SetVolume
from deebot_client.commands.json.water_info import GetWaterInfo, SetWaterInfo
from deebot_client.commands.json.work_mode import GetWorkMode, SetWorkMode
from deebot_client.const import DataType
from deebot_client.events import (
AdvancedModeEvent,
AvailabilityEvent,
BatteryEvent,
CachedMapInfoEvent,
CarpetAutoFanBoostEvent,
CleanCountEvent,
CleanLogEvent,
CleanPreferenceEvent,
ContinuousCleaningEvent,
CustomCommandEvent,
ErrorEvent,
FanSpeedEvent,
FanSpeedLevel,
LifeSpan,
LifeSpanEvent,
MajorMapEvent,
MapChangedEvent,
MapTraceEvent,
MultimapStateEvent,
NetworkInfoEvent,
PositionsEvent,
ReportStatsEvent,
RoomsEvent,
StateEvent,
StatsEvent,
TotalStatsEvent,
TrueDetectEvent,
VolumeEvent,
WorkMode,
WorkModeEvent,
water_info,
)
from deebot_client.models import StaticDeviceInfo
from deebot_client.util import short_name
from . import DEVICES
DEVICES[short_name(__name__)] = StaticDeviceInfo(
DataType.JSON,
Capabilities(
device_type=DeviceType.VACUUM,
availability=CapabilityEvent(
AvailabilityEvent, [GetBattery(is_available_check=True)]
),
battery=CapabilityEvent(BatteryEvent, [GetBattery()]),
charge=CapabilityExecute(Charge),
clean=CapabilityClean(
action=CapabilityCleanAction(command=Clean, area=CleanArea),
continuous=CapabilitySetEnable(
ContinuousCleaningEvent,
[GetContinuousCleaning()],
SetContinuousCleaning,
),
count=CapabilitySet(CleanCountEvent, [GetCleanCount()], SetCleanCount),
log=CapabilityEvent(CleanLogEvent, [GetCleanLogs()]),
preference=CapabilitySetEnable(
CleanPreferenceEvent, [GetCleanPreference()], SetCleanPreference
),
work_mode=CapabilitySetTypes(
event=WorkModeEvent,
get=[GetWorkMode()],
set=SetWorkMode,
types=(
WorkMode.MOP,
WorkMode.MOP_AFTER_VACUUM,
WorkMode.VACUUM,
WorkMode.VACUUM_AND_MOP,
),
),
),
custom=CapabilityCustomCommand(
event=CustomCommandEvent, get=[], set=CustomCommand
),
error=CapabilityEvent(ErrorEvent, [GetError()]),
fan_speed=CapabilitySetTypes(
event=FanSpeedEvent,
get=[GetFanSpeed()],
set=SetFanSpeed,
types=(
FanSpeedLevel.QUIET,
FanSpeedLevel.NORMAL,
FanSpeedLevel.MAX,
FanSpeedLevel.MAX_PLUS,
),
),
life_span=CapabilityLifeSpan(
types=(LifeSpan.BRUSH, LifeSpan.FILTER, LifeSpan.SIDE_BRUSH),
event=LifeSpanEvent,
get=[GetLifeSpan([LifeSpan.BRUSH, LifeSpan.FILTER, LifeSpan.SIDE_BRUSH])],
reset=ResetLifeSpan,
),
map=CapabilityMap(
cached_info=CapabilityEvent(CachedMapInfoEvent, [GetCachedMapInfo()]),
changed=CapabilityEvent(MapChangedEvent, []),
major=CapabilityEvent(MajorMapEvent, [GetMajorMap()]),
minor=CapabilityExecute(GetMinorMap),
multi_state=CapabilitySetEnable(
MultimapStateEvent, [GetMultimapState()], SetMultimapState
),
position=CapabilityEvent(PositionsEvent, [GetPos()]),
relocation=CapabilityExecute(SetRelocationState),
rooms=CapabilityEvent(RoomsEvent, [GetCachedMapInfo()]),
trace=CapabilityEvent(MapTraceEvent, [GetMapTrace()]),
),
network=CapabilityEvent(NetworkInfoEvent, [GetNetInfo()]),
play_sound=CapabilityExecute(PlaySound),
settings=CapabilitySettings(
advanced_mode=CapabilitySetEnable(
AdvancedModeEvent, [GetAdvancedMode()], SetAdvancedMode
),
carpet_auto_fan_boost=CapabilitySetEnable(
CarpetAutoFanBoostEvent,
[GetCarpetAutoFanBoost()],
SetCarpetAutoFanBoost,
),
true_detect=CapabilitySetEnable(
TrueDetectEvent, [GetTrueDetect()], SetTrueDetect
),
volume=CapabilitySet(VolumeEvent, [GetVolume()], SetVolume),
),
state=CapabilityEvent(StateEvent, [GetChargeState(), GetCleanInfo()]),
stats=CapabilityStats(
clean=CapabilityEvent(StatsEvent, [GetStats()]),
report=CapabilityEvent(ReportStatsEvent, []),
total=CapabilityEvent(TotalStatsEvent, [GetTotalStats()]),
),
water=CapabilityWater(
amount=CapabilitySetTypes(
event=water_info.WaterAmountEvent,
get=[GetWaterInfo()],
set=SetWaterInfo,
types=(
water_info.WaterAmount.LOW,
water_info.WaterAmount.MEDIUM,
water_info.WaterAmount.HIGH,
water_info.WaterAmount.ULTRAHIGH,
),
),
mop_attached=CapabilityEvent(water_info.MopAttachedEvent, [GetWaterInfo()]),
),
),
)
```

76
07-Other/生活/租房.md Normal file
View File

@@ -0,0 +1,76 @@
## 平台
- 链家
- 自如
## 注意点:
- 马桶
- 马桶水压够不够
- 公寓:马桶是粗管还是细管
- 费用
- 水电费是自己交还是给房东
- 水电表在哪看
- 房子有没有网,能不能拉网?哪些公司的网可以啦?
- 要不要物业费?物业费多少?
- 房租是押几付几
- 合同
- 合同写明多几个沙发几个电视以及物品损坏赔偿事宜,如果损坏了如何赔偿
- 能不能换锁
- 退房手续,退房是不是退了之后押金马上到账,需写明多少天到账
- 清洁费
- 身份证复印件**仅供本次租房使用**
- 需要房东出示房产证与身份证,需要拍照
- 因为第一次租没啥经验,而且又是学校的家属区,就自以为很安全,不会有什么幺蛾子,就没看她房产证,结果这房子是她前夫的,她俩离婚的时候就判给他前夫了,然后没办法我又去找他前夫协商,乱了差不多一个多月才搞定,真是无语了
- 合同注意
- 如何付款
- 水表、电表数值写在合同上
- 确认合同期限
- 租房的地址写在合同上
- 电费、水费写在合同上,并且确认上一个租客的水电费是否结清
- WC卫生、水压
- 维修相关
- 马桶堵了
- 灯泡坏了
- 热水器不出热水了
- 违约了怎么
- 房东提前收回违约
- 房客提前退租违约
- 签完合同之后
- 去房子里拍摄全景视频,记录所有的破损包括电器损坏、墙面脱落、灯泡与电器损坏、家具、玻璃破碎。之后发给中介或者房东,没让他们去解决
- 检查所有插座面板、空调(制冷制热)、灯、冰箱、抽油烟机、地漏是否可以正常工作
- 洗手池水槽是否够深是否正常
- 卧室查看床是否结实
- 确定以上问题解决之后,再付押金与租金
- 附近设施:菜场、超市、快递柜
# 需要的东西
- 洗漱
- [x] 牙刷、牙杯、牙膏
- [x] 3块毛巾
- [x] 洗面奶/肥皂/洗发水/沐浴露
- [x] 洗澡球
- [x] 剃须刀/泡沫
- [x] 晾衣杆/衣架/无印良品同款衣物(内衣)收纳盒/换洗衣服 收纳(竹子)
- [x] 洗衣粉(洗衣球)
- [x] 脸盆
- [x] 吹风机
- 衣服
- 内裤/衣服/被子/垫被
- 烹饪
- [x] 3~4个碗/2个盘子
- [x] 洗碗布/洗洁精
- [x] 烧水壶
- 卫生清洁
- [x] 洁厕灵
- [x] 抹布2块/扫帚/拖把
- [x] 垃圾桶/垃圾袋
- 电脑相关
- [x] 显示器
- [x] 台灯
- [x] 耳塞
- 其他
- [ ] 玫瑰花茶
- [ ] 电磁炉
- [ ] 蒸煮锅
- [ ] 水饺/牛肉卷/汉堡
- [ ] 哑铃
- [ ] 健身用衣服

BIN
07-Other/生活/装修/圆子.jpg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
07-Other/生活/装修/圆子的家-装修设计需求.pptx (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
07-Other/生活/装修/开关插座-便利贴.pdf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
07-Other/生活/装修/微信图片_20240222124703.jpg (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
07-Other/生活/装修/水电验收表.pdf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
07-Other/生活/装修/装修流程大全.pdf (Stored with Git LFS) Normal file

Binary file not shown.

View File

@@ -0,0 +1,382 @@
---
excalidraw-plugin: parsed
tags: [excalidraw]
---
==⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠==
# Text Elements
弱电箱4孔排插、光猫、交换机。 ^8isV55uP
厨房台面插座各两个 ^7sVOjSFg
# Embedded files
df180a0e12b78ad2fa53a01c90d94d2597b6c003: [[Pasted Image 20240420210222_918.png]]
%%
# Drawing
```json
{
"type": "excalidraw",
"version": 2,
"source": "https://github.com/zsviczian/obsidian-excalidraw-plugin/releases/tag/2.1.4",
"elements": [
{
"type": "image",
"version": 789,
"versionNonce": 764974089,
"isDeleted": false,
"id": "zcuApeMV2QX3cij9--JHB",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": -706.7071237048208,
"y": -258.80754288575235,
"strokeColor": "transparent",
"backgroundColor": "transparent",
"width": 1089.5781185246583,
"height": 480.47368421052636,
"seed": 1196693447,
"groupIds": [],
"frameId": null,
"roundness": null,
"boundElements": [],
"updated": 1713618162961,
"link": null,
"locked": true,
"status": "pending",
"fileId": "df180a0e12b78ad2fa53a01c90d94d2597b6c003",
"scale": [
1,
1
]
},
{
"type": "text",
"version": 472,
"versionNonce": 1219856039,
"isDeleted": false,
"id": "8isV55uP",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": -176.74297119756858,
"y": 238.0608781668792,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"width": 312.79998779296875,
"height": 25,
"seed": 893160585,
"groupIds": [],
"frameId": null,
"roundness": null,
"boundElements": [],
"updated": 1713618167895,
"link": null,
"locked": false,
"fontSize": 20,
"fontFamily": 1,
"text": "弱电箱4孔排插、光猫、交换机。",
"rawText": "弱电箱4孔排插、光猫、交换机。",
"textAlign": "left",
"verticalAlign": "top",
"containerId": null,
"originalText": "弱电箱4孔排插、光猫、交换机。",
"lineHeight": 1.25
},
{
"type": "line",
"version": 121,
"versionNonce": 471233895,
"isDeleted": false,
"id": "XEOP3tBbRIuTaJtGmpDFu",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": -21.82849751335789,
"y": 235.37666764056343,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"width": 77,
"height": 132,
"seed": 1690365833,
"groupIds": [],
"frameId": null,
"roundness": {
"type": 2
},
"boundElements": [],
"updated": 1713618349508,
"link": null,
"locked": false,
"startBinding": null,
"endBinding": null,
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": null,
"points": [
[
0,
0
],
[
77,
-132
]
]
},
{
"id": "7sVOjSFg",
"type": "text",
"x": 121.71829919387187,
"y": -290.0009240659757,
"width": 180,
"height": 25,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"roundness": null,
"seed": 1729638567,
"version": 12,
"versionNonce": 2015071111,
"isDeleted": false,
"boundElements": null,
"updated": 1713619944853,
"link": null,
"locked": false,
"text": "厨房台面插座各两个",
"rawText": "厨房台面插座各两个",
"fontSize": 20,
"fontFamily": 1,
"textAlign": "left",
"verticalAlign": "top",
"containerId": null,
"originalText": "厨房台面插座各两个",
"lineHeight": 1.25
},
{
"id": "IndZR9XWUGZwXLa7ySitf",
"type": "line",
"x": 208.99102646659924,
"y": -263.33425739930897,
"width": 0,
"height": 251.5151515151515,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"roundness": {
"type": 2
},
"seed": 1079350727,
"version": 115,
"versionNonce": 333529865,
"isDeleted": false,
"boundElements": null,
"updated": 1713619957200,
"link": null,
"locked": false,
"points": [
[
0,
0
],
[
0,
251.5151515151515
]
],
"lastCommittedPoint": null,
"startBinding": null,
"endBinding": null,
"startArrowhead": null,
"endArrowhead": null
},
{
"id": "TragiTpWhCklJa5fZuOZw",
"type": "line",
"x": 210.20314767872048,
"y": -262.72819679324834,
"width": 30.303030303030255,
"height": 233.33333333333326,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"roundness": {
"type": 2
},
"seed": 605202791,
"version": 134,
"versionNonce": 1732225033,
"isDeleted": false,
"boundElements": null,
"updated": 1713619965506,
"link": null,
"locked": false,
"points": [
[
0,
0
],
[
30.303030303030255,
233.33333333333326
]
],
"lastCommittedPoint": null,
"startBinding": null,
"endBinding": null,
"startArrowhead": null,
"endArrowhead": null
},
{
"type": "text",
"version": 725,
"versionNonce": 1180359849,
"isDeleted": true,
"id": "rBlbkrxw",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 152.1715024866421,
"y": -289.1233323594366,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"width": 220,
"height": 25,
"seed": 1593870889,
"groupIds": [],
"frameId": null,
"roundness": null,
"boundElements": [],
"updated": 1713619018694,
"link": null,
"locked": false,
"fontSize": 20,
"fontFamily": 1,
"text": "水槽下预留插座给净水器",
"rawText": "水槽下预留插座给净水器",
"textAlign": "left",
"verticalAlign": "top",
"containerId": null,
"originalText": "水槽下预留插座给净水器",
"lineHeight": 1.25
},
{
"type": "line",
"version": 353,
"versionNonce": 579008903,
"isDeleted": true,
"id": "eMzAQa1esr9PFX38aViYh",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 332.6715024866421,
"y": -258.6233323594366,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"width": 100.5,
"height": 174,
"seed": 546243463,
"groupIds": [],
"frameId": null,
"roundness": {
"type": 2
},
"boundElements": [],
"updated": 1713619021154,
"link": null,
"locked": false,
"startBinding": null,
"endBinding": null,
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": null,
"points": [
[
0,
0
],
[
-100.5,
174
]
]
}
],
"appState": {
"theme": "light",
"viewBackgroundColor": "#ffffff",
"currentItemStrokeColor": "#1e1e1e",
"currentItemBackgroundColor": "transparent",
"currentItemFillStyle": "solid",
"currentItemStrokeWidth": 2,
"currentItemStrokeStyle": "solid",
"currentItemRoughness": 1,
"currentItemOpacity": 100,
"currentItemFontFamily": 1,
"currentItemFontSize": 20,
"currentItemTextAlign": "left",
"currentItemStartArrowhead": null,
"currentItemEndArrowhead": "arrow",
"scrollX": 742.8271553515825,
"scrollY": 415.4554695205211,
"zoom": {
"value": 1.6500000000000001
},
"currentItemRoundness": "round",
"gridSize": null,
"gridColor": {
"Bold": "#C9C9C9FF",
"Regular": "#EDEDEDFF"
},
"currentStrokeOptions": null,
"previousGridSize": null,
"frameRendering": {
"enabled": true,
"clip": true,
"name": true,
"outline": true
}
},
"files": {}
}
```
%%

View File

@@ -0,0 +1,56 @@
1. 考虑相关电器的插座正倒装。
2. 扫地机器人位置以及上下水。
# 厨房
1. 主要电器:
1. 冰箱。
2. 油烟机。
3. 洗碗机。
4. 燃气热水器。
2. 设备井中央空调外机。
3. 台面上:
1. 电饭煲
2. 蒸锅
3. 烧水壶
4. 水槽下:预留插座给前置净水器、净水器。以及水管。
# 卫生间
1. 主要电器:
1. 风暖浴霸
2. 电热毛巾架
2. 洗手台预留1~2个插座。
3. 镜柜内留2个插座考虑给电动牙刷或者电动剃须刀充电。
4. 马桶一侧留1个插座考虑给智能马桶垫供电。
5. 马桶对面开关与插座。
# 客厅
1. 主要电器:
1. 电视机
2. 靠近次卧的餐边柜上需要放置若干插座,以及保留轨道插座。
3. 电视机柜若干
1. 电视机
2. AppleTV
3. NAS
4. UPS
5. 路由器
4. 弱电箱
1. 光猫
2. 交换机
5. 沙发附近
6. 窗帘盒附近留插座,保证监控与智能电动窗帘。
7. 网口
1. 电视机柜3~4个
2. 次卧餐边柜1个
3. 鞋柜上1个
4. 玄关口1个智能网关
# 次卧
1. 飘窗:两边各一个插座。
2. 网口:书桌二个;飘窗处一个。
# 主卧
1. 窗的左右两边放置若干。
2. 网口:床的两边各放一个。
# 阳台
1. 主要电器:
1. 洗衣机、烘干机。
2. 电动晾衣架。
3. 小厨宝。

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
07-Other/生活/装修/装修这些东西要提前购买.pdf (Stored with Git LFS) Normal file

Binary file not shown.

View File

@@ -0,0 +1,450 @@
# 已看视频
- 住范儿
- [x] [装修小白扫盲指南](https://space.bilibili.com/454560170/channel/collectiondetail?sid=38426 "装修小白扫盲指南")
- [x] ~~合集·真实人家·装修全记录~~没有意义
- [x] [BUY家攻略](https://space.bilibili.com/454560170/channel/collectiondetail?sid=705850 "BUY家攻略")
- [x] [小白装修指南 · 新手必看](https://space.bilibili.com/454560170/channel/seriesdetail?sid=338011)
- 阳仔的装修思路
- [x] [合集·3分钟装修知识科普](https://space.bilibili.com/22464649/channel/collectiondetail?sid=2025328)
- [x] 【【阳仔】无 废 话 速 通 版 装 修】 https://www.bilibili.com/video/BV1DZ421z7w6/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- [x] 【【阳仔】第二期丨 无 废 话 速 通 版 装 修】 https://www.bilibili.com/video/BV11r421s7BK/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- [x] [好看却垃圾的网红家](https://space.bilibili.com/22464649/channel/collectiondetail?sid=1689365)
- [x] [装修家电知识](https://space.bilibili.com/22464649/channel/collectiondetail?sid=1719265 "装修家电知识")
- [ ] [装修净水器知识](https://space.bilibili.com/22464649/channel/collectiondetail?sid=1720855 "装修净水器知识")
- [ ] [装修封阳台知识](https://space.bilibili.com/22464649/channel/collectiondetail?sid=1720830 "装修封阳台知识")
- [x] 装修极简知识
- [小东聊家装](https://space.bilibili.com/1881219345):https://www.bilibili.com/video/BV1vJ4m1a7Tg/?spm_id_from=333.1007.top_right_bar_window_default_collection.content.click&vd_source=d47c0bb42f9c72fd7d74562185cee290
-
- [诡计从不拖更](https://space.bilibili.com/96781097)https://www.bilibili.com/video/BV1Tq4y1m7cu/?spm_id_from=333.999.0.0&vd_source=d47c0bb42f9c72fd7d74562185cee290
- [科技宅小明](https://space.bilibili.com/5626102):https://www.bilibili.com/video/BV1K84y1o7bz/?spm_id_from=333.999.0.0&vd_source=d47c0bb42f9c72fd7d74562185cee290
- 合集·识屋·爆改:沉浸式体验装修全过程! https://www.bilibili.com/video/BV1gA4m137hp/?spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=d47c0bb42f9c72fd7d74562185cee290
- [设计师一修](https://space.bilibili.com/3461566464789457):https://www.bilibili.com/video/BV1Pv421y7PR/?spm_id_from=333.788.top_right_bar_window_history.content.click&vd_source=d47c0bb42f9c72fd7d74562185cee290
- Mr迷瞪:https://space.bilibili.com/304578055
# 验收
相机+稳定器 拍摄全景照片Windows PTGui。
毛坯房验房:
- 【毛坯房要这样验15步全攻略毛坯房验收大总结准备装修的你一定要看完】 https://www.bilibili.com/video/BV1Tx4y1u7oD/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
装修注意:
- 【装修避坑指南】 https://www.bilibili.com/video/BV1eJ4m1p7BN/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
装修验收
- 【【阳仔】这些装修节点,师傅最不喜欢你来工地盯着!】 https://www.bilibili.com/video/BV1GU421o7NV/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 【【阳仔】第2期丨这些装修节点你最好来工地盯着】 https://www.bilibili.com/video/BV1nZ42187T2/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 水电验收
- 【【阳仔】装修水电完工后,你家有没有做水管试压?】 https://www.bilibili.com/video/BV1xx4y1o7VM/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 【【装修宝典】施工交底到底在交什么?土改要注意哪些?】 https://www.bilibili.com/video/BV1ei4y1v7qN/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 【做全屋智能一定要和水电师傅交代的事情,否则一不小不仅预留不到位,还会被掏兜!】 https://www.bilibili.com/video/BV1EC411t7mF/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- [[智能家居水电交底.png]]
# 装修规划
10W硬装+10W软装+5W电器+5W备用金
- 厨房
- 电器 30300
- 油烟机:美的 CXW-140-YL28   **¥6999** 宽896mm 深 360mm 高960mm 顶部宽407mm
- 燃气灶美的烟灶套餐JZT-Q69-M + CXW-140-YL28 **¥6999**
- 冰箱400L左右
- 海尔 黑色 410WLHC245S1U1 宽595mm深682mm高1925mm ¥ 3679
- 海尔 白色 410WLHC214GXU1 宽595mm深682mm高1925mm ¥4999
- 美的 白色 400升占用面积更小一些 BCD-400WUTGPZM(E)玉霜白 ¥ 4999 宽643mm深600mm高1910mm
- 燃气热水器:~~A.O.Smith JSQ31-ESCX ¥**5958**~~
- A.O.SmithJSQ31-TM5 2021年款 ¥**2998**
- A.O.SmithJSQ31-TM5WI 2023年款¥**3598.00** 长350mm宽162mm高550mm
- 美的JSQ30-M9 Max¥**3299** 唯一的缺点就是换热器是无氧铜而AO史密斯是不锈钢。长575mm宽370mm高190mm
- 美的JSLQ27-16LN7 Pro ¥**3499** 长370mm宽180mm高600mm
- 林内JSQ31-GD32
- 电蒸烤一体机:
- 2024年西门子、凯度、美的微蒸烤一体机怎么选 https://zhuanlan.zhihu.com/p/675246825
- 【【蒸烤箱选购】蒸烤箱怎么选不踩坑5个蒸烤箱品牌25分钟全面解析】 https://www.bilibili.com/video/BV1Vu4y137ag/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 美的
- ~~R6 ¥6699 长560mm宽550mm高450mm 55L ~~
- 凯度
- 52L嵌入式微蒸烤 双热风 11.26英寸臻彩屏 微蒸烤炸炖5合1 变频微波 蒸烤箱一体机 SR52FW1-ZRPro ¥ 6999.00 长560mm宽540mm高450mm
- 凯度CASDON52L嵌入式微蒸烤 双热风 微蒸烤炸炖5合1 WIFI智控 变频微波 蒸烤箱一体机 SR52BW1-GRPro ¥ 5699 长665mm宽665mm高584mm
- 电饭煲:
- 洗碗机:
- 美的GX1000Pro 6299 600 * 570 * 775
- 净水机/管线机(考虑桌面式既热饮水机)
- 海尔Haier净水器1200G鲜活水 HKC3000-R793D2U1 ¥**2699** ,一个出水口。
- 滤芯更换成本299 / 2 年999 / 6年
- 美的净水机星河1200G PRO ¥**3099** 一个链接水龙头一个链接管线机,**是否是两个出水口存疑,买的时候得确定**
- 滤芯更换成本298 / 2 年1098 / 6年
- 卫生间
- 电器 ¥1600
- 风暖浴霸
- 奥普E172入门款 ¥399
- 雷士NVC真双风口取暖/换气浴霸暖风照明排气一体机浴室电暖器集成吊顶 ¥599带米家智控的 ¥799
- 电热毛巾架
- 京东京造暖云Plus电热毛巾架 ¥799
- 铝合金玻璃门
- 蜂窝大板吊顶(铝扣板不好看)
- 客厅
- 电器
- 中央空调
- 美的高端款Colom 24500 (五星电器 各种活动最低价)
- 电视机
- TCL
- TCL75T7H ¥5599
- TCL75Q9K  ¥7499
- 电子锁:
- 绿米
- Aqara联创智能门锁A100 pro ¥ 1999
- Aqara全自动智能猫眼锁H100 ¥ 2999
- 智能开关 & 智能网关:
- 绿米
- M2网关 ¥399
- M3网关 ¥989
- Aqara联创智能墙壁开关D1 ¥169 * 4
- Aqara卡农Z1Pro四键版¥349
- 人体传感器 ¥99 * 2
- Aqara绿米联创星云吸顶灯 https://item.jd.com/10093040812074.html#crumb-wrap
- Aqara绿米联创智能防眩射灯LED明装筒射灯控制吊顶灯 ¥269
- 柔光砖/亮光转(容易打滑)
- 主卧
- 电器 17050
- 洗烘套装(待确定):
- 海尔 H9 Y12BLD12U1 内筒525mm ¥11498
- 小天鹅 小乌梅 TG100RVIC + S83 内筒535mm 10kg+10kg¥9999
- 高级版 11499
- 电动晾衣架米家智能晾衣机1S电动晾衣架 ¥949
- 扫地机器人:
- 从去年测到今年追觅X30Pro 科沃斯T30 云鲸J4 石头P10Pro体验 https://www.bilibili.com/video/BV14m411f7wr/?spm_id_from=333.337.search-card.all.click&vd_source=d47c0bb42f9c72fd7d74562185cee290
- 科沃斯扫地机器人T30PRO+上下水模块 ¥4599
- T30 Pro **预留 500 * 500 * 600* **
- X5 Pro **预留 500 * 520 * 770**
- 追觅扫地机器人X40 Pro自动上下水版 ¥ 6699 340 * 456.7 * 590.5
- 封阳台
- **2W+若干石材铺贴费用**
- 次卧
- 可伸缩飘窗书桌
- 承重飘窗不能砸 DIY飘窗书桌太香啦!-哔哩哔哩:https://b23.tv/jGOIdG9
# 硬装与风格
***设计阶段就需要考虑买的电器并把大小算进去精确到mm。***
- 装修合同
- 【【阳仔】装修签合同注意这6点可避免95%的坑!】 https://www.bilibili.com/video/BV1zh4y1Y7E5/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 【【阳仔】装修前,最好能让装修公司答应这些要求!】 https://www.bilibili.com/video/BV1Dj421S7HH/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 设计师:最好是需要懂施工的。
- 设计风格:意式极简(奶油配色)现代极简(奶油配色)
- ***原木奶油风(使用的地板颜色与墙体乳胶漆颜色)***
- 【原木奶油风其实很简单】 https://www.bilibili.com/video/BV13P411p7zt/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- https://www.xiaohongshu.com/explore?app_platform=android&app_version=7.95.0&author_share=1&share_from_user_hidden=true&type=normal&xhsshare=WeixinSession&appuid=5e3c28ed00000000010006df&apptime=1713708245&wechatWid=bd211683351d3525425e2ff823a00b27&wechatOrigin=menu&target_note_id=64328ca000000000130301d6&note_flow_source=wechat
- 意式奶油风:https://www.bilibili.com/video/BV15q4y1B7SG/?spm_id_from=333.788&vd_source=d47c0bb42f9c72fd7d74562185cee290
- 考虑奶油风色调。需要获取到色卡。
- 这个很不错 https://www.bilibili.com/video/BV1ci4y1B7j1/?spm_id_from=333.337.search-card.all.click&vd_source=d47c0bb42f9c72fd7d74562185cee290
- 舒舒喜欢的
- https://www.xiaohongshu.com/explore/64dcad68000000000800c17d?note_flow_source=wechat
- 现代奶油风
- 意式极简风
- 意式轻奢风
- 我觉得可以的
- 【实锤了! 果然是~越简单越高级🤣】 https://www.bilibili.com/video/BV1wu4m1w7ro/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 配色方案 https://www.bilibili.com/video/BV1Vv4y1V7f9/?spm_id_from=333.337.search-card.all.click&vd_source=d47c0bb42f9c72fd7d74562185cee290
- https://www.bilibili.com/video/BV1np4y1A7Ei/?spm_id_from=333.788&vd_source=d47c0bb42f9c72fd7d74562185cee290
- https://www.bilibili.com/video/BV1Wp4y1j7FH/?p=7&spm_id_from=pageDriver
- https://www.bilibili.com/video/BV1Cu4y1z7YL/?spm_id_from=333.788&vd_source=d47c0bb42f9c72fd7d74562185cee290
- 法式奶油风:
- https://www.bilibili.com/video/BV1bT411C7fb/?spm_id_from=333.337.search-card.all.click&vd_source=d47c0bb42f9c72fd7d74562185cee290
- 现代风:
- 【【阳仔】房子设计到一半,设计师离家出走了吗?】 https://www.bilibili.com/video/BV1uj421Z7LH/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 板材使用ENF级别板材甲醛释放量只有0.025mg/m3
- https://www.bilibili.com/video/BV1Fx4y127P2/?spm_id_from=333.1007.tianma.6-4-22.click&vd_source=d47c0bb42f9c72fd7d74562185cee290
- 干湿问题:https://www.bilibili.com/video/BV1W34y1X7bJ/?spm_id_from=333.788&vd_source=d47c0bb42f9c72fd7d74562185cee290
- 厨房
- 考虑使用**奶白色石英石台面**
- 【卖橱柜的都不做饭吗从来没人告诉你这3个设计细节】 【精准空降到 04:29】 https://www.bilibili.com/video/BV1MC4y1A74X/?p=11&share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e&t=269
- 【【阳仔】厨房收纳量翻倍,都靠这些小心思!】 https://www.bilibili.com/video/BV15u4m1A7RG/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 案例:
- 【🏡砸墙做的开放式厨房,使用半年我后悔了吗?】 https://www.bilibili.com/video/BV1Zi42197qp/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 卫生间
- 轻松解决卫生间的潮湿异味💩暗卫必看!: https://www.bilibili.com/video/BV14K411M7u2/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e&t=79
- 卫生间的门需要安装百叶或者下面留缝,不然换气扇抽不动。
- 关闭马桶盖再冲厕所。
- 布线&WiFi
- 【【阳仔】家中这13处地方加上插座生活超级方便】 https://www.bilibili.com/video/BV1gx4y1e7T7/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- https://www.bilibili.com/video/BV1RB4y1j7wK/?p=5&spm_id_from=pageDriver
- ***装修最怕遗漏的20处插座*** https://www.xiaohongshu.com/explore/6606bcde00000000140055e9
- 使用Wifi Man APP测试WiFi信号强度。还有OpenSpeedTest、SpeedTest、全球网测
- 衣柜
- 砌墙衣柜好处与施工事项:https://www.bilibili.com/video/BV1wY4y197q5/?spm_id_from=333.788&vd_source=d47c0bb42f9c72fd7d74562185cee290
- 砌墙衣柜。可以自由改变格局,可以放入更多的东西。
- 不推荐半开放式的砌墙衣柜(杭州太潮湿了),可以考虑封闭式的。
- 品牌铂耐、BOAXEL、美达斯。
- 橱柜:
- 地柜高度:常做饭人的身高 / 2 + 5~10cm
- 吊柜高度比地柜高50~60cm
- 隔音&封阳台:
- 【【阳仔】只花几百块,就能提升全屋隔音效果的办法!】 https://www.bilibili.com/video/BV1vP41187fb/?p=20&share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 卧室使用**双层隔空夹胶玻璃**
- 【【装修宝典】窗户要不要换?到底什么是系统窗?】 https://www.bilibili.com/video/BV1kg4y1d7QA/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- **【注意断桥铝封阳台一定要注意这5点**-哔哩哔哩】  https://b23.tv/XpA8BvZ
- 系统窗配置:
- 现场验货:
- 和商家约定现场打开角码并且测量壁厚。
- 玻璃上的3C认证。用指甲扣一下看一下是否可以扣掉。
- 五金件:
- 国产HOPO深圳好博、坚朗、斯坦福、维哈根、希美克、派阁、合和、春光、国强、立兴、悍高。
- 进口索宾柯SOBINCO、诺托ROTO、德国好博HOPPE、格屋GU、丝吉利娅、吉斯GIESSE、威必驰VBH、迈柯MACO、海福乐HAFELE、福适博FBS
- 宽度90#以上,具体看层高与玻璃面积
- 最薄宽度多多少国标是1.8mm老国标1.4mm
- 玻璃:南玻,双层隔空夹胶玻璃,超白玻,保证所有玻璃没有色差。
- Low-e镀膜
- 断桥尼龙条PA66尼龙条PVC为偷工减料
- 密封胶条:三元乙丙泡沫胶条
- 手柄:德国好博
- 固定螺丝:不锈钢拉爆螺丝
- 高空作业资质:商家证件、施工师傅的高空作业证、商业保险。
- 宜家攻略:https://www.bilibili.com/video/BV1Xh411C7Xg/?spm_id_from=333.788&vd_source=d47c0bb42f9c72fd7d74562185cee290
- 宜家最适合买基础款PAX衣柜、MALM抽屉柜、BILLY书柜、BESTA电视柜、METOD橱柜。
- 建议买深色的
- 建议买大号家具
## 问题避免
- 拱形门:https://www.bilibili.com/video/BV1sS4y1v7V1/?spm_id_from=333.999.0.0&vd_source=d47c0bb42f9c72fd7d74562185cee290
## 施工问题注意
- 安装空调:https://www.bilibili.com/video/BV1pN4y1L7fa/?spm_id_from=pageDriver&vd_source=d47c0bb42f9c72fd7d74562185cee290
- 门套不要做出头(需要师傅注意):https://www.bilibili.com/video/BV1c3411c7gH/?spm_id_from=333.788&vd_source=d47c0bb42f9c72fd7d74562185cee290
- 贴瓷砖注意事项:
- https://www.bilibili.com/video/BV1Li4y1a78g/?spm_id_from=333.788&vd_source=d47c0bb42f9c72fd7d74562185cee290
- https://www.bilibili.com/video/BV1UQ4y1G7Kj/?spm_id_from=333.788&vd_source=d47c0bb42f9c72fd7d74562185cee290
- https://www.bilibili.com/video/BV1zu4y1V7nz/?spm_id_from=pageDriver&vd_source=d47c0bb42f9c72fd7d74562185cee290
- https://www.bilibili.com/video/BV1Ca4y1o7k5/?p=11&spm_id_from=pageDriver
- 【【阳仔】美缝这样做,省钱!效果一级棒!】 https://www.bilibili.com/video/BV1HX4y1q7Pq/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 乳胶漆
- 是否环保:乳胶漆桶身上的检测标志。需要有绿色的中国环境标志认证 GB18582-2020 强制执行。
- [[建材_各国乳胶漆环保等级标准.png]]
- 【【阳仔】乳胶漆是否环保?秘密都在桶身上!】 https://www.bilibili.com/video/BV1cG411j7bZ/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 【【阳仔】揭秘进口乳胶漆的秘密,真假进口漆?!】 https://www.bilibili.com/video/BV17G4y1f7gC/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 【乳胶漆怎么选,几百元的和几千元的差别大吗?听听老师傅怎么说】 https://www.bilibili.com/video/BV15K421v7PT/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- **决定滚涂还是喷涂?**【【阳仔】千万别让油漆工帮你决定如何刷乳胶漆!】 https://www.bilibili.com/video/BV1Qt4y1Z7F5/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- **乳胶漆按比例加水**【【阳仔】乳胶漆加水后效果居然相差这么大?】 https://www.bilibili.com/video/BV1dQ4y157pd/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 杂类
- 【【阳仔】装修师傅的5句话句句能让你加钱整改】 https://www.bilibili.com/video/BV1gF4m1u7pK/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
## 其他考虑问题
- 厨房吊柜高度建议离底柜50~60cm
- 装修设计tip
- [[装修设计_鞋柜下沉做两层.jpg]]
- [[装修设计_洗烘套装加隔板与晾衣杆.jpg]]
- [[装修设计_轨道做成交错.jpg]]
## 后续关注事项
- 【【阳仔】装修内行人才知道的建材省钱平替关键词!】 https://www.bilibili.com/video/BV14z4y137jT/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 【【阳仔】家里这5个地方要经常检查关键时刻能保命】 https://www.bilibili.com/video/BV1nu411E7M3/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
# 电器
- 洗碗机
- https://www.bilibili.com/video/BV1DP4y1Z7Lt/?spm_id_from=333.999.0.0&vd_source=d47c0bb42f9c72fd7d74562185cee290
- 【【阳仔】双11入手洗碗机精准避雷看这一篇就够】 https://www.bilibili.com/video/BV1G8411k7yt/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 是否带有消杀模式75°水喷淋20分钟 + 2个紫外灯。
- 母婴认证
- 星级消杀认证
- 品牌考虑美的、西门子。4000~6000
- 美的U1
- **热机风干(有几个风机) & 自动开门**
- **内部材质**:需要全是不锈钢
- 是否是3层碗篮设计是否可以调节上层碗篮高度。
- 存碗时间
- 选完洗碗机再考虑橱柜。这样可以与橱柜保持面板统一。**门板厚度**与**颜色**
- 人多12套人少6套。
- 软水片 + 洗碗耗材
- 中央空调
- https://www.bilibili.com/video/BV1Ue411J7Sv/?spm_id_from=333.1007.tianma.1-1-1.click&vd_source=d47c0bb42f9c72fd7d74562185cee290
- 分为氟机与水机两种类型。
- 日系:**大金**、日立、东芝、三菱
- 美系:约克、开利、特灵、麦克维尔
- 国产:格力、美的、海信、海尔
- 出风口注意
- 【【阳仔】中央空调的出回风口,原来也有这么大的坑!】 https://www.bilibili.com/video/BV1E8411o7LC/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 保温棉问题导致漏水选择PE保温棉
- 【【阳仔】太离谱!中央空调因为保温棉漏水泡吊顶!】 https://www.bilibili.com/video/BV1cJ4m1L7Lv/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 洗衣机
- 注意滚筒大小。
- 需要有正反转
- 智能家居
- 施工注意
- 【一个视频帮你搞定全屋智能装修,保姆级施工避坑指南,发给你的水电工看,保证不多花一分冤枉米#全屋智能 】 https://www.bilibili.com/video/BV1Hj421S7ZZ/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 【做全屋智能一定要和水电师傅交代的事情,否则一不小不仅预留不到位,还会被掏兜!】 https://www.bilibili.com/video/BV1EC411t7mF/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 【不到2000实现你的米家全屋智能梦】 https://www.bilibili.com/video/BV1Aw4m1o7sv/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 推窗器
- 【【开箱】出门下雨!忘记关窗?领普雨水传感器+智能推窗器:暴雨克星!】 https://www.bilibili.com/video/BV13h4y1z7nT/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 网关
- 品牌
- HomeKitAqara、易来
- 一定要接入的电器空调、智能控制开关智能插座、AppleTV电视机顶盒、扫地机器人。
- 最好要接入的电器:空气净化器、加湿器、智能浴霸、晾衣架。
- 智能家居装修时需要注意:
- 灯光设计
- 【三句话讲明白全屋智能照明不到500元就能升级豪宅照明体验的攻略】 https://www.bilibili.com/video/BV1YD4y1V7e3/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 布线与电位(主路由网线需要有来回,尽可能多留网口、开关盒留零线、多留插座)
- HomeAssistant
- 【各种智能家居接入苹果家庭App/HomeKit超详细30分钟保姆级教程分享】 https://www.bilibili.com/video/BV1mm4y157n1/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 烘干机
- 需要有正反转
- 海尔、美的
- 建议5000以上
- 油烟机:
- 考虑风量。集成灶18~22m3/min普通油烟机25+
- 冰箱
- 嵌入式冰箱
- 【【阳仔】设计师不会告诉你的,关于嵌入式冰箱的内幕】 https://www.bilibili.com/video/BV1da41147ea/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 嵌入式直饮机/管线机(要买无水箱的)
- 【花30W测净水器结果不如烧开水】 https://www.bilibili.com/video/BV1Lw4m1f77P/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 海尔 鲜活水Pro 1200G 2000~3000 RMB
- 蒸烤一体机
- 微蒸烤一体机,需要自动供水。
- 可以考虑 美的5055
- 电热毛巾架
- 前置物架与挂杆都需要能加热的
- 碳纤维加热
- 防腐低碳钢
- 防水 IPX4
- 定时开关
# 建材
- 【**装修省钱网购你就这么搜,内行人才知道的家居源头产地!**】 https://www.bilibili.com/video/BV1Dq421w7q9/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 建材参考
- https://www.bilibili.com/video/BV1ix421f7Z9/?spm_id_from=333.1007.tianma.8-1-27.click&vd_source=d47c0bb42f9c72fd7d74562185cee290
- 美缝
- 板材
- 密度板(纤维板):易于雕刻造型、受潮容易膨胀变形。移动家居的底板、背板、有造型的门板。
- 刨花板(颗粒板、实木刨花板):结构稳定不易变形、握钉力较差。大部分定柜体和柜门。
- 胶合板(层压板、多层板):耐潮湿、大尺寸易弯曲变形。底板、浴室柜、移动家居。
- 欧松板(奥松板、定向刨花板、顺芯板):握钉力强、较少做饰板。建筑墙体搭建、代替细木工板的使用。
- 实木拼板(实木指接板、实木直拼板):握钉力强、拥有真实的木材纹理。移动家具、桌面。
- 视频
- 【【阳仔】一口气讲完全屋定制里6种板材的优缺点和价格段】 https://www.bilibili.com/video/BV1dC4y1o7FB/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 全屋定制
- 一次安装成功率
- 小红书全屋定制攻略
- 15家杭州全屋定制探店笔记 http://xhslink.com/lXLAEF
- 全屋定制我劝大家一定要慎重再慎重!记全屋定制过程 https://zhuanlan.zhihu.com/p/447043964?utm_psn=1759855198431031296
- 南京全屋定制30问:https://zhuanlan.zhihu.com/p/504571823
- **柜子**
1、背板多厚、加厚要加多少钱、如何收费
2、每平米的报价中除了板材外还包含什么基础五金、需要增加基础五金的话报价是多少
3、套餐内增加抽屉是否要加钱、格子抽屉是怎么收费的
4、铰链和轨道分别是什么品牌可不可以自己买
5、超出了规定的板材数量如何收费
6、衣柜一门到顶设计是否要加拉直器、拉直器价格
7、展示柜中的灯带可以自购还是送
8、衣柜的拉手标配、做一门到顶的换别的款式ok吗收不收费
9、基础五金/功能五金自购或者外采,安装费怎么算
10、穿衣镜、裤架收费或者款式更换
11、顶封板有没有算在内收费标准
12、门板设计隐藏拉手要多少钱
13、见光板算不算在门板价格
- **定制过程**
1、整套定制时间、确定安装时间逾期可赔偿
2、L型转角位置测量方法重复计费
2、安装费、上楼费合同写不写明
3、售后质保几年、质保内维修收费情况、质保外维修收费
4、安装之后效果跟预期不符如何处理部分重做收费
5、全部装完后味道较重甲醛含量测量谁负责
- 图片
- ![南京定制品牌](https://pic4.zhimg.com/v2-94c289b10954e18334134ddeaf2ea13f_r.jpg)
- ![](https://pic4.zhimg.com/80/v2-82930989a7ae85d2e03e02e20e4571d7_720w.webp)
- ![](https://pic3.zhimg.com/80/v2-2dcebc806255f9fce7afd647d53dbcb2_720w.webp)
- ![](https://pic3.zhimg.com/80/v2-40f3a9494941b49a0837be20cd151fde_720w.webp)
- ![](https://pic2.zhimg.com/80/v2-8183acd5430382973487986758449fa5_720w.webp)
- 75 【一期学会全屋定制谈判至少帮你省2W❗ - 春光装修学堂 | 小红书 - 你的生活指南】 😆 TcIJ8Qqm7u8rpSt 😆 http://xhslink.com/05slFF
- 2 【杭州全屋定制总结 - 数羊 | 小红书 - 你的生活指南】 😆 lXpb37lmF6s7dm8 😆 http://xhslink.com/riMlFF
- 视频
- 【「全屋定制」看不完算你亏,讲不清算我输】 https://www.bilibili.com/video/BV11m411Q75k/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 【全屋定制的「优等生」工厂到底该有多优秀】 https://www.bilibili.com/video/BV1tm411k775/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 【【装修宝典】全屋定制的配置怎么选?购买渠道有哪些?】 https://www.bilibili.com/video/BV1Be411B7S1/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 厨房台面和可以跟着窗台板过门石找石材厂定;玻璃门可以跟着卖厨卫推拉门的厂家定。
- 问清楚哪些是加钱项,哪些是包含项。
- 【【装修宝典】全屋定制该怎么选?板材选择需要注意哪些?】 https://www.bilibili.com/video/BV1s64y1W7LP/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 收纳设计师
- 参观工厂
- 板材库
- 全屋定制的板材需要有相关**板材企业的授权书**、五金厂授权书
- 欧派的板材是代工的
- 板材
- 没必要追求激光封边
- 国外(一平米 1500~2000
- 艾格
- 克诺斯邦
- 可丽芙
- 住友
- 国内(一平米 800~1200
- 万华
- 鲁丽
- 福人
- 芦花
- 天坛
- 露水河
- 五金件
- 国外
- 百隆
- 海蒂诗
- 萨郦奇
- 格拉斯
- 海福乐
- 国内
- 悍高
- 东泰
- 顶固
- 诺米
- 亚当斯
- 石英石台面
- 阳台规划案例:https://www.xiaohongshu.com/explore/662242350000000004018196?app_platform=android&app_version=7.95.0&author_share=1&share_from_user_hidden=true&type=video&xhsshare=WeixinSession&appuid=5e3c28ed00000000010006df&apptime=1713604283
- 石英石台面
- 【【阳仔】太惨了!遇到这种厨房台面渗色该怎么办?】 https://www.bilibili.com/video/BV1Qj411b7Vv/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 岩板
- 德利丰
- 东鹏
- 新明珠
- 蒙娜丽莎瓷砖
- 冠军瓷砖
- 瓷砖
- 厨卫300 * 600、300 * 300
- 卧室、厨房、客厅800 * 800、900 * 900
- 卫生间300 * 300 (淋浴房为了找坡)
- 柔光砖
- 若再铺一次瓷砖我发誓要坚持“5不做”:https://zhuanlan.zhihu.com/p/662108779
- 需要选择肌肤釉,最容易打理。特征:打光后能看到类似鸡蛋皮一样的纹理质感。
- 一定要避免买到柔抛砖。特征:表面会非常顺滑、光亮,高光会非常平整。
- 质量检查
- 到货后检查平截面与背面是否平整。
- 哑光瓷砖检测耐污性。
- 检查耐划性。
- 施工考虑墙压地
- 瓷砖不打滑,石材保护剂。
- 【一个卫生间装了小半年只为优雅的拉shit | 狮子的新家 vol.7】 【精准空降到 04:31】 https://www.bilibili.com/video/BV16A4y1D7Ap/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e&t=271
- 地板
- 选择包安装、包售后的地板品牌。
- 包送货上门。
- 包踢脚线、扣条、防潮垫。以及其他辅料。
- 地板有损耗,客户需要承担多少比例。
- 增项清单。
- VOC检测报告。
- 【【硬核科普】实木复合地板适合你吗优缺点实木复合地板和实木地板对比DIY君】 https://www.bilibili.com/video/BV1Qv4y1f7Ah/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 【【阳仔】注意地板黑店的4个明显特征】 https://www.bilibili.com/video/BV1eF41177fF/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 【【保姆级整理】家装地板选择全攻略!】 https://www.bilibili.com/video/BV183411x7EV/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 【【阳仔】拳打实木,碾压强化!这种地板真这么强?】 https://www.bilibili.com/video/BV1vz421X7Qn/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
# 其他
- 床垫
- 3000左右
- 【【阳仔】床垫黑店都有这5个特征你踩坑了吗】 https://www.bilibili.com/video/BV1r14y167E9/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
- 地漏:https://www.bilibili.com/video/BV1ys4y187ek/?spm_id_from=333.788&vd_source=d47c0bb42f9c72fd7d74562185cee290
- 铅坠侧开地漏芯+普通的大圆孔地漏面板
- 岩板桌
- 【【阳仔】第六期丨好看却垃圾的网红家居设计,还跟风吗?】 【精准空降到 00:12】 https://www.bilibili.com/video/BV1Nh4y1c7cf/?p=6&share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e&t=12
# 费用
- 电器
- 中央空调大金2.8w
- 洗烘套装1w~1.2w
- 冰箱2000~7000
- 热水器+小厨宝: 2000左右
- 油烟机500~4000
- 电视机75寸 3000~8000
- 电动晾衣架800~1200
- 洗碗机西门子sj436B00QC 6299、美的RX600s4599~GX1000S 8999
- 新风系统1w5~2W有耗材费一年24小时开2000块。半年~一年、噪音。南方没必要
- 新风没有除甲醛功能。
- 全屋净水:没有必要。
- 垃圾处理器:几千,不值得购买。

View File

@@ -0,0 +1,334 @@
# 题目
在一个月的时间内快速完成《沙巫之旅》这款竞品的复刻,分析这款游戏的
- 玩法特色和美术风格
- 找到并阐述核心重点
- 产出一个游戏开发计划和时间安排
# 逆向分析
## UModel
使用魔改的UModel就可以对该游戏进行解包并且提取资源。
使用资产:
- UI Material Lab:https://www.fab.com/listings/69680f34-e5d2-44e6-b023-f054bbf629eb
- 插件
- 加载屏幕插件 Async Loading Screen:https://www.fab.com/listings/f8aabb9a-7c96-4f79-97ff-04bcc146e595
- 自动缩放蓝图中Comment插件 Auto Size Comments:https://www.fab.com/listings/fdb7e77d-be37-4feb-a6c9-60e317c10adf
- CreateRedirectors
- CustomPlaceActor
- CustomShortcuts
- 对话框系统 DlgSystem:https://www.fab.com/listings/419d697f-9975-4940-a3f3-fde08a61f440
- DX11AutoFallback
- 存档系统 EasyMultiSave:https://www.fab.com/listings/49f745a1-cbdd-4b18-8278-22ae1075d91d
- ElgEditorScripting
- 事件节点编辑器 FlowGraph:https://github.com/MothCocoon/FlowGraph
- 游戏音频相关 FMODSpline:
- 游戏音频相关 FMODStudo
- FoliageAudio
- FSR3-540
- InstancedMeshChunks
- 触觉反馈开发SDK Interhaptics:https://www.fab.com/listings/fbc3dd13-38d9-4625-8f09-bd07354af3d6
- 实用蓝图库 LowEntryExtStdLib:https://www.fab.com/listings/0aadd41b-c02d-4f63-9009-bffad0070ebc
- 模块化吸附插件 ModularSnapSystem:https://www.fab.com/listings/5f5ca52e-4444-4ba5-a97d-98cf05962a26
- 一个序列化插件可以序列化Actor、Object NumberskullSerialization:https://www.fab.com/listings/d466670b-3458-4cad-a294-b36c853bc53c
- OnlineSubsystemBlueprints:https://www.fab.com/listings/16baded3-b354-42b5-b7ca-e3ffaba4ae77
- 扁平化图标库 OpenIcon:https://www.fab.com/listings/4505c6be-cfeb-4fc9-859c-7679ffdcdece
- PCStoreManager
- PropsExtender
- ShareXAutoRecord
- SlateIconBrowser
- 吸附增强 SnappingHelper:https://www.fab.com/listings/fb345179-6943-46c1-aab5-460cdc16975a
- SteamDeckConfig
- Stove 提供游戏上线服务 StoveSDKPlugin
- 绳索创建工具 Tether: https://www.fab.com/listings/e5621d4c-5774-4c6e-8057-530fc5249eb9
## Renderdoc
因为本游戏使用了一个简单的防盗版措施,所以需要进行简单的逆向。
![[Launch_Cracker.png]]
在Renderdoc里修改启动路径与启动程序之后添加“-PCStore=Steam” 启动参数就可以了。
![[Renderdoc1.png|800]]
![[Renderdoc2.png|800]]
---
# 玩法特色
《Caravan SandWitch》的开放式世界以“后启示录普罗旺斯”为基调融合科幻与废土美学构建了一个充满诗意与谜题的探索舞台。其核心玩法围绕自由探索、环境叙事与动态互动展开。
## 1. 内容丰富的箱庭开放世界
- **普罗旺斯科幻风格**
游戏以类普罗旺斯的“西加罗”星球为背景,地貌涵盖广袤的沙丘、干涸的河床、废弃的工业遗迹、古老的雷内托文明遗址,以及被沙尘覆盖的未来城市废墟。色彩以黄沙、锈红、灰绿为主,搭配科幻元素(如悬浮残骸、发光植物)营造独特的视觉风格。
- **隐藏的古代文明线索**
整个世界布满雷内托文明遗留的符号、壁画和机械装置,玩家需通过观察环境细节(如岩画、废墟结构)通过解密打开隐藏通路,甚至发现隐藏的地下设施或古代实验室。
## 2. 货车系统与解谜
- **作为代步工具**除了作为自己的代步工具一些同行任务还需要玩家载上NPC同行到新的地点推动剧情发展。
- **载具升级与解谜要素解锁** 通过收集零部件制作新的解谜工具以此探索更多之前无法前往的区域进而完成NPC任务、获取更多道具最终推动剧情发展。
## 3. 社区互动与动态叙事
- **西加罗社区的动态发展**
游戏中散布多个小型定居点,玩家通过帮助居民(如修复房屋、提供资源)逐步建立信任,解锁新任务线。社区会随玩家行为变化:
- **基础设施升级**:帮助居民后,定居点会扩建(如新建医疗站、工坊),提供新功能(如制作工具、修复货车)。
- **角色关系网**NPC会因玩家选择产生情绪变化甚至触发隐藏剧情例如某居民透露姐姐失踪的线索
- **推特系统**
一个类似P5的聊天界面本质是一个附带聊天记录与任务日志的任务系统。能够更加有血有肉、立体的塑造NPC。
## 玩法循环(使用图表示)
![[CaravanSandwitch玩法循环.png|500]]
大致玩法循环NPC => 接任务 => 解锁大地图区域(关闭干扰器/使用雷达) => 解谜 => 完成任务,经过几个循环开启下一轮主线。收集图纸+元器件制作新的解谜道具。
关闭干扰器可以解锁新的交互元素、NPC对话开启新任务
指引玩家前往新地点的方式:
1. 鸟瞰点指引:在鸟瞰点让玩家观察到新的地点。
2. 任务指引在大地图上标记NPC的位置。
## 解谜点分布逻辑
在这个箱庭开发世界中分布着一些废弃建筑、山洞作为解谜点。
![[WorldMap.png|800]]
根据规模可以分为:
-野外机关或是单个建筑一般只有1~3个机关的解谜元素。
- 中:复数存在的野外机关与建筑,若干机关组合在一起的解谜元素。
- 大:地图上标注的几个图标。一般是若干大型建筑组合,开发人员着重设计具备较大的深度与复合度的解谜地点。随着剧情发展与车辆的解谜工具增加会反复前往这些解谜地点。
---
# 美术风格
属于偏向LowPoly的风格化卡通渲染。
## 角色
![[Character1.png|500]]
![[Character2.png|500]]
渲染要素:
- BaseColor高饱和度与亮度的BaseColor
- Specular无高光效果。
- RimLighting基于NoL的边缘光效果。
经过分析:
![[Character_Renderdoc.png|1000]]
1. 角色主要在Translucent阶段渲染。通过采样Lambert材质渲染结果来获取阴影渲染结果。
2. 采用顶点色RGB通道赋予基础颜色A通道用于区分身上的单肩背包部分。
3. 使用一个NoL采样Ramp贴图来控制自阴影效果。
## 场景
除了主要的LowPoly场景模型场景中的一些素材看得出是将常规资产进行了一些低频风格化的处理。
![[Scene1.png|800]]
![[LightingPass_Renderdoc.png|1000]]
- BaseColor颜色归一化降低高频信息。
- Normal抹平法线。
- Spcular大部分物体基本没有高光与反射只有较少的金属物体会有。
- Metallic绝大部分物体都是0
- Specular
- 角色、粒子0
- 地形、载具0.5
- 场景物体、金属1
- Rougness除了角色皆为高频信息。猜测通过高频Roughness再通过Lumen的GI效果提高细节。
PS. 植被采用了预积分次表面实现。
---
# 快速开发计划
## GamePlay
游戏的主要功能按照重要性排序如下:
- 3C
- [x] Locomtion: 直接套用AdvancedLococmotionV4或者[ALS-Community](https://github.com/ShadowfallStudios/ALS-Community)直接构建项目。
- Walk
- Run
- Jump
- 攀爬
- [x] 额外运动方式使用商城资源合并到LococmotionV4中。
- 使用梯子(强行混合)
- 使用滑索,第四章获取(强行混合)
- 商城方案:
- ![500](https://media.fab.com/image_previews/gallery_images/dafb522f-17e9-4de0-9aad-6276d3a447db/7e25799b-5c55-4dca-a557-234f3b860da4.jpg)https://www.fab.com/listings/ba155b30-ccb8-42c5-b136-b70933a13e32
- 备选https://www.fab.com/listings/c92c2470-6e09-4303-a532-b042c6057ca2
- 车辆(载具):使用引擎的载具模版进行修改。
- 进入/离开 载具
- 刹车
- 加速
- 模拟悬挂系统
- 载具功能:
- [x] 区域扫描。可以描边显示被遮挡的关键物品/交互式元素。并且显示物品/交互式元素名称。
- [x] UI手动实现。
- [x] 扫描效果后处理材质:
- ![500](https://media.fab.com/image_previews/gallery_images/df04d200-d12b-49c7-ab6e-468c4a99b87d/f0f419e9-cfa7-447e-91fc-079841948081.jpg)https://www.fab.com/listings/9de276bc-2200-431b-be39-23f1eada3063
- ![500](https://media.fab.com/image_previews/gallery_images/2d2bc3d3-35e8-4797-aa81-375b1d5a74d4/f960f494-e7f9-467d-a2d6-990cef0770f2.jpg)https://www.fab.com/listings/f1ea87b7-d992-4c6b-8beb-1c120b4e1c3e
- 勾爪:可以抓住可交互元素。之后可以选择:
- 用车拉开(破坏交互元素)
- 用车拖拽从XX中获取新道具或者新的可交互道具。
- 构建临时滑索,之后玩家可以上到一些之前不能上去的地方。
- 黑客:可以破解某一些无法交互的元素。
- 变压中继器:可以给某些可交互元素供电,来打开某些开关。
- 物品系统:
- 只会显示物品描述。
- 代币:主要用于制作车辆功能生成部件(拆解零部件功能)
- 普通零部件:绿色
- 特殊零部件:红色
- 稀有零部件:黄色
- 珍贵零部件:紫色
- 商城方案:[RPG Inventory Template](https://www.fab.com/listings/bd9e297c-219d-4bd9-807c-21e8746f9ebe)
- 场景交互元素
- 游戏存在的交互元素
- 电缆(显示是否通电)
- 使用梯子
- 使用滑索
- 拾取物品
- 解谜机关(可能需要某些物品才能打开)
- 重力开关(需要车持续停上去才能开启)
- 中继器2个中继器连线后即可开启
- 鸟瞰点(坐垫)
- 宝箱(直接获取物品)
-
- 传送门VR教学管卡出入口/传送到指定地点
- 商城方案
- ![500](https://media.fab.com/image_previews/gallery_images/aefd66f2-e8d1-4022-ba86-2fb9a1db1c76/d80245fd-004d-4d69-9e9f-3a52743f5d9b.jpg)https://www.fab.com/listings/16335b65-1c61-49af-8014-1ddfd3987bb1
- 对话系统
- 备选商城插件:
- ![500](https://media.fab.com/image_previews/gallery_images/d79ca2a7-aeed-4e7d-beae-c726a91fdc47/8410a6cf-6f95-4d43-b043-40c87d4fa40d.jpg)https://www.fab.com/listings/052820ab-f423-48e8-978a-eefd4087b1a4
- ![500](https://media.fab.com/image_previews/gallery_images/79cab463-107a-4aa8-b4d4-5b5168830e37/7bc5795f-1704-4a55-a4d9-adaf59da061d.jpg)https://www.fab.com/listings/60a2d531-5d43-4bcc-a758-54440dfabecc
- https://www.fab.com/listings/0efb8a75-7fd1-49ea-a07c-9aa5621334e0
- https://www.fab.com/listings/419d697f-9975-4940-a3f3-fde08a61f440
- 任务系统:
- 备选商城插件:
- ![500](https://media.fab.com/image_previews/gallery_images/a1b366ea-acd4-4998-95d3-636de111d593/d685ec5d-ce22-4381-9cbb-ea1a96d33b5b.jpg)https://www.fab.com/listings/079f4df6-f7c5-4837-982b-c19eda87aa84
- ![500](https://media.fab.com/image_previews/gallery_images/d8e855ea-0866-419f-8ef2-274936cf54c8/6c58da41-4922-4a79-b665-a97f0b60ec6f.jpg)https://www.fab.com/listings/66f81152-15e6-42e8-8ba4-6ddcf49bdb3a
- [x] 存档系统:
- 存档系统 EasyMultiSave:https://www.fab.com/listings/49f745a1-cbdd-4b18-8278-22ae1075d91d
- 解谜点可以抽象成一个Actor之后使用[NumberskullSerialization](https://www.fab.com/listings/d466670b-3458-4cad-a294-b36c853bc53c)进行序列化,再保存。
- 大地图 & 小地图:
- 备选商城插件:
- ![500](https://media.fab.com/image_previews/gallery_images/e7dc199d-bc32-4e51-9b38-6fa01abc23d6/9b909ccb-5d29-4386-b2d1-044c8521743c.jpg)https://www.fab.com/listings/9af1c512-d619-4e13-a091-2944e45ea547
- ![500](https://media.fab.com/image_previews/gallery_images/85c5855f-e217-48f0-9d1d-3c02f6dd7a76/3b9ff93c-e878-430f-bea3-e80469686ef6.jpg)https://www.fab.com/listings/201fc801-3d30-4d51-b9f0-c030de9f822e
- 堆特:手动实现。
- 显示分类
- 钉选帖子:主线 & 重要任务。
- 活动帖子:支线任务。
- 归档贴图:已完成任务。
- 商城UI资产
- https://www.fab.com/listings/5be4e83d-165a-4c1b-aa37-839619998f75
- UI
- 主要UI组件
- ![500](https://media.fab.com/image_previews/gallery_images/7f60a4a8-69ba-487c-8439-5dccff5eab27/b6db5f17-e1be-4099-934c-63b0eaf95728.jpg)https://www.fab.com/listings/e678636b-66f1-40bd-b5bb-4d937376713e
- ![500](https://media.fab.com/image_previews/gallery_images/732b051d-21ef-491f-9e75-fdd80d03203b/fff8dab9-8e53-41e7-9a90-814505834a3b.jpg)![500](https://media.fab.com/image_previews/gallery_images/732b051d-21ef-491f-9e75-fdd80d03203b/fff8dab9-8e53-41e7-9a90-814505834a3b.jpg)
- ![500](https://media.fab.com/image_previews/gallery_images/3e225483-d257-4d67-acbf-032e3932aeb2/d680ca26-98a4-4b12-80d5-deaafe3cfc25.jpg)
- ![500](https://media.fab.com/image_previews/gallery_images/822fd927-f025-4771-a57f-de7ff527e721/f0f5afda-3541-406f-82ac-957575bec5c5.jpg)
- https://www.fab.com/listings/999c85c8-9a7b-43aa-a3e5-2b3bc6bc57ff
- 主菜单:![500](https://media.fab.com/image_previews/gallery_images/f2af58f9-a317-40dc-8273-36ab2773a9e6/5d323ad7-4b54-4a43-a4b9-b1fbcc0f7f89.jpg)https://www.fab.com/listings/8fc87fae-f72c-42d7-8e87-9c88475332f6
## 风格化资产生产方式
针对PBR资产
- 贴图进行降频、颜色归一。
- Substance 3D Painter 11.0新增 Stylization滤镜详解快速打造风格化纹理
- 模型使用Blender进行LowPoly处理。
- https://github.com/JulienVanelian/lowpolyzer
- https://github.com/sakana3/PolyQuilt
## 备选风格化资产
- 风格化渲染
- 角色
- 使用UModel提取角色模型与贴图
- 渲染
- 采用不透明模型只渲染深度具体效果使用Translucent的邪道渲染方式。
- 可使用商城资源Lightweight Toon Shader (No Post Processing)https://www.fab.com/listings/063f7f81-4703-46e2-87b5-a4b859629952
- ![https://media.fab.com/image_previews/gallery_images/1d35be67-8b0c-404a-809d-aa73a840fe21/edef85eb-740a-4c65-9a13-1b0b88eef2d0.jpg|500](https://media.fab.com/image_previews/gallery_images/1d35be67-8b0c-404a-809d-aa73a840fe21/edef85eb-740a-4c65-9a13-1b0b88eef2d0.jpg)
- 天空盒
- ![1|500](https://media.fab.com/image_previews/gallery_images/998498a4-51be-4fb7-9199-9d9b4f2c1434/24834cf2-3d1d-49ca-a035-d0226117e50d.jpg)
- 综合
- https://www.fab.com/listings/23869931-3e46-4c42-b541-9f6057f12d13
- ![500](https://media.fab.com/image_previews/gallery_images/379e0119-1311-4429-8057-857d8b453452/95b57b4d-02d3-4198-a567-c6f56f926591.jpg)
- 粒子
- 烟尘
- https://www.fab.com/listings/111e39b5-5bd7-4b46-a915-2eda020a4ca2
- ![500](https://media.fab.com/image_previews/gallery_images/c71707fe-c1e1-4a31-ad66-7be19f3e50ef/ca9f0204-9f77-42bd-a3a7-dc267733c9a9.jpg)
-
- https://www.fab.com/listings/b9eeb225-77a8-4541-bf13-ba15dcb04182
- ![500](https://media.fab.com/image_previews/gallery_images/947ca2ba-f776-43ea-bd46-6a6b0d97c876/18ccba2f-bd2c-45ca-8c43-5e4477a8ec8d.jpg)
- https://www.fab.com/listings/a5d68ec0-fc5f-4f7b-8b70-dde2847b71dc
- ![500](https://media.fab.com/image_previews/gallery_images/48ed8dd4-9908-48d2-bb7e-bca5fbcabd85/e235e7ee-a691-4cf0-a282-f699dae30a77.jpg)
-
- 水面
- https://www.fab.com/listings/9527db5d-9a12-4577-9507-35bd4e20e8ad
- ![500](https://media.fab.com/image_previews/gallery_images/7ea01b5f-e048-4a0e-b700-c0a6039cf57b/d09c13fc-5d22-4b25-8183-c366d3c34cb3.jpg)
- 地形材质
- https://www.fab.com/listings/6d473ba6-f95d-45e8-8b6b-8b522ca2bf82
- ![500](https://media.fab.com/image_previews/gallery_images/b73b0233-cefd-433a-8e6a-c4471882bd68/37bc7c4a-f8ee-4fcc-9aae-f7f2d2b53c35.jpg)
- ![500](https://media.fab.com/image_previews/gallery_images/a2080386-149f-48a3-a8b2-6e4150895e2d/2f0a70db-0f9a-475e-bf7c-ff70c748a80c.jpg)
- https://www.fab.com/listings/b003cb7c-f466-478b-8a3c-61cf21d3b678
- ![500](https://media.fab.com/image_previews/gallery_images/c3cfb18a-87cf-4002-97b4-92208a78cbe2/45180dde-500f-4fea-bdc9-cb952bb66e77.jpg)
- https://www.fab.com/listings/c56b6094-d8c3-403a-8133-b73260bfa25e
- ![500](https://media.fab.com/image_previews/gallery_images/1d77dc4c-7f82-4bbd-bcd0-139df3ec12d7/016f0198-1b75-4584-bd9b-3c10de3d3e7d.jpg)
- 备选资产
- https://www.fab.com/sellers/AleksandrIvanov
- https://www.fab.com/listings/ee6d82a3-19b6-4a81-9999-79a5ce3f5f3c
- ![500](https://media.fab.com/image_previews/gallery_images/acc50030-30c4-4b21-948b-faafcbc84b4c/e4da80db-7e71-4361-94f0-ca28d6c51a87.jpg)
- https://www.fab.com/listings/3be0cc6f-df42-4646-9745-024ca945e474
- ![500](https://media.fab.com/image_previews/gallery_images/0efd64d9-16e7-4a4b-ad49-defe19e060c2/779d7139-d5c8-4a70-84b4-e25b4e4cc0ed.jpg)
- https://www.fab.com/listings/b0fb5f1c-2f92-49a3-989d-084656275e4d
- ![500](https://media.fab.com/image_previews/gallery_images/0a2693c7-eb6d-4213-9bdc-812a460e0902/83213ef0-8295-46b5-8d78-66b58f34287d.jpg)
---
# 时间表
1. 第一周
1. 第一天
1. 使用RPG Inventory Template与插件ALS-Commutiy作为基础构建项目将ALS-Commutiy的3C融合进项目。实现物品、交互系统以及基础Locomotion。
2. 将Ladders, Glides and Ziplines System加入项目实现滑索、爬梯子功能。添加滑索、梯子可交互Actor。
3. 构建版本管理系统,上传项目。
2. 第二天
1. 移植UE的载具模版到项目中并且使用从游戏中提取车辆资产完成基础载具功能。
2. 实现角色上下车的逻辑。
3. 给可交互Actor基类添加逻辑
1. 在屏幕显示对应的详细信息。
2. 被破坏、被拖拽、被破解、被车压住、被充电接口。
4. 实现扫描功能。
5. 实现车辆瞄准可交互Actor的功能。
6. 实现车辆发射钩锁的功能。
3. 第三天
1. 提取游戏资源完成被破坏、被拖拽、被破解、被车压住、被充电的交互Actor。
2. 在地图中测试。
3. 完成相关交互UI。
4. 搭建类似游戏的VR教学场景并进行测试。
4. 第四天
1. 添加游戏代币物品与UI。
2. 添加场景中拾取代币的交互Actor。
3. 添加制造新解密道具消耗代币的交互Actor。
4. 添加Quest and Dialog Kit资产
5. 添加Dialog UI完成Dialog系统。
5. 第五天
1. 提取游戏资产并且添加主角以及其他NPC模型。
2. 修改Quest and Dialog Kit资产逻辑完成任务系统。
3. 添加“堆特”UI完成堆特系统。
6. 第六~七天
1. 测试、完善、解决遗留bug。
2. 使用EasyMultiSave与[NumberskullSerialization](https://www.fab.com/listings/d466670b-3458-4cad-a294-b36c853bc53c)实现存档系统。
3. 进行打包测试。
2. 第二周
1. 第八天
1. 实现角色渲染材质。
2. 使用接近的纯色完成初步地形材质。
3. 按照游戏第一章的地形大致刷一下地形。
2. 第九~十二天
1. 使用提取资产与商城资产搭建天空盒、水面。
2. 大致构建第一章的场景并且布置解密要素、NPC。
3. 第十三~十四天
1. 添加主菜单与其他UI。
2. 测试并且解决遗留bug。
3. 第三周
1. 第十五天
1. 对RPG Inventory Template的大地图进行适配显示地形与等高线。
2. 使用World Map System资产实现小地图功能。
2. 第十六~十八天
1. 按照游戏设计,堆砌场景细节与解密要素。
2. 添加鸟瞰点、信号干扰器。
3. 第十九天
1. 交给领导与同事试玩,并且收集意见。
4. 第二十~二十一天
1. 按照收集到的意见完善游戏体验。
4. 第四周
1. 第二十二
1. 交给领导与同事试玩,再次收集意见
2. 第二十三~二十七天
1. 按照收集到的意见进一步完善游戏体验。
3. 第二十八天
1. 提交给主管最终评审。

View File

@@ -0,0 +1,22 @@
{
"nodes":[
{"id":"f3af455939c15565","type":"text","text":"到达新区域\n","x":-203,"y":-286,"width":250,"height":60},
{"id":"c6556ec9594182fa","x":-203,"y":60,"width":250,"height":60,"type":"text","text":"使用堆特"},
{"id":"2581bd493de7db9d","x":-203,"y":220,"width":250,"height":60,"type":"text","text":"接新的任务"},
{"id":"4ac8994171dfb450","x":-203,"y":340,"width":250,"height":60,"type":"text","text":"解谜"},
{"id":"01926c963790e97c","x":-540,"y":340,"width":250,"height":60,"type":"text","text":"破坏信号干扰器"},
{"id":"29ceff92ce038882","x":-540,"y":-120,"width":250,"height":60,"type":"text","text":"与NPC沟通"},
{"id":"64b852a3680c0876","x":-203,"y":480,"width":250,"height":60,"type":"text","text":"完成任务"}
],
"edges":[
{"id":"8798438ce7ca228c","fromNode":"f3af455939c15565","fromSide":"bottom","toNode":"29ceff92ce038882","toSide":"top"},
{"id":"782332fc911e051c","fromNode":"f3af455939c15565","fromSide":"bottom","toNode":"c6556ec9594182fa","toSide":"top"},
{"id":"dfc4313997588d95","fromNode":"29ceff92ce038882","fromSide":"bottom","toNode":"c6556ec9594182fa","toSide":"top"},
{"id":"916554cf03afe01c","fromNode":"c6556ec9594182fa","fromSide":"bottom","toNode":"2581bd493de7db9d","toSide":"top"},
{"id":"c338ab36f2a0ff63","fromNode":"4ac8994171dfb450","fromSide":"bottom","toNode":"64b852a3680c0876","toSide":"top"},
{"id":"ca462a6730a8296f","fromNode":"2581bd493de7db9d","fromSide":"bottom","toNode":"4ac8994171dfb450","toSide":"top"},
{"id":"e0d7dfcf441cea11","fromNode":"4ac8994171dfb450","fromSide":"left","toNode":"01926c963790e97c","toSide":"right"},
{"id":"ab26ae485f68ca1d","fromNode":"01926c963790e97c","fromSide":"top","toNode":"c6556ec9594182fa","toSide":"left"},
{"id":"39fba7b5f2ad38c9","fromNode":"64b852a3680c0876","fromSide":"right","toNode":"f3af455939c15565","toSide":"right"}
]
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More