Init
This commit is contained in:
1
07-Other/.keep
Normal file
1
07-Other/.keep
Normal file
@@ -0,0 +1 @@
|
||||
this file is created for keeping the folder after git.
|
3
07-Other/AI/AI SD绘画细化流程.md
Normal file
3
07-Other/AI/AI SD绘画细化流程.md
Normal 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
|
2
07-Other/AI/AI生成Logo.md
Normal file
2
07-Other/AI/AI生成Logo.md
Normal file
@@ -0,0 +1,2 @@
|
||||
AI生成Logo
|
||||
https://www.bilibili.com/video/BV1AG411E7Am/?vd_source=d47c0bb42f9c72fd7d74562185cee290
|
59
07-Other/AI/AI相关.md
Normal file
59
07-Other/AI/AI相关.md
Normal 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×tamp=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
|
||||
|
||||
### 具体作品关键词
|
||||

|
||||
character_concept_design_portrait_a_handsome_young_
|
||||
|
||||

|
||||
https://pic1.zhimg.com/80/v2-c4fba4813ccec87d2662c56e357a3c37_720w.webp?source=1940ef5c
|
||||
|
||||
## 汉服少女
|
||||
|
||||

|
||||
|
||||
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://civitai.com/
|
||||
- https://civitai.com/models/4468/counterfeit-v25
|
||||
- 
|
||||
- https://civitai.com/models/1411/counterfeit
|
||||
- 
|
||||
- https://civitai.com/models/7178/counterfeitlora300
|
||||
- 
|
||||
- https://civitai.com/models/6424
|
||||
- 
|
||||
|
||||
## AI诱导生成
|
||||
Controlnet插件诱导生成
|
||||

|
||||
.JPG)
|
||||
.JPG)
|
||||
.JPG)
|
||||

|
||||
.JPG)
|
||||

|
4
07-Other/MAC/IPA IOS重签名.md
Normal file
4
07-Other/MAC/IPA IOS重签名.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# 前言
|
||||
- https://github.com/DanTheMan827/ios-app-signer
|
||||
- https://dantheman827.github.io/ios-app-signer/
|
||||
- https://github.com/maciekish/iReSign
|
158
07-Other/MAC/安装Mac UE开发环境.md
Normal file
158
07-Other/MAC/安装Mac UE开发环境.md
Normal 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。
|
277
07-Other/MAC/黑苹果配置笔记.md
Normal file
277
07-Other/MAC/黑苹果配置笔记.md
Normal 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 -> 67,MISC - Debug -Target 设置成67,之后就可以输出Log到安装U盘根目录。
|
||||
- 开启Sysreport
|
||||
- 添加跑码模式,找到NVRAM---7C436110-AB2A-4BBB-A880-FE41995C9F82 ,给boot-args 添加上 -v选项。
|
||||

|
||||
# 显卡型号查询与替换
|
||||
型号可以在GPU-Z中查询,前几位厂商型号,后面4位就是显卡型号。
|
||||
找到之后在 https://devicehunt.com/search/type/pci/vendor/1002/device/any 查询确认即可。
|
||||
|
||||
比如GPU-Z显示的DeviceID是1002 73FF - 1DA2 E451。所以我的显卡型号是73FF(1002是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 |
|
250
07-Other/Node.js/Electron/Electron笔记.md
Normal file
250
07-Other/Node.js/Electron/Electron笔记.md
Normal 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的效果,也可以处理不同环境的不同逻辑,一些示例:
|
||||

|
||||

|
||||

|
||||
|
||||
## 打开新窗口的“最佳”做法
|
||||
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;
|
||||
})
|
||||
}
|
||||
```
|
112
07-Other/Node.js/Electron/Vue-cli项目移植Electron-vue的相关经验.md
Normal file
112
07-Other/Node.js/Electron/Vue-cli项目移植Electron-vue的相关经验.md
Normal 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的字符集。而不同系统(win10,win7)字符集是不同的。所以就需要对启动程序进行修改。
|
||||
|
||||
## 关于多条命令启动
|
||||
因为本人写的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
|
@@ -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放在项目目录中即可。
|
18
07-Other/Node.js/H5 PPT与Node.js生成pptx方案.md
Normal file
18
07-Other/Node.js/H5 PPT与Node.js生成pptx方案.md
Normal 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用的人最多。
|
11
07-Other/Node.js/Node.js实现word文档生成以及word模板浏览器端实现下载文件.md
Normal file
11
07-Other/Node.js/Node.js实现word文档生成以及word模板浏览器端实现下载文件.md
Normal 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/
|
168
07-Other/Node.js/Node.js的Mysql模块中的转义字符.md
Normal file
168
07-Other/Node.js/Node.js的Mysql模块中的转义字符.md
Normal 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.
|
105
07-Other/Node.js/VUE、Electron升级笔记.md
Normal file
105
07-Other/Node.js/VUE、Electron升级笔记.md
Normal 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])*?-->
|
||||
```
|
1
07-Other/Node.js/前后端API管理工具.md
Normal file
1
07-Other/Node.js/前后端API管理工具.md
Normal file
@@ -0,0 +1 @@
|
||||
1. Apifox
|
54
07-Other/Node.js/在Node.js中使用ffi调用dll.md
Normal file
54
07-Other/Node.js/在Node.js中使用ffi调用dll.md
Normal 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-napi(api是一样的,同时支持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
|
45
07-Other/Node.js/在Nodejs中使用Axios下载文件的方法.md
Normal file
45
07-Other/Node.js/在Nodejs中使用Axios下载文件的方法.md
Normal 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);
|
||||
```
|
19
07-Other/Qt/QGraphics系列笔记.md
Normal file
19
07-Other/Qt/QGraphics系列笔记.md
Normal 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的左上角顶点与视图坐标原点对齐。**显示时默认中心对齐**,当场景大小小于视图大小的时候,将中心对齐,此中指的仍然是整个图元的中心,同时,图元原点与场景原点对齐,场景左上角顶点与视图原点对齐,视图左上角顶点不一定是原点???,此时也将出现视图坐标有正值有负值。
|
||||
|
||||
- 1)translate()将当前视图坐标原点平移,从而实现显示图像的平移变换。由于默认场景的左上角顶点与视图坐标原点对齐,translate()将坐标原点平移,也就实现了将场景的平移。
|
||||
- 2)rotate()将当前视图围绕视图坐标原点旋转,从而实现显示图像的旋转变换。
|
||||
- 3)size()返回视图大小,默认大小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
|
79
07-Other/Qt/Qt On Android 隐藏状态栏的方法.md
Normal file
79
07-Other/Qt/Qt On Android 隐藏状态栏的方法.md
Normal 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不能使用设计师界面类来创建!
|
24
07-Other/Qt/Qt6编译步骤.md
Normal file
24
07-Other/Qt/Qt6编译步骤.md
Normal 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。
|
53
07-Other/Qt/QtGUI/IconFont.md
Normal file
53
07-Other/Qt/QtGUI/IconFont.md
Normal 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
|
||||
返回一个可用的字体对象。
|
104
07-Other/Qt/QtGUI/QSS笔记.md
Normal file
104
07-Other/Qt/QtGUI/QSS笔记.md
Normal 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 Selector,descendant 的表达比较到位。
|
||||
|
||||
选择器之间用空格隔开,作用于 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;
|
||||
}
|
||||
```
|
102
07-Other/Qt/QtGUI/QtUI方案.md
Normal file
102
07-Other/Qt/QtGUI/QtUI方案.md
Normal 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
|
121
07-Other/Qt/QtQuick/QtQuick中QML文件间的通讯问题.md
Normal file
121
07-Other/Qt/QtQuick/QtQuick中QML文件间的通讯问题.md
Normal 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中的函数,实现了传返回值。
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
另一种就是信号与槽
|
163
07-Other/Qt/QtQuick/QtQuick基础笔记.md
Normal file
163
07-Other/Qt/QtQuick/QtQuick基础笔记.md
Normal 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的Progress(0。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});
|
||||
```
|
113
07-Other/Qt/QtQuick/QtQuick大坑笔记之Http的Get与Post操作(带cookie).md
Normal file
113
07-Other/Qt/QtQuick/QtQuick大坑笔记之Http的Get与Post操作(带cookie).md
Normal 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,以后有时间会重新封装一个简单版本。
|
118
07-Other/Qt/QtQuick/QtQuick自定义主题以及控件样式.md
Normal file
118
07-Other/Qt/QtQuick/QtQuick自定义主题以及控件样式.md
Normal 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应用”
|
291
07-Other/Qt/QtQuick/Qt:解决QtQuick(QML)程序,在虚拟机或者某些环境下,动画速度过快的问题.md
Normal file
291
07-Other/Qt/QtQuick/Qt:解决QtQuick(QML)程序,在虚拟机或者某些环境下,动画速度过快的问题.md
Normal file
@@ -0,0 +1,291 @@
|
||||
## 参考网址
|
||||
https://blog.csdn.net/wsj18808050/article/details/54234956
|
||||
|
||||
Qt:获取屏幕物理长度和宽度(CM)
|
||||
https://blog.csdn.net/wsj18808050/article/details/54345537
|
||||
|
||||
Qt:5.8新特新,QtLite使用方法,以及缩减应用体积的效果
|
||||
https://blog.csdn.net/wsj18808050/article/details/55808104
|
||||
|
||||
QML 中的屏幕适配问题
|
||||
https://blog.csdn.net/qyvlik/article/details/51241425
|
||||
|
||||
QT5(9)HTTP 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
|
23
07-Other/Qt/QtQuick/c++与QML混合编程笔记.md
Normal file
23
07-Other/Qt/QtQuick/c++与QML混合编程笔记.md
Normal 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() 注册具有附加属性的附加类型
|
24
07-Other/Qt/Qt多个子项目管理.md
Normal file
24
07-Other/Qt/Qt多个子项目管理.md
Normal 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
103
07-Other/Qt/Qt总览.md
Normal 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
|
70
07-Other/Qt/为Qt中的SQLite添加密码并加密.md
Normal file
70
07-Other/Qt/为Qt中的SQLite添加密码并加密.md
Normal file
@@ -0,0 +1,70 @@
|
||||
## 前言
|
||||
因为Sqlite的源代码中只提供了Sqlite3_key()的接口,没有实现。所以Qt中的Sqlite没有密码功能。于是我找了一下资料,,并且总结一下思路。现成的方法在最后。
|
||||
|
||||
## SQLite历代版本与下载
|
||||
所有ReleaseTag:https://www.sqlite.org/cgi/src/taglist
|
||||
SQLite不提供明确的下载地址,所以地址需要开发者去猜……,也就是通过最新的地址以及你所需要的版本号去推:
|
||||
|
||||
例如:
|
||||
- sqlite-amalgamation-3240000:https://www.sqlite.org/2018/sqlite-amalgamation-3240000.zip
|
||||
- sqlite-dll-win32-x86-3240000.zip:https://www.sqlite.org/2018/sqlite-dll-win32-x86-3240000.zip
|
||||
- sqlite-dll-win64-x64-3240000.zip:https://www.sqlite.org/2018/sqlite-dll-win64-x64-3240000.zip
|
||||
- sqlite-amalgamation-3270200.zip:https://www.sqlite.org/2019/sqlite-amalgamation-3270200.zip
|
||||
- sqlite-dll-win64-x64-3270200.zip:https://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
|
58
07-Other/Qt/使用WSL安装Ubantu来测试Qt.md
Normal file
58
07-Other/Qt/使用WSL安装Ubantu来测试Qt.md
Normal 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
|
163
07-Other/Qt/在Qt中使用Mupdf库显示pdf文档.md
Normal file
163
07-Other/Qt/在Qt中使用Mupdf库显示pdf文档.md
Normal 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下改成/MDd,release下改成/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
|
44
07-Other/Unity/ComputeShader笔记.md
Normal file
44
07-Other/Unity/ComputeShader笔记.md
Normal 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。
|
234
07-Other/Unity/ShaderLab笔记.md
Normal file
234
07-Other/Unity/ShaderLab笔记.md
Normal 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)把方向矢量从世界空间变换到模型空间中
|
||||
|
||||
## 浮点格式
|
||||
float:32位
|
||||
half:16位 -60000~+60000
|
||||
fixed:11位 -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_DeltaTime:dt为时间的增量,4个分量为:dt,1/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);
|
||||
}
|
||||
```
|
48
07-Other/Unity/Unity-Chan 2种模式比对.md
Normal file
48
07-Other/Unity/Unity-Chan 2种模式比对.md
Normal 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)`
|
685
07-Other/Unity/Unity-Chan Toon Shader Body.md
Normal file
685
07-Other/Unity/Unity-Chan Toon Shader Body.md
Normal 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
|
||||
}
|
||||
```
|
204
07-Other/Unity/Unity-Chan Toon Shader Outline.md
Normal file
204
07-Other/Unity/Unity-Chan Toon Shader Outline.md
Normal 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
|
||||

|
||||
黑色表示“无线条”,白色表示宽度为 100%。
|
||||
|
||||
### BakedNormal
|
||||
采样烘焙的法线贴图,并将值赋予顶点法线。
|
||||
|
||||
### Offset_Camera_Z
|
||||
>`Offset_Camera_Z`就是单纯的深度偏移,可以用于调整模型尖端与模型交界处处效果。但本人认为这个没有必要。
|
||||
效果如图:
|
||||

|
||||
|
||||
## 顶点着色器
|
||||
主要用于偏移顶点坐标、采样法线贴图值并传递给顶点以及传递顶点数据。计算物体与摄像机距离,对设定的最近距离与最远距离插值得到距离因子。之后乘以`_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
|
||||
}
|
||||
```
|
793
07-Other/Unity/Unity-Chan Toon Shader PixelShader.md
Normal file
793
07-Other/Unity/Unity-Chan Toon Shader PixelShader.md
Normal file
@@ -0,0 +1,793 @@
|
||||
## 总览
|
||||
- `DoubleShadeWithFeather`:UTS/UniversalToon 的标准工作流程模式。允许 2 种阴影颜色(双阴影颜色)和颜色之间的渐变(羽化)。
|
||||
- `ShadingGradeMap`:更高级的工作流程模式。除了 DoubleShadeWithFeather 功能之外,此着色器还可以保存称为 ShadingGradeMap 的特殊贴图。
|
||||
|
||||

|
||||
|
||||
### 案例文件
|
||||
示例文件于`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.unity:Angel ring and ShadingGradeMap sample.
|
||||
- Baked Normal\Cube_HardEdge.unity:Baked Normal reference.
|
||||
- BoxProjection\BoxProjection.unity :Lighting a dark room using Box Projection.
|
||||
- EmissiveAnimation\EmisssiveAnimation.unity:EmissiveAnimation sample.
|
||||
- LightAndShadows\LightAndShadows.unity:Comparison between the PBR shader and UTS2.
|
||||
- MatCapMask\MatCapMask.unity:MatcapMask sample.
|
||||
- Mirror\MirrorTest.unity: Sample scene checking for a mirror object
|
||||
- NormalMap\NormalMap.unity :Tricks for using the normal map with UTS2.
|
||||
- PointLightTest\PointLightTest.unity:Sample of cel-style content with point lights.
|
||||
- Sample\Sample.unity :Introduction to the basic UTS2 shaders.
|
||||
- ShaderBall\ShaderBall.unity:UTS2 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`效果:
|
||||

|
||||
|
||||
RimLight 通常从相机的角度显示在对象边缘周围。在 UTS2 中,您可以相对于主灯的位置调整边缘灯的显示位置。('LightDirection 蒙版')
|
||||
|
||||
您还可以将 RimLight 设置为与光源相反的方向。您还可以使用“添加 Antipodean_RimLight”渲染“光反射”。
|
||||
|
||||
如果您只想在光源的相反方向上显示边缘光并沿光源方向切割边缘光,请将边缘光的光方向颜色指定为黑色 (0,0,0)。
|
||||
|
||||

|
||||
|
||||
### 天使环
|
||||
https://github.com/unity3d-jp/UnityChanToonShaderVer2_Project/blob/release/urp/2.3.0/Documentation~/index.md#-making-materials-for-angel-ring
|
||||
|
||||
制作天使环材质首先需要将头发模型正面投射到UV上,做制作贴图。
|
||||
|
||||
## 颜色
|
||||
高光颜色
|
||||
通常色(被光照射部分)
|
||||
1号阴影(淡)
|
||||
2号阴影(深)
|
||||
|
||||
## 模板功能
|
||||

|
||||
可以用来制作睫毛挡住头发的效果。
|
||||
|
||||
## 半兰伯特模型
|
||||
>有一个问题仍然存在。在光照无法到达的区域,模型的外观通常是全黑的,没有任何明暗变化,这会使模型的背光区域看起来就像一个平面一样,失去了模型细节表现。
|
||||
|
||||
为此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主要用来计算envColor(GI)、主灯光数据与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;
|
||||
}
|
||||
}
|
||||
```
|
||||

|
105
07-Other/Unity/Unity-Chan Toon Shader 学习.md
Normal file
105
07-Other/Unity/Unity-Chan Toon Shader 学习.md
Normal 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
|
||||
|
||||
## 总览
|
||||
没有后处理效果
|
||||
|
||||
两种着色方式:
|
||||
DoubleShadeWithFeather:UTS/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"
|
||||
```
|
71
07-Other/Unity/Unity通用渲染管线(URP)系列(一)——自定义渲染管线.md
Normal file
71
07-Other/Unity/Unity通用渲染管线(URP)系列(一)——自定义渲染管线.md
Normal 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()
|
||||
提交给渲染队列。
|
@@ -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;
|
||||
}
|
||||
```
|
@@ -0,0 +1,185 @@
|
||||
## 建立自己的管线Shader以及ShaderLibrary
|
||||
|
||||
## Shader宏分支控制
|
||||
### multi_complie
|
||||
常用的两种做法:
|
||||
1. 使用multi_complie或shader_feature来定义宏,根据不同的宏指令编译出多套Shader,Unity内建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个variant(RedPink,RedYellow,GreenPink,GreenYellow,BluePink,BlueYellow),因为他们之间会两两组合。
|
||||
|
||||
### 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 Functions:UniversalFragmentPBR、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_ObjectToWorld,unity_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。
|
103
07-Other/Unity/Unity通用渲染管线(URP)系列(十一)——后处理(Bloom).md
Normal file
103
07-Other/Unity/Unity通用渲染管线(URP)系列(十一)——后处理(Bloom).md
Normal file
@@ -0,0 +1,103 @@
|
||||
## Post-FX Stack
|
||||
为了效率跳过。
|
||||
|
||||
|
||||
`void Draw(RenderTargetIdentifier from,RenderTargetIdentifier to,Pass pass)`
|
||||

|
||||
|
||||
to代表绘制的RT id,通过`buffer.SetRenderTarget()`来设置。from为原始渲染结果,使用`buffer.SetGlobalTexture()`来向Shader传递贴图资源。
|
||||
|
||||
## Bloom
|
||||
对图像进行双线性采样,以生成分辨率不断对半分的Bloom金字塔。
|
||||

|
||||
|
||||
给`_BloomPyramid1`~`_BloomPyramid16`传递id。
|
||||

|
||||
|
||||
创建一个DoBloom方法。首先将摄像机的像素宽度和高度减半,然后选择默认的渲染纹理格式。最初,我们将从源复制到金字塔中的第一个纹理。追踪那些标识符。
|
||||

|
||||
|
||||
|
||||
## 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`中的给低分辨率结果乘以强度之后在于高分辨率结果叠加。
|
14
07-Other/Unity/Unity通用渲染管线(URP)系列(十二)——HDR.md
Normal file
14
07-Other/Unity/Unity通用渲染管线(URP)系列(十二)——HDR.md
Normal file
@@ -0,0 +1,14 @@
|
||||
## HDR渲染纹理
|
||||
HDR渲染仅与后处理结合使用才有意义,因为我们无法更改最终的帧缓冲区格式。因此,当我们在CameraRenderer.Setup中创建自己的中间帧缓冲区时,我们将在适当的时候使用默认的HDR格式`buffer.GetTemporarayRT()`中使用`RenderTextureFormat.DefaultHDR`(后处理效果的RT格式),也就是R16G16B16A16_SFloat,而不是LDR的常规默认格式。
|
||||
|
||||
逐步执行DrawCall时,你会注意到场景看起来比最终结果要暗。发生这种情况是因为这些步骤存储在HDR纹理中。由于线性颜色数据按原样显示,因此看起来很暗,它错误地解释为sRGB。
|
||||

|
||||
|
||||

|
||||
|
||||
为什么亮度会变化?
|
||||
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>
|
@@ -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 级联阴影贴图
|
||||
现在终于得到阴影,但它们看起来很糟糕。不应被阴影化的表面最终会被形成像素化带的阴影伪影所覆盖。这些是由于阴影贴图的有限分辨率导致的自我阴影化。使用不同的分辨率会更改伪影模式,但不会消除它们。
|
||||

|
||||
|
||||
#### 球形剔除
|
||||
Unity通过为其创建一个选择球来确定每个级联覆盖的区域。由于阴影投影是正交的且呈正方形,因此它们最终会紧密契合其剔除球,但还会覆盖周围的一些空间。这就是为什么可以在剔除区域之外看到一些阴影的原因。同样,光的方向与球无关,因此所有定向光最终都使用相同的剔除球。
|
||||
|
||||
剔除球是ComputeDirectionalShadowMatricesAndCullingPrimitives函数中计算出的`ShadowSplitData`分离出的数据。传递`_CascadeCount`级联数以及`_CascadeCullingSpheres`以及剔除球的位置数据。
|
||||
|
||||
创建`ShadowData`结构体以传递级联级别以及阴影硬度。将会在`GetLighting`中通过像素的世界坐标是否在球体内来计算`ShadowData`结构体中的级联级别。
|
||||
|
||||
#### 最大距离
|
||||
此时阴影会在超过最后一个剔除球后消失,为了解决这个问题,会设置一个最大距离,超过最大距离阴影才会消失。具体操作是在`GetShadowData`中比较像素深度以及阴影最大距离值,如果超过则将`strength`设置为0。
|
||||
|
||||
#### 给阴影添加衰减与级联渐变
|
||||
阴影衰减见git,
|
||||

|
||||
|
||||
级联衰减公式:
|
||||

|
||||
其中f为下面式子的倒数:
|
||||

|
||||
在`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
|
||||
```
|
||||
##### 其他功能效果
|
||||
- 透明度
|
||||
- 阴影模式
|
||||
- 裁切阴影
|
||||
- 抖动阴影
|
||||
- 无阴影
|
||||
- 不受光阴影投射器
|
||||
- 接受阴影
|
204
07-Other/Unity/《UnityShader入门》NPR部分学习.md
Normal file
204
07-Other/Unity/《UnityShader入门》NPR部分学习.md
Normal 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);
|
||||
```
|
313
07-Other/Unity/《UnityShader入门》后处理部分学习.md
Normal file
313
07-Other/Unity/《UnityShader入门》后处理部分学习.md
Normal 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"
|
||||
}
|
||||
|
||||
```
|
103
07-Other/VPS账号 以及PS4 DNS.md
Normal file
103
07-Other/VPS账号 以及PS4 DNS.md
Normal 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
|
||||
- 主要DNS:112.106.53.22
|
||||
- 备用DNS:112.106.53.34
|
||||
|
||||
- 主要DNS:203.112.2.4
|
||||
- 备用DNS:203.112.2.5
|
||||
|
||||
- 主要DNS:168.126.63.1
|
||||
- 备用DNS:168.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
|
16
07-Other/github-recovery-codes.txt
Normal file
16
07-Other/github-recovery-codes.txt
Normal 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
|
5
07-Other/如何打开微软商店.md
Normal file
5
07-Other/如何打开微软商店.md
Normal 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
BIN
07-Other/技术演示.pptx
(Stored with Git LFS)
Normal file
Binary file not shown.
114
07-Other/生活/婚礼.md
Normal file
114
07-Other/生活/婚礼.md
Normal 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
|
34
07-Other/生活/待办事项.md
Normal file
34
07-Other/生活/待办事项.md
Normal 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.
|
1
07-Other/生活/提取网易云 & QQ音乐工具.md
Normal file
1
07-Other/生活/提取网易云 & QQ音乐工具.md
Normal file
@@ -0,0 +1 @@
|
||||
https://git.unlock-music.dev/um/cli
|
25
07-Other/生活/收支记账/2024.md
Normal file
25
07-Other/生活/收支记账/2024.md
Normal 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
|
117
07-Other/生活/收支记账/2025.md
Normal file
117
07-Other/生活/收支记账/2025.md
Normal 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月
|
14
07-Other/生活/智能家居/AppleTV.md
Normal file
14
07-Other/生活/智能家居/AppleTV.md
Normal 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. 看电视台节目的App:APTV、iSTB Lite、iSTB、iPlay TV、TV+
|
||||
5. 阿里云盘播放器:iiVA和Alplayer
|
41
07-Other/生活/智能家居/Aqara Homekit & HomeAssistant.md
Normal file
41
07-Other/生活/智能家居/Aqara Homekit & HomeAssistant.md
Normal 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、苹果家庭APP(homekit),再删除homekit,然后接入ha,最后从ha接回homekit(ha自带的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>(4)Homekit 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了。
|
||||
|
3
07-Other/生活/智能家居/HDIM CEC.md
Normal file
3
07-Other/生活/智能家居/HDIM CEC.md
Normal 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
|
62
07-Other/生活/智能家居/HomeAssistant设置天气相关.md
Normal file
62
07-Other/生活/智能家居/HomeAssistant设置天气相关.md
Normal 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
|
||||
```
|
11
07-Other/生活/智能家居/Node-Red.md
Normal file
11
07-Other/生活/智能家居/Node-Red.md
Normal 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
|
1
07-Other/生活/智能家居/“Homeassistant+Siri+捷径”实现更多个性化智能家居场景.md
Normal file
1
07-Other/生活/智能家居/“Homeassistant+Siri+捷径”实现更多个性化智能家居场景.md
Normal file
@@ -0,0 +1 @@
|
||||
https://zhuanlan.zhihu.com/p/352298928
|
271
07-Other/生活/智能家居/扫地机器人.md
Normal file
271
07-Other/生活/智能家居/扫地机器人.md
Normal file
@@ -0,0 +1,271 @@
|
||||
科沃斯ID:ECJ3FLME
|
||||
|
||||
# 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
76
07-Other/生活/租房.md
Normal 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
BIN
07-Other/生活/装修/圆子.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
07-Other/生活/装修/圆子的家-装修设计需求.pptx
(Stored with Git LFS)
Normal file
BIN
07-Other/生活/装修/圆子的家-装修设计需求.pptx
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
07-Other/生活/装修/客厅玻璃隔间书房/微信图片_20240312183541.jpg
(Stored with Git LFS)
Normal file
BIN
07-Other/生活/装修/客厅玻璃隔间书房/微信图片_20240312183541.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
07-Other/生活/装修/客厅玻璃隔间书房/微信图片_20240312183547.jpg
(Stored with Git LFS)
Normal file
BIN
07-Other/生活/装修/客厅玻璃隔间书房/微信图片_20240312183547.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
07-Other/生活/装修/建材/建材_各国乳胶漆环保等级标准.png
(Stored with Git LFS)
Normal file
BIN
07-Other/生活/装修/建材/建材_各国乳胶漆环保等级标准.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
07-Other/生活/装修/开关插座-便利贴.pdf
(Stored with Git LFS)
Normal file
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
BIN
07-Other/生活/装修/微信图片_20240222124703.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
07-Other/生活/装修/智能记账表预算表.xlsx
Normal file
BIN
07-Other/生活/装修/智能记账表预算表.xlsx
Normal file
Binary file not shown.
BIN
07-Other/生活/装修/橱柜截图/Snipaste_2024-03-01_14-16-55.png
(Stored with Git LFS)
Normal file
BIN
07-Other/生活/装修/橱柜截图/Snipaste_2024-03-01_14-16-55.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
07-Other/生活/装修/橱柜截图/Snipaste_2024-03-01_14-18-06.png
(Stored with Git LFS)
Normal file
BIN
07-Other/生活/装修/橱柜截图/Snipaste_2024-03-01_14-18-06.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
07-Other/生活/装修/水电交底/智能家居水电交底.png
(Stored with Git LFS)
Normal file
BIN
07-Other/生活/装修/水电交底/智能家居水电交底.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
07-Other/生活/装修/水电验收表.pdf
(Stored with Git LFS)
Normal file
BIN
07-Other/生活/装修/水电验收表.pdf
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
07-Other/生活/装修/装修流程大全.pdf
(Stored with Git LFS)
Normal file
BIN
07-Other/生活/装修/装修流程大全.pdf
(Stored with Git LFS)
Normal file
Binary file not shown.
382
07-Other/生活/装修/装修电位图.excalidraw.md
Normal file
382
07-Other/生活/装修/装修电位图.excalidraw.md
Normal 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": {}
|
||||
}
|
||||
```
|
||||
%%
|
56
07-Other/生活/装修/装修电位布置.md
Normal file
56
07-Other/生活/装修/装修电位布置.md
Normal 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. 小厨宝。
|
BIN
07-Other/生活/装修/装修设计/装修设计_洗烘套装加隔板与晾衣杆.jpg
(Stored with Git LFS)
Normal file
BIN
07-Other/生活/装修/装修设计/装修设计_洗烘套装加隔板与晾衣杆.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
07-Other/生活/装修/装修设计/装修设计_轨道做成交错.jpg
(Stored with Git LFS)
Normal file
BIN
07-Other/生活/装修/装修设计/装修设计_轨道做成交错.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
07-Other/生活/装修/装修设计/装修设计_鞋柜下沉做两层.jpg
(Stored with Git LFS)
Normal file
BIN
07-Other/生活/装修/装修设计/装修设计_鞋柜下沉做两层.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
07-Other/生活/装修/装修这些东西要提前购买.pdf
(Stored with Git LFS)
Normal file
BIN
07-Other/生活/装修/装修这些东西要提前购买.pdf
(Stored with Git LFS)
Normal file
Binary file not shown.
450
07-Other/生活/装修相关.md
Normal file
450
07-Other/生活/装修相关.md
Normal 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
|
||||
- 凯度(CASDON)52L嵌入式微蒸烤 双热风 微蒸烤炸炖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¬e_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
|
||||
- 网关
|
||||
- 品牌
|
||||
- HomeKit:Aqara、易来
|
||||
- 一定要接入的电器:空调、智能控制开关(智能插座)、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、全部装完后味道较重甲醛含量测量谁负责
|
||||
- 图片
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
- 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块。半年~一年、噪音。南方没必要
|
||||
- 新风没有除甲醛功能。
|
||||
- 全屋净水:没有必要。
|
||||
- 垃圾处理器:几千,不值得购买。
|
334
07-Other/简历/CaravanSandWitch仿制文档.md
Normal file
334
07-Other/简历/CaravanSandWitch仿制文档.md
Normal 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中。
|
||||
- 使用梯子(强行混合)
|
||||
- 使用滑索,第四章获取(强行混合)
|
||||
- 商城方案:
|
||||
- https://www.fab.com/listings/ba155b30-ccb8-42c5-b136-b70933a13e32
|
||||
- 备选:https://www.fab.com/listings/c92c2470-6e09-4303-a532-b042c6057ca2
|
||||
- 车辆(载具):使用引擎的载具模版进行修改。
|
||||
- 进入/离开 载具
|
||||
- 刹车
|
||||
- 加速
|
||||
- 模拟悬挂系统
|
||||
- 载具功能:
|
||||
- [x] 区域扫描。可以描边显示被遮挡的关键物品/交互式元素。并且显示物品/交互式元素名称。
|
||||
- [x] UI:手动实现。
|
||||
- [x] 扫描效果后处理材质:
|
||||
- https://www.fab.com/listings/9de276bc-2200-431b-be39-23f1eada3063
|
||||
- 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教学管卡出入口/传送到指定地点
|
||||
- 商城方案
|
||||
- https://www.fab.com/listings/16335b65-1c61-49af-8014-1ddfd3987bb1
|
||||
- 对话系统
|
||||
- 备选商城插件:
|
||||
- https://www.fab.com/listings/052820ab-f423-48e8-978a-eefd4087b1a4
|
||||
- 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
|
||||
- 任务系统:
|
||||
- 备选商城插件:
|
||||
- https://www.fab.com/listings/079f4df6-f7c5-4837-982b-c19eda87aa84
|
||||
- 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)进行序列化,再保存。
|
||||
- 大地图 & 小地图:
|
||||
- 备选商城插件:
|
||||
- https://www.fab.com/listings/9af1c512-d619-4e13-a091-2944e45ea547
|
||||
- https://www.fab.com/listings/201fc801-3d30-4d51-b9f0-c030de9f822e
|
||||
- 堆特:手动实现。
|
||||
- 显示分类
|
||||
- 钉选帖子:主线 & 重要任务。
|
||||
- 活动帖子:支线任务。
|
||||
- 归档贴图:已完成任务。
|
||||
- 商城UI资产:
|
||||
- https://www.fab.com/listings/5be4e83d-165a-4c1b-aa37-839619998f75
|
||||
- UI
|
||||
- 主要UI组件
|
||||
- https://www.fab.com/listings/e678636b-66f1-40bd-b5bb-4d937376713e
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
- https://www.fab.com/listings/999c85c8-9a7b-43aa-a3e5-2b3bc6bc57ff
|
||||
- 主菜单: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://www.fab.com/listings/23869931-3e46-4c42-b541-9f6057f12d13
|
||||
- 
|
||||
- 粒子
|
||||
- 烟尘
|
||||
- https://www.fab.com/listings/111e39b5-5bd7-4b46-a915-2eda020a4ca2
|
||||
- 
|
||||
- 草
|
||||
- https://www.fab.com/listings/b9eeb225-77a8-4541-bf13-ba15dcb04182
|
||||
- 
|
||||
- https://www.fab.com/listings/a5d68ec0-fc5f-4f7b-8b70-dde2847b71dc
|
||||
- 
|
||||
-
|
||||
- 水面
|
||||
- https://www.fab.com/listings/9527db5d-9a12-4577-9507-35bd4e20e8ad
|
||||
- 
|
||||
- 地形材质
|
||||
- https://www.fab.com/listings/6d473ba6-f95d-45e8-8b6b-8b522ca2bf82
|
||||
- 
|
||||
- 
|
||||
- https://www.fab.com/listings/b003cb7c-f466-478b-8a3c-61cf21d3b678
|
||||
- 
|
||||
- https://www.fab.com/listings/c56b6094-d8c3-403a-8133-b73260bfa25e
|
||||
- 
|
||||
- 备选资产
|
||||
- https://www.fab.com/sellers/AleksandrIvanov
|
||||
- https://www.fab.com/listings/ee6d82a3-19b6-4a81-9999-79a5ce3f5f3c
|
||||
- 
|
||||
- https://www.fab.com/listings/3be0cc6f-df42-4646-9745-024ca945e474
|
||||
- 
|
||||
- https://www.fab.com/listings/b0fb5f1c-2f92-49a3-989d-084656275e4d
|
||||
- 
|
||||
|
||||
---
|
||||
|
||||
# 时间表
|
||||
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. 提交给主管最终评审。
|
@@ -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"}
|
||||
]
|
||||
}
|
BIN
07-Other/简历/CaravanSandWitch仿制文档图片/CaravanSandwitch玩法循环.png
(Stored with Git LFS)
Normal file
BIN
07-Other/简历/CaravanSandWitch仿制文档图片/CaravanSandwitch玩法循环.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
07-Other/简历/CaravanSandWitch仿制文档图片/Character1.png
(Stored with Git LFS)
Normal file
BIN
07-Other/简历/CaravanSandWitch仿制文档图片/Character1.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
07-Other/简历/CaravanSandWitch仿制文档图片/Character2.png
(Stored with Git LFS)
Normal file
BIN
07-Other/简历/CaravanSandWitch仿制文档图片/Character2.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
07-Other/简历/CaravanSandWitch仿制文档图片/Character_Renderdoc.png
(Stored with Git LFS)
Normal file
BIN
07-Other/简历/CaravanSandWitch仿制文档图片/Character_Renderdoc.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
07-Other/简历/CaravanSandWitch仿制文档图片/Launch_Cracker.png
(Stored with Git LFS)
Normal file
BIN
07-Other/简历/CaravanSandWitch仿制文档图片/Launch_Cracker.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
07-Other/简历/CaravanSandWitch仿制文档图片/LightingPass_Renderdoc.png
(Stored with Git LFS)
Normal file
BIN
07-Other/简历/CaravanSandWitch仿制文档图片/LightingPass_Renderdoc.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
07-Other/简历/CaravanSandWitch仿制文档图片/Renderdoc1.png
(Stored with Git LFS)
Normal file
BIN
07-Other/简历/CaravanSandWitch仿制文档图片/Renderdoc1.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
07-Other/简历/CaravanSandWitch仿制文档图片/Renderdoc2.png
(Stored with Git LFS)
Normal file
BIN
07-Other/简历/CaravanSandWitch仿制文档图片/Renderdoc2.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
07-Other/简历/CaravanSandWitch仿制文档图片/Scene1.png
(Stored with Git LFS)
Normal file
BIN
07-Other/简历/CaravanSandWitch仿制文档图片/Scene1.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
07-Other/简历/CaravanSandWitch仿制文档图片/Snipaste_2025-04-02_21-56-06.png
(Stored with Git LFS)
Normal file
BIN
07-Other/简历/CaravanSandWitch仿制文档图片/Snipaste_2025-04-02_21-56-06.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
07-Other/简历/CaravanSandWitch仿制文档图片/UI车辆提示以及收集物提示.png
(Stored with Git LFS)
Normal file
BIN
07-Other/简历/CaravanSandWitch仿制文档图片/UI车辆提示以及收集物提示.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
07-Other/简历/CaravanSandWitch仿制文档图片/WorldMap.png
(Stored with Git LFS)
Normal file
BIN
07-Other/简历/CaravanSandWitch仿制文档图片/WorldMap.png
(Stored with Git LFS)
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user