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.
|
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)
|
||||

|
34
07-Other/MAC/安装Mac UE开发环境.md
Normal file
34
07-Other/MAC/安装Mac UE开发环境.md
Normal file
@@ -0,0 +1,34 @@
|
||||
## 安装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
|
17
07-Other/MAC/安装UE开发环境.md
Normal file
17
07-Other/MAC/安装UE开发环境.md
Normal file
@@ -0,0 +1,17 @@
|
||||
## 安装homebrew
|
||||
参考:
|
||||
- https://www.jianshu.com/p/e0471aa6672d
|
||||
|
||||
国内镜像安装命令:
|
||||
```c++
|
||||
/bin/bash -c "$(curl -fsSL https://gitee.com/ineo6/homebrew-install/raw/master/install.sh)"
|
||||
```
|
||||
|
||||
## 编译注意事项
|
||||
一定要注意虚拟机的CPU以及内存的关系,CPU线程数 * 2 > 内存GB 数。所以给的CPU数目一定不能多。
|
||||
|
||||
## 编译命令
|
||||
```bash
|
||||
cd ./Desktop/UnrealEngine/Engine/Build/BatchFiles
|
||||
./RunUAT.sh BuildGraph -target="Make Installed Build Win64" -script=Engine/Build/InstalledEngineBuild.xml -set:WithDDC=true -set:SignExecutables=false -set:EmbedSrcSrvInfo=false -set:GameConfigurations=Development;Shipping -set:WithFullDebugInfo=false -set:HostPlatformEditorOnly=false -set:AnalyticsTypeOverride=false -set:HostPlatformDDCOnly=true -set:WithWin64=false -set:WithMac=true -set:WithAndroid=false -set:WithIOS=true -set:WithTVOS=false -set:WithLinux=false -set:WithLumin=false -set:WithLinuxArm64=false -set:CompileDatasmithPlugins=false -set:WithServer=false -set:WithClient=false -set:WithHoloLens=false
|
||||
```
|
108
07-Other/MAC/黑苹果配置笔记.md
Normal file
108
07-Other/MAC/黑苹果配置笔记.md
Normal file
@@ -0,0 +1,108 @@
|
||||
|
||||
# 概念
|
||||
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)
|
||||
|
||||
# 相关软件
|
||||
- [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编辑器。
|
||||
- DiskGenius:查看EFI用。
|
||||
- balenaEtcher-Setup:写入镜像工具。
|
||||
- [ProperTree](https://github.com/corpnewt/ProperTree):
|
||||
- [Hackintool](https://github.com/benbaker76/Hackintool):
|
||||
|
||||
https://pan.baidu.com/s/1VFuLao3LDb_VwQzXxx6Eyg 提取码:ile9
|
||||
也可以参考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
|
||||
|
||||
# Asus X670E Gene EFI -- Adaptable to Other X670, X670E, B650 and B650E Boards
|
||||
https://forum.amd-osx.com/threads/asus-x670e-gene-efi-adaptable-to-other-x670-x670e-b650-and-b650e-boards.4160/
|
||||
|
||||
为了使此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. 修改MMIO白名单
|
||||
4. 可选修改
|
||||
1. [ ] 将OpenCore加入Bios启动项中。[【Windows&macOS】完美双系统系列教程第2集,Windows环境下配置OC引导】 【精准空降到 05:35】](https://www.bilibili.com/video/BV1Bi4y1S7DN/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e&t=335)
|
||||
2. [ ] 为了EXPO关闭雷电4接口。(**Bios中操作,可能需要禁用**)
|
||||
3. [ ] USBToolBox 定制
|
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])*?-->
|
||||
```
|
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
|
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不能使用设计师界面类来创建!
|
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
|
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
|
||||
}
|
||||
```
|
753
07-Other/Unity/Unity-Chan Toon Shader PixelShader.md
Normal file
753
07-Other/Unity/Unity-Chan Toon Shader PixelShader.md
Normal file
@@ -0,0 +1,753 @@
|
||||
## 总览
|
||||
- `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;
|
||||
|
||||
|
||||
}
|
||||
|
||||
```
|
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"
|
||||
}
|
||||
|
||||
```
|
96
07-Other/VPS账号 以及PS4 DNS.md
Normal file
96
07-Other/VPS账号 以及PS4 DNS.md
Normal file
@@ -0,0 +1,96 @@
|
||||
## EPIC商城
|
||||
https://epicgames.hyperwallet.com/hw2web/landing.xhtml?faces-redirect=true&refreshme=true
|
||||
|
||||
lou jiajie
|
||||
Lou JiaJie
|
||||
owner
|
||||
INDIVIDUAL
|
||||
|
||||
## 向日葵
|
||||
blueroseslol
|
||||
orkj694780U9t7r1
|
||||
ljj199221
|
||||
|
||||
## 光猫
|
||||
useradmin
|
||||
nhg9h
|
||||
|
||||
telecomadmin60087438
|
||||
使用老爸的账号可以查看账号密码。
|
||||
|
||||
## Github Token
|
||||
- gitea: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
|
||||
- 图床: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
|
||||
|
||||
## youtube视频下载
|
||||
https://qdownloader.net/youtube-video-downloader
|
||||
https://www.y2mate.com/youtube/
|
||||
|
||||
## 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
|
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
Normal file
BIN
07-Other/技术演示.pptx
Normal file
Binary file not shown.
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] 耳塞
|
||||
- 其他
|
||||
- [ ] 玫瑰花茶
|
||||
- [ ] 电磁炉
|
||||
- [ ] 蒸煮锅
|
||||
- [ ] 水饺/牛肉卷/汉堡
|
||||
- [ ] 哑铃
|
||||
- [ ] 健身用衣服
|
186
07-Other/简历/压力面导图 .md
Normal file
186
07-Other/简历/压力面导图 .md
Normal file
@@ -0,0 +1,186 @@
|
||||
```markmap {scale=1.1}
|
||||
## 对于项目管理的了解
|
||||
### PSD通用格式
|
||||
灼华互娱
|
||||
主要使用ABC缓存结算头发与布料,但ABC缓存很可能因为过大而导致导入崩溃。所以灼华互娱对DCC的ABC导出插件进行魔改从而减少ABC缓存大小。但个人认为最佳的解决方案还是重新实现导出缓存格式,不过这需要实现DCC导出插件与Unreal4导入插件(引擎没有必要使用高精度数据),工作相对来说会大一些。
|
||||
|
||||
Niagara与Houdini结合
|
||||
|
||||
灼华互娱采用的方案是: 使用Niagara制作普通粒子特效。 对于需要解算巨量粒子则使用Houdini生成粒子缓存,导入Ue4中。在Ue4中生成模型Instance作为粒子。
|
||||
|
||||
#### 个人想法
|
||||
- 多版本美术资产浏览与比对功能。
|
||||
|
||||
### 聊聊自搭建服务器,以及Gitlab、Gitea
|
||||
- 小巧耗资源少,但功能有限。貌似邮件系统还有bug。
|
||||
|
||||
#### GitLFS与UE4
|
||||
- 二进制文件臃肿与git清理功能
|
||||
|
||||
#### GitHook与WebHook
|
||||
- GitHook:执行Git操作所会调用的钩子。
|
||||
- WebHook:是GitHook的一种,通过WebSocket通知其他软件与平台的钩子。
|
||||
|
||||
### 自动化集成系统
|
||||
- 多操作系统开发需要
|
||||
- 构建与发布产品(不同环境)
|
||||
- 构建DLL并且上传到哨兵程序中
|
||||
- 构建自定义引擎
|
||||
- 运行自动测试程序(UE4内置Session Frontend或者通过自动化集成系统直接与UE4自动测试框架进行交互)
|
||||
- 检测代码与分支的有效性
|
||||
- 为每个设置构建对应的版本
|
||||
|
||||
#### 好处
|
||||
- 减少不必要的加班时间
|
||||
- 可以及时发现bug(即使不写自动测试代码)
|
||||
|
||||
#### 相关的CL工具
|
||||
##### 因为是个人开发所以,仅仅是大致了解一下。
|
||||
- TeamCity
|
||||
- Jenkins
|
||||
- Bamboo
|
||||
|
||||
#### 为单位写个一个简单的全流程数据生产平台,功能为数据录入、展示、查询、以及打包成单机版程序
|
||||
|
||||
## 渲染知识
|
||||
- 我个人认为渲染的本质是:通过对场景与光源的采样,计算传递到摄像机与实际情况接近的光辐射量。
|
||||
|
||||
### 光谱
|
||||
- 光通常指的是人类眼睛可以见的电磁波(可见光),视知觉就是对于可见光的知觉。可见光只是电磁波谱上的某一段频谱,一般是定义为波长介于400至750奈(纳)米(nm)之间的电磁波。实际生活中,我们看到的光由很多不同强度和波长的光混合而成。
|
||||
- PBRT中会使用光谱值进行采样与计算,最后再将光谱值转化为RGB值输出。
|
||||
|
||||
### 采样
|
||||
根据场景不同,存在不同的采样方式。
|
||||
#### 采样器基本原理
|
||||
事先生成若干组采样点样本,之后进行乱序操作。在进行逐像素采样时,使用伪随机的方式选取随机序号的采样点组。
|
||||
#### 采样分布
|
||||
##### 分层采样
|
||||
对采样区域进行分割,各个子区域单独生成样本。
|
||||
|
||||
##### 低差异序列
|
||||
- Van der Corput:根据底数将指定长度的正整数序列转换为指定进制的数序列,之后以小数符号为分界线,将每个数左边翻转到右边去。
|
||||
- HAMMERSLEY 哈默斯利:使用底数为 样本序号/样本总数 的Van der Corput序列。
|
||||
- Halton 霍尔顿:使用底数为质数序列的Van der Corput序列。
|
||||
|
||||
#### 贴图采样
|
||||
- 双线性插值
|
||||
- ISOTROPIC TRIANGLE FILTER 各项同性三角形过滤 然无法生成高质量的结果,但速度比较快。该滤波器因为各项同性的关系不支持非正方形或非轴对称的范围。该滤波器的主要缺点是:在斜角度观察纹理时图像容易变模糊。因为不同的角度会导致采样率不一致。
|
||||
- ELLIPTICALLY WEIGHTED AVERAGE 椭圆权重均值
|
||||
|
||||
#### 抗锯齿
|
||||
- SSAA
|
||||
- MSAA
|
||||
- FXAA
|
||||
- TAA(使用了低差异序列作为采样点)
|
||||
|
||||
#### 采样偏差以及信号重建没看
|
||||
|
||||
### 材质
|
||||
- 材质分为金属(导体)、非金属(绝缘体)、半导体。
|
||||
|
||||
#### 反射模型概念:BRDF、BTDF、BSDF、BSSSDF
|
||||
|
||||
#### 菲尼尔
|
||||
- 近似函数Schlick(施利克)
|
||||
- 菲涅尔方程描述了光线接触到表面后反射与透射的比,它实际上Maxwell方程在光滑表面上的求解。 因为在现实环境中光的偏振现象较少,所以在PBRT假设光不偏振。
|
||||
- 一般情况下,计算计算机图形学中的常见操作都会忽略色散现象,以此极大地简化光线传输计算(色散:折射率随着光的波长而变化)。
|
||||
|
||||
#### 微表面模型
|
||||
与普通的着色模型的区别在于,普通的着色模型假设着色的区域是一个平滑的表面,表面的方向可以用一个单一的法线向量来定义来定义。
|
||||
通过统计学方式来模拟这种微观结构分布。假设物体表面由大量微观几何表面组成,且每个表面都是绝对光滑的。必须保证每个微表面都能反射。因此只能用一个概率分布函数来计算任意方向的微小表面在着色区域中存在的概率。
|
||||
|
||||
#### 漫反射模型
|
||||
- LAMBERTIAN漫反射(辐射度/π)
|
||||
- OREN–NAYAR(奥伦-纳亚尔)漫反射模型(基于微表面模型)
|
||||
|
||||
#### 半向量(位于入射法线与观察法线中间的向量)
|
||||
#### 微表面分布函数(法线分布函数)
|
||||
- 法线分布函数由粗糙度决定,主要影响高光
|
||||
- Beckmann–Spizzichino(贝克曼-斯皮兹奇诺)
|
||||
- Phong
|
||||
- GGX
|
||||
|
||||
#### 几何衰减因子(Shadowing和Masking)
|
||||
- Trowbridge–Reitz(特罗布里奇 瑞兹)
|
||||
|
||||
#### 金属微表面模型(BRDF)
|
||||
- TORRANCE–SPARROW(托伦斯-斯派若)
|
||||
- Cook-Torrance(托伦斯)
|
||||
- $$f(\omega_{o},\omega_{i})=\frac{D(\omega_{h})F(\omega_{o})G(\omega_{o},\omega_{i})}{4 cos\theta_o cos\theta_i}$$
|
||||
|
||||
#### 迪士尼PBR模型
|
||||
##### 漫反射
|
||||
- 用两个Fresnel项来增加入射角度低时反射的光线能量强度来模拟这种效果
|
||||
|
||||
float DisneyDiffuse(Vector3 In, Vector3 Out)
|
||||
{
|
||||
float oneMinusCosL = 1.0f - AbsCosTheta(In);
|
||||
float oneMinusCosLSqr = oneMinusCosL * oneMinusCosL;
|
||||
float oneMinusCosV = 1.0f - AbsCosTheta(Out);
|
||||
float oneMinusCosVSqr = oneMinusCosV * oneMinusCosV;
|
||||
|
||||
// Roughness是粗糙度,IDotH的意思会在下一篇讲Microfacet模型时提到
|
||||
float IDotH = Dot(In, Normalize(In + Out));
|
||||
float F_D90 = 0.5f + 2.0f * IDotH * IDotH * Roughness;
|
||||
|
||||
return INV_PI * (1.0f + (F_D90 - 1.0f) * oneMinusCosLSqr * oneMinusCosLSqr * oneMinusCosL) *
|
||||
(1.0f + (F_D90 - 1.0f) * oneMinusCosVSqr * oneMinusCosVSqr * oneMinusCosV);
|
||||
}
|
||||
|
||||
|
||||
#### FourierBSDF
|
||||
|
||||
#### 曲线拟合BSDF-使用真实测量值进行曲线拟合(FF15)
|
||||
|
||||
### 光线追踪框架
|
||||
#### 求交加速框架:BVH、KD树、SBVH
|
||||
- BVH:在一个轴上对两个图元进行分割。分割过程中通过公式计算最佳分割点(最佳的表面积与体积)步骤1、计算每个图元的边界信息并且存储在数组中2、使用指定的方法构建树3、优化树,使得树更加紧凑
|
||||
- KD树:经过空间分割的图元数。
|
||||
- SBVH:Nvidia改进过的BVH结构,先对图元进行空间分割,最后使用BVH方式分割。 https://www.nvidia.com/object/nvidia_research_pub_012.html
|
||||
|
||||
#### 蒙特卡洛方法与重要性采样(忘记了)
|
||||
- 逆推法、舍选法
|
||||
|
||||
#### 路径追踪
|
||||
##### 双向路径追踪
|
||||
|
||||
## UE4灯光相关
|
||||
### 物理灯光:使用现实世界的测量值作为标准进行灯光设置
|
||||
- 之前版本中Unreal4的灯光使用的是虚幻单位,这是的之前版本的灯光没有一个物理值的衡量标准。你很可能会因为后处理空间、曝光等因素影响而无法设置出正确的亮度值。例如:你已经将一个区域的效果调整的很自然,但是别的区域就会很奇怪(bloom、阴影、光照等效果互相不匹配)。 而前人已经积累了大量的摄影知识与经验,所以我们没有必要光靠感觉来设置灯光亮度与曝光度,而需要使用摄影标准来设置灯光,从而使得场景更加真实。
|
||||
- 大晴天 15~16
|
||||
- 多云 13
|
||||
- 晚上月光 -6~-2
|
||||
|
||||
### UE4的曝光问题
|
||||
- 因为UE4的渲染框架都是以EV100 为0设计的,所以需要将经验上使用的灯光强度从当前曝光值下转换到EV100 0,不然会出现若干bug,比如默认的天空盒。
|
||||
|
||||
### 流程
|
||||
#### 场景初始化
|
||||
- 禁用自动曝光
|
||||
- 禁用SSAO以及SSR
|
||||
- 保持默认Tone Mapping
|
||||
- 确认天光与方向光为默认数值。天光:亮度为1 方向光:亮度为3.141593(π)
|
||||
- 设置测试用球(与色卡) 使用亮度为3.141593(Pi)的方向光,此时灰球的亮度为0.5。(使用Color Picker、或者截图使用Photoshop查看)
|
||||
- 确定你的材质是物理正确的。BaseColor:太暗或者太亮 Metallic:应该为0或者1。(黑色或者白色Mask) Specular:应该为0~1范围的浮点数。不应该为颜色值。
|
||||
|
||||
#### 后续流程
|
||||
- 设置曝光相关属性。
|
||||
- 将所有灯光都是设置为Stationary类型。并开始设置方向光与其他主光源的基础属性(方向、亮度等)以及天光(与反弹次数)。
|
||||
- 设置LightMass属性,使用预览级对场景进行测试渲染。
|
||||
- 开启LightingOnly模式对光照进行检查。
|
||||
- 给场景中的带有反射属性的物体添加反射球并调整属性。(命令行全局的反射质量、与抗锯齿质量 )
|
||||
- 调整场景雾气
|
||||
- 后处理调色
|
||||
|
||||
#### 静态方案
|
||||
- lightMass(lightMap)
|
||||
- VolumeLightMap(二阶球谐与旧版)区别在于可以使用流加载以及手机使用
|
||||
|
||||
#### 动态方案
|
||||
- 级联阴影
|
||||
- 胶囊阴影
|
||||
- 接触阴影(后处理)
|
||||
- 距离场GI(废弃)
|
||||
- 距离场阴影与AO
|
||||
- 4.25屏幕空间GI
|
||||
```
|
173
07-Other/简历/图形学细节.md
Normal file
173
07-Other/简历/图形学细节.md
Normal file
@@ -0,0 +1,173 @@
|
||||
## RDG
|
||||
### FrameGraph
|
||||
首先需要了解Frame Graphe的设计思想,为了速成,看了知乎大佬 奔驰 的文章。但也因为速成,必然会在一些地方有所误解,还请见谅。这里说一下我的理解:Frame Graphe思想可以理解为一帧中执行逻辑的图,没有限定是渲染还是Gameplay逻辑,但不管怎么说其重点在于图。
|
||||
大致操作为将逻辑以最小功能为标准封装成单独的Pass,Pass与Pass之间是变量(或者可以理解为资源)的传递的关系,因此就可以把整个逻辑关系转换成一个Graphe。
|
||||
|
||||
UE4的RDG全名为Render Dependency Graph,感觉是为了能更好的管理资源的加载回收(尤其是RT),拯救一下显卡上珍贵的显存以及IO带宽。同时方便程序员理解代码,不会写出毛线团一样的代码。至于生成的图没有看过在哪,估计在4.25加在Insight调试系统里吧。
|
||||
### 大致流程
|
||||
|
||||
通过FRDGBuilder对象构建RDG渲染流程。之后塞入定义的RDG Uniform以及Shader后,运行这个流程。RT会使用外部的IPooledRenderTarget接口取得(比如各种GBuffer),当然也可以自己创建,之后绑定到RDG Uniform中的RenderTargets[0]数组中。最后根据是ComputShader还是PixelShader选择分发或是绘制即可。但源码里也没有RDG的PixelShader的代码(95%都是ComputeShader的),经过试验,RDG的PixelShader的使用方法还是和旧版的GloalShader比较像的。
|
||||
|
||||
既然说到这个,之前我也是看了虚幻开放日的技术分享,知道了两者的区别。
|
||||
### ComputeShader与PixelShader对比
|
||||
ComputeShader对于PixelShader的优势:
|
||||
1. PixelShader只能处理当前Shader,ComputeShader是任意位置可写。可以用于编写屏幕空间反射等需要将效果写入任意位置的效果。
|
||||
2. 可以更好地利用显卡的并线单元。
|
||||
3. 共享内存:举个例子模糊、等需要多次采样各个像素的算法,使用共享内存就可以减少采样次数与消耗。
|
||||
|
||||
PixelShader对于ComputeShader的优势
|
||||
1. PixelShader可以预加载贴图并且缓存UV,使得读取贴图的效率会非常高。而ComputeShader需要计算出UV,所以做不到这点。
|
||||
2. PixelShader支持FrameBuffer压缩,减少带宽压力。
|
||||
3. PixelShader支持更多的贴图格式。
|
||||
|
||||
ComputeShader还存在无法读取UAV,比如把RT转化成SRV后才能读取,问了EPIC的luoshuang,说是历史遗留问题,UE5会解决。
|
||||
|
||||
## 制作玉石效果
|
||||
使用寒霜的FastSSS方案,这里我下载了演讲的PPT。厚度贴图可以使用Substance Painter烘焙。算法是反转法线后使用AO算法,在像素对应的三维坐标球形区域内生成若干采样点,计算模型内部的点与总点数的比。之后就是直接套寒霜公式了。
|
||||
|
||||
## 光线追踪
|
||||
《Ray Tracing in One Weekend》只是尝个鲜,学习《Ray Tracing from the Ground Up》后,拥有了一个基础的Ray Tracing框架。然后就是PBRT了,相当不错的教科书。因为单线程渲染效率太低,所以使用了直接使用Inter的TBB库,但最后发现写的渲染器
|
||||
会出现样本混乱的情况的。同时因为没有写并行库的基础,所以之后的后面几章就没有去写代码了。如果有时间会去看《c++并发编程实战》的英文版来学习并行编程。
|
||||
### PBRT
|
||||
1. pbrt采用了FLOAT宏来调整浮点类型,不过我记得VS的设置就可以直接调整。并且手动实现了EFLoat类来消除浮点数在四则运算后积累的误差。
|
||||
2. pbrt采用c++11的线程编写,没使用任何并行库。
|
||||
3. 实现了一个场景描述文件格式,可以将dcc软件制作的场景导入渲染。
|
||||
|
||||
### 一束光线
|
||||
我们从眼睛中看到的图形可以理解为视觉细胞接收到场景中辐射与反射的光辐射信息的结果,光线追踪就是计算机通过模拟这一过程所发明出的算法。根据光的波长不同,将其排列成为光谱。但受限于人眼感光细胞,人眼无法看到所有颜色,对于可见光,人们建立了不同色域范围的色彩标准。常用的是rgb、sRGB等。
|
||||
|
||||
#### 光源
|
||||
PBRT为了将光谱值转换为RGB值,实现了SampledSpectrum(采样频谱)与RGBSpectrum(RPG频谱)类,这里我没看懂,好像是通过对3条曲线采样求积分得到3轴的比值。
|
||||
|
||||
#### 采样
|
||||
除非直视光源,不然人眼接收到的光全都是经过N次反射的,因为我们可以把人眼接收到的图像理解成一个N阶多项式方程。同时这也相当于将一个自然现象模拟问题转化为一个数学问题,可以用其他的数学工具来解决问题。但该方程无法直接求解,所以会使用蒙特卡洛方法求近似值。
|
||||
|
||||
#### 蒙特卡洛方法
|
||||
是一种以概率统计理论为基础的数值计算方法。它的核心思想就是使用随机数(或更常见的伪随机数)来解决一些复杂的计算问题。通过对大量抽样样本进行统计,计算出其分布规律。
|
||||
|
||||
概率密度函数 pdf 一个描述这个随机变量的输出值,在某个确定的取值点附近的可能性的函数。
|
||||
累积分布函数 cdf 是概率密度函数的积分,能完整描述一个实随机变量X的概率分布。
|
||||
|
||||
有关抽样:
|
||||
#### 逆转法
|
||||
|
||||
#### 舍选法
|
||||
某些函数,可能无法通过对其进行积分从而获得pdf,或者无法通过计算获得逆cdf。舍选法是一种无需进行以上任意一个步骤就能根据函数分布生成样本的技术。本质上它是一种投飞镖法,假设我们想从某个函数f(x)中抽取样本,但是我们有一个pdf p(x)满足f(x)< c p(x),假设我们知道如何从p中获取样本
|
||||
|
||||
这个过程反复选取一对随机变量(X,ξ),如果点(X,ξc p(X))是在f(X)下面,那么接受样本X。否则,它将被拒绝,并再次选取一个新的样本对。这个方法的效率取决于f(x)与cp(x)的接近程度,越相似收敛地越快。而且适用于任意维度的函数。
|
||||
|
||||
|
||||
#### 重要性采样
|
||||
选择一个与目标概率密度函数具有相同形状的分布函数进行抽样,来减少方差。形状越接近,收敛越快。在《Raytracing in weekend》中对方形面光源运行了重要性采样。
|
||||
|
||||
#### 分层采样
|
||||
对采样区域进行分割,各个子区域单独生成样本。
|
||||
|
||||
#### 低差异序列
|
||||
低差异序列可以非常高效的生成分布非常均匀的高质量样本集合,相比伪随机数极大的提高蒙特卡洛积分收敛的效率,并且它们的实现都不复杂。
|
||||
|
||||
- Van der Corput:根据底数将指定长度的正整数序列转换为指定进制的数序列,之后以小数符号为分界线,将每个数左边翻转到右边去。
|
||||
- HAMMERSLEY 哈默斯利:使用底数为 样本序号/样本总数 的Van der Corput序列。
|
||||
- Halton 霍尔顿:使用底数为质数序列的Van der Corput序列。
|
||||
|
||||
UE4中的TAA好像就是用的低差异序列生成的样本。
|
||||
|
||||
#### 使用均值样本会产生 混叠现象,也就是摩尔纹
|
||||
在信号处理以及相关领域中,走样(混叠)在对不同的信号进行采样时,导致得出的信号相同的现象。它也可以指信号从采样点重新信号导致的跟原始信号不匹配的瑕疵。它分为时间走样(比如数字音乐、以及在电影中看到车轮倒转等)和空间走样两种(摩尔纹)。这里我们不详细展开。
|
||||
|
||||
#### 采样器基本原理
|
||||
事先生成若干组采样点样本,之后进行乱序操作。在进行逐像素采样时,使用伪随机的方式选取随机序号的采样点组。
|
||||
|
||||
#### 抗锯齿
|
||||
因为场景的定义在三维空间中是连续的,而最终显示的像素则是一个离散的二维数组。所以判断一个点到底没有被某个像素覆盖的时候单纯是一个“有”或者“没有"问题,丢失了连续性的信息,导致锯齿。
|
||||
|
||||
SSAA:超采样抗锯齿,高分辨渲染,之后取平均值结果。
|
||||
MSAA:在光栅化阶段判断一个三角形是否被像素覆盖的时候会计算多个覆盖样本(Coverage sample),最后根据权重样本占比所在的片元进行着色,不兼容延迟渲染管线。
|
||||
FXAA:边缘检测抗锯齿,是一种后处理抗锯齿技术。
|
||||
TAA:时间抗锯齿,将采样放到时间轴上的抗锯齿技术。对一个像素使用抖动效果,之后再将所有的采样混合起来。他需要解决判断每帧中这个像素的移动位置。但在一些运动较大、或者帧数较低的情况会出现 重影的问题。
|
||||
|
||||
#### 贴图采样
|
||||
- 双线性插值
|
||||
- ISOTROPIC TRIANGLE FILTER 各项同性三角形过滤 然无法生成高质量的结果,但速度比较快。该滤波器因为各项同性的关系不支持非正方形或非轴对称的范围。该滤波器的主要缺点是:在斜角度观察纹理时图像容易变模糊。因为不同的角度会导致采样率不一致。
|
||||
- ELLIPTICALLY WEIGHTED AVERAGE 椭圆权重均值
|
||||
|
||||
#### 偏差分析与重采样
|
||||
没学过相关知识,无法继续。
|
||||
|
||||
#### 加速结构
|
||||
如果光线每次与物体交互都需要遍历所有三角形,那效率将会非常低,为了提高求交速度,我们可以在渲染前实现构建加速结构,常用的有:BVH、KD。
|
||||
|
||||
BVH:
|
||||
1. 计算每个图元的边界信息并且存储在数组中(图元号,包围盒以及中心点)
|
||||
2. 使用指定的方法构建树(中间对半切的方法、SAH:首先计算图元位置,将其分配到12个桶中,并且计算每个桶最大边界盒,再遍历每个桶边界盒的Min与Max坐标计算最佳切分点,通过 切分后两个边界盒的表面积/当前包围盒的表面积来计算求交效率)子图元中质心距离最大的轴向作为分割方向。
|
||||
|
||||
KD树:对3个轴的所有的切分方案进行尝试,最终计算出最优的空间分割方案。
|
||||
|
||||
KD树的分割(每个轴遍历所有分割方案)与BVH(SAH)分割(每个轴12次分割)更加精确,BVH保证每个图元只有一次引用,内存占用较少,构建较为简单,而KD树并不保证,所以内存占用较多。因此KD树的求交效率要比BVH好,也导致了渲染时需要花费比BVH更多的时间在构建KD树上。 同时KD树在求交时会计算与光线与分割面的相交位置是否在[min,max]中来判断是否需要与茎节点中的两个节点一一求交,以此来减少求交计算量。而BVH通过深度优先遍历树进行求交的。 总结:BVH是基于物体分割的,KD数是基于空间分割的。
|
||||
|
||||
早几年NVIDIA研发出将两者结合的方案SBVH。这种算法可以节约因为图元分布(大小)不均匀而造成的BVH树求交效率低下。
|
||||
1. 找到一个对象分割候选者:使用SAH算法构建一个BVH。
|
||||
2. 找到一个空间分割候选者:类似构建KD树算法。
|
||||
3. 选择获胜者:基于SAH,选择消耗最小的作为最后结果。如果不满足叶子节点生成条件则继续分割。
|
||||
|
||||
我记得是前几次进行空间分割,后续使用对象分割。
|
||||
|
||||
### 材质模型
|
||||
接下来就是处理光线与物体交互的结果了。PBRT把材质分开金属、非金属、与半导体,其中半导体较为复杂,Pbrt里没有介绍。
|
||||
|
||||
根据材质性质不同,可以分为一下表面分布函数:
|
||||
BRDF双向反射分布函数:在单位微表面上反射辐亮度与入射辐射照度的比值。
|
||||
BTDF双向透射分布函数:在单位微表面上折射辐亮度与入射辐射照度的比值。
|
||||
与两者结合的BSDF,对于半透明材质则使用BSSSDF双向表面散射分布函数。
|
||||
|
||||
#### 微表面模型
|
||||
本来对于一个不确定表面可能需要使用采样要求积分,因为物体的微观表面参差不齐,无法通过分析的方法精准模拟。但微表面模型假定物体由大量微小的绝对光滑表面组成,之后就可以根据统计学计算出模拟出看上去正确的结果。
|
||||
现在用得较多的Burley模型(迪士尼模型),也是UE4使用的,就是基于这个微表面模型。
|
||||
|
||||
#### BRDF公式
|
||||
对于各项同性的BRDF公式就是: 漫反射 +(菲尼尔函数 * 法线分布项 D * 几何项G)/(4 cos入射角 * cos出射角)
|
||||
Cook-Torrance
|
||||
|
||||
##### 菲尼尔
|
||||
- 一般都会使用近似近似函数Schlick(施利克)
|
||||
- 菲涅尔方程描述了光线接触到表面后反射与透射的比,它实际上Maxwell方程在光滑表面上的求解。 因为在现实环境中光的偏振现象较少,所以在PBRT假设光不偏振。
|
||||
- 一般情况下,计算计算机图形学中的常见操作都会忽略色散现象,以此极大地简化光线传输计算(色散:折射率随着光的波长而变化)。
|
||||
|
||||
##### 微表面法线分布函数 (法线分布项 D)
|
||||
- 法线分布函数由粗糙度决定,主要影响高光
|
||||
- Beckmann–Spizzichino(贝克曼-斯皮兹奇诺)
|
||||
- Phong
|
||||
- Trowbridge–Reitz(特罗布里奇 瑞兹)ggx
|
||||
- 迪士尼改进了 Trowbridge–Reitz ,将其命名为GTR。
|
||||
|
||||
##### 几何衰减因子(Shadowing和Masking)几何项G
|
||||
- Smith阴影函数
|
||||
- 迪士尼对Smith 改造,smith-ggx
|
||||
|
||||
##### 漫反射
|
||||
- pbrt中使用 OREN–NAYAR(奥伦-纳亚尔)漫反射模型(基于微表面模型)
|
||||
|
||||
在《Raytracing in weekend》中,会使用LAMBERTIAN漫反射 (辐射度/π) 作为漫反射项,即不论任何角度,都会反射出一样的辐射度。
|
||||
Lambert漫反射模型在边缘上通常太暗,Disney开发了一种用于漫反射的新的经验模型,以在光滑表面的漫反射菲涅尔阴影和粗糙表面之间进行平滑过渡。思路是使用了2个Schlick Fresnel近似相乘作为调节因子
|
||||
|
||||
##### 测量BRDF
|
||||
pbrt中还介绍了一种通过测量来拟合brdf的方法,ff15中用到这种方法。
|
||||
|
||||
##### BTDF
|
||||
没看过,应该乘以 菲尼尔来计算的
|
||||
|
||||
##### BSSDF
|
||||
没看过,UE4好像通过生成均值采样点,之后再通过距离进行近似计算的
|
||||
|
||||
##### 迪士尼模型
|
||||
根据透明度参数,对透明渲染结果与绝缘体渲染结果混合,之后再根据金属度与金属渲染结果混合。
|
||||
|
||||
### 路径追踪
|
||||
从摄像机发射采样光线,最后把这条光线若干次反射过程中结果累加起来。
|
||||
|
||||
### 优化
|
||||
#### 光子映射
|
||||
通过让光源发射光子,来生成光子贴图,这样就能让收敛速度变快。
|
||||
|
||||
#### 双向路径追踪追踪
|
||||
同时从光源与摄像机发射采样光线,一起构建光路。之后将各个反射点直接连接起来,这样就能快速构建多条光路了。大大加快收敛速度。当然这些光路需要进行可见性测试。
|
84
07-Other/简历/成果.md
Normal file
84
07-Other/简历/成果.md
Normal file
@@ -0,0 +1,84 @@
|
||||
```markmap {scale=1.1}
|
||||
## 美术流程
|
||||
### Ue4中的场景物体融合方式与优化
|
||||
1. World aligned texturing 世界坐标映射贴图
|
||||
2. Pixel depth offset 像素深度偏移
|
||||
3. Distance field mesh blending 距离场模型融合
|
||||
4. Runtime virtual texturing 实时虚拟贴图
|
||||
|
||||
### Layered Material
|
||||
1. 尝试外包一个角色,并让外包以Material Mask的方式使用Substance Painter完成贴图。最后走完EPIC的Layered Material流程。
|
||||
|
||||
### UE4头发方案研究
|
||||
1. 学习Ornatrix for Maya插件,并且尝试制作头发。
|
||||
2. 学习Ornatrix制作面片头发方案,并且制作出成品。
|
||||
3. 走通UE4 Niagara Hair流程。
|
||||
|
||||
### UE4物理灯光流程
|
||||
1. 如何使用物理灯光系统
|
||||
2. 流程包括:初始化场景(关闭若干后处理效果,并且固定曝光)=》场景检查(贴图饱和度与亮度)=》按照当前环境对应的现实状况设置曝光度与天光HDRI(因为每个HDRI的亮度范围不一样,还可以在环境球材质中调整)=》主要自然光源完成后,烘焙二阶球谐GI=》按照场景发光物体进行布光=》烘焙阴影并检查是否有错误阴影=》逐个开启后处理效果并检查(反射=》AO)=》
|
||||
3. 了解UE4静态光照与动态光照所有相关的功能与用法。
|
||||
|
||||
### 动画蓝图
|
||||
1. 学习物理模拟动画节点
|
||||
2. 学习AdvancedLocomotionV3/V4 项目,以此对UE4动画系统有了一定了解。(FootIk、8方向运动、状态切换逻辑)
|
||||
3. 为了复现AdvancedLocomotionV4项目中动画的动画曲线(这些曲线会控制状态机以及其他融合动画效果),使用AnimModifier编写生成逻辑,最终复现了Run、Walk等移动动画的曲线。
|
||||
4. 学习动画蓝图的动态加载功能,解决当动画蓝图逻辑较多时的性能问题。对于每个武器拥有不同攻击动画与逻辑的情况,有很大的作用。
|
||||
|
||||
### UE4FaceARSample Iphone面部方案学习
|
||||
|
||||
## 程序
|
||||
### GameplayAbilitySystem GAS
|
||||
1. 学习GAS系统,了解Ability、Effect、AttributeSet、Tasks等类。
|
||||
1. 完全学习ActionRPG项目,并且将物品系统封装成组件,放在插件中以备后续使用。
|
||||
2. 解决只使用一个Ability类实现一套连招技能。
|
||||
3. 学习GameplayAbilitySystem中配套的Debug方法
|
||||
|
||||
### 编写Slate控件
|
||||
1. 实现整数版本的SpinBox
|
||||
2. 实现类似血源诅咒那种带有虚血的进度条
|
||||
3. 实现通过函数调用C++、蓝图通用的MessageBox,顺便了解了UMG、Slate、GameViewportClient之间的大致关系
|
||||
|
||||
### DCC插件
|
||||
1. 调用Maya的HumanIK插件实现骨骼批量重定向功能,将Max的Bip骨骼转换成Ue4骨骼。后续可以再加入其它定制功能。
|
||||
2. 实现在选中父骨骼后批量生指向若干选中顶点的之骨骼,并且生成蒙皮权重。大致作用是将低模角色的BlenderShape权重传递到高精度角色上。
|
||||
|
||||
### 项目管理
|
||||
1. 和他人合作Demo时,在租用的VPS上,搭建Gitea以实现版本管理(包括二进制文件)。
|
||||
2. 学习UE4的自动测试框架。
|
||||
|
||||
## 图形学效果
|
||||
### 渲染框架
|
||||
1. 通过学习EPIC的PPT以及源代码,最后撰写全网第一份RenderDependencyGraph(RDG)的使用教程以及HelloWorld案例。(案例已更新至4.26版本)
|
||||
2. 通过学习源代码,了解4.25版本传统渲染框架GlobalShader的变动,并撰写文章。
|
||||
|
||||
### Shader
|
||||
1. UE4后处理材质:各种卷积描边、各种模糊等。
|
||||
2. 在Ue4中实现对二次元模型进行透视校正,让模型更加二次元,顺便学习了BasePass中的顶点数据处理过程。
|
||||
3. 使用Gerstner波制作海洋效果,水体透射使用(水面深度-水底深度)来得到深度值,之后对使用2种颜色差值进行得到最后颜色,白沫使用基于Saturate高度的方法。后面如果再做,打算使用houdini通过粒子模拟的方式来生成白沫贴图。
|
||||
4. 学习EPIC TA的RayMarching 溶球效果,(使用了SDF)。
|
||||
5. 学习EPIC TA的RayMarching 2D 3D云效果。
|
||||
6. 修改ShaderModel以实现卡通渲染材质。
|
||||
|
||||
### 自定义图元类
|
||||
1. 在不修改管线的情况下,以插件的方式实现物体外描边效果
|
||||
2. 在不修改管线的情况下,以插件的方式实现UE4剖面效果
|
||||
|
||||
### 离线渲染学习
|
||||
1. 学习完《An Introduction to Ray Tracing》 系列3本书,并且渲染出对应效果的图片。
|
||||
2. 学习《Ray Tracing from the Ground Up》,并且使用Qt重新实现一遍离线渲染器(单核渲染)。
|
||||
3. 学习《pbrt》至第10章,后续因为没有多线程库编写能力,只能大致看了看。
|
||||
|
||||
## 其他效果
|
||||
### 面试J3时的面试题
|
||||
1. 看gif图实现枪支表面的能量效果,使用一张Noise贴图,分别使用平移与旋转,2种移动UV方式,叠加到一起。2种移动方式的贴图各自乘以了不同的颜色。
|
||||
2. 在不是用UE4 SSS ShaderModel情况下,使用寒霜的FastSSS方案,实现了玉石枪支的效果。因为家里电脑性能以及面试周期问题,没有通过添加ShaderModel的方式来修改,所以能量不够守恒。
|
||||
3. 使用Python为Maya编写一个生成圆柱体的插件,点、面、法线都是手动计算的,可以任意设置大小、横纵以及圆弧段数,UV采用Maya内置的映射命令。 使用Qt编写了插件界面。
|
||||
4. 实现了一个在屏幕空间宽度恒定的“赛博朋克”风格网格材质,通过使用了透视矩阵 对宽度进行校正来实现。
|
||||
5. 在不改变模型拓扑结构或者动态改变模型结构的情况下,设计一种基于Vertex Program的算法,动态改变毒圈模型的顶点位置。使得玩家走近毒圈边缘时,看到尽可能平滑的弧线。——最后因为时间不够,只实现了核心逻辑:使用一个没有封口的圆柱体,输入2个角度,让圆柱体像扇子那样按照角度进行折叠,也就是将顶点挤压到一起。最后只差通过摄像机方向、位置、fov、圆心位置计算2个角度以及威风口处的旋转位置(将威风口藏在摄像机背后)。
|
||||
|
||||
## 代码
|
||||
1. GAS
|
||||
2. 网络联机
|
||||
3.
|
||||
```
|
52
07-Other/简历/技能.md
Normal file
52
07-Other/简历/技能.md
Normal file
@@ -0,0 +1,52 @@
|
||||
|
||||
|
||||
## UE4
|
||||
|
||||
使用Custom节点实现后处理效果 (https://www.cnblogs.com/blueroses/p/6361517.html、
|
||||
https://www.cnblogs.com/blueroses/p/6511751.html)
|
||||
学习Ryan Brucks的溶球视频教程(https://www.cnblogs.com/blueroses/p/6391995.html)
|
||||
学习Ue4的GlobalShader与Computer写法(https://zhuanlan.zhihu.com/p/66514192、
|
||||
https://zhuanlan.zhihu.com/p/66841824)
|
||||
在不修改源代码的情况下实现多Pass效果(https://zhuanlan.zhihu.com/p/69139579、
|
||||
https://zhuanlan.zhihu.com/p/69156465)
|
||||
学习GameplayAbility系统、ActionRPG项目(https://zhuanlan.zhihu.com/p/76395757、
|
||||
https://zhuanlan.zhihu.com/p/75987858等)
|
||||
学习UE4的灯光流程(https://zhuanlan.zhihu.com/p/89270442、https://zhuanlan.zhihu.com/p/92456949等)
|
||||
探索UE4头发制作方法(https://zhuanlan.zhihu.com/p/98751524、https://zhuanlan.zhihu.com/p/99345059)
|
||||
|
||||
Ue4动画系统学习(https://zhuanlan.zhihu.com/
|
||||
|
||||
### Slate
|
||||
扩展Slate的进度条以实现血源诅咒中的虚血效果。
|
||||
|
||||
### 渲染
|
||||
|
||||
Shader
|
||||
|
||||
灯光流程
|
||||
|
||||
渲染框架
|
||||
|
||||
### 动画系统
|
||||
|
||||
### 编程
|
||||
GAS
|
||||
|
||||
## 其他语言与框架
|
||||
Qt、Python、JavaScript、VUE全家桶、Node.js。
|
||||
|
||||
## DCC
|
||||
1. 对Maya比较熟悉,编写过2个Maya插件,分为调用HumanIK对动作文件进行批量重定向、批量生成指向选中顶点的骨骼并赋予蒙皮(用于表情)。
|
||||
2. 目前正在学习Houdini。
|
||||
|
||||
## 能力佐证
|
||||
- 知乎:https://www.zhihu.com/people/blueroseslol/
|
||||
- Github: https://github.com/blueroseslol
|
||||
- Blog:http://blueroses.top
|
||||
- Blog(已废弃):https://www.cnblogs.com/blueroses
|
||||
|
||||
## 工作经历
|
||||
国家锁具产品质量监督检验中心 2013年07月~至今
|
||||
- 使用Node.js为公司编写内网数据管理系统,具有数据汇总、展示、打包单机版等功能。
|
||||
- 使用Electron为公司编写数据查询软件。
|
||||
- 使用Qt为公司编写其他工具产品。
|
BIN
07-Other/背景.png
Normal file
BIN
07-Other/背景.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.9 KiB |
33
07-Other/装机/配置单.md
Normal file
33
07-Other/装机/配置单.md
Normal file
@@ -0,0 +1,33 @@
|
||||
- CPU AMD 7950x ¥3899.00
|
||||
- https://item.jd.com/100039537667.html
|
||||
- 板U套装 https://item.jd.com/100040434245.html#none
|
||||
- 机箱
|
||||
- 追风者P500A黑中塔EATX(前/顶360水冷)侧透版 ¥579.00
|
||||
- https://item.m.jd.com/product/2285652.html?utm_user=plusmember&gx=RnExy2RbYGXRmtRP--txX5kFp3gD3KX1Scsq&ad_od=share&utm_source=androidapp&utm_medium=appshare&utm_campaign=t_335139774&utm_term=Wxfriends
|
||||
- 追风者(PHANTEKS)G500A白P500A ¥889.00
|
||||
- https://item.m.jd.com/product/2285652.html?utm_user=plusmember&gx=RnExy2RbYGXRmtRP--txX5kFp3gD3KX1Scsq&ad_od=share&utm_source=androidapp&utm_medium=appshare&utm_campaign=t_335139774&utm_term=Wxfriends
|
||||
- 主板
|
||||
- ROG CROSSHAIR X670E GENE ¥ 3999.00
|
||||
- https://item.jd.com/100038989087.html
|
||||
- (白色)ROG Strix X670E-A ¥2999
|
||||
- https://item.jd.com/100038989095.html#crumb-wrap
|
||||
- ROG Strix X670E-E ¥3999
|
||||
- https://item.jd.com/100038989121.html#crumb-wrap
|
||||
- 硬盘
|
||||
- 致态(ZhiTai)长江存储 2TB TiPlus7100 ¥1099 * 4
|
||||
- https://item.m.jd.com/product/100049404395.html?utm_user=plusmember&gx=RnExy2RbYGXRmtRP--txX5kFp3gD3KX1Scsq&ad_od=share&utm_source=androidapp&utm_medium=appshare&utm_campaign=t_335139774&utm_term=QQfriends
|
||||
- 电源
|
||||
- 海韵SEASONIC 金牌全模FOCUS GX1000W ¥1299
|
||||
- https://item.m.jd.com/product/100004187691.html?utm_user=plusmember&gx=RnExy2RbYGXRmtRP--txX5kFp3gD3KX1Scsq&ad_od=share&utm_source=androidapp&utm_medium=appshare&utm_campaign=t_335139774&utm_term=QQfriends
|
||||
- 内存
|
||||
- 芝奇(G.SKILL)64GB(32Gx2) DDR5 6000 https://item.jd.com/100044083045.html
|
||||
- 散热
|
||||
- 瓦尔基里(VALKYRIE)E360 LOKI VK 一体式 ¥948
|
||||
- https://item.m.jd.com/product/100047920915.html?utm_user=plusmember&gx=RnExy2RbYGXRmtRP--txX5kFp3gD3KX1Scsq&ad_od=share&utm_source=androidapp&utm_medium=appshare&utm_campaign=t_335139774&utm_term=QQfriends
|
||||
- 显卡
|
||||
- 七彩虹(Colorful)战斧 GeForce RTX 4060 Ti ¥ 3199
|
||||
- https://item.jd.com/100058595037.html
|
||||
- 风扇 8块一个
|
||||
- https://item.taobao.com/item.htm?id=528014114456&price=8&sourceType=item&sourceType=item&suid=acb41b6f-9be2-4b61-9fd4-2e6c5dcaba8e&shareUniqueId=20688680961&ut_sk=1.Y9fmoWP2y8sDADFVchIQ9X6F_21646297_1679985712096.Copy.1&un=da848ce999c44d6e0f8768acc41474bb&share_crt_v=1&un_site=0&spm=a2159r.13376460.0.0&sp_abtk=gray_1_code_simpleAndroid2&tbSocialPopKey=shareItem&sp_tk=ZFZMY2Q5Z3h2VnM%3D&cpp=1&shareurl=true&short_name=h.UIy0Zm8&bxsign=scdQS6K-k4f5NIJlD-U8U_OvYntdUFj3Bz89wSvxWAiOdJ6XGqMJsWuATqex2bMX6ByYm_6Ee0JtsNO5OqdeVAYWgAU6WoqwMyFkme0PL0abkeFweBQOQP7R2LyLRw9hn2j&tk=dVLcd9gxvVs&app=chrome
|
||||
|
||||
24000
|
Reference in New Issue
Block a user